Community코딩 & 개발github.com

partite-ai/write-particle-typescript

TypeScript 또는 JavaScript로 Partite Particle을 생성, 편집 또는 확장합니다. Particlefile.ts 또는 Particlefile.js를 사용합니다. 도구 핸들러, API 통합, 자격 증명 및 런타임 기능에 사용됩니다. Python Particle의 경우 해당 언어 스킬을 사용 가능할 때 사용하세요.

write-particle-typescript란 무엇인가요?

이 스킬은 TypeScript 또는 JavaScript로 Partite Particle을 작성하는 방법을 설명합니다. Particle은 단일 파일 프로그램으로, JSON-Schema 입력을 받는 하나 이상의 **도구**를 Partite 런타임에 노출합니다. 주요 특징으로는 `Particlefile.ts`(권장) 또는 `Particlefile.js` 파일 하나만 필요하며, npm 종속성은 인라인 `import` 문을 통해 선언하고 빌드 시 자동으로 해결됩니다. 또한 `fetch`를 통한 아웃바운드 HTTP 요청, `node:fs` API를 통한 파일 시스템 액세스, `credentials`를 통한 인증, `kv`를 통한 키-값 저장소 등 다양한 런타임 API를 지원합니다. 이 스킬은 Claude Code, Cursor, Codex 등 다양한 AI 에이전트 플랫폼에서 활용할 수 있으며, 도구 핸들러 작성부터 자격 증명 설정, 배포 및 실행 워크플로우까지 상세히 안내합니다.

지원 대상✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/partite-ai/partite-skills/tree/main/skills/write-particle-typescript

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

TypeScript/JavaScript Particle 작성

Particle은 도구(JSON-Schema 입력을 사용하는 명명된 작업)를 Particle 런타임에 노출하는 단일 파일 프로그램입니다. 이 스킬은 빌드 및 실행 가능한 Particle을 만드는 데 필요한 모든 것을 알려줍니다.

1. 파일 레이아웃

프로젝트 루트에 하나의 소스 파일을 다음 이름 중 하나로 배치합니다:

  • Particlefile.ts — 권장됨 (완전한 타입 검사)
  • Particlefile.js — 작동하지만 타입 없음

다른 것은 필요하지 않습니다. npm 종속성은 표준 import 문으로 인라인 선언되며, 빌드 시 해결됩니다. package.json, tsconfig.json, node_modules를 관리할 필요가 없습니다.

2. 기본 내보내기 — 전체 API

파일의 기본 내보내기는 particle 선언입니다. 형태:

export default {
  name: "kebab-case-name",        // 필수, 케밥-케이스
  description: "...",              // 필수, 한 줄
  version: "0.1.0",                // 필수, semver

  capabilities: {
    // 아웃바운드 HTTP 허용 목록. 여기에 없는 호스트는
    // 네트워크 경계에서 차단됩니다. 아웃바운드 요청이 없으면
    // `http`를 완전히 생략하세요.
    http: { allowedHosts: ["api.example.com"] },

    // 호스트 디렉토리 액세스, 기본적으로 꺼져 있습니다. 아래 "파일 시스템" 참조.
    // filesystem: { mounts: { ... }, temp: { ... } },
  },

  // 선택 사항. 이름별로 선언하며, 사용자가 설정 시 메서드를 선택합니다.
  credentials: {
    example: {
      hosts: ["api.example.com"],   // 바인드 치환을 호스트에 적용
      required: true,
      methods: {
        // 아래 "자격 증명 메서드" 참조
      },
    },
  },

  // 필수. 도구 이름 → { description, inputSchema, handler } 매핑.
  tools: {
    do_thing: {
      description: "이 도구가 수행하는 작업 — 한 줄, LLM용으로 작성.",
      inputSchema: {
        type: "object",
        properties: {
          name: { type: "string", description: "..." },
        },
        required: ["name"],
      },
      handler: async ({ name }: { name: string }) => {
        return { result: `hello ${name}` };
      },
    },
  },

  // 선택 사항. 활성/준비 상태를 반환합니다. `particle ping`에 의해 호출됩니다.
  ping: async () => ({ status: "ok" as const, message: "살아 있음" }),
};

