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,650 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// shell/workflow-app-loader.ts — the real workflow-app loader + discovery.
|
|
3
|
+
//
|
|
4
|
+
// See core/workflow-apps/app-schema.ts's file header for the validation
|
|
5
|
+
// half this file calls into. This file `require()`s real bundled apps'
|
|
6
|
+
// `app.json` + `workflow.js` (the SAME files
|
|
7
|
+
// plugins/cool-workflow/apps/<id>/ ships today, plus the legacy
|
|
8
|
+
// `workflows/<name>.workflow.js` factory files) and interprets the REAL
|
|
9
|
+
// manifest — it does not hard-code any specific app's behavior.
|
|
10
|
+
//
|
|
11
|
+
// Bundled app root resolution: `CW_APPS_DIR` env override (used by
|
|
12
|
+
// conformance/tests to point at a fixture tree), else the real
|
|
13
|
+
// `plugins/cool-workflow/apps` directory shipped alongside this build
|
|
14
|
+
// (walked up from this file's own location), else `<cwd>/apps`. The
|
|
15
|
+
// legacy `workflows/` root sits alongside `apps/` (one level up), same
|
|
16
|
+
// walk-up resolution, `CW_WORKFLOWS_DIR` override.
|
|
17
|
+
//
|
|
18
|
+
// MILESTONE 12 additions on top of the milestone 6+7 minimal loader
|
|
19
|
+
// (`loadWorkflowApp`, kept byte-for-byte — `shell/drive.ts` and
|
|
20
|
+
// `shell/pipeline-cli.ts` call it directly and must keep working):
|
|
21
|
+
// - `listWorkflowApps()` / `listWorkflows()` — full discovery over BOTH
|
|
22
|
+
// `apps/*/app.json` and legacy `workflows/*.workflow.js`, sorted by
|
|
23
|
+
// id then source path, fail-closed on a duplicate app id.
|
|
24
|
+
// - `loadWorkflowAppRecordById()` — the full LoadedWorkflowAppRecord
|
|
25
|
+
// (source kind, legacy flag, compatibility) `app show`/`app validate`
|
|
26
|
+
// need, vs. the minimal `LoadedWorkflowApp` `plan`/`drive` use.
|
|
27
|
+
// - `showWorkflowApp()` / `validateWorkflowAppTarget()` / `initApp()` /
|
|
28
|
+
// `packageApp()` — the `cw app show|validate|init|package` bodies.
|
|
29
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
30
|
+
if (k2 === undefined) k2 = k;
|
|
31
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
32
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
33
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
34
|
+
}
|
|
35
|
+
Object.defineProperty(o, k2, desc);
|
|
36
|
+
}) : (function(o, m, k, k2) {
|
|
37
|
+
if (k2 === undefined) k2 = k;
|
|
38
|
+
o[k2] = m[k];
|
|
39
|
+
}));
|
|
40
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
41
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
42
|
+
}) : function(o, v) {
|
|
43
|
+
o["default"] = v;
|
|
44
|
+
});
|
|
45
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
46
|
+
var ownKeys = function(o) {
|
|
47
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
48
|
+
var ar = [];
|
|
49
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
50
|
+
return ar;
|
|
51
|
+
};
|
|
52
|
+
return ownKeys(o);
|
|
53
|
+
};
|
|
54
|
+
return function (mod) {
|
|
55
|
+
if (mod && mod.__esModule) return mod;
|
|
56
|
+
var result = {};
|
|
57
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
58
|
+
__setModuleDefault(result, mod);
|
|
59
|
+
return result;
|
|
60
|
+
};
|
|
61
|
+
})();
|
|
62
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
63
|
+
exports.WorkflowAppNotFoundError = void 0;
|
|
64
|
+
exports.loadWorkflowApp = loadWorkflowApp;
|
|
65
|
+
exports.listWorkflowAppRecords = listWorkflowAppRecords;
|
|
66
|
+
exports.loadWorkflowAppRecordById = loadWorkflowAppRecordById;
|
|
67
|
+
exports.listWorkflowApps = listWorkflowApps;
|
|
68
|
+
exports.listWorkflowsShallow = listWorkflowsShallow;
|
|
69
|
+
exports.showWorkflowApp = showWorkflowApp;
|
|
70
|
+
exports.validateWorkflowAppTarget = validateWorkflowAppTarget;
|
|
71
|
+
exports.initWorkflowApp = initWorkflowApp;
|
|
72
|
+
exports.packageWorkflowApp = packageWorkflowApp;
|
|
73
|
+
const fs = __importStar(require("node:fs"));
|
|
74
|
+
const path = __importStar(require("node:path"));
|
|
75
|
+
const app_schema_1 = require("../core/workflow-apps/app-schema");
|
|
76
|
+
const sandbox_profile_1 = require("./sandbox-profile");
|
|
77
|
+
const version_1 = require("../core/version");
|
|
78
|
+
class WorkflowAppNotFoundError extends Error {
|
|
79
|
+
constructor(appId) {
|
|
80
|
+
super(`Workflow app not found: ${appId}`);
|
|
81
|
+
this.name = "WorkflowAppNotFoundError";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.WorkflowAppNotFoundError = WorkflowAppNotFoundError;
|
|
85
|
+
function walkUpFor(...tail) {
|
|
86
|
+
const roots = [];
|
|
87
|
+
let dir = __dirname;
|
|
88
|
+
for (let i = 0; i < 8; i++) {
|
|
89
|
+
roots.push(path.join(dir, "plugins", "cool-workflow", ...tail));
|
|
90
|
+
const parent = path.dirname(dir);
|
|
91
|
+
if (parent === dir)
|
|
92
|
+
break;
|
|
93
|
+
dir = parent;
|
|
94
|
+
}
|
|
95
|
+
return roots;
|
|
96
|
+
}
|
|
97
|
+
/** Walk up from this file's own location and, at each ancestor that looks
|
|
98
|
+
* like the package root (has a package.json), yield `<pkgRoot>/<tail>`.
|
|
99
|
+
* On a published/globally-installed package the bundled `apps/`+`workflows/`
|
|
100
|
+
* sit at the PACKAGE ROOT (`<pkg>/apps`), NOT under a nested
|
|
101
|
+
* `plugins/cool-workflow/` segment — walkUpFor above only ever builds the
|
|
102
|
+
* nested monorepo path, so a global install found 0 apps. This restores the
|
|
103
|
+
* old build's resolvePluginRoot behavior (find the dir with package.json,
|
|
104
|
+
* then read `<pluginRoot>/apps`). The loader lives at
|
|
105
|
+
* `<pkg>/dist/shell/workflow-app-loader.js`, so `<pkg>` is two hops up. */
|
|
106
|
+
function walkUpForPackageRoot(...tail) {
|
|
107
|
+
const roots = [];
|
|
108
|
+
let dir = __dirname;
|
|
109
|
+
for (let i = 0; i < 8; i++) {
|
|
110
|
+
if (fs.existsSync(path.join(dir, "package.json"))) {
|
|
111
|
+
roots.push(path.join(dir, ...tail));
|
|
112
|
+
}
|
|
113
|
+
const parent = path.dirname(dir);
|
|
114
|
+
if (parent === dir)
|
|
115
|
+
break;
|
|
116
|
+
dir = parent;
|
|
117
|
+
}
|
|
118
|
+
return roots;
|
|
119
|
+
}
|
|
120
|
+
function candidateAppsRoots() {
|
|
121
|
+
const roots = [];
|
|
122
|
+
if (process.env.CW_APPS_DIR)
|
|
123
|
+
roots.push(path.resolve(process.env.CW_APPS_DIR));
|
|
124
|
+
// Walk up from this file's own location looking for a sibling
|
|
125
|
+
// plugins/cool-workflow/apps tree (the real bundled apps ship there in
|
|
126
|
+
// this monorepo checkout).
|
|
127
|
+
roots.push(...walkUpFor("apps"));
|
|
128
|
+
// ...and for the package-root `<pkg>/apps` tree an npm-installed (global or
|
|
129
|
+
// local) package ships. Placed before the cwd fallback so bundled apps win.
|
|
130
|
+
roots.push(...walkUpForPackageRoot("apps"));
|
|
131
|
+
roots.push(path.join(process.cwd(), "apps"));
|
|
132
|
+
return roots;
|
|
133
|
+
}
|
|
134
|
+
function candidateWorkflowsRoots() {
|
|
135
|
+
const roots = [];
|
|
136
|
+
if (process.env.CW_WORKFLOWS_DIR)
|
|
137
|
+
roots.push(path.resolve(process.env.CW_WORKFLOWS_DIR));
|
|
138
|
+
roots.push(...walkUpFor("workflows"));
|
|
139
|
+
roots.push(...walkUpForPackageRoot("workflows"));
|
|
140
|
+
roots.push(path.join(process.cwd(), "workflows"));
|
|
141
|
+
return roots;
|
|
142
|
+
}
|
|
143
|
+
function findAppDir(appId) {
|
|
144
|
+
for (const root of candidateAppsRoots()) {
|
|
145
|
+
const dir = path.join(root, appId);
|
|
146
|
+
if (fs.existsSync(path.join(dir, "app.json")))
|
|
147
|
+
return dir;
|
|
148
|
+
}
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
function validationContext() {
|
|
152
|
+
return { bundledSandboxProfileIds: (0, sandbox_profile_1.bundledSandboxProfileIds)(), currentCoolWorkflowVersion: version_1.CURRENT_COOL_WORKFLOW_VERSION };
|
|
153
|
+
}
|
|
154
|
+
function authorNameOf(author) {
|
|
155
|
+
if (typeof author === "string")
|
|
156
|
+
return author;
|
|
157
|
+
if (author && typeof author === "object" && typeof author.name === "string")
|
|
158
|
+
return author.name;
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
/** Convert a discovery record (from listWorkflowAppRecords) into the minimal
|
|
162
|
+
* LoadedWorkflowApp shape `plan`/`drive` consume. Used as the fallback path
|
|
163
|
+
* in loadWorkflowApp so a legacy `<name>.workflow.js` wrapper — which `cw
|
|
164
|
+
* list`/`cw app list` already resolve as a record but which has no
|
|
165
|
+
* `apps/<id>/app.json` directory — can also be planned by id. Without this,
|
|
166
|
+
* `cw plan legacy-research-synthesis` died "Workflow app not found" even
|
|
167
|
+
* though `cw list` shows it. */
|
|
168
|
+
function loadedAppFromRecord(record) {
|
|
169
|
+
const workflowDefinition = record.app.workflow;
|
|
170
|
+
return {
|
|
171
|
+
id: record.app.id,
|
|
172
|
+
title: record.app.title,
|
|
173
|
+
summary: record.app.summary || workflowDefinition.summary || "",
|
|
174
|
+
version: record.app.version,
|
|
175
|
+
author: authorNameOf(record.app.author),
|
|
176
|
+
workflow: workflowDefinition,
|
|
177
|
+
sandboxProfiles: record.app.sandboxProfiles || workflowDefinition.sandboxProfiles || [],
|
|
178
|
+
sourcePath: sourcePathOf(record),
|
|
179
|
+
compatibility: record.app.compatibility,
|
|
180
|
+
metadata: record.app.metadata,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/** Load one real bundled workflow app by id: reads its `app.json`
|
|
184
|
+
* manifest, then `require()`s its declared `workflow.entrypoint` factory
|
|
185
|
+
* file (the same `({workflow, phase, parallel, agent, artifact, input})
|
|
186
|
+
* => workflow({...})` shape the old build's workflow-api.ts factories
|
|
187
|
+
* produce), and returns the interpreted WorkflowDefinition. A legacy
|
|
188
|
+
* workflow-file id (no `apps/<id>/app.json`) resolves through the record
|
|
189
|
+
* fallback below. */
|
|
190
|
+
function loadWorkflowApp(appId) {
|
|
191
|
+
const dir = findAppDir(appId);
|
|
192
|
+
if (!dir) {
|
|
193
|
+
// No `apps/<id>/app.json` directory: fall back to the full discovery set
|
|
194
|
+
// (app directories + legacy `<name>.workflow.js` wrappers) so a legacy
|
|
195
|
+
// workflow-file app id resolves for plan/drive the same way `cw list`
|
|
196
|
+
// resolves it. loadWorkflowAppRecordById throws WorkflowAppNotFoundError
|
|
197
|
+
// when nothing matches, preserving the not-found contract.
|
|
198
|
+
return loadedAppFromRecord(loadWorkflowAppRecordById(appId));
|
|
199
|
+
}
|
|
200
|
+
const manifestPath = path.join(dir, "app.json");
|
|
201
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
202
|
+
const entrypointPath = path.resolve(dir, manifest.workflow.entrypoint);
|
|
203
|
+
// Workflow apps are plain runtime JavaScript, not TypeScript.
|
|
204
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
205
|
+
const rawExport = require(entrypointPath);
|
|
206
|
+
const selected = manifest.workflow.exportName && rawExport && typeof rawExport === "object" ? rawExport[manifest.workflow.exportName] : rawExport;
|
|
207
|
+
if (typeof selected !== "function") {
|
|
208
|
+
throw new Error(`Workflow app entrypoint must export a factory function: ${entrypointPath}`);
|
|
209
|
+
}
|
|
210
|
+
const definition = selected({
|
|
211
|
+
...(0, app_schema_1.createWorkflowApi)(),
|
|
212
|
+
parallel: app_schema_1.parallel,
|
|
213
|
+
phase: app_schema_1.phase,
|
|
214
|
+
workflow: app_schema_1.workflow,
|
|
215
|
+
agent: app_schema_1.agent,
|
|
216
|
+
artifact: app_schema_1.artifact,
|
|
217
|
+
input: app_schema_1.input,
|
|
218
|
+
});
|
|
219
|
+
return {
|
|
220
|
+
id: manifest.id,
|
|
221
|
+
title: manifest.title,
|
|
222
|
+
summary: manifest.summary || definition.summary || "",
|
|
223
|
+
version: manifest.version,
|
|
224
|
+
author: authorNameOf(manifest.author),
|
|
225
|
+
workflow: definition,
|
|
226
|
+
sandboxProfiles: manifest.sandboxProfiles || definition.sandboxProfiles || [],
|
|
227
|
+
sourcePath: manifestPath,
|
|
228
|
+
// Thread the manifest's compatibility window + metadata (incl. domain)
|
|
229
|
+
// into the loaded app so workflowAppRunMetadata can stamp them onto
|
|
230
|
+
// run.workflow.app — this is what lets report.md label a research-domain
|
|
231
|
+
// run's source line "Source:" instead of "Repository:".
|
|
232
|
+
compatibility: manifest.compatibility,
|
|
233
|
+
metadata: manifest.metadata,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
// ---------------------------------------------------------------------
|
|
237
|
+
// Full discovery: apps/*/app.json + legacy workflows/*.workflow.js
|
|
238
|
+
// (MILESTONE 12). Byte-exact in spirit to
|
|
239
|
+
// src/orchestrator/app-operations.ts's loadWorkflowApps/listApps/showApp/
|
|
240
|
+
// validateApp/initApp/packageApp.
|
|
241
|
+
// ---------------------------------------------------------------------
|
|
242
|
+
const FACTORY_API = { ...(0, app_schema_1.createWorkflowApi)(), parallel: app_schema_1.parallel, phase: app_schema_1.phase, loop: app_schema_1.loop, workflow: app_schema_1.workflow, agent: app_schema_1.agent, artifact: app_schema_1.artifact, subWorkflow: app_schema_1.subWorkflow, input: app_schema_1.input };
|
|
243
|
+
function materializeFactoryExport(entrypointPath, exportName) {
|
|
244
|
+
if (!fs.existsSync(entrypointPath)) {
|
|
245
|
+
throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app entrypoint", [
|
|
246
|
+
{ code: "workflow-app-entrypoint-not-found", message: `Workflow app entrypoint does not exist: ${entrypointPath}`, path: entrypointPath },
|
|
247
|
+
]);
|
|
248
|
+
}
|
|
249
|
+
// Workflow apps are plain runtime JavaScript, not TypeScript.
|
|
250
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
251
|
+
const rawExport = require(entrypointPath);
|
|
252
|
+
const selected = exportName && rawExport && typeof rawExport === "object" ? rawExport[exportName] : rawExport;
|
|
253
|
+
if (exportName && selected === undefined) {
|
|
254
|
+
throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app entrypoint", [
|
|
255
|
+
{ code: "workflow-app-entrypoint-export", message: `Workflow app entrypoint does not export ${exportName}`, path: entrypointPath },
|
|
256
|
+
]);
|
|
257
|
+
}
|
|
258
|
+
if (typeof selected === "function") {
|
|
259
|
+
return selected(FACTORY_API);
|
|
260
|
+
}
|
|
261
|
+
return selected;
|
|
262
|
+
}
|
|
263
|
+
function isWorkflowDefinitionShape(value) {
|
|
264
|
+
return Boolean(value &&
|
|
265
|
+
typeof value === "object" &&
|
|
266
|
+
!Array.isArray(value) &&
|
|
267
|
+
typeof value.id === "string" &&
|
|
268
|
+
typeof value.title === "string" &&
|
|
269
|
+
Array.isArray(value.phases));
|
|
270
|
+
}
|
|
271
|
+
function isWorkflowAppDefinitionShape(value) {
|
|
272
|
+
return Boolean(value && typeof value === "object" && value.schemaVersion === 1 && "workflow" in value);
|
|
273
|
+
}
|
|
274
|
+
function extractFactoryExport(value, file) {
|
|
275
|
+
if (isWorkflowAppDefinitionShape(value) && isWorkflowDefinitionShape(value.workflow)) {
|
|
276
|
+
return { app: value, workflow: value.workflow };
|
|
277
|
+
}
|
|
278
|
+
if (isWorkflowDefinitionShape(value)) {
|
|
279
|
+
return { workflow: value };
|
|
280
|
+
}
|
|
281
|
+
throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app entrypoint", [
|
|
282
|
+
{ code: "workflow-app-entrypoint-export", message: "Workflow app entrypoint must export a workflow definition, workflow app, or factory", path: file },
|
|
283
|
+
]);
|
|
284
|
+
}
|
|
285
|
+
function collectWorkflowSandboxProfiles(definition) {
|
|
286
|
+
const profiles = new Set();
|
|
287
|
+
for (const phaseDefinition of definition.phases) {
|
|
288
|
+
for (const taskDefinition of phaseDefinition.tasks) {
|
|
289
|
+
if (taskDefinition.sandboxProfileId)
|
|
290
|
+
profiles.add(taskDefinition.sandboxProfileId);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return [...profiles].sort();
|
|
294
|
+
}
|
|
295
|
+
function createLegacyWorkflowApp(definition, source) {
|
|
296
|
+
return {
|
|
297
|
+
schemaVersion: 1,
|
|
298
|
+
id: definition.id,
|
|
299
|
+
title: definition.title,
|
|
300
|
+
summary: definition.summary || "",
|
|
301
|
+
version: "0.0.0",
|
|
302
|
+
workflow: definition,
|
|
303
|
+
inputs: definition.inputs || [],
|
|
304
|
+
sandboxProfiles: definition.sandboxProfiles || collectWorkflowSandboxProfiles(definition),
|
|
305
|
+
compatibility: { maxVersion: version_1.CURRENT_COOL_WORKFLOW_VERSION, notes: "Compatibility wrapper for legacy .workflow.js factory files." },
|
|
306
|
+
metadata: { legacyWorkflow: true, sourcePath: source.path },
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/** Loads one workflow app from a legacy `<name>.workflow.js` entrypoint
|
|
310
|
+
* (no `app.json`). Ported from `loadWorkflowAppFromEntrypoint`. */
|
|
311
|
+
function loadWorkflowAppFromEntrypoint(file) {
|
|
312
|
+
const entrypointPath = path.resolve(file);
|
|
313
|
+
const materialized = materializeFactoryExport(entrypointPath);
|
|
314
|
+
const loaded = extractFactoryExport(materialized, entrypointPath);
|
|
315
|
+
const source = { kind: "workflow-file", path: entrypointPath, entrypointPath };
|
|
316
|
+
if (loaded.app) {
|
|
317
|
+
const app = { ...loaded.app, workflow: loaded.workflow };
|
|
318
|
+
assertValid(app, { appPath: entrypointPath });
|
|
319
|
+
return { app, source, legacy: false };
|
|
320
|
+
}
|
|
321
|
+
const app = createLegacyWorkflowApp(loaded.workflow, source);
|
|
322
|
+
assertValid(app, { appPath: entrypointPath });
|
|
323
|
+
return { app, source, legacy: true };
|
|
324
|
+
}
|
|
325
|
+
function validateEntrypointAppMatchesManifest(entrypointApp, manifest, manifestPath) {
|
|
326
|
+
const issues = [];
|
|
327
|
+
for (const key of ["schemaVersion", "id", "title", "version"]) {
|
|
328
|
+
if (entrypointApp[key] !== manifest[key]) {
|
|
329
|
+
issues.push({ code: "workflow-app-manifest-mismatch", message: `Entrypoint app ${key} must match manifest ${key}`, path: `${manifestPath}.${key}` });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (issues.length)
|
|
333
|
+
throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app manifest", issues);
|
|
334
|
+
}
|
|
335
|
+
function assertValid(app, options) {
|
|
336
|
+
const result = (0, app_schema_1.validateWorkflowApp)(app, validationContext(), options);
|
|
337
|
+
if (!result.valid)
|
|
338
|
+
throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app", result.issues);
|
|
339
|
+
}
|
|
340
|
+
/** Loads one workflow app from an `app.json` manifest path. Ported from
|
|
341
|
+
* `loadWorkflowAppFromManifest`. */
|
|
342
|
+
function loadWorkflowAppFromManifest(manifestPath) {
|
|
343
|
+
const absoluteManifestPath = path.resolve(manifestPath);
|
|
344
|
+
if (!fs.existsSync(absoluteManifestPath)) {
|
|
345
|
+
throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app manifest", [
|
|
346
|
+
{ code: "workflow-app-manifest-not-found", message: `Workflow app manifest does not exist: ${absoluteManifestPath}`, path: absoluteManifestPath },
|
|
347
|
+
]);
|
|
348
|
+
}
|
|
349
|
+
let manifest;
|
|
350
|
+
try {
|
|
351
|
+
manifest = JSON.parse(fs.readFileSync(absoluteManifestPath, "utf8"));
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app manifest", [
|
|
355
|
+
{
|
|
356
|
+
code: "workflow-app-manifest-json",
|
|
357
|
+
message: `Workflow app manifest is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
358
|
+
path: absoluteManifestPath,
|
|
359
|
+
},
|
|
360
|
+
]);
|
|
361
|
+
}
|
|
362
|
+
const manifestValidation = (0, app_schema_1.validateWorkflowApp)(manifest, validationContext(), { appPath: absoluteManifestPath });
|
|
363
|
+
if (!manifestValidation.valid) {
|
|
364
|
+
throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app manifest", manifestValidation.issues);
|
|
365
|
+
}
|
|
366
|
+
const workflowField = manifest.workflow;
|
|
367
|
+
const isEntrypoint = Boolean(workflowField && typeof workflowField === "object" && "entrypoint" in workflowField && !("phases" in workflowField));
|
|
368
|
+
if (!isEntrypoint) {
|
|
369
|
+
throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app manifest", [
|
|
370
|
+
{ code: "workflow-app-entrypoint", message: "Manifest workflow must be an entrypoint object", path: `${absoluteManifestPath}.workflow` },
|
|
371
|
+
]);
|
|
372
|
+
}
|
|
373
|
+
const entrypointPath = path.resolve(path.dirname(absoluteManifestPath), manifest.workflow.entrypoint);
|
|
374
|
+
const materialized = materializeFactoryExport(entrypointPath, manifest.workflow.exportName);
|
|
375
|
+
const loaded = extractFactoryExport(materialized, entrypointPath);
|
|
376
|
+
const workflowDefinition = loaded.workflow;
|
|
377
|
+
const source = {
|
|
378
|
+
kind: path.basename(absoluteManifestPath) === "app.json" ? "app-directory" : "app-manifest",
|
|
379
|
+
path: path.dirname(absoluteManifestPath),
|
|
380
|
+
manifestPath: absoluteManifestPath,
|
|
381
|
+
entrypointPath,
|
|
382
|
+
};
|
|
383
|
+
if (loaded.app) {
|
|
384
|
+
validateEntrypointAppMatchesManifest(loaded.app, manifest, absoluteManifestPath);
|
|
385
|
+
}
|
|
386
|
+
const app = { ...manifest, workflow: workflowDefinition };
|
|
387
|
+
assertValid(app, { appPath: absoluteManifestPath, loadedWorkflow: workflowDefinition });
|
|
388
|
+
return { app, source, legacy: false };
|
|
389
|
+
}
|
|
390
|
+
function loadWorkflowFiles(workflowsDir) {
|
|
391
|
+
if (!fs.existsSync(workflowsDir))
|
|
392
|
+
return [];
|
|
393
|
+
return fs
|
|
394
|
+
.readdirSync(workflowsDir)
|
|
395
|
+
.filter((file) => file.endsWith(".workflow.js"))
|
|
396
|
+
.sort()
|
|
397
|
+
.map((file) => path.join(workflowsDir, file));
|
|
398
|
+
}
|
|
399
|
+
function loadAppManifestFiles(appsDir) {
|
|
400
|
+
if (!fs.existsSync(appsDir))
|
|
401
|
+
return [];
|
|
402
|
+
return fs
|
|
403
|
+
.readdirSync(appsDir, { withFileTypes: true })
|
|
404
|
+
.filter((entry) => entry.isDirectory())
|
|
405
|
+
.map((entry) => path.join(appsDir, entry.name, "app.json"))
|
|
406
|
+
.filter((file) => fs.existsSync(file))
|
|
407
|
+
.sort();
|
|
408
|
+
}
|
|
409
|
+
function sourcePathOf(record) {
|
|
410
|
+
return record.source.manifestPath || record.source.entrypointPath || record.source.path;
|
|
411
|
+
}
|
|
412
|
+
/** Full discovery over every real bundled app + legacy workflow file: one
|
|
413
|
+
* root each, walked up from this file's own location (or `CW_APPS_DIR`/
|
|
414
|
+
* `CW_WORKFLOWS_DIR`). Sorted by app id then source path; a duplicate id
|
|
415
|
+
* across BOTH roots is a fail-closed error, matching
|
|
416
|
+
* `loadWorkflowApps` in the old build. */
|
|
417
|
+
function listWorkflowAppRecords() {
|
|
418
|
+
const appsDir = candidateAppsRoots().find((root) => fs.existsSync(root)) || candidateAppsRoots()[0];
|
|
419
|
+
const workflowsDir = candidateWorkflowsRoots().find((root) => fs.existsSync(root)) || candidateWorkflowsRoots()[0];
|
|
420
|
+
const records = [
|
|
421
|
+
...loadWorkflowFiles(workflowsDir).map((file) => loadWorkflowAppFromEntrypoint(file)),
|
|
422
|
+
...loadAppManifestFiles(appsDir).map((file) => loadWorkflowAppFromManifest(file)),
|
|
423
|
+
].sort((left, right) => {
|
|
424
|
+
const byId = left.app.id.localeCompare(right.app.id);
|
|
425
|
+
if (byId)
|
|
426
|
+
return byId;
|
|
427
|
+
return sourcePathOf(left).localeCompare(sourcePathOf(right));
|
|
428
|
+
});
|
|
429
|
+
const seen = new Map();
|
|
430
|
+
for (const record of records) {
|
|
431
|
+
const previous = seen.get(record.app.id);
|
|
432
|
+
if (previous) {
|
|
433
|
+
throw new Error(`Duplicate workflow app id ${record.app.id}: ${sourcePathOf(previous)} and ${sourcePathOf(record)}`);
|
|
434
|
+
}
|
|
435
|
+
seen.set(record.app.id, record);
|
|
436
|
+
}
|
|
437
|
+
return records;
|
|
438
|
+
}
|
|
439
|
+
function loadWorkflowAppRecordById(appId) {
|
|
440
|
+
const record = listWorkflowAppRecords().find((candidate) => candidate.app.id === appId);
|
|
441
|
+
if (!record)
|
|
442
|
+
throw new WorkflowAppNotFoundError(appId);
|
|
443
|
+
return record;
|
|
444
|
+
}
|
|
445
|
+
/** Resolves a `cw app validate <path-or-id>` / `cw app show <path-or-id>`
|
|
446
|
+
* target: an existing directory/`app.json`/other-file path wins over an
|
|
447
|
+
* id lookup, matching `loadWorkflowAppTarget` in the old build. */
|
|
448
|
+
function loadWorkflowAppRecordTarget(target) {
|
|
449
|
+
if (!target)
|
|
450
|
+
throw new Error("Missing workflow app path or id");
|
|
451
|
+
const resolved = path.resolve(target);
|
|
452
|
+
if (fs.existsSync(resolved)) {
|
|
453
|
+
const stat = fs.statSync(resolved);
|
|
454
|
+
if (stat.isDirectory())
|
|
455
|
+
return loadWorkflowAppFromManifest(path.join(resolved, "app.json"));
|
|
456
|
+
if (path.basename(resolved) === "app.json" || resolved.endsWith(".json"))
|
|
457
|
+
return loadWorkflowAppFromManifest(resolved);
|
|
458
|
+
return loadWorkflowAppFromEntrypoint(resolved);
|
|
459
|
+
}
|
|
460
|
+
return loadWorkflowAppRecordById(target);
|
|
461
|
+
}
|
|
462
|
+
function summarizeWorkflowAppRecord(record) {
|
|
463
|
+
const workflowDefinition = record.app.workflow;
|
|
464
|
+
const sandboxProfiles = record.app.sandboxProfiles || workflowDefinition.sandboxProfiles || [];
|
|
465
|
+
return {
|
|
466
|
+
id: record.app.id,
|
|
467
|
+
title: record.app.title,
|
|
468
|
+
summary: record.app.summary || workflowDefinition.summary || "",
|
|
469
|
+
version: record.app.version,
|
|
470
|
+
author: record.app.author,
|
|
471
|
+
file: sourcePathOf(record),
|
|
472
|
+
sourceKind: record.source.kind,
|
|
473
|
+
legacy: record.legacy,
|
|
474
|
+
compatible: (0, app_schema_1.isWorkflowAppCompatible)(record.app, validationContext()),
|
|
475
|
+
inputs: record.app.inputs || workflowDefinition.inputs || [],
|
|
476
|
+
sandboxProfiles,
|
|
477
|
+
phases: workflowDefinition.phases.map((phaseDefinition) => ({ id: phaseDefinition.id, name: phaseDefinition.name, taskCount: phaseDefinition.tasks.length })),
|
|
478
|
+
taskCount: workflowDefinition.phases.reduce((total, phaseDefinition) => total + phaseDefinition.tasks.length, 0),
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
/** `cw app list` / `cw_app_list`. Key order matches `WorkflowAppSummary`;
|
|
482
|
+
* a legacy app's `author` key is OMITTED (never present, never null) —
|
|
483
|
+
* callers must build the JSON object with the same conditional-spread
|
|
484
|
+
* discipline used here. */
|
|
485
|
+
function listWorkflowApps() {
|
|
486
|
+
return listWorkflowAppRecords().map((record) => summaryToJson(summarizeWorkflowAppRecord(record)));
|
|
487
|
+
}
|
|
488
|
+
function summaryToJson(summary) {
|
|
489
|
+
const { author, ...rest } = summary;
|
|
490
|
+
return {
|
|
491
|
+
id: rest.id,
|
|
492
|
+
title: rest.title,
|
|
493
|
+
summary: rest.summary,
|
|
494
|
+
version: rest.version,
|
|
495
|
+
...(author !== undefined ? { author } : {}),
|
|
496
|
+
file: rest.file,
|
|
497
|
+
sourceKind: rest.sourceKind,
|
|
498
|
+
legacy: rest.legacy,
|
|
499
|
+
compatible: rest.compatible,
|
|
500
|
+
inputs: rest.inputs,
|
|
501
|
+
sandboxProfiles: rest.sandboxProfiles,
|
|
502
|
+
phases: rest.phases,
|
|
503
|
+
taskCount: rest.taskCount,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
/** `cw list` / `cw_list` — the lighter {id,title,summary,file} view over
|
|
507
|
+
* the SAME discovered app set. */
|
|
508
|
+
function listWorkflowsShallow() {
|
|
509
|
+
return listWorkflowAppRecords().map((record) => {
|
|
510
|
+
const summary = summarizeWorkflowAppRecord(record);
|
|
511
|
+
return { id: summary.id, title: summary.title, summary: summary.summary, file: summary.file };
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
/** `cw app show <id>` / `cw_app_show`. Throws `WorkflowAppNotFoundError`
|
|
515
|
+
* on an unknown id (`recoveryHint` in cli/entry.ts turns this into the
|
|
516
|
+
* `Try: cw app list` tip). */
|
|
517
|
+
function showWorkflowApp(appId) {
|
|
518
|
+
const record = loadWorkflowAppRecordById(appId);
|
|
519
|
+
const summary = summarizeWorkflowAppRecord(record);
|
|
520
|
+
return {
|
|
521
|
+
...summaryToJson(summary),
|
|
522
|
+
source: record.source,
|
|
523
|
+
app: {
|
|
524
|
+
schemaVersion: record.app.schemaVersion,
|
|
525
|
+
id: record.app.id,
|
|
526
|
+
title: record.app.title,
|
|
527
|
+
summary: record.app.summary || "",
|
|
528
|
+
version: record.app.version,
|
|
529
|
+
author: record.app.author,
|
|
530
|
+
inputs: record.app.inputs || record.app.workflow.inputs,
|
|
531
|
+
sandboxProfiles: record.app.sandboxProfiles || record.app.workflow.sandboxProfiles || [],
|
|
532
|
+
compatibility: record.app.compatibility,
|
|
533
|
+
metadata: record.app.metadata || {},
|
|
534
|
+
},
|
|
535
|
+
workflow: {
|
|
536
|
+
id: record.app.workflow.id,
|
|
537
|
+
title: record.app.workflow.title,
|
|
538
|
+
summary: record.app.workflow.summary || "",
|
|
539
|
+
limits: record.app.workflow.limits,
|
|
540
|
+
inputs: record.app.workflow.inputs,
|
|
541
|
+
sandboxProfiles: record.app.workflow.sandboxProfiles || [],
|
|
542
|
+
phases: record.app.workflow.phases.map((phaseDefinition) => ({
|
|
543
|
+
id: phaseDefinition.id,
|
|
544
|
+
name: phaseDefinition.name,
|
|
545
|
+
status: phaseDefinition.status,
|
|
546
|
+
tasks: phaseDefinition.tasks.map((taskDefinition) => ({
|
|
547
|
+
id: taskDefinition.id,
|
|
548
|
+
kind: taskDefinition.kind,
|
|
549
|
+
requiresEvidence: Boolean(taskDefinition.requiresEvidence),
|
|
550
|
+
sandboxProfileId: taskDefinition.sandboxProfileId,
|
|
551
|
+
})),
|
|
552
|
+
})),
|
|
553
|
+
},
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
/** `cw app validate <path-or-id>` / `cw_app_validate`. Never throws — an
|
|
557
|
+
* unresolvable target gives `{valid:false, appId:target, appPath:target,
|
|
558
|
+
* issues}`, matching `validateApp` in the old build. */
|
|
559
|
+
function validateWorkflowAppTarget(target) {
|
|
560
|
+
const resolvedTarget = path.resolve(target);
|
|
561
|
+
try {
|
|
562
|
+
const record = loadWorkflowAppRecordTarget(target);
|
|
563
|
+
const result = (0, app_schema_1.validateWorkflowApp)(record.app, validationContext(), { appPath: sourcePathOf(record) });
|
|
564
|
+
return { ...result, summary: summarizeWorkflowAppRecord(record) };
|
|
565
|
+
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
return { valid: false, appId: target, appPath: resolvedTarget, issues: (0, app_schema_1.validationIssuesFromError)(error) };
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
const SYSTEM_DIR_PATTERN = /^\/(etc|bin|sbin|usr|Library|System|Applications|boot|dev|proc|sys|root|var\/log|var\/run)\//;
|
|
571
|
+
function titleize(value) {
|
|
572
|
+
return value
|
|
573
|
+
.split("-")
|
|
574
|
+
.filter(Boolean)
|
|
575
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
576
|
+
.join(" ");
|
|
577
|
+
}
|
|
578
|
+
function renderManifestTemplate(id, title) {
|
|
579
|
+
return `${JSON.stringify({
|
|
580
|
+
schemaVersion: 1,
|
|
581
|
+
id,
|
|
582
|
+
title,
|
|
583
|
+
summary: "Describe what this workflow app does.",
|
|
584
|
+
version: "0.1.0",
|
|
585
|
+
author: "COOLWHITE LLC",
|
|
586
|
+
inputs: [{ name: "question", type: "string", required: true, description: "Question or task this workflow should answer." }],
|
|
587
|
+
sandboxProfiles: ["readonly"],
|
|
588
|
+
compatibility: { minVersion: "0.1.9" },
|
|
589
|
+
workflow: { entrypoint: "workflow.js" },
|
|
590
|
+
}, null, 2)}\n`;
|
|
591
|
+
}
|
|
592
|
+
function renderEntrypointTemplate(id, title) {
|
|
593
|
+
return `module.exports = ({ workflow, phase, agent, artifact, input }) => {\n const inputs = [\n input("question", { type: "string", required: true, description: "Question or task this workflow should answer." })\n ];\n\n return workflow({\n id: ${JSON.stringify(id)},\n title: ${JSON.stringify(title)},\n summary: "Describe what this workflow app does.",\n limits: {\n maxAgents: 8,\n maxConcurrentAgents: 4\n },\n inputs,\n sandboxProfiles: ["readonly"],\n phases: [\n phase("Map", [\n agent("map:context", "Map the task context, constraints, and evidence needed for {{question}}.", { sandboxProfileId: "readonly" })\n ]),\n phase("Assess", [\n agent("assess:risks", "Assess risks, tradeoffs, and unknowns for {{question}}.", { sandboxProfileId: "readonly" })\n ]),\n phase("Synthesize", [\n artifact("synthesis:report", "Synthesize the final answer for {{question}}.", { requiresEvidence: true, sandboxProfileId: "readonly" })\n ])\n ]\n });\n};\n`;
|
|
594
|
+
}
|
|
595
|
+
/** `cw app init <id>` / `cw_app_init`. Writes `app.json` + `workflow.js`
|
|
596
|
+
* from the templates, refusing system directories and (without
|
|
597
|
+
* `--force`) an existing app. Ported from `initApp`. */
|
|
598
|
+
function initWorkflowApp(appId, options = {}) {
|
|
599
|
+
const id = (0, app_schema_1.slugify)(appId);
|
|
600
|
+
if (!id)
|
|
601
|
+
throw new Error("App id must include at least one letter or digit");
|
|
602
|
+
const title = String(options.title || titleize(id));
|
|
603
|
+
const appsDir = candidateAppsRoots().find((root) => fs.existsSync(root)) || candidateAppsRoots()[0];
|
|
604
|
+
const destinationDir = path.resolve(String(options.directory || options.output || path.join(appsDir, id)));
|
|
605
|
+
if (SYSTEM_DIR_PATTERN.test(destinationDir)) {
|
|
606
|
+
throw new Error(`Refusing to create app in a system directory: ${destinationDir}`);
|
|
607
|
+
}
|
|
608
|
+
const manifestPath = path.join(destinationDir, "app.json");
|
|
609
|
+
const entrypointPath = path.join(destinationDir, "workflow.js");
|
|
610
|
+
if (!options.force && (fs.existsSync(manifestPath) || fs.existsSync(entrypointPath))) {
|
|
611
|
+
throw new Error(`Refusing to overwrite existing workflow app: ${destinationDir}`);
|
|
612
|
+
}
|
|
613
|
+
fs.mkdirSync(destinationDir, { recursive: true });
|
|
614
|
+
fs.writeFileSync(manifestPath, renderManifestTemplate(id, title), "utf8");
|
|
615
|
+
fs.writeFileSync(entrypointPath, renderEntrypointTemplate(id, title), "utf8");
|
|
616
|
+
const validation = validateWorkflowAppTarget(manifestPath);
|
|
617
|
+
if (!validation.valid) {
|
|
618
|
+
throw new app_schema_1.WorkflowAppValidationError("Generated workflow app is invalid", validation.issues);
|
|
619
|
+
}
|
|
620
|
+
return { id, manifestPath, entrypointPath };
|
|
621
|
+
}
|
|
622
|
+
/** `cw app package <id>` / `cw_app_package`. Ported from `packageApp`. The
|
|
623
|
+
* default package path is anchored under the caller's explicit `cwd`
|
|
624
|
+
* (`options.cwd`) so the CLI/MCP surfaces never write into the process cwd
|
|
625
|
+
* when a different working directory was requested — and never chdir. */
|
|
626
|
+
function packageWorkflowApp(appId, options = {}, cwd) {
|
|
627
|
+
const base = cwd || (typeof options.cwd === "string" && options.cwd.trim() ? path.resolve(String(options.cwd)) : process.cwd());
|
|
628
|
+
const record = loadWorkflowAppRecordById(appId);
|
|
629
|
+
const destination = path.resolve(base, String(options.output || path.join(".cw", "packages", `${record.app.id}-${record.app.version}.cwapp.json`)));
|
|
630
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
631
|
+
const payload = {
|
|
632
|
+
schemaVersion: 1,
|
|
633
|
+
app: {
|
|
634
|
+
schemaVersion: 1,
|
|
635
|
+
id: record.app.id,
|
|
636
|
+
title: record.app.title,
|
|
637
|
+
summary: record.app.summary || record.app.workflow.summary || "",
|
|
638
|
+
version: record.app.version,
|
|
639
|
+
author: record.app.author,
|
|
640
|
+
compatibility: record.app.compatibility,
|
|
641
|
+
sandboxProfiles: record.app.sandboxProfiles || record.app.workflow.sandboxProfiles,
|
|
642
|
+
source: record.source,
|
|
643
|
+
metadata: record.app.metadata,
|
|
644
|
+
},
|
|
645
|
+
workflow: record.app.workflow,
|
|
646
|
+
packagedAt: new Date().toISOString(),
|
|
647
|
+
};
|
|
648
|
+
fs.writeFileSync(destination, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
649
|
+
return { id: record.app.id, version: record.app.version, path: destination };
|
|
650
|
+
}
|