@elizaos/plugin-claude-code-workbench 0.1.0
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 +59 -0
- package/dist/index.js +896 -0
- package/dist/index.js.map +18 -0
- package/package.json +92 -0
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @milady/plugin-claude-code-workbench
|
|
2
|
+
|
|
3
|
+
Claude Code companion plugin for Milady/ElizaOS.
|
|
4
|
+
|
|
5
|
+
This plugin provides an allowlisted, policy-controlled workflow runner for this repository so agents can execute common quality/build/test/review tasks safely.
|
|
6
|
+
|
|
7
|
+
## What it adds
|
|
8
|
+
|
|
9
|
+
- **Service**: `claude_code_workbench` (`ClaudeCodeWorkbenchService`)
|
|
10
|
+
- **Actions**:
|
|
11
|
+
- `CLAUDE_CODE_WORKBENCH_RUN`
|
|
12
|
+
- `CLAUDE_CODE_WORKBENCH_LIST`
|
|
13
|
+
- **Provider**: `CLAUDE_CODE_WORKBENCH_STATUS`
|
|
14
|
+
- **Routes**:
|
|
15
|
+
- `GET /claude-code-workbench/status`
|
|
16
|
+
- `GET /claude-code-workbench/workflows`
|
|
17
|
+
- `POST /claude-code-workbench/run`
|
|
18
|
+
|
|
19
|
+
## Default workflows
|
|
20
|
+
|
|
21
|
+
Includes repo-specific workflows such as:
|
|
22
|
+
|
|
23
|
+
- `repo_status`, `repo_diff_stat`, `recent_commits`
|
|
24
|
+
- `check`, `typecheck`, `lint`
|
|
25
|
+
- `test_once`, `test_e2e`
|
|
26
|
+
- `pre_review_local`
|
|
27
|
+
- `build_local_plugins`, `build`, `docs_build`
|
|
28
|
+
- `format_fix`, `lint_fix` (mutating; disabled by default)
|
|
29
|
+
|
|
30
|
+
## Configuration
|
|
31
|
+
|
|
32
|
+
Set via plugin config or environment variables:
|
|
33
|
+
|
|
34
|
+
- `CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT` (default: process cwd)
|
|
35
|
+
- `CLAUDE_CODE_WORKBENCH_TIMEOUT_MS` (default: `600000`)
|
|
36
|
+
- `CLAUDE_CODE_WORKBENCH_MAX_OUTPUT_CHARS` (default: `120000`)
|
|
37
|
+
- `CLAUDE_CODE_WORKBENCH_MAX_STDIN_BYTES` (default: `65536`)
|
|
38
|
+
- `CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS` (default: `*`)
|
|
39
|
+
- `CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS` (default: `false`)
|
|
40
|
+
|
|
41
|
+
## Action usage
|
|
42
|
+
|
|
43
|
+
You can invoke through options or natural text:
|
|
44
|
+
|
|
45
|
+
- `workbench run check`
|
|
46
|
+
- `ccw pre_review_local`
|
|
47
|
+
|
|
48
|
+
## Security model
|
|
49
|
+
|
|
50
|
+
- Uses `spawn` with `shell: false`
|
|
51
|
+
- Enforces workflow allowlist
|
|
52
|
+
- Enforces workspace-root cwd boundaries
|
|
53
|
+
- Enforces timeout and output caps
|
|
54
|
+
- Serializes runs to avoid overlapping command execution
|
|
55
|
+
- Mutating workflows require explicit opt-in
|
|
56
|
+
|
|
57
|
+
## Claude Code construction notes
|
|
58
|
+
|
|
59
|
+
Claude Code extension points are primarily MCP servers, hooks, custom slash commands, settings/policies, and subagents. This plugin is designed as a runtime-side companion that can back those extension points with deterministic repository workflows.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,896 @@
|
|
|
1
|
+
// src/plugin.ts
|
|
2
|
+
import { logger as logger3 } from "@elizaos/core";
|
|
3
|
+
import { z as z2 } from "zod";
|
|
4
|
+
|
|
5
|
+
// src/actions/list-workflows.ts
|
|
6
|
+
function toText(service) {
|
|
7
|
+
const workflows = service.listWorkflows();
|
|
8
|
+
if (workflows.length === 0) {
|
|
9
|
+
return "No workbench workflows are available.";
|
|
10
|
+
}
|
|
11
|
+
const lines = ["Available workbench workflows:"];
|
|
12
|
+
for (const workflow of workflows) {
|
|
13
|
+
const mutating = workflow.mutatesRepo ? " [mutates repo]" : "";
|
|
14
|
+
const disabled = workflow.enabled ? "" : " [disabled]";
|
|
15
|
+
lines.push(`- ${workflow.id}${mutating}${disabled}: ${workflow.description}`);
|
|
16
|
+
}
|
|
17
|
+
return lines.join(`
|
|
18
|
+
`);
|
|
19
|
+
}
|
|
20
|
+
var claudeCodeWorkbenchListAction = {
|
|
21
|
+
name: "CLAUDE_CODE_WORKBENCH_LIST",
|
|
22
|
+
similes: ["LIST_WORKBENCH_WORKFLOWS", "WORKBENCH_LIST", "CCW_LIST"],
|
|
23
|
+
description: "List available Claude Code workbench workflows.",
|
|
24
|
+
validate: async (runtime, _message, _state) => {
|
|
25
|
+
return Boolean(runtime.getService("claude_code_workbench"));
|
|
26
|
+
},
|
|
27
|
+
handler: async (runtime, message, _state, _options = {}, callback) => {
|
|
28
|
+
const service = runtime.getService("claude_code_workbench");
|
|
29
|
+
if (!service) {
|
|
30
|
+
const error = "Claude Code workbench service is not available. Ensure plugin-claude-code-workbench is enabled.";
|
|
31
|
+
if (callback) {
|
|
32
|
+
await callback({ text: error, source: message.content.source });
|
|
33
|
+
}
|
|
34
|
+
return { success: false, error };
|
|
35
|
+
}
|
|
36
|
+
const text = toText(service);
|
|
37
|
+
if (callback) {
|
|
38
|
+
await callback({ text, source: message.content.source });
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
success: true,
|
|
42
|
+
text,
|
|
43
|
+
data: { workflows: service.listWorkflows() }
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
examples: [
|
|
47
|
+
[
|
|
48
|
+
{
|
|
49
|
+
name: "{{name1}}",
|
|
50
|
+
content: {
|
|
51
|
+
text: "List workbench workflows"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "{{name2}}",
|
|
56
|
+
content: {
|
|
57
|
+
text: "Available workbench workflows:",
|
|
58
|
+
actions: ["CLAUDE_CODE_WORKBENCH_LIST"]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
]
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/actions/run-workflow.ts
|
|
66
|
+
import {
|
|
67
|
+
logger
|
|
68
|
+
} from "@elizaos/core";
|
|
69
|
+
function normalizeString(value) {
|
|
70
|
+
if (typeof value !== "string") {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const normalized = value.trim();
|
|
74
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
75
|
+
}
|
|
76
|
+
function extractRunInput(message, options) {
|
|
77
|
+
const workflow = normalizeString(options.workflow);
|
|
78
|
+
const base = {
|
|
79
|
+
workflow: workflow ?? "",
|
|
80
|
+
cwd: normalizeString(options.cwd),
|
|
81
|
+
stdin: normalizeString(options.stdin)
|
|
82
|
+
};
|
|
83
|
+
if (workflow) {
|
|
84
|
+
return base;
|
|
85
|
+
}
|
|
86
|
+
const rawText = normalizeString(message.content?.text);
|
|
87
|
+
if (!rawText) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const match = rawText.match(/^(?:\/)?(?:workbench|claude-workbench|ccw)\s+(?:run\s+)?([a-zA-Z0-9._-]+)/i);
|
|
91
|
+
if (!match?.[1]) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
...base,
|
|
96
|
+
workflow: match[1]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function summarizeResult(result) {
|
|
100
|
+
const status = result.ok ? `✅ Workflow ${result.workflow} completed.` : `❌ Workflow ${result.workflow} failed.`;
|
|
101
|
+
const outputSource = (result.ok ? result.stdout : result.stderr || result.stdout).trim();
|
|
102
|
+
if (!outputSource) {
|
|
103
|
+
return status;
|
|
104
|
+
}
|
|
105
|
+
const preview = outputSource.slice(0, 800);
|
|
106
|
+
const suffix = outputSource.length > 800 ? `
|
|
107
|
+
…` : "";
|
|
108
|
+
return `${status}
|
|
109
|
+
|
|
110
|
+
${preview}${suffix}`;
|
|
111
|
+
}
|
|
112
|
+
var claudeCodeWorkbenchRunAction = {
|
|
113
|
+
name: "CLAUDE_CODE_WORKBENCH_RUN",
|
|
114
|
+
similes: ["RUN_WORKBENCH_WORKFLOW", "WORKBENCH_RUN", "CCW_RUN"],
|
|
115
|
+
description: "Run an allowlisted repo workflow through the Claude Code workbench service.",
|
|
116
|
+
validate: async (runtime, _message, _state) => {
|
|
117
|
+
return Boolean(runtime.getService("claude_code_workbench"));
|
|
118
|
+
},
|
|
119
|
+
handler: async (runtime, message, _state, options = {}, callback) => {
|
|
120
|
+
const service = runtime.getService("claude_code_workbench");
|
|
121
|
+
if (!service) {
|
|
122
|
+
const error = "Claude Code workbench service is not available. Ensure plugin-claude-code-workbench is enabled.";
|
|
123
|
+
if (callback) {
|
|
124
|
+
await callback({ text: error, source: message.content.source });
|
|
125
|
+
}
|
|
126
|
+
return { success: false, error };
|
|
127
|
+
}
|
|
128
|
+
const runInput = extractRunInput(message, options);
|
|
129
|
+
if (!runInput) {
|
|
130
|
+
const error = "No workflow provided. Pass `workflow` in options or use message text like `workbench run check`.";
|
|
131
|
+
if (callback) {
|
|
132
|
+
await callback({ text: error, source: message.content.source });
|
|
133
|
+
}
|
|
134
|
+
return { success: false, error };
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const result = await service.run(runInput);
|
|
138
|
+
const text = summarizeResult(result);
|
|
139
|
+
if (callback) {
|
|
140
|
+
await callback({ text, source: message.content.source });
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
success: result.ok,
|
|
144
|
+
text,
|
|
145
|
+
data: { ...result },
|
|
146
|
+
...result.ok ? {} : {
|
|
147
|
+
error: result.stderr || `Workflow ${result.workflow} exited with code ${String(result.exitCode)}`
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
} catch (error) {
|
|
151
|
+
const messageText = error instanceof Error ? error.message : `Unknown workbench error: ${String(error)}`;
|
|
152
|
+
logger.error(`CLAUDE_CODE_WORKBENCH_RUN failed: ${messageText}`);
|
|
153
|
+
if (callback) {
|
|
154
|
+
await callback({ text: messageText, source: message.content.source });
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
success: false,
|
|
158
|
+
error: messageText
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
examples: [
|
|
163
|
+
[
|
|
164
|
+
{
|
|
165
|
+
name: "{{name1}}",
|
|
166
|
+
content: {
|
|
167
|
+
text: "workbench run check"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: "{{name2}}",
|
|
172
|
+
content: {
|
|
173
|
+
text: "✅ Workflow check completed.",
|
|
174
|
+
actions: ["CLAUDE_CODE_WORKBENCH_RUN"]
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
]
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// src/config.ts
|
|
182
|
+
import path from "node:path";
|
|
183
|
+
import { z } from "zod";
|
|
184
|
+
|
|
185
|
+
// src/workflows.ts
|
|
186
|
+
var DEFAULT_WORKFLOWS = [
|
|
187
|
+
{
|
|
188
|
+
id: "repo_status",
|
|
189
|
+
title: "Repo Status",
|
|
190
|
+
description: "Show git status with branch summary.",
|
|
191
|
+
category: "repo",
|
|
192
|
+
command: "git",
|
|
193
|
+
args: ["status", "--short", "--branch"],
|
|
194
|
+
mutatesRepo: false
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: "repo_diff_stat",
|
|
198
|
+
title: "Repo Diff Stat",
|
|
199
|
+
description: "Show unstaged diff statistics.",
|
|
200
|
+
category: "repo",
|
|
201
|
+
command: "git",
|
|
202
|
+
args: ["diff", "--stat"],
|
|
203
|
+
mutatesRepo: false
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: "repo_diff_cached",
|
|
207
|
+
title: "Repo Diff Cached",
|
|
208
|
+
description: "Show staged diff statistics.",
|
|
209
|
+
category: "repo",
|
|
210
|
+
command: "git",
|
|
211
|
+
args: ["diff", "--cached", "--stat"],
|
|
212
|
+
mutatesRepo: false
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: "recent_commits",
|
|
216
|
+
title: "Recent Commits",
|
|
217
|
+
description: "Show recent commit history.",
|
|
218
|
+
category: "repo",
|
|
219
|
+
command: "git",
|
|
220
|
+
args: ["log", "--oneline", "-n", "20"],
|
|
221
|
+
mutatesRepo: false
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
id: "check",
|
|
225
|
+
title: "Check",
|
|
226
|
+
description: "Run project check (typecheck + lint).",
|
|
227
|
+
category: "quality",
|
|
228
|
+
command: "bun",
|
|
229
|
+
args: ["run", "check"],
|
|
230
|
+
mutatesRepo: false
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: "typecheck",
|
|
234
|
+
title: "Typecheck",
|
|
235
|
+
description: "Run TypeScript typecheck.",
|
|
236
|
+
category: "quality",
|
|
237
|
+
command: "bun",
|
|
238
|
+
args: ["run", "typecheck"],
|
|
239
|
+
mutatesRepo: false
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
id: "lint",
|
|
243
|
+
title: "Lint",
|
|
244
|
+
description: "Run Biome lint checks.",
|
|
245
|
+
category: "quality",
|
|
246
|
+
command: "bun",
|
|
247
|
+
args: ["run", "lint"],
|
|
248
|
+
mutatesRepo: false
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: "test_once",
|
|
252
|
+
title: "Test Once",
|
|
253
|
+
description: "Run Vitest suite one time.",
|
|
254
|
+
category: "test",
|
|
255
|
+
command: "bun",
|
|
256
|
+
args: ["run", "test:once"],
|
|
257
|
+
mutatesRepo: false
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
id: "test_e2e",
|
|
261
|
+
title: "Test E2E",
|
|
262
|
+
description: "Run E2E tests.",
|
|
263
|
+
category: "test",
|
|
264
|
+
command: "bun",
|
|
265
|
+
args: ["run", "test:e2e"],
|
|
266
|
+
mutatesRepo: false
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
id: "pre_review_local",
|
|
270
|
+
title: "Pre-Review Local",
|
|
271
|
+
description: "Run local pre-review parity workflow.",
|
|
272
|
+
category: "quality",
|
|
273
|
+
command: "bun",
|
|
274
|
+
args: ["run", "pre-review:local"],
|
|
275
|
+
mutatesRepo: false
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: "build_local_plugins",
|
|
279
|
+
title: "Build Local Plugins",
|
|
280
|
+
description: "Build local plugin packages used by this repo.",
|
|
281
|
+
category: "plugins",
|
|
282
|
+
command: "bun",
|
|
283
|
+
args: ["run", "build:local-plugins"],
|
|
284
|
+
mutatesRepo: false
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
id: "build",
|
|
288
|
+
title: "Build",
|
|
289
|
+
description: "Run full monorepo build.",
|
|
290
|
+
category: "build",
|
|
291
|
+
command: "bun",
|
|
292
|
+
args: ["run", "build"],
|
|
293
|
+
mutatesRepo: false
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: "docs_build",
|
|
297
|
+
title: "Docs Build",
|
|
298
|
+
description: "Validate docs build and broken links.",
|
|
299
|
+
category: "docs",
|
|
300
|
+
command: "bun",
|
|
301
|
+
args: ["run", "docs:build"],
|
|
302
|
+
mutatesRepo: false
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
id: "format_fix",
|
|
306
|
+
title: "Format Fix",
|
|
307
|
+
description: "Apply Biome formatting changes.",
|
|
308
|
+
category: "quality",
|
|
309
|
+
command: "bun",
|
|
310
|
+
args: ["run", "format:fix"],
|
|
311
|
+
mutatesRepo: true
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
id: "lint_fix",
|
|
315
|
+
title: "Lint Fix",
|
|
316
|
+
description: "Apply Biome lint autofixes.",
|
|
317
|
+
category: "quality",
|
|
318
|
+
command: "bun",
|
|
319
|
+
args: ["run", "lint:fix"],
|
|
320
|
+
mutatesRepo: true
|
|
321
|
+
}
|
|
322
|
+
];
|
|
323
|
+
function normalizeWorkflowId(workflowId) {
|
|
324
|
+
return workflowId.trim().toLowerCase().replace(/[\s.]+/g, "_").replace(/[^a-z0-9_-]/g, "");
|
|
325
|
+
}
|
|
326
|
+
function listDefaultWorkflows() {
|
|
327
|
+
return DEFAULT_WORKFLOWS.map((workflow) => ({
|
|
328
|
+
...workflow,
|
|
329
|
+
args: [...workflow.args]
|
|
330
|
+
}));
|
|
331
|
+
}
|
|
332
|
+
function getDefaultWorkflowIds() {
|
|
333
|
+
return DEFAULT_WORKFLOWS.map((workflow) => workflow.id);
|
|
334
|
+
}
|
|
335
|
+
function findDefaultWorkflowById(workflowId) {
|
|
336
|
+
const normalized = normalizeWorkflowId(workflowId);
|
|
337
|
+
return DEFAULT_WORKFLOWS.find((workflow) => workflow.id === normalized);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/config.ts
|
|
341
|
+
var DEFAULT_TIMEOUT_MS = 10 * 60000;
|
|
342
|
+
var DEFAULT_MAX_OUTPUT_CHARS = 120000;
|
|
343
|
+
var DEFAULT_MAX_STDIN_BYTES = 64 * 1024;
|
|
344
|
+
var MIN_TIMEOUT_MS = 500;
|
|
345
|
+
var MAX_TIMEOUT_MS = 60 * 60000;
|
|
346
|
+
var MIN_OUTPUT_CHARS = 512;
|
|
347
|
+
var MAX_OUTPUT_CHARS = 1e6;
|
|
348
|
+
var MIN_STDIN_BYTES = 0;
|
|
349
|
+
var MAX_STDIN_BYTES = 5 * 1024 * 1024;
|
|
350
|
+
function parseBoolean(value) {
|
|
351
|
+
if (value === undefined || value === null || value === "") {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (typeof value === "boolean") {
|
|
355
|
+
return value;
|
|
356
|
+
}
|
|
357
|
+
if (typeof value === "number") {
|
|
358
|
+
if (value === 1)
|
|
359
|
+
return true;
|
|
360
|
+
if (value === 0)
|
|
361
|
+
return false;
|
|
362
|
+
throw new Error("CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS must be 0 or 1 when set as a number.");
|
|
363
|
+
}
|
|
364
|
+
if (typeof value === "string") {
|
|
365
|
+
const normalized = value.trim().toLowerCase();
|
|
366
|
+
if (["1", "true", "yes", "on"].includes(normalized))
|
|
367
|
+
return true;
|
|
368
|
+
if (["0", "false", "no", "off"].includes(normalized))
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
throw new Error("CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS must be a boolean-like value.");
|
|
372
|
+
}
|
|
373
|
+
var allowedWorkflowsSchema = z.union([z.array(z.string()), z.string()]).optional().transform((value) => {
|
|
374
|
+
if (!value) {
|
|
375
|
+
return ["*"];
|
|
376
|
+
}
|
|
377
|
+
const raw = Array.isArray(value) ? value : value.split(",");
|
|
378
|
+
const normalized = raw.map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) => entry === "*" ? "*" : normalizeWorkflowId(entry));
|
|
379
|
+
if (normalized.length === 0) {
|
|
380
|
+
return ["*"];
|
|
381
|
+
}
|
|
382
|
+
return Array.from(new Set(normalized));
|
|
383
|
+
});
|
|
384
|
+
var claudeCodeWorkbenchConfigSchema = z.object({
|
|
385
|
+
CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT: z.string().trim().min(1).optional(),
|
|
386
|
+
CLAUDE_CODE_WORKBENCH_TIMEOUT_MS: z.coerce.number().int().min(MIN_TIMEOUT_MS).max(MAX_TIMEOUT_MS).default(DEFAULT_TIMEOUT_MS),
|
|
387
|
+
CLAUDE_CODE_WORKBENCH_MAX_OUTPUT_CHARS: z.coerce.number().int().min(MIN_OUTPUT_CHARS).max(MAX_OUTPUT_CHARS).default(DEFAULT_MAX_OUTPUT_CHARS),
|
|
388
|
+
CLAUDE_CODE_WORKBENCH_MAX_STDIN_BYTES: z.coerce.number().int().min(MIN_STDIN_BYTES).max(MAX_STDIN_BYTES).default(DEFAULT_MAX_STDIN_BYTES),
|
|
389
|
+
CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS: allowedWorkflowsSchema,
|
|
390
|
+
CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS: z.preprocess(parseBoolean, z.boolean()).default(false)
|
|
391
|
+
});
|
|
392
|
+
function isWorkflowAllowed(workflowId, allowedWorkflowIds) {
|
|
393
|
+
const normalized = normalizeWorkflowId(workflowId);
|
|
394
|
+
return allowedWorkflowIds.some((entry) => {
|
|
395
|
+
if (entry === "*") {
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
return normalizeWorkflowId(entry) === normalized;
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
function loadClaudeCodeWorkbenchConfig(raw) {
|
|
402
|
+
const parsed = claudeCodeWorkbenchConfigSchema.parse(raw);
|
|
403
|
+
const allowedWorkflowIds = parsed.CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS?.length > 0 ? parsed.CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS : ["*"];
|
|
404
|
+
return {
|
|
405
|
+
workspaceRoot: path.resolve(parsed.CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT ?? process.cwd()),
|
|
406
|
+
timeoutMs: parsed.CLAUDE_CODE_WORKBENCH_TIMEOUT_MS,
|
|
407
|
+
maxOutputChars: parsed.CLAUDE_CODE_WORKBENCH_MAX_OUTPUT_CHARS,
|
|
408
|
+
maxStdinBytes: parsed.CLAUDE_CODE_WORKBENCH_MAX_STDIN_BYTES,
|
|
409
|
+
allowedWorkflowIds,
|
|
410
|
+
enableMutatingWorkflows: parsed.CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
var DEFAULT_WORKBENCH_WORKFLOWS = getDefaultWorkflowIds();
|
|
414
|
+
|
|
415
|
+
// src/providers/status.ts
|
|
416
|
+
var claudeCodeWorkbenchStatusProvider = {
|
|
417
|
+
name: "CLAUDE_CODE_WORKBENCH_STATUS",
|
|
418
|
+
description: "Provides Claude Code workbench availability, workflow policy, and recent run metadata.",
|
|
419
|
+
get: async (runtime, _message, _state) => {
|
|
420
|
+
const service = runtime.getService("claude_code_workbench");
|
|
421
|
+
if (!service) {
|
|
422
|
+
return {
|
|
423
|
+
text: "Claude Code workbench plugin is not active (service not found).",
|
|
424
|
+
values: { workbenchAvailable: false },
|
|
425
|
+
data: { available: false }
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
const status = service.getStatus();
|
|
429
|
+
const lines = [
|
|
430
|
+
`Workbench availability: ${status.available ? "available" : "unavailable"}`,
|
|
431
|
+
`Workflow count: ${status.workflows.length}`,
|
|
432
|
+
`Workspace root: ${status.workspaceRoot}`,
|
|
433
|
+
`Timeout: ${status.timeoutMs}ms`,
|
|
434
|
+
`Output cap: ${status.maxOutputChars} chars`,
|
|
435
|
+
`Mutating workflows: ${status.enableMutatingWorkflows ? "enabled" : "disabled"}`
|
|
436
|
+
];
|
|
437
|
+
if (status.lastRunAt) {
|
|
438
|
+
lines.push(`Last run: ${new Date(status.lastRunAt).toISOString()}`);
|
|
439
|
+
}
|
|
440
|
+
if (status.lastWorkflow) {
|
|
441
|
+
lines.push(`Last workflow: ${status.lastWorkflow}`);
|
|
442
|
+
}
|
|
443
|
+
if (typeof status.lastExitCode !== "undefined") {
|
|
444
|
+
lines.push(`Last exit code: ${String(status.lastExitCode)}`);
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
text: lines.join(`
|
|
448
|
+
`),
|
|
449
|
+
values: {
|
|
450
|
+
workbenchAvailable: status.available,
|
|
451
|
+
workbenchRunning: status.running
|
|
452
|
+
},
|
|
453
|
+
data: { ...status }
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
// src/routes.ts
|
|
459
|
+
import {
|
|
460
|
+
logger as logger2
|
|
461
|
+
} from "@elizaos/core";
|
|
462
|
+
function getService(runtime) {
|
|
463
|
+
const service = runtime.getService("claude_code_workbench");
|
|
464
|
+
if (!service) {
|
|
465
|
+
throw new Error("Claude Code workbench service not available");
|
|
466
|
+
}
|
|
467
|
+
return service;
|
|
468
|
+
}
|
|
469
|
+
function toStringOrUndefined(value) {
|
|
470
|
+
if (typeof value !== "string") {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const normalized = value.trim();
|
|
474
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
475
|
+
}
|
|
476
|
+
function parseRunInput(body) {
|
|
477
|
+
if (!body || typeof body !== "object") {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
const record = body;
|
|
481
|
+
const workflow = toStringOrUndefined(record.workflow);
|
|
482
|
+
if (!workflow) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
workflow,
|
|
487
|
+
cwd: toStringOrUndefined(record.cwd),
|
|
488
|
+
stdin: toStringOrUndefined(record.stdin)
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
var statusRoute = {
|
|
492
|
+
name: "claude-code-workbench-status",
|
|
493
|
+
public: false,
|
|
494
|
+
path: "/status",
|
|
495
|
+
type: "GET",
|
|
496
|
+
handler: async (_req, res, runtime) => {
|
|
497
|
+
try {
|
|
498
|
+
const service = getService(runtime);
|
|
499
|
+
res.json({ ok: true, status: service.getStatus() });
|
|
500
|
+
} catch (error) {
|
|
501
|
+
res.status(500).json({
|
|
502
|
+
ok: false,
|
|
503
|
+
error: error instanceof Error ? error.message : String(error)
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
var workflowsRoute = {
|
|
509
|
+
name: "claude-code-workbench-workflows",
|
|
510
|
+
public: false,
|
|
511
|
+
path: "/workflows",
|
|
512
|
+
type: "GET",
|
|
513
|
+
handler: async (_req, res, runtime) => {
|
|
514
|
+
try {
|
|
515
|
+
const service = getService(runtime);
|
|
516
|
+
res.json({ ok: true, workflows: service.listWorkflows() });
|
|
517
|
+
} catch (error) {
|
|
518
|
+
res.status(500).json({
|
|
519
|
+
ok: false,
|
|
520
|
+
error: error instanceof Error ? error.message : String(error)
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
var runRoute = {
|
|
526
|
+
name: "claude-code-workbench-run",
|
|
527
|
+
public: false,
|
|
528
|
+
path: "/run",
|
|
529
|
+
type: "POST",
|
|
530
|
+
handler: async (req, res, runtime) => {
|
|
531
|
+
try {
|
|
532
|
+
const input = parseRunInput(req.body);
|
|
533
|
+
if (!input) {
|
|
534
|
+
res.status(400).json({
|
|
535
|
+
ok: false,
|
|
536
|
+
error: "Invalid run request. Provide non-empty `workflow` in request body."
|
|
537
|
+
});
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const service = getService(runtime);
|
|
541
|
+
const result = await service.run(input);
|
|
542
|
+
res.status(result.ok ? 200 : 500).json({ ok: result.ok, result });
|
|
543
|
+
} catch (error) {
|
|
544
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
545
|
+
logger2.error(`Claude Code workbench route error: ${message}`);
|
|
546
|
+
res.status(500).json({ ok: false, error: message });
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
var claudeCodeWorkbenchRoutes = [
|
|
551
|
+
statusRoute,
|
|
552
|
+
workflowsRoute,
|
|
553
|
+
runRoute
|
|
554
|
+
];
|
|
555
|
+
|
|
556
|
+
// src/services/workbench-service.ts
|
|
557
|
+
import { spawn } from "node:child_process";
|
|
558
|
+
import fs from "node:fs";
|
|
559
|
+
import path2 from "node:path";
|
|
560
|
+
import { Service } from "@elizaos/core";
|
|
561
|
+
function appendWithLimit(current, chunk, limit) {
|
|
562
|
+
if (current.length >= limit) {
|
|
563
|
+
return { value: current, truncated: true };
|
|
564
|
+
}
|
|
565
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
566
|
+
const remaining = limit - current.length;
|
|
567
|
+
if (text.length <= remaining) {
|
|
568
|
+
return { value: current + text, truncated: false };
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
value: current + text.slice(0, remaining),
|
|
572
|
+
truncated: true
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
function withTruncationSuffix(value, truncated) {
|
|
576
|
+
if (!truncated) {
|
|
577
|
+
return value;
|
|
578
|
+
}
|
|
579
|
+
const suffix = `
|
|
580
|
+
...[truncated by plugin-claude-code-workbench]`;
|
|
581
|
+
return value.endsWith(suffix) ? value : `${value}${suffix}`;
|
|
582
|
+
}
|
|
583
|
+
function cleanOptionalValue(value) {
|
|
584
|
+
const cleaned = value?.trim();
|
|
585
|
+
return cleaned && cleaned.length > 0 ? cleaned : undefined;
|
|
586
|
+
}
|
|
587
|
+
function isPathInside(baseDir, targetDir) {
|
|
588
|
+
if (baseDir === targetDir) {
|
|
589
|
+
return true;
|
|
590
|
+
}
|
|
591
|
+
const normalizedBase = process.platform === "win32" ? baseDir.toLowerCase() : baseDir;
|
|
592
|
+
const normalizedTarget = process.platform === "win32" ? targetDir.toLowerCase() : targetDir;
|
|
593
|
+
return normalizedTarget.startsWith(`${normalizedBase}${path2.sep}`);
|
|
594
|
+
}
|
|
595
|
+
function toSummary(workflow, enableMutatingWorkflows) {
|
|
596
|
+
return {
|
|
597
|
+
id: workflow.id,
|
|
598
|
+
title: workflow.title,
|
|
599
|
+
description: workflow.description,
|
|
600
|
+
category: workflow.category,
|
|
601
|
+
mutatesRepo: workflow.mutatesRepo,
|
|
602
|
+
enabled: !workflow.mutatesRepo || enableMutatingWorkflows,
|
|
603
|
+
commandPreview: `${workflow.command} ${workflow.args.join(" ")}`.trim()
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
class ClaudeCodeWorkbenchService extends Service {
|
|
608
|
+
static serviceType = "claude_code_workbench";
|
|
609
|
+
capabilityDescription = "Run allowlisted repository workflows for this monorepo with safe process controls.";
|
|
610
|
+
runtimeConfig;
|
|
611
|
+
workflows;
|
|
612
|
+
runQueue = Promise.resolve();
|
|
613
|
+
running = false;
|
|
614
|
+
available = true;
|
|
615
|
+
lastRunAt;
|
|
616
|
+
lastWorkflow;
|
|
617
|
+
lastExitCode;
|
|
618
|
+
lastDurationMs;
|
|
619
|
+
lastError;
|
|
620
|
+
constructor(runtime, config, workflows) {
|
|
621
|
+
super(runtime);
|
|
622
|
+
this.runtimeConfig = config ?? loadClaudeCodeWorkbenchConfig(process.env);
|
|
623
|
+
const sourceWorkflows = workflows ?? listDefaultWorkflows();
|
|
624
|
+
const filtered = sourceWorkflows.filter((workflow) => isWorkflowAllowed(workflow.id, this.runtimeConfig.allowedWorkflowIds));
|
|
625
|
+
this.workflows = new Map(filtered.map((workflow) => [
|
|
626
|
+
normalizeWorkflowId(workflow.id),
|
|
627
|
+
{
|
|
628
|
+
...workflow,
|
|
629
|
+
id: normalizeWorkflowId(workflow.id),
|
|
630
|
+
args: [...workflow.args]
|
|
631
|
+
}
|
|
632
|
+
]));
|
|
633
|
+
}
|
|
634
|
+
static async start(runtime) {
|
|
635
|
+
return new ClaudeCodeWorkbenchService(runtime);
|
|
636
|
+
}
|
|
637
|
+
static async stop(runtime) {
|
|
638
|
+
const service = runtime.getService(ClaudeCodeWorkbenchService.serviceType);
|
|
639
|
+
if (service && "stop" in service && typeof service.stop === "function") {
|
|
640
|
+
await service.stop();
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
async stop() {
|
|
644
|
+
this.running = false;
|
|
645
|
+
}
|
|
646
|
+
listWorkflows() {
|
|
647
|
+
return Array.from(this.workflows.values()).sort((left, right) => left.id.localeCompare(right.id)).map((workflow) => toSummary(workflow, this.runtimeConfig.enableMutatingWorkflows));
|
|
648
|
+
}
|
|
649
|
+
getStatus() {
|
|
650
|
+
return {
|
|
651
|
+
available: this.available,
|
|
652
|
+
running: this.running,
|
|
653
|
+
workspaceRoot: this.runtimeConfig.workspaceRoot,
|
|
654
|
+
timeoutMs: this.runtimeConfig.timeoutMs,
|
|
655
|
+
maxOutputChars: this.runtimeConfig.maxOutputChars,
|
|
656
|
+
maxStdinBytes: this.runtimeConfig.maxStdinBytes,
|
|
657
|
+
allowedWorkflowIds: [...this.runtimeConfig.allowedWorkflowIds],
|
|
658
|
+
enableMutatingWorkflows: this.runtimeConfig.enableMutatingWorkflows,
|
|
659
|
+
workflows: this.listWorkflows(),
|
|
660
|
+
lastRunAt: this.lastRunAt,
|
|
661
|
+
lastWorkflow: this.lastWorkflow,
|
|
662
|
+
lastExitCode: this.lastExitCode,
|
|
663
|
+
lastDurationMs: this.lastDurationMs,
|
|
664
|
+
lastError: this.lastError
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
async run(input) {
|
|
668
|
+
const task = this.runQueue.then(() => this.executeRun(input), () => this.executeRun(input));
|
|
669
|
+
this.runQueue = task.then(() => {
|
|
670
|
+
return;
|
|
671
|
+
}, () => {
|
|
672
|
+
return;
|
|
673
|
+
});
|
|
674
|
+
return task;
|
|
675
|
+
}
|
|
676
|
+
resolveWorkingDirectory(inputCwd) {
|
|
677
|
+
const workspaceRootPath = path2.resolve(this.runtimeConfig.workspaceRoot);
|
|
678
|
+
const requestedPath = path2.resolve(cleanOptionalValue(inputCwd) ?? workspaceRootPath);
|
|
679
|
+
let workspaceRoot;
|
|
680
|
+
let requested;
|
|
681
|
+
try {
|
|
682
|
+
workspaceRoot = fs.realpathSync(workspaceRootPath);
|
|
683
|
+
} catch (error) {
|
|
684
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
685
|
+
throw new Error(`CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT is not accessible (${workspaceRootPath}): ${message}`);
|
|
686
|
+
}
|
|
687
|
+
try {
|
|
688
|
+
requested = fs.realpathSync(requestedPath);
|
|
689
|
+
} catch (error) {
|
|
690
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
691
|
+
throw new Error(`Workbench cwd is not accessible (${requestedPath}): ${message}`);
|
|
692
|
+
}
|
|
693
|
+
if (!isPathInside(workspaceRoot, requested)) {
|
|
694
|
+
throw new Error(`Workbench cwd must stay within CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT (${workspaceRoot}). Received: ${requested}`);
|
|
695
|
+
}
|
|
696
|
+
return requested;
|
|
697
|
+
}
|
|
698
|
+
buildChildEnv() {
|
|
699
|
+
const env = {};
|
|
700
|
+
const baseKeys = [
|
|
701
|
+
"PATH",
|
|
702
|
+
"HOME",
|
|
703
|
+
"USERPROFILE",
|
|
704
|
+
"SHELL",
|
|
705
|
+
"COMSPEC",
|
|
706
|
+
"PATHEXT",
|
|
707
|
+
"SystemRoot",
|
|
708
|
+
"TMP",
|
|
709
|
+
"TEMP",
|
|
710
|
+
"TERM",
|
|
711
|
+
"CI",
|
|
712
|
+
"FORCE_COLOR"
|
|
713
|
+
];
|
|
714
|
+
for (const key of baseKeys) {
|
|
715
|
+
const value = process.env[key];
|
|
716
|
+
if (typeof value === "string" && value.length > 0) {
|
|
717
|
+
env[key] = value;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
721
|
+
if (key.startsWith("CLAUDE_CODE_WORKBENCH_") && typeof value === "string" && value.length > 0) {
|
|
722
|
+
env[key] = value;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
return env;
|
|
726
|
+
}
|
|
727
|
+
resolveWorkflow(workflowId) {
|
|
728
|
+
const normalized = normalizeWorkflowId(workflowId);
|
|
729
|
+
const workflow = this.workflows.get(normalized);
|
|
730
|
+
if (!workflow) {
|
|
731
|
+
const allowed = this.listWorkflows().map((entry) => entry.id).join(", ");
|
|
732
|
+
throw new Error(`Unknown workflow "${workflowId}". Allowed workflows: ${allowed || "none"}.`);
|
|
733
|
+
}
|
|
734
|
+
if (workflow.mutatesRepo && !this.runtimeConfig.enableMutatingWorkflows) {
|
|
735
|
+
throw new Error(`Workflow "${workflow.id}" mutates the repository and is disabled. Set CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS=true to enable it.`);
|
|
736
|
+
}
|
|
737
|
+
return workflow;
|
|
738
|
+
}
|
|
739
|
+
async executeRun(input) {
|
|
740
|
+
const workflow = this.resolveWorkflow(input.workflow);
|
|
741
|
+
if (input.stdin && Buffer.byteLength(input.stdin, "utf8") > this.runtimeConfig.maxStdinBytes) {
|
|
742
|
+
throw new Error(`Workbench stdin exceeds limit (${this.runtimeConfig.maxStdinBytes} bytes).`);
|
|
743
|
+
}
|
|
744
|
+
const cwd = this.resolveWorkingDirectory(input.cwd);
|
|
745
|
+
this.running = true;
|
|
746
|
+
this.lastWorkflow = workflow.id;
|
|
747
|
+
const startedAt = Date.now();
|
|
748
|
+
let stdout = "";
|
|
749
|
+
let stderr = "";
|
|
750
|
+
let stdoutTruncated = false;
|
|
751
|
+
let stderrTruncated = false;
|
|
752
|
+
let timedOut = false;
|
|
753
|
+
try {
|
|
754
|
+
const child = spawn(workflow.command, workflow.args, {
|
|
755
|
+
cwd,
|
|
756
|
+
env: this.buildChildEnv(),
|
|
757
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
758
|
+
shell: false
|
|
759
|
+
});
|
|
760
|
+
if (input.stdin) {
|
|
761
|
+
child.stdin?.write(input.stdin);
|
|
762
|
+
}
|
|
763
|
+
child.stdin?.end();
|
|
764
|
+
child.stdout?.on("data", (chunk) => {
|
|
765
|
+
const next = appendWithLimit(stdout, chunk, this.runtimeConfig.maxOutputChars);
|
|
766
|
+
stdout = next.value;
|
|
767
|
+
stdoutTruncated = stdoutTruncated || next.truncated;
|
|
768
|
+
});
|
|
769
|
+
child.stderr?.on("data", (chunk) => {
|
|
770
|
+
const next = appendWithLimit(stderr, chunk, this.runtimeConfig.maxOutputChars);
|
|
771
|
+
stderr = next.value;
|
|
772
|
+
stderrTruncated = stderrTruncated || next.truncated;
|
|
773
|
+
});
|
|
774
|
+
const timeoutHandle = setTimeout(() => {
|
|
775
|
+
timedOut = true;
|
|
776
|
+
try {
|
|
777
|
+
child.kill("SIGTERM");
|
|
778
|
+
} catch {}
|
|
779
|
+
setTimeout(() => {
|
|
780
|
+
try {
|
|
781
|
+
child.kill("SIGKILL");
|
|
782
|
+
} catch {}
|
|
783
|
+
}, 250);
|
|
784
|
+
}, this.runtimeConfig.timeoutMs);
|
|
785
|
+
const exitCode = await new Promise((resolve, reject) => {
|
|
786
|
+
child.once("error", reject);
|
|
787
|
+
child.once("close", (code) => resolve(code));
|
|
788
|
+
}).finally(() => {
|
|
789
|
+
clearTimeout(timeoutHandle);
|
|
790
|
+
});
|
|
791
|
+
if (timedOut) {
|
|
792
|
+
const timeoutMessage = `Workflow timed out after ${this.runtimeConfig.timeoutMs}ms.`;
|
|
793
|
+
const next = appendWithLimit(stderr, timeoutMessage, this.runtimeConfig.maxOutputChars);
|
|
794
|
+
stderr = next.value;
|
|
795
|
+
stderrTruncated = stderrTruncated || next.truncated;
|
|
796
|
+
}
|
|
797
|
+
const durationMs = Date.now() - startedAt;
|
|
798
|
+
const ok = !timedOut && exitCode === 0;
|
|
799
|
+
this.available = true;
|
|
800
|
+
this.lastRunAt = startedAt;
|
|
801
|
+
this.lastExitCode = exitCode;
|
|
802
|
+
this.lastDurationMs = durationMs;
|
|
803
|
+
this.lastError = ok ? undefined : stderr || `Process exited with code ${String(exitCode)}`;
|
|
804
|
+
return {
|
|
805
|
+
ok,
|
|
806
|
+
workflow: workflow.id,
|
|
807
|
+
command: workflow.command,
|
|
808
|
+
args: [...workflow.args],
|
|
809
|
+
exitCode,
|
|
810
|
+
stdout: withTruncationSuffix(stdout, stdoutTruncated),
|
|
811
|
+
stderr: withTruncationSuffix(stderr, stderrTruncated),
|
|
812
|
+
durationMs,
|
|
813
|
+
timedOut,
|
|
814
|
+
stdoutTruncated,
|
|
815
|
+
stderrTruncated
|
|
816
|
+
};
|
|
817
|
+
} catch (error) {
|
|
818
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
819
|
+
this.lastRunAt = startedAt;
|
|
820
|
+
this.lastExitCode = null;
|
|
821
|
+
this.lastDurationMs = Date.now() - startedAt;
|
|
822
|
+
this.lastError = message;
|
|
823
|
+
const maybeErrno = error;
|
|
824
|
+
if (maybeErrno?.code === "ENOENT") {
|
|
825
|
+
this.available = false;
|
|
826
|
+
}
|
|
827
|
+
throw error;
|
|
828
|
+
} finally {
|
|
829
|
+
this.running = false;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// src/plugin.ts
|
|
835
|
+
var claudeCodeWorkbenchPlugin = {
|
|
836
|
+
name: "claude-code-workbench",
|
|
837
|
+
description: "Claude Code companion for this repository. Adds secure, allowlisted repo workflows via service, actions, provider, and API routes.",
|
|
838
|
+
get config() {
|
|
839
|
+
return {
|
|
840
|
+
CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT: process.env.CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT ?? null,
|
|
841
|
+
CLAUDE_CODE_WORKBENCH_TIMEOUT_MS: process.env.CLAUDE_CODE_WORKBENCH_TIMEOUT_MS ?? null,
|
|
842
|
+
CLAUDE_CODE_WORKBENCH_MAX_OUTPUT_CHARS: process.env.CLAUDE_CODE_WORKBENCH_MAX_OUTPUT_CHARS ?? null,
|
|
843
|
+
CLAUDE_CODE_WORKBENCH_MAX_STDIN_BYTES: process.env.CLAUDE_CODE_WORKBENCH_MAX_STDIN_BYTES ?? null,
|
|
844
|
+
CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS: process.env.CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS ?? null,
|
|
845
|
+
CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS: process.env.CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS ?? null
|
|
846
|
+
};
|
|
847
|
+
},
|
|
848
|
+
async init(config) {
|
|
849
|
+
logger3.info("Claude Code workbench: initializing plugin");
|
|
850
|
+
try {
|
|
851
|
+
const normalized = loadClaudeCodeWorkbenchConfig(config);
|
|
852
|
+
for (const [key, value] of Object.entries(config)) {
|
|
853
|
+
if (!key.startsWith("CLAUDE_CODE_WORKBENCH_")) {
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
if (value) {
|
|
857
|
+
process.env[key] = value;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
logger3.info(`Claude Code workbench: initialized (root=${normalized.workspaceRoot}, workflows=${normalized.allowedWorkflowIds.join(",")})`);
|
|
861
|
+
} catch (error) {
|
|
862
|
+
if (error instanceof z2.ZodError) {
|
|
863
|
+
const issues = error.issues?.map((issue) => issue.message).join(", ") || "Unknown validation error";
|
|
864
|
+
throw new Error(`Claude Code workbench configuration error: ${issues}`);
|
|
865
|
+
}
|
|
866
|
+
throw new Error(`Claude Code workbench initialization failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
867
|
+
}
|
|
868
|
+
},
|
|
869
|
+
services: [ClaudeCodeWorkbenchService],
|
|
870
|
+
actions: [claudeCodeWorkbenchRunAction, claudeCodeWorkbenchListAction],
|
|
871
|
+
providers: [claudeCodeWorkbenchStatusProvider],
|
|
872
|
+
routes: claudeCodeWorkbenchRoutes
|
|
873
|
+
};
|
|
874
|
+
|
|
875
|
+
// src/index.ts
|
|
876
|
+
var src_default = claudeCodeWorkbenchPlugin;
|
|
877
|
+
export {
|
|
878
|
+
normalizeWorkflowId,
|
|
879
|
+
loadClaudeCodeWorkbenchConfig,
|
|
880
|
+
listDefaultWorkflows,
|
|
881
|
+
isWorkflowAllowed,
|
|
882
|
+
getDefaultWorkflowIds,
|
|
883
|
+
findDefaultWorkflowById,
|
|
884
|
+
src_default as default,
|
|
885
|
+
claudeCodeWorkbenchStatusProvider,
|
|
886
|
+
claudeCodeWorkbenchRunAction,
|
|
887
|
+
claudeCodeWorkbenchRoutes,
|
|
888
|
+
claudeCodeWorkbenchPlugin,
|
|
889
|
+
claudeCodeWorkbenchListAction,
|
|
890
|
+
claudeCodeWorkbenchConfigSchema,
|
|
891
|
+
DEFAULT_WORKBENCH_WORKFLOWS,
|
|
892
|
+
ClaudeCodeWorkbenchService
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
//# debugId=275C68524586278864756E2164756E21
|
|
896
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/plugin.ts", "../src/actions/list-workflows.ts", "../src/actions/run-workflow.ts", "../src/config.ts", "../src/workflows.ts", "../src/providers/status.ts", "../src/routes.ts", "../src/services/workbench-service.ts", "../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { Plugin } from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport { z } from \"zod\";\nimport { claudeCodeWorkbenchListAction } from \"./actions/list-workflows.ts\";\nimport { claudeCodeWorkbenchRunAction } from \"./actions/run-workflow.ts\";\nimport { loadClaudeCodeWorkbenchConfig } from \"./config.ts\";\nimport { claudeCodeWorkbenchStatusProvider } from \"./providers/status.ts\";\nimport { claudeCodeWorkbenchRoutes } from \"./routes.ts\";\nimport { ClaudeCodeWorkbenchService } from \"./services/workbench-service.ts\";\n\nexport const claudeCodeWorkbenchPlugin: Plugin = {\n name: \"claude-code-workbench\",\n description:\n \"Claude Code companion for this repository. Adds secure, allowlisted repo workflows via service, actions, provider, and API routes.\",\n\n get config() {\n return {\n CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT:\n process.env.CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT ?? null,\n CLAUDE_CODE_WORKBENCH_TIMEOUT_MS:\n process.env.CLAUDE_CODE_WORKBENCH_TIMEOUT_MS ?? null,\n CLAUDE_CODE_WORKBENCH_MAX_OUTPUT_CHARS:\n process.env.CLAUDE_CODE_WORKBENCH_MAX_OUTPUT_CHARS ?? null,\n CLAUDE_CODE_WORKBENCH_MAX_STDIN_BYTES:\n process.env.CLAUDE_CODE_WORKBENCH_MAX_STDIN_BYTES ?? null,\n CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS:\n process.env.CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS ?? null,\n CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS:\n process.env.CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS ?? null,\n };\n },\n\n async init(config: Record<string, string>) {\n logger.info(\"Claude Code workbench: initializing plugin\");\n\n try {\n const normalized = loadClaudeCodeWorkbenchConfig(config);\n\n for (const [key, value] of Object.entries(config)) {\n if (!key.startsWith(\"CLAUDE_CODE_WORKBENCH_\")) {\n continue;\n }\n\n if (value) {\n process.env[key] = value;\n }\n }\n\n logger.info(\n `Claude Code workbench: initialized (root=${normalized.workspaceRoot}, workflows=${normalized.allowedWorkflowIds.join(\",\")})`,\n );\n } catch (error) {\n if (error instanceof z.ZodError) {\n const issues =\n error.issues?.map((issue) => issue.message).join(\", \") ||\n \"Unknown validation error\";\n throw new Error(`Claude Code workbench configuration error: ${issues}`);\n }\n\n throw new Error(\n `Claude Code workbench initialization failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n },\n\n services: [ClaudeCodeWorkbenchService],\n actions: [claudeCodeWorkbenchRunAction, claudeCodeWorkbenchListAction],\n providers: [claudeCodeWorkbenchStatusProvider],\n routes: claudeCodeWorkbenchRoutes,\n};\n\nexport default claudeCodeWorkbenchPlugin;\n",
|
|
6
|
+
"import type {\n Action,\n ActionResult,\n HandlerCallback,\n IAgentRuntime,\n Memory,\n State,\n} from \"@elizaos/core\";\nimport type { ClaudeCodeWorkbenchService } from \"../services/workbench-service.ts\";\n\nfunction toText(service: ClaudeCodeWorkbenchService): string {\n const workflows = service.listWorkflows();\n if (workflows.length === 0) {\n return \"No workbench workflows are available.\";\n }\n\n const lines = [\"Available workbench workflows:\"];\n for (const workflow of workflows) {\n const mutating = workflow.mutatesRepo ? \" [mutates repo]\" : \"\";\n const disabled = workflow.enabled ? \"\" : \" [disabled]\";\n lines.push(\n `- ${workflow.id}${mutating}${disabled}: ${workflow.description}`,\n );\n }\n\n return lines.join(\"\\n\");\n}\n\nexport const claudeCodeWorkbenchListAction: Action = {\n name: \"CLAUDE_CODE_WORKBENCH_LIST\",\n similes: [\"LIST_WORKBENCH_WORKFLOWS\", \"WORKBENCH_LIST\", \"CCW_LIST\"],\n description: \"List available Claude Code workbench workflows.\",\n\n validate: async (\n runtime: IAgentRuntime,\n _message: Memory,\n _state: State | undefined,\n ): Promise<boolean> => {\n return Boolean(runtime.getService(\"claude_code_workbench\"));\n },\n\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state: State | undefined,\n _options: Record<string, unknown> = {},\n callback?: HandlerCallback,\n ): Promise<ActionResult> => {\n const service = runtime.getService(\n \"claude_code_workbench\",\n ) as ClaudeCodeWorkbenchService | null;\n\n if (!service) {\n const error =\n \"Claude Code workbench service is not available. Ensure plugin-claude-code-workbench is enabled.\";\n if (callback) {\n await callback({ text: error, source: message.content.source });\n }\n return { success: false, error };\n }\n\n const text = toText(service);\n\n if (callback) {\n await callback({ text, source: message.content.source });\n }\n\n return {\n success: true,\n text,\n data: { workflows: service.listWorkflows() },\n };\n },\n\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"List workbench workflows\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Available workbench workflows:\",\n actions: [\"CLAUDE_CODE_WORKBENCH_LIST\"],\n },\n },\n ],\n ],\n};\n",
|
|
7
|
+
"import {\n type Action,\n type ActionResult,\n type HandlerCallback,\n type IAgentRuntime,\n logger,\n type Memory,\n type State,\n} from \"@elizaos/core\";\nimport type {\n ClaudeCodeWorkbenchService,\n WorkbenchRunInput,\n WorkbenchRunResult,\n} from \"../services/workbench-service.ts\";\n\ninterface WorkbenchRunActionOptions extends Record<string, unknown> {\n workflow?: string;\n cwd?: string;\n stdin?: string;\n}\n\nfunction normalizeString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n\n const normalized = value.trim();\n return normalized.length > 0 ? normalized : undefined;\n}\n\nexport function extractRunInput(\n message: Memory,\n options: WorkbenchRunActionOptions,\n): WorkbenchRunInput | null {\n const workflow = normalizeString(options.workflow);\n const base: WorkbenchRunInput = {\n workflow: workflow ?? \"\",\n cwd: normalizeString(options.cwd),\n stdin: normalizeString(options.stdin),\n };\n\n if (workflow) {\n return base;\n }\n\n const rawText = normalizeString(message.content?.text);\n if (!rawText) {\n return null;\n }\n\n const match = rawText.match(\n /^(?:\\/)?(?:workbench|claude-workbench|ccw)\\s+(?:run\\s+)?([a-zA-Z0-9._-]+)/i,\n );\n if (!match?.[1]) {\n return null;\n }\n\n return {\n ...base,\n workflow: match[1],\n };\n}\n\nfunction summarizeResult(result: WorkbenchRunResult): string {\n const status = result.ok\n ? `✅ Workflow ${result.workflow} completed.`\n : `❌ Workflow ${result.workflow} failed.`;\n\n const outputSource = (\n result.ok ? result.stdout : result.stderr || result.stdout\n ).trim();\n\n if (!outputSource) {\n return status;\n }\n\n const preview = outputSource.slice(0, 800);\n const suffix = outputSource.length > 800 ? \"\\n…\" : \"\";\n return `${status}\\n\\n${preview}${suffix}`;\n}\n\nexport const claudeCodeWorkbenchRunAction: Action = {\n name: \"CLAUDE_CODE_WORKBENCH_RUN\",\n similes: [\"RUN_WORKBENCH_WORKFLOW\", \"WORKBENCH_RUN\", \"CCW_RUN\"],\n description:\n \"Run an allowlisted repo workflow through the Claude Code workbench service.\",\n\n validate: async (\n runtime: IAgentRuntime,\n _message: Memory,\n _state: State | undefined,\n ): Promise<boolean> => {\n return Boolean(runtime.getService(\"claude_code_workbench\"));\n },\n\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state: State | undefined,\n options: Record<string, unknown> = {},\n callback?: HandlerCallback,\n ): Promise<ActionResult> => {\n const service = runtime.getService(\n \"claude_code_workbench\",\n ) as ClaudeCodeWorkbenchService | null;\n\n if (!service) {\n const error =\n \"Claude Code workbench service is not available. Ensure plugin-claude-code-workbench is enabled.\";\n if (callback) {\n await callback({ text: error, source: message.content.source });\n }\n return { success: false, error };\n }\n\n const runInput = extractRunInput(\n message,\n options as WorkbenchRunActionOptions,\n );\n\n if (!runInput) {\n const error =\n \"No workflow provided. Pass `workflow` in options or use message text like `workbench run check`.\";\n if (callback) {\n await callback({ text: error, source: message.content.source });\n }\n return { success: false, error };\n }\n\n try {\n const result = await service.run(runInput);\n const text = summarizeResult(result);\n\n if (callback) {\n await callback({ text, source: message.content.source });\n }\n\n return {\n success: result.ok,\n text,\n data: { ...result },\n ...(result.ok\n ? {}\n : {\n error:\n result.stderr ||\n `Workflow ${result.workflow} exited with code ${String(result.exitCode)}`,\n }),\n };\n } catch (error) {\n const messageText =\n error instanceof Error\n ? error.message\n : `Unknown workbench error: ${String(error)}`;\n logger.error(`CLAUDE_CODE_WORKBENCH_RUN failed: ${messageText}`);\n\n if (callback) {\n await callback({ text: messageText, source: message.content.source });\n }\n\n return {\n success: false,\n error: messageText,\n };\n }\n },\n\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"workbench run check\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"✅ Workflow check completed.\",\n actions: [\"CLAUDE_CODE_WORKBENCH_RUN\"],\n },\n },\n ],\n ],\n};\n",
|
|
8
|
+
"import path from \"node:path\";\nimport { z } from \"zod\";\nimport { getDefaultWorkflowIds, normalizeWorkflowId } from \"./workflows.ts\";\n\nconst DEFAULT_TIMEOUT_MS = 10 * 60_000;\nconst DEFAULT_MAX_OUTPUT_CHARS = 120_000;\nconst DEFAULT_MAX_STDIN_BYTES = 64 * 1024;\n\nconst MIN_TIMEOUT_MS = 500;\nconst MAX_TIMEOUT_MS = 60 * 60_000;\nconst MIN_OUTPUT_CHARS = 512;\nconst MAX_OUTPUT_CHARS = 1_000_000;\nconst MIN_STDIN_BYTES = 0;\nconst MAX_STDIN_BYTES = 5 * 1024 * 1024;\n\nfunction parseBoolean(value: unknown): boolean | undefined {\n if (value === undefined || value === null || value === \"\") {\n return undefined;\n }\n\n if (typeof value === \"boolean\") {\n return value;\n }\n\n if (typeof value === \"number\") {\n if (value === 1) return true;\n if (value === 0) return false;\n throw new Error(\n \"CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS must be 0 or 1 when set as a number.\",\n );\n }\n\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"1\", \"true\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"0\", \"false\", \"no\", \"off\"].includes(normalized)) return false;\n }\n\n throw new Error(\n \"CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS must be a boolean-like value.\",\n );\n}\n\nconst allowedWorkflowsSchema = z\n .union([z.array(z.string()), z.string()])\n .optional()\n .transform((value): string[] => {\n if (!value) {\n return [\"*\"];\n }\n\n const raw = Array.isArray(value) ? value : value.split(\",\");\n const normalized = raw\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0)\n .map((entry) => (entry === \"*\" ? \"*\" : normalizeWorkflowId(entry)));\n\n if (normalized.length === 0) {\n return [\"*\"];\n }\n\n return Array.from(new Set(normalized));\n });\n\nexport const claudeCodeWorkbenchConfigSchema = z.object({\n CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT: z.string().trim().min(1).optional(),\n CLAUDE_CODE_WORKBENCH_TIMEOUT_MS: z.coerce\n .number()\n .int()\n .min(MIN_TIMEOUT_MS)\n .max(MAX_TIMEOUT_MS)\n .default(DEFAULT_TIMEOUT_MS),\n CLAUDE_CODE_WORKBENCH_MAX_OUTPUT_CHARS: z.coerce\n .number()\n .int()\n .min(MIN_OUTPUT_CHARS)\n .max(MAX_OUTPUT_CHARS)\n .default(DEFAULT_MAX_OUTPUT_CHARS),\n CLAUDE_CODE_WORKBENCH_MAX_STDIN_BYTES: z.coerce\n .number()\n .int()\n .min(MIN_STDIN_BYTES)\n .max(MAX_STDIN_BYTES)\n .default(DEFAULT_MAX_STDIN_BYTES),\n CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS: allowedWorkflowsSchema,\n CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS: z\n .preprocess(parseBoolean, z.boolean())\n .default(false),\n});\n\nexport interface ClaudeCodeWorkbenchConfig {\n workspaceRoot: string;\n timeoutMs: number;\n maxOutputChars: number;\n maxStdinBytes: number;\n allowedWorkflowIds: string[];\n enableMutatingWorkflows: boolean;\n}\n\nexport function isWorkflowAllowed(\n workflowId: string,\n allowedWorkflowIds: string[],\n): boolean {\n const normalized = normalizeWorkflowId(workflowId);\n return allowedWorkflowIds.some((entry) => {\n if (entry === \"*\") {\n return true;\n }\n return normalizeWorkflowId(entry) === normalized;\n });\n}\n\nexport function loadClaudeCodeWorkbenchConfig(\n raw: Record<string, string | undefined>,\n): ClaudeCodeWorkbenchConfig {\n const parsed = claudeCodeWorkbenchConfigSchema.parse(raw);\n\n const allowedWorkflowIds =\n parsed.CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS?.length > 0\n ? parsed.CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS\n : [\"*\"];\n\n return {\n workspaceRoot: path.resolve(\n parsed.CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT ?? process.cwd(),\n ),\n timeoutMs: parsed.CLAUDE_CODE_WORKBENCH_TIMEOUT_MS,\n maxOutputChars: parsed.CLAUDE_CODE_WORKBENCH_MAX_OUTPUT_CHARS,\n maxStdinBytes: parsed.CLAUDE_CODE_WORKBENCH_MAX_STDIN_BYTES,\n allowedWorkflowIds,\n enableMutatingWorkflows:\n parsed.CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS,\n };\n}\n\nexport const DEFAULT_WORKBENCH_WORKFLOWS = getDefaultWorkflowIds();\n",
|
|
9
|
+
"export type WorkbenchWorkflowCategory =\n | \"repo\"\n | \"quality\"\n | \"test\"\n | \"build\"\n | \"docs\"\n | \"plugins\";\n\nexport interface WorkbenchWorkflow {\n id: string;\n title: string;\n description: string;\n category: WorkbenchWorkflowCategory;\n command: string;\n args: string[];\n mutatesRepo: boolean;\n}\n\nconst DEFAULT_WORKFLOWS: readonly WorkbenchWorkflow[] = [\n {\n id: \"repo_status\",\n title: \"Repo Status\",\n description: \"Show git status with branch summary.\",\n category: \"repo\",\n command: \"git\",\n args: [\"status\", \"--short\", \"--branch\"],\n mutatesRepo: false,\n },\n {\n id: \"repo_diff_stat\",\n title: \"Repo Diff Stat\",\n description: \"Show unstaged diff statistics.\",\n category: \"repo\",\n command: \"git\",\n args: [\"diff\", \"--stat\"],\n mutatesRepo: false,\n },\n {\n id: \"repo_diff_cached\",\n title: \"Repo Diff Cached\",\n description: \"Show staged diff statistics.\",\n category: \"repo\",\n command: \"git\",\n args: [\"diff\", \"--cached\", \"--stat\"],\n mutatesRepo: false,\n },\n {\n id: \"recent_commits\",\n title: \"Recent Commits\",\n description: \"Show recent commit history.\",\n category: \"repo\",\n command: \"git\",\n args: [\"log\", \"--oneline\", \"-n\", \"20\"],\n mutatesRepo: false,\n },\n {\n id: \"check\",\n title: \"Check\",\n description: \"Run project check (typecheck + lint).\",\n category: \"quality\",\n command: \"bun\",\n args: [\"run\", \"check\"],\n mutatesRepo: false,\n },\n {\n id: \"typecheck\",\n title: \"Typecheck\",\n description: \"Run TypeScript typecheck.\",\n category: \"quality\",\n command: \"bun\",\n args: [\"run\", \"typecheck\"],\n mutatesRepo: false,\n },\n {\n id: \"lint\",\n title: \"Lint\",\n description: \"Run Biome lint checks.\",\n category: \"quality\",\n command: \"bun\",\n args: [\"run\", \"lint\"],\n mutatesRepo: false,\n },\n {\n id: \"test_once\",\n title: \"Test Once\",\n description: \"Run Vitest suite one time.\",\n category: \"test\",\n command: \"bun\",\n args: [\"run\", \"test:once\"],\n mutatesRepo: false,\n },\n {\n id: \"test_e2e\",\n title: \"Test E2E\",\n description: \"Run E2E tests.\",\n category: \"test\",\n command: \"bun\",\n args: [\"run\", \"test:e2e\"],\n mutatesRepo: false,\n },\n {\n id: \"pre_review_local\",\n title: \"Pre-Review Local\",\n description: \"Run local pre-review parity workflow.\",\n category: \"quality\",\n command: \"bun\",\n args: [\"run\", \"pre-review:local\"],\n mutatesRepo: false,\n },\n {\n id: \"build_local_plugins\",\n title: \"Build Local Plugins\",\n description: \"Build local plugin packages used by this repo.\",\n category: \"plugins\",\n command: \"bun\",\n args: [\"run\", \"build:local-plugins\"],\n mutatesRepo: false,\n },\n {\n id: \"build\",\n title: \"Build\",\n description: \"Run full monorepo build.\",\n category: \"build\",\n command: \"bun\",\n args: [\"run\", \"build\"],\n mutatesRepo: false,\n },\n {\n id: \"docs_build\",\n title: \"Docs Build\",\n description: \"Validate docs build and broken links.\",\n category: \"docs\",\n command: \"bun\",\n args: [\"run\", \"docs:build\"],\n mutatesRepo: false,\n },\n {\n id: \"format_fix\",\n title: \"Format Fix\",\n description: \"Apply Biome formatting changes.\",\n category: \"quality\",\n command: \"bun\",\n args: [\"run\", \"format:fix\"],\n mutatesRepo: true,\n },\n {\n id: \"lint_fix\",\n title: \"Lint Fix\",\n description: \"Apply Biome lint autofixes.\",\n category: \"quality\",\n command: \"bun\",\n args: [\"run\", \"lint:fix\"],\n mutatesRepo: true,\n },\n];\n\nexport function normalizeWorkflowId(workflowId: string): string {\n return workflowId\n .trim()\n .toLowerCase()\n .replace(/[\\s.]+/g, \"_\")\n .replace(/[^a-z0-9_-]/g, \"\");\n}\n\nexport function listDefaultWorkflows(): WorkbenchWorkflow[] {\n return DEFAULT_WORKFLOWS.map((workflow) => ({\n ...workflow,\n args: [...workflow.args],\n }));\n}\n\nexport function getDefaultWorkflowIds(): string[] {\n return DEFAULT_WORKFLOWS.map((workflow) => workflow.id);\n}\n\nexport function findDefaultWorkflowById(\n workflowId: string,\n): WorkbenchWorkflow | undefined {\n const normalized = normalizeWorkflowId(workflowId);\n return DEFAULT_WORKFLOWS.find((workflow) => workflow.id === normalized);\n}\n",
|
|
10
|
+
"import type {\n IAgentRuntime,\n Memory,\n Provider,\n ProviderResult,\n State,\n} from \"@elizaos/core\";\nimport type { ClaudeCodeWorkbenchService } from \"../services/workbench-service.ts\";\n\nexport const claudeCodeWorkbenchStatusProvider: Provider = {\n name: \"CLAUDE_CODE_WORKBENCH_STATUS\",\n description:\n \"Provides Claude Code workbench availability, workflow policy, and recent run metadata.\",\n\n get: async (\n runtime: IAgentRuntime,\n _message: Memory,\n _state: State | undefined,\n ): Promise<ProviderResult> => {\n const service = runtime.getService(\n \"claude_code_workbench\",\n ) as ClaudeCodeWorkbenchService | null;\n\n if (!service) {\n return {\n text: \"Claude Code workbench plugin is not active (service not found).\",\n values: { workbenchAvailable: false },\n data: { available: false },\n };\n }\n\n const status = service.getStatus();\n\n const lines = [\n `Workbench availability: ${status.available ? \"available\" : \"unavailable\"}`,\n `Workflow count: ${status.workflows.length}`,\n `Workspace root: ${status.workspaceRoot}`,\n `Timeout: ${status.timeoutMs}ms`,\n `Output cap: ${status.maxOutputChars} chars`,\n `Mutating workflows: ${status.enableMutatingWorkflows ? \"enabled\" : \"disabled\"}`,\n ];\n\n if (status.lastRunAt) {\n lines.push(`Last run: ${new Date(status.lastRunAt).toISOString()}`);\n }\n if (status.lastWorkflow) {\n lines.push(`Last workflow: ${status.lastWorkflow}`);\n }\n if (typeof status.lastExitCode !== \"undefined\") {\n lines.push(`Last exit code: ${String(status.lastExitCode)}`);\n }\n\n return {\n text: lines.join(\"\\n\"),\n values: {\n workbenchAvailable: status.available,\n workbenchRunning: status.running,\n },\n data: { ...status },\n };\n },\n};\n",
|
|
11
|
+
"import {\n type IAgentRuntime,\n logger,\n type Route,\n type RouteRequest,\n type RouteResponse,\n} from \"@elizaos/core\";\nimport type {\n ClaudeCodeWorkbenchService,\n WorkbenchRunInput,\n} from \"./services/workbench-service.ts\";\n\nfunction getService(runtime: IAgentRuntime): ClaudeCodeWorkbenchService {\n const service = runtime.getService(\n \"claude_code_workbench\",\n ) as ClaudeCodeWorkbenchService | null;\n if (!service) {\n throw new Error(\"Claude Code workbench service not available\");\n }\n return service;\n}\n\nfunction toStringOrUndefined(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n\n const normalized = value.trim();\n return normalized.length > 0 ? normalized : undefined;\n}\n\nfunction parseRunInput(body: unknown): WorkbenchRunInput | null {\n if (!body || typeof body !== \"object\") {\n return null;\n }\n\n const record = body as Record<string, unknown>;\n const workflow = toStringOrUndefined(record.workflow);\n\n if (!workflow) {\n return null;\n }\n\n return {\n workflow,\n cwd: toStringOrUndefined(record.cwd),\n stdin: toStringOrUndefined(record.stdin),\n };\n}\n\nconst statusRoute: Route = {\n name: \"claude-code-workbench-status\",\n public: false,\n path: \"/status\",\n type: \"GET\",\n handler: async (\n _req: RouteRequest,\n res: RouteResponse,\n runtime: IAgentRuntime,\n ) => {\n try {\n const service = getService(runtime);\n res.json({ ok: true, status: service.getStatus() });\n } catch (error) {\n res.status(500).json({\n ok: false,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n },\n};\n\nconst workflowsRoute: Route = {\n name: \"claude-code-workbench-workflows\",\n public: false,\n path: \"/workflows\",\n type: \"GET\",\n handler: async (\n _req: RouteRequest,\n res: RouteResponse,\n runtime: IAgentRuntime,\n ) => {\n try {\n const service = getService(runtime);\n res.json({ ok: true, workflows: service.listWorkflows() });\n } catch (error) {\n res.status(500).json({\n ok: false,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n },\n};\n\nconst runRoute: Route = {\n name: \"claude-code-workbench-run\",\n public: false,\n path: \"/run\",\n type: \"POST\",\n handler: async (\n req: RouteRequest,\n res: RouteResponse,\n runtime: IAgentRuntime,\n ) => {\n try {\n const input = parseRunInput(req.body);\n if (!input) {\n res.status(400).json({\n ok: false,\n error:\n \"Invalid run request. Provide non-empty `workflow` in request body.\",\n });\n return;\n }\n\n const service = getService(runtime);\n const result = await service.run(input);\n res.status(result.ok ? 200 : 500).json({ ok: result.ok, result });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(`Claude Code workbench route error: ${message}`);\n res.status(500).json({ ok: false, error: message });\n }\n },\n};\n\nexport const claudeCodeWorkbenchRoutes: Route[] = [\n statusRoute,\n workflowsRoute,\n runRoute,\n];\n",
|
|
12
|
+
"import { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { type IAgentRuntime, Service } from \"@elizaos/core\";\nimport {\n type ClaudeCodeWorkbenchConfig,\n isWorkflowAllowed,\n loadClaudeCodeWorkbenchConfig,\n} from \"../config.ts\";\nimport {\n listDefaultWorkflows,\n normalizeWorkflowId,\n type WorkbenchWorkflow,\n} from \"../workflows.ts\";\n\nexport interface WorkbenchWorkflowSummary {\n id: string;\n title: string;\n description: string;\n category: string;\n mutatesRepo: boolean;\n enabled: boolean;\n commandPreview: string;\n}\n\nexport interface WorkbenchRunInput {\n workflow: string;\n cwd?: string;\n stdin?: string;\n}\n\nexport interface WorkbenchRunResult {\n ok: boolean;\n workflow: string;\n command: string;\n args: string[];\n exitCode: number | null;\n stdout: string;\n stderr: string;\n durationMs: number;\n timedOut: boolean;\n stdoutTruncated: boolean;\n stderrTruncated: boolean;\n}\n\nexport interface WorkbenchStatus {\n available: boolean;\n running: boolean;\n workspaceRoot: string;\n timeoutMs: number;\n maxOutputChars: number;\n maxStdinBytes: number;\n allowedWorkflowIds: string[];\n enableMutatingWorkflows: boolean;\n workflows: WorkbenchWorkflowSummary[];\n lastRunAt?: number;\n lastWorkflow?: string;\n lastExitCode?: number | null;\n lastDurationMs?: number;\n lastError?: string;\n}\n\nfunction appendWithLimit(\n current: string,\n chunk: Buffer | string,\n limit: number,\n): { value: string; truncated: boolean } {\n if (current.length >= limit) {\n return { value: current, truncated: true };\n }\n\n const text = typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\");\n const remaining = limit - current.length;\n\n if (text.length <= remaining) {\n return { value: current + text, truncated: false };\n }\n\n return {\n value: current + text.slice(0, remaining),\n truncated: true,\n };\n}\n\nfunction withTruncationSuffix(value: string, truncated: boolean): string {\n if (!truncated) {\n return value;\n }\n\n const suffix = \"\\n...[truncated by plugin-claude-code-workbench]\";\n return value.endsWith(suffix) ? value : `${value}${suffix}`;\n}\n\nfunction cleanOptionalValue(value: string | undefined): string | undefined {\n const cleaned = value?.trim();\n return cleaned && cleaned.length > 0 ? cleaned : undefined;\n}\n\nfunction isPathInside(baseDir: string, targetDir: string): boolean {\n if (baseDir === targetDir) {\n return true;\n }\n\n const normalizedBase =\n process.platform === \"win32\" ? baseDir.toLowerCase() : baseDir;\n const normalizedTarget =\n process.platform === \"win32\" ? targetDir.toLowerCase() : targetDir;\n return normalizedTarget.startsWith(`${normalizedBase}${path.sep}`);\n}\n\nfunction toSummary(\n workflow: WorkbenchWorkflow,\n enableMutatingWorkflows: boolean,\n): WorkbenchWorkflowSummary {\n return {\n id: workflow.id,\n title: workflow.title,\n description: workflow.description,\n category: workflow.category,\n mutatesRepo: workflow.mutatesRepo,\n enabled: !workflow.mutatesRepo || enableMutatingWorkflows,\n commandPreview: `${workflow.command} ${workflow.args.join(\" \")}`.trim(),\n };\n}\n\nexport class ClaudeCodeWorkbenchService extends Service {\n static override serviceType = \"claude_code_workbench\";\n\n override capabilityDescription =\n \"Run allowlisted repository workflows for this monorepo with safe process controls.\";\n\n private readonly runtimeConfig: ClaudeCodeWorkbenchConfig;\n private readonly workflows: Map<string, WorkbenchWorkflow>;\n private runQueue: Promise<unknown> = Promise.resolve();\n private running = false;\n private available = true;\n private lastRunAt?: number;\n private lastWorkflow?: string;\n private lastExitCode?: number | null;\n private lastDurationMs?: number;\n private lastError?: string;\n\n constructor(\n runtime?: IAgentRuntime,\n config?: ClaudeCodeWorkbenchConfig,\n workflows?: WorkbenchWorkflow[],\n ) {\n super(runtime);\n this.runtimeConfig = config ?? loadClaudeCodeWorkbenchConfig(process.env);\n\n const sourceWorkflows = workflows ?? listDefaultWorkflows();\n const filtered = sourceWorkflows.filter((workflow) =>\n isWorkflowAllowed(workflow.id, this.runtimeConfig.allowedWorkflowIds),\n );\n\n this.workflows = new Map(\n filtered.map((workflow) => [\n normalizeWorkflowId(workflow.id),\n {\n ...workflow,\n id: normalizeWorkflowId(workflow.id),\n args: [...workflow.args],\n },\n ]),\n );\n }\n\n static async start(runtime: IAgentRuntime): Promise<Service> {\n return new ClaudeCodeWorkbenchService(runtime);\n }\n\n static async stop(runtime: IAgentRuntime): Promise<void> {\n const service = runtime.getService(ClaudeCodeWorkbenchService.serviceType);\n if (service && \"stop\" in service && typeof service.stop === \"function\") {\n await service.stop();\n }\n }\n\n async stop(): Promise<void> {\n this.running = false;\n }\n\n listWorkflows(): WorkbenchWorkflowSummary[] {\n return Array.from(this.workflows.values())\n .sort((left, right) => left.id.localeCompare(right.id))\n .map((workflow) =>\n toSummary(workflow, this.runtimeConfig.enableMutatingWorkflows),\n );\n }\n\n getStatus(): WorkbenchStatus {\n return {\n available: this.available,\n running: this.running,\n workspaceRoot: this.runtimeConfig.workspaceRoot,\n timeoutMs: this.runtimeConfig.timeoutMs,\n maxOutputChars: this.runtimeConfig.maxOutputChars,\n maxStdinBytes: this.runtimeConfig.maxStdinBytes,\n allowedWorkflowIds: [...this.runtimeConfig.allowedWorkflowIds],\n enableMutatingWorkflows: this.runtimeConfig.enableMutatingWorkflows,\n workflows: this.listWorkflows(),\n lastRunAt: this.lastRunAt,\n lastWorkflow: this.lastWorkflow,\n lastExitCode: this.lastExitCode,\n lastDurationMs: this.lastDurationMs,\n lastError: this.lastError,\n };\n }\n\n async run(input: WorkbenchRunInput): Promise<WorkbenchRunResult> {\n const task = this.runQueue.then(\n () => this.executeRun(input),\n () => this.executeRun(input),\n );\n\n this.runQueue = task.then(\n () => undefined,\n () => undefined,\n );\n\n return task;\n }\n\n private resolveWorkingDirectory(inputCwd?: string): string {\n const workspaceRootPath = path.resolve(this.runtimeConfig.workspaceRoot);\n const requestedPath = path.resolve(\n cleanOptionalValue(inputCwd) ?? workspaceRootPath,\n );\n\n let workspaceRoot: string;\n let requested: string;\n\n try {\n workspaceRoot = fs.realpathSync(workspaceRootPath);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n `CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT is not accessible (${workspaceRootPath}): ${message}`,\n );\n }\n\n try {\n requested = fs.realpathSync(requestedPath);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Workbench cwd is not accessible (${requestedPath}): ${message}`,\n );\n }\n\n if (!isPathInside(workspaceRoot, requested)) {\n throw new Error(\n `Workbench cwd must stay within CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT (${workspaceRoot}). Received: ${requested}`,\n );\n }\n\n return requested;\n }\n\n private buildChildEnv(): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n const baseKeys = [\n \"PATH\",\n \"HOME\",\n \"USERPROFILE\",\n \"SHELL\",\n \"COMSPEC\",\n \"PATHEXT\",\n \"SystemRoot\",\n \"TMP\",\n \"TEMP\",\n \"TERM\",\n \"CI\",\n \"FORCE_COLOR\",\n ];\n\n for (const key of baseKeys) {\n const value = process.env[key];\n if (typeof value === \"string\" && value.length > 0) {\n env[key] = value;\n }\n }\n\n for (const [key, value] of Object.entries(process.env)) {\n if (\n key.startsWith(\"CLAUDE_CODE_WORKBENCH_\") &&\n typeof value === \"string\" &&\n value.length > 0\n ) {\n env[key] = value;\n }\n }\n\n return env;\n }\n\n private resolveWorkflow(workflowId: string): WorkbenchWorkflow {\n const normalized = normalizeWorkflowId(workflowId);\n const workflow = this.workflows.get(normalized);\n\n if (!workflow) {\n const allowed = this.listWorkflows()\n .map((entry) => entry.id)\n .join(\", \");\n throw new Error(\n `Unknown workflow \"${workflowId}\". Allowed workflows: ${allowed || \"none\"}.`,\n );\n }\n\n if (workflow.mutatesRepo && !this.runtimeConfig.enableMutatingWorkflows) {\n throw new Error(\n `Workflow \"${workflow.id}\" mutates the repository and is disabled. Set CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS=true to enable it.`,\n );\n }\n\n return workflow;\n }\n\n private async executeRun(\n input: WorkbenchRunInput,\n ): Promise<WorkbenchRunResult> {\n const workflow = this.resolveWorkflow(input.workflow);\n\n if (\n input.stdin &&\n Buffer.byteLength(input.stdin, \"utf8\") > this.runtimeConfig.maxStdinBytes\n ) {\n throw new Error(\n `Workbench stdin exceeds limit (${this.runtimeConfig.maxStdinBytes} bytes).`,\n );\n }\n\n const cwd = this.resolveWorkingDirectory(input.cwd);\n this.running = true;\n this.lastWorkflow = workflow.id;\n\n const startedAt = Date.now();\n let stdout = \"\";\n let stderr = \"\";\n let stdoutTruncated = false;\n let stderrTruncated = false;\n let timedOut = false;\n\n try {\n const child = spawn(workflow.command, workflow.args, {\n cwd,\n env: this.buildChildEnv(),\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n shell: false,\n });\n\n if (input.stdin) {\n child.stdin?.write(input.stdin);\n }\n child.stdin?.end();\n\n child.stdout?.on(\"data\", (chunk) => {\n const next = appendWithLimit(\n stdout,\n chunk,\n this.runtimeConfig.maxOutputChars,\n );\n stdout = next.value;\n stdoutTruncated = stdoutTruncated || next.truncated;\n });\n\n child.stderr?.on(\"data\", (chunk) => {\n const next = appendWithLimit(\n stderr,\n chunk,\n this.runtimeConfig.maxOutputChars,\n );\n stderr = next.value;\n stderrTruncated = stderrTruncated || next.truncated;\n });\n\n const timeoutHandle = setTimeout(() => {\n timedOut = true;\n try {\n child.kill(\"SIGTERM\");\n } catch {\n // ignore\n }\n\n setTimeout(() => {\n try {\n child.kill(\"SIGKILL\");\n } catch {\n // ignore\n }\n }, 250);\n }, this.runtimeConfig.timeoutMs);\n\n const exitCode = await new Promise<number | null>((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"close\", (code) => resolve(code));\n }).finally(() => {\n clearTimeout(timeoutHandle);\n });\n\n if (timedOut) {\n const timeoutMessage = `Workflow timed out after ${this.runtimeConfig.timeoutMs}ms.`;\n const next = appendWithLimit(\n stderr,\n timeoutMessage,\n this.runtimeConfig.maxOutputChars,\n );\n stderr = next.value;\n stderrTruncated = stderrTruncated || next.truncated;\n }\n\n const durationMs = Date.now() - startedAt;\n const ok = !timedOut && exitCode === 0;\n\n this.available = true;\n this.lastRunAt = startedAt;\n this.lastExitCode = exitCode;\n this.lastDurationMs = durationMs;\n this.lastError = ok\n ? undefined\n : stderr || `Process exited with code ${String(exitCode)}`;\n\n return {\n ok,\n workflow: workflow.id,\n command: workflow.command,\n args: [...workflow.args],\n exitCode,\n stdout: withTruncationSuffix(stdout, stdoutTruncated),\n stderr: withTruncationSuffix(stderr, stderrTruncated),\n durationMs,\n timedOut,\n stdoutTruncated,\n stderrTruncated,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.lastRunAt = startedAt;\n this.lastExitCode = null;\n this.lastDurationMs = Date.now() - startedAt;\n this.lastError = message;\n\n const maybeErrno = error as NodeJS.ErrnoException;\n if (maybeErrno?.code === \"ENOENT\") {\n this.available = false;\n }\n\n throw error;\n } finally {\n this.running = false;\n }\n }\n}\n",
|
|
13
|
+
"import { claudeCodeWorkbenchPlugin } from \"./plugin.ts\";\n\nexport { claudeCodeWorkbenchListAction } from \"./actions/list-workflows.ts\";\nexport { claudeCodeWorkbenchRunAction } from \"./actions/run-workflow.ts\";\nexport type { ClaudeCodeWorkbenchConfig } from \"./config.ts\";\nexport {\n claudeCodeWorkbenchConfigSchema,\n DEFAULT_WORKBENCH_WORKFLOWS,\n isWorkflowAllowed,\n loadClaudeCodeWorkbenchConfig,\n} from \"./config.ts\";\nexport { claudeCodeWorkbenchPlugin } from \"./plugin.ts\";\nexport { claudeCodeWorkbenchStatusProvider } from \"./providers/status.ts\";\nexport { claudeCodeWorkbenchRoutes } from \"./routes.ts\";\n\nexport type {\n WorkbenchRunInput,\n WorkbenchRunResult,\n WorkbenchStatus,\n WorkbenchWorkflowSummary,\n} from \"./services/workbench-service.ts\";\nexport { ClaudeCodeWorkbenchService } from \"./services/workbench-service.ts\";\nexport type {\n WorkbenchWorkflow,\n WorkbenchWorkflowCategory,\n} from \"./workflows.ts\";\nexport {\n findDefaultWorkflowById,\n getDefaultWorkflowIds,\n listDefaultWorkflows,\n normalizeWorkflowId,\n} from \"./workflows.ts\";\n\nexport default claudeCodeWorkbenchPlugin;\n"
|
|
14
|
+
],
|
|
15
|
+
"mappings": "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",
|
|
16
|
+
"debugId": "275C68524586278864756E2164756E21",
|
|
17
|
+
"names": []
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@elizaos/plugin-claude-code-workbench",
|
|
3
|
+
"description": "Claude Code companion workflow plugin for Milady/ElizaOS",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"packageType": "plugin",
|
|
10
|
+
"platform": "node",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"plugin",
|
|
14
|
+
"elizaos",
|
|
15
|
+
"claude-code",
|
|
16
|
+
"workflow",
|
|
17
|
+
"milady"
|
|
18
|
+
],
|
|
19
|
+
"homepage": "https://github.com/milady-ai/milady",
|
|
20
|
+
"exports": {
|
|
21
|
+
"./package.json": "./package.json",
|
|
22
|
+
".": {
|
|
23
|
+
"import": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"package.json"
|
|
33
|
+
],
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@elizaos/core": "next",
|
|
36
|
+
"zod": "^4.3.5"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"dotenv": "^17.2.3",
|
|
40
|
+
"prettier": "^3.7.4",
|
|
41
|
+
"typescript": "^5.9.3"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"start": "elizaos start",
|
|
45
|
+
"dev": "elizaos dev",
|
|
46
|
+
"build": "bun run build.ts",
|
|
47
|
+
"build:watch": "bun run build.ts --watch",
|
|
48
|
+
"test": "bun test",
|
|
49
|
+
"format": "prettier --write ./src"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"resolutions": {
|
|
55
|
+
"zod": "4.3.5"
|
|
56
|
+
},
|
|
57
|
+
"agentConfig": {
|
|
58
|
+
"pluginType": "elizaos:plugin:1.0.0",
|
|
59
|
+
"pluginParameters": {
|
|
60
|
+
"CLAUDE_CODE_WORKBENCH_WORKSPACE_ROOT": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"description": "Workspace root for workflow execution (default: cwd)",
|
|
63
|
+
"required": false
|
|
64
|
+
},
|
|
65
|
+
"CLAUDE_CODE_WORKBENCH_TIMEOUT_MS": {
|
|
66
|
+
"type": "number",
|
|
67
|
+
"description": "Timeout per workflow run in milliseconds",
|
|
68
|
+
"required": false
|
|
69
|
+
},
|
|
70
|
+
"CLAUDE_CODE_WORKBENCH_MAX_OUTPUT_CHARS": {
|
|
71
|
+
"type": "number",
|
|
72
|
+
"description": "Maximum stdout/stderr characters captured per run",
|
|
73
|
+
"required": false
|
|
74
|
+
},
|
|
75
|
+
"CLAUDE_CODE_WORKBENCH_MAX_STDIN_BYTES": {
|
|
76
|
+
"type": "number",
|
|
77
|
+
"description": "Maximum stdin bytes accepted per run",
|
|
78
|
+
"required": false
|
|
79
|
+
},
|
|
80
|
+
"CLAUDE_CODE_WORKBENCH_ALLOWED_WORKFLOWS": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "Comma-separated allowlist of workflow ids. Use '*' for all.",
|
|
83
|
+
"required": false
|
|
84
|
+
},
|
|
85
|
+
"CLAUDE_CODE_WORKBENCH_ENABLE_MUTATING_WORKFLOWS": {
|
|
86
|
+
"type": "boolean",
|
|
87
|
+
"description": "Allow workflows that can change repository files",
|
|
88
|
+
"required": false
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|