도구

  • description: LLM 호출자를 위해 작성 — 간결하고 행동 지향적입니다.
  • inputSchema: JSON Schema Draft 2020-12, 객체 루트. 호스트는 핸들러를 호출하기 전에 인수의 유효성을 검사하므로 핸들러 내에서 입력 형태를 신뢰할 수 있습니다. required를 사용하여 필수 필드를 표시하고, 선택 사항에는 default를 제공하세요.
  • handler: 동기 또는 비동기. JSON 직렬화 가능한 값을 반환하세요. 오류를 호출자에게 표시하려면 throw하세요 — throw된 메시지가 표시됩니다.

Ping

선택 사항이지만 권장됩니다. 반환 형태:

{ status: "ok" | "degraded" | "unhealthy",
  message?: string,
  details?: string }

3. 핸들러에서 사용 가능한 런타임 API

다음은 가져올 수 있는 유일한 비표준 라이브러리 항목입니다. 각각은 런타임에서 제공하는 호스트 모듈입니다 — 타입 전용 npm 패키지로 제공되며, 구현은 실행 시 주입됩니다.

아웃바운드 HTTP

전역 fetch()를 사용하세요. 표준 Web Fetch API이며, 허용 목록과 자격 증명 치환이 적용되도록 호스트를 통해 라우팅됩니다.

const res = await fetch("https://api.example.com/things", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ ... }),
});
if (!res.ok) throw new Error(`업스트림 ${res.status}`);
const data = await res.json();

capabilities.http.allowedHosts에 없는 호스트는 차단됩니다. 일반 fetch는 자격 증명을 적용하지 않습니다 — 이를 위해 credentials.fetcher를 사용하세요.

파일 시스템

기본적으로 꺼져 있습니다 — 핸들러는 자체 번들만 볼 수 있습니다. 호스트 디렉토리 액세스를 얻으려면 capabilities.filesystem을 선언한 다음 일반 node:fs API로 읽고 쓰세요 (런타임이 wasi:filesystem을 통해 라우팅합니다). 가져올 기능 모듈이 없습니다.

capabilities: {
  filesystem: {
    // *사용자*가 실제 경로에 매핑하는 호스트 디렉토리 — `particle mount <particle> <name> <host-path>`로 영구적으로,
    // 또는 `--mount <name>=<host-path>`로 실행 시 매핑합니다.
    mounts: {
      data: {
        description: "보고서가 작성되는 위치",  // 설치 프롬프트에 표시됨
        path: "/mnt/data",                          // 샌드박스 내부의 절대 경로
        access: "readwrite",                        // "readonly" | "readwrite"
        required: true,                             // 매핑될 때까지 실행 거부 (기본값 false)
      },
    },
    // 호스트가 각 실행마다 새로 프로비저닝하고 종료 시 지우는 스크래치 공간.
    // 항상 읽기-쓰기; 사용자가 매핑하지 않습니다.
    temp: {
      work: { description: "스크래치 공간", path: "/tmp/work", maxSize: "10MB" },
    },
  },
},
import { readFile, writeFile } from "node:fs/promises";

const input = await readFile("/mnt/data/in.json", "utf8");
await writeFile("/mnt/data/out.json", JSON.stringify(result));
  • 핸들러는 선언된 path를 직접 엽니다; 마운트 이름은 샌드박스 내부에 나타나지 않습니다.
  • readonly 마운트에 쓰거나 선언된 마운트 외부의 경로를 읽거나 쓰면 오류가 발생합니다.
  • 사용자가 매핑하지 않은 required 마운트는 런타임에 실패하며, 빌드/설치 시에는 실패하지 않습니다.
  • maxSize는 선택적 KB/MB/GB 접미사("10MB")가 있는 바이트 수입니다; 이를 초과하는 쓰기는 실패합니다.

자격 증명

import { credentials } from "@partite-ai/particle-credentials";

// fetcher(name)은 요청이 샌드박스를 떠나기 전에 호스트 구성 위치(헤더, 쿼리 매개변수 등)에 자격 증명을 자동으로 적용하는 fetch 형태 함수를 반환합니다.
const fetcher = await credentials.fetcher("example");
const res = await fetcher("https://api.example.com/me");

// 사용자가 설정 시 구성한 메서드("oauth", "pat" 등)를 확인합니다.
const method = credentials.getConfiguredMethod("example"); // string | null

// `type: "raw"`로 선언된 자격 증명에만 해당합니다.
const value = await credentials.getRaw("some-raw-cred");

credentials.fetcher는 basic, oauth2, apikey 자격 증명에 대해 작동합니다. signing-key의 경우 signing.sign을 사용하세요. raw의 경우 getRaw를 사용하세요.

키/값 저장소

