openclaw-workflowskill 0.2.1 → 0.3.2

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 CHANGED
@@ -13,53 +13,27 @@ Author, validate, run, and review WorkflowSkill workflows — without leaving th
13
13
  2. The agent writes, validates, and test-runs the workflow in chat
14
14
  3. Schedule it with cron — runs autonomously, no agent session needed
15
15
 
16
- ## What it looks like
17
-
18
- > **You:** I want to check Hacker News for AI stories every morning and email me a summary.
19
- >
20
- > **Agent:** I'll author a WorkflowSkill for that. *(invokes `/workflowskill-author`, writes a SKILL.md, runs `workflowskill_validate`)*
21
- >
22
- > Validated — 3 steps: `fetch`, `filter`, `email`. Running a test now... *(invokes `workflowskill_run`)*
23
- >
24
- > Run complete: 4 AI stories found, summary drafted. Ready to schedule — want me to set up a daily cron at 8 AM?
25
-
26
- ## Workflow Lifecycle
27
-
28
- ```
29
- describe workflow in natural language
30
-
31
- /workflowskill-author (agent writes YAML)
32
-
33
- workflowskill_validate (catch errors early)
34
-
35
- workflowskill_run (test run, review RunLog)
36
-
37
- workflowskill_runs (diagnose failures, iterate)
38
-
39
- cron (schedule for automated execution)
40
- ```
41
-
42
16
  ## Repositories
43
17
 
