Community程式設計與開發github.com

partite-ai/write-particle-typescript

使用 TypeScript 或 JavaScript 建立、編輯或擴充 Partite Particle,利用 Particlefile.ts 或 Particlefile.js。適用於工具處理器、API 整合、憑證和執行階段功能。

write-particle-typescript 是什麼?

此技能提供建立 Partite Particle 的完整指南,這是一種單一檔案程式,可向 Particle 執行階段公開工具(已命名操作)。它支援 TypeScript 或 JavaScript,使用 `Particlefile.ts`(首選,有完整型別檢查)或 `Particlefile.js`(無型別)。指南涵蓋從檔案結構(一個根目錄來源檔案,無需 `package.json` 或 `tsconfig.json`)到預設匯出的所有內容,其中包含 `name`、`description`、`version`、`capabilities`(如 HTTP 允許清單和檔案系統掛載)、`credentials` 以及必要的 `tools` 映射。每個工具都包含 `description`(供 LLM 使用)、`inputSchema`(JSON Schema Draft 2020-12)和非同步處理器。執行階段 API 包括用於 HTTP 請求的全域 `fetch()`、用於憑證管理的 `@partite-ai/particle-credentials`、用於檔案系統存取的標準 `node:fs`(需宣告)、用於狀態管理的 `@partite-ai/particle-kv`,以及用於 OAuth 重新整理的 `@partite-ai/particle-oauth`。此技能也支援純 ESM npm 相依性,並提供關鍵規則,例如避免模組範圍的執行階段呼叫以及確保工具處理器傳回 JSON 可序列化的值。支援的憑證方法包括 `oauth2`、`apikey`、`basic`、`signing-key` 和 `raw`。提供一個用於 GitHub 查詢的完整範例,以及用於建置、執行和連結 Particle 的 CLI 工作流程命令。

相容平台✓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 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

撰寫 TypeScript/JavaScript Particle

Particle 是一個單一檔案程式,它向 Particle 執行階段公開一個或多個工具(具有 JSON-Schema 輸入的命名操作)。此技能告訴您產生一個可建置和執行所需的一切知識。

1. 檔案佈局

在專案根目錄下的一個原始檔,名稱必須為:

  • Particlefile.ts — 首選(完整的型別檢查)
  • Particlefile.js — 可行,但無型別

不需要其他檔案。npm 相依性使用標準的 import 語句內聯宣告;建置過程會解析它們。無需管理 package.json、tsconfig.json 或 node_modules。

2. 預設匯出 — 完整的 API

檔案的預設匯出是 Particle 宣告。結構如下:

export default {
  name: "kebab-case-name",        // 必填,kebab-case
  description: "...",              // 必填,一行
  version: "0.1.0",                // 必填,semver

  capabilities: {
    // 對外 HTTP 允許清單。未列於此的主機將在網路邊界被拒絕。
    // 如果 Particle 不發送任何對外請求,則完全省略 `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: "alive" }),
};

工具

  • description:為 LLM 呼叫者編寫 — 簡短、以行動為導向。
  • inputSchema:JSON Schema Draft 2020-12,以物件為根。主機在呼叫您的處理器之前會根據它驗證引數,因此您可以信任處理器內部的輸入形狀。使用 required 標記必填欄位;為可選欄位提供 default。
  • handler:同步或非同步。回傳任何 JSON 可序列化的值。拋出例外以向呼叫者顯示錯誤 — 他們會看到拋出的訊息。

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 憑證。金鑰材料永遠不會進入 JavaScript。

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 中缺少的主機的請求將被拒絕。錯誤會在處理器中以拋出的例外形式呈現。

  3. credentials.<name>.hosts 必須是 HTTP 允許清單的子集。建置過程會拒絕綁定到 Particle 無法到達的主機的憑證。

  4. name 必須是 kebab-case(小寫,連字號)。註冊表鍵是 (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 技能和 shell 使用),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)
讀寫已掛載的目錄在宣告的 path 上使用 node:fs/promises(需要 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.

相關技能