cclaw-cli 0.49.0 → 0.51.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 +54 -82
- package/dist/artifact-linter.d.ts +4 -0
- package/dist/artifact-linter.js +24 -3
- package/dist/cli.d.ts +1 -19
- package/dist/cli.js +49 -491
- package/dist/constants.d.ts +2 -13
- package/dist/constants.js +1 -43
- package/dist/content/closeout-guidance.d.ts +14 -0
- package/dist/content/closeout-guidance.js +42 -0
- package/dist/content/core-agents.js +51 -9
- package/dist/content/decision-protocol.d.ts +12 -0
- package/dist/content/decision-protocol.js +20 -0
- package/dist/content/diff-command.d.ts +1 -2
- package/dist/content/diff-command.js +8 -94
- package/dist/content/examples.d.ts +4 -10
- package/dist/content/examples.js +10 -20
- package/dist/content/hook-events.js +2 -2
- package/dist/content/hook-inline-snippets.d.ts +5 -2
- package/dist/content/hook-inline-snippets.js +33 -1
- package/dist/content/hook-manifest.d.ts +3 -4
- package/dist/content/hook-manifest.js +11 -12
- package/dist/content/hooks.js +2 -0
- package/dist/content/ideate-command.d.ts +2 -0
- package/dist/content/ideate-command.js +31 -25
- package/dist/content/iron-laws.d.ts +5 -5
- package/dist/content/iron-laws.js +5 -5
- package/dist/content/learnings.d.ts +3 -4
- package/dist/content/learnings.js +24 -50
- package/dist/content/meta-skill.js +31 -21
- package/dist/content/next-command.js +38 -38
- package/dist/content/node-hooks.js +17 -343
- package/dist/content/opencode-plugin.js +2 -100
- package/dist/content/research-playbooks.js +14 -14
- package/dist/content/review-loop.d.ts +2 -0
- package/dist/content/review-loop.js +8 -0
- package/dist/content/session-hooks.js +14 -46
- package/dist/content/skills.d.ts +0 -5
- package/dist/content/skills.js +53 -128
- package/dist/content/stage-common-guidance.d.ts +0 -1
- package/dist/content/stage-common-guidance.js +15 -14
- package/dist/content/stage-schema.d.ts +26 -1
- package/dist/content/stage-schema.js +121 -40
- package/dist/content/stages/_lint-metadata/index.js +9 -15
- package/dist/content/stages/brainstorm.js +22 -43
- package/dist/content/stages/design.js +37 -57
- package/dist/content/stages/plan.js +22 -13
- package/dist/content/stages/review.js +24 -27
- package/dist/content/stages/scope.js +34 -46
- package/dist/content/stages/ship.js +7 -4
- package/dist/content/stages/spec.js +20 -9
- package/dist/content/stages/tdd.js +64 -44
- package/dist/content/start-command.js +10 -12
- package/dist/content/status-command.d.ts +2 -7
- package/dist/content/status-command.js +19 -146
- package/dist/content/subagents.d.ts +0 -5
- package/dist/content/subagents.js +47 -28
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +126 -135
- package/dist/content/track-render-context.d.ts +17 -0
- package/dist/content/track-render-context.js +44 -0
- package/dist/content/tree-command.d.ts +1 -2
- package/dist/content/tree-command.js +4 -87
- package/dist/content/utility-skills.d.ts +2 -29
- package/dist/content/utility-skills.js +2 -1534
- package/dist/content/view-command.js +29 -11
- package/dist/delegation.d.ts +1 -1
- package/dist/delegation.js +5 -15
- package/dist/doctor-registry.js +20 -21
- package/dist/doctor.js +88 -344
- package/dist/flow-state.d.ts +3 -0
- package/dist/flow-state.js +2 -0
- package/dist/harness-adapters.d.ts +1 -1
- package/dist/harness-adapters.js +48 -57
- package/dist/install.js +128 -358
- package/dist/internal/advance-stage.js +3 -9
- package/dist/internal/compound-readiness.d.ts +1 -1
- package/dist/internal/compound-readiness.js +1 -1
- package/dist/internal/tdd-loop-status.d.ts +1 -1
- package/dist/internal/tdd-loop-status.js +1 -1
- package/dist/knowledge-store.d.ts +16 -10
- package/dist/knowledge-store.js +51 -15
- package/dist/policy.js +16 -105
- package/dist/run-archive.d.ts +4 -6
- package/dist/run-archive.js +15 -20
- package/dist/run-persistence.d.ts +2 -2
- package/dist/run-persistence.js +3 -9
- package/package.json +1 -2
- package/dist/content/archive-command.d.ts +0 -2
- package/dist/content/archive-command.js +0 -124
- package/dist/content/compound-command.d.ts +0 -5
- package/dist/content/compound-command.js +0 -193
- package/dist/content/contexts.d.ts +0 -18
- package/dist/content/contexts.js +0 -24
- package/dist/content/contracts.d.ts +0 -2
- package/dist/content/contracts.js +0 -51
- package/dist/content/doctor-references.d.ts +0 -2
- package/dist/content/doctor-references.js +0 -150
- package/dist/content/eval-scaffold.d.ts +0 -15
- package/dist/content/eval-scaffold.js +0 -370
- package/dist/content/feature-command.d.ts +0 -2
- package/dist/content/feature-command.js +0 -123
- package/dist/content/flow-map.d.ts +0 -23
- package/dist/content/flow-map.js +0 -134
- package/dist/content/harness-doc.d.ts +0 -2
- package/dist/content/harness-doc.js +0 -202
- package/dist/content/harness-playbooks.d.ts +0 -24
- package/dist/content/harness-playbooks.js +0 -393
- package/dist/content/harness-tool-refs.d.ts +0 -20
- package/dist/content/harness-tool-refs.js +0 -268
- package/dist/content/ops-command.d.ts +0 -2
- package/dist/content/ops-command.js +0 -71
- package/dist/content/protocols.d.ts +0 -7
- package/dist/content/protocols.js +0 -215
- package/dist/content/retro-command.d.ts +0 -2
- package/dist/content/retro-command.js +0 -165
- package/dist/content/rewind-command.d.ts +0 -2
- package/dist/content/rewind-command.js +0 -106
- package/dist/content/tdd-log-command.d.ts +0 -2
- package/dist/content/tdd-log-command.js +0 -85
- package/dist/eval/agents/single-shot.d.ts +0 -27
- package/dist/eval/agents/single-shot.js +0 -79
- package/dist/eval/agents/with-tools.d.ts +0 -44
- package/dist/eval/agents/with-tools.js +0 -261
- package/dist/eval/agents/workflow.d.ts +0 -31
- package/dist/eval/agents/workflow.js +0 -155
- package/dist/eval/baseline.d.ts +0 -38
- package/dist/eval/baseline.js +0 -282
- package/dist/eval/config-loader.d.ts +0 -14
- package/dist/eval/config-loader.js +0 -395
- package/dist/eval/corpus.d.ts +0 -30
- package/dist/eval/corpus.js +0 -330
- package/dist/eval/cost-guard.d.ts +0 -102
- package/dist/eval/cost-guard.js +0 -190
- package/dist/eval/diff.d.ts +0 -64
- package/dist/eval/diff.js +0 -323
- package/dist/eval/llm-client.d.ts +0 -176
- package/dist/eval/llm-client.js +0 -267
- package/dist/eval/mode.d.ts +0 -28
- package/dist/eval/mode.js +0 -61
- package/dist/eval/progress.d.ts +0 -83
- package/dist/eval/progress.js +0 -59
- package/dist/eval/report.d.ts +0 -11
- package/dist/eval/report.js +0 -181
- package/dist/eval/rubric-loader.d.ts +0 -20
- package/dist/eval/rubric-loader.js +0 -143
- package/dist/eval/runner.d.ts +0 -81
- package/dist/eval/runner.js +0 -746
- package/dist/eval/runs.d.ts +0 -41
- package/dist/eval/runs.js +0 -114
- package/dist/eval/sandbox.d.ts +0 -38
- package/dist/eval/sandbox.js +0 -137
- package/dist/eval/tools/glob.d.ts +0 -2
- package/dist/eval/tools/glob.js +0 -163
- package/dist/eval/tools/grep.d.ts +0 -2
- package/dist/eval/tools/grep.js +0 -152
- package/dist/eval/tools/index.d.ts +0 -7
- package/dist/eval/tools/index.js +0 -35
- package/dist/eval/tools/read.d.ts +0 -2
- package/dist/eval/tools/read.js +0 -122
- package/dist/eval/tools/types.d.ts +0 -49
- package/dist/eval/tools/types.js +0 -41
- package/dist/eval/tools/write.d.ts +0 -2
- package/dist/eval/tools/write.js +0 -92
- package/dist/eval/types.d.ts +0 -561
- package/dist/eval/types.js +0 -47
- package/dist/eval/verifiers/judge.d.ts +0 -40
- package/dist/eval/verifiers/judge.js +0 -256
- package/dist/eval/verifiers/rules.d.ts +0 -24
- package/dist/eval/verifiers/rules.js +0 -218
- package/dist/eval/verifiers/structural.d.ts +0 -14
- package/dist/eval/verifiers/structural.js +0 -171
- package/dist/eval/verifiers/traceability.d.ts +0 -23
- package/dist/eval/verifiers/traceability.js +0 -84
- package/dist/eval/verifiers/workflow-consistency.d.ts +0 -21
- package/dist/eval/verifiers/workflow-consistency.js +0 -225
- package/dist/eval/workflow-corpus.d.ts +0 -7
- package/dist/eval/workflow-corpus.js +0 -207
- package/dist/feature-system.d.ts +0 -42
- package/dist/feature-system.js +0 -432
- package/dist/internal/knowledge-digest.d.ts +0 -7
- package/dist/internal/knowledge-digest.js +0 -93
package/dist/eval/tools/index.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Registry of sandbox-confined tools used by the with-tools agent (agent/workflow mode).
|
|
3
|
-
*
|
|
4
|
-
* The registry order defines the advertised schema order in the
|
|
5
|
-
* function-calling payload. Keeping it stable means judges reading
|
|
6
|
-
* generated traces can rely on predictable tool descriptions.
|
|
7
|
-
*/
|
|
8
|
-
import { globTool } from "./glob.js";
|
|
9
|
-
import { grepTool } from "./grep.js";
|
|
10
|
-
import { readTool } from "./read.js";
|
|
11
|
-
import { writeTool } from "./write.js";
|
|
12
|
-
export { truncatePayload } from "./types.js";
|
|
13
|
-
export const BUILTIN_TOOLS = [readTool, writeTool, globTool, grepTool];
|
|
14
|
-
/** Build a lookup for the agent loop. */
|
|
15
|
-
export function toolsByName(tools = BUILTIN_TOOLS) {
|
|
16
|
-
const map = new Map();
|
|
17
|
-
for (const tool of tools) {
|
|
18
|
-
if (map.has(tool.descriptor.name)) {
|
|
19
|
-
throw new Error(`duplicate tool name: ${tool.descriptor.name}`);
|
|
20
|
-
}
|
|
21
|
-
map.set(tool.descriptor.name, tool);
|
|
22
|
-
}
|
|
23
|
-
return map;
|
|
24
|
-
}
|
|
25
|
-
/** Shape a tool list for OpenAI-style `tools[]` in the chat request. */
|
|
26
|
-
export function toolsForRequest(tools = BUILTIN_TOOLS) {
|
|
27
|
-
return tools.map((tool) => ({
|
|
28
|
-
type: "function",
|
|
29
|
-
function: {
|
|
30
|
-
name: tool.descriptor.name,
|
|
31
|
-
description: tool.descriptor.description,
|
|
32
|
-
parameters: tool.descriptor.parameters
|
|
33
|
-
}
|
|
34
|
-
}));
|
|
35
|
-
}
|
package/dist/eval/tools/read.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import { SandboxEscapeError } from "../sandbox.js";
|
|
3
|
-
import { parseArgs, requireString, optionalNumber, truncatePayload } from "./types.js";
|
|
4
|
-
const DESCRIPTION = "Read a UTF-8 text file from the sandbox. Returns the file contents. " +
|
|
5
|
-
"Supports optional 1-indexed `offset` and `limit` to read a slice.";
|
|
6
|
-
export const readTool = {
|
|
7
|
-
descriptor: {
|
|
8
|
-
name: "read_file",
|
|
9
|
-
description: DESCRIPTION,
|
|
10
|
-
parameters: {
|
|
11
|
-
type: "object",
|
|
12
|
-
additionalProperties: false,
|
|
13
|
-
required: ["path"],
|
|
14
|
-
properties: {
|
|
15
|
-
path: {
|
|
16
|
-
type: "string",
|
|
17
|
-
description: "Path relative to the sandbox root."
|
|
18
|
-
},
|
|
19
|
-
offset: {
|
|
20
|
-
type: "integer",
|
|
21
|
-
minimum: 1,
|
|
22
|
-
description: "1-indexed start line (inclusive)."
|
|
23
|
-
},
|
|
24
|
-
limit: {
|
|
25
|
-
type: "integer",
|
|
26
|
-
minimum: 1,
|
|
27
|
-
description: "Maximum number of lines to return."
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
async invoke(rawArgs, ctx) {
|
|
33
|
-
let args;
|
|
34
|
-
try {
|
|
35
|
-
args = parseArgs(rawArgs);
|
|
36
|
-
}
|
|
37
|
-
catch (err) {
|
|
38
|
-
return { ok: false, name: this.descriptor.name, error: err.message };
|
|
39
|
-
}
|
|
40
|
-
let relPath;
|
|
41
|
-
try {
|
|
42
|
-
relPath = requireString(args, "path");
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
return { ok: false, name: this.descriptor.name, error: err.message };
|
|
46
|
-
}
|
|
47
|
-
let offset;
|
|
48
|
-
let limit;
|
|
49
|
-
try {
|
|
50
|
-
offset = optionalNumber(args, "offset");
|
|
51
|
-
limit = optionalNumber(args, "limit");
|
|
52
|
-
}
|
|
53
|
-
catch (err) {
|
|
54
|
-
return {
|
|
55
|
-
ok: false,
|
|
56
|
-
name: this.descriptor.name,
|
|
57
|
-
error: err.message
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
if (offset !== undefined && (!Number.isInteger(offset) || offset < 1)) {
|
|
61
|
-
return {
|
|
62
|
-
ok: false,
|
|
63
|
-
name: this.descriptor.name,
|
|
64
|
-
error: '"offset" must be a positive integer'
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
if (limit !== undefined && (!Number.isInteger(limit) || limit < 1)) {
|
|
68
|
-
return {
|
|
69
|
-
ok: false,
|
|
70
|
-
name: this.descriptor.name,
|
|
71
|
-
error: '"limit" must be a positive integer'
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
let abs;
|
|
75
|
-
try {
|
|
76
|
-
abs = await ctx.sandbox.resolve(relPath);
|
|
77
|
-
}
|
|
78
|
-
catch (err) {
|
|
79
|
-
const denied = err instanceof SandboxEscapeError ? relPath : undefined;
|
|
80
|
-
return {
|
|
81
|
-
ok: false,
|
|
82
|
-
name: this.descriptor.name,
|
|
83
|
-
error: err.message,
|
|
84
|
-
details: denied ? { deniedPath: denied } : undefined
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
let raw;
|
|
88
|
-
try {
|
|
89
|
-
raw = await fs.readFile(abs, "utf8");
|
|
90
|
-
}
|
|
91
|
-
catch (err) {
|
|
92
|
-
return {
|
|
93
|
-
ok: false,
|
|
94
|
-
name: this.descriptor.name,
|
|
95
|
-
error: `read failed: ${err.message}`,
|
|
96
|
-
details: { path: relPath }
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
let content = raw;
|
|
100
|
-
let effectiveLines;
|
|
101
|
-
if (offset !== undefined || limit !== undefined) {
|
|
102
|
-
const lines = raw.split(/\r?\n/);
|
|
103
|
-
const start = Math.max(0, (offset ?? 1) - 1);
|
|
104
|
-
const end = limit !== undefined ? Math.min(lines.length, start + limit) : lines.length;
|
|
105
|
-
const slice = lines.slice(start, end);
|
|
106
|
-
content = slice.join("\n");
|
|
107
|
-
effectiveLines = slice.length;
|
|
108
|
-
}
|
|
109
|
-
const truncated = truncatePayload(content, ctx.maxResultBytes);
|
|
110
|
-
return {
|
|
111
|
-
ok: true,
|
|
112
|
-
name: this.descriptor.name,
|
|
113
|
-
content: truncated,
|
|
114
|
-
details: {
|
|
115
|
-
path: relPath,
|
|
116
|
-
bytes: Buffer.byteLength(truncated, "utf8"),
|
|
117
|
-
truncated: truncated !== content,
|
|
118
|
-
...(effectiveLines !== undefined ? { lines: effectiveLines } : {})
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
};
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared types for sandbox-confined tools (agent/workflow mode).
|
|
3
|
-
*
|
|
4
|
-
* Tools are plain async functions: they take validated arguments and a
|
|
5
|
-
* sandbox handle and return a structured result. The runner serializes
|
|
6
|
-
* results for the model as JSON; the `SandboxTool.invoke` wrapper keeps
|
|
7
|
-
* both the raw structured output (for tests/metrics) and the stringified
|
|
8
|
-
* model-facing payload.
|
|
9
|
-
*/
|
|
10
|
-
import type { Sandbox } from "../sandbox.js";
|
|
11
|
-
export interface ToolDescriptor {
|
|
12
|
-
/** Name the model calls (must match the function-calling schema). */
|
|
13
|
-
name: string;
|
|
14
|
-
/** Human-readable prompt shown to the model. */
|
|
15
|
-
description: string;
|
|
16
|
-
/** JSON schema shipped with the OpenAI-style `tools[]` array. */
|
|
17
|
-
parameters: Record<string, unknown>;
|
|
18
|
-
}
|
|
19
|
-
export interface ToolContext {
|
|
20
|
-
sandbox: Sandbox;
|
|
21
|
-
/**
|
|
22
|
-
* Maximum bytes the tool may return in `content`. Results longer than
|
|
23
|
-
* this are truncated with a trailing marker so the model sees the
|
|
24
|
-
* cutoff.
|
|
25
|
-
*/
|
|
26
|
-
maxResultBytes: number;
|
|
27
|
-
}
|
|
28
|
-
export interface ToolSuccess {
|
|
29
|
-
ok: true;
|
|
30
|
-
name: string;
|
|
31
|
-
content: string;
|
|
32
|
-
details?: Record<string, unknown>;
|
|
33
|
-
}
|
|
34
|
-
export interface ToolFailure {
|
|
35
|
-
ok: false;
|
|
36
|
-
name: string;
|
|
37
|
-
error: string;
|
|
38
|
-
details?: Record<string, unknown>;
|
|
39
|
-
}
|
|
40
|
-
export type ToolResult = ToolSuccess | ToolFailure;
|
|
41
|
-
export interface SandboxTool {
|
|
42
|
-
descriptor: ToolDescriptor;
|
|
43
|
-
invoke(rawArgs: string, ctx: ToolContext): Promise<ToolResult>;
|
|
44
|
-
}
|
|
45
|
-
/** Truncate a result payload to `maxBytes` with a visible cutoff marker. */
|
|
46
|
-
export declare function truncatePayload(payload: string, maxBytes: number): string;
|
|
47
|
-
export declare function parseArgs(raw: string): Record<string, unknown>;
|
|
48
|
-
export declare function requireString(args: Record<string, unknown>, key: string): string;
|
|
49
|
-
export declare function optionalNumber(args: Record<string, unknown>, key: string): number | undefined;
|
package/dist/eval/tools/types.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/** Truncate a result payload to `maxBytes` with a visible cutoff marker. */
|
|
2
|
-
export function truncatePayload(payload, maxBytes) {
|
|
3
|
-
if (Buffer.byteLength(payload, "utf8") <= maxBytes)
|
|
4
|
-
return payload;
|
|
5
|
-
const marker = "\n…[truncated by cclaw sandbox]";
|
|
6
|
-
const budget = Math.max(0, maxBytes - Buffer.byteLength(marker, "utf8"));
|
|
7
|
-
const buf = Buffer.from(payload, "utf8").subarray(0, budget);
|
|
8
|
-
return `${buf.toString("utf8")}${marker}`;
|
|
9
|
-
}
|
|
10
|
-
export function parseArgs(raw) {
|
|
11
|
-
if (typeof raw !== "string" || raw.trim() === "") {
|
|
12
|
-
throw new Error("tool arguments missing");
|
|
13
|
-
}
|
|
14
|
-
let parsed;
|
|
15
|
-
try {
|
|
16
|
-
parsed = JSON.parse(raw);
|
|
17
|
-
}
|
|
18
|
-
catch (err) {
|
|
19
|
-
throw new Error(`tool arguments are not valid JSON: ${err.message}`);
|
|
20
|
-
}
|
|
21
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
22
|
-
throw new Error("tool arguments must be a JSON object");
|
|
23
|
-
}
|
|
24
|
-
return parsed;
|
|
25
|
-
}
|
|
26
|
-
export function requireString(args, key) {
|
|
27
|
-
const value = args[key];
|
|
28
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
29
|
-
throw new Error(`"${key}" must be a non-empty string`);
|
|
30
|
-
}
|
|
31
|
-
return value;
|
|
32
|
-
}
|
|
33
|
-
export function optionalNumber(args, key) {
|
|
34
|
-
const value = args[key];
|
|
35
|
-
if (value === undefined || value === null)
|
|
36
|
-
return undefined;
|
|
37
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
38
|
-
throw new Error(`"${key}" must be a finite number`);
|
|
39
|
-
}
|
|
40
|
-
return value;
|
|
41
|
-
}
|
package/dist/eval/tools/write.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { SandboxEscapeError } from "../sandbox.js";
|
|
4
|
-
import { parseArgs, requireString, truncatePayload } from "./types.js";
|
|
5
|
-
const DESCRIPTION = "Write a UTF-8 text file inside the sandbox. Creates parent directories " +
|
|
6
|
-
"as needed. Overwrites existing files. Only paths inside the sandbox " +
|
|
7
|
-
"are accepted.";
|
|
8
|
-
export const writeTool = {
|
|
9
|
-
descriptor: {
|
|
10
|
-
name: "write_file",
|
|
11
|
-
description: DESCRIPTION,
|
|
12
|
-
parameters: {
|
|
13
|
-
type: "object",
|
|
14
|
-
additionalProperties: false,
|
|
15
|
-
required: ["path", "content"],
|
|
16
|
-
properties: {
|
|
17
|
-
path: {
|
|
18
|
-
type: "string",
|
|
19
|
-
description: "Path relative to the sandbox root."
|
|
20
|
-
},
|
|
21
|
-
content: {
|
|
22
|
-
type: "string",
|
|
23
|
-
description: "UTF-8 contents to write."
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
async invoke(rawArgs, ctx) {
|
|
29
|
-
let args;
|
|
30
|
-
try {
|
|
31
|
-
args = parseArgs(rawArgs);
|
|
32
|
-
}
|
|
33
|
-
catch (err) {
|
|
34
|
-
return { ok: false, name: this.descriptor.name, error: err.message };
|
|
35
|
-
}
|
|
36
|
-
let relPath;
|
|
37
|
-
try {
|
|
38
|
-
relPath = requireString(args, "path");
|
|
39
|
-
}
|
|
40
|
-
catch (err) {
|
|
41
|
-
return { ok: false, name: this.descriptor.name, error: err.message };
|
|
42
|
-
}
|
|
43
|
-
const rawContent = args.content;
|
|
44
|
-
if (typeof rawContent !== "string") {
|
|
45
|
-
return {
|
|
46
|
-
ok: false,
|
|
47
|
-
name: this.descriptor.name,
|
|
48
|
-
error: '"content" must be a string'
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
const payloadBytes = Buffer.byteLength(rawContent, "utf8");
|
|
52
|
-
if (payloadBytes > ctx.maxResultBytes * 4) {
|
|
53
|
-
return {
|
|
54
|
-
ok: false,
|
|
55
|
-
name: this.descriptor.name,
|
|
56
|
-
error: `"content" exceeds per-invocation ceiling (${payloadBytes} bytes).`
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
let abs;
|
|
60
|
-
try {
|
|
61
|
-
abs = await ctx.sandbox.resolve(relPath, { allowMissing: true });
|
|
62
|
-
}
|
|
63
|
-
catch (err) {
|
|
64
|
-
const denied = err instanceof SandboxEscapeError ? relPath : undefined;
|
|
65
|
-
return {
|
|
66
|
-
ok: false,
|
|
67
|
-
name: this.descriptor.name,
|
|
68
|
-
error: err.message,
|
|
69
|
-
details: denied ? { deniedPath: denied } : undefined
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
74
|
-
await fs.writeFile(abs, rawContent, "utf8");
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
return {
|
|
78
|
-
ok: false,
|
|
79
|
-
name: this.descriptor.name,
|
|
80
|
-
error: `write failed: ${err.message}`,
|
|
81
|
-
details: { path: relPath }
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
const summary = `wrote ${payloadBytes} byte(s) to ${relPath}`;
|
|
85
|
-
return {
|
|
86
|
-
ok: true,
|
|
87
|
-
name: this.descriptor.name,
|
|
88
|
-
content: truncatePayload(summary, ctx.maxResultBytes),
|
|
89
|
-
details: { path: relPath, bytes: payloadBytes }
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
};
|