44
- | Repo | Description |
45
- |------|-------------|
46
- | [workflowskill](https://github.com/matthew-h-cromer/workflowskill) | Specification and reference runtime |
47
- | **openclaw-workflowskill** (this repo) | OpenClaw plugin — author, validate, run, and review workflows from the agent |
18
+ | Repo | Description |
19
+ | ------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
20
+ | [workflowskill](https://github.com/matthew-h-cromer/workflowskill) | Specification and reference runtime |
21
+ | **openclaw-workflowskill** (this repo) | OpenClaw plugin — author, validate, run, and review workflows from the agent |
48
22
 
49
23
  ## Quick Start
50
24
 
51
25
  Requires [OpenClaw](https://openclaw.ai).
52
26
 
53
- ### 1. Install the plugin
27
+ ### 1. Set your profile to Coding
28
+
29
+ In OpenClaw settings, change your profile from `messaging` (the default) to `coding`. The `coding` profile grants the agent filesystem access, which is required to read and write workflow files. For more granular access configuration, refer to the [OpenClaw documentation](https://docs.openclaw.ai/).
30
+
31
+ ### 2. Install the plugin
54
32
 
55
33
  ```bash
56
34
  openclaw plugins install openclaw-workflowskill
57
35
  ```
58
36
 
59
- ### 2. Configure Anthropic credentials
60
-
61
- The plugin reads your Anthropic API key from OpenClaw's credential store — no `.env` file needed. Make sure an Anthropic auth profile is configured in OpenClaw (`~/.openclaw/agents/main/agent/auth-profiles.json`).
62
-
63
37
  ### 3. Restart the gateway
64
38
 
65
39
  ```bash
@@ -76,16 +50,46 @@ openclaw skills list
76
50
  # → workflowskill-author (user-invocable)
77
51
  ```
78
52
 
53
+ > **Note:** `workflowskill_llm` uses your Anthropic credentials from the main agent — no separate API key configuration needed.
54
+
55
+ ### 5. Create a workflow
56
+
57
+ Just tell the agent what you want to automate:
58
+
59
+ > **You:** I want to check Hacker News for AI stories every morning and email me a summary.
60
+ >
61
+ > **Agent:** I'll author a WorkflowSkill for that. _(invokes `/workflowskill-author`, writes a SKILL.md, runs `workflowskill_validate`)_
62
+ >
63
+ > Validated — 3 steps: `fetch`, `filter`, `email`. Running a test now... _(invokes `workflowskill_run`)_
64
+ >
65
+ > Run complete: 4 AI stories found, summary drafted. Ready to schedule — want me to set up a daily cron at 8 AM?
66
+
67
+ ### 6. Schedule it
68
+
69
+ Ask the agent to set up a cron job, or add one manually at `~/.openclaw/cron/jobs.json`:
70
+
71
+ ```json
72
+ {
73
+ "payload": {
74
+ "kind": "agentTurn",
75
+ "message": "Run the daily-triage workflow using workflowskill_run\n\nSend results to Slack in the #general channel",
76
+ "model": "haiku"
77
+ }
78
+ }
79
+ ```
80
+
81
+ Your agent will use `"model": "haiku"` for cron jobs, ensuring execution is cheap and lightweight.
82
+
79
83
  ## Tools
80
84
 
81
85
  Registers four tools with the OpenClaw agent:
82
86
 
83
- | Tool | Description |
84
- |------|-------------|
85
- | `workflowskill_validate` | Parse and validate a SKILL.md or raw YAML workflow |
86
- | `workflowskill_run` | Execute a workflow and return a compact run summary |
87
- | `workflowskill_runs` | List and inspect past run logs |
88
- | `workflowskill_llm` | Call Anthropic directly for inline LLM reasoning in workflows |
87
+ | Tool | Description |
88
+ | ------------------------ | ------------------------------------------------------------- |
89
+ | `workflowskill_validate` | Parse and validate a SKILL.md or raw YAML workflow |
90
+ | `workflowskill_run` | Execute a workflow and return a compact run summary |
91
+ | `workflowskill_runs` | List and inspect past run logs |
92
+ | `workflowskill_llm` | Call Anthropic directly for inline LLM reasoning in workflows |
89
93
 
90
94
  Also ships the `/workflowskill-author` skill — just say "I want to automate X" and the agent handles the rest: researching, writing, validating, and test-running the workflow in chat.
91
95
 
@@ -100,22 +104,6 @@ Also ships the `/workflowskill-author` skill — just say "I want to automate X"
100
104
  daily-triage-2024-01-15T09-00-00.000Z.json
101
105
  ```
102
106
 
103
- ## Cron Scheduling
104
-
105
- Schedule a workflow to run autonomously via OpenClaw's cron, at `~/.openclaw/cron/jobs.json`:
106
-
107
- ```json
108
- {
109
- "payload": {
110
- "kind": "agentTurn",
111
- "message": "Run the daily-triage workflow using workflowskill_run\n\nSend results to Slack in the #general channel",
112
- "model": "haiku"
113
- }
114
- }
115
- ```
116
-
117
- Always set `"model": "haiku"` — cron runs are lightweight orchestration and don't need a powerful model. Put delivery instructions (e.g. Slack channel) in the cron message, not in the workflow, so workflows stay reusable.
118
-
119
107
  Review past runs via `workflowskill_runs`.
120
108
 
121
109
  ## Architecture
@@ -134,9 +122,9 @@ The plugin's own four tools (`workflowskill_validate`, `workflowskill_run`, `wor
134
122
 
135
123
  Parse and validate a SKILL.md or raw YAML workflow.
136
124
 
137
- | Param | Type | Required | Description |
138
- |-------|------|----------|-------------|
139
- | `content` | string | yes | SKILL.md text or raw workflow YAML |
125
+ | Param | Type | Required | Description |
126
+ | --------- | ------ | -------- | ---------------------------------- |
127
+ | `content` | string | yes | SKILL.md text or raw workflow YAML |
140
128
 
141
129
  Returns `{ valid, errors[], name, stepCount, stepTypes[] }`.
142
130
 
@@ -144,23 +132,23 @@ Returns `{ valid, errors[], name, stepCount, stepTypes[] }`.
144
132
 
145
133
  Execute a workflow and return a compact run summary. The full RunLog is persisted to `workflow-runs/` and retrievable via `workflowskill_runs` with `run_id`.
146
134
 
147
- | Param | Type | Required | Description |
148
- |-------|------|----------|-------------|
149
- | `workflow_name` | string | no* | Name of a skill resolved from skills directories |
150
- | `content` | string | no* | Inline SKILL.md content (bypasses skill files) |
151
- | `inputs` | object | no | Override workflow input defaults |
135
+ | Param | Type | Required | Description |
136
+ | --------------- | ------ | -------- | ------------------------------------------------ |
137
+ | `workflow_name` | string | no\* | Name of a skill resolved from skills directories |
138
+ | `content` | string | no\* | Inline SKILL.md content (bypasses skill files) |
139
+ | `inputs` | object | no | Override workflow input defaults |
152
140
 
153
- *One of `workflow_name` or `content` is required.
141
+ \*One of `workflow_name` or `content` is required.
154
142
 
155
143
  ### `workflowskill_runs`
156
144
 
157
145
  List and inspect past run logs.
158
146
 
159
- | Param | Type | Required | Description |
160
- |-------|------|----------|-------------|
161
- | `workflow_name` | string | no | Filter by workflow name |
162
- | `run_id` | string | no | Get full RunLog detail for one run |
163
- | `status` | string | no | Filter by `"success"` or `"failed"` |
147
+ | Param | Type | Required | Description |
148
+ | --------------- | ------ | -------- | ----------------------------------- |
149
+ | `workflow_name` | string | no | Filter by workflow name |
150
+ | `run_id` | string | no | Get full RunLog detail for one run |
151
+ | `status` | string | no | Filter by `"success"` or `"failed"` |
164
152
 
165
153
  No params → 20 most recent runs (summary view).
166
154
 
@@ -168,16 +156,16 @@ No params → 20 most recent runs (summary view).
168
156
 
169
157
  Call Anthropic directly and return the text response. Uses the API key from OpenClaw's credential store. Useful in workflow `tool` steps when you need inline LLM reasoning.
170
158
 
171
- | Param | Type | Required | Description |
172
- |-------|------|----------|-------------|
173
- | `prompt` | string | yes | The prompt to send to the LLM |
174
- | `model` | string | no | Model alias (`haiku`, `sonnet`, `opus`) or full model ID — omit to use the default |
159
+ | Param | Type | Required | Description |
160
+ | -------- | ------ | -------- | ---------------------------------------------------------------------------------- |
161
+ | `prompt` | string | yes | The prompt to send to the LLM |
162
+ | `model` | string | no | Model alias (`haiku`, `sonnet`, `opus`) or full model ID — omit to use the default |
175
163
 
176
164
  Returns `{ text: string }`.
177
165
 
178
166
  ## Development
179
167
 
180
- The plugin imports from `workflowskill` (peer dependency), installed from npm. No build step is required for type checking:
168
+ The plugin imports from `workflowskill`, installed from npm. No build step is required for type checking:
181
169
 
182
170
  ```bash
183
171
  npm install
package/index.ts CHANGED
@@ -9,7 +9,8 @@ import { AUTHORING_SKILL } from 'workflowskill';
9
9
  import { validateHandler } from './tools/validate.js';
10
10
  import { runHandler } from './tools/run.js';
11
11
  import { runsHandler } from './tools/runs.js';
12
- import { createAdapters, type GatewayConfig } from './lib/adapters.js';
12
+ import { llmHandler } from './tools/llm.js';
13
+ import { createToolAdapter, type GatewayConfig } from './lib/adapters.js';
13
14
 
14
15
  // ─── OpenClaw plugin API types ─────────────────────────────────────────────
15
16
 
@@ -83,7 +84,7 @@ function toContent(result: unknown): { content: TextContent[] } {
83
84
  // ─── Plugin entry point ────────────────────────────────────────────────────
84
85
 
85
86
  export default {
86
- id: 'workflowskill',
87
+ id: 'openclaw-workflowskill',
87
88
 
88
89
  register(api: PluginApi): void {
89
90
  const workspace = api?.config?.agents?.defaults?.workspace;
@@ -104,7 +105,7 @@ export default {
104
105
  writeFileSync(join(skillDir, 'SKILL.md'), AUTHORING_SKILL + '\n' + openclawContext, 'utf-8');
105
106
 
106
107
  const gatewayConfig = buildGatewayConfig(api.config);
107
- const adapters = createAdapters(gatewayConfig);
108
+ const toolAdapter = createToolAdapter(gatewayConfig);
108
109
  const { registerTool } = api;
109
110
 
110
111
  // ── workflowskill_validate ────────────────────────────────────────────
@@ -125,7 +126,7 @@ export default {
125
126
  required: ['content'],
126
127
  },
127
128
  execute: async (_id, params) => {
128
- return toContent(await validateHandler(params as { content: string }, adapters.toolAdapter));
129
+ return toContent(validateHandler(params as { content: string }, toolAdapter));
129
130
  },
130
131
  });
131
132
 
@@ -159,7 +160,7 @@ export default {
159
160
  await runHandler(
160
161
  params as { workflow_name?: string; content?: string; inputs?: Record<string, unknown> },
161
162
  workspace,
162
- adapters,
163
+ toolAdapter,
163
164
  ),
164
165
  );
165
166
  },
@@ -203,14 +204,14 @@ export default {
203
204
  },
204
205
  });
205
206
 
206
- // ── workflowskill_llm ─────────────────────────────────────────────────────
207
+ // ── workflowskill_llm ─────────────────────────────────────────────────
207
208
  registerTool({
208
209
  name: 'workflowskill_llm',
209
210
  description:
210
- 'Call Anthropic directly and return the text response. ' +
211
- 'Uses the API key from OpenClaw\'s credential store (~/.openclaw/agents/main/agent/auth-profiles.json). ' +
211
+ 'Call the Anthropic LLM and return { text }. ' +
212
+ 'Uses the API key from OpenClaw\'s credential store. ' +
212
213
  'Use in workflow tool steps when you need LLM reasoning inline. ' +
213
- 'model is optional (haiku / sonnet / opus or full model ID); omit for the default.',
214
+ 'model is optional (haiku / sonnet / opus or full model ID); omit for haiku.',
214
215
  parameters: {
215
216
  type: 'object',
216
217
  properties: {
@@ -220,16 +221,15 @@ export default {
220
221
  },
221
222
  model: {
222
223
  type: 'string',
223
- description: 'Model alias or ID. Optional — omit to use the Anthropic default.',
224
+ description: 'Model alias (haiku/sonnet/opus) or full model ID. Optional.',
224
225
  },
225
226
  },
226
227
  required: ['prompt'],
227
228
  },
228
229
  execute: async (_id, params) => {
229
- const { prompt, model } = params as { prompt: string; model?: string };
230
- const result = await adapters.llmAdapter.call(model, prompt);
231
- return toContent({ text: result.text });
230
+ return toContent(await llmHandler(params as { prompt: string; model?: string }));
232
231
  },
233
232
  });
233
+
234
234
  },
235
235
  };
package/lib/adapters.ts CHANGED
@@ -1,27 +1,8 @@
1
- // adapters.ts — host-delegating adapters for the OpenClaw plugin.
1
+ // adapters.ts — host-delegating tool adapter for the OpenClaw plugin.
2
2
  //
3
3
  // Tool steps delegate to the Gateway HTTP API via HostToolAdapter (POST /tools/invoke).
4
- // LLM steps use AnthropicLLMAdapter with the API key read directly from
5
- // OpenClaw's credential store at ~/.openclaw/agents/main/agent/auth-profiles.json.
6
4
 
7
- import { readFileSync } from 'node:fs';
8
- import { homedir } from 'node:os';
9
- import { join } from 'node:path';
10
- import type { LLMAdapter, ToolAdapter, ToolDescriptor, ToolResult } from 'workflowskill';
11
- import { AnthropicLLMAdapter, BuiltinToolAdapter } from 'workflowskill';
12
-
13
- // Tools served locally by BuiltinToolAdapter (web.scrape, etc.)
14
- // These bypass the gateway and run in-process.
15
- const BUILTIN_TOOL_NAMES = new Set(['web.scrape']);
16
-
17
- // Lazy singleton — BuiltinToolAdapter.create() is async so we initialize on first use.
18
- let _builtinToolsPromise: Promise<BuiltinToolAdapter> | null = null;
19
- function getBuiltinTools(): Promise<BuiltinToolAdapter> {
20
- if (!_builtinToolsPromise) {
21
- _builtinToolsPromise = BuiltinToolAdapter.create();
22
- }
23
- return _builtinToolsPromise as Promise<BuiltinToolAdapter>;
24
- }
5
+ import type { ToolAdapter, ToolDescriptor, ToolResult } from 'workflowskill';
25
6
 
26
7
  export interface GatewayConfig {
27
8
  baseUrl: string;
@@ -29,11 +10,6 @@ export interface GatewayConfig {
29
10
  timeoutMs?: number;
30
11
  }
31
12
 
32
- export interface AdapterSet {
33
- toolAdapter: ToolAdapter;
34
- llmAdapter: LLMAdapter;
35
- }
36
-
37
13
  // Tools this plugin registers — must not be forwarded to the gateway to prevent infinite recursion.
38
14
  const SELF_REFERENCING_TOOLS = new Set([
39
15
  'workflowskill_validate',
@@ -116,84 +92,11 @@ export class HostToolAdapter implements ToolAdapter {
116
92
  }
117
93
 
118
94
  /**
119
- * Read the Anthropic API key from OpenClaw's credential store.
120
- * Throws a clear error if the file is missing or has no anthropic profile.
121
- */
122
- function readAnthropicApiKey(): string {
123
- const profilesPath = join(homedir(), '.openclaw', 'agents', 'main', 'agent', 'auth-profiles.json');
124
- let parsed: {
125
- profiles?: Record<string, { provider?: string; key?: string }>;
126
- lastGood?: Record<string, string>;
127
- };
128
- try {
129
- parsed = JSON.parse(readFileSync(profilesPath, 'utf-8')) as typeof parsed;
130
- } catch (err) {
131
- throw new Error(
132
- `WorkflowSkill: could not read OpenClaw auth profiles from ${profilesPath}: ${err instanceof Error ? err.message : String(err)}`,
133
- );
134
- }
135
- const profiles = parsed.profiles ?? {};
136
- // Prefer the profile OpenClaw last used successfully for anthropic.
137
- const lastGoodName = parsed.lastGood?.['anthropic'];
138
- const profile = lastGoodName
139
- ? profiles[lastGoodName]
140
- : Object.values(profiles).find((p) => p.provider === 'anthropic');
141
- if (!profile?.key) {
142
- throw new Error(
143
- `WorkflowSkill: no anthropic profile found in ${profilesPath}. Add a profile with provider "anthropic" and a key.`,
144
- );
145
- }
146
- return profile.key;
147
- }
148
-
149
- /**
150
- * Create host adapters backed by the Gateway HTTP API.
95
+ * Create a ToolAdapter backed by the Gateway HTTP API.
151
96
  *
152
97
  * HostToolAdapter forwards tool steps to the gateway's POST /tools/invoke endpoint.
153
- * Self-referencing tools (the plugin's own four tools) are blocked to prevent recursion.
154
- * LLM steps use AnthropicLLMAdapter with the key read from OpenClaw's credential store.
98
+ * Self-referencing tools (the plugin's own tools) are blocked to prevent recursion.
155
99
  */
156
- export function createAdapters(gatewayConfig: GatewayConfig): AdapterSet {
157
- const hostTools = new HostToolAdapter(gatewayConfig);
158
- const llmAdapter = new AnthropicLLMAdapter(readAnthropicApiKey());
159
-
160
- const LLM_COMPLETE = 'workflowskill_llm';
161
- const LLM_COMPLETE_DESCRIPTOR: ToolDescriptor = {
162
- name: LLM_COMPLETE,
163
- description: 'Call the host LLM with a prompt; returns { text }.',
164
- };
165
-
166
- const toolAdapter: ToolAdapter = {
167
- has(toolName: string): boolean {
168
- if (toolName === LLM_COMPLETE) return true;
169
- if (BUILTIN_TOOL_NAMES.has(toolName)) return true;
170
- return hostTools.has(toolName);
171
- },
172
- async invoke(toolName: string, args: Record<string, unknown>): Promise<ToolResult> {
173
- if (toolName === LLM_COMPLETE) {
174
- const result = await llmAdapter.call(
175
- args.model as string | undefined,
176
- args.prompt as string,
177
- );
178
- return { output: { text: result.text } };
179
- }
180
- if (BUILTIN_TOOL_NAMES.has(toolName)) {
181
- const builtinTools = await getBuiltinTools();
182
- return builtinTools.invoke(toolName, args);
183
- }
184
- return hostTools.invoke(toolName, args);
185
- },
186
- list(): ToolDescriptor[] {
187
- const hostToolList = hostTools.list();
188
- return [
189
- LLM_COMPLETE_DESCRIPTOR,
190
- ...hostToolList.filter((t) => t.name !== LLM_COMPLETE),
191
- ];
192
- },
193
- };
194
-
195
- return {
196
- toolAdapter,
197
- llmAdapter,
198
- };
100
+ export function createToolAdapter(gatewayConfig: GatewayConfig): ToolAdapter {
101
+ return new HostToolAdapter(gatewayConfig);
199
102
  }
package/lib/storage.ts CHANGED
@@ -53,7 +53,7 @@ export interface RunSummaryEntry {
53
53
  duration_ms: number;
54
54
  steps_executed: number;
55
55
  steps_skipped: number;
56
- total_tokens: number;
56
+ total_duration_ms: number;
57
57
  error_message?: string;
58
58
  }
59
59
 
@@ -87,7 +87,7 @@ export function listRuns(
87
87
  duration_ms: log.duration_ms,
88
88
  steps_executed: log.summary.steps_executed,
89
89
  steps_skipped: log.summary.steps_skipped,
90
- total_tokens: log.summary.total_tokens,
90
+ total_duration_ms: log.summary.total_duration_ms,
91
91
  error_message: log.error?.message,
92
92
  });
93
93
  } catch {
@@ -1,8 +1,8 @@
1
1
  {
2
- "id": "workflowskill",
2
+ "id": "openclaw-workflowskill",
3
3
  "name": "WorkflowSkill",
4
4
  "description": "Author, validate, run, and review WorkflowSkill YAML workflows",
5
- "version": "0.1.0",
5
+ "version": "0.3.1",
6
6
  "skills": ["skills/workflowskill-author"],
7
7
  "configSchema": {
8
8
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-workflowskill",
3
- "version": "0.2.1",
3
+ "version": "0.3.2",
4
4
  "description": "WorkflowSkill plugin for OpenClaw — author, validate, run, and review YAML workflows",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -35,7 +35,7 @@
35
35
  ]
36
36
  },
37
37
  "dependencies": {
38
- "workflowskill": "^0.2.0"
38
+ "workflowskill": "^0.3.1"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^25.3.0",
package/tools/llm.ts ADDED
@@ -0,0 +1,81 @@
1
+ // workflowskill_llm — call Anthropic LLM using OpenClaw's credential store.
2
+
3
+ import { readFileSync } from 'node:fs';
4
+ import { homedir } from 'node:os';
5
+ import { join } from 'node:path';
6
+
7
+ export interface LlmParams {
8
+ prompt: string;
9
+ model?: string;
10
+ }
11
+
12
+ export interface LlmResult {
13
+ text: string;
14
+ }
15
+
16
+ const MODEL_ALIASES: Record<string, string> = {
17
+ haiku: 'claude-haiku-4-5-20251001',
18
+ sonnet: 'claude-sonnet-4-6',
19
+ opus: 'claude-opus-4-6',
20
+ };
21
+ const DEFAULT_MODEL = 'claude-haiku-4-5-20251001';
22
+ const ANTHROPIC_TIMEOUT_MS = 60_000;
23
+
24
+ function readAnthropicApiKey(): string {
25
+ const profilesPath = join(homedir(), '.openclaw', 'agents', 'main', 'agent', 'auth-profiles.json');
26
+ let parsed: {
27
+ profiles?: Record<string, { provider?: string; key?: string }>;
28
+ lastGood?: Record<string, string>;
29
+ };
30
+ try {
31
+ parsed = JSON.parse(readFileSync(profilesPath, 'utf-8')) as typeof parsed;
32
+ } catch (err) {
33
+ throw new Error(
34
+ `WorkflowSkill: could not read OpenClaw auth profiles from ${profilesPath}: ${err instanceof Error ? err.message : String(err)}`,
35
+ );
36
+ }
37
+ const profiles = parsed.profiles ?? {};
38
+ const lastGoodName = parsed.lastGood?.['anthropic'];
39
+ const profile = lastGoodName
40
+ ? profiles[lastGoodName]
41
+ : Object.values(profiles).find((p) => p.provider === 'anthropic');
42
+ if (!profile?.key) {
43
+ throw new Error(
44
+ `WorkflowSkill: no anthropic profile found in ${profilesPath}. Add a profile with provider "anthropic" and a key.`,
45
+ );
46
+ }
47
+ return profile.key;
48
+ }
49
+
50
+ async function callAnthropic(apiKey: string, model: string, prompt: string): Promise<string> {
51
+ const resolvedModel = MODEL_ALIASES[model] ?? model;
52
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
53
+ method: 'POST',
54
+ headers: {
55
+ 'Content-Type': 'application/json',
56
+ 'x-api-key': apiKey,
57
+ 'anthropic-version': '2023-06-01',
58
+ },
59
+ body: JSON.stringify({
60
+ model: resolvedModel,
61
+ max_tokens: 8192,
62
+ messages: [{ role: 'user', content: prompt }],
63
+ }),
64
+ signal: AbortSignal.timeout(ANTHROPIC_TIMEOUT_MS),
65
+ });
66
+ if (!response.ok) {
67
+ const body = await response.text().catch(() => '');
68
+ throw new Error(`Anthropic API error ${response.status}: ${body}`);
69
+ }
70
+ const data = (await response.json()) as {
71
+ content: Array<{ type: string; text: string }>;
72
+ };
73
+ return data.content.find((b) => b.type === 'text')?.text ?? '';
74
+ }
75
+
76
+ export async function llmHandler(params: LlmParams): Promise<LlmResult> {
77
+ const { prompt, model = DEFAULT_MODEL } = params;
78
+ const apiKey = readAnthropicApiKey();
79
+ const text = await callAnthropic(apiKey, model, prompt);
80
+ return { text };
81
+ }
package/tools/run.ts CHANGED
@@ -5,8 +5,7 @@
5
5
  // Returns a compact summary to avoid blowing up the calling agent's context.
6
6
 
7
7
  import { runWorkflowSkill } from 'workflowskill';
8
- import type { RunLog, RunSummary } from 'workflowskill';
9
- import type { AdapterSet } from '../lib/adapters.js';
8
+ import type { RunLog, RunSummary, ToolAdapter } from 'workflowskill';
10
9
  import { resolveSkillContent, saveRunLog } from '../lib/storage.js';
11
10
 
12
11
  export interface RunParams {
@@ -64,7 +63,7 @@ function summarizeRunLog(log: RunLog): RunSummarySuccess | RunSummaryFailed {
64
63
  export async function runHandler(
65
64
  params: RunParams,
66
65
  workspace: string,
67
- adapters: AdapterSet,
66
+ toolAdapter: ToolAdapter,
68
67
  ): Promise<RunSummarySuccess | RunSummaryFailed> {
69
68
  const { workflow_name, content: inlineContent, inputs = {} } = params;
70
69
 
@@ -73,13 +72,10 @@ export async function runHandler(
73
72
  content = resolveSkillContent(workspace, workflow_name);
74
73
  }
75
74
 
76
- const { toolAdapter, llmAdapter } = adapters;
77
-
78
75
  const log: RunLog = await runWorkflowSkill({
79
76
  content,
80
77
  inputs,
81
78
  toolAdapter,
82
- llmAdapter,
83
79
  workflowName: workflow_name ?? 'inline',
84
80
  });
85
81
 
package/tools/validate.ts CHANGED
@@ -15,6 +15,6 @@ export interface ValidateResult {
15
15
  stepTypes?: string[];
16
16
  }
17
17
 
18
- export async function validateHandler(params: ValidateParams, toolAdapter: ToolAdapter): Promise<ValidateResult> {
18
+ export function validateHandler(params: ValidateParams, toolAdapter: ToolAdapter): ValidateResult {
19
19
  return validateWorkflowSkill({ content: params.content, toolAdapter });
20
20
  }