cool-workflow 0.1.98 → 0.2.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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +11 -2
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/cli/dispatch.js +236 -0
- package/dist/cli/entry.js +120 -0
- package/dist/cli/io.js +21 -4
- package/dist/cli/parseargv.js +157 -0
- package/dist/cli.js +6 -33
- package/dist/core/capability-table.js +3534 -0
- package/dist/core/format/help.js +314 -0
- package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
- package/dist/core/hash.js +137 -0
- package/dist/core/multi-agent/candidate-scoring.js +219 -0
- package/dist/core/multi-agent/collaboration.js +481 -0
- package/dist/core/multi-agent/coordinator.js +515 -0
- package/dist/core/multi-agent/eval-replay.js +306 -0
- package/dist/core/multi-agent/runtime.js +929 -0
- package/dist/core/multi-agent/topology.js +298 -0
- package/dist/core/multi-agent/trust-policy.js +197 -0
- package/dist/core/pipeline/commit-gate.js +320 -0
- package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
- package/dist/core/pipeline/dispatch.js +103 -0
- package/dist/core/pipeline/drive-decide.js +227 -0
- package/dist/core/pipeline/error-feedback.js +161 -0
- package/dist/core/pipeline/loop-expansion.js +124 -0
- package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
- package/dist/core/pipeline/runner.js +230 -0
- package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
- package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
- package/dist/core/state/node-projection.js +95 -0
- package/dist/core/state/node-snapshot.js +230 -0
- package/dist/core/state/run-paths.js +93 -0
- package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
- package/dist/core/state/schema.js +50 -0
- package/dist/core/state/state-explosion/digest.js +243 -0
- package/dist/core/state/state-explosion/graph.js +527 -0
- package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
- package/dist/core/state/state-explosion/report.js +187 -0
- package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
- package/dist/{state-node.js → core/state/state-node.js} +90 -113
- package/dist/core/state/types.js +19 -0
- package/dist/{validation.js → core/state/validation.js} +64 -131
- package/dist/core/trust/evidence-grounding.js +134 -0
- package/dist/core/trust/ledger.js +199 -0
- package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
- package/dist/core/trust/telemetry-ledger.js +127 -0
- package/dist/core/types.js +20 -0
- package/dist/core/version.js +16 -0
- package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
- package/dist/mcp/dispatch.js +104 -0
- package/dist/mcp/server.js +144 -0
- package/dist/mcp-server.js +6 -84
- package/dist/{agent-config.js → shell/agent-config.js} +118 -71
- package/dist/shell/app-run-cli.js +88 -0
- package/dist/shell/audit-cli.js +259 -0
- package/dist/shell/audit-provenance.js +83 -0
- package/dist/shell/candidate-scoring-io.js +459 -0
- package/dist/shell/collaboration-io.js +264 -0
- package/dist/shell/commit-summary.js +104 -0
- package/dist/shell/commit.js +290 -0
- package/dist/shell/coordinator-io.js +476 -0
- package/dist/shell/demo-cli.js +19 -0
- package/dist/shell/dispatch.js +162 -0
- package/dist/{doctor.js → shell/doctor.js} +123 -56
- package/dist/shell/drive.js +873 -0
- package/dist/shell/error-feedback-io.js +305 -0
- package/dist/shell/eval-io.js +473 -0
- package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
- package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
- package/dist/shell/exec-backend-cli.js +88 -0
- package/dist/shell/execution-backend/agent.js +475 -0
- package/dist/shell/execution-backend/ci.js +15 -0
- package/dist/shell/execution-backend/container.js +69 -0
- package/dist/shell/execution-backend/envelopes.js +55 -0
- package/dist/shell/execution-backend/local.js +113 -0
- package/dist/shell/execution-backend/probes.js +175 -0
- package/dist/shell/execution-backend/registry.js +402 -0
- package/dist/shell/execution-backend/remote.js +128 -0
- package/dist/shell/execution-backend/types.js +11 -0
- package/dist/shell/feedback-cli.js +81 -0
- package/dist/shell/feedback-operations.js +48 -0
- package/dist/shell/fs-atomic.js +276 -0
- package/dist/shell/harness.js +98 -0
- package/dist/shell/ledger-cli.js +212 -0
- package/dist/shell/ledger-io.js +169 -0
- package/dist/shell/man-cli.js +89 -0
- package/dist/shell/metrics-cli.js +98 -0
- package/dist/shell/multi-agent-cli.js +1002 -0
- package/dist/shell/multi-agent-host.js +563 -0
- package/dist/shell/multi-agent-io.js +387 -0
- package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
- package/dist/shell/node-store.js +124 -0
- package/dist/{observability/format.js → shell/observability-format.js} +7 -1
- package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
- package/dist/{observability.js → shell/observability.js} +159 -332
- package/dist/{onramp.js → shell/onramp.js} +11 -0
- package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
- package/dist/shell/operator-ux.js +431 -0
- package/dist/shell/orchestrator.js +231 -0
- package/dist/shell/pipeline-cli.js +667 -0
- package/dist/shell/pipeline.js +217 -0
- package/dist/shell/reclamation-io.js +1366 -0
- package/dist/shell/registry-cli.js +329 -0
- package/dist/{remote-source.js → shell/remote-source.js} +2 -2
- package/dist/shell/report-cli.js +101 -0
- package/dist/shell/report-view-cli.js +117 -0
- package/dist/{orchestrator → shell}/report.js +289 -282
- package/dist/shell/reporter.js +62 -0
- package/dist/shell/run-export-cli.js +106 -0
- package/dist/shell/run-export.js +680 -0
- package/dist/shell/run-registry-io.js +1014 -0
- package/dist/shell/run-store.js +164 -0
- package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
- package/dist/{scheduler.js → shell/scheduler-io.js} +248 -48
- package/dist/shell/scheduling-io.js +311 -0
- package/dist/shell/state-cli.js +181 -0
- package/dist/shell/state-explosion-cli.js +197 -0
- package/dist/shell/telemetry-cli.js +85 -0
- package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
- package/dist/shell/telemetry-ledger-io.js +132 -0
- package/dist/{term.js → shell/term.js} +36 -46
- package/dist/shell/topology-io.js +361 -0
- package/dist/shell/trust-audit.js +471 -0
- package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
- package/dist/shell/verifier.js +48 -0
- package/dist/shell/workbench-host.js +250 -0
- package/dist/shell/workbench-text.js +18 -0
- package/dist/shell/workbench.js +175 -0
- package/dist/shell/worker-cli.js +124 -0
- package/dist/shell/worker-isolation.js +852 -0
- package/dist/shell/workflow-app-loader.js +650 -0
- package/docs/agent-delegation-drive.7.md +2 -0
- package/docs/cli-mcp-parity.7.md +280 -219
- package/docs/contract-migration-tooling.7.md +2 -0
- package/docs/control-plane-scheduling.7.md +2 -0
- package/docs/durable-state-and-locking.7.md +2 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
- package/docs/execution-backends.7.md +2 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
- package/docs/multi-agent-eval-replay-harness.7.md +2 -0
- package/docs/multi-agent-operator-ux.7.md +2 -0
- package/docs/node-snapshot-diff-replay.7.md +2 -0
- package/docs/observability-cost-accounting.7.md +2 -0
- package/docs/project-index.md +132 -71
- package/docs/real-execution-backends.7.md +2 -0
- package/docs/release-and-migration.7.md +2 -0
- package/docs/release-tooling.7.md +2 -0
- package/docs/run-registry-control-plane.7.md +2 -0
- package/docs/run-retention-reclamation.7.md +23 -0
- package/docs/state-explosion-management.7.md +2 -0
- package/docs/team-collaboration.7.md +2 -0
- package/docs/web-desktop-workbench.7.md +2 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +9 -13
- package/package.json +1 -1
- package/scripts/agents/cw-attest-wrap.js +2 -2
- package/scripts/bump-version.js +4 -3
- package/scripts/canonical-apps.js +4 -4
- package/scripts/dogfood-architecture-review.js +2 -3
- package/scripts/dogfood-release.js +3 -3
- package/scripts/gen-parity-doc.js +15 -2
- package/scripts/golden-path.js +4 -4
- package/scripts/onramp-check.js +1 -1
- package/scripts/parity-check.js +38 -21
- package/scripts/release-flow.js +2 -2
- package/scripts/sync-project-index.js +51 -27
- package/scripts/validate-run-state-schema.js +2 -2
- package/scripts/version-sync-check.js +30 -30
- package/dist/candidate-scoring.js +0 -729
- package/dist/capability-core.js +0 -1189
- package/dist/capability-registry.js +0 -885
- package/dist/cli/command-surface.js +0 -494
- package/dist/cli/format.js +0 -56
- package/dist/cli/handlers/audit.js +0 -82
- package/dist/cli/handlers/blackboard.js +0 -81
- package/dist/cli/handlers/candidate.js +0 -40
- package/dist/cli/handlers/clones.js +0 -34
- package/dist/cli/handlers/collaboration.js +0 -61
- package/dist/cli/handlers/eval.js +0 -40
- package/dist/cli/handlers/ledger.js +0 -169
- package/dist/cli/handlers/maintenance.js +0 -107
- package/dist/cli/handlers/multi-agent.js +0 -165
- package/dist/cli/handlers/node.js +0 -41
- package/dist/cli/handlers/operational.js +0 -155
- package/dist/cli/handlers/operator.js +0 -146
- package/dist/cli/handlers/registry.js +0 -68
- package/dist/cli/handlers/run.js +0 -153
- package/dist/cli/handlers/scheduling.js +0 -132
- package/dist/cli/handlers/workbench.js +0 -41
- package/dist/cli/handlers/worker.js +0 -45
- package/dist/cli/run-summary.js +0 -45
- package/dist/clones.js +0 -162
- package/dist/collaboration.js +0 -726
- package/dist/commit.js +0 -592
- package/dist/compare.js +0 -18
- package/dist/coordinator/classify.js +0 -45
- package/dist/coordinator/paths.js +0 -42
- package/dist/coordinator/util.js +0 -126
- package/dist/coordinator.js +0 -990
- package/dist/daemon.js +0 -44
- package/dist/dispatch.js +0 -250
- package/dist/drive.js +0 -864
- package/dist/error-feedback.js +0 -419
- package/dist/evidence-grounding.js +0 -184
- package/dist/execution-backend/agent.js +0 -354
- package/dist/execution-backend/probes.js +0 -112
- package/dist/execution-backend/util.js +0 -47
- package/dist/execution-backend.js +0 -961
- package/dist/gates.js +0 -48
- package/dist/harness.js +0 -61
- package/dist/ledger.js +0 -313
- package/dist/loop-expansion.js +0 -60
- package/dist/mcp/tool-call.js +0 -470
- package/dist/mcp/tool-definitions.js +0 -1066
- package/dist/mcp-surface.js +0 -30
- package/dist/multi-agent/graph.js +0 -84
- package/dist/multi-agent/helpers.js +0 -141
- package/dist/multi-agent/ids.js +0 -20
- package/dist/multi-agent/paths.js +0 -22
- package/dist/multi-agent-eval/normalize.js +0 -51
- package/dist/multi-agent-eval.js +0 -678
- package/dist/multi-agent-host.js +0 -777
- package/dist/multi-agent.js +0 -984
- package/dist/node-projection.js +0 -59
- package/dist/node-snapshot.js +0 -260
- package/dist/operator-ux.js +0 -631
- package/dist/orchestrator/app-operations.js +0 -211
- package/dist/orchestrator/audit-operations.js +0 -182
- package/dist/orchestrator/candidate-operations.js +0 -117
- package/dist/orchestrator/cli-options.js +0 -294
- package/dist/orchestrator/collaboration-operations.js +0 -86
- package/dist/orchestrator/feedback-operations.js +0 -81
- package/dist/orchestrator/host-operations.js +0 -78
- package/dist/orchestrator/lifecycle-operations.js +0 -650
- package/dist/orchestrator/migration-operations.js +0 -44
- package/dist/orchestrator/multi-agent-operations.js +0 -362
- package/dist/orchestrator/topology-operations.js +0 -84
- package/dist/orchestrator.js +0 -925
- package/dist/pipeline-runner.js +0 -285
- package/dist/reclamation/hash.js +0 -72
- package/dist/reclamation.js +0 -812
- package/dist/reporter.js +0 -67
- package/dist/run-export.js +0 -815
- package/dist/run-registry/derive.js +0 -175
- package/dist/run-registry/format.js +0 -124
- package/dist/run-registry/gc.js +0 -251
- package/dist/run-registry/policy.js +0 -16
- package/dist/run-registry/queue.js +0 -115
- package/dist/run-registry.js +0 -850
- package/dist/run-state-schema.js +0 -68
- package/dist/scheduling.js +0 -184
- package/dist/state-explosion.js +0 -1014
- package/dist/state.js +0 -367
- package/dist/telemetry-ledger.js +0 -196
- package/dist/topology.js +0 -565
- package/dist/triggers.js +0 -184
- package/dist/trust-audit.js +0 -644
- package/dist/types/blackboard.js +0 -2
- package/dist/types/candidate.js +0 -2
- package/dist/types/collaboration.js +0 -2
- package/dist/types/core.js +0 -2
- package/dist/types/drive.js +0 -10
- package/dist/types/error-feedback.js +0 -2
- package/dist/types/evidence-reasoning.js +0 -2
- package/dist/types/execution-backend.js +0 -2
- package/dist/types/multi-agent.js +0 -2
- package/dist/types/observability.js +0 -2
- package/dist/types/pipeline.js +0 -2
- package/dist/types/reclamation.js +0 -8
- package/dist/types/report-bundle.js +0 -6
- package/dist/types/result.js +0 -2
- package/dist/types/run-registry.js +0 -2
- package/dist/types/run.js +0 -2
- package/dist/types/sandbox.js +0 -2
- package/dist/types/schedule.js +0 -2
- package/dist/types/state-node.js +0 -2
- package/dist/types/topology.js +0 -2
- package/dist/types/trust.js +0 -2
- package/dist/types/workbench.js +0 -2
- package/dist/types/worker.js +0 -2
- package/dist/types/workflow-app.js +0 -2
- package/dist/types.js +0 -44
- package/dist/util/fingerprint.js +0 -19
- package/dist/util/fingerprint.test.js +0 -27
- package/dist/verifier.js +0 -78
- package/dist/version.js +0 -8
- package/dist/workbench-host.js +0 -192
- package/dist/workbench.js +0 -192
- package/dist/worker-accept/acceptance.js +0 -114
- package/dist/worker-accept/blackboard-fanout.js +0 -80
- package/dist/worker-accept/blackboard-linkage.js +0 -19
- package/dist/worker-accept/context.js +0 -2
- package/dist/worker-accept/telemetry-ledger.js +0 -126
- package/dist/worker-accept/validation.js +0 -77
- package/dist/worker-accept/verifier-completion.js +0 -73
- package/dist/worker-isolation/helpers.js +0 -51
- package/dist/worker-isolation/paths.js +0 -46
- package/dist/worker-isolation.js +0 -656
- package/dist/workflow-api.js +0 -131
- /package/dist/{types → core/types}/boundary.js +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// mcp/dispatch.ts — the generic MCP tool-call reader over
|
|
3
|
+
// core/capability-table.ts.
|
|
4
|
+
//
|
|
5
|
+
// MILESTONE 2 (v2/PLAN.md build order, step 2). Replaces the old build's
|
|
6
|
+
// 196-arm `callTool` switch (src/mcp/tool-call.ts) with a data-driven
|
|
7
|
+
// lookup: resolve the row by tool name, check its declared required-arg
|
|
8
|
+
// groups (SPEC/mcp.md's `McpBinding.requiredArgs`), re-base `cwd` if
|
|
9
|
+
// given, then call the row's `mcp.handler`. Every future milestone adds
|
|
10
|
+
// capabilities as capability-table ROWS; this file is not touched again.
|
|
11
|
+
//
|
|
12
|
+
// `cwd` handling here (resolve, must be a directory, inject the resolved
|
|
13
|
+
// path back into a COPY of args) matches SPEC/mcp.md invariant 7. This
|
|
14
|
+
// milestone's own real handlers (`list`, `sandbox.list`, `status`) do not
|
|
15
|
+
// themselves need cwd, but the re-base is still performed uniformly so a
|
|
16
|
+
// later milestone's handler can rely on it without dispatch.ts changing.
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.toolDefinitions = toolDefinitions;
|
|
52
|
+
exports.requiredToolArguments = requiredToolArguments;
|
|
53
|
+
exports.callTool = callTool;
|
|
54
|
+
const fs = __importStar(require("node:fs"));
|
|
55
|
+
const path = __importStar(require("node:path"));
|
|
56
|
+
const capability_table_1 = require("../core/capability-table");
|
|
57
|
+
/** `toolDefinitions()` — the full, ordered `tools/list` array. */
|
|
58
|
+
function toolDefinitions() {
|
|
59
|
+
return (0, capability_table_1.mcpToolDefinitions)();
|
|
60
|
+
}
|
|
61
|
+
/** SPEC/mcp.md invariant 6 / src/mcp-surface.ts:7-25 — required-argument
|
|
62
|
+
* groups are declared data, not code. `undefined`/`null` become `{}`; a
|
|
63
|
+
* non-object throws; a group "keyA|keyB" passes when at least one named
|
|
64
|
+
* key is not `undefined`, not `null`, and not `""`. */
|
|
65
|
+
function requiredToolArguments(name, value) {
|
|
66
|
+
const args = value === undefined || value === null ? {} : value;
|
|
67
|
+
if (typeof args !== "object" || Array.isArray(args)) {
|
|
68
|
+
throw new Error(`MCP tool ${name} arguments must be an object.`);
|
|
69
|
+
}
|
|
70
|
+
const row = (0, capability_table_1.findCapabilityByMcpTool)(name);
|
|
71
|
+
const requiredArgs = row?.mcp?.requiredArgs ?? [];
|
|
72
|
+
for (const group of requiredArgs) {
|
|
73
|
+
const keys = group.split("|");
|
|
74
|
+
const satisfied = keys.some((key) => {
|
|
75
|
+
const v = args[key];
|
|
76
|
+
return v !== undefined && v !== null && v !== "";
|
|
77
|
+
});
|
|
78
|
+
if (!satisfied) {
|
|
79
|
+
throw new Error(`MCP tool ${name} missing required argument: ${keys.join(" or ")}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return args;
|
|
83
|
+
}
|
|
84
|
+
/** `callTool(name, args)` — resolves `cwd` (SPEC/mcp.md invariant 7),
|
|
85
|
+
* checks required args, then calls the row's `mcp.handler`. Throws
|
|
86
|
+
* `Unknown tool: <name>` for a name with no row (src/mcp/tool-call.ts:513
|
|
87
|
+
* parity). */
|
|
88
|
+
function callTool(name, rawArgs) {
|
|
89
|
+
const row = (0, capability_table_1.findCapabilityByMcpTool)(name);
|
|
90
|
+
if (!row || !row.mcp) {
|
|
91
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
92
|
+
}
|
|
93
|
+
const args = requiredToolArguments(name, rawArgs);
|
|
94
|
+
const resolvedArgs = { ...args };
|
|
95
|
+
const cwdInput = args.cwd;
|
|
96
|
+
if (typeof cwdInput === "string" && cwdInput.length > 0) {
|
|
97
|
+
const resolved = path.resolve(cwdInput);
|
|
98
|
+
if (!fs.statSync(resolved).isDirectory()) {
|
|
99
|
+
throw new Error(`MCP cwd is not a directory: ${resolved}`);
|
|
100
|
+
}
|
|
101
|
+
resolvedArgs.cwd = resolved;
|
|
102
|
+
}
|
|
103
|
+
return row.mcp.handler(resolvedArgs);
|
|
104
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// mcp/server.ts — the stdio JSON-RPC loop: initialize / tools/list /
|
|
3
|
+
// tools/call.
|
|
4
|
+
//
|
|
5
|
+
// MILESTONE 2 (v2/PLAN.md build order, step 2). Byte-exact port of the
|
|
6
|
+
// framing rules in SPEC/mcp.md's "JSON-RPC methods" / "Exact outputs" /
|
|
7
|
+
// "Invariants and error behavior" #9 / "Edge cases" sections:
|
|
8
|
+
// - transport: stdin/stdout, ONE JSON object per line, no
|
|
9
|
+
// Content-Length headers (mcp.md:13);
|
|
10
|
+
// - `initialize` -> protocolVersion/capabilities/serverInfo, ignoring
|
|
11
|
+
// params (mcp.md:19,263-269);
|
|
12
|
+
// - `tools/list` -> { tools: [...] } from core/capability-table.ts via
|
|
13
|
+
// mcp/dispatch.ts, ignoring params (mcp.md:20,271-277);
|
|
14
|
+
// - `tools/call` -> { content: [{ type: "text", text: <2-space pretty
|
|
15
|
+
// JSON string> }] } (mcp.md:21,279-285);
|
|
16
|
+
// - any other method: error -32601 if the request has an `id` key,
|
|
17
|
+
// otherwise NO reply at all (mcp.md:22,427-428);
|
|
18
|
+
// - a request with `"id": null` DOES get an error answer for an unknown
|
|
19
|
+
// method (the guard is `id !== undefined`, and `null !== undefined`)
|
|
20
|
+
// (mcp.md:427);
|
|
21
|
+
// - `initialize`/`tools/list`/`tools/call` answer even with no `id` key
|
|
22
|
+
// at all — the reply then omits `id` (JSON.stringify drops
|
|
23
|
+
// `undefined`) (mcp.md:428);
|
|
24
|
+
// - parse errors always answer with `id: null`, even when the broken
|
|
25
|
+
// line contained one (mcp.md:429, 290-291);
|
|
26
|
+
// - a line that parses to non-object JSON (number/string/array/null)
|
|
27
|
+
// gets -32600 "Invalid Request: not a JSON-RPC object" (mcp.md:292,
|
|
28
|
+
// 430);
|
|
29
|
+
// - many requests in one stdin chunk: split on every "\n", .trim() each
|
|
30
|
+
// line, skip empty lines (mcp.md:431);
|
|
31
|
+
// - `tools/call` with a missing/empty/whitespace-only `name` throws
|
|
32
|
+
// "MCP tools/call missing required field: name" as -32000 (mcp.md:432);
|
|
33
|
+
// - `arguments: null`/absent -> `{}` before the required-args check
|
|
34
|
+
// (mcp.md:433);
|
|
35
|
+
// - fail-closed input framing: MAX_LINE_BYTES = 16 * 1024 * 1024; when
|
|
36
|
+
// the unconsumed buffer exceeds this with no newline yet, the partial
|
|
37
|
+
// bytes are DROPPED and a -32700 "Parse error: request line exceeds
|
|
38
|
+
// 16777216 bytes" is sent with id: null (mcp.md:291,418).
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.startServer = startServer;
|
|
41
|
+
const version_1 = require("../core/version");
|
|
42
|
+
const dispatch_1 = require("./dispatch");
|
|
43
|
+
const MAX_LINE_BYTES = 16 * 1024 * 1024;
|
|
44
|
+
function writeMessage(message) {
|
|
45
|
+
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
46
|
+
}
|
|
47
|
+
function errorMessage(id, code, message) {
|
|
48
|
+
return { jsonrpc: "2.0", id: id === undefined ? null : id, error: { code, message } };
|
|
49
|
+
}
|
|
50
|
+
function resultMessage(id, result) {
|
|
51
|
+
const message = { jsonrpc: "2.0", result };
|
|
52
|
+
if (id !== undefined)
|
|
53
|
+
message.id = id;
|
|
54
|
+
return message;
|
|
55
|
+
}
|
|
56
|
+
/** Handles one already-parsed JSON-RPC request object. May write zero or
|
|
57
|
+
* one reply line to stdout. */
|
|
58
|
+
function handleRequest(message) {
|
|
59
|
+
const hasId = Object.prototype.hasOwnProperty.call(message, "id");
|
|
60
|
+
const id = message.id;
|
|
61
|
+
if (typeof message.method !== "string") {
|
|
62
|
+
// Not really reachable from valid JSON-RPC callers, but stay
|
|
63
|
+
// fail-closed rather than throw synchronously out of the read loop.
|
|
64
|
+
if (hasId)
|
|
65
|
+
writeMessage(errorMessage(id, -32601, `Unknown method: ${String(message.method)}`));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
switch (message.method) {
|
|
70
|
+
case "initialize": {
|
|
71
|
+
writeMessage(resultMessage(id, {
|
|
72
|
+
protocolVersion: "2024-11-05",
|
|
73
|
+
capabilities: { tools: {} },
|
|
74
|
+
serverInfo: { name: "cool-workflow", version: version_1.CURRENT_COOL_WORKFLOW_VERSION },
|
|
75
|
+
}));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
case "tools/list": {
|
|
79
|
+
writeMessage(resultMessage(id, { tools: (0, dispatch_1.toolDefinitions)() }));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
case "tools/call": {
|
|
83
|
+
const params = message.params ?? {};
|
|
84
|
+
const name = params.name;
|
|
85
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
86
|
+
throw new Error("MCP tools/call missing required field: name");
|
|
87
|
+
}
|
|
88
|
+
const args = params.arguments;
|
|
89
|
+
const coreResult = (0, dispatch_1.callTool)(name, args ?? {});
|
|
90
|
+
writeMessage(resultMessage(id, { content: [{ type: "text", text: JSON.stringify(coreResult, null, 2) }] }));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
default: {
|
|
94
|
+
if (hasId)
|
|
95
|
+
writeMessage(errorMessage(id, -32601, `Unknown method: ${message.method}`));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
102
|
+
writeMessage(errorMessage(id, -32000, text));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Handles one raw (already-trimmed, non-empty) stdin line. */
|
|
106
|
+
function handleLine(line) {
|
|
107
|
+
let parsed;
|
|
108
|
+
try {
|
|
109
|
+
parsed = JSON.parse(line);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
113
|
+
writeMessage(errorMessage(null, -32700, `Parse error: ${detail}`));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
117
|
+
writeMessage(errorMessage(null, -32600, "Invalid Request: not a JSON-RPC object"));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
handleRequest(parsed);
|
|
121
|
+
}
|
|
122
|
+
/** Starts the stdio read loop. Never resolves — the server is long-lived
|
|
123
|
+
* and stops only when its stdin closes / the process exits. */
|
|
124
|
+
function startServer() {
|
|
125
|
+
process.stdin.setEncoding("utf8");
|
|
126
|
+
let buffer = "";
|
|
127
|
+
process.stdin.on("data", (chunk) => {
|
|
128
|
+
buffer += chunk;
|
|
129
|
+
for (;;) {
|
|
130
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
131
|
+
if (newlineIndex === -1)
|
|
132
|
+
break;
|
|
133
|
+
const line = buffer.slice(0, newlineIndex);
|
|
134
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
135
|
+
const trimmed = line.trim();
|
|
136
|
+
if (trimmed)
|
|
137
|
+
handleLine(trimmed);
|
|
138
|
+
}
|
|
139
|
+
if (buffer.length > MAX_LINE_BYTES) {
|
|
140
|
+
buffer = "";
|
|
141
|
+
writeMessage(errorMessage(null, -32700, `Parse error: request line exceeds ${MAX_LINE_BYTES} bytes`));
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
package/dist/mcp-server.js
CHANGED
|
@@ -1,87 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
// src/mcp-server.ts — the real MCP server binary entry point. Compiles to
|
|
4
|
+
// dist/mcp-server.js, a sibling of dist/cli.js. Byte-exact shape of the
|
|
5
|
+
// old build's src/mcp-server.ts (scripts/mcp-server.js requires it; a
|
|
6
|
+
// real MCP client launches it directly with `node`).
|
|
3
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
process.stdin.setEncoding("utf8");
|
|
7
|
-
let buffer = "";
|
|
8
|
-
// A single line-delimited JSON-RPC request is bounded; an un-terminated stream
|
|
9
|
-
// (a peer that never sends a newline, or a slow byte dribble) must not grow the
|
|
10
|
-
// long-lived server's buffer without limit until it OOMs. Cap the unconsumed
|
|
11
|
-
// buffer and fail closed on a frame that exceeds it.
|
|
12
|
-
const MAX_LINE_BYTES = 16 * 1024 * 1024;
|
|
13
|
-
process.stdin.on("data", (chunk) => {
|
|
14
|
-
buffer += chunk;
|
|
15
|
-
let newlineIndex;
|
|
16
|
-
while ((newlineIndex = buffer.indexOf("\n")) >= 0) {
|
|
17
|
-
const line = buffer.slice(0, newlineIndex).trim();
|
|
18
|
-
buffer = buffer.slice(newlineIndex + 1);
|
|
19
|
-
if (line)
|
|
20
|
-
handleLine(line);
|
|
21
|
-
}
|
|
22
|
-
if (buffer.length > MAX_LINE_BYTES) {
|
|
23
|
-
// No newline in an over-cap buffer => a malformed or hostile frame. Reject it
|
|
24
|
-
// and drop the partial bytes rather than accumulate toward OOM.
|
|
25
|
-
buffer = "";
|
|
26
|
-
sendError(null, -32700, `Parse error: request line exceeds ${MAX_LINE_BYTES} bytes`);
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
function handleLine(line) {
|
|
30
|
-
let message;
|
|
31
|
-
try {
|
|
32
|
-
message = JSON.parse(line);
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
sendError(null, -32700, `Parse error: ${messageOf(error)}`);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
if (message === null || typeof message !== "object" || Array.isArray(message)) {
|
|
39
|
-
sendError(null, -32600, "Invalid Request: not a JSON-RPC object");
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
try {
|
|
43
|
-
if (message.method === "initialize") {
|
|
44
|
-
sendResult(message.id, {
|
|
45
|
-
protocolVersion: "2024-11-05",
|
|
46
|
-
capabilities: { tools: {} },
|
|
47
|
-
serverInfo: { name: "cool-workflow", version: version_1.CURRENT_COOL_WORKFLOW_VERSION }
|
|
48
|
-
});
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (message.method === "tools/list") {
|
|
52
|
-
sendResult(message.id, { tools: (0, mcp_surface_1.toolDefinitions)() });
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
if (message.method === "tools/call") {
|
|
56
|
-
const toolName = requiredToolName(message.params?.name);
|
|
57
|
-
const toolArgs = (0, mcp_surface_1.requiredToolArguments)(toolName, message.params?.arguments);
|
|
58
|
-
const result = (0, mcp_surface_1.callTool)(toolName, toolArgs);
|
|
59
|
-
sendResult(message.id, {
|
|
60
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
61
|
-
});
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (message.id !== undefined)
|
|
65
|
-
sendError(message.id, -32601, `Unknown method: ${message.method}`);
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
sendError(message.id, -32000, messageOf(error));
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
function requiredToolName(value) {
|
|
72
|
-
if (typeof value !== "string" || !value.trim())
|
|
73
|
-
throw new Error("MCP tools/call missing required field: name");
|
|
74
|
-
return value;
|
|
75
|
-
}
|
|
76
|
-
function sendResult(id, result) {
|
|
77
|
-
send({ jsonrpc: "2.0", id, result });
|
|
78
|
-
}
|
|
79
|
-
function sendError(id, code, message) {
|
|
80
|
-
send({ jsonrpc: "2.0", id, error: { code, message } });
|
|
81
|
-
}
|
|
82
|
-
function send(message) {
|
|
83
|
-
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
84
|
-
}
|
|
85
|
-
function messageOf(error) {
|
|
86
|
-
return error instanceof Error ? error.message : String(error);
|
|
87
|
-
}
|
|
8
|
+
const server_1 = require("./mcp/server");
|
|
9
|
+
(0, server_1.startServer)();
|
|
@@ -1,40 +1,78 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// diffable config record, NEVER a CW dependency. Resolution order is
|
|
5
|
-
// flags > env > a durable $CW_HOME/agent-config.json > fail-closed.
|
|
2
|
+
// shell/agent-config.ts — agent delegation config: env/file/flag resolution
|
|
3
|
+
// order, CW_HOME resolution, builtin:<name> expansion, PATH auto-detect.
|
|
6
4
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// policy data it reads. The kernel never learns which vendor was chosen.
|
|
10
|
-
// - NO SECRETS IN COMMITTED STATE [load-bearing]: the durable config holds a
|
|
11
|
-
// command-TEMPLATE + endpoint + operator-chosen model only. API keys come from
|
|
12
|
-
// the AGENT's own inherited env, never written into .cw/ or the config file.
|
|
13
|
-
// Any secret-looking arg is stripped before it is persisted OR shown.
|
|
14
|
-
// - DETERMINISTIC SHOW: `agentConfigShow` is a pure projection of env + file —
|
|
15
|
-
// no now-derived field — so `cw backend agent config show --json` is
|
|
16
|
-
// byte-identical to the MCP tool (CLI<->MCP parity).
|
|
5
|
+
// MILESTONE 5 (v2/PLAN.md build order, step 5). Byte-exact port of
|
|
6
|
+
// plugins/cool-workflow/src/agent-config.ts.
|
|
17
7
|
//
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
// Resolution order per field: flags > env > a durable
|
|
9
|
+
// $CW_HOME/agent-config.json > auto-detect > none. NO SECRETS IN COMMITTED
|
|
10
|
+
// STATE: the durable config holds a command-TEMPLATE + endpoint + operator-
|
|
11
|
+
// chosen model only; a secret-looking arg is stripped before persisting or
|
|
12
|
+
// showing.
|
|
13
|
+
//
|
|
14
|
+
// Evidence: SPEC/execution-backend.md "Agent delegation config".
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
22
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
49
|
exports.AGENT_CONFIG_SCHEMA_VERSION = void 0;
|
|
50
|
+
exports.resolveCwHome = resolveCwHome;
|
|
24
51
|
exports.agentConfigPath = agentConfigPath;
|
|
25
52
|
exports.loadAgentConfigFile = loadAgentConfigFile;
|
|
26
53
|
exports.resolveAgentConfig = resolveAgentConfig;
|
|
27
54
|
exports.agentConfigured = agentConfigured;
|
|
28
55
|
exports.setAgentConfigFile = setAgentConfigFile;
|
|
29
56
|
exports.agentConfigShow = agentConfigShow;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
57
|
+
exports.backendAgentConfigSet = backendAgentConfigSet;
|
|
58
|
+
exports.backendAgentConfigShow = backendAgentConfigShow;
|
|
59
|
+
const fs = __importStar(require("node:fs"));
|
|
60
|
+
const os = __importStar(require("node:os"));
|
|
61
|
+
const path = __importStar(require("node:path"));
|
|
62
|
+
const fs_atomic_1 = require("./fs-atomic");
|
|
63
|
+
const agent_1 = require("./execution-backend/agent");
|
|
35
64
|
exports.AGENT_CONFIG_SCHEMA_VERSION = 1;
|
|
65
|
+
/** CW_HOME resolution: CW_HOME > XDG_STATE_HOME/cool-workflow > ~/.local/state/cool-workflow. */
|
|
66
|
+
function resolveCwHome(env = process.env) {
|
|
67
|
+
if (env.CW_HOME && String(env.CW_HOME).trim())
|
|
68
|
+
return path.resolve(String(env.CW_HOME));
|
|
69
|
+
if (env.XDG_STATE_HOME && String(env.XDG_STATE_HOME).trim()) {
|
|
70
|
+
return path.join(path.resolve(String(env.XDG_STATE_HOME)), "cool-workflow");
|
|
71
|
+
}
|
|
72
|
+
return path.join(os.homedir(), ".local", "state", "cool-workflow");
|
|
73
|
+
}
|
|
36
74
|
function agentConfigPath(env = process.env) {
|
|
37
|
-
return
|
|
75
|
+
return path.join(resolveCwHome(env), "agent-config.json");
|
|
38
76
|
}
|
|
39
77
|
function trimmed(value) {
|
|
40
78
|
if (typeof value !== "string")
|
|
@@ -42,8 +80,6 @@ function trimmed(value) {
|
|
|
42
80
|
const out = value.trim();
|
|
43
81
|
return out ? out : undefined;
|
|
44
82
|
}
|
|
45
|
-
/** Parse a boolean from a flag (boolean) or an env/file string. Returns undefined
|
|
46
|
-
* for unset/unrecognized so `firstDefined` falls through to the next layer. */
|
|
47
83
|
function boolish(value) {
|
|
48
84
|
if (typeof value === "boolean")
|
|
49
85
|
return value;
|
|
@@ -68,8 +104,9 @@ function firstDefined(...values) {
|
|
|
68
104
|
return value;
|
|
69
105
|
return undefined;
|
|
70
106
|
}
|
|
71
|
-
/** Split a single command string ("claude -p {{manifest}}") into binary +
|
|
72
|
-
* template — NEVER shell-interpreted. An explicit args array always
|
|
107
|
+
/** Split a single command string ("claude -p {{manifest}}") into binary +
|
|
108
|
+
* argv template — NEVER shell-interpreted. An explicit args array always
|
|
109
|
+
* wins. */
|
|
73
110
|
function splitCommand(command, args) {
|
|
74
111
|
if (!command)
|
|
75
112
|
return { command: undefined, args };
|
|
@@ -85,10 +122,10 @@ function splitCommand(command, args) {
|
|
|
85
122
|
* treated as absent (fail closed). */
|
|
86
123
|
function loadAgentConfigFile(env = process.env) {
|
|
87
124
|
const file = agentConfigPath(env);
|
|
88
|
-
if (!
|
|
125
|
+
if (!fs.existsSync(file))
|
|
89
126
|
return undefined;
|
|
90
127
|
try {
|
|
91
|
-
const parsed = JSON.parse(
|
|
128
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
92
129
|
return {
|
|
93
130
|
schemaVersion: 1,
|
|
94
131
|
command: trimmed(parsed.command),
|
|
@@ -98,7 +135,7 @@ function loadAgentConfigFile(env = process.env) {
|
|
|
98
135
|
timeoutMs: typeof parsed.timeoutMs === "number" ? parsed.timeoutMs : undefined,
|
|
99
136
|
attestPublicKey: trimmed(parsed.attestPublicKey),
|
|
100
137
|
requireAttestedTelemetry: boolish(parsed.requireAttestedTelemetry),
|
|
101
|
-
source: "file"
|
|
138
|
+
source: "file",
|
|
102
139
|
};
|
|
103
140
|
}
|
|
104
141
|
catch {
|
|
@@ -116,7 +153,7 @@ function agentConfigFromEnv(env) {
|
|
|
116
153
|
timeoutMs: trimmed(env.CW_AGENT_TIMEOUT_MS) ? Number(env.CW_AGENT_TIMEOUT_MS) : undefined,
|
|
117
154
|
attestPublicKey: trimmed(env.CW_AGENT_ATTEST_PUBKEY),
|
|
118
155
|
requireAttestedTelemetry: boolish(env.CW_REQUIRE_ATTESTED_TELEMETRY),
|
|
119
|
-
source: "env"
|
|
156
|
+
source: "env",
|
|
120
157
|
};
|
|
121
158
|
}
|
|
122
159
|
function agentConfigFromArgs(args) {
|
|
@@ -133,51 +170,41 @@ function agentConfigFromArgs(args) {
|
|
|
133
170
|
timeoutMs: rawTimeout !== undefined ? Number(rawTimeout) : undefined,
|
|
134
171
|
attestPublicKey: trimmed(args.agentAttestPublicKey ?? args["agent-attest-public-key"]),
|
|
135
172
|
requireAttestedTelemetry: boolish(args.requireAttestedTelemetry ?? args["require-attested-telemetry"]),
|
|
136
|
-
source: "flag"
|
|
173
|
+
source: "flag",
|
|
137
174
|
};
|
|
138
175
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// WORKING agent without knowing where the package landed on disk:
|
|
142
|
-
// --agent-command builtin:claude (or CW_AGENT_COMMAND=builtin:claude)
|
|
143
|
-
// resolves to the packaged wrapper invocation. Still pure config — the template
|
|
144
|
-
// is an out-of-process delegation script; CW never calls a model API.
|
|
145
|
-
//
|
|
146
|
-
// The builtin set is DATA, not a kernel TS literal (FreeBSD-audit L15): it lives
|
|
147
|
-
// in scripts/agents/builtin-templates.json (vendor name -> wrapper script name).
|
|
148
|
-
// Adding a vendor is a content/distribution step (drop a wrapper + a JSON line),
|
|
149
|
-
// not a kernel edit — keeping CW vendor-agnostic at the source level.
|
|
150
|
-
/** Detect which known agent CLIs are on PATH. Returns the first found, or undefined.
|
|
151
|
-
* Skipped when CW_NO_AUTO_AGENT=1 (test sandboxes, CI without real agents). */
|
|
176
|
+
/** Detect which known agent CLIs are on PATH. Returns the first found, or
|
|
177
|
+
* undefined. Skipped when CW_NO_AUTO_AGENT=1. */
|
|
152
178
|
function detectAgentFromPath(env = process.env) {
|
|
153
179
|
if (env.CW_NO_AUTO_AGENT === "1")
|
|
154
180
|
return undefined;
|
|
155
181
|
const known = ["claude", "codex", "gemini", "opencode"];
|
|
156
|
-
const dirs = (env.PATH || "").split(
|
|
182
|
+
const dirs = (env.PATH || "").split(path.delimiter).filter(Boolean);
|
|
157
183
|
const exts = process.platform === "win32" ? (env.PATHEXT || ".EXE;.CMD;.BAT").split(";") : [""];
|
|
184
|
+
const templates = builtinAgentTemplates();
|
|
158
185
|
for (const name of known) {
|
|
159
|
-
// Also check if the builtin template exists (so we don't suggest an unsupported agent).
|
|
160
|
-
const templates = builtinAgentTemplates();
|
|
161
186
|
if (!templates[name])
|
|
162
187
|
continue;
|
|
163
188
|
for (const dir of dirs) {
|
|
164
189
|
for (const ext of exts) {
|
|
165
190
|
try {
|
|
166
|
-
if (
|
|
191
|
+
if (fs.statSync(path.join(dir, name + ext)).isFile())
|
|
167
192
|
return name;
|
|
168
193
|
}
|
|
169
|
-
catch {
|
|
194
|
+
catch {
|
|
195
|
+
/* keep looking */
|
|
196
|
+
}
|
|
170
197
|
}
|
|
171
198
|
}
|
|
172
199
|
}
|
|
173
200
|
return undefined;
|
|
174
201
|
}
|
|
175
202
|
function builtinAgentTemplates() {
|
|
176
|
-
const agentsDir =
|
|
177
|
-
const manifest = JSON.parse(
|
|
203
|
+
const agentsDir = path.join(__dirname, "..", "..", "scripts", "agents");
|
|
204
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(agentsDir, "builtin-templates.json"), "utf8"));
|
|
178
205
|
const out = {};
|
|
179
206
|
for (const [name, script] of Object.entries(manifest.templates || {})) {
|
|
180
|
-
out[name] = `node ${
|
|
207
|
+
out[name] = `node ${path.join(agentsDir, script)} {{input}} {{result}}`;
|
|
181
208
|
}
|
|
182
209
|
return out;
|
|
183
210
|
}
|
|
@@ -192,8 +219,9 @@ function expandBuiltinAgentCommand(command) {
|
|
|
192
219
|
}
|
|
193
220
|
return template;
|
|
194
221
|
}
|
|
195
|
-
/** Resolve the EFFECTIVE agent config: flags > env > file >
|
|
196
|
-
* `source` names the layer the command/endpoint came
|
|
222
|
+
/** Resolve the EFFECTIVE agent config: flags > env > file > auto-detect >
|
|
223
|
+
* none. The returned `source` names the layer the command/endpoint came
|
|
224
|
+
* from. */
|
|
197
225
|
function resolveAgentConfig(args = {}, env = process.env) {
|
|
198
226
|
const flagCfg = agentConfigFromArgs(args);
|
|
199
227
|
const envCfg = agentConfigFromEnv(env);
|
|
@@ -212,9 +240,6 @@ function resolveAgentConfig(args = {}, env = process.env) {
|
|
|
212
240
|
: fileCfg && (fileCfg.command || fileCfg.endpoint)
|
|
213
241
|
? "file"
|
|
214
242
|
: "none";
|
|
215
|
-
// Auto-detect: if no agent is configured, check PATH for known agent CLIs and
|
|
216
|
-
// resolve via builtin:<name>. This makes `cw quickstart` work without
|
|
217
|
-
// --agent-command on machines where claude/codex/gemini/opencode are installed.
|
|
218
243
|
let finalCommand = command;
|
|
219
244
|
let finalSource = source;
|
|
220
245
|
let finalModel = model;
|
|
@@ -223,27 +248,38 @@ function resolveAgentConfig(args = {}, env = process.env) {
|
|
|
223
248
|
if (detected) {
|
|
224
249
|
finalCommand = expandBuiltinAgentCommand(`builtin:${detected}`);
|
|
225
250
|
finalSource = "auto";
|
|
226
|
-
// Store the detected vendor name as the model hint so doctor can show it.
|
|
227
251
|
if (!finalModel)
|
|
228
252
|
finalModel = `builtin:${detected}`;
|
|
229
253
|
}
|
|
230
254
|
}
|
|
231
|
-
return {
|
|
255
|
+
return {
|
|
256
|
+
schemaVersion: 1,
|
|
257
|
+
command: finalCommand,
|
|
258
|
+
args: cfgArgs,
|
|
259
|
+
endpoint,
|
|
260
|
+
model: finalModel,
|
|
261
|
+
timeoutMs,
|
|
262
|
+
attestPublicKey,
|
|
263
|
+
requireAttestedTelemetry,
|
|
264
|
+
source: finalSource,
|
|
265
|
+
};
|
|
232
266
|
}
|
|
233
267
|
/** True iff a command-template OR endpoint is configured (after resolution). */
|
|
234
268
|
function agentConfigured(args = {}, env = process.env) {
|
|
235
269
|
const resolved = resolveAgentConfig(args, env);
|
|
236
270
|
return Boolean(resolved.command || resolved.endpoint);
|
|
237
271
|
}
|
|
238
|
-
/** Secret-stripped copy safe to persist or show — never carries raw
|
|
272
|
+
/** Secret-stripped copy safe to persist or show — never carries raw
|
|
273
|
+
* credentials. */
|
|
239
274
|
function redacted(config) {
|
|
240
275
|
return {
|
|
241
276
|
...config,
|
|
242
|
-
args: config.args ? (0,
|
|
277
|
+
args: config.args ? (0, agent_1.stripSecretArgs)(config.args) : undefined,
|
|
243
278
|
};
|
|
244
279
|
}
|
|
245
|
-
/** Persist the durable config (secret-stripped). Returns the stored,
|
|
246
|
-
* config. API keys are NEVER written — they come from the
|
|
280
|
+
/** Persist the durable config (secret-stripped). Returns the stored,
|
|
281
|
+
* redacted config. API keys are NEVER written — they come from the
|
|
282
|
+
* agent's own env. */
|
|
247
283
|
function setAgentConfigFile(patch, env = process.env) {
|
|
248
284
|
const current = loadAgentConfigFile(env) || { schemaVersion: 1 };
|
|
249
285
|
const incoming = agentConfigFromArgs(patch);
|
|
@@ -256,16 +292,16 @@ function setAgentConfigFile(patch, env = process.env) {
|
|
|
256
292
|
timeoutMs: firstDefined(incoming.timeoutMs, current.timeoutMs),
|
|
257
293
|
attestPublicKey: firstDefined(incoming.attestPublicKey, current.attestPublicKey),
|
|
258
294
|
requireAttestedTelemetry: firstDefined(incoming.requireAttestedTelemetry, current.requireAttestedTelemetry),
|
|
259
|
-
source: "file"
|
|
295
|
+
source: "file",
|
|
260
296
|
};
|
|
261
297
|
const stored = redacted(merged);
|
|
262
298
|
const file = agentConfigPath(env);
|
|
263
|
-
|
|
264
|
-
(0, state_1.writeJson)(file, stored);
|
|
299
|
+
(0, fs_atomic_1.writeJson)(file, stored);
|
|
265
300
|
return stored;
|
|
266
301
|
}
|
|
267
|
-
/** Read-only, deterministic projection of the effective config
|
|
268
|
-
* No now-derived field — safe for CLI<->MCP payload
|
|
302
|
+
/** Read-only, deterministic projection of the effective config
|
|
303
|
+
* (secret-stripped). No now-derived field — safe for CLI<->MCP payload
|
|
304
|
+
* parity. */
|
|
269
305
|
function agentConfigShow(args = {}, env = process.env) {
|
|
270
306
|
const resolved = resolveAgentConfig(args, env);
|
|
271
307
|
return {
|
|
@@ -274,6 +310,17 @@ function agentConfigShow(args = {}, env = process.env) {
|
|
|
274
310
|
source: resolved.source,
|
|
275
311
|
config: redacted(resolved),
|
|
276
312
|
path: agentConfigPath(env),
|
|
277
|
-
fileExists:
|
|
313
|
+
fileExists: fs.existsSync(agentConfigPath(env)),
|
|
278
314
|
};
|
|
279
315
|
}
|
|
316
|
+
/** Persist the durable agent config (secret-stripped) and return the new
|
|
317
|
+
* state. */
|
|
318
|
+
function backendAgentConfigSet(args, env = process.env) {
|
|
319
|
+
setAgentConfigFile(args, env);
|
|
320
|
+
return agentConfigShow(args, env);
|
|
321
|
+
}
|
|
322
|
+
/** Read-only, deterministic projection of the effective agent config
|
|
323
|
+
* (secret-stripped). */
|
|
324
|
+
function backendAgentConfigShow(args, env = process.env) {
|
|
325
|
+
return agentConfigShow(args, env);
|
|
326
|
+
}
|