Particle별로 범위가 지정됩니다. 문자열만 가능 — 바이너리가 필요하면 base64로 인코딩하세요. capabilities.kv 아래에 선언하세요 — 선언 없이 모든 호출은 not-declared 오류를 발생시킵니다 (사용자 승인은 필요하지 않으며 선언만 있으면 됩니다):

capabilities: { kv: { enabled: true } },
import { kv } from "@partite-ai/particle-kv";

await kv.set("cursor", "abc123");
const v = await kv.get("cursor");        // string | null
await kv.delete("cursor");
const keys = await kv.list("prefix:");    // string[]

OAuth 갱신

oauth2 자격 증명이 있는 경우에만 해당합니다. fetcher가 만료된 토큰을 자동으로 갱신합니다; 여전히 유효하다고 생각하는 토큰을 업스트림이 거부하는 경우에만 호출하세요.

import { oauth } from "@partite-ai/particle-oauth";
await oauth.refresh("example");

서명

signing-key 자격 증명에만 해당합니다. 키 자료가 JS에 유입되지 않습니다.

import { signing } from "@partite-ai/particle-signing";

const sig = await signing.sign("webhook-key", new TextEncoder().encode(payload));
const ok  = await signing.verify("webhook-key", data, sig);

4. 자격 증명 메서드

각 자격 증명에는 methods 맵이 있습니다. 사용자가 설정 시 하나를 선택합니다. Particle은 일반적으로 동일한 공급자에 대해 여러 메서드를 제공합니다.

oauth2

oauth: {
  type: "oauth2",
  description: "OAuth로 로그인",
  flows: ["authorization-code", "device-code"],   // 이 세 가지 중 하나를 선택
  scopes: ["read", "write"],
  authorizationUrl: "https://provider/oauth/authorize",
  tokenUrl:         "https://provider/oauth/token",
  deviceAuthUrl:    "https://provider/oauth/device/code",  // "device-code"에 필요
},

유효한 흐름 문자열: "authorization-code", "authorization-code-pkce", "device-code".

apikey

pat: {
  type: "apikey",
  description: "개인 액세스 토큰 사용",
  location: { kind: "auth-scheme", scheme: "Bearer" },
},

location.kind는 다음 중 하나입니다:

  • "header" + name: <name>: <key>
  • "auth-scheme" + scheme: Authorization: <scheme> <key> (예: Bearer)
  • "query-param" + name: ?<name>=<key>로 추가됨

location을 완전히 생략하면 가져오는 쪽에서 사용자에게 묻습니다.

basic

basic_auth: { type: "basic", description: "사용자 이름 + 비밀번호" },

Authorization: Basic <base64>로 치환됩니다.

signing-key

webhook: { type: "signing-key", description: "...", algorithm: "hmac-sha256" },

유효: "hmac-sha256", "hmac-sha512". signing.sign을 통해 사용합니다.

raw

api_key_blob: { type: "raw", description: "..." },

credentials.getRaw(name)을 통해 사용합니다. 원시 바이트가 정말로 필요한 경우에만 이 옵션을 선택하세요 — 다른 유형은 값이 코드에 유입되지 않으므로 더 안전합니다.

5. npm 종속성

순수 ESM npm 패키지를 직접 가져올 수 있습니다:

import { z } from "zod";
import * as YAML from "yaml";

빌드가 가져오기를 스캔하고 자동으로 해결합니다 — package.json이 필요 없습니다. 제약 사항:

  • 패키지는 순수 JS/TS여야 합니다 (네이티브 바인딩, Node 전용 API 없음).
  • 브라우저 호환 빌드가 가장 잘 작동합니다.
  • fs, child_process 등은 피하세요 — 런타임에 존재하지 않습니다.

