@ryanfw/prompt-orchestration-pipeline 1.3.3 → 1.3.5
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 +17 -4
- package/docs/pop-task-guide.md +8 -0
- package/package.json +5 -5
- package/src/api/__tests__/index.test.ts +144 -41
- package/src/api/index.ts +15 -83
- package/src/cli/__tests__/analyze-task.test.ts +39 -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__/config-schema.test.ts +42 -0
- package/src/config/__tests__/legacy-literal-guard.test.ts +156 -0
- package/src/config/__tests__/models.test.ts +139 -1
- 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/models.ts +121 -19
- 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/core/__tests__/agent-step.test.ts +167 -0
- package/src/core/__tests__/config.test.ts +517 -107
- package/src/core/__tests__/core-boot-resolution.test.ts +181 -0
- package/src/core/__tests__/job-concurrency.test.ts +200 -0
- package/src/core/__tests__/job-submission.test.ts +396 -0
- package/src/core/__tests__/job-view.test.ts +583 -3
- 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 +373 -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 +45 -0
- package/src/core/__tests__/seed-naming.test.ts +57 -0
- package/src/core/__tests__/single-derivation.test.ts +1 -1
- 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 +9 -2
- package/src/core/agent-types.ts +10 -4
- package/src/core/config.ts +127 -234
- package/src/core/control.ts +3 -0
- package/src/core/job-concurrency.ts +50 -19
- package/src/core/job-submission.ts +133 -0
- package/src/core/job-view.ts +183 -19
- 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 +124 -70
- package/src/core/pipeline-runner.ts +85 -53
- package/src/core/redact.ts +40 -0
- package/src/core/runner-liveness.ts +8 -4
- package/src/core/seed-naming.ts +9 -0
- package/src/core/status-writer.ts +3 -28
- 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__/dispatch.test.ts +130 -0
- package/src/llm/__tests__/index.test.ts +693 -43
- package/src/llm/__tests__/legacy-normalization.test.ts +109 -0
- package/src/llm/index.ts +405 -165
- 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 +108 -17
- 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 +60 -3
- 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 +128 -0
- package/src/providers/__tests__/zero-fill-guard.test.ts +62 -0
- package/src/providers/__tests__/zhipu.test.ts +49 -40
- 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 +22 -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 +36 -9
- package/src/providers/zhipu.ts +15 -46
- package/src/task-analysis/__tests__/analyzer.test.ts +0 -0
- package/src/task-analysis/__tests__/enrichers-schema-deducer.test.ts +69 -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 +85 -0
- package/src/task-analysis/enrichers/schema-deducer.ts +12 -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 +125 -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 +31 -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 +14 -20
- package/src/ui/components/DAGGrid.tsx +6 -1
- package/src/ui/components/JobDetail.tsx +12 -4
- package/src/ui/components/JobTable.tsx +13 -2
- 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 +83 -0
- 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 +33 -15
- package/src/ui/dist/assets/{index-L6cvsCAx.js → index-lBbVeNZC.js} +4304 -253
- package/src/ui/dist/assets/index-lBbVeNZC.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/Code.tsx +5 -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__/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__/http-api-contract.test.ts +838 -0
- package/src/ui/server/__tests__/index.test.ts +63 -0
- package/src/ui/server/__tests__/job-control-endpoints.test.ts +582 -21
- package/src/ui/server/__tests__/job-control-service.test.ts +282 -0
- package/src/ui/server/__tests__/job-endpoints.test.ts +43 -1
- package/src/ui/server/__tests__/job-process-registry.test.ts +151 -0
- package/src/ui/server/__tests__/job-repository.test.ts +142 -0
- 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 +39 -0
- package/src/ui/server/config-bridge-node.ts +8 -26
- package/src/ui/server/config-bridge.ts +9 -6
- 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__/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 +24 -12
- package/src/ui/server/endpoints/job-control-endpoints.ts +19 -714
- package/src/ui/server/endpoints/job-endpoints.ts +1 -0
- 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-control-service.ts +520 -0
- package/src/ui/server/job-process-registry.ts +250 -0
- package/src/ui/server/job-reader.ts +14 -2
- package/src/ui/server/job-repository.ts +316 -0
- 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 +70 -15
- package/src/ui/state/__tests__/types.test.ts +6 -0
- package/src/ui/state/snapshot.ts +1 -1
- package/src/ui/state/transformers/__tests__/list-transformer.test.ts +42 -0
- package/src/ui/state/transformers/__tests__/status-transformer.test.ts +45 -3
- package/src/ui/state/transformers/list-transformer.ts +6 -0
- package/src/ui/state/types.ts +6 -1
- 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-L6cvsCAx.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
|
@@ -1,7 +1,42 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, spyOn } from "bun:test";
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, spyOn, mock } from "bun:test";
|
|
2
2
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import type { ChatResponse, ProviderAvailability } from "../../providers/types.ts";
|
|
6
|
+
|
|
7
|
+
// Mock the gateway boundary so the analyzer produces a deterministic, valid
|
|
8
|
+
// result without a real LLM call. (See task-analysis/__tests__/analyzer.test.ts.)
|
|
9
|
+
const availability: ProviderAvailability = {
|
|
10
|
+
openai: false,
|
|
11
|
+
deepseek: true,
|
|
12
|
+
anthropic: false,
|
|
13
|
+
gemini: false,
|
|
14
|
+
zai: false,
|
|
15
|
+
moonshot: false,
|
|
16
|
+
alibaba: false,
|
|
17
|
+
opencode: false,
|
|
18
|
+
"claude-code": false,
|
|
19
|
+
mock: false,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const VALID_ANALYSIS = {
|
|
23
|
+
summary: "Ingests raw data and writes a processed artifact.",
|
|
24
|
+
stages: [{ name: "ingestion", purpose: "Reads raw input and normalizes it." }],
|
|
25
|
+
artifacts: {
|
|
26
|
+
reads: [{ fileName: "seed.json", role: "raw input" }],
|
|
27
|
+
writes: [{ fileName: "processed.json", role: "normalized output" }],
|
|
28
|
+
},
|
|
29
|
+
models: [{ provider: "deepseek", method: "chat", stage: "ingestion" }],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
mock.module("../../llm/index.ts", () => ({
|
|
33
|
+
chat: (): Promise<ChatResponse> =>
|
|
34
|
+
Promise.resolve({
|
|
35
|
+
content: VALID_ANALYSIS,
|
|
36
|
+
usage: { promptTokens: 1, completionTokens: 1, totalTokens: 2, source: "reported" },
|
|
37
|
+
}),
|
|
38
|
+
getAvailableProviders: () => availability,
|
|
39
|
+
}));
|
|
5
40
|
|
|
6
41
|
describe("analyzeTaskFile", () => {
|
|
7
42
|
let tmpDir: string;
|
|
@@ -22,11 +57,11 @@ describe("analyzeTaskFile", () => {
|
|
|
22
57
|
stdoutSpy.mockRestore();
|
|
23
58
|
});
|
|
24
59
|
|
|
25
|
-
it("
|
|
60
|
+
it("prints a status-tagged analysis as JSON for a sample file", async () => {
|
|
26
61
|
const taskFile = join(tmpDir, "task.ts");
|
|
27
62
|
await writeFile(
|
|
28
63
|
taskFile,
|
|
29
|
-
'export async function ingestion({ io, llm }) { await io.readArtifact("seed.json");
|
|
64
|
+
'export async function ingestion({ io, llm }) { await io.readArtifact("seed.json"); }',
|
|
30
65
|
);
|
|
31
66
|
|
|
32
67
|
const { analyzeTaskFile } = await import("../analyze-task.ts");
|
|
@@ -35,10 +70,7 @@ describe("analyzeTaskFile", () => {
|
|
|
35
70
|
expect(stdoutSpy).toHaveBeenCalledTimes(1);
|
|
36
71
|
const written = (stdoutSpy.mock.calls[0] as [string])[0];
|
|
37
72
|
const parsed = JSON.parse(written);
|
|
38
|
-
expect(parsed.
|
|
39
|
-
expect(parsed.stages).toEqual([{ name: "ingestion", order: 1, isAsync: true }]);
|
|
40
|
-
expect(parsed.artifacts.reads).toEqual([{ fileName: "seed.json", stage: "ingestion", required: true }]);
|
|
41
|
-
expect(parsed.models).toEqual([{ provider: "openai", method: "complete", stage: "ingestion" }]);
|
|
73
|
+
expect(parsed.analysisStatus).toBe("ok");
|
|
42
74
|
});
|
|
43
75
|
|
|
44
76
|
it("calls process.exit(1) for a non-existent file", async () => {
|
|
@@ -1,14 +1,44 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach, type MockInstance } from "vitest";
|
|
2
|
+
import { mock } from "bun:test";
|
|
2
3
|
import { mkdtemp, rm, mkdir, readdir } from "node:fs/promises";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { tmpdir } from "node:os";
|
|
5
6
|
import { access } from "node:fs/promises";
|
|
6
7
|
|
|
8
|
+
// Module mocks for the hidden-subcommand tests. These are evaluated when
|
|
9
|
+
// the dynamic imports inside the hidden handlers first run. The CLI file
|
|
10
|
+
// statically imports neither target, so the mocks are inert for the
|
|
11
|
+
// existing tests. Note: this file uses bun:test's mock.module (not
|
|
12
|
+
// vi.mock) because vitest's mock-hoist does not intercept dynamic
|
|
13
|
+
// imports under the bun runtime. scripts/test.sh isolates this file
|
|
14
|
+
// because of the mock.module literal token, which is the intended seam.
|
|
15
|
+
const mockStartServer = vi.fn();
|
|
16
|
+
const mockStartOrchestrator = vi.fn();
|
|
17
|
+
const mockRunPipelineJob = vi.fn();
|
|
18
|
+
|
|
19
|
+
mock.module("../../ui/server/index.ts", () => ({
|
|
20
|
+
startServer: mockStartServer,
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
mock.module("../../core/orchestrator.ts", () => ({
|
|
24
|
+
startOrchestrator: mockStartOrchestrator,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
mock.module("../../core/pipeline-runner.ts", () => ({
|
|
28
|
+
runPipelineJob: mockRunPipelineJob,
|
|
29
|
+
}));
|
|
30
|
+
|
|
7
31
|
import {
|
|
8
32
|
handleInit,
|
|
9
33
|
handleAddPipeline,
|
|
10
34
|
handleAddPipelineTask,
|
|
11
35
|
handleSubmit,
|
|
36
|
+
handleStart,
|
|
37
|
+
handleStartUi,
|
|
38
|
+
handleStartOrchestrator,
|
|
39
|
+
handleRunJob,
|
|
40
|
+
handleStatus,
|
|
41
|
+
buildStartChildEnv,
|
|
12
42
|
parseTaskIndex,
|
|
13
43
|
serializeTaskIndex,
|
|
14
44
|
buildUiCorsEnv,
|
|
@@ -66,14 +96,8 @@ describe("handleInit", () => {
|
|
|
66
96
|
|
|
67
97
|
it("writes registry.json with empty pipelines", async () => {
|
|
68
98
|
await handleInit(tmpDir);
|
|
69
|
-
const reg = await readJson(join(tmpDir, "registry.json"));
|
|
70
|
-
expect(reg).toEqual({ pipelines: {} });
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("writes registry.json with trailing newline", async () => {
|
|
74
|
-
await handleInit(tmpDir);
|
|
75
|
-
const text = await Bun.file(join(tmpDir, "registry.json")).text();
|
|
76
|
-
expect(text.endsWith("\n")).toBe(true);
|
|
99
|
+
const reg = await readJson(join(tmpDir, "pipeline-config", "registry.json"));
|
|
100
|
+
expect(reg).toEqual({ version: 1, pipelines: {} });
|
|
77
101
|
});
|
|
78
102
|
|
|
79
103
|
it("is idempotent — can be re-run without error", async () => {
|
|
@@ -130,21 +154,13 @@ describe("handleAddPipeline", () => {
|
|
|
130
154
|
|
|
131
155
|
it("adds entry to registry.json", async () => {
|
|
132
156
|
await handleAddPipeline("my-pipeline", tmpDir);
|
|
133
|
-
const reg = await readJson(join(tmpDir, "registry.json")) as { pipelines: Record<string, unknown> };
|
|
157
|
+
const reg = await readJson(join(tmpDir, "pipeline-config", "registry.json")) as { pipelines: Record<string, unknown> };
|
|
134
158
|
expect(reg.pipelines["my-pipeline"]).toBeDefined();
|
|
135
159
|
const entry = reg.pipelines["my-pipeline"] as Record<string, unknown>;
|
|
136
160
|
expect(entry["name"]).toBe("my-pipeline");
|
|
137
161
|
expect(entry["description"]).toBe("New pipeline");
|
|
138
162
|
});
|
|
139
163
|
|
|
140
|
-
it("stores .ts paths in registry entry", async () => {
|
|
141
|
-
await handleAddPipeline("my-pipeline", tmpDir);
|
|
142
|
-
const reg = await readJson(join(tmpDir, "registry.json")) as { pipelines: Record<string, unknown> };
|
|
143
|
-
const entry = reg.pipelines["my-pipeline"] as Record<string, string>;
|
|
144
|
-
expect(entry["taskRegistryPath"]).toMatch(/index\.ts$/);
|
|
145
|
-
expect(entry["pipelinePath"]).toMatch(/pipeline\.json$/);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
164
|
it("exits with 1 on invalid slug", async () => {
|
|
149
165
|
await expect(handleAddPipeline("INVALID", tmpDir)).rejects.toThrow("process.exit(1)");
|
|
150
166
|
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
@@ -329,8 +345,11 @@ describe("handleSubmit", () => {
|
|
|
329
345
|
await Bun.write(
|
|
330
346
|
join(tmpDir, "pipeline-config", "registry.json"),
|
|
331
347
|
JSON.stringify({
|
|
348
|
+
version: 1,
|
|
332
349
|
pipelines: {
|
|
333
350
|
"test-pipeline": {
|
|
351
|
+
name: "Test Pipeline",
|
|
352
|
+
description: "test pipeline",
|
|
334
353
|
configDir,
|
|
335
354
|
tasksDir: join(configDir, "tasks"),
|
|
336
355
|
},
|
|
@@ -360,6 +379,190 @@ describe("handleSubmit", () => {
|
|
|
360
379
|
});
|
|
361
380
|
});
|
|
362
381
|
|
|
382
|
+
// ─── submit --root precedence ─────────────────────────────────────────────────
|
|
383
|
+
|
|
384
|
+
describe("submit --root precedence", () => {
|
|
385
|
+
let rootFlag: string;
|
|
386
|
+
let rootEnv: string;
|
|
387
|
+
let rootCwd: string;
|
|
388
|
+
let exitSpy: MockInstance;
|
|
389
|
+
let cwdSpy: MockInstance;
|
|
390
|
+
const originalPoRoot = process.env["PO_ROOT"];
|
|
391
|
+
|
|
392
|
+
async function scaffoldWorkspace(dir: string): Promise<string> {
|
|
393
|
+
const configDir = join(dir, "pipeline-config", "test-pipeline");
|
|
394
|
+
await mkdir(configDir, { recursive: true });
|
|
395
|
+
await mkdir(join(dir, "pipeline-data", "pending"), { recursive: true });
|
|
396
|
+
await Bun.write(
|
|
397
|
+
join(dir, "pipeline-config", "registry.json"),
|
|
398
|
+
JSON.stringify({
|
|
399
|
+
version: 1,
|
|
400
|
+
pipelines: {
|
|
401
|
+
"test-pipeline": {
|
|
402
|
+
name: "Test Pipeline",
|
|
403
|
+
description: "test pipeline",
|
|
404
|
+
configDir,
|
|
405
|
+
tasksDir: join(configDir, "tasks"),
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
}),
|
|
409
|
+
);
|
|
410
|
+
await Bun.write(
|
|
411
|
+
join(configDir, "pipeline.json"),
|
|
412
|
+
JSON.stringify({ name: "test-pipeline", tasks: [] }),
|
|
413
|
+
);
|
|
414
|
+
const seedPath = join(dir, "seed.json");
|
|
415
|
+
await Bun.write(seedPath, JSON.stringify({ pipeline: "test-pipeline" }));
|
|
416
|
+
return seedPath;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
async function pendingSeeds(dir: string): Promise<string[]> {
|
|
420
|
+
const entries = await readdir(join(dir, "pipeline-data", "pending"));
|
|
421
|
+
return entries.filter((f) => f.endsWith("-seed.json"));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
beforeEach(async () => {
|
|
425
|
+
rootFlag = await mkdtemp(join(tmpdir(), "pop-submit-flag-"));
|
|
426
|
+
rootEnv = await mkdtemp(join(tmpdir(), "pop-submit-env-"));
|
|
427
|
+
rootCwd = await mkdtemp(join(tmpdir(), "pop-submit-cwd-"));
|
|
428
|
+
delete process.env["PO_ROOT"];
|
|
429
|
+
exitSpy = vi.spyOn(process, "exit").mockImplementation(
|
|
430
|
+
(_code?: string | number | null | undefined) => {
|
|
431
|
+
throw new Error(`process.exit(${_code})`);
|
|
432
|
+
}
|
|
433
|
+
);
|
|
434
|
+
cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(rootCwd);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
afterEach(async () => {
|
|
438
|
+
await rm(rootFlag, { recursive: true, force: true });
|
|
439
|
+
await rm(rootEnv, { recursive: true, force: true });
|
|
440
|
+
await rm(rootCwd, { recursive: true, force: true });
|
|
441
|
+
exitSpy.mockRestore();
|
|
442
|
+
cwdSpy.mockRestore();
|
|
443
|
+
if (originalPoRoot === undefined) {
|
|
444
|
+
delete process.env["PO_ROOT"];
|
|
445
|
+
} else {
|
|
446
|
+
process.env["PO_ROOT"] = originalPoRoot;
|
|
447
|
+
}
|
|
448
|
+
vi.clearAllMocks();
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it("writes the pending seed under --root when --root, PO_ROOT, and cwd all differ (AC-2 precedence)", async () => {
|
|
452
|
+
const flagSeed = await scaffoldWorkspace(rootFlag);
|
|
453
|
+
await scaffoldWorkspace(rootEnv);
|
|
454
|
+
await scaffoldWorkspace(rootCwd);
|
|
455
|
+
|
|
456
|
+
process.env["PO_ROOT"] = rootEnv;
|
|
457
|
+
|
|
458
|
+
await handleSubmit(flagSeed, { root: rootFlag });
|
|
459
|
+
|
|
460
|
+
expect(exitSpy).not.toHaveBeenCalled();
|
|
461
|
+
expect(await pendingSeeds(rootFlag)).toHaveLength(1);
|
|
462
|
+
expect(await pendingSeeds(rootEnv)).toHaveLength(0);
|
|
463
|
+
expect(await pendingSeeds(rootCwd)).toHaveLength(0);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it("writes the pending seed under PO_ROOT when PO_ROOT and cwd differ and --root is absent (AC-3 headline regression)", async () => {
|
|
467
|
+
const cwdSeed = await scaffoldWorkspace(rootCwd);
|
|
468
|
+
await scaffoldWorkspace(rootEnv);
|
|
469
|
+
|
|
470
|
+
process.env["PO_ROOT"] = rootEnv;
|
|
471
|
+
|
|
472
|
+
await handleSubmit(cwdSeed);
|
|
473
|
+
|
|
474
|
+
expect(exitSpy).not.toHaveBeenCalled();
|
|
475
|
+
expect(await pendingSeeds(rootEnv)).toHaveLength(1);
|
|
476
|
+
expect(await pendingSeeds(rootCwd)).toHaveLength(0);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it("writes the pending seed under --root when --root is set without PO_ROOT (AC-3 second half)", async () => {
|
|
480
|
+
const flagSeed = await scaffoldWorkspace(rootFlag);
|
|
481
|
+
await scaffoldWorkspace(rootCwd);
|
|
482
|
+
|
|
483
|
+
await handleSubmit(flagSeed, { root: rootFlag });
|
|
484
|
+
|
|
485
|
+
expect(exitSpy).not.toHaveBeenCalled();
|
|
486
|
+
expect(await pendingSeeds(rootFlag)).toHaveLength(1);
|
|
487
|
+
expect(await pendingSeeds(rootCwd)).toHaveLength(0);
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// ─── status --root precedence ─────────────────────────────────────────────────
|
|
492
|
+
|
|
493
|
+
describe("status --root precedence", () => {
|
|
494
|
+
let rootFlag: string;
|
|
495
|
+
let rootEnv: string;
|
|
496
|
+
let rootCwd: string;
|
|
497
|
+
let cwdSpy: MockInstance;
|
|
498
|
+
let tableSpy: MockInstance;
|
|
499
|
+
const originalPoRoot = process.env["PO_ROOT"];
|
|
500
|
+
|
|
501
|
+
async function scaffoldPendingSeed(dir: string, jobId: string): Promise<void> {
|
|
502
|
+
await mkdir(join(dir, "pipeline-data", "pending"), { recursive: true });
|
|
503
|
+
await Bun.write(
|
|
504
|
+
join(dir, "pipeline-data", "pending", `${jobId}-seed.json`),
|
|
505
|
+
JSON.stringify({ name: jobId, pipeline: "test-pipeline" }),
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function listedJobIds(): string[] {
|
|
510
|
+
const rows = tableSpy.mock.calls[0]?.[0] as Array<{ jobId?: string }> | undefined;
|
|
511
|
+
return rows?.map((row) => row.jobId).filter((jobId): jobId is string => typeof jobId === "string") ?? [];
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
beforeEach(async () => {
|
|
515
|
+
rootFlag = await mkdtemp(join(tmpdir(), "pop-status-flag-"));
|
|
516
|
+
rootEnv = await mkdtemp(join(tmpdir(), "pop-status-env-"));
|
|
517
|
+
rootCwd = await mkdtemp(join(tmpdir(), "pop-status-cwd-"));
|
|
518
|
+
delete process.env["PO_ROOT"];
|
|
519
|
+
cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(rootCwd);
|
|
520
|
+
tableSpy = vi.spyOn(console, "table").mockImplementation(() => undefined);
|
|
521
|
+
await scaffoldPendingSeed(rootFlag, "flag-job");
|
|
522
|
+
await scaffoldPendingSeed(rootEnv, "env-job");
|
|
523
|
+
await scaffoldPendingSeed(rootCwd, "cwd-job");
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
afterEach(async () => {
|
|
527
|
+
await rm(rootFlag, { recursive: true, force: true });
|
|
528
|
+
await rm(rootEnv, { recursive: true, force: true });
|
|
529
|
+
await rm(rootCwd, { recursive: true, force: true });
|
|
530
|
+
cwdSpy.mockRestore();
|
|
531
|
+
tableSpy.mockRestore();
|
|
532
|
+
if (originalPoRoot === undefined) {
|
|
533
|
+
delete process.env["PO_ROOT"];
|
|
534
|
+
} else {
|
|
535
|
+
process.env["PO_ROOT"] = originalPoRoot;
|
|
536
|
+
}
|
|
537
|
+
vi.clearAllMocks();
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it("lists jobs under --root when --root, PO_ROOT, and cwd all differ", async () => {
|
|
541
|
+
process.env["PO_ROOT"] = rootEnv;
|
|
542
|
+
|
|
543
|
+
await handleStatus(undefined, { root: rootFlag });
|
|
544
|
+
|
|
545
|
+
expect(listedJobIds()).toEqual(["flag-job"]);
|
|
546
|
+
expect(process.env["PO_ROOT"]).toBe(rootEnv);
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it("lists jobs under PO_ROOT when --root is absent", async () => {
|
|
550
|
+
process.env["PO_ROOT"] = rootEnv;
|
|
551
|
+
|
|
552
|
+
await handleStatus(undefined);
|
|
553
|
+
|
|
554
|
+
expect(listedJobIds()).toEqual(["env-job"]);
|
|
555
|
+
expect(process.env["PO_ROOT"]).toBe(rootEnv);
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it("lists jobs under cwd when --root and PO_ROOT are absent", async () => {
|
|
559
|
+
await handleStatus(undefined);
|
|
560
|
+
|
|
561
|
+
expect(listedJobIds()).toEqual(["cwd-job"]);
|
|
562
|
+
expect(process.env["PO_ROOT"]).toBeUndefined();
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
|
|
363
566
|
// ─── parseTaskIndex ───────────────────────────────────────────────────────────
|
|
364
567
|
|
|
365
568
|
describe("parseTaskIndex", () => {
|
|
@@ -422,6 +625,123 @@ describe("serializeTaskIndex", () => {
|
|
|
422
625
|
});
|
|
423
626
|
});
|
|
424
627
|
|
|
628
|
+
// ─── start --root handoff (AC-7) ────────────────────────────────────────────
|
|
629
|
+
|
|
630
|
+
describe("start --root handoff", () => {
|
|
631
|
+
const originalPoRoot = process.env["PO_ROOT"];
|
|
632
|
+
|
|
633
|
+
beforeEach(() => {
|
|
634
|
+
delete process.env["PO_ROOT"];
|
|
635
|
+
mockStartServer.mockReset();
|
|
636
|
+
mockStartOrchestrator.mockReset();
|
|
637
|
+
mockStartOrchestrator.mockResolvedValue({ stop: vi.fn() });
|
|
638
|
+
mockRunPipelineJob.mockReset();
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
afterEach(() => {
|
|
642
|
+
if (originalPoRoot === undefined) {
|
|
643
|
+
delete process.env["PO_ROOT"];
|
|
644
|
+
} else {
|
|
645
|
+
process.env["PO_ROOT"] = originalPoRoot;
|
|
646
|
+
}
|
|
647
|
+
process.removeAllListeners("SIGINT");
|
|
648
|
+
process.removeAllListeners("SIGTERM");
|
|
649
|
+
vi.restoreAllMocks();
|
|
650
|
+
vi.clearAllMocks();
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
describe("buildStartChildEnv", () => {
|
|
654
|
+
it("forwards absoluteRoot to PO_ROOT in both child envs and sets PORT/NODE_ENV in the UI env", () => {
|
|
655
|
+
const { uiEnv, orchEnv } = buildStartChildEnv("/A", "4000");
|
|
656
|
+
|
|
657
|
+
expect(uiEnv["PO_ROOT"]).toBe("/A");
|
|
658
|
+
expect(uiEnv["PORT"]).toBe("4000");
|
|
659
|
+
expect(uiEnv["NODE_ENV"]).toBe("production");
|
|
660
|
+
expect(orchEnv["PO_ROOT"]).toBe("/A");
|
|
661
|
+
expect(orchEnv["NODE_ENV"]).toBe("production");
|
|
662
|
+
expect(orchEnv["PORT"]).toBeUndefined();
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it("layers CORS options onto the UI env alongside PO_ROOT/PORT", () => {
|
|
666
|
+
const { uiEnv } = buildStartChildEnv("/A", "4000", {
|
|
667
|
+
port: "4000",
|
|
668
|
+
corsOrigins: "https://app.example",
|
|
669
|
+
corsAllowNullOrigin: true,
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
expect(uiEnv["PO_ROOT"]).toBe("/A");
|
|
673
|
+
expect(uiEnv["PORT"]).toBe("4000");
|
|
674
|
+
expect(uiEnv["PO_CORS_ORIGINS"]).toBe("https://app.example");
|
|
675
|
+
expect(uiEnv["PO_CORS_ALLOW_NULL_ORIGIN"]).toBe("1");
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
describe("handleStart error path", () => {
|
|
680
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
681
|
+
let exitSpy: MockInstance<any>;
|
|
682
|
+
|
|
683
|
+
beforeEach(() => {
|
|
684
|
+
exitSpy = vi.spyOn(process, "exit").mockImplementation(
|
|
685
|
+
(_code?: string | number | null | undefined) => {
|
|
686
|
+
throw new Error(`process.exit(${_code})`);
|
|
687
|
+
},
|
|
688
|
+
);
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
it("exits 1 with the resolver message when --root and PO_ROOT are both absent", async () => {
|
|
692
|
+
await expect(handleStart(undefined, "4000")).rejects.toThrow("process.exit(1)");
|
|
693
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
694
|
+
});
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
describe("hidden subcommand boot delegation", () => {
|
|
698
|
+
it("handleStartUi calls startServer with dataDir: undefined when PO_ROOT is unset (no cwd fallback)", async () => {
|
|
699
|
+
await handleStartUi();
|
|
700
|
+
|
|
701
|
+
expect(mockStartServer).toHaveBeenCalledTimes(1);
|
|
702
|
+
expect(mockStartServer).toHaveBeenCalledWith({
|
|
703
|
+
dataDir: undefined,
|
|
704
|
+
port: 4000,
|
|
705
|
+
});
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it("handleStartUi calls startServer with the transported PO_ROOT when set", async () => {
|
|
709
|
+
process.env["PO_ROOT"] = "/transported";
|
|
710
|
+
|
|
711
|
+
await handleStartUi();
|
|
712
|
+
|
|
713
|
+
expect(mockStartServer).toHaveBeenCalledTimes(1);
|
|
714
|
+
expect(mockStartServer).toHaveBeenCalledWith({
|
|
715
|
+
dataDir: "/transported",
|
|
716
|
+
port: 4000,
|
|
717
|
+
});
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it("handleStartOrchestrator calls startOrchestrator with dataDir: undefined when PO_ROOT is unset (no manual guard)", async () => {
|
|
721
|
+
await handleStartOrchestrator();
|
|
722
|
+
|
|
723
|
+
expect(mockStartOrchestrator).toHaveBeenCalledTimes(1);
|
|
724
|
+
expect(mockStartOrchestrator).toHaveBeenCalledWith({ dataDir: undefined });
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it("handleStartOrchestrator calls startOrchestrator with the transported PO_ROOT when set", async () => {
|
|
728
|
+
process.env["PO_ROOT"] = "/transported";
|
|
729
|
+
|
|
730
|
+
await handleStartOrchestrator();
|
|
731
|
+
|
|
732
|
+
expect(mockStartOrchestrator).toHaveBeenCalledTimes(1);
|
|
733
|
+
expect(mockStartOrchestrator).toHaveBeenCalledWith({ dataDir: "/transported" });
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
it("handleRunJob calls runPipelineJob(jobId) without reading env", async () => {
|
|
737
|
+
await handleRunJob("job-123");
|
|
738
|
+
|
|
739
|
+
expect(mockRunPipelineJob).toHaveBeenCalledTimes(1);
|
|
740
|
+
expect(mockRunPipelineJob).toHaveBeenCalledWith("job-123");
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
});
|
|
744
|
+
|
|
425
745
|
// ─── buildUiCorsEnv ─────────────────────────────────────────────────────────
|
|
426
746
|
|
|
427
747
|
describe("buildUiCorsEnv", () => {
|
|
@@ -1,29 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
2
|
import type {
|
|
3
|
-
Registry,
|
|
4
|
-
PipelineRegistryEntry,
|
|
5
3
|
PipelineConfig,
|
|
6
4
|
ReexecArgs,
|
|
7
5
|
TaskIndex,
|
|
8
6
|
} from "../types.js";
|
|
9
7
|
|
|
10
8
|
describe("CLI types", () => {
|
|
11
|
-
it("Registry with empty pipelines is valid", () => {
|
|
12
|
-
const registry: Registry = { pipelines: {} };
|
|
13
|
-
expect(registry.pipelines).toEqual({});
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("Registry with pipeline entries is valid", () => {
|
|
17
|
-
const entry: PipelineRegistryEntry = {
|
|
18
|
-
name: "my-pipeline",
|
|
19
|
-
description: "A test pipeline",
|
|
20
|
-
pipelinePath: "./pipeline-config/my-pipeline/pipeline.json",
|
|
21
|
-
taskRegistryPath: "./pipeline-config/my-pipeline/tasks/index.ts",
|
|
22
|
-
};
|
|
23
|
-
const registry: Registry = { pipelines: { "my-pipeline": entry } };
|
|
24
|
-
expect(registry.pipelines["my-pipeline"]).toBe(entry);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
9
|
it("PipelineConfig with all required fields is valid", () => {
|
|
28
10
|
const config: PipelineConfig = {
|
|
29
11
|
name: "my-pipeline",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
2
|
-
import { mkdtemp, mkdir, rm } from "node:fs/promises";
|
|
2
|
+
import { mkdtemp, mkdir, readFile, rm } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { JsonFileError } from "../../core/json-file.ts";
|
|
5
6
|
import { updatePipelineJson } from "../update-pipeline-json.ts";
|
|
6
7
|
|
|
7
8
|
describe("updatePipelineJson", () => {
|
|
@@ -18,7 +19,7 @@ describe("updatePipelineJson", () => {
|
|
|
18
19
|
await rm(tmpDir, { recursive: true, force: true });
|
|
19
20
|
});
|
|
20
21
|
|
|
21
|
-
it("appends a task slug
|
|
22
|
+
it("appends a task slug, preserving name/version/description and existing tasks", async () => {
|
|
22
23
|
const filePath = join(pipelineDir, "pipeline.json");
|
|
23
24
|
await Bun.write(
|
|
24
25
|
filePath,
|
|
@@ -32,6 +33,9 @@ describe("updatePipelineJson", () => {
|
|
|
32
33
|
await updatePipelineJson(tmpDir, "test-pipeline", "b");
|
|
33
34
|
|
|
34
35
|
const result = JSON.parse(await Bun.file(filePath).text());
|
|
36
|
+
expect(result.name).toBe("test");
|
|
37
|
+
expect(result.version).toBe("1.0.0");
|
|
38
|
+
expect(result.description).toBe("Test");
|
|
35
39
|
expect(result.tasks).toEqual(["a", "b"]);
|
|
36
40
|
});
|
|
37
41
|
|
|
@@ -53,15 +57,21 @@ describe("updatePipelineJson", () => {
|
|
|
53
57
|
expect(result.tasks).toEqual(["a", "b"]);
|
|
54
58
|
});
|
|
55
59
|
|
|
56
|
-
it("
|
|
60
|
+
it("throws and leaves the file unchanged when pipeline.json is malformed", async () => {
|
|
57
61
|
const filePath = join(pipelineDir, "pipeline.json");
|
|
62
|
+
const original = "{ not valid json ";
|
|
63
|
+
await Bun.write(filePath, original);
|
|
58
64
|
|
|
59
|
-
await updatePipelineJson(tmpDir, "test-pipeline", "b")
|
|
65
|
+
await expect(updatePipelineJson(tmpDir, "test-pipeline", "b")).rejects.toBeInstanceOf(
|
|
66
|
+
JsonFileError
|
|
67
|
+
);
|
|
60
68
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
expect(
|
|
69
|
+
expect(await readFile(filePath, "utf8")).toBe(original);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("throws when pipeline.json is missing", async () => {
|
|
73
|
+
await expect(updatePipelineJson(tmpDir, "test-pipeline", "b")).rejects.toBeInstanceOf(
|
|
74
|
+
JsonFileError
|
|
75
|
+
);
|
|
66
76
|
});
|
|
67
77
|
});
|
package/src/cli/analyze-task.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { analyzeTask } from "../task-analysis/
|
|
2
|
+
import { analyzeTask } from "../task-analysis/analyzer.ts";
|
|
3
3
|
|
|
4
4
|
export async function analyzeTaskFile(taskPath: string): Promise<void> {
|
|
5
5
|
const absolutePath = path.resolve(taskPath);
|
|
@@ -17,17 +17,6 @@ export async function analyzeTaskFile(taskPath: string): Promise<void> {
|
|
|
17
17
|
throw err;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
23
|
-
} catch (err) {
|
|
24
|
-
const isDev =
|
|
25
|
-
process.env.NODE_ENV === "development" || process.env.DEBUG_TASK_ANALYSIS === "1";
|
|
26
|
-
if (isDev && err instanceof Error && err.stack) {
|
|
27
|
-
console.error(err.stack);
|
|
28
|
-
} else {
|
|
29
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
30
|
-
}
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
20
|
+
const result = await analyzeTask(code);
|
|
21
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
33
22
|
}
|