opencastle 0.31.7 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/bin/cli.mjs +15 -0
- package/dist/cli/agents.d.ts.map +1 -1
- package/dist/cli/agents.js +19 -5
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/artifacts-cli.d.ts +3 -0
- package/dist/cli/artifacts-cli.d.ts.map +1 -0
- package/dist/cli/artifacts-cli.js +36 -0
- package/dist/cli/artifacts-cli.js.map +1 -0
- package/dist/cli/baselines.d.ts.map +1 -1
- package/dist/cli/baselines.js +11 -0
- package/dist/cli/baselines.js.map +1 -1
- package/dist/cli/convoy/artifacts.d.ts +25 -0
- package/dist/cli/convoy/artifacts.d.ts.map +1 -0
- package/dist/cli/convoy/artifacts.js +129 -0
- package/dist/cli/convoy/artifacts.js.map +1 -0
- package/dist/cli/convoy/artifacts.test.d.ts +2 -0
- package/dist/cli/convoy/artifacts.test.d.ts.map +1 -0
- package/dist/cli/convoy/artifacts.test.js +169 -0
- package/dist/cli/convoy/artifacts.test.js.map +1 -0
- package/dist/cli/convoy/compaction.d.ts +23 -0
- package/dist/cli/convoy/compaction.d.ts.map +1 -0
- package/dist/cli/convoy/compaction.js +117 -0
- package/dist/cli/convoy/compaction.js.map +1 -0
- package/dist/cli/convoy/compaction.test.d.ts +2 -0
- package/dist/cli/convoy/compaction.test.d.ts.map +1 -0
- package/dist/cli/convoy/compaction.test.js +205 -0
- package/dist/cli/convoy/compaction.test.js.map +1 -0
- package/dist/cli/convoy/contracts.d.ts +22 -0
- package/dist/cli/convoy/contracts.d.ts.map +1 -0
- package/dist/cli/convoy/contracts.js +254 -0
- package/dist/cli/convoy/contracts.js.map +1 -0
- package/dist/cli/convoy/contracts.test.d.ts +2 -0
- package/dist/cli/convoy/contracts.test.d.ts.map +1 -0
- package/dist/cli/convoy/contracts.test.js +239 -0
- package/dist/cli/convoy/contracts.test.js.map +1 -0
- package/dist/cli/convoy/dag-analysis.d.ts +40 -0
- package/dist/cli/convoy/dag-analysis.d.ts.map +1 -0
- package/dist/cli/convoy/dag-analysis.js +282 -0
- package/dist/cli/convoy/dag-analysis.js.map +1 -0
- package/dist/cli/convoy/dag-analysis.test.d.ts +2 -0
- package/dist/cli/convoy/dag-analysis.test.d.ts.map +1 -0
- package/dist/cli/convoy/dag-analysis.test.js +289 -0
- package/dist/cli/convoy/dag-analysis.test.js.map +1 -0
- package/dist/cli/convoy/effort-scaling.d.ts +20 -0
- package/dist/cli/convoy/effort-scaling.d.ts.map +1 -0
- package/dist/cli/convoy/effort-scaling.js +82 -0
- package/dist/cli/convoy/effort-scaling.js.map +1 -0
- package/dist/cli/convoy/effort-scaling.test.d.ts +2 -0
- package/dist/cli/convoy/effort-scaling.test.d.ts.map +1 -0
- package/dist/cli/convoy/effort-scaling.test.js +120 -0
- package/dist/cli/convoy/effort-scaling.test.js.map +1 -0
- package/dist/cli/convoy/engine.d.ts.map +1 -1
- package/dist/cli/convoy/engine.js +280 -6
- package/dist/cli/convoy/engine.js.map +1 -1
- package/dist/cli/convoy/engine.test.js +155 -18
- package/dist/cli/convoy/engine.test.js.map +1 -1
- package/dist/cli/convoy/event-schemas.d.ts.map +1 -1
- package/dist/cli/convoy/event-schemas.js +55 -0
- package/dist/cli/convoy/event-schemas.js.map +1 -1
- package/dist/cli/convoy/isolation.d.ts +27 -0
- package/dist/cli/convoy/isolation.d.ts.map +1 -0
- package/dist/cli/convoy/isolation.js +120 -0
- package/dist/cli/convoy/isolation.js.map +1 -0
- package/dist/cli/convoy/isolation.test.d.ts +2 -0
- package/dist/cli/convoy/isolation.test.d.ts.map +1 -0
- package/dist/cli/convoy/isolation.test.js +105 -0
- package/dist/cli/convoy/isolation.test.js.map +1 -0
- package/dist/cli/convoy/review-stages.d.ts +9 -0
- package/dist/cli/convoy/review-stages.d.ts.map +1 -0
- package/dist/cli/convoy/review-stages.js +134 -0
- package/dist/cli/convoy/review-stages.js.map +1 -0
- package/dist/cli/convoy/review-stages.test.d.ts +2 -0
- package/dist/cli/convoy/review-stages.test.d.ts.map +1 -0
- package/dist/cli/convoy/review-stages.test.js +197 -0
- package/dist/cli/convoy/review-stages.test.js.map +1 -0
- package/dist/cli/convoy/skill-refinement.d.ts +39 -0
- package/dist/cli/convoy/skill-refinement.d.ts.map +1 -0
- package/dist/cli/convoy/skill-refinement.js +239 -0
- package/dist/cli/convoy/skill-refinement.js.map +1 -0
- package/dist/cli/convoy/skill-refinement.test.d.ts +2 -0
- package/dist/cli/convoy/skill-refinement.test.d.ts.map +1 -0
- package/dist/cli/convoy/skill-refinement.test.js +230 -0
- package/dist/cli/convoy/skill-refinement.test.js.map +1 -0
- package/dist/cli/convoy/spec-builder.d.ts +1 -0
- package/dist/cli/convoy/spec-builder.d.ts.map +1 -1
- package/dist/cli/convoy/spec-builder.js +11 -0
- package/dist/cli/convoy/spec-builder.js.map +1 -1
- package/dist/cli/convoy/spec-builder.test.js +54 -0
- package/dist/cli/convoy/spec-builder.test.js.map +1 -1
- package/dist/cli/convoy/store.d.ts +3 -2
- package/dist/cli/convoy/store.d.ts.map +1 -1
- package/dist/cli/convoy/store.js +20 -2
- package/dist/cli/convoy/store.js.map +1 -1
- package/dist/cli/convoy/store.test.js +15 -15
- package/dist/cli/convoy/store.test.js.map +1 -1
- package/dist/cli/convoy/tdd-gate.d.ts +15 -0
- package/dist/cli/convoy/tdd-gate.d.ts.map +1 -0
- package/dist/cli/convoy/tdd-gate.js +119 -0
- package/dist/cli/convoy/tdd-gate.js.map +1 -0
- package/dist/cli/convoy/tdd-gate.test.d.ts +2 -0
- package/dist/cli/convoy/tdd-gate.test.d.ts.map +1 -0
- package/dist/cli/convoy/tdd-gate.test.js +227 -0
- package/dist/cli/convoy/tdd-gate.test.js.map +1 -0
- package/dist/cli/convoy/types.d.ts +91 -0
- package/dist/cli/convoy/types.d.ts.map +1 -1
- package/dist/cli/convoy/types.js +8 -0
- package/dist/cli/convoy/types.js.map +1 -1
- package/dist/cli/insights.d.ts +3 -0
- package/dist/cli/insights.d.ts.map +1 -0
- package/dist/cli/insights.js +94 -0
- package/dist/cli/insights.js.map +1 -0
- package/dist/cli/lesson.d.ts.map +1 -1
- package/dist/cli/lesson.js +7 -0
- package/dist/cli/lesson.js.map +1 -1
- package/dist/cli/log.d.ts.map +1 -1
- package/dist/cli/log.js +7 -0
- package/dist/cli/log.js.map +1 -1
- package/dist/cli/package-config.d.ts +12 -0
- package/dist/cli/package-config.d.ts.map +1 -0
- package/dist/cli/package-config.js +37 -0
- package/dist/cli/package-config.js.map +1 -0
- package/dist/cli/package.d.ts +23 -0
- package/dist/cli/package.d.ts.map +1 -0
- package/dist/cli/package.js +285 -0
- package/dist/cli/package.js.map +1 -0
- package/dist/cli/package.test.d.ts +2 -0
- package/dist/cli/package.test.d.ts.map +1 -0
- package/dist/cli/package.test.js +236 -0
- package/dist/cli/package.test.js.map +1 -0
- package/dist/cli/pipeline.d.ts +6 -0
- package/dist/cli/pipeline.d.ts.map +1 -1
- package/dist/cli/pipeline.js +15 -2
- package/dist/cli/pipeline.js.map +1 -1
- package/dist/cli/run/schema.d.ts.map +1 -1
- package/dist/cli/run/schema.js +32 -0
- package/dist/cli/run/schema.js.map +1 -1
- package/dist/cli/run/schema.test.js +51 -0
- package/dist/cli/run/schema.test.js.map +1 -1
- package/dist/cli/skills.d.ts +3 -0
- package/dist/cli/skills.d.ts.map +1 -0
- package/dist/cli/skills.js +107 -0
- package/dist/cli/skills.js.map +1 -0
- package/dist/cli/types.d.ts +4 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/dashboard/scripts/etl.d.ts.map +1 -1
- package/dist/dashboard/scripts/etl.js +44 -11
- package/dist/dashboard/scripts/etl.js.map +1 -1
- package/package.json +2 -1
- package/src/cli/agents.ts +20 -5
- package/src/cli/artifacts-cli.ts +41 -0
- package/src/cli/baselines.ts +12 -0
- package/src/cli/convoy/artifacts.test.ts +201 -0
- package/src/cli/convoy/artifacts.ts +186 -0
- package/src/cli/convoy/compaction.test.ts +245 -0
- package/src/cli/convoy/compaction.ts +164 -0
- package/src/cli/convoy/contracts.test.ts +279 -0
- package/src/cli/convoy/contracts.ts +280 -0
- package/src/cli/convoy/dag-analysis.test.ts +349 -0
- package/src/cli/convoy/dag-analysis.ts +371 -0
- package/src/cli/convoy/effort-scaling.test.ts +140 -0
- package/src/cli/convoy/effort-scaling.ts +90 -0
- package/src/cli/convoy/engine.test.ts +175 -18
- package/src/cli/convoy/engine.ts +301 -7
- package/src/cli/convoy/event-schemas.ts +55 -0
- package/src/cli/convoy/isolation.test.ts +137 -0
- package/src/cli/convoy/isolation.ts +165 -0
- package/src/cli/convoy/review-stages.test.ts +235 -0
- package/src/cli/convoy/review-stages.ts +166 -0
- package/src/cli/convoy/skill-refinement.test.ts +277 -0
- package/src/cli/convoy/skill-refinement.ts +306 -0
- package/src/cli/convoy/spec-builder.test.ts +61 -0
- package/src/cli/convoy/spec-builder.ts +9 -0
- package/src/cli/convoy/store.test.ts +15 -15
- package/src/cli/convoy/store.ts +26 -4
- package/src/cli/convoy/tdd-gate.test.ts +281 -0
- package/src/cli/convoy/tdd-gate.ts +154 -0
- package/src/cli/convoy/types.ts +51 -0
- package/src/cli/insights.ts +99 -0
- package/src/cli/lesson.ts +8 -0
- package/src/cli/log.ts +8 -0
- package/src/cli/package-config.ts +48 -0
- package/src/cli/package.test.ts +276 -0
- package/src/cli/package.ts +329 -0
- package/src/cli/pipeline.ts +21 -2
- package/src/cli/run/schema.test.ts +58 -0
- package/src/cli/run/schema.ts +33 -0
- package/src/cli/skills.ts +121 -0
- package/src/cli/types.ts +4 -1
- package/src/dashboard/dist/_astro/index.D6quLrA6.css +1 -0
- package/src/dashboard/dist/data/convoy-list.json +21 -7
- package/src/dashboard/dist/data/convoys/demo-api-v2.json +3 -3
- package/src/dashboard/dist/data/convoys/demo-auth-revamp.json +5 -5
- package/src/dashboard/dist/data/convoys/demo-convoy-1.json +2 -2
- package/src/dashboard/dist/data/convoys/demo-convoy-2.json +1 -1
- package/src/dashboard/dist/data/convoys/demo-dashboard-ui.json +7 -7
- package/src/dashboard/dist/data/convoys/demo-data-pipeline.json +3 -3
- package/src/dashboard/dist/data/convoys/demo-deploy-ci.json +2 -2
- package/src/dashboard/dist/data/convoys/demo-docs-update.json +2 -2
- package/src/dashboard/dist/data/convoys/demo-perf-opt.json +4 -4
- package/src/dashboard/dist/index.html +306 -33
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/public/data/convoy-list.json +21 -7
- package/src/dashboard/public/data/convoys/demo-api-v2.json +3 -3
- package/src/dashboard/public/data/convoys/demo-auth-revamp.json +5 -5
- package/src/dashboard/public/data/convoys/demo-convoy-1.json +2 -2
- package/src/dashboard/public/data/convoys/demo-convoy-2.json +1 -1
- package/src/dashboard/public/data/convoys/demo-dashboard-ui.json +7 -7
- package/src/dashboard/public/data/convoys/demo-data-pipeline.json +3 -3
- package/src/dashboard/public/data/convoys/demo-deploy-ci.json +2 -2
- package/src/dashboard/public/data/convoys/demo-docs-update.json +2 -2
- package/src/dashboard/public/data/convoys/demo-perf-opt.json +4 -4
- package/src/dashboard/scripts/etl.test.ts +14 -0
- package/src/dashboard/scripts/etl.ts +48 -16
- package/src/dashboard/scripts/generate-demo-db.ts +18 -10
- package/src/dashboard/src/pages/index.astro +348 -45
- package/src/dashboard/src/styles/dashboard.css +56 -0
- package/src/orchestrator/prompts/assess-complexity.prompt.md +13 -0
- package/src/orchestrator/prompts/generate-convoy.prompt.md +19 -0
- package/src/dashboard/dist/_astro/index.BRDFmNzR.css +0 -1
package/README.md
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
<a href="https://github.com/etylsarin/opencastle/stargazers"><img src="https://img.shields.io/github/stars/etylsarin/opencastle?style=flat" alt="GitHub stars" /></a>
|
|
13
13
|
<a href="https://www.npmjs.com/package/opencastle"><img src="https://img.shields.io/npm/v/opencastle.svg?v=1" alt="npm version" /></a>
|
|
14
14
|
<a href="https://github.com/etylsarin/opencastle/actions/workflows/ci.yml"><img src="https://github.com/etylsarin/opencastle/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
15
|
-
<a href="LICENSE"><img src="https://img.shields.io/npm/l/opencastle.svg?v=1" alt="license" /></a>
|
|
16
15
|
<a href="https://www.npmjs.com/package/opencastle"><img src="https://img.shields.io/npm/dm/opencastle.svg?v=1" alt="downloads" /></a>
|
|
17
16
|
</p>
|
|
18
17
|
|
|
@@ -82,6 +81,10 @@ MCP servers are auto-configured for your stack in each IDE's native format.
|
|
|
82
81
|
| `opencastle baselines` | Manage visual regression baselines |
|
|
83
82
|
| `opencastle log` | Append a structured event to the observability log |
|
|
84
83
|
| `opencastle lesson` | Append a structured lesson to LESSONS-LEARNED.md |
|
|
84
|
+
| `opencastle artifacts` | Manage filesystem artifact storage (prune old convoy artifacts) |
|
|
85
|
+
| `opencastle insights` | Analyze convoy execution history and generate recommendations |
|
|
86
|
+
| `opencastle skills` | Skill refinement and failure tracking |
|
|
87
|
+
| `opencastle package` | Package orchestrator as a plugin for IDE marketplaces |
|
|
85
88
|
|
|
86
89
|
Add `--dry-run` to any command to preview what it would change without writing files.
|
|
87
90
|
|
package/bin/cli.mjs
CHANGED
|
@@ -39,6 +39,8 @@ const HELP = `
|
|
|
39
39
|
baselines Manage visual regression baselines
|
|
40
40
|
log Append a structured event to the observability log
|
|
41
41
|
lesson Append a structured lesson to LESSONS-LEARNED.md
|
|
42
|
+
artifacts Manage filesystem artifact storage (prune old convoy artifacts)
|
|
43
|
+
insights Analyze convoy execution history and generate recommendations
|
|
42
44
|
|
|
43
45
|
Options:
|
|
44
46
|
--dry-run Preview what a command would change without writing files
|
|
@@ -59,6 +61,15 @@ if (command === '--version' || command === '-v') {
|
|
|
59
61
|
process.exit(0)
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
// Handle --version anywhere in args (e.g., "opencastle init --version")
|
|
65
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
66
|
+
const pkg = JSON.parse(
|
|
67
|
+
await readFile(resolve(pkgRoot, 'package.json'), 'utf8')
|
|
68
|
+
)
|
|
69
|
+
console.log(pkg.version)
|
|
70
|
+
process.exit(0)
|
|
71
|
+
}
|
|
72
|
+
|
|
62
73
|
const commands = {
|
|
63
74
|
init: () => import('../dist/cli/init.js'),
|
|
64
75
|
update: () => import('../dist/cli/update.js'),
|
|
@@ -75,6 +86,10 @@ const commands = {
|
|
|
75
86
|
dispute: () => import('../dist/cli/dispute.js'),
|
|
76
87
|
baselines: () => import('../dist/cli/baselines.js'),
|
|
77
88
|
validate: () => import('../dist/cli/validate.js'),
|
|
89
|
+
artifacts: () => import('../dist/cli/artifacts-cli.js'),
|
|
90
|
+
insights: () => import('../dist/cli/insights.js'),
|
|
91
|
+
skills: () => import('../dist/cli/skills.js'),
|
|
92
|
+
package: () => import('../dist/cli/package.js'),
|
|
78
93
|
}
|
|
79
94
|
|
|
80
95
|
if (!commands[command]) {
|
package/dist/cli/agents.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/cli/agents.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/cli/agents.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAgF5C,wBAA8B,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA6GxE"}
|
package/dist/cli/agents.js
CHANGED
|
@@ -15,6 +15,7 @@ const HELP = `
|
|
|
15
15
|
Options:
|
|
16
16
|
--older-than <days> Days threshold for compact subcommand
|
|
17
17
|
--yes, -y Skip confirmation prompt
|
|
18
|
+
--dry-run Preview what would be deleted without deleting
|
|
18
19
|
--help, -h Show this help
|
|
19
20
|
`;
|
|
20
21
|
function parseAgentsArgs(args) {
|
|
@@ -23,6 +24,7 @@ function parseAgentsArgs(args) {
|
|
|
23
24
|
agentName: null,
|
|
24
25
|
olderThan: null,
|
|
25
26
|
yes: false,
|
|
27
|
+
dryRun: false,
|
|
26
28
|
help: false,
|
|
27
29
|
};
|
|
28
30
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -47,6 +49,10 @@ function parseAgentsArgs(args) {
|
|
|
47
49
|
case '-y':
|
|
48
50
|
opts.yes = true;
|
|
49
51
|
break;
|
|
52
|
+
case '--dry-run':
|
|
53
|
+
case '--dryRun':
|
|
54
|
+
opts.dryRun = true;
|
|
55
|
+
break;
|
|
50
56
|
default:
|
|
51
57
|
if (arg.startsWith('--')) {
|
|
52
58
|
console.error(` \u2717 Unknown option: ${arg}`);
|
|
@@ -125,12 +131,16 @@ export default async function agents({ args }) {
|
|
|
125
131
|
console.error(' \u2717 purge requires an agent name: opencastle agents purge <agent>');
|
|
126
132
|
process.exit(1);
|
|
127
133
|
}
|
|
134
|
+
const existing = store.getAgentIdentities(opts.agentName, 1000);
|
|
135
|
+
if (existing.length === 0) {
|
|
136
|
+
console.log(` No identities found for agent "${opts.agentName}".`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (opts.dryRun) {
|
|
140
|
+
console.log(` [dry-run] Would purge ${existing.length} identities for agent "${opts.agentName}"`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
128
143
|
if (!opts.yes) {
|
|
129
|
-
const existing = store.getAgentIdentities(opts.agentName, 1000);
|
|
130
|
-
if (existing.length === 0) {
|
|
131
|
-
console.log(` No identities found for agent "${opts.agentName}".`);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
144
|
console.log(`\n This will delete ${existing.length} identities for agent "${opts.agentName}".`);
|
|
135
145
|
console.log(` Use --yes or -y to confirm.`);
|
|
136
146
|
return;
|
|
@@ -144,6 +154,10 @@ export default async function agents({ args }) {
|
|
|
144
154
|
console.error(' \u2717 compact requires --older-than <days>');
|
|
145
155
|
process.exit(1);
|
|
146
156
|
}
|
|
157
|
+
if (opts.dryRun) {
|
|
158
|
+
console.log(` [dry-run] Would delete identities older than ${opts.olderThan} days`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
147
161
|
const deleted = store.deleteAgentIdentitiesOlderThan(opts.olderThan);
|
|
148
162
|
console.log(` \u2713 Deleted ${deleted} identities older than ${opts.olderThan} days.`);
|
|
149
163
|
break;
|
package/dist/cli/agents.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/cli/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,CAAC,EAAE,MAAM,aAAa,CAAA;AAE/B,MAAM,IAAI,GAAG
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/cli/agents.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts-cli.d.ts","sourceRoot":"","sources":["../../src/cli/artifacts-cli.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAgB5C,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,UAAU,GAAG,IAAI,CAuB/D"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { pruneArtifacts } from './convoy/artifacts.js';
|
|
2
|
+
const HELP = `
|
|
3
|
+
opencastle artifacts <subcommand> [options]
|
|
4
|
+
|
|
5
|
+
Manage filesystem artifact storage.
|
|
6
|
+
|
|
7
|
+
Subcommands:
|
|
8
|
+
prune Prune old convoy artifact directories
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
--keep <N> Number of recent artifacts to keep (default: 10)
|
|
12
|
+
--dry-run Preview what would be pruned without deleting
|
|
13
|
+
--help, -h Show this help
|
|
14
|
+
`;
|
|
15
|
+
export default function artifactsCli({ args }) {
|
|
16
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
17
|
+
console.log(HELP);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const dryRun = args.includes('--dry-run') || args.includes('--dryRun');
|
|
21
|
+
const subcommand = args.find(a => !a.startsWith('--'));
|
|
22
|
+
if (subcommand === 'prune') {
|
|
23
|
+
const keepIdx = args.indexOf('--keep');
|
|
24
|
+
const keepCount = keepIdx >= 0 && args[keepIdx + 1] ? parseInt(args[keepIdx + 1], 10) : 10;
|
|
25
|
+
if (dryRun) {
|
|
26
|
+
console.log(` [dry-run] Would prune convoy artifacts, keeping ${keepCount} most recent`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const result = pruneArtifacts(keepCount);
|
|
30
|
+
console.log(`Pruned ${result.removed} convoy artifact directories, freed ${(result.freed_bytes / 1024).toFixed(1)} KB`);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
console.log(HELP);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=artifacts-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts-cli.js","sourceRoot":"","sources":["../../src/cli/artifacts-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAGtD,MAAM,IAAI,GAAG;;;;;;;;;;;;CAYZ,CAAA;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,IAAI,EAAc;IACvD,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACjB,OAAM;IACR,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;IACtE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;IAEtD,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QACtC,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QAE1F,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,qDAAqD,SAAS,cAAc,CAAC,CAAA;YACzF,OAAM;QACR,CAAC;QAED,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,CAAA;QACxC,OAAO,CAAC,GAAG,CAAC,UAAU,MAAM,CAAC,OAAO,uCAAuC,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IACzH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACnB,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baselines.d.ts","sourceRoot":"","sources":["../../src/cli/baselines.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"baselines.d.ts","sourceRoot":"","sources":["../../src/cli/baselines.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAoF5C,wBAA8B,SAAS,CAAC,EAAE,IAAI,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAoE3E"}
|
package/dist/cli/baselines.js
CHANGED
|
@@ -14,6 +14,7 @@ const HELP = `
|
|
|
14
14
|
--slug <name> Baseline name (used as filename)
|
|
15
15
|
--from <file> Source PNG file path
|
|
16
16
|
--dir <path> Baselines directory (default: .opencastle/baselines)
|
|
17
|
+
--dry-run Preview what would be updated without writing
|
|
17
18
|
--help, -h Show this help
|
|
18
19
|
`;
|
|
19
20
|
function parseBaselinesArgs(args) {
|
|
@@ -22,6 +23,7 @@ function parseBaselinesArgs(args) {
|
|
|
22
23
|
slug: null,
|
|
23
24
|
from: null,
|
|
24
25
|
dir: '.opencastle/baselines',
|
|
26
|
+
dryRun: false,
|
|
25
27
|
help: false,
|
|
26
28
|
};
|
|
27
29
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -52,6 +54,10 @@ function parseBaselinesArgs(args) {
|
|
|
52
54
|
}
|
|
53
55
|
opts.dir = args[++i];
|
|
54
56
|
break;
|
|
57
|
+
case '--dry-run':
|
|
58
|
+
case '--dryRun':
|
|
59
|
+
opts.dryRun = true;
|
|
60
|
+
break;
|
|
55
61
|
default:
|
|
56
62
|
if (arg.startsWith('--')) {
|
|
57
63
|
console.error(` \u2717 Unknown option: ${arg}`);
|
|
@@ -95,6 +101,11 @@ export default async function baselines({ args }) {
|
|
|
95
101
|
console.error(' \u2717 Source file contains potential secrets \u2014 baseline not updated');
|
|
96
102
|
process.exit(1);
|
|
97
103
|
}
|
|
104
|
+
if (opts.dryRun) {
|
|
105
|
+
const dest = join(opts.dir, `${opts.slug}.png`);
|
|
106
|
+
console.log(` [dry-run] Would update baseline: ${dest}`);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
98
109
|
mkdirSync(opts.dir, { recursive: true });
|
|
99
110
|
const dest = join(opts.dir, `${opts.slug}.png`);
|
|
100
111
|
writeFileSync(dest, data);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baselines.js","sourceRoot":"","sources":["../../src/cli/baselines.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACnG,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAE1C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAElD,MAAM,IAAI,GAAG
|
|
1
|
+
{"version":3,"file":"baselines.js","sourceRoot":"","sources":["../../src/cli/baselines.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface Artifact {
|
|
2
|
+
task_id: string;
|
|
3
|
+
convoy_id: string;
|
|
4
|
+
filename: string;
|
|
5
|
+
type: 'report' | 'code' | 'data' | 'diff' | 'log' | 'other';
|
|
6
|
+
size_bytes: number;
|
|
7
|
+
summary: string;
|
|
8
|
+
path: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ArtifactRef {
|
|
11
|
+
task_id: string;
|
|
12
|
+
filename: string;
|
|
13
|
+
summary: string;
|
|
14
|
+
path: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function getArtifactDir(convoyId: string, taskId: string): string;
|
|
17
|
+
export declare function writeArtifact(convoyId: string, taskId: string, filename: string, content: string, type: Artifact['type']): Artifact;
|
|
18
|
+
export declare function listArtifacts(convoyId: string, taskId: string): ArtifactRef[];
|
|
19
|
+
export declare function readArtifact(ref: ArtifactRef): string;
|
|
20
|
+
export declare function extractArtifactRefs(taskId: string, convoyId: string, output: string): ArtifactRef[];
|
|
21
|
+
export declare function pruneArtifacts(keepCount: number): {
|
|
22
|
+
removed: number;
|
|
23
|
+
freed_bytes: number;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=artifacts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../../src/cli/convoy/artifacts.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAA;IAC3D,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAoBD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAIvE;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GACrB,QAAQ,CAgBV;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CA6B7E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAErD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CA2BnG;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CA4B1F"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
|
+
// ── Sanitization ──────────────────────────────────────────────────────────────
|
|
4
|
+
function sanitizeSegment(input) {
|
|
5
|
+
if (input.includes('..') || input.includes('/') || input.includes('\\')) {
|
|
6
|
+
throw new Error(`Invalid path segment "${input}": path traversal characters not allowed`);
|
|
7
|
+
}
|
|
8
|
+
return input.replace(/[^a-zA-Z0-9\-_.]/g, '');
|
|
9
|
+
}
|
|
10
|
+
// ── Core ──────────────────────────────────────────────────────────────────────
|
|
11
|
+
export function getArtifactDir(convoyId, taskId) {
|
|
12
|
+
const safeConvoyId = sanitizeSegment(convoyId);
|
|
13
|
+
const safeTaskId = sanitizeSegment(taskId);
|
|
14
|
+
return join(process.cwd(), '.opencastle', 'artifacts', safeConvoyId, safeTaskId) + '/';
|
|
15
|
+
}
|
|
16
|
+
export function writeArtifact(convoyId, taskId, filename, content, type) {
|
|
17
|
+
const safeFilename = sanitizeSegment(filename);
|
|
18
|
+
const dir = getArtifactDir(convoyId, taskId);
|
|
19
|
+
mkdirSync(dir, { recursive: true });
|
|
20
|
+
const filePath = join(dir, safeFilename);
|
|
21
|
+
writeFileSync(filePath, content, 'utf8');
|
|
22
|
+
const size_bytes = Buffer.byteLength(content, 'utf8');
|
|
23
|
+
const firstLine = content.split('\n')[0] ?? '';
|
|
24
|
+
const summary = firstLine.slice(0, 120);
|
|
25
|
+
const meta = { type, summary, size_bytes, created_at: new Date().toISOString() };
|
|
26
|
+
writeFileSync(join(dir, safeFilename + '.meta.json'), JSON.stringify(meta, null, 2), 'utf8');
|
|
27
|
+
return { task_id: taskId, convoy_id: convoyId, filename: safeFilename, type, size_bytes, summary, path: filePath };
|
|
28
|
+
}
|
|
29
|
+
export function listArtifacts(convoyId, taskId) {
|
|
30
|
+
const dir = getArtifactDir(convoyId, taskId);
|
|
31
|
+
if (!existsSync(dir))
|
|
32
|
+
return [];
|
|
33
|
+
const refs = [];
|
|
34
|
+
for (const entry of readdirSync(dir)) {
|
|
35
|
+
if (entry.endsWith('.meta.json'))
|
|
36
|
+
continue;
|
|
37
|
+
const filePath = join(dir, entry);
|
|
38
|
+
const metaPath = join(dir, entry + '.meta.json');
|
|
39
|
+
let summary = '';
|
|
40
|
+
if (existsSync(metaPath)) {
|
|
41
|
+
try {
|
|
42
|
+
const meta = JSON.parse(readFileSync(metaPath, 'utf8'));
|
|
43
|
+
summary = meta.summary;
|
|
44
|
+
}
|
|
45
|
+
catch { /* fallback */ }
|
|
46
|
+
}
|
|
47
|
+
if (!summary) {
|
|
48
|
+
try {
|
|
49
|
+
const firstLine = readFileSync(filePath, 'utf8').split('\n')[0] ?? '';
|
|
50
|
+
summary = firstLine.slice(0, 120);
|
|
51
|
+
}
|
|
52
|
+
catch { /* non-critical */ }
|
|
53
|
+
}
|
|
54
|
+
refs.push({ task_id: taskId, filename: entry, summary, path: filePath });
|
|
55
|
+
}
|
|
56
|
+
return refs;
|
|
57
|
+
}
|
|
58
|
+
export function readArtifact(ref) {
|
|
59
|
+
return readFileSync(ref.path, 'utf8');
|
|
60
|
+
}
|
|
61
|
+
export function extractArtifactRefs(taskId, convoyId, output) {
|
|
62
|
+
const pattern = /\[ARTIFACT:\s*([^\]]+)\]\s*(.+)/g;
|
|
63
|
+
const refs = [];
|
|
64
|
+
let match;
|
|
65
|
+
while ((match = pattern.exec(output)) !== null) {
|
|
66
|
+
// Use basename to prevent path traversal from untrusted agent output
|
|
67
|
+
const filename = basename(match[1].trim());
|
|
68
|
+
const summary = match[2].trim();
|
|
69
|
+
if (!filename || filename === '..') {
|
|
70
|
+
process.stderr.write('[artifacts] Warning: invalid artifact filename from agent output\n');
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const dir = getArtifactDir(convoyId, taskId);
|
|
74
|
+
const filePath = join(dir, filename);
|
|
75
|
+
if (!existsSync(filePath)) {
|
|
76
|
+
process.stderr.write(`[artifacts] Warning: referenced artifact not found: ${filePath}\n`);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
refs.push({ task_id: taskId, filename, summary, path: filePath });
|
|
80
|
+
}
|
|
81
|
+
return refs;
|
|
82
|
+
}
|
|
83
|
+
export function pruneArtifacts(keepCount) {
|
|
84
|
+
const artifactsRoot = join(process.cwd(), '.opencastle', 'artifacts');
|
|
85
|
+
if (!existsSync(artifactsRoot))
|
|
86
|
+
return { removed: 0, freed_bytes: 0 };
|
|
87
|
+
const convoyDirs = readdirSync(artifactsRoot)
|
|
88
|
+
.map(name => {
|
|
89
|
+
const dirPath = join(artifactsRoot, name);
|
|
90
|
+
try {
|
|
91
|
+
return { name, path: dirPath, mtime: statSync(dirPath).mtime.getTime() };
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return { name, path: dirPath, mtime: 0 };
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
98
|
+
const toRemove = convoyDirs.slice(keepCount);
|
|
99
|
+
let removed = 0;
|
|
100
|
+
let freed_bytes = 0;
|
|
101
|
+
for (const dir of toRemove) {
|
|
102
|
+
try {
|
|
103
|
+
freed_bytes += calcDirSize(dir.path);
|
|
104
|
+
rmSync(dir.path, { recursive: true, force: true });
|
|
105
|
+
removed++;
|
|
106
|
+
}
|
|
107
|
+
catch { /* non-critical */ }
|
|
108
|
+
}
|
|
109
|
+
return { removed, freed_bytes };
|
|
110
|
+
}
|
|
111
|
+
function calcDirSize(dirPath) {
|
|
112
|
+
let total = 0;
|
|
113
|
+
try {
|
|
114
|
+
for (const entry of readdirSync(dirPath)) {
|
|
115
|
+
const p = join(dirPath, entry);
|
|
116
|
+
try {
|
|
117
|
+
const s = statSync(p);
|
|
118
|
+
if (s.isDirectory())
|
|
119
|
+
total += calcDirSize(p);
|
|
120
|
+
else
|
|
121
|
+
total += s.size;
|
|
122
|
+
}
|
|
123
|
+
catch { /* skip */ }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch { /* skip */ }
|
|
127
|
+
return total;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=artifacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../../src/cli/convoy/artifacts.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,MAAM,EACN,QAAQ,EACR,aAAa,GACd,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AA4B1C,iFAAiF;AAEjF,SAAS,eAAe,CAAC,KAAa;IACpC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,0CAA0C,CAAC,CAAA;IAC3F,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAA;AAC/C,CAAC;AAED,iFAAiF;AAEjF,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,MAAc;IAC7D,MAAM,YAAY,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;IAC9C,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,CAAA;IAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,CAAC,GAAG,GAAG,CAAA;AACxF,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,QAAgB,EAChB,MAAc,EACd,QAAgB,EAChB,OAAe,EACf,IAAsB;IAEtB,MAAM,YAAY,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;IAC9C,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAC5C,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAEnC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA;IACxC,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;IAExC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IACrD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC9C,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAEvC,MAAM,IAAI,GAAiB,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAA;IAC9F,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,YAAY,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;IAE5F,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;AACpH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,MAAc;IAC5D,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAC5C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAA;IAE/B,MAAM,IAAI,GAAkB,EAAE,CAAA;IAC9B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC;YAAE,SAAQ;QAE1C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,GAAG,YAAY,CAAC,CAAA;QAEhD,IAAI,OAAO,GAAG,EAAE,CAAA;QAChB,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAiB,CAAA;gBACvE,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;YACxB,CAAC;YAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;gBACrE,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YACnC,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;QAChC,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC1E,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAgB;IAC3C,OAAO,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AACvC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,QAAgB,EAAE,MAAc;IAClF,MAAM,OAAO,GAAG,kCAAkC,CAAA;IAClD,MAAM,IAAI,GAAkB,EAAE,CAAA;IAC9B,IAAI,KAA6B,CAAA;IAEjC,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/C,qEAAqE;QACrE,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAE/B,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAA;YAC1F,SAAQ;QACV,CAAC;QAED,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;QAEpC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uDAAuD,QAAQ,IAAI,CAAC,CAAA;YACzF,SAAQ;QACV,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;IACnE,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,WAAW,CAAC,CAAA;IACrE,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAA;IAErE,MAAM,UAAU,GAAG,WAAW,CAAC,aAAa,CAAC;SAC1C,GAAG,CAAC,IAAI,CAAC,EAAE;QACV,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAA;QACzC,IAAI,CAAC;YACH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAA;QAC1E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAA;QAC1C,CAAC;IACH,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAA;IAEpC,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IAC5C,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,IAAI,WAAW,GAAG,CAAC,CAAA;IAEnB,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,WAAW,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACpC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YAClD,OAAO,EAAE,CAAA;QACX,CAAC;QAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,CAAA;AACjC,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,CAAC;QACH,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YACzC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;YAC9B,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;gBACrB,IAAI,CAAC,CAAC,WAAW,EAAE;oBAAE,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAAA;;oBACvC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAA;YACtB,CAAC;YAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;IACtB,OAAO,KAAK,CAAA;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.test.d.ts","sourceRoot":"","sources":["../../../src/cli/convoy/artifacts.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mkdtempSync, rmSync, existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { getArtifactDir, writeArtifact, listArtifacts, readArtifact, extractArtifactRefs, pruneArtifacts, } from './artifacts.js';
|
|
6
|
+
describe('artifacts', () => {
|
|
7
|
+
let tmpDir;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
tmpDir = mkdtempSync(join(tmpdir(), 'artifacts-test-'));
|
|
10
|
+
vi.spyOn(process, 'cwd').mockReturnValue(tmpDir);
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.restoreAllMocks();
|
|
14
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
15
|
+
});
|
|
16
|
+
describe('writeArtifact', () => {
|
|
17
|
+
it('creates file in correct directory structure', () => {
|
|
18
|
+
const artifact = writeArtifact('convoy-1', 'task-1', 'report.md', '# Report\nContent here', 'report');
|
|
19
|
+
expect(artifact.path).toContain('convoy-1');
|
|
20
|
+
expect(artifact.path).toContain('task-1');
|
|
21
|
+
expect(artifact.path).toContain('report.md');
|
|
22
|
+
expect(existsSync(artifact.path)).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
it('creates .meta.json sidecar', () => {
|
|
25
|
+
const artifact = writeArtifact('convoy-1', 'task-1', 'report.md', '# Report\nContent here', 'report');
|
|
26
|
+
const metaPath = artifact.path + '.meta.json';
|
|
27
|
+
expect(existsSync(metaPath)).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
it('returns Artifact with correct metadata', () => {
|
|
30
|
+
const content = '# Report\nLine 2';
|
|
31
|
+
const artifact = writeArtifact('convoy-1', 'task-1', 'report.md', content, 'report');
|
|
32
|
+
expect(artifact.task_id).toBe('task-1');
|
|
33
|
+
expect(artifact.convoy_id).toBe('convoy-1');
|
|
34
|
+
expect(artifact.filename).toBe('report.md');
|
|
35
|
+
expect(artifact.type).toBe('report');
|
|
36
|
+
expect(artifact.size_bytes).toBe(Buffer.byteLength(content, 'utf8'));
|
|
37
|
+
expect(artifact.summary).toBe('# Report');
|
|
38
|
+
});
|
|
39
|
+
it('summary is truncated to 120 chars when first line is long', () => {
|
|
40
|
+
const longFirstLine = 'A'.repeat(200);
|
|
41
|
+
const content = longFirstLine + '\nSecond line';
|
|
42
|
+
const artifact = writeArtifact('convoy-1', 'task-1', 'long.md', content, 'report');
|
|
43
|
+
expect(artifact.summary.length).toBe(120);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe('listArtifacts', () => {
|
|
47
|
+
it('returns refs for all files in task dir excluding .meta.json', () => {
|
|
48
|
+
writeArtifact('convoy-1', 'task-1', 'report.md', '# Report', 'report');
|
|
49
|
+
writeArtifact('convoy-1', 'task-1', 'migration.sql', 'ALTER TABLE users', 'code');
|
|
50
|
+
const refs = listArtifacts('convoy-1', 'task-1');
|
|
51
|
+
expect(refs).toHaveLength(2);
|
|
52
|
+
const filenames = refs.map(r => r.filename);
|
|
53
|
+
expect(filenames).toContain('report.md');
|
|
54
|
+
expect(filenames).toContain('migration.sql');
|
|
55
|
+
});
|
|
56
|
+
it('returns empty array when dir does not exist', () => {
|
|
57
|
+
const refs = listArtifacts('nonexistent-convoy', 'nonexistent-task');
|
|
58
|
+
expect(refs).toHaveLength(0);
|
|
59
|
+
});
|
|
60
|
+
it('populates summary from .meta.json sidecar', () => {
|
|
61
|
+
writeArtifact('convoy-1', 'task-1', 'diff.patch', 'diff --git a/file.ts\nchanges here', 'diff');
|
|
62
|
+
const refs = listArtifacts('convoy-1', 'task-1');
|
|
63
|
+
expect(refs[0].summary).toBe('diff --git a/file.ts');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('readArtifact', () => {
|
|
67
|
+
it('returns file content', () => {
|
|
68
|
+
const content = '# Report\nDetailed content here';
|
|
69
|
+
const artifact = writeArtifact('convoy-1', 'task-1', 'report.md', content, 'report');
|
|
70
|
+
const ref = { task_id: 'task-1', filename: 'report.md', summary: '# Report', path: artifact.path };
|
|
71
|
+
expect(readArtifact(ref)).toBe(content);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('extractArtifactRefs', () => {
|
|
75
|
+
it('parses [ARTIFACT: filename] summary pattern correctly', () => {
|
|
76
|
+
writeArtifact('convoy-1', 'task-1', 'report.md', '# Report', 'report');
|
|
77
|
+
const output = 'I completed the work.\n[ARTIFACT: report.md] Full analysis report\nSee the artifact for details.';
|
|
78
|
+
const refs = extractArtifactRefs('task-1', 'convoy-1', output);
|
|
79
|
+
expect(refs).toHaveLength(1);
|
|
80
|
+
expect(refs[0].filename).toBe('report.md');
|
|
81
|
+
expect(refs[0].summary).toBe('Full analysis report');
|
|
82
|
+
});
|
|
83
|
+
it('parses multiple artifacts in one output', () => {
|
|
84
|
+
writeArtifact('convoy-1', 'task-1', 'report.md', '# Report', 'report');
|
|
85
|
+
writeArtifact('convoy-1', 'task-1', 'migration.sql', 'ALTER TABLE', 'code');
|
|
86
|
+
const output = '[ARTIFACT: report.md] Analysis report\n[ARTIFACT: migration.sql] Database migration script';
|
|
87
|
+
const refs = extractArtifactRefs('task-1', 'convoy-1', output);
|
|
88
|
+
expect(refs).toHaveLength(2);
|
|
89
|
+
});
|
|
90
|
+
it('logs warning to stderr for referenced but missing artifacts', () => {
|
|
91
|
+
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
92
|
+
const output = '[ARTIFACT: missing.md] A report that does not exist on disk';
|
|
93
|
+
const refs = extractArtifactRefs('task-1', 'convoy-1', output);
|
|
94
|
+
expect(refs).toHaveLength(0);
|
|
95
|
+
expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('[artifacts] Warning'));
|
|
96
|
+
});
|
|
97
|
+
it('returns empty array when no artifact patterns found', () => {
|
|
98
|
+
const refs = extractArtifactRefs('task-1', 'convoy-1', 'No artifacts here, just a normal response.');
|
|
99
|
+
expect(refs).toHaveLength(0);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('pruneArtifacts', () => {
|
|
103
|
+
it('removes convoy dirs beyond keepCount', () => {
|
|
104
|
+
writeArtifact('convoy-a', 'task-1', 'file.md', 'content', 'report');
|
|
105
|
+
writeArtifact('convoy-b', 'task-1', 'file.md', 'content', 'report');
|
|
106
|
+
writeArtifact('convoy-c', 'task-1', 'file.md', 'content', 'report');
|
|
107
|
+
const result = pruneArtifacts(1);
|
|
108
|
+
expect(result.removed).toBe(2);
|
|
109
|
+
expect(result.freed_bytes).toBeGreaterThan(0);
|
|
110
|
+
});
|
|
111
|
+
it('returns zero removed when within keepCount', () => {
|
|
112
|
+
writeArtifact('convoy-1', 'task-1', 'file.md', 'content', 'report');
|
|
113
|
+
writeArtifact('convoy-2', 'task-1', 'file.md', 'content', 'report');
|
|
114
|
+
const result = pruneArtifacts(5);
|
|
115
|
+
expect(result.removed).toBe(0);
|
|
116
|
+
expect(result.freed_bytes).toBe(0);
|
|
117
|
+
});
|
|
118
|
+
it('returns zero when no artifacts directory exists', () => {
|
|
119
|
+
const result = pruneArtifacts(10);
|
|
120
|
+
expect(result.removed).toBe(0);
|
|
121
|
+
expect(result.freed_bytes).toBe(0);
|
|
122
|
+
});
|
|
123
|
+
it('returns correct freed bytes count', () => {
|
|
124
|
+
const content = 'A'.repeat(1000);
|
|
125
|
+
writeArtifact('convoy-old1', 'task-1', 'large.md', content, 'report');
|
|
126
|
+
writeArtifact('convoy-old2', 'task-1', 'large.md', content, 'report');
|
|
127
|
+
writeArtifact('convoy-new', 'task-1', 'large.md', content, 'report');
|
|
128
|
+
const result = pruneArtifacts(1);
|
|
129
|
+
expect(result.removed).toBe(2);
|
|
130
|
+
expect(result.freed_bytes).toBeGreaterThan(0);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
describe('round-trip', () => {
|
|
134
|
+
it('write → list → read produces identical content', () => {
|
|
135
|
+
const content = '# Report\nLine 2\nLine 3';
|
|
136
|
+
writeArtifact('convoy-1', 'task-1', 'report.md', content, 'report');
|
|
137
|
+
const refs = listArtifacts('convoy-1', 'task-1');
|
|
138
|
+
expect(refs).toHaveLength(1);
|
|
139
|
+
const retrieved = readArtifact(refs[0]);
|
|
140
|
+
expect(retrieved).toBe(content);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe('path sanitization', () => {
|
|
144
|
+
it('rejects filenames with ..', () => {
|
|
145
|
+
expect(() => writeArtifact('convoy-1', 'task-1', '../evil.ts', 'content', 'code')).toThrow();
|
|
146
|
+
});
|
|
147
|
+
it('rejects filenames with /', () => {
|
|
148
|
+
expect(() => writeArtifact('convoy-1', 'task-1', 'sub/evil.ts', 'content', 'code')).toThrow();
|
|
149
|
+
});
|
|
150
|
+
it('rejects convoy IDs with path traversal', () => {
|
|
151
|
+
expect(() => writeArtifact('../evil', 'task-1', 'file.md', 'content', 'report')).toThrow();
|
|
152
|
+
});
|
|
153
|
+
it('rejects task IDs with backslash', () => {
|
|
154
|
+
expect(() => writeArtifact('convoy-1', 'task\\evil', 'file.md', 'content', 'report')).toThrow();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
describe('getArtifactDir', () => {
|
|
158
|
+
it('returns path ending with trailing slash', () => {
|
|
159
|
+
const dir = getArtifactDir('convoy-1', 'task-1');
|
|
160
|
+
expect(dir.endsWith('/')).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
it('includes convoy-id and task-id in path', () => {
|
|
163
|
+
const dir = getArtifactDir('convoy-abc', 'task-xyz');
|
|
164
|
+
expect(dir).toContain('convoy-abc');
|
|
165
|
+
expect(dir).toContain('task-xyz');
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=artifacts.test.js.map
|