6. 중요 규칙

  1. 모듈 범위 호스트 호출 금지. 파일의 최상위 레벨에서 fetch, kv.get, credentials.fetcher 등을 호출하면 빌드 시 실패합니다 (인트로스펙트 단계에서 트랩 저장소 아래에서 모듈을 실행합니다). 모든 호스트 호출은 handler 또는 ping 내부에서 수행하세요.

    // ✗ 빌드 실패
    const fetcher = await credentials.fetcher("github");
    export default { tools: { ... } };
    
    // ✓ 핸들러 내부
    export default {
      tools: {
        go: { handler: async (args) => {
          const fetcher = await credentials.fetcher("github");
          // ...
        }},
      },
    };
    
  2. 가져오는 모든 호스트를 선언하세요. capabilities.http.allowedHosts에 없는 호스트에 대한 요청은 거부됩니다. 오류는 핸들러에서 throw된 예외로 나타납니다.

  3. credentials.<name>.hosts는 HTTP 허용 목록의 하위 집합이어야 합니다. 빌드는 Particle이 도달할 수 없는 호스트에 바인딩된 자격 증명을 거부합니다.

  4. name은 케밥-케이스여야 합니다. (소문자, 하이픈) 레지스트리 키는 (name, version)입니다.

  5. version은 유효한 semver여야 합니다. 0.1.0, 1.2.3-rc.1 등.

  6. 도구 핸들러는 JSON 직렬화 가능한 값을 반환해야 합니다. undefined를 반환해도 괜찮습니다 (null이 됩니다). 클래스 인스턴스, Date, BigInt 또는 Map을 반환하면 직렬화에 실패합니다.

7. 워크플로우

Particlefile.ts를 작성한 후:

particle build                          # 타입 검사, 번들, 인트로스펙트, 등록
particle ping <name>                    # ping 확인 (정의된 경우)
particle run  <name>                    # 도구 목록
particle run  <name> <tool> --help      # 도구 플래그 표시
particle run  <name> <tool> --foo=bar   # 호출

particle build는 처음에 대화식으로 자격 증명 설정을 안내합니다. 빌드가 실패하면 오류는 단계(import-scan, typecheck, bundle, manifest-extract)와 특정 문제를 명명합니다.

Particle이 파일 시스템 마운트를 선언하는 경우 실행하기 전에 매핑하거나 (또는 일회성으로 particle run에 --mount name=path 전달):

particle mount <name> <mount-name> <host-path>   # 영구 매핑 저장
particle mount <name>                            # 마운트 + 현재 매핑 목록

Particle을 독립 실행형 실행 파일로 노출하려면 (Claude Code 스킬 및 셸 사용에 편리함), particle link <name> ./<name>은 인수를 particle run <name>으로 전달하는 실행기를 작성합니다.

8. 작업 예제

GitHub 저장소 조회 도구를 노출하는 최소 Particle:

import { credentials } from "@partite-ai/particle-credentials";

export default {
  name: "github-lookup",
  description: "GitHub 저장소 메타데이터를 조회합니다.",
  version: "0.1.0",

  capabilities: { http: { allowedHosts: ["api.github.com"] } },

  credentials: {
    github: {
      hosts: ["api.github.com"],
      required: true,
      methods: {
        pat: {
          type: "apikey",
          description: "GitHub 개인 액세스 토큰",
          location: { kind: "auth-scheme", scheme: "Bearer" },
        },
      },
    },
  },

  tools: {
    get_repo: {
      description: "GitHub 저장소의 메타데이터를 가져옵니다.",
      inputSchema: {
        type: "object",
        properties: {
          owner: { type: "string", description: "소유자 로그인." },
          repo:  { type: "string", description: "저장소 이름." },
        },
        required: ["owner", "repo"],
      },
      handler: async ({ owner, repo }: { owner: string; repo: string }) => {
        const fetcher = await credentials.fetcher("github");
        const res = await fetcher(`https://api.github.com/repos/${owner}/${repo}`, {
          headers: { Accept: "application/vnd.github+json" },
        });
        if (!res.ok) throw new Error(`GitHub ${res.status}: ${await res.text()}`);
        const r = (await res.json()) as Record<string, unknown>;
        return {
          full_name: r.full_name,
          stars:     r.stargazers_count,
          url:       r.html_url,
        };
      },
    },
  },
};

9. 빠른 참조

필요가져오기 / 호출
HTTP 요청await fetch(url, init)
인증된 HTTPconst f = await credentials.fetcher(name); await f(url, init)
마운트된 디렉토리 읽기/쓰기node:fs/promises — 선언된 path 사용 (capabilities.filesystem 필요)
Particle별 상태kv.get / set / delete / list
OAuth 갱신 강제oauth.refresh(name)
HMAC 서명 / 확인signing.sign(name, data) / signing.verify(name, data, sig)
설정 시 선택된 메서드 확인credentials.getConfiguredMethod(name)
원시 자격 증명 바이트credentials.getRaw(name)

가져오기는 항상 @partite-ai/particle-<area> 패키지에서 가져옵니다 — 다른 이름을 만들지 마세요.

출처 및 라이선스

[적응됨: ...]

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

관련 스킬