carapace-plugin-sdk 1.0.3 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/generate-cli.js +7 -26
- package/dist/generate-cli.js.map +1 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.js +11 -24
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -125,6 +125,8 @@ jobs:
|
|
|
125
125
|
|
|
126
126
|
```yaml
|
|
127
127
|
# .github/workflows/release.yml
|
|
128
|
+
# Bumps the version, commits + tags, and creates a draft GitHub release.
|
|
129
|
+
# Review and edit the draft, then publish it to trigger npm publish.
|
|
128
130
|
on:
|
|
129
131
|
workflow_dispatch:
|
|
130
132
|
inputs:
|
|
@@ -140,6 +142,17 @@ jobs:
|
|
|
140
142
|
with:
|
|
141
143
|
version-bump: ${{ inputs.version-bump }}
|
|
142
144
|
prerelease: ${{ inputs.prerelease }}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```yaml
|
|
148
|
+
# .github/workflows/publish.yml
|
|
149
|
+
# Publishes to npm when the draft GitHub release is promoted to published.
|
|
150
|
+
on:
|
|
151
|
+
release:
|
|
152
|
+
types: [published]
|
|
153
|
+
jobs:
|
|
154
|
+
publish:
|
|
155
|
+
uses: JeffSteinbok/carapace-plugin-sdk/.github/workflows/plugin-publish.yml@main
|
|
143
156
|
secrets:
|
|
144
157
|
npm-token: ${{ secrets.NPM_TOKEN }}
|
|
145
158
|
```
|
package/dist/generate-cli.js
CHANGED
|
@@ -4,31 +4,6 @@
|
|
|
4
4
|
import { writeFileSync, mkdirSync, readFileSync } from "fs";
|
|
5
5
|
import { resolve, relative, dirname, sep } from "path";
|
|
6
6
|
import { pathToFileURL } from "url";
|
|
7
|
-
|
|
8
|
-
// src/index.ts
|
|
9
|
-
import { createRequire } from "module";
|
|
10
|
-
function ensureContracts(entry) {
|
|
11
|
-
if (entry.contracts?.tools?.length) return entry;
|
|
12
|
-
try {
|
|
13
|
-
const toolNames = [];
|
|
14
|
-
const fakeApi = {
|
|
15
|
-
registerTool: (tool) => {
|
|
16
|
-
if (tool && typeof tool === "object" && "name" in tool) {
|
|
17
|
-
toolNames.push(tool.name);
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
pluginConfig: {}
|
|
21
|
-
};
|
|
22
|
-
entry.register(fakeApi);
|
|
23
|
-
if (toolNames.length > 0) {
|
|
24
|
-
entry.contracts = { tools: toolNames };
|
|
25
|
-
}
|
|
26
|
-
} catch {
|
|
27
|
-
}
|
|
28
|
-
return entry;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// src/generate-cli.ts
|
|
32
7
|
function parseGeneratorArgs() {
|
|
33
8
|
const args = process.argv.slice(2);
|
|
34
9
|
let entry = "";
|
|
@@ -124,7 +99,13 @@ async function main() {
|
|
|
124
99
|
console.error(`Error: ${entry} does not export createEntry()`);
|
|
125
100
|
process.exit(1);
|
|
126
101
|
}
|
|
127
|
-
const pluginEntry =
|
|
102
|
+
const pluginEntry = createEntry();
|
|
103
|
+
if (!pluginEntry.contracts?.tools?.length) {
|
|
104
|
+
console.error(
|
|
105
|
+
`Error: plugin "${pluginEntry.id ?? pluginEntry.name}" is missing contracts.tools. Migrate to definePlugin() from carapace-plugin-sdk.`
|
|
106
|
+
);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
128
109
|
const binName = name ?? pluginEntry.id;
|
|
129
110
|
const envPrefix = binName.replace(/-/g, "_").toUpperCase();
|
|
130
111
|
generateCli(entry, outDir, binName, envPrefix);
|
package/dist/generate-cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generate-cli.ts","../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * carapace-generate-cli — Build-time generator for OpenClaw plugins.\n *\n * Reads a plugin's createEntry() metadata and emits:\n * - dist/bin/<plugin-id>.js — standalone CLI entry point\n * - dist/adapter.js — OpenClaw adapter (always identical, no source file needed)\n * - openclaw.plugin.json — plugin manifest (derived from createEntry() + package.json)\n *\n * Usage:\n * carapace-generate-cli --entry ./dist/plugin.js --out ./dist/bin\n * carapace-generate-cli --entry ./dist/plugin.js --out ./dist/bin --name my-tool\n */\n\nimport { writeFileSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { resolve, relative, dirname, sep } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { ensureContracts } from \"./index.js\";\n\n// ---------------------------------------------------------------------------\n// Arg parsing\n// ---------------------------------------------------------------------------\n\nfunction parseGeneratorArgs(): { entry: string; outDir: string; name?: string } {\n const args = process.argv.slice(2);\n let entry = \"\";\n let outDir = \"\";\n let name: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === \"--entry\" && args[i + 1]) entry = args[++i];\n else if (args[i] === \"--out\" && args[i + 1]) outDir = args[++i];\n else if (args[i] === \"--name\" && args[i + 1]) name = args[++i];\n }\n\n if (!entry || !outDir) {\n console.error(\"Usage: carapace-generate-cli --entry <path-to-dist/index.js> --out <output-dir> [--name <bin-name>]\");\n process.exit(1);\n }\n\n return { entry: resolve(entry), outDir: resolve(outDir), name };\n}\n\n// ---------------------------------------------------------------------------\n// Path helper\n// ---------------------------------------------------------------------------\n\nfunction toPosixRelative(from: string, to: string): string {\n let rel = relative(from, to);\n if (sep === \"\\\\\") rel = rel.replaceAll(\"\\\\\", \"/\");\n if (!rel.startsWith(\".\")) rel = `./${rel}`;\n return rel;\n}\n\n// ---------------------------------------------------------------------------\n// Generators\n// ---------------------------------------------------------------------------\n\nfunction generateCli(entry: string, outDir: string, binName: string, envPrefix: string): void {\n const relativeEntry = toPosixRelative(outDir, entry);\n\n const content = `#!/usr/bin/env node\n// Auto-generated by carapace-plugin-sdk — do not edit\nimport { run } from \"carapace-plugin-sdk/cli\";\nimport { createEntry } from \"${relativeEntry}\";\n\nrun(createEntry(), { binName: \"${binName}\", envPrefix: \"${envPrefix}\" });\n`;\n\n mkdirSync(outDir, { recursive: true });\n const outPath = resolve(outDir, `${binName}.js`);\n writeFileSync(outPath, content, { mode: 0o755 });\n console.log(`Generated CLI: ${outPath}`);\n}\n\nfunction generateAdapter(entry: string): void {\n const adapterDir = dirname(entry);\n const relativeEntry = toPosixRelative(adapterDir, entry);\n\n const content = `// Auto-generated by carapace-plugin-sdk — do not edit\nimport { createAdapter } from \"carapace-plugin-sdk\";\nimport { createEntry } from \"${relativeEntry}\";\n\nexport default createAdapter(createEntry(), import.meta.url);\n`;\n\n const outPath = resolve(adapterDir, \"adapter.js\");\n writeFileSync(outPath, content);\n console.log(`Generated adapter: ${outPath}`);\n}\n\n/**\n * Generate openclaw.plugin.json from createEntry() metadata + package.json.\n *\n * createEntry() is the single source of truth for:\n * id, name, description, configSchema, contracts, activation\n *\n * version comes from package.json (it's the npm package version).\n * entry is always ./dist/adapter.js.\n *\n * The manifest is written to the current working directory (the plugin root),\n * not into dist/, because OpenClaw reads it from the installed package root.\n */\nfunction generateManifest(pluginEntry: {\n id: string;\n name: string;\n description?: string;\n configSchema?: unknown;\n contracts?: unknown;\n activation?: unknown;\n}): void {\n // Read version from package.json in the plugin's root directory.\n // process.cwd() is the plugin root when invoked via `npm run build`.\n let version = \"0.0.0\";\n let source: unknown = undefined;\n try {\n const pkg = JSON.parse(readFileSync(resolve(process.cwd(), \"package.json\"), \"utf8\"));\n version = pkg.version ?? version;\n if (pkg.repository) {\n // Derive source from package.json repository field\n const repo = typeof pkg.repository === \"string\" ? pkg.repository : pkg.repository.url ?? \"\";\n const match = repo.match(/(?:github\\.com[/:]|^github:)([^/]+\\/[^/.]+)/);\n if (match) {\n source = { canonicalRepo: match[1] };\n }\n }\n } catch {\n console.warn(\"Warning: could not read package.json for version — defaulting to 0.0.0\");\n }\n\n const manifest: Record<string, unknown> = {\n id: pluginEntry.id,\n name: pluginEntry.name,\n description: pluginEntry.description ?? \"\",\n version,\n // The adapter is always at this path — generated by generateAdapter() above.\n entry: \"./dist/adapter.js\",\n };\n\n manifest.configSchema = pluginEntry.configSchema ?? { type: \"object\", properties: {} };\n\n if (pluginEntry.contracts) {\n manifest.contracts = pluginEntry.contracts;\n }\n\n // activation defaults to onStartup: true if not specified.\n manifest.activation = pluginEntry.activation ?? { onStartup: true };\n\n if (source) {\n manifest.source = source;\n }\n\n // openclaw is a host-specific extension block. Default to empty.\n manifest.openclaw = {};\n\n const outPath = resolve(process.cwd(), \"openclaw.plugin.json\");\n writeFileSync(outPath, JSON.stringify(manifest, null, 2) + \"\\n\");\n console.log(`Generated manifest: ${outPath}`);\n}\n\n// ---------------------------------------------------------------------------\n// Main\n// ---------------------------------------------------------------------------\n\nasync function main() {\n const { entry, outDir, name } = parseGeneratorArgs();\n\n const entryUrl = pathToFileURL(entry).href;\n const mod = await import(entryUrl);\n\n const createEntry = mod.createEntry ?? mod.default?.createEntry;\n if (typeof createEntry !== \"function\") {\n console.error(`Error: ${entry} does not export createEntry()`);\n process.exit(1);\n }\n\n const pluginEntry = ensureContracts(createEntry());\n const binName = name ?? pluginEntry.id;\n const envPrefix = binName.replace(/-/g, \"_\").toUpperCase();\n\n generateCli(entry, outDir, binName, envPrefix);\n generateAdapter(entry);\n generateManifest(pluginEntry);\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exit(1);\n});\n","/**\n * carapace-plugin-sdk — Core types and helpers for OpenClaw plugins.\n *\n * This is the main entry point. Import `definePlugin` to author a plugin\n * with full TypeScript inference — typed config and typed tool parameters\n * with no boilerplate.\n *\n * Quick-start:\n *\n * src/plugin.ts — the only file you write; export `createEntry` from `definePlugin`\n * dist/adapter.js — auto-generated at build time; do not write by hand\n *\n * @module carapace-plugin-sdk\n */\n\nimport { createRequire } from \"node:module\";\nimport { type TObject, type Static } from \"@sinclair/typebox\";\n\n// ---------------------------------------------------------------------------\n// definePlugin — the primary authoring API\n// ---------------------------------------------------------------------------\n\n/**\n * Internal (erased) tool definition stored at runtime.\n * The typed version lives only in TypeScript's type system via ToolFactory<TConfig>.\n */\ninterface ToolDef {\n name: string;\n label?: string;\n description: string;\n parameters: unknown;\n execute(params: Record<string, unknown>, config: unknown): Promise<unknown>;\n}\n\n/**\n * Typed tool factory injected into the `tools` callback of `definePlugin`.\n *\n * TConfig is fixed by the enclosing `definePlugin` call, so every tool in the\n * array receives the same config type. TSchema is inferred per tool from the\n * `parameters` field, giving typed `params` in `execute`.\n *\n * You never construct this directly — it is passed to you by `definePlugin`.\n */\ntype ToolFactory<TConfig> = <TSchema extends TObject>(def: {\n /** Machine-readable name, used as the CLI subcommand. snake_case recommended. */\n name: string;\n /** Human-readable label shown in OpenClaw's UI. Defaults to `name`. */\n label?: string;\n /** One-sentence description shown in --help and OpenClaw's tool inspector. */\n description: string;\n /**\n * TypeBox schema for the tool's parameters.\n * The type is inferred as `Static<TSchema>` in `execute`'s first argument.\n */\n parameters: TSchema;\n /**\n * The tool's implementation.\n *\n * @param params - Typed parameters derived from `parameters` schema. No casts needed.\n * @param config - Typed config derived from `definePlugin`'s `configSchema`. No casts needed.\n * @returns Any JSON-serialisable value. The SDK wraps it in the OpenClaw result format.\n */\n execute(params: Static<TSchema>, config: TConfig): Promise<unknown>;\n}) => ToolDef;\n\n/**\n * Define an OpenClaw plugin with full TypeScript inference.\n *\n * Returns a `createEntry` function — export it from your `src/index.ts`.\n * The SDK handles all registration, result wrapping, and config plumbing.\n *\n * Config type is inferred from `configSchema` and flows into every tool's\n * `execute(params, config)` without any manual type annotations.\n *\n * @example\n * // src/index.ts — the entire plugin\n * import { definePlugin } from \"carapace-plugin-sdk\";\n * import { Type } from \"@sinclair/typebox\";\n *\n * export const createEntry = definePlugin({\n * id: \"my-plugin\",\n * name: \"My Plugin\",\n * configSchema: Type.Object({\n * apiKey: Type.Optional(Type.String({ description: \"API key.\" })),\n * }),\n * tools: (tool) => [\n * tool({\n * name: \"do_thing\",\n * description: \"Does the thing.\",\n * parameters: Type.Object({\n * input: Type.String({ description: \"Input value.\" }),\n * }),\n * execute: async ({ input }, config) => {\n * // input: string ✓ config.apiKey: string | undefined ✓\n * return { result: input };\n * },\n * }),\n * ],\n * });\n */\nexport function definePlugin<TConfigSchema extends TObject = TObject>(def: {\n /** Unique plugin id. Lowercase alphanumeric with hyphens. Used as the CLI binary name. */\n id: string;\n /** Human-readable display name. */\n name: string;\n /** One-sentence description of what the plugin does. */\n description?: string;\n /** When to load the plugin. Defaults to `{ onStartup: true }`. */\n activation?: { onStartup?: boolean };\n /**\n * TypeBox schema for the plugin's config block.\n *\n * Used for three things simultaneously:\n * 1. Runtime JSON Schema for the OpenClaw manifest (validated before register())\n * 2. TypeScript type inference for `config` in every tool's `execute`\n * 3. Environment variable mapping for the standalone CLI\n */\n configSchema?: TConfigSchema;\n /**\n * Declare your tools here. Receives a typed `tool()` factory as its argument.\n *\n * Using a callback (rather than a plain array) is what allows TypeScript to\n * thread the config type through to each tool's `execute` function.\n */\n tools: (tool: ToolFactory<Static<TConfigSchema>>) => ToolDef[];\n}): () => PluginEntry {\n return () => {\n // The factory is identity at runtime — all type magic is compile-time only.\n const toolFactory = ((toolDef: unknown) => toolDef) as ToolFactory<Static<TConfigSchema>>;\n const toolDefs = def.tools(toolFactory);\n\n return {\n id: def.id,\n name: def.name,\n description: def.description,\n activation: def.activation ?? { onStartup: true },\n // Derive contracts from the declared tools so the manifest is always accurate.\n contracts: { tools: toolDefs.map((t) => t.name) },\n // TypeBox TObject is valid JSON Schema — pass through for the manifest generator.\n configSchema: def.configSchema as unknown as PluginEntry[\"configSchema\"],\n register(api: PluginApi) {\n // OpenClaw validates pluginConfig against configSchema before calling register(),\n // so this cast is safe. Fall back to empty object if config is not yet set.\n const config = (api.pluginConfig ?? {}) as Static<TConfigSchema>;\n\n for (const toolDef of toolDefs) {\n api.registerTool({\n name: toolDef.name,\n label: toolDef.label ?? toolDef.name,\n description: toolDef.description,\n parameters: toolDef.parameters,\n // Wrap the result automatically — execute() returns plain values, not formatResult().\n execute: async (_toolCallId: string, params: Record<string, unknown>) =>\n formatResult(await toolDef.execute(params, config)),\n });\n }\n },\n };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Low-level types — the plugin contract\n//\n// These are used by the generated adapter and by advanced plugins that need\n// more control than definePlugin provides (e.g. dynamic tool registration).\n// ---------------------------------------------------------------------------\n\n/**\n * The API object passed to your plugin's `register()` function.\n *\n * When using `definePlugin`, you never see this directly — the SDK handles it.\n * It is exported for advanced use cases and for the generated adapter.\n */\nexport type PluginApi = {\n /** Register a tool with OpenClaw. Call once per tool inside register(). */\n registerTool: (tool: unknown) => void;\n /**\n * The user's config values for this plugin, keyed by field name.\n * Validated against configSchema by OpenClaw before register() is called.\n * May be undefined if the user has not configured the plugin.\n */\n pluginConfig?: Record<string, unknown>;\n};\n\n/**\n * The object returned by `createEntry()` — the plugin's public contract.\n *\n * When using `definePlugin`, this is constructed automatically.\n * Exported for advanced plugins that build it manually.\n */\nexport interface PluginEntry {\n /** Unique plugin id. Used as the CLI binary name and OpenClaw config key. */\n id: string;\n /** Human-readable display name. */\n name: string;\n /** One-sentence description. */\n description?: string;\n /** Tool names this plugin promises to register. Derived automatically by `definePlugin`. */\n contracts?: { tools: string[] };\n /** When to load the plugin. Defaults to `{ onStartup: true }`. */\n activation?: { onStartup?: boolean };\n /**\n * JSON Schema for the plugin's config block.\n * Pass a TypeBox `Type.Object(...)` — it is valid JSON Schema and gives you type inference.\n */\n configSchema?: unknown;\n /** Called once by OpenClaw at startup. Use `definePlugin` instead of implementing this directly. */\n register(api: PluginApi): void;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap a value in the standard OpenClaw tool result format.\n *\n * When using `definePlugin`, you do NOT call this yourself — the SDK calls it\n * automatically after your `execute` function returns.\n *\n * For advanced plugins that implement `register()` directly, wrap your\n * execute return values with this function.\n *\n * @param data - Anything JSON-serialisable, or a plain string.\n * @returns `{ content: [{ type: \"text\", text: \"<json>\" }], details: {} }`\n */\nexport function formatResult(data: unknown) {\n let text: string;\n if (typeof data === \"string\") {\n text = data;\n } else {\n try {\n text = JSON.stringify(data) ?? String(data);\n } catch {\n text = String(data);\n }\n }\n return {\n content: [{ type: \"text\" as const, text }],\n details: {},\n };\n}\n\n// ---------------------------------------------------------------------------\n// Adapter factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create the plugin's OpenClaw adapter export.\n *\n * **You never call this yourself.** It is called by the generated `dist/adapter.js`.\n *\n * Attempts to load the optional `openclaw` peer dependency and wrap the plugin\n * entry with the host's `definePluginEntry()`. Falls back to the raw entry if\n * `openclaw` is not installed (standalone CLI mode).\n *\n * @param entry - The object returned by `createEntry()`.\n * @param callerUrl - Pass `import.meta.url` from the generated adapter file.\n */\nexport function createAdapter(entry: PluginEntry, callerUrl: string): unknown {\n // Ensure contracts.tools is populated even for plugins using raw register().\n // Dry-run register() with a fake API to collect tool names.\n const enriched = ensureContracts(entry);\n\n const req = createRequire(callerUrl);\n\n try {\n const sdk = req(\"openclaw/plugin-sdk/plugin-entry\") as {\n definePluginEntry?: (e: unknown) => unknown;\n };\n\n if (typeof sdk.definePluginEntry !== \"function\") {\n throw new Error(\n \"OpenClaw SDK loaded but did not export `definePluginEntry`. Upgrade the `openclaw` package.\",\n );\n }\n\n return sdk.definePluginEntry(enriched);\n } catch (err: unknown) {\n if (isModuleNotFoundError(err)) return enriched;\n throw err;\n }\n}\n\n/**\n * If the entry lacks contracts.tools, dry-run register() to discover tool names.\n */\nexport function ensureContracts(entry: PluginEntry): PluginEntry {\n if (entry.contracts?.tools?.length) return entry;\n\n try {\n const toolNames: string[] = [];\n const fakeApi: PluginApi = {\n registerTool: (tool: unknown) => {\n if (tool && typeof tool === \"object\" && \"name\" in tool) {\n toolNames.push((tool as { name: string }).name);\n }\n },\n pluginConfig: {},\n };\n entry.register(fakeApi);\n if (toolNames.length > 0) {\n entry.contracts = { tools: toolNames };\n }\n } catch {\n // register() may have side effects that fail without a real API — ignore.\n }\n\n return entry;\n}\n\nfunction isModuleNotFoundError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n return (\n \"code\" in err &&\n ((err as { code: string }).code === \"MODULE_NOT_FOUND\" ||\n (err as { code: string }).code === \"ERR_MODULE_NOT_FOUND\")\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/generate-cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * carapace-generate-cli — Build-time generator for OpenClaw plugins.\n *\n * Reads a plugin's createEntry() metadata and emits:\n * - dist/bin/<plugin-id>.js — standalone CLI entry point\n * - dist/adapter.js — OpenClaw adapter (always identical, no source file needed)\n * - openclaw.plugin.json — plugin manifest (derived from createEntry() + package.json)\n *\n * Usage:\n * carapace-generate-cli --entry ./dist/plugin.js --out ./dist/bin\n * carapace-generate-cli --entry ./dist/plugin.js --out ./dist/bin --name my-tool\n */\n\nimport { writeFileSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { resolve, relative, dirname, sep } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { PluginEntry } from \"./index.js\";\n\n// ---------------------------------------------------------------------------\n// Arg parsing\n// ---------------------------------------------------------------------------\n\nfunction parseGeneratorArgs(): { entry: string; outDir: string; name?: string } {\n const args = process.argv.slice(2);\n let entry = \"\";\n let outDir = \"\";\n let name: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === \"--entry\" && args[i + 1]) entry = args[++i];\n else if (args[i] === \"--out\" && args[i + 1]) outDir = args[++i];\n else if (args[i] === \"--name\" && args[i + 1]) name = args[++i];\n }\n\n if (!entry || !outDir) {\n console.error(\"Usage: carapace-generate-cli --entry <path-to-dist/index.js> --out <output-dir> [--name <bin-name>]\");\n process.exit(1);\n }\n\n return { entry: resolve(entry), outDir: resolve(outDir), name };\n}\n\n// ---------------------------------------------------------------------------\n// Path helper\n// ---------------------------------------------------------------------------\n\nfunction toPosixRelative(from: string, to: string): string {\n let rel = relative(from, to);\n if (sep === \"\\\\\") rel = rel.replaceAll(\"\\\\\", \"/\");\n if (!rel.startsWith(\".\")) rel = `./${rel}`;\n return rel;\n}\n\n// ---------------------------------------------------------------------------\n// Generators\n// ---------------------------------------------------------------------------\n\nfunction generateCli(entry: string, outDir: string, binName: string, envPrefix: string): void {\n const relativeEntry = toPosixRelative(outDir, entry);\n\n const content = `#!/usr/bin/env node\n// Auto-generated by carapace-plugin-sdk — do not edit\nimport { run } from \"carapace-plugin-sdk/cli\";\nimport { createEntry } from \"${relativeEntry}\";\n\nrun(createEntry(), { binName: \"${binName}\", envPrefix: \"${envPrefix}\" });\n`;\n\n mkdirSync(outDir, { recursive: true });\n const outPath = resolve(outDir, `${binName}.js`);\n writeFileSync(outPath, content, { mode: 0o755 });\n console.log(`Generated CLI: ${outPath}`);\n}\n\nfunction generateAdapter(entry: string): void {\n const adapterDir = dirname(entry);\n const relativeEntry = toPosixRelative(adapterDir, entry);\n\n const content = `// Auto-generated by carapace-plugin-sdk — do not edit\nimport { createAdapter } from \"carapace-plugin-sdk\";\nimport { createEntry } from \"${relativeEntry}\";\n\nexport default createAdapter(createEntry(), import.meta.url);\n`;\n\n const outPath = resolve(adapterDir, \"adapter.js\");\n writeFileSync(outPath, content);\n console.log(`Generated adapter: ${outPath}`);\n}\n\n/**\n * Generate openclaw.plugin.json from createEntry() metadata + package.json.\n *\n * createEntry() is the single source of truth for:\n * id, name, description, configSchema, contracts, activation\n *\n * version comes from package.json (it's the npm package version).\n * entry is always ./dist/adapter.js.\n *\n * The manifest is written to the current working directory (the plugin root),\n * not into dist/, because OpenClaw reads it from the installed package root.\n */\nfunction generateManifest(pluginEntry: {\n id: string;\n name: string;\n description?: string;\n configSchema?: unknown;\n contracts?: unknown;\n activation?: unknown;\n}): void {\n // Read version from package.json in the plugin's root directory.\n // process.cwd() is the plugin root when invoked via `npm run build`.\n let version = \"0.0.0\";\n let source: unknown = undefined;\n try {\n const pkg = JSON.parse(readFileSync(resolve(process.cwd(), \"package.json\"), \"utf8\"));\n version = pkg.version ?? version;\n if (pkg.repository) {\n // Derive source from package.json repository field\n const repo = typeof pkg.repository === \"string\" ? pkg.repository : pkg.repository.url ?? \"\";\n const match = repo.match(/(?:github\\.com[/:]|^github:)([^/]+\\/[^/.]+)/);\n if (match) {\n source = { canonicalRepo: match[1] };\n }\n }\n } catch {\n console.warn(\"Warning: could not read package.json for version — defaulting to 0.0.0\");\n }\n\n const manifest: Record<string, unknown> = {\n id: pluginEntry.id,\n name: pluginEntry.name,\n description: pluginEntry.description ?? \"\",\n version,\n // The adapter is always at this path — generated by generateAdapter() above.\n entry: \"./dist/adapter.js\",\n };\n\n manifest.configSchema = pluginEntry.configSchema ?? { type: \"object\", properties: {} };\n\n if (pluginEntry.contracts) {\n manifest.contracts = pluginEntry.contracts;\n }\n\n // activation defaults to onStartup: true if not specified.\n manifest.activation = pluginEntry.activation ?? { onStartup: true };\n\n if (source) {\n manifest.source = source;\n }\n\n // openclaw is a host-specific extension block. Default to empty.\n manifest.openclaw = {};\n\n const outPath = resolve(process.cwd(), \"openclaw.plugin.json\");\n writeFileSync(outPath, JSON.stringify(manifest, null, 2) + \"\\n\");\n console.log(`Generated manifest: ${outPath}`);\n}\n\n// ---------------------------------------------------------------------------\n// Main\n// ---------------------------------------------------------------------------\n\nasync function main() {\n const { entry, outDir, name } = parseGeneratorArgs();\n\n const entryUrl = pathToFileURL(entry).href;\n const mod = await import(entryUrl);\n\n const createEntry = mod.createEntry ?? mod.default?.createEntry;\n if (typeof createEntry !== \"function\") {\n console.error(`Error: ${entry} does not export createEntry()`);\n process.exit(1);\n }\n\n const pluginEntry = createEntry() as PluginEntry;\n if (!pluginEntry.contracts?.tools?.length) {\n console.error(\n `Error: plugin \"${pluginEntry.id ?? pluginEntry.name}\" is missing contracts.tools. ` +\n `Migrate to definePlugin() from carapace-plugin-sdk.`,\n );\n process.exit(1);\n }\n const binName = name ?? pluginEntry.id;\n const envPrefix = binName.replace(/-/g, \"_\").toUpperCase();\n\n generateCli(entry, outDir, binName, envPrefix);\n generateAdapter(entry);\n generateManifest(pluginEntry);\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -196,9 +196,5 @@ declare function formatResult(data: unknown): {
|
|
|
196
196
|
* @param callerUrl - Pass `import.meta.url` from the generated adapter file.
|
|
197
197
|
*/
|
|
198
198
|
declare function createAdapter(entry: PluginEntry, callerUrl: string): unknown;
|
|
199
|
-
/**
|
|
200
|
-
* If the entry lacks contracts.tools, dry-run register() to discover tool names.
|
|
201
|
-
*/
|
|
202
|
-
declare function ensureContracts(entry: PluginEntry): PluginEntry;
|
|
203
199
|
|
|
204
|
-
export { type PluginApi, type PluginEntry, createAdapter, definePlugin,
|
|
200
|
+
export { type PluginApi, type PluginEntry, createAdapter, definePlugin, formatResult };
|
package/dist/index.js
CHANGED
|
@@ -46,7 +46,11 @@ function formatResult(data) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
function createAdapter(entry, callerUrl) {
|
|
49
|
-
|
|
49
|
+
if (!entry.contracts?.tools?.length) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Plugin "${entry.id ?? entry.name}" is missing contracts.tools. Migrate to definePlugin() from carapace-plugin-sdk \u2014 see https://github.com/JeffSteinbok/carapace-plugin-sdk`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
50
54
|
const req = createRequire(callerUrl);
|
|
51
55
|
try {
|
|
52
56
|
const sdk = req("openclaw/plugin-sdk/plugin-entry");
|
|
@@ -55,32 +59,16 @@ function createAdapter(entry, callerUrl) {
|
|
|
55
59
|
"OpenClaw SDK loaded but did not export `definePluginEntry`. Upgrade the `openclaw` package."
|
|
56
60
|
);
|
|
57
61
|
}
|
|
58
|
-
|
|
62
|
+
const defined = sdk.definePluginEntry(entry);
|
|
63
|
+
if (entry.contracts && !defined.contracts) {
|
|
64
|
+
defined.contracts = entry.contracts;
|
|
65
|
+
}
|
|
66
|
+
return defined;
|
|
59
67
|
} catch (err) {
|
|
60
|
-
if (isModuleNotFoundError(err)) return
|
|
68
|
+
if (isModuleNotFoundError(err)) return entry;
|
|
61
69
|
throw err;
|
|
62
70
|
}
|
|
63
71
|
}
|
|
64
|
-
function ensureContracts(entry) {
|
|
65
|
-
if (entry.contracts?.tools?.length) return entry;
|
|
66
|
-
try {
|
|
67
|
-
const toolNames = [];
|
|
68
|
-
const fakeApi = {
|
|
69
|
-
registerTool: (tool) => {
|
|
70
|
-
if (tool && typeof tool === "object" && "name" in tool) {
|
|
71
|
-
toolNames.push(tool.name);
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
pluginConfig: {}
|
|
75
|
-
};
|
|
76
|
-
entry.register(fakeApi);
|
|
77
|
-
if (toolNames.length > 0) {
|
|
78
|
-
entry.contracts = { tools: toolNames };
|
|
79
|
-
}
|
|
80
|
-
} catch {
|
|
81
|
-
}
|
|
82
|
-
return entry;
|
|
83
|
-
}
|
|
84
72
|
function isModuleNotFoundError(err) {
|
|
85
73
|
if (!(err instanceof Error)) return false;
|
|
86
74
|
return "code" in err && (err.code === "MODULE_NOT_FOUND" || err.code === "ERR_MODULE_NOT_FOUND");
|
|
@@ -88,7 +76,6 @@ function isModuleNotFoundError(err) {
|
|
|
88
76
|
export {
|
|
89
77
|
createAdapter,
|
|
90
78
|
definePlugin,
|
|
91
|
-
ensureContracts,
|
|
92
79
|
formatResult
|
|
93
80
|
};
|
|
94
81
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * carapace-plugin-sdk — Core types and helpers for OpenClaw plugins.\n *\n * This is the main entry point. Import `definePlugin` to author a plugin\n * with full TypeScript inference — typed config and typed tool parameters\n * with no boilerplate.\n *\n * Quick-start:\n *\n * src/plugin.ts — the only file you write; export `createEntry` from `definePlugin`\n * dist/adapter.js — auto-generated at build time; do not write by hand\n *\n * @module carapace-plugin-sdk\n */\n\nimport { createRequire } from \"node:module\";\nimport { type TObject, type Static } from \"@sinclair/typebox\";\n\n// ---------------------------------------------------------------------------\n// definePlugin — the primary authoring API\n// ---------------------------------------------------------------------------\n\n/**\n * Internal (erased) tool definition stored at runtime.\n * The typed version lives only in TypeScript's type system via ToolFactory<TConfig>.\n */\ninterface ToolDef {\n name: string;\n label?: string;\n description: string;\n parameters: unknown;\n execute(params: Record<string, unknown>, config: unknown): Promise<unknown>;\n}\n\n/**\n * Typed tool factory injected into the `tools` callback of `definePlugin`.\n *\n * TConfig is fixed by the enclosing `definePlugin` call, so every tool in the\n * array receives the same config type. TSchema is inferred per tool from the\n * `parameters` field, giving typed `params` in `execute`.\n *\n * You never construct this directly — it is passed to you by `definePlugin`.\n */\ntype ToolFactory<TConfig> = <TSchema extends TObject>(def: {\n /** Machine-readable name, used as the CLI subcommand. snake_case recommended. */\n name: string;\n /** Human-readable label shown in OpenClaw's UI. Defaults to `name`. */\n label?: string;\n /** One-sentence description shown in --help and OpenClaw's tool inspector. */\n description: string;\n /**\n * TypeBox schema for the tool's parameters.\n * The type is inferred as `Static<TSchema>` in `execute`'s first argument.\n */\n parameters: TSchema;\n /**\n * The tool's implementation.\n *\n * @param params - Typed parameters derived from `parameters` schema. No casts needed.\n * @param config - Typed config derived from `definePlugin`'s `configSchema`. No casts needed.\n * @returns Any JSON-serialisable value. The SDK wraps it in the OpenClaw result format.\n */\n execute(params: Static<TSchema>, config: TConfig): Promise<unknown>;\n}) => ToolDef;\n\n/**\n * Define an OpenClaw plugin with full TypeScript inference.\n *\n * Returns a `createEntry` function — export it from your `src/index.ts`.\n * The SDK handles all registration, result wrapping, and config plumbing.\n *\n * Config type is inferred from `configSchema` and flows into every tool's\n * `execute(params, config)` without any manual type annotations.\n *\n * @example\n * // src/index.ts — the entire plugin\n * import { definePlugin } from \"carapace-plugin-sdk\";\n * import { Type } from \"@sinclair/typebox\";\n *\n * export const createEntry = definePlugin({\n * id: \"my-plugin\",\n * name: \"My Plugin\",\n * configSchema: Type.Object({\n * apiKey: Type.Optional(Type.String({ description: \"API key.\" })),\n * }),\n * tools: (tool) => [\n * tool({\n * name: \"do_thing\",\n * description: \"Does the thing.\",\n * parameters: Type.Object({\n * input: Type.String({ description: \"Input value.\" }),\n * }),\n * execute: async ({ input }, config) => {\n * // input: string ✓ config.apiKey: string | undefined ✓\n * return { result: input };\n * },\n * }),\n * ],\n * });\n */\nexport function definePlugin<TConfigSchema extends TObject = TObject>(def: {\n /** Unique plugin id. Lowercase alphanumeric with hyphens. Used as the CLI binary name. */\n id: string;\n /** Human-readable display name. */\n name: string;\n /** One-sentence description of what the plugin does. */\n description?: string;\n /** When to load the plugin. Defaults to `{ onStartup: true }`. */\n activation?: { onStartup?: boolean };\n /**\n * TypeBox schema for the plugin's config block.\n *\n * Used for three things simultaneously:\n * 1. Runtime JSON Schema for the OpenClaw manifest (validated before register())\n * 2. TypeScript type inference for `config` in every tool's `execute`\n * 3. Environment variable mapping for the standalone CLI\n */\n configSchema?: TConfigSchema;\n /**\n * Declare your tools here. Receives a typed `tool()` factory as its argument.\n *\n * Using a callback (rather than a plain array) is what allows TypeScript to\n * thread the config type through to each tool's `execute` function.\n */\n tools: (tool: ToolFactory<Static<TConfigSchema>>) => ToolDef[];\n}): () => PluginEntry {\n return () => {\n // The factory is identity at runtime — all type magic is compile-time only.\n const toolFactory = ((toolDef: unknown) => toolDef) as ToolFactory<Static<TConfigSchema>>;\n const toolDefs = def.tools(toolFactory);\n\n return {\n id: def.id,\n name: def.name,\n description: def.description,\n activation: def.activation ?? { onStartup: true },\n // Derive contracts from the declared tools so the manifest is always accurate.\n contracts: { tools: toolDefs.map((t) => t.name) },\n // TypeBox TObject is valid JSON Schema — pass through for the manifest generator.\n configSchema: def.configSchema as unknown as PluginEntry[\"configSchema\"],\n register(api: PluginApi) {\n // OpenClaw validates pluginConfig against configSchema before calling register(),\n // so this cast is safe. Fall back to empty object if config is not yet set.\n const config = (api.pluginConfig ?? {}) as Static<TConfigSchema>;\n\n for (const toolDef of toolDefs) {\n api.registerTool({\n name: toolDef.name,\n label: toolDef.label ?? toolDef.name,\n description: toolDef.description,\n parameters: toolDef.parameters,\n // Wrap the result automatically — execute() returns plain values, not formatResult().\n execute: async (_toolCallId: string, params: Record<string, unknown>) =>\n formatResult(await toolDef.execute(params, config)),\n });\n }\n },\n };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Low-level types — the plugin contract\n//\n// These are used by the generated adapter and by advanced plugins that need\n// more control than definePlugin provides (e.g. dynamic tool registration).\n// ---------------------------------------------------------------------------\n\n/**\n * The API object passed to your plugin's `register()` function.\n *\n * When using `definePlugin`, you never see this directly — the SDK handles it.\n * It is exported for advanced use cases and for the generated adapter.\n */\nexport type PluginApi = {\n /** Register a tool with OpenClaw. Call once per tool inside register(). */\n registerTool: (tool: unknown) => void;\n /**\n * The user's config values for this plugin, keyed by field name.\n * Validated against configSchema by OpenClaw before register() is called.\n * May be undefined if the user has not configured the plugin.\n */\n pluginConfig?: Record<string, unknown>;\n};\n\n/**\n * The object returned by `createEntry()` — the plugin's public contract.\n *\n * When using `definePlugin`, this is constructed automatically.\n * Exported for advanced plugins that build it manually.\n */\nexport interface PluginEntry {\n /** Unique plugin id. Used as the CLI binary name and OpenClaw config key. */\n id: string;\n /** Human-readable display name. */\n name: string;\n /** One-sentence description. */\n description?: string;\n /** Tool names this plugin promises to register. Derived automatically by `definePlugin`. */\n contracts?: { tools: string[] };\n /** When to load the plugin. Defaults to `{ onStartup: true }`. */\n activation?: { onStartup?: boolean };\n /**\n * JSON Schema for the plugin's config block.\n * Pass a TypeBox `Type.Object(...)` — it is valid JSON Schema and gives you type inference.\n */\n configSchema?: unknown;\n /** Called once by OpenClaw at startup. Use `definePlugin` instead of implementing this directly. */\n register(api: PluginApi): void;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap a value in the standard OpenClaw tool result format.\n *\n * When using `definePlugin`, you do NOT call this yourself — the SDK calls it\n * automatically after your `execute` function returns.\n *\n * For advanced plugins that implement `register()` directly, wrap your\n * execute return values with this function.\n *\n * @param data - Anything JSON-serialisable, or a plain string.\n * @returns `{ content: [{ type: \"text\", text: \"<json>\" }], details: {} }`\n */\nexport function formatResult(data: unknown) {\n let text: string;\n if (typeof data === \"string\") {\n text = data;\n } else {\n try {\n text = JSON.stringify(data) ?? String(data);\n } catch {\n text = String(data);\n }\n }\n return {\n content: [{ type: \"text\" as const, text }],\n details: {},\n };\n}\n\n// ---------------------------------------------------------------------------\n// Adapter factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create the plugin's OpenClaw adapter export.\n *\n * **You never call this yourself.** It is called by the generated `dist/adapter.js`.\n *\n * Attempts to load the optional `openclaw` peer dependency and wrap the plugin\n * entry with the host's `definePluginEntry()`. Falls back to the raw entry if\n * `openclaw` is not installed (standalone CLI mode).\n *\n * @param entry - The object returned by `createEntry()`.\n * @param callerUrl - Pass `import.meta.url` from the generated adapter file.\n */\nexport function createAdapter(entry: PluginEntry, callerUrl: string): unknown {\n // Ensure contracts.tools is populated even for plugins using raw register().\n // Dry-run register() with a fake API to collect tool names.\n const enriched = ensureContracts(entry);\n\n const req = createRequire(callerUrl);\n\n try {\n const sdk = req(\"openclaw/plugin-sdk/plugin-entry\") as {\n definePluginEntry?: (e: unknown) => unknown;\n };\n\n if (typeof sdk.definePluginEntry !== \"function\") {\n throw new Error(\n \"OpenClaw SDK loaded but did not export `definePluginEntry`. Upgrade the `openclaw` package.\",\n );\n }\n\n return sdk.definePluginEntry(enriched);\n } catch (err: unknown) {\n if (isModuleNotFoundError(err)) return enriched;\n throw err;\n }\n}\n\n/**\n * If the entry lacks contracts.tools, dry-run register() to discover tool names.\n */\nexport function ensureContracts(entry: PluginEntry): PluginEntry {\n if (entry.contracts?.tools?.length) return entry;\n\n try {\n const toolNames: string[] = [];\n const fakeApi: PluginApi = {\n registerTool: (tool: unknown) => {\n if (tool && typeof tool === \"object\" && \"name\" in tool) {\n toolNames.push((tool as { name: string }).name);\n }\n },\n pluginConfig: {},\n };\n entry.register(fakeApi);\n if (toolNames.length > 0) {\n entry.contracts = { tools: toolNames };\n }\n } catch {\n // register() may have side effects that fail without a real API — ignore.\n }\n\n return entry;\n}\n\nfunction isModuleNotFoundError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n return (\n \"code\" in err &&\n ((err as { code: string }).code === \"MODULE_NOT_FOUND\" ||\n (err as { code: string }).code === \"ERR_MODULE_NOT_FOUND\")\n );\n}\n"],"mappings":";AAeA,SAAS,qBAAqB;AAqFvB,SAAS,aAAsD,KAyBhD;AACpB,SAAO,MAAM;AAEX,UAAM,eAAe,CAAC,YAAqB;AAC3C,UAAM,WAAW,IAAI,MAAM,WAAW;AAEtC,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,YAAY,IAAI,cAAc,EAAE,WAAW,KAAK;AAAA;AAAA,MAEhD,WAAW,EAAE,OAAO,SAAS,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE;AAAA;AAAA,MAEhD,cAAc,IAAI;AAAA,MAClB,SAAS,KAAgB;AAGvB,cAAM,SAAU,IAAI,gBAAgB,CAAC;AAErC,mBAAW,WAAW,UAAU;AAC9B,cAAI,aAAa;AAAA,YACf,MAAM,QAAQ;AAAA,YACd,OAAO,QAAQ,SAAS,QAAQ;AAAA,YAChC,aAAa,QAAQ;AAAA,YACrB,YAAY,QAAQ;AAAA;AAAA,YAEpB,SAAS,OAAO,aAAqB,WACnC,aAAa,MAAM,QAAQ,QAAQ,QAAQ,MAAM,CAAC;AAAA,UACtD,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAoEO,SAAS,aAAa,MAAe;AAC1C,MAAI;AACJ,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO;AAAA,EACT,OAAO;AACL,QAAI;AACF,aAAO,KAAK,UAAU,IAAI,KAAK,OAAO,IAAI;AAAA,IAC5C,QAAQ;AACN,aAAO,OAAO,IAAI;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAiB,KAAK,CAAC;AAAA,IACzC,SAAS,CAAC;AAAA,EACZ;AACF;AAkBO,SAAS,cAAc,OAAoB,WAA4B;AAG5E,QAAM,WAAW,gBAAgB,KAAK;AAEtC,QAAM,MAAM,cAAc,SAAS;AAEnC,MAAI;AACF,UAAM,MAAM,IAAI,kCAAkC;AAIlD,QAAI,OAAO,IAAI,sBAAsB,YAAY;AAC/C,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,WAAO,IAAI,kBAAkB,QAAQ;AAAA,EACvC,SAAS,KAAc;AACrB,QAAI,sBAAsB,GAAG,EAAG,QAAO;AACvC,UAAM;AAAA,EACR;AACF;AAKO,SAAS,gBAAgB,OAAiC;AAC/D,MAAI,MAAM,WAAW,OAAO,OAAQ,QAAO;AAE3C,MAAI;AACF,UAAM,YAAsB,CAAC;AAC7B,UAAM,UAAqB;AAAA,MACzB,cAAc,CAAC,SAAkB;AAC/B,YAAI,QAAQ,OAAO,SAAS,YAAY,UAAU,MAAM;AACtD,oBAAU,KAAM,KAA0B,IAAI;AAAA,QAChD;AAAA,MACF;AAAA,MACA,cAAc,CAAC;AAAA,IACjB;AACA,UAAM,SAAS,OAAO;AACtB,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,YAAY,EAAE,OAAO,UAAU;AAAA,IACvC;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;AAEA,SAAS,sBAAsB,KAAuB;AACpD,MAAI,EAAE,eAAe,OAAQ,QAAO;AACpC,SACE,UAAU,QACR,IAAyB,SAAS,sBACjC,IAAyB,SAAS;AAEzC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * carapace-plugin-sdk — Core types and helpers for OpenClaw plugins.\n *\n * This is the main entry point. Import `definePlugin` to author a plugin\n * with full TypeScript inference — typed config and typed tool parameters\n * with no boilerplate.\n *\n * Quick-start:\n *\n * src/plugin.ts — the only file you write; export `createEntry` from `definePlugin`\n * dist/adapter.js — auto-generated at build time; do not write by hand\n *\n * @module carapace-plugin-sdk\n */\n\nimport { createRequire } from \"node:module\";\nimport { type TObject, type Static } from \"@sinclair/typebox\";\n\n// ---------------------------------------------------------------------------\n// definePlugin — the primary authoring API\n// ---------------------------------------------------------------------------\n\n/**\n * Internal (erased) tool definition stored at runtime.\n * The typed version lives only in TypeScript's type system via ToolFactory<TConfig>.\n */\ninterface ToolDef {\n name: string;\n label?: string;\n description: string;\n parameters: unknown;\n execute(params: Record<string, unknown>, config: unknown): Promise<unknown>;\n}\n\n/**\n * Typed tool factory injected into the `tools` callback of `definePlugin`.\n *\n * TConfig is fixed by the enclosing `definePlugin` call, so every tool in the\n * array receives the same config type. TSchema is inferred per tool from the\n * `parameters` field, giving typed `params` in `execute`.\n *\n * You never construct this directly — it is passed to you by `definePlugin`.\n */\ntype ToolFactory<TConfig> = <TSchema extends TObject>(def: {\n /** Machine-readable name, used as the CLI subcommand. snake_case recommended. */\n name: string;\n /** Human-readable label shown in OpenClaw's UI. Defaults to `name`. */\n label?: string;\n /** One-sentence description shown in --help and OpenClaw's tool inspector. */\n description: string;\n /**\n * TypeBox schema for the tool's parameters.\n * The type is inferred as `Static<TSchema>` in `execute`'s first argument.\n */\n parameters: TSchema;\n /**\n * The tool's implementation.\n *\n * @param params - Typed parameters derived from `parameters` schema. No casts needed.\n * @param config - Typed config derived from `definePlugin`'s `configSchema`. No casts needed.\n * @returns Any JSON-serialisable value. The SDK wraps it in the OpenClaw result format.\n */\n execute(params: Static<TSchema>, config: TConfig): Promise<unknown>;\n}) => ToolDef;\n\n/**\n * Define an OpenClaw plugin with full TypeScript inference.\n *\n * Returns a `createEntry` function — export it from your `src/index.ts`.\n * The SDK handles all registration, result wrapping, and config plumbing.\n *\n * Config type is inferred from `configSchema` and flows into every tool's\n * `execute(params, config)` without any manual type annotations.\n *\n * @example\n * // src/index.ts — the entire plugin\n * import { definePlugin } from \"carapace-plugin-sdk\";\n * import { Type } from \"@sinclair/typebox\";\n *\n * export const createEntry = definePlugin({\n * id: \"my-plugin\",\n * name: \"My Plugin\",\n * configSchema: Type.Object({\n * apiKey: Type.Optional(Type.String({ description: \"API key.\" })),\n * }),\n * tools: (tool) => [\n * tool({\n * name: \"do_thing\",\n * description: \"Does the thing.\",\n * parameters: Type.Object({\n * input: Type.String({ description: \"Input value.\" }),\n * }),\n * execute: async ({ input }, config) => {\n * // input: string ✓ config.apiKey: string | undefined ✓\n * return { result: input };\n * },\n * }),\n * ],\n * });\n */\nexport function definePlugin<TConfigSchema extends TObject = TObject>(def: {\n /** Unique plugin id. Lowercase alphanumeric with hyphens. Used as the CLI binary name. */\n id: string;\n /** Human-readable display name. */\n name: string;\n /** One-sentence description of what the plugin does. */\n description?: string;\n /** When to load the plugin. Defaults to `{ onStartup: true }`. */\n activation?: { onStartup?: boolean };\n /**\n * TypeBox schema for the plugin's config block.\n *\n * Used for three things simultaneously:\n * 1. Runtime JSON Schema for the OpenClaw manifest (validated before register())\n * 2. TypeScript type inference for `config` in every tool's `execute`\n * 3. Environment variable mapping for the standalone CLI\n */\n configSchema?: TConfigSchema;\n /**\n * Declare your tools here. Receives a typed `tool()` factory as its argument.\n *\n * Using a callback (rather than a plain array) is what allows TypeScript to\n * thread the config type through to each tool's `execute` function.\n */\n tools: (tool: ToolFactory<Static<TConfigSchema>>) => ToolDef[];\n}): () => PluginEntry {\n return () => {\n // The factory is identity at runtime — all type magic is compile-time only.\n const toolFactory = ((toolDef: unknown) => toolDef) as ToolFactory<Static<TConfigSchema>>;\n const toolDefs = def.tools(toolFactory);\n\n return {\n id: def.id,\n name: def.name,\n description: def.description,\n activation: def.activation ?? { onStartup: true },\n // Derive contracts from the declared tools so the manifest is always accurate.\n contracts: { tools: toolDefs.map((t) => t.name) },\n // TypeBox TObject is valid JSON Schema — pass through for the manifest generator.\n configSchema: def.configSchema as unknown as PluginEntry[\"configSchema\"],\n register(api: PluginApi) {\n // OpenClaw validates pluginConfig against configSchema before calling register(),\n // so this cast is safe. Fall back to empty object if config is not yet set.\n const config = (api.pluginConfig ?? {}) as Static<TConfigSchema>;\n\n for (const toolDef of toolDefs) {\n api.registerTool({\n name: toolDef.name,\n label: toolDef.label ?? toolDef.name,\n description: toolDef.description,\n parameters: toolDef.parameters,\n // Wrap the result automatically — execute() returns plain values, not formatResult().\n execute: async (_toolCallId: string, params: Record<string, unknown>) =>\n formatResult(await toolDef.execute(params, config)),\n });\n }\n },\n };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Low-level types — the plugin contract\n//\n// These are used by the generated adapter and by advanced plugins that need\n// more control than definePlugin provides (e.g. dynamic tool registration).\n// ---------------------------------------------------------------------------\n\n/**\n * The API object passed to your plugin's `register()` function.\n *\n * When using `definePlugin`, you never see this directly — the SDK handles it.\n * It is exported for advanced use cases and for the generated adapter.\n */\nexport type PluginApi = {\n /** Register a tool with OpenClaw. Call once per tool inside register(). */\n registerTool: (tool: unknown) => void;\n /**\n * The user's config values for this plugin, keyed by field name.\n * Validated against configSchema by OpenClaw before register() is called.\n * May be undefined if the user has not configured the plugin.\n */\n pluginConfig?: Record<string, unknown>;\n};\n\n/**\n * The object returned by `createEntry()` — the plugin's public contract.\n *\n * When using `definePlugin`, this is constructed automatically.\n * Exported for advanced plugins that build it manually.\n */\nexport interface PluginEntry {\n /** Unique plugin id. Used as the CLI binary name and OpenClaw config key. */\n id: string;\n /** Human-readable display name. */\n name: string;\n /** One-sentence description. */\n description?: string;\n /** Tool names this plugin promises to register. Derived automatically by `definePlugin`. */\n contracts?: { tools: string[] };\n /** When to load the plugin. Defaults to `{ onStartup: true }`. */\n activation?: { onStartup?: boolean };\n /**\n * JSON Schema for the plugin's config block.\n * Pass a TypeBox `Type.Object(...)` — it is valid JSON Schema and gives you type inference.\n */\n configSchema?: unknown;\n /** Called once by OpenClaw at startup. Use `definePlugin` instead of implementing this directly. */\n register(api: PluginApi): void;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap a value in the standard OpenClaw tool result format.\n *\n * When using `definePlugin`, you do NOT call this yourself — the SDK calls it\n * automatically after your `execute` function returns.\n *\n * For advanced plugins that implement `register()` directly, wrap your\n * execute return values with this function.\n *\n * @param data - Anything JSON-serialisable, or a plain string.\n * @returns `{ content: [{ type: \"text\", text: \"<json>\" }], details: {} }`\n */\nexport function formatResult(data: unknown) {\n let text: string;\n if (typeof data === \"string\") {\n text = data;\n } else {\n try {\n text = JSON.stringify(data) ?? String(data);\n } catch {\n text = String(data);\n }\n }\n return {\n content: [{ type: \"text\" as const, text }],\n details: {},\n };\n}\n\n// ---------------------------------------------------------------------------\n// Adapter factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create the plugin's OpenClaw adapter export.\n *\n * **You never call this yourself.** It is called by the generated `dist/adapter.js`.\n *\n * Attempts to load the optional `openclaw` peer dependency and wrap the plugin\n * entry with the host's `definePluginEntry()`. Falls back to the raw entry if\n * `openclaw` is not installed (standalone CLI mode).\n *\n * @param entry - The object returned by `createEntry()`.\n * @param callerUrl - Pass `import.meta.url` from the generated adapter file.\n */\nexport function createAdapter(entry: PluginEntry, callerUrl: string): unknown {\n // v2: contracts.tools is required — plugins must declare tools via definePlugin\n // or set contracts.tools explicitly. The ensureContracts fallback has been removed.\n if (!entry.contracts?.tools?.length) {\n throw new Error(\n `Plugin \"${entry.id ?? entry.name}\" is missing contracts.tools. ` +\n `Migrate to definePlugin() from carapace-plugin-sdk — see https://github.com/JeffSteinbok/carapace-plugin-sdk`,\n );\n }\n\n const req = createRequire(callerUrl);\n\n try {\n const sdk = req(\"openclaw/plugin-sdk/plugin-entry\") as {\n definePluginEntry?: (e: unknown) => unknown;\n };\n\n if (typeof sdk.definePluginEntry !== \"function\") {\n throw new Error(\n \"OpenClaw SDK loaded but did not export `definePluginEntry`. Upgrade the `openclaw` package.\",\n );\n }\n\n const defined = sdk.definePluginEntry(entry) as Record<string, unknown>;\n // definePluginEntry may strip contracts — preserve them from the entry.\n if (entry.contracts && !defined.contracts) {\n defined.contracts = entry.contracts;\n }\n return defined;\n } catch (err: unknown) {\n if (isModuleNotFoundError(err)) return entry;\n throw err;\n }\n}\n\nfunction isModuleNotFoundError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n return (\n \"code\" in err &&\n ((err as { code: string }).code === \"MODULE_NOT_FOUND\" ||\n (err as { code: string }).code === \"ERR_MODULE_NOT_FOUND\")\n );\n}\n"],"mappings":";AAeA,SAAS,qBAAqB;AAqFvB,SAAS,aAAsD,KAyBhD;AACpB,SAAO,MAAM;AAEX,UAAM,eAAe,CAAC,YAAqB;AAC3C,UAAM,WAAW,IAAI,MAAM,WAAW;AAEtC,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,YAAY,IAAI,cAAc,EAAE,WAAW,KAAK;AAAA;AAAA,MAEhD,WAAW,EAAE,OAAO,SAAS,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE;AAAA;AAAA,MAEhD,cAAc,IAAI;AAAA,MAClB,SAAS,KAAgB;AAGvB,cAAM,SAAU,IAAI,gBAAgB,CAAC;AAErC,mBAAW,WAAW,UAAU;AAC9B,cAAI,aAAa;AAAA,YACf,MAAM,QAAQ;AAAA,YACd,OAAO,QAAQ,SAAS,QAAQ;AAAA,YAChC,aAAa,QAAQ;AAAA,YACrB,YAAY,QAAQ;AAAA;AAAA,YAEpB,SAAS,OAAO,aAAqB,WACnC,aAAa,MAAM,QAAQ,QAAQ,QAAQ,MAAM,CAAC;AAAA,UACtD,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAoEO,SAAS,aAAa,MAAe;AAC1C,MAAI;AACJ,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO;AAAA,EACT,OAAO;AACL,QAAI;AACF,aAAO,KAAK,UAAU,IAAI,KAAK,OAAO,IAAI;AAAA,IAC5C,QAAQ;AACN,aAAO,OAAO,IAAI;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAiB,KAAK,CAAC;AAAA,IACzC,SAAS,CAAC;AAAA,EACZ;AACF;AAkBO,SAAS,cAAc,OAAoB,WAA4B;AAG5E,MAAI,CAAC,MAAM,WAAW,OAAO,QAAQ;AACnC,UAAM,IAAI;AAAA,MACR,WAAW,MAAM,MAAM,MAAM,IAAI;AAAA,IAEnC;AAAA,EACF;AAEA,QAAM,MAAM,cAAc,SAAS;AAEnC,MAAI;AACF,UAAM,MAAM,IAAI,kCAAkC;AAIlD,QAAI,OAAO,IAAI,sBAAsB,YAAY;AAC/C,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,UAAU,IAAI,kBAAkB,KAAK;AAE3C,QAAI,MAAM,aAAa,CAAC,QAAQ,WAAW;AACzC,cAAQ,YAAY,MAAM;AAAA,IAC5B;AACA,WAAO;AAAA,EACT,SAAS,KAAc;AACrB,QAAI,sBAAsB,GAAG,EAAG,QAAO;AACvC,UAAM;AAAA,EACR;AACF;AAEA,SAAS,sBAAsB,KAAuB;AACpD,MAAI,EAAE,eAAe,OAAQ,QAAO;AACpC,SACE,UAAU,QACR,IAAyB,SAAS,sBACjC,IAAyB,SAAS;AAEzC;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carapace-plugin-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "🦞🐚 SDK for building OpenClaw plugins — types, helpers, CLI generation",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@types/node": "^22",
|
|
48
48
|
"tsup": "^8.3.0",
|
|
49
49
|
"typescript": "^5.7",
|
|
50
|
-
"vitest": "^
|
|
50
|
+
"vitest": "^4.1.8"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">=20"
|