@ryanfw/prompt-orchestration-pipeline 1.3.2 → 1.3.4
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/docs/http-api.md +80 -4
- package/package.json +1 -4
- package/src/api/__tests__/index.test.ts +443 -32
- package/src/api/index.ts +108 -127
- package/src/cli/__tests__/analyze-task.test.ts +40 -7
- package/src/cli/__tests__/index.test.ts +337 -17
- package/src/cli/__tests__/types.test.ts +0 -18
- package/src/cli/__tests__/update-pipeline-json.test.ts +19 -9
- package/src/cli/analyze-task.ts +3 -14
- package/src/cli/index.ts +73 -61
- package/src/cli/types.ts +0 -13
- package/src/cli/update-pipeline-json.ts +3 -14
- package/src/config/__tests__/paths.test.ts +46 -1
- package/src/config/__tests__/pipeline-registry.test.ts +767 -0
- package/src/config/__tests__/sse-events.test.ts +470 -0
- package/src/config/__tests__/statuses.test.ts +41 -0
- package/src/config/paths.ts +11 -2
- package/src/config/pipeline-registry.ts +258 -0
- package/src/config/sse-events.ts +122 -0
- package/src/config/statuses.ts +20 -1
- package/src/core/__tests__/agent-step.test.ts +115 -0
- package/src/core/__tests__/config.test.ts +545 -105
- package/src/core/__tests__/core-boot-resolution.test.ts +181 -0
- package/src/core/__tests__/job-concurrency.test.ts +260 -0
- package/src/core/__tests__/job-submission.test.ts +396 -0
- package/src/core/__tests__/job-view.test.ts +1341 -0
- package/src/core/__tests__/json-file.test.ts +111 -0
- package/src/core/__tests__/lifecycle-policy.test.ts +81 -7
- package/src/core/__tests__/logger.test.ts +22 -0
- package/src/core/__tests__/orchestrator-no-deprecated-exports.test.ts +41 -0
- package/src/core/__tests__/orchestrator.test.ts +615 -2
- package/src/core/__tests__/pipeline-runner.test.ts +946 -9
- package/src/core/__tests__/redact.test.ts +153 -0
- package/src/core/__tests__/runner-liveness.test.ts +910 -0
- package/src/core/__tests__/seed-naming.test.ts +57 -0
- package/src/core/__tests__/single-derivation.test.ts +159 -0
- package/src/core/__tests__/task-runner.test.ts +594 -3
- package/src/core/__tests__/task-telemetry.test.ts +241 -0
- package/src/core/__tests__/workspace-root-resolution-guard.test.ts +62 -0
- package/src/core/agent-step.ts +4 -1
- package/src/core/agent-types.ts +5 -4
- package/src/core/config.ts +134 -222
- package/src/core/control.ts +3 -0
- package/src/core/job-concurrency.ts +56 -20
- package/src/core/job-submission.ts +133 -0
- package/src/core/job-view.ts +473 -0
- package/src/core/json-file.ts +45 -0
- package/src/core/lifecycle-policy.ts +23 -8
- package/src/core/logger.ts +0 -29
- package/src/core/orchestrator.ts +200 -74
- package/src/core/pipeline-runner.ts +85 -53
- package/src/core/redact.ts +40 -0
- package/src/core/runner-liveness.ts +280 -0
- package/src/core/seed-naming.ts +9 -0
- package/src/core/status-writer.ts +27 -33
- package/src/core/task-runner.ts +356 -319
- package/src/core/task-telemetry.ts +107 -0
- package/src/harness/__tests__/subprocess.test.ts +112 -1
- package/src/harness/subprocess.ts +55 -16
- package/src/llm/__tests__/index.test.ts +684 -33
- package/src/llm/index.ts +310 -67
- package/src/providers/__tests__/alibaba.test.ts +67 -6
- package/src/providers/__tests__/anthropic.test.ts +35 -14
- package/src/providers/__tests__/base.test.ts +62 -0
- package/src/providers/__tests__/claude-code.test.ts +99 -14
- package/src/providers/__tests__/deepseek.test.ts +16 -6
- package/src/providers/__tests__/gemini.test.ts +47 -25
- package/src/providers/__tests__/moonshot.test.ts +27 -0
- package/src/providers/__tests__/openai.test.ts +262 -74
- package/src/providers/__tests__/opencode.test.ts +77 -0
- package/src/providers/__tests__/request-timeout-contract.test.ts +226 -0
- package/src/providers/__tests__/stream-accumulator.test.ts +52 -0
- package/src/providers/__tests__/types.test.ts +85 -0
- package/src/providers/__tests__/zero-fill-guard.test.ts +62 -0
- package/src/providers/__tests__/zhipu.test.ts +27 -14
- package/src/providers/alibaba.ts +20 -39
- package/src/providers/anthropic.ts +23 -11
- package/src/providers/base.ts +19 -0
- package/src/providers/claude-code.ts +27 -18
- package/src/providers/deepseek.ts +9 -28
- package/src/providers/gemini.ts +20 -58
- package/src/providers/moonshot.ts +15 -11
- package/src/providers/openai.ts +79 -61
- package/src/providers/opencode.ts +16 -33
- package/src/providers/stream-accumulator.ts +27 -21
- package/src/providers/types.ts +29 -4
- package/src/providers/zhipu.ts +15 -44
- package/src/task-analysis/__tests__/analyzer.test.ts +0 -0
- package/src/task-analysis/__tests__/enrichers-schema-deducer.test.ts +34 -2
- package/src/task-analysis/__tests__/no-ast-imports.test.ts +52 -0
- package/src/task-analysis/__tests__/repository.test.ts +65 -0
- package/src/task-analysis/__tests__/types.test.ts +63 -130
- package/src/task-analysis/analyzer.ts +91 -0
- package/src/task-analysis/enrichers/schema-deducer.ts +13 -2
- package/src/task-analysis/index.ts +2 -36
- package/src/task-analysis/repository.ts +45 -0
- package/src/task-analysis/types.ts +42 -58
- package/src/ui/client/__tests__/api.test.ts +143 -1
- package/src/ui/client/__tests__/bootstrap.test.ts +178 -62
- package/src/ui/client/__tests__/job-adapter.test.ts +203 -2
- package/src/ui/client/__tests__/load-state.test.ts +70 -0
- package/src/ui/client/__tests__/types.test.ts +66 -3
- package/src/ui/client/__tests__/useJobDetailWithUpdates.test.ts +390 -77
- package/src/ui/client/__tests__/useJobList.test.ts +198 -23
- package/src/ui/client/__tests__/useJobListWithUpdates.test.ts +218 -7
- package/src/ui/client/adapters/__tests__/job-adapter.test.ts +186 -0
- package/src/ui/client/adapters/job-adapter.ts +41 -16
- package/src/ui/client/api.ts +38 -15
- package/src/ui/client/bootstrap.ts +19 -14
- package/src/ui/client/hooks/useJobDetailWithUpdates.ts +73 -97
- package/src/ui/client/hooks/useJobList.ts +26 -31
- package/src/ui/client/hooks/useJobListWithUpdates.ts +42 -76
- package/src/ui/client/load-state.ts +20 -0
- package/src/ui/client/reducers/__tests__/job-events.test.ts +568 -0
- package/src/ui/client/reducers/job-events.ts +137 -0
- package/src/ui/client/types.ts +16 -20
- package/src/ui/components/DAGGrid.tsx +6 -1
- package/src/ui/components/JobDetail.tsx +12 -4
- package/src/ui/components/JobTable.tsx +41 -13
- package/src/ui/components/StageTimeline.tsx +8 -20
- package/src/ui/components/TaskAnalysisDisplay.tsx +48 -6
- package/src/ui/components/__tests__/DAGGrid.test.tsx +36 -0
- package/src/ui/components/__tests__/JobDetail.test.tsx +112 -0
- package/src/ui/components/__tests__/JobTable.test.tsx +137 -1
- package/src/ui/components/__tests__/StageTimeline.test.tsx +5 -6
- package/src/ui/components/__tests__/TaskAnalysisDisplay.test.tsx +64 -0
- package/src/ui/components/types.ts +35 -15
- package/src/ui/dist/assets/{index--RH3sAt3.js → index-DN3-zvtP.js} +4324 -263
- package/src/ui/dist/assets/index-DN3-zvtP.js.map +1 -0
- package/src/ui/dist/assets/style-CtZBnjlR.css +2 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/pages/PipelineDetail.tsx +60 -4
- package/src/ui/pages/PromptPipelineDashboard.tsx +59 -7
- package/src/ui/pages/__tests__/PromptPipelineDashboard.test.tsx +114 -32
- package/src/ui/pages/__tests__/pages.test.tsx +236 -42
- package/src/ui/server/__tests__/concurrency-endpoint.test.ts +54 -0
- package/src/ui/server/__tests__/config-bridge-node.test.ts +34 -1
- package/src/ui/server/__tests__/config-bridge.test.ts +9 -1
- package/src/ui/server/__tests__/embedded-assets.test.ts +66 -0
- package/src/ui/server/__tests__/file-endpoints.test.ts +183 -5
- package/src/ui/server/__tests__/gate-endpoints.test.ts +55 -0
- package/src/ui/server/__tests__/index.test.ts +63 -0
- package/src/ui/server/__tests__/job-control-endpoints.test.ts +512 -2
- package/src/ui/server/__tests__/job-endpoints.test.ts +158 -2
- package/src/ui/server/__tests__/read-static-path-guard.test.ts +94 -0
- package/src/ui/server/__tests__/router-root-isolation.test.ts +204 -0
- package/src/ui/server/__tests__/router-threading.test.ts +148 -0
- package/src/ui/server/__tests__/router.test.ts +104 -0
- package/src/ui/server/__tests__/sse-broadcast.test.ts +44 -0
- package/src/ui/server/__tests__/sse-enhancer.test.ts +70 -0
- package/src/ui/server/config-bridge-node.ts +8 -26
- package/src/ui/server/config-bridge.ts +3 -4
- package/src/ui/server/embedded-assets-imports.d.ts +44 -0
- package/src/ui/server/embedded-assets.ts +13 -2
- package/src/ui/server/endpoints/__tests__/meta-endpoint.test.ts +1 -0
- package/src/ui/server/endpoints/__tests__/pipeline-analysis-endpoint.test.ts +167 -0
- package/src/ui/server/endpoints/__tests__/schema-file-endpoint.test.ts +104 -0
- package/src/ui/server/endpoints/__tests__/task-analysis-endpoint.test.ts +103 -0
- package/src/ui/server/endpoints/__tests__/upload-endpoints.test.ts +162 -96
- package/src/ui/server/endpoints/concurrency-endpoint.ts +2 -1
- package/src/ui/server/endpoints/create-pipeline-endpoint.ts +14 -29
- package/src/ui/server/endpoints/file-endpoints.ts +15 -10
- package/src/ui/server/endpoints/gate-endpoints.ts +2 -6
- package/src/ui/server/endpoints/job-control-endpoints.ts +129 -141
- package/src/ui/server/endpoints/job-endpoints.ts +7 -0
- package/src/ui/server/endpoints/meta-endpoint.ts +1 -1
- package/src/ui/server/endpoints/pipeline-analysis-endpoint.ts +99 -11
- package/src/ui/server/endpoints/schema-file-endpoint.ts +11 -3
- package/src/ui/server/endpoints/task-analysis-endpoint.ts +21 -5
- package/src/ui/server/endpoints/task-save-endpoint.ts +1 -2
- package/src/ui/server/endpoints/upload-endpoints.ts +5 -40
- package/src/ui/server/index.ts +19 -14
- package/src/ui/server/job-reader.ts +14 -2
- package/src/ui/server/router.ts +33 -10
- package/src/ui/server/sse-broadcast.ts +14 -3
- package/src/ui/server/sse-enhancer.ts +12 -2
- package/src/ui/state/__tests__/schema-loader.test.ts +11 -1
- package/src/ui/state/__tests__/snapshot.test.ts +120 -14
- package/src/ui/state/__tests__/types.test.ts +104 -5
- package/src/ui/state/snapshot.ts +2 -2
- package/src/ui/state/transformers/__tests__/list-transformer.test.ts +85 -2
- package/src/ui/state/transformers/__tests__/status-transformer.test.ts +250 -22
- package/src/ui/state/transformers/list-transformer.ts +13 -3
- package/src/ui/state/transformers/status-transformer.ts +36 -170
- package/src/ui/state/types.ts +15 -48
- package/src/utils/__tests__/path-containment.test.ts +160 -0
- package/src/{ui/server/utils → utils}/path-containment.ts +21 -0
- package/src/task-analysis/__tests__/enrichers-analysis-writer.test.ts +0 -86
- package/src/task-analysis/__tests__/enrichers-artifact-resolver.test.ts +0 -124
- package/src/task-analysis/__tests__/extractors-artifacts.test.ts +0 -133
- package/src/task-analysis/__tests__/extractors-llm-calls.test.ts +0 -46
- package/src/task-analysis/__tests__/extractors-stages.test.ts +0 -52
- package/src/task-analysis/__tests__/index.test.ts +0 -143
- package/src/task-analysis/__tests__/parser.test.ts +0 -41
- package/src/task-analysis/__tests__/utils-ast.test.ts +0 -82
- package/src/task-analysis/enrichers/analysis-writer.ts +0 -75
- package/src/task-analysis/enrichers/artifact-resolver.ts +0 -89
- package/src/task-analysis/extractors/artifacts.ts +0 -143
- package/src/task-analysis/extractors/llm-calls.ts +0 -117
- package/src/task-analysis/extractors/stages.ts +0 -45
- package/src/task-analysis/parser.ts +0 -20
- package/src/task-analysis/utils/ast.ts +0 -45
- package/src/ui/dist/assets/index--RH3sAt3.js.map +0 -1
- package/src/ui/dist/assets/style-CSSKuMOe.css +0 -2
- package/src/ui/embedded-assets.js +0 -12
- package/src/ui/server/__tests__/path-containment.test.ts +0 -54
package/src/cli/index.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { mkdir, access } from "node:fs/promises";
|
|
4
|
-
import {
|
|
4
|
+
import { join } from "node:path";
|
|
5
5
|
|
|
6
6
|
import { STAGE_NAMES, getStagePurpose, KEBAB_CASE_REGEX } from "./constants.ts";
|
|
7
7
|
import { buildReexecArgs, isCompiledBinary } from "./self-reexec.ts";
|
|
8
8
|
import { updatePipelineJson } from "./update-pipeline-json.ts";
|
|
9
9
|
import { analyzeTaskFile } from "./analyze-task.ts";
|
|
10
10
|
import { submitJobWithValidation, PipelineOrchestrator } from "../api/index.ts";
|
|
11
|
-
import
|
|
11
|
+
import { resolveWorkspaceRoot } from "../config/paths.ts";
|
|
12
|
+
import {
|
|
13
|
+
writeRegistry,
|
|
14
|
+
registerPipeline,
|
|
15
|
+
REGISTRY_VERSION,
|
|
16
|
+
} from "../config/pipeline-registry.ts";
|
|
12
17
|
import pkg from "../../package.json";
|
|
13
18
|
|
|
14
19
|
// ─── init ─────────────────────────────────────────────────────────────────────
|
|
@@ -21,10 +26,7 @@ export async function handleInit(root: string): Promise<void> {
|
|
|
21
26
|
await mkdir(join(root, "pipeline-data", sub), { recursive: true });
|
|
22
27
|
await Bun.write(join(root, "pipeline-data", sub, ".gitkeep"), "");
|
|
23
28
|
}
|
|
24
|
-
await
|
|
25
|
-
join(root, "registry.json"),
|
|
26
|
-
JSON.stringify({ pipelines: {} }, null, 2) + "\n"
|
|
27
|
-
);
|
|
29
|
+
await writeRegistry(root, { version: REGISTRY_VERSION, pipelines: {} });
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
// ─── start ────────────────────────────────────────────────────────────────────
|
|
@@ -43,18 +45,49 @@ export function buildUiCorsEnv(opts: StartOptions): Record<string, string> {
|
|
|
43
45
|
return env;
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Build the child env objects passed to the spawned `_start-ui` and
|
|
50
|
+
* `_start-orchestrator` processes. The parent `handleStart` resolves the
|
|
51
|
+
* workspace root once via `resolveWorkspaceRoot` and transports the absolute
|
|
52
|
+
* value to the children via `PO_ROOT`; the children perform their own boot
|
|
53
|
+
* resolution from that transported value.
|
|
54
|
+
*/
|
|
55
|
+
export function buildStartChildEnv(
|
|
56
|
+
absoluteRoot: string,
|
|
57
|
+
port: string,
|
|
58
|
+
opts?: StartOptions,
|
|
59
|
+
): { uiEnv: Record<string, string>; orchEnv: Record<string, string> } {
|
|
60
|
+
const uiEnv: Record<string, string> = Object.fromEntries(
|
|
61
|
+
Object.entries({ ...process.env, NODE_ENV: "production", PO_ROOT: absoluteRoot, PORT: port }).filter(
|
|
62
|
+
(entry): entry is [string, string] => entry[1] !== undefined,
|
|
63
|
+
),
|
|
64
|
+
);
|
|
65
|
+
delete uiEnv["PO_UI_PORT"];
|
|
66
|
+
|
|
67
|
+
const corsEnv = opts ? buildUiCorsEnv(opts) : {};
|
|
68
|
+
Object.assign(uiEnv, corsEnv);
|
|
69
|
+
|
|
70
|
+
const orchEnv: Record<string, string> = Object.fromEntries(
|
|
71
|
+
Object.entries({ ...process.env, NODE_ENV: "production", PO_ROOT: absoluteRoot }).filter(
|
|
72
|
+
(entry): entry is [string, string] => entry[1] !== undefined,
|
|
73
|
+
),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
return { uiEnv, orchEnv };
|
|
77
|
+
}
|
|
78
|
+
|
|
46
79
|
export async function handleStart(
|
|
47
80
|
root: string | undefined,
|
|
48
81
|
port: string,
|
|
49
82
|
opts?: StartOptions,
|
|
50
83
|
): Promise<void> {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
84
|
+
let absoluteRoot: string;
|
|
85
|
+
try {
|
|
86
|
+
absoluteRoot = resolveWorkspaceRoot(root);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
54
89
|
process.exit(1);
|
|
55
90
|
}
|
|
56
|
-
|
|
57
|
-
const absoluteRoot = resolve(rawRoot);
|
|
58
91
|
process.env["PO_ROOT"] = absoluteRoot;
|
|
59
92
|
|
|
60
93
|
if (!isCompiledBinary()) {
|
|
@@ -69,21 +102,7 @@ export async function handleStart(
|
|
|
69
102
|
}
|
|
70
103
|
}
|
|
71
104
|
|
|
72
|
-
const uiEnv
|
|
73
|
-
Object.entries({ ...process.env, NODE_ENV: "production", PO_ROOT: absoluteRoot, PORT: port }).filter(
|
|
74
|
-
(entry): entry is [string, string] => entry[1] !== undefined
|
|
75
|
-
)
|
|
76
|
-
);
|
|
77
|
-
delete uiEnv["PO_UI_PORT"];
|
|
78
|
-
|
|
79
|
-
const corsEnv = opts ? buildUiCorsEnv(opts) : {};
|
|
80
|
-
Object.assign(uiEnv, corsEnv);
|
|
81
|
-
|
|
82
|
-
const orchEnv: Record<string, string> = Object.fromEntries(
|
|
83
|
-
Object.entries({ ...process.env, NODE_ENV: "production", PO_ROOT: absoluteRoot }).filter(
|
|
84
|
-
(entry): entry is [string, string] => entry[1] !== undefined
|
|
85
|
-
)
|
|
86
|
-
);
|
|
105
|
+
const { uiEnv, orchEnv } = buildStartChildEnv(absoluteRoot, port, opts);
|
|
87
106
|
|
|
88
107
|
const uiReexec = buildReexecArgs(["_start-ui"]);
|
|
89
108
|
const orchReexec = buildReexecArgs(["_start-orchestrator"]);
|
|
@@ -182,7 +201,10 @@ export async function handleStart(
|
|
|
182
201
|
|
|
183
202
|
// ─── submit ───────────────────────────────────────────────────────────────────
|
|
184
203
|
|
|
185
|
-
export async function handleSubmit(
|
|
204
|
+
export async function handleSubmit(
|
|
205
|
+
seedFile: string,
|
|
206
|
+
opts: { root?: string } = {},
|
|
207
|
+
): Promise<void> {
|
|
186
208
|
let seedObject: unknown;
|
|
187
209
|
try {
|
|
188
210
|
const text = await Bun.file(seedFile).text();
|
|
@@ -192,8 +214,9 @@ export async function handleSubmit(seedFile: string): Promise<void> {
|
|
|
192
214
|
process.exit(1);
|
|
193
215
|
}
|
|
194
216
|
|
|
217
|
+
const root = opts.root ?? process.env["PO_ROOT"] ?? process.cwd();
|
|
195
218
|
try {
|
|
196
|
-
const result = await submitJobWithValidation({
|
|
219
|
+
const result = await submitJobWithValidation({ root, seedObject });
|
|
197
220
|
if (result.success) {
|
|
198
221
|
console.log(`Job submitted: ${result.jobId} (${result.jobName})`);
|
|
199
222
|
} else {
|
|
@@ -208,8 +231,12 @@ export async function handleSubmit(seedFile: string): Promise<void> {
|
|
|
208
231
|
|
|
209
232
|
// ─── status ───────────────────────────────────────────────────────────────────
|
|
210
233
|
|
|
211
|
-
export async function handleStatus(
|
|
212
|
-
|
|
234
|
+
export async function handleStatus(
|
|
235
|
+
jobName: string | undefined,
|
|
236
|
+
opts: { root?: string } = {},
|
|
237
|
+
): Promise<void> {
|
|
238
|
+
const root = opts.root ?? process.env["PO_ROOT"] ?? process.cwd();
|
|
239
|
+
const orchestrator = new PipelineOrchestrator({ autoStart: false, root });
|
|
213
240
|
if (jobName) {
|
|
214
241
|
const result = await orchestrator.getStatus(jobName);
|
|
215
242
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -245,25 +272,7 @@ export async function handleAddPipeline(slug: string, root: string): Promise<voi
|
|
|
245
272
|
);
|
|
246
273
|
await Bun.write(join(tasksDir, "index.ts"), "export default {};\n");
|
|
247
274
|
|
|
248
|
-
|
|
249
|
-
let registry: Registry = { pipelines: {} };
|
|
250
|
-
try {
|
|
251
|
-
const text = await Bun.file(registryPath).text();
|
|
252
|
-
registry = JSON.parse(text) as Registry;
|
|
253
|
-
} catch {
|
|
254
|
-
// fallback to empty registry
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const pipelinePath = join(pipelineDir, "pipeline.json");
|
|
258
|
-
const taskRegistryPath = join(tasksDir, "index.ts");
|
|
259
|
-
registry.pipelines[slug] = {
|
|
260
|
-
name: slug,
|
|
261
|
-
description: "New pipeline",
|
|
262
|
-
pipelinePath,
|
|
263
|
-
taskRegistryPath,
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
await Bun.write(registryPath, JSON.stringify(registry, null, 2) + "\n");
|
|
275
|
+
await registerPipeline(root, slug, { name: slug, description: "New pipeline" });
|
|
267
276
|
} catch (err) {
|
|
268
277
|
console.error(`Error creating pipeline: ${err instanceof Error ? err.message : String(err)}`);
|
|
269
278
|
process.exit(1);
|
|
@@ -393,21 +402,21 @@ export async function handleAnalyze(taskPath: string): Promise<void> {
|
|
|
393
402
|
|
|
394
403
|
// ─── Hidden: _start-ui ───────────────────────────────────────────────────────
|
|
395
404
|
|
|
396
|
-
async function handleStartUi(): Promise<void> {
|
|
405
|
+
export async function handleStartUi(): Promise<void> {
|
|
406
|
+
// PO_ROOT is the inter-process transport set by `handleStart`; the boot
|
|
407
|
+
// function (startServer) owns the runtime resolution.
|
|
397
408
|
const { startServer } = await import("../ui/server/index.ts");
|
|
398
409
|
await startServer({
|
|
399
|
-
dataDir: process.env["PO_ROOT"]
|
|
410
|
+
dataDir: process.env["PO_ROOT"],
|
|
400
411
|
port: parseInt(process.env["PORT"] ?? "4000", 10),
|
|
401
412
|
});
|
|
402
413
|
}
|
|
403
414
|
|
|
404
415
|
// ─── Hidden: _start-orchestrator ─────────────────────────────────────────────
|
|
405
416
|
|
|
406
|
-
async function handleStartOrchestrator(): Promise<void> {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
process.exit(1);
|
|
410
|
-
}
|
|
417
|
+
export async function handleStartOrchestrator(): Promise<void> {
|
|
418
|
+
// PO_ROOT is the inter-process transport set by `handleStart`; the boot
|
|
419
|
+
// function (startOrchestrator) owns the runtime resolution.
|
|
411
420
|
const { startOrchestrator } = await import("../core/orchestrator.ts");
|
|
412
421
|
const handle = await startOrchestrator({ dataDir: process.env["PO_ROOT"] });
|
|
413
422
|
process.on("SIGINT", () => {
|
|
@@ -420,7 +429,8 @@ async function handleStartOrchestrator(): Promise<void> {
|
|
|
420
429
|
|
|
421
430
|
// ─── Hidden: _run-job ─────────────────────────────────────────────────────────
|
|
422
431
|
|
|
423
|
-
async function handleRunJob(jobId: string): Promise<void> {
|
|
432
|
+
export async function handleRunJob(jobId: string): Promise<void> {
|
|
433
|
+
// Boot resolution happens inside runPipelineJob; this handler only forwards.
|
|
424
434
|
const { runPipelineJob } = await import("../core/pipeline-runner.ts");
|
|
425
435
|
await runPipelineJob(jobId);
|
|
426
436
|
}
|
|
@@ -456,15 +466,17 @@ program
|
|
|
456
466
|
program
|
|
457
467
|
.command("submit <seed-file>")
|
|
458
468
|
.description("Submit a job from a seed JSON file")
|
|
459
|
-
.
|
|
460
|
-
|
|
469
|
+
.option("--root <path>", "Workspace root")
|
|
470
|
+
.action(async (seedFile: string, opts: { root?: string }) => {
|
|
471
|
+
await handleSubmit(seedFile, opts);
|
|
461
472
|
});
|
|
462
473
|
|
|
463
474
|
program
|
|
464
475
|
.command("status [job-name]")
|
|
465
476
|
.description("Query job status")
|
|
466
|
-
.
|
|
467
|
-
|
|
477
|
+
.option("--root <path>", "Workspace root")
|
|
478
|
+
.action(async (jobName: string | undefined, opts: { root?: string }) => {
|
|
479
|
+
await handleStatus(jobName, opts);
|
|
468
480
|
});
|
|
469
481
|
|
|
470
482
|
program
|
package/src/cli/types.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/** Central index of all pipelines in the workspace. */
|
|
2
|
-
export interface Registry {
|
|
3
|
-
pipelines: Record<string, PipelineRegistryEntry>;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
/** A single pipeline's entry in the registry. */
|
|
7
|
-
export interface PipelineRegistryEntry {
|
|
8
|
-
name: string;
|
|
9
|
-
description: string;
|
|
10
|
-
pipelinePath: string;
|
|
11
|
-
taskRegistryPath: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
1
|
/** A pipeline's configuration file. */
|
|
15
2
|
export interface PipelineConfig {
|
|
16
3
|
name: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readJsonFile, writeJsonFile } from "../core/json-file.ts";
|
|
1
2
|
import type { PipelineConfig } from "./types.ts";
|
|
2
3
|
|
|
3
4
|
export async function updatePipelineJson(
|
|
@@ -7,19 +8,7 @@ export async function updatePipelineJson(
|
|
|
7
8
|
): Promise<void> {
|
|
8
9
|
const filePath = `${root}/pipeline-config/${pipelineSlug}/pipeline.json`;
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
const text = await Bun.file(filePath).text();
|
|
13
|
-
const parsed = JSON.parse(text) as PipelineConfig;
|
|
14
|
-
config = parsed;
|
|
15
|
-
} catch {
|
|
16
|
-
config = {
|
|
17
|
-
name: pipelineSlug,
|
|
18
|
-
version: "1.0.0",
|
|
19
|
-
description: "New pipeline",
|
|
20
|
-
tasks: [],
|
|
21
|
-
};
|
|
22
|
-
}
|
|
11
|
+
const config = await readJsonFile<PipelineConfig>(filePath);
|
|
23
12
|
|
|
24
13
|
if (!Array.isArray(config.tasks)) {
|
|
25
14
|
config.tasks = [];
|
|
@@ -29,5 +18,5 @@ export async function updatePipelineJson(
|
|
|
29
18
|
config.tasks.push(taskSlug);
|
|
30
19
|
}
|
|
31
20
|
|
|
32
|
-
await
|
|
21
|
+
await writeJsonFile(filePath, config);
|
|
33
22
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
2
|
+
import { resolve } from "node:path";
|
|
2
3
|
import {
|
|
4
|
+
resolveWorkspaceRoot,
|
|
3
5
|
resolvePipelinePaths,
|
|
4
6
|
getPendingSeedPath,
|
|
5
7
|
getCurrentSeedPath,
|
|
@@ -119,3 +121,46 @@ describe("getJobPipelinePath", () => {
|
|
|
119
121
|
);
|
|
120
122
|
});
|
|
121
123
|
});
|
|
124
|
+
|
|
125
|
+
describe("resolveWorkspaceRoot", () => {
|
|
126
|
+
const originalPoRoot = process.env["PO_ROOT"];
|
|
127
|
+
|
|
128
|
+
beforeEach(() => {
|
|
129
|
+
delete process.env["PO_ROOT"];
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
afterEach(() => {
|
|
133
|
+
if (originalPoRoot === undefined) {
|
|
134
|
+
delete process.env["PO_ROOT"];
|
|
135
|
+
} else {
|
|
136
|
+
process.env["PO_ROOT"] = originalPoRoot;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("returns resolve(explicit) when explicit is set", () => {
|
|
141
|
+
process.env["PO_ROOT"] = "/from-env";
|
|
142
|
+
expect(resolveWorkspaceRoot("/from-arg")).toBe("/from-arg");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("returns resolve(PO_ROOT) when only PO_ROOT is set", () => {
|
|
146
|
+
process.env["PO_ROOT"] = "/from-env";
|
|
147
|
+
expect(resolveWorkspaceRoot()).toBe(resolve("/from-env"));
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("explicit wins over PO_ROOT", () => {
|
|
151
|
+
process.env["PO_ROOT"] = "/from-env";
|
|
152
|
+
expect(resolveWorkspaceRoot("/from-arg")).not.toBe(resolve("/from-env"));
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("throws naming --root and PO_ROOT when neither is set", () => {
|
|
156
|
+
expect(() => resolveWorkspaceRoot()).toThrow(
|
|
157
|
+
"Workspace root is required: pass --root or set PO_ROOT",
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("returns an absolute path for a relative input", () => {
|
|
162
|
+
expect(resolveWorkspaceRoot("relative/root")).toBe(
|
|
163
|
+
resolve("relative/root"),
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
});
|