openclaw-workflowskill 0.3.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
@@ -24,19 +24,23 @@ Author, validate, run, and review WorkflowSkill workflows — without leaving th
24
24
 
25
25
  Requires [OpenClaw](https://openclaw.ai).
26
26
 
27
- ### 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
28
32
 
29
33
  ```bash
30
34
  openclaw plugins install openclaw-workflowskill
31
35
  ```
32
36
 
33
- ### 2. Restart the gateway
37
+ ### 3. Restart the gateway
34
38
 
35
39
  ```bash
36
40
  openclaw gateway restart
37
41
  ```
38
42
 
39
- ### 3. Verify
43
+ ### 4. Verify
40
44
 
41
45
  ```bash
42
46
  openclaw plugins list
@@ -48,7 +52,7 @@ openclaw skills list
48
52
 
49
53
  > **Note:** `workflowskill_llm` uses your Anthropic credentials from the main agent — no separate API key configuration needed.
50
54
 
51
- ### 4. Create a workflow
55
+ ### 5. Create a workflow
52
56
 
53
57
  Just tell the agent what you want to automate:
54
58
 
@@ -60,7 +64,7 @@ Just tell the agent what you want to automate:
60
64
  >
61
65
  > Run complete: 4 AI stories found, summary drafted. Ready to schedule — want me to set up a daily cron at 8 AM?
62
66
 
63
- ### 5. Schedule it
67
+ ### 6. Schedule it
64
68
 
65
69
  Ask the agent to set up a cron job, or add one manually at `~/.openclaw/cron/jobs.json`:
66
70
 
package/index.ts CHANGED
@@ -4,12 +4,12 @@
4
4
  // Default-exports an object with id + register(api) per the OpenClaw plugin API.
5
5
 
6
6
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
7
- import { homedir } from 'node:os';
8
7
  import { join } from 'node:path';
9
8
  import { AUTHORING_SKILL } from 'workflowskill';
10
9
  import { validateHandler } from './tools/validate.js';
11
10
  import { runHandler } from './tools/run.js';
12
11
  import { runsHandler } from './tools/runs.js';
12
+ import { llmHandler } from './tools/llm.js';
13
13
  import { createToolAdapter, type GatewayConfig } from './lib/adapters.js';
14
14
 
15
15
  // ─── OpenClaw plugin API types ─────────────────────────────────────────────
@@ -81,74 +81,10 @@ function toContent(result: unknown): { content: TextContent[] } {
81
81
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
82
82
  }
83
83
 
84
- // ─── LLM helpers ───────────────────────────────────────────────────────────
85
-
86
- const MODEL_ALIASES: Record<string, string> = {
87
- haiku: 'claude-haiku-4-5-20251001',
88
- sonnet: 'claude-sonnet-4-6',
89
- opus: 'claude-opus-4-6',
90
- };
91
- const DEFAULT_MODEL = 'claude-haiku-4-5-20251001';
92
-
93
- function readAnthropicApiKey(): string {
94
- const profilesPath = join(homedir(), '.openclaw', 'agents', 'main', 'agent', 'auth-profiles.json');
95
- let parsed: {
96
- profiles?: Record<string, { provider?: string; key?: string }>;
97
- lastGood?: Record<string, string>;
98
- };
99
- try {
100
- parsed = JSON.parse(readFileSync(profilesPath, 'utf-8')) as typeof parsed;
101
- } catch (err) {
102
- throw new Error(
103
- `WorkflowSkill: could not read OpenClaw auth profiles from ${profilesPath}: ${err instanceof Error ? err.message : String(err)}`,
104
- );
105
- }
106
- const profiles = parsed.profiles ?? {};
107
- const lastGoodName = parsed.lastGood?.['anthropic'];
108
- const profile = lastGoodName
109
- ? profiles[lastGoodName]
110
- : Object.values(profiles).find((p) => p.provider === 'anthropic');
111
- if (!profile?.key) {
112
- throw new Error(
113
- `WorkflowSkill: no anthropic profile found in ${profilesPath}. Add a profile with provider "anthropic" and a key.`,
114
- );
115
- }
116
- return profile.key;
117
- }
118
-
119
- const ANTHROPIC_TIMEOUT_MS = 60_000;
120
-
121
- async function callAnthropic(apiKey: string, model: string, prompt: string): Promise<string> {
122
- const resolvedModel = MODEL_ALIASES[model] ?? model;
123
- const response = await fetch('https://api.anthropic.com/v1/messages', {
124
- method: 'POST',
125
- headers: {
126
- 'Content-Type': 'application/json',
127
- 'x-api-key': apiKey,
128
- 'anthropic-version': '2023-06-01',
129
- },
130
- body: JSON.stringify({
131
- model: resolvedModel,
132
- max_tokens: 8192,
133
- messages: [{ role: 'user', content: prompt }],
134
- }),
135
- signal: AbortSignal.timeout(ANTHROPIC_TIMEOUT_MS),
136
- });
137
- if (!response.ok) {
138
- const body = await response.text().catch(() => '');
139
- throw new Error(`Anthropic API error ${response.status}: ${body}`);
140
- }
141
- const data = (await response.json()) as {
142
- content: Array<{ type: string; text: string }>;
143
- };
144
- const text = data.content.find((b) => b.type === 'text')?.text ?? '';
145
- return text;
146
- }
147
-
148
84
  // ─── Plugin entry point ────────────────────────────────────────────────────
149
85
 
150
86
  export default {
151
- id: 'workflowskill',
87
+ id: 'openclaw-workflowskill',
152
88
 
153
89
  register(api: PluginApi): void {
154
90
  const workspace = api?.config?.agents?.defaults?.workspace;
@@ -291,10 +227,7 @@ export default {
291
227
  required: ['prompt'],
292
228
  },
293
229
  execute: async (_id, params) => {
294
- const { prompt, model = DEFAULT_MODEL } = params as { prompt: string; model?: string };
295
- const apiKey = readAnthropicApiKey();
296
- const text = await callAnthropic(apiKey, model, prompt);
297
- return toContent({ text });
230
+ return toContent(await llmHandler(params as { prompt: string; model?: string }));
298
231
  },
299
232
  });
300
233
 
@@ -1,5 +1,5 @@
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
5
  "version": "0.3.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-workflowskill",
3
- "version": "0.3.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",
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
+ }