@tagma/sdk 0.4.2 → 0.4.3
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/LICENSE +21 -0
- package/dist/adapters/stdin-approval.d.ts +6 -0
- package/dist/adapters/stdin-approval.d.ts.map +1 -0
- package/dist/adapters/stdin-approval.js +90 -0
- package/dist/adapters/stdin-approval.js.map +1 -0
- package/dist/adapters/websocket-approval.d.ts +28 -0
- package/dist/adapters/websocket-approval.d.ts.map +1 -0
- package/dist/adapters/websocket-approval.js +145 -0
- package/dist/adapters/websocket-approval.js.map +1 -0
- package/dist/approval.d.ts +13 -0
- package/dist/approval.d.ts.map +1 -0
- package/dist/approval.js +91 -0
- package/dist/approval.js.map +1 -0
- package/dist/bootstrap.d.ts +2 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +30 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/completions/exit-code.d.ts +3 -0
- package/dist/completions/exit-code.d.ts.map +1 -0
- package/dist/completions/exit-code.js +25 -0
- package/dist/completions/exit-code.js.map +1 -0
- package/dist/completions/file-exists.d.ts +3 -0
- package/dist/completions/file-exists.d.ts.map +1 -0
- package/dist/completions/file-exists.js +58 -0
- package/dist/completions/file-exists.js.map +1 -0
- package/dist/completions/output-check.d.ts +3 -0
- package/dist/completions/output-check.d.ts.map +1 -0
- package/dist/completions/output-check.js +81 -0
- package/dist/completions/output-check.js.map +1 -0
- package/dist/config-ops.d.ts +55 -0
- package/dist/config-ops.d.ts.map +1 -0
- package/dist/config-ops.js +258 -0
- package/dist/config-ops.js.map +1 -0
- package/dist/dag.d.ts +46 -0
- package/dist/dag.d.ts.map +1 -0
- package/dist/dag.js +193 -0
- package/dist/dag.js.map +1 -0
- package/dist/drivers/claude-code.d.ts +3 -0
- package/dist/drivers/claude-code.d.ts.map +1 -0
- package/dist/drivers/claude-code.js +210 -0
- package/dist/drivers/claude-code.js.map +1 -0
- package/dist/engine.d.ts +89 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +815 -0
- package/dist/engine.js.map +1 -0
- package/dist/hooks.d.ts +73 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +103 -0
- package/dist/hooks.js.map +1 -0
- package/dist/logger.d.ts +61 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +151 -0
- package/dist/logger.js.map +1 -0
- package/dist/middlewares/static-context.d.ts +3 -0
- package/dist/middlewares/static-context.d.ts.map +1 -0
- package/dist/middlewares/static-context.js +36 -0
- package/dist/middlewares/static-context.js.map +1 -0
- package/dist/pipeline-runner.d.ts +50 -0
- package/dist/pipeline-runner.d.ts.map +1 -0
- package/dist/pipeline-runner.js +139 -0
- package/dist/pipeline-runner.js.map +1 -0
- package/dist/registry.d.ts +43 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +182 -0
- package/dist/registry.js.map +1 -0
- package/dist/runner.d.ts +19 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +364 -0
- package/dist/runner.js.map +1 -0
- package/dist/schema.d.ts +27 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +373 -0
- package/dist/schema.js.map +1 -0
- package/dist/sdk.d.ts +27 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +33 -0
- package/dist/sdk.js.map +1 -0
- package/dist/templates.d.ts +20 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +93 -0
- package/dist/templates.js.map +1 -0
- package/dist/triggers/file.d.ts +3 -0
- package/dist/triggers/file.d.ts.map +1 -0
- package/dist/triggers/file.js +123 -0
- package/dist/triggers/file.js.map +1 -0
- package/dist/triggers/manual.d.ts +3 -0
- package/dist/triggers/manual.d.ts.map +1 -0
- package/dist/triggers/manual.js +73 -0
- package/dist/triggers/manual.js.map +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +14 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +138 -0
- package/dist/utils.js.map +1 -0
- package/dist/validate-raw.d.ts +26 -0
- package/dist/validate-raw.d.ts.map +1 -0
- package/dist/validate-raw.js +260 -0
- package/dist/validate-raw.js.map +1 -0
- package/package.json +26 -17
- package/src/approval.ts +5 -3
- package/src/registry.ts +214 -214
- package/scripts/preinstall.js +0 -38
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { shellArgs, parseDuration } from '../utils';
|
|
2
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
3
|
+
export const OutputCheckCompletion = {
|
|
4
|
+
name: 'output_check',
|
|
5
|
+
schema: {
|
|
6
|
+
description: 'Pipe task stdout into a shell command; mark success when that command exits 0.',
|
|
7
|
+
fields: {
|
|
8
|
+
check: {
|
|
9
|
+
type: 'string',
|
|
10
|
+
required: true,
|
|
11
|
+
description: 'Shell command to run. Task stdout is piped to its stdin.',
|
|
12
|
+
placeholder: "grep -q 'PASS'",
|
|
13
|
+
},
|
|
14
|
+
timeout: {
|
|
15
|
+
type: 'duration',
|
|
16
|
+
default: '30s',
|
|
17
|
+
description: 'Maximum time to wait for the check command.',
|
|
18
|
+
placeholder: '30s',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
async check(config, result, ctx) {
|
|
23
|
+
const checkCmd = config.check;
|
|
24
|
+
if (!checkCmd)
|
|
25
|
+
throw new Error('output_check completion: "check" is required');
|
|
26
|
+
const timeoutMs = config.timeout != null
|
|
27
|
+
? parseDuration(String(config.timeout))
|
|
28
|
+
: DEFAULT_TIMEOUT_MS;
|
|
29
|
+
const controller = new AbortController();
|
|
30
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
31
|
+
// Wire pipeline abort signal into the check process so external abort
|
|
32
|
+
// terminates the child instead of leaving it running undetected.
|
|
33
|
+
const onAbort = () => controller.abort();
|
|
34
|
+
if (ctx.signal) {
|
|
35
|
+
if (ctx.signal.aborted) {
|
|
36
|
+
controller.abort();
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
ctx.signal.addEventListener('abort', onAbort, { once: true });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const proc = Bun.spawn(shellArgs(checkCmd), {
|
|
43
|
+
cwd: ctx.workDir,
|
|
44
|
+
stdin: 'pipe',
|
|
45
|
+
stdout: 'pipe',
|
|
46
|
+
stderr: 'pipe',
|
|
47
|
+
signal: controller.signal,
|
|
48
|
+
});
|
|
49
|
+
try {
|
|
50
|
+
if (proc.stdin) {
|
|
51
|
+
try {
|
|
52
|
+
proc.stdin.write(result.stdout);
|
|
53
|
+
proc.stdin.end(); // no await — consistent with runner.ts; proc.exited handles sync
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
// EPIPE is expected when the check process exits before reading all of stdin
|
|
57
|
+
// (e.g. `grep -q` exits on first match). Anything else is a real failure.
|
|
58
|
+
const code = err?.code;
|
|
59
|
+
if (code !== 'EPIPE')
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Consume stderr concurrently with waiting for exit to prevent pipe-buffer
|
|
64
|
+
// deadlock when check script emits more than ~64 KB of stderr output.
|
|
65
|
+
const [exitCode, stderr] = await Promise.all([
|
|
66
|
+
proc.exited,
|
|
67
|
+
new Response(proc.stderr).text(),
|
|
68
|
+
]);
|
|
69
|
+
if (exitCode !== 0 && stderr.trim()) {
|
|
70
|
+
console.warn(`[output_check] "${checkCmd}" exit=${exitCode}: ${stderr.trim()}`);
|
|
71
|
+
}
|
|
72
|
+
return exitCode === 0;
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
if (ctx.signal)
|
|
77
|
+
ctx.signal.removeEventListener('abort', onAbort);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=output-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-check.js","sourceRoot":"","sources":["../../src/completions/output-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { RawPipelineConfig, RawTrackConfig, RawTaskConfig } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Create a minimal empty pipeline config.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createEmptyPipeline(name: string): RawPipelineConfig;
|
|
6
|
+
/**
|
|
7
|
+
* Update a top-level pipeline field (name, driver, timeout, etc.).
|
|
8
|
+
*/
|
|
9
|
+
export declare function setPipelineField(config: RawPipelineConfig, fields: Partial<Omit<RawPipelineConfig, 'tracks'>>): RawPipelineConfig;
|
|
10
|
+
/**
|
|
11
|
+
* Insert or replace a track by id. Appends if the id is new.
|
|
12
|
+
*/
|
|
13
|
+
export declare function upsertTrack(config: RawPipelineConfig, track: RawTrackConfig): RawPipelineConfig;
|
|
14
|
+
/**
|
|
15
|
+
* Remove a track by id. No-op if the id is not found.
|
|
16
|
+
*/
|
|
17
|
+
export declare function removeTrack(config: RawPipelineConfig, trackId: string): RawPipelineConfig;
|
|
18
|
+
/**
|
|
19
|
+
* Move a track to a new index position (0-based).
|
|
20
|
+
* Clamps toIndex to valid bounds.
|
|
21
|
+
*/
|
|
22
|
+
export declare function moveTrack(config: RawPipelineConfig, trackId: string, toIndex: number): RawPipelineConfig;
|
|
23
|
+
/**
|
|
24
|
+
* Update fields on a single track (excluding tasks list, use upsertTask / removeTask for that).
|
|
25
|
+
*/
|
|
26
|
+
export declare function updateTrack(config: RawPipelineConfig, trackId: string, fields: Partial<Omit<RawTrackConfig, 'id' | 'tasks'>>): RawPipelineConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Insert or replace a task within a track, matched by task.id. Appends if new.
|
|
29
|
+
* No-op if the trackId is not found.
|
|
30
|
+
*/
|
|
31
|
+
export declare function upsertTask(config: RawPipelineConfig, trackId: string, task: RawTaskConfig): RawPipelineConfig;
|
|
32
|
+
/**
|
|
33
|
+
* Remove a task from a track. No-op if either id is not found.
|
|
34
|
+
*
|
|
35
|
+
* When `cleanRefs` is true, all `depends_on` and `continue_from` references to the
|
|
36
|
+
* removed task are also removed from every other task in the pipeline. This prevents
|
|
37
|
+
* validateRaw from reporting dangling-ref errors after the deletion.
|
|
38
|
+
*/
|
|
39
|
+
export declare function removeTask(config: RawPipelineConfig, trackId: string, taskId: string, cleanRefs?: boolean): RawPipelineConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Reorder a task within its track.
|
|
42
|
+
* Clamps toIndex to valid bounds.
|
|
43
|
+
*/
|
|
44
|
+
export declare function moveTask(config: RawPipelineConfig, trackId: string, taskId: string, toIndex: number): RawPipelineConfig;
|
|
45
|
+
/**
|
|
46
|
+
* Move a task from one track to another (appends to the target track).
|
|
47
|
+
* No-op if either trackId or taskId is not found.
|
|
48
|
+
*
|
|
49
|
+
* When `qualifyRefs` is true (the default), bare references (`depends_on`,
|
|
50
|
+
* `continue_from`) pointing to the moved task are converted to fully-qualified
|
|
51
|
+
* refs (`toTrackId.taskId`) so that same-track resolution doesn't silently
|
|
52
|
+
* break after the task changes tracks.
|
|
53
|
+
*/
|
|
54
|
+
export declare function transferTask(config: RawPipelineConfig, fromTrackId: string, taskId: string, toTrackId: string, qualifyRefs?: boolean): RawPipelineConfig;
|
|
55
|
+
//# sourceMappingURL=config-ops.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-ops.d.ts","sourceRoot":"","sources":["../src/config-ops.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAIhF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAEnE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC,GACjD,iBAAiB,CAEnB;AAID;;GAEG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,cAAc,GACpB,iBAAiB,CAQnB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,GACd,iBAAiB,CAEnB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,iBAAiB,CAQnB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,GACpD,iBAAiB,CAOnB;AAID;;;GAGG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,aAAa,GAClB,iBAAiB,CAcnB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,SAAS,UAAQ,GAChB,iBAAiB,CAmDnB;AAoBD;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,iBAAiB,CAcnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,iBAAiB,EACzB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,UAAO,GACjB,iBAAiB,CA6DnB"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// ═══ RawPipelineConfig CRUD Operations ═══
|
|
2
|
+
//
|
|
3
|
+
// Pure, immutable helper functions for building and editing pipeline configs
|
|
4
|
+
// in a visual editor. None of these functions have runtime dependencies —
|
|
5
|
+
// safe to import in any context (sidecar, renderer, tests).
|
|
6
|
+
//
|
|
7
|
+
// All operations return a new config object; inputs are never mutated.
|
|
8
|
+
// ── Pipeline ──
|
|
9
|
+
/**
|
|
10
|
+
* Create a minimal empty pipeline config.
|
|
11
|
+
*/
|
|
12
|
+
export function createEmptyPipeline(name) {
|
|
13
|
+
return { name, tracks: [] };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Update a top-level pipeline field (name, driver, timeout, etc.).
|
|
17
|
+
*/
|
|
18
|
+
export function setPipelineField(config, fields) {
|
|
19
|
+
return { ...config, ...fields };
|
|
20
|
+
}
|
|
21
|
+
// ── Tracks ──
|
|
22
|
+
/**
|
|
23
|
+
* Insert or replace a track by id. Appends if the id is new.
|
|
24
|
+
*/
|
|
25
|
+
export function upsertTrack(config, track) {
|
|
26
|
+
const exists = config.tracks.some(t => t.id === track.id);
|
|
27
|
+
return {
|
|
28
|
+
...config,
|
|
29
|
+
tracks: exists
|
|
30
|
+
? config.tracks.map(t => (t.id === track.id ? track : t))
|
|
31
|
+
: [...config.tracks, track],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Remove a track by id. No-op if the id is not found.
|
|
36
|
+
*/
|
|
37
|
+
export function removeTrack(config, trackId) {
|
|
38
|
+
return { ...config, tracks: config.tracks.filter(t => t.id !== trackId) };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Move a track to a new index position (0-based).
|
|
42
|
+
* Clamps toIndex to valid bounds.
|
|
43
|
+
*/
|
|
44
|
+
export function moveTrack(config, trackId, toIndex) {
|
|
45
|
+
const idx = config.tracks.findIndex(t => t.id === trackId);
|
|
46
|
+
if (idx === -1)
|
|
47
|
+
return config;
|
|
48
|
+
const track = config.tracks[idx];
|
|
49
|
+
const withoutTrack = [...config.tracks.slice(0, idx), ...config.tracks.slice(idx + 1)];
|
|
50
|
+
const clamped = Math.max(0, Math.min(toIndex, withoutTrack.length));
|
|
51
|
+
const tracks = [...withoutTrack.slice(0, clamped), track, ...withoutTrack.slice(clamped)];
|
|
52
|
+
return { ...config, tracks };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Update fields on a single track (excluding tasks list, use upsertTask / removeTask for that).
|
|
56
|
+
*/
|
|
57
|
+
export function updateTrack(config, trackId, fields) {
|
|
58
|
+
return {
|
|
59
|
+
...config,
|
|
60
|
+
tracks: config.tracks.map(t => t.id === trackId ? { ...t, ...fields } : t),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// ── Tasks ──
|
|
64
|
+
/**
|
|
65
|
+
* Insert or replace a task within a track, matched by task.id. Appends if new.
|
|
66
|
+
* No-op if the trackId is not found.
|
|
67
|
+
*/
|
|
68
|
+
export function upsertTask(config, trackId, task) {
|
|
69
|
+
return {
|
|
70
|
+
...config,
|
|
71
|
+
tracks: config.tracks.map(t => {
|
|
72
|
+
if (t.id !== trackId)
|
|
73
|
+
return t;
|
|
74
|
+
const exists = t.tasks.some(tk => tk.id === task.id);
|
|
75
|
+
return {
|
|
76
|
+
...t,
|
|
77
|
+
tasks: exists
|
|
78
|
+
? t.tasks.map(tk => (tk.id === task.id ? task : tk))
|
|
79
|
+
: [...t.tasks, task],
|
|
80
|
+
};
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Remove a task from a track. No-op if either id is not found.
|
|
86
|
+
*
|
|
87
|
+
* When `cleanRefs` is true, all `depends_on` and `continue_from` references to the
|
|
88
|
+
* removed task are also removed from every other task in the pipeline. This prevents
|
|
89
|
+
* validateRaw from reporting dangling-ref errors after the deletion.
|
|
90
|
+
*/
|
|
91
|
+
export function removeTask(config, trackId, taskId, cleanRefs = false) {
|
|
92
|
+
const withoutTask = {
|
|
93
|
+
...config,
|
|
94
|
+
tracks: config.tracks.map(t => {
|
|
95
|
+
if (t.id !== trackId)
|
|
96
|
+
return t;
|
|
97
|
+
return { ...t, tasks: t.tasks.filter(tk => tk.id !== taskId) };
|
|
98
|
+
}),
|
|
99
|
+
};
|
|
100
|
+
if (!cleanRefs)
|
|
101
|
+
return withoutTask;
|
|
102
|
+
const qualId = `${trackId}.${taskId}`;
|
|
103
|
+
// After deletion, can a bare ref "taskId" still resolve to some other task globally?
|
|
104
|
+
// It can if any track in the post-deletion config still contains a task with that bare id.
|
|
105
|
+
const bareIdSurvivesGlobally = withoutTask.tracks.some(t => t.tasks.some(tk => tk.id === taskId));
|
|
106
|
+
return {
|
|
107
|
+
...withoutTask,
|
|
108
|
+
tracks: withoutTask.tracks.map(t => {
|
|
109
|
+
// Build the set of task IDs remaining in this track (the deleted task
|
|
110
|
+
// has already been removed from its own track in withoutTask).
|
|
111
|
+
const remainingIds = new Set(t.tasks.map(tk => tk.id));
|
|
112
|
+
// Resolve whether a ref in THIS track points to the deleted task:
|
|
113
|
+
// - Fully-qualified ref ("trackId.taskId") — always points to the deleted task.
|
|
114
|
+
// - Bare ref ("taskId") from the SAME track as the deleted task — always pointed
|
|
115
|
+
// to the deleted task (same-track lookup takes priority).
|
|
116
|
+
// - Bare ref from a DIFFERENT track:
|
|
117
|
+
// 1. If this track has a local task with that id → ref resolves locally, not removed.
|
|
118
|
+
// 2. Else if some other track still has a task with that id → ref will resolve
|
|
119
|
+
// there after deletion, not removed.
|
|
120
|
+
// 3. Else → ref is dangling, remove it.
|
|
121
|
+
const isRemovedFrom = (ref) => {
|
|
122
|
+
if (ref === qualId)
|
|
123
|
+
return true;
|
|
124
|
+
if (ref === taskId) {
|
|
125
|
+
if (t.id === trackId)
|
|
126
|
+
return true; // same track — was pointing here
|
|
127
|
+
if (remainingIds.has(taskId))
|
|
128
|
+
return false; // local task shadows — ref is fine
|
|
129
|
+
return !bareIdSurvivesGlobally; // remove only if truly dangling
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
};
|
|
133
|
+
return {
|
|
134
|
+
...t,
|
|
135
|
+
tasks: t.tasks.map(tk => cleanTaskRefs(tk, isRemovedFrom)),
|
|
136
|
+
};
|
|
137
|
+
}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function cleanTaskRefs(task, isRemoved) {
|
|
141
|
+
const filteredDeps = task.depends_on?.filter(d => !isRemoved(d));
|
|
142
|
+
const dropContinueFrom = task.continue_from !== undefined && isRemoved(task.continue_from);
|
|
143
|
+
const depsUnchanged = filteredDeps === undefined || filteredDeps.length === task.depends_on.length;
|
|
144
|
+
if (depsUnchanged && !dropContinueFrom)
|
|
145
|
+
return task;
|
|
146
|
+
const { depends_on, continue_from, ...rest } = task;
|
|
147
|
+
return {
|
|
148
|
+
...rest,
|
|
149
|
+
...(filteredDeps !== undefined && filteredDeps.length > 0 ? { depends_on: filteredDeps } : {}),
|
|
150
|
+
...(!dropContinueFrom && continue_from !== undefined ? { continue_from } : {}),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Reorder a task within its track.
|
|
155
|
+
* Clamps toIndex to valid bounds.
|
|
156
|
+
*/
|
|
157
|
+
export function moveTask(config, trackId, taskId, toIndex) {
|
|
158
|
+
return {
|
|
159
|
+
...config,
|
|
160
|
+
tracks: config.tracks.map(t => {
|
|
161
|
+
if (t.id !== trackId)
|
|
162
|
+
return t;
|
|
163
|
+
const idx = t.tasks.findIndex(tk => tk.id === taskId);
|
|
164
|
+
if (idx === -1)
|
|
165
|
+
return t;
|
|
166
|
+
const task = t.tasks[idx];
|
|
167
|
+
const withoutTask = [...t.tasks.slice(0, idx), ...t.tasks.slice(idx + 1)];
|
|
168
|
+
const clamped = Math.max(0, Math.min(toIndex, withoutTask.length));
|
|
169
|
+
const tasks = [...withoutTask.slice(0, clamped), task, ...withoutTask.slice(clamped)];
|
|
170
|
+
return { ...t, tasks };
|
|
171
|
+
}),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Move a task from one track to another (appends to the target track).
|
|
176
|
+
* No-op if either trackId or taskId is not found.
|
|
177
|
+
*
|
|
178
|
+
* When `qualifyRefs` is true (the default), bare references (`depends_on`,
|
|
179
|
+
* `continue_from`) pointing to the moved task are converted to fully-qualified
|
|
180
|
+
* refs (`toTrackId.taskId`) so that same-track resolution doesn't silently
|
|
181
|
+
* break after the task changes tracks.
|
|
182
|
+
*/
|
|
183
|
+
export function transferTask(config, fromTrackId, taskId, toTrackId, qualifyRefs = true) {
|
|
184
|
+
if (fromTrackId === toTrackId)
|
|
185
|
+
return config;
|
|
186
|
+
let task;
|
|
187
|
+
const afterRemove = {
|
|
188
|
+
...config,
|
|
189
|
+
tracks: config.tracks.map(t => {
|
|
190
|
+
if (t.id !== fromTrackId)
|
|
191
|
+
return t;
|
|
192
|
+
const found = t.tasks.find(tk => tk.id === taskId);
|
|
193
|
+
if (!found)
|
|
194
|
+
return t;
|
|
195
|
+
task = found;
|
|
196
|
+
return { ...t, tasks: t.tasks.filter(tk => tk.id !== taskId) };
|
|
197
|
+
}),
|
|
198
|
+
};
|
|
199
|
+
if (!task)
|
|
200
|
+
return config;
|
|
201
|
+
const afterInsert = upsertTask(afterRemove, toTrackId, task);
|
|
202
|
+
if (!qualifyRefs)
|
|
203
|
+
return afterInsert;
|
|
204
|
+
// Qualify bare references to the moved task. After the move, bare ref
|
|
205
|
+
// "taskId" from the old track no longer resolves via same-track priority.
|
|
206
|
+
// Convert it to the qualified form "toTrackId.taskId" so the dependency
|
|
207
|
+
// graph stays correct.
|
|
208
|
+
const qualId = `${toTrackId}.${taskId}`;
|
|
209
|
+
const oldQualId = `${fromTrackId}.${taskId}`;
|
|
210
|
+
// Does any track (other than the destination) still have a task with this bare id?
|
|
211
|
+
const bareIdSurvivesElsewhere = afterInsert.tracks.some(t => t.id !== toTrackId && t.tasks.some(tk => tk.id === taskId));
|
|
212
|
+
return {
|
|
213
|
+
...afterInsert,
|
|
214
|
+
tracks: afterInsert.tracks.map(t => {
|
|
215
|
+
const localHasId = t.tasks.some(tk => tk.id === taskId);
|
|
216
|
+
const qualifyRef = (ref) => {
|
|
217
|
+
// Already-qualified ref to old location → rewrite to new location
|
|
218
|
+
if (ref === oldQualId)
|
|
219
|
+
return qualId;
|
|
220
|
+
// Bare ref: only needs qualifying if it would have resolved to the
|
|
221
|
+
// moved task before the transfer
|
|
222
|
+
if (ref === taskId) {
|
|
223
|
+
if (t.id === fromTrackId) {
|
|
224
|
+
// Was same-track in the old track — now the task is gone.
|
|
225
|
+
// If no other local task shadows it, qualify to new location.
|
|
226
|
+
if (!localHasId)
|
|
227
|
+
return qualId;
|
|
228
|
+
}
|
|
229
|
+
// From a different track: bare ref resolved globally before.
|
|
230
|
+
// If the bare id is now ambiguous or gone from this track's
|
|
231
|
+
// perspective, qualify it.
|
|
232
|
+
if (!localHasId && !bareIdSurvivesElsewhere)
|
|
233
|
+
return qualId;
|
|
234
|
+
}
|
|
235
|
+
return ref;
|
|
236
|
+
};
|
|
237
|
+
return {
|
|
238
|
+
...t,
|
|
239
|
+
tasks: t.tasks.map(tk => qualifyTaskRefs(tk, qualifyRef)),
|
|
240
|
+
};
|
|
241
|
+
}),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/** Rewrite `depends_on` and `continue_from` refs using a mapping function. */
|
|
245
|
+
function qualifyTaskRefs(task, rewrite) {
|
|
246
|
+
const newDeps = task.depends_on?.map(rewrite);
|
|
247
|
+
const newContinue = task.continue_from !== undefined ? rewrite(task.continue_from) : undefined;
|
|
248
|
+
const depsChanged = newDeps !== undefined && newDeps.some((d, i) => d !== task.depends_on[i]);
|
|
249
|
+
const continueChanged = newContinue !== undefined && newContinue !== task.continue_from;
|
|
250
|
+
if (!depsChanged && !continueChanged)
|
|
251
|
+
return task;
|
|
252
|
+
return {
|
|
253
|
+
...task,
|
|
254
|
+
...(newDeps !== undefined ? { depends_on: newDeps } : {}),
|
|
255
|
+
...(newContinue !== undefined ? { continue_from: newContinue } : {}),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=config-ops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-ops.js","sourceRoot":"","sources":["../src/config-ops.ts"],"names":[],"mappings":"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"}
|
package/dist/dag.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { PipelineConfig, RawPipelineConfig, RawTaskConfig, TaskConfig, TrackConfig } from './types';
|
|
2
|
+
export interface DagNode {
|
|
3
|
+
readonly taskId: string;
|
|
4
|
+
readonly task: TaskConfig;
|
|
5
|
+
readonly track: TrackConfig;
|
|
6
|
+
readonly dependsOn: readonly string[];
|
|
7
|
+
/**
|
|
8
|
+
* H1: `task.continue_from` may be written by users as a bare task id
|
|
9
|
+
* (e.g. `review`) or a same-track shorthand. The driver needs the
|
|
10
|
+
* fully-qualified upstream id to look up output/session/normalized maps
|
|
11
|
+
* deterministically — bare lookups race when two tracks happen to share
|
|
12
|
+
* a task name. dag.ts performs the qualification once, here, so the
|
|
13
|
+
* engine never has to.
|
|
14
|
+
*/
|
|
15
|
+
readonly resolvedContinueFrom?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface Dag {
|
|
18
|
+
readonly nodes: ReadonlyMap<string, DagNode>;
|
|
19
|
+
readonly sorted: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
export declare function buildDag(config: PipelineConfig): Dag;
|
|
22
|
+
export interface RawDagNode {
|
|
23
|
+
readonly taskId: string;
|
|
24
|
+
readonly trackId: string;
|
|
25
|
+
readonly rawTask: RawTaskConfig;
|
|
26
|
+
readonly dependsOn: readonly string[];
|
|
27
|
+
}
|
|
28
|
+
export interface RawDag {
|
|
29
|
+
readonly nodes: ReadonlyMap<string, RawDagNode>;
|
|
30
|
+
/** Directed edges: from → to means "from must complete before to starts" */
|
|
31
|
+
readonly edges: readonly {
|
|
32
|
+
readonly from: string;
|
|
33
|
+
readonly to: string;
|
|
34
|
+
}[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Build a lightweight DAG from a raw (unresolved) pipeline config.
|
|
38
|
+
* Unlike buildDag, this function:
|
|
39
|
+
* - Does not require a workDir or resolved PipelineConfig
|
|
40
|
+
* - Is lenient: missing or ambiguous refs are silently skipped
|
|
41
|
+
* - Skips template-expansion tasks (those with a `use` field)
|
|
42
|
+
*
|
|
43
|
+
* Intended for the visual editor to render the flow graph before a pipeline is run.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildRawDag(config: RawPipelineConfig): RawDag;
|
|
46
|
+
//# sourceMappingURL=dag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dag.d.ts","sourceRoot":"","sources":["../src/dag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEzG,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;;;;OAOG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,GAAG;IAClB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAQD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,GAAG,CAqIpD;AAID,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAChD,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CA2D7D"}
|
package/dist/dag.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// Build a global task ID: for cross-track refs we use "track_id.task_id"
|
|
2
|
+
// Within a track, bare "task_id" is also valid
|
|
3
|
+
function qualifyId(trackId, taskId) {
|
|
4
|
+
return `${trackId}.${taskId}`;
|
|
5
|
+
}
|
|
6
|
+
export function buildDag(config) {
|
|
7
|
+
const nodes = new Map();
|
|
8
|
+
// Map bare task IDs to qualified IDs (for resolving unqualified refs)
|
|
9
|
+
const bareToQualified = new Map();
|
|
10
|
+
// 1. Register all nodes
|
|
11
|
+
for (const track of config.tracks) {
|
|
12
|
+
for (const task of track.tasks) {
|
|
13
|
+
const qid = qualifyId(track.id, task.id);
|
|
14
|
+
if (nodes.has(qid)) {
|
|
15
|
+
throw new Error(`Duplicate task ID: "${qid}"`);
|
|
16
|
+
}
|
|
17
|
+
// Track bare ID → qualified. If same bare ID in multiple tracks, mark ambiguous
|
|
18
|
+
if (bareToQualified.has(task.id)) {
|
|
19
|
+
bareToQualified.set(task.id, '__ambiguous__');
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
bareToQualified.set(task.id, qid);
|
|
23
|
+
}
|
|
24
|
+
nodes.set(qid, {
|
|
25
|
+
taskId: qid,
|
|
26
|
+
task,
|
|
27
|
+
track,
|
|
28
|
+
dependsOn: [], // filled below
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Helper to resolve a dependency ref to a qualified ID
|
|
33
|
+
function resolveRef(ref, fromTrackId) {
|
|
34
|
+
// Already qualified (contains dot)
|
|
35
|
+
if (ref.includes('.')) {
|
|
36
|
+
if (!nodes.has(ref)) {
|
|
37
|
+
throw new Error(`Task reference "${ref}" not found`);
|
|
38
|
+
}
|
|
39
|
+
return ref;
|
|
40
|
+
}
|
|
41
|
+
// Try within same track first
|
|
42
|
+
const sameTrack = qualifyId(fromTrackId, ref);
|
|
43
|
+
if (nodes.has(sameTrack))
|
|
44
|
+
return sameTrack;
|
|
45
|
+
// Try global bare lookup
|
|
46
|
+
const global = bareToQualified.get(ref);
|
|
47
|
+
if (global && global !== '__ambiguous__')
|
|
48
|
+
return global;
|
|
49
|
+
if (global === '__ambiguous__') {
|
|
50
|
+
throw new Error(`Ambiguous task reference "${ref}" exists in multiple tracks. ` +
|
|
51
|
+
`Use "track_id.task_id" format.`);
|
|
52
|
+
}
|
|
53
|
+
throw new Error(`Task reference "${ref}" not found`);
|
|
54
|
+
}
|
|
55
|
+
// 2. Resolve depends_on and continue_from to qualified IDs
|
|
56
|
+
for (const track of config.tracks) {
|
|
57
|
+
for (const task of track.tasks) {
|
|
58
|
+
const qid = qualifyId(track.id, task.id);
|
|
59
|
+
const deps = [];
|
|
60
|
+
let resolvedContinueFrom;
|
|
61
|
+
if (task.depends_on) {
|
|
62
|
+
for (const dep of task.depends_on) {
|
|
63
|
+
deps.push(resolveRef(dep, track.id));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (task.continue_from) {
|
|
67
|
+
let resolved;
|
|
68
|
+
try {
|
|
69
|
+
resolved = resolveRef(task.continue_from, track.id);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
throw new Error(`Task "${qid}": continue_from "${task.continue_from}" — no such task found. ` +
|
|
73
|
+
`Use a fully-qualified reference (trackId.taskId) or ensure the target task exists.`);
|
|
74
|
+
}
|
|
75
|
+
resolvedContinueFrom = resolved;
|
|
76
|
+
if (!deps.includes(resolved)) {
|
|
77
|
+
deps.push(resolved); // continue_from implies dependency
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Replace node with resolved deps + qualified continue_from.
|
|
81
|
+
const node = nodes.get(qid);
|
|
82
|
+
nodes.set(qid, { ...node, dependsOn: deps, resolvedContinueFrom });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// 3. Topological sort + cycle detection (Kahn's algorithm)
|
|
86
|
+
const inDegree = new Map();
|
|
87
|
+
const adjacency = new Map(); // parent → children
|
|
88
|
+
for (const [id] of nodes) {
|
|
89
|
+
inDegree.set(id, 0);
|
|
90
|
+
adjacency.set(id, []);
|
|
91
|
+
}
|
|
92
|
+
for (const [id, node] of nodes) {
|
|
93
|
+
for (const dep of node.dependsOn) {
|
|
94
|
+
adjacency.get(dep).push(id);
|
|
95
|
+
inDegree.set(id, (inDegree.get(id) ?? 0) + 1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const queue = [];
|
|
99
|
+
for (const [id, degree] of inDegree) {
|
|
100
|
+
if (degree === 0)
|
|
101
|
+
queue.push(id);
|
|
102
|
+
}
|
|
103
|
+
const sorted = [];
|
|
104
|
+
// Use an index pointer instead of shift() to avoid O(n) per dequeue.
|
|
105
|
+
let qi = 0;
|
|
106
|
+
while (qi < queue.length) {
|
|
107
|
+
const current = queue[qi++];
|
|
108
|
+
sorted.push(current);
|
|
109
|
+
for (const child of adjacency.get(current)) {
|
|
110
|
+
const newDegree = inDegree.get(child) - 1;
|
|
111
|
+
inDegree.set(child, newDegree);
|
|
112
|
+
if (newDegree === 0)
|
|
113
|
+
queue.push(child);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (sorted.length !== nodes.size) {
|
|
117
|
+
// Only report nodes that are actually part of cycles (in-degree > 0
|
|
118
|
+
// after Kahn's algorithm), not their downstream dependents.
|
|
119
|
+
const sortedSet = new Set(sorted);
|
|
120
|
+
const cycleMembers = [...nodes.keys()].filter(id => !sortedSet.has(id) && (inDegree.get(id) ?? 0) > 0);
|
|
121
|
+
throw new Error(`Circular dependency detected involving tasks: ${cycleMembers.join(', ')}`);
|
|
122
|
+
}
|
|
123
|
+
return { nodes, sorted };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Build a lightweight DAG from a raw (unresolved) pipeline config.
|
|
127
|
+
* Unlike buildDag, this function:
|
|
128
|
+
* - Does not require a workDir or resolved PipelineConfig
|
|
129
|
+
* - Is lenient: missing or ambiguous refs are silently skipped
|
|
130
|
+
* - Skips template-expansion tasks (those with a `use` field)
|
|
131
|
+
*
|
|
132
|
+
* Intended for the visual editor to render the flow graph before a pipeline is run.
|
|
133
|
+
*/
|
|
134
|
+
export function buildRawDag(config) {
|
|
135
|
+
const nodes = new Map();
|
|
136
|
+
const bareToQualified = new Map();
|
|
137
|
+
// 1. Register all concrete tasks
|
|
138
|
+
for (const track of config.tracks) {
|
|
139
|
+
for (const task of track.tasks) {
|
|
140
|
+
if (task.use)
|
|
141
|
+
continue; // template-expansion tasks are not yet materialized
|
|
142
|
+
const qid = `${track.id}.${task.id}`;
|
|
143
|
+
if (nodes.has(qid))
|
|
144
|
+
continue; // skip duplicates silently
|
|
145
|
+
if (bareToQualified.has(task.id)) {
|
|
146
|
+
bareToQualified.set(task.id, '__ambiguous__');
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
bareToQualified.set(task.id, qid);
|
|
150
|
+
}
|
|
151
|
+
nodes.set(qid, { taskId: qid, trackId: track.id, rawTask: task, dependsOn: [] });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// 2. Resolve dependency refs leniently (missing / ambiguous refs are skipped)
|
|
155
|
+
function tryResolve(ref, fromTrackId) {
|
|
156
|
+
if (ref.includes('.'))
|
|
157
|
+
return nodes.has(ref) ? ref : null;
|
|
158
|
+
const sameTrack = `${fromTrackId}.${ref}`;
|
|
159
|
+
if (nodes.has(sameTrack))
|
|
160
|
+
return sameTrack;
|
|
161
|
+
const global = bareToQualified.get(ref);
|
|
162
|
+
if (global && global !== '__ambiguous__')
|
|
163
|
+
return global;
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
const edges = [];
|
|
167
|
+
for (const track of config.tracks) {
|
|
168
|
+
for (const task of track.tasks) {
|
|
169
|
+
if (task.use)
|
|
170
|
+
continue;
|
|
171
|
+
const qid = `${track.id}.${task.id}`;
|
|
172
|
+
const deps = [];
|
|
173
|
+
for (const ref of task.depends_on ?? []) {
|
|
174
|
+
const resolved = tryResolve(ref, track.id);
|
|
175
|
+
if (resolved && !deps.includes(resolved)) {
|
|
176
|
+
deps.push(resolved);
|
|
177
|
+
edges.push({ from: resolved, to: qid });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (task.continue_from) {
|
|
181
|
+
const resolved = tryResolve(task.continue_from, track.id);
|
|
182
|
+
if (resolved && !deps.includes(resolved)) {
|
|
183
|
+
deps.push(resolved);
|
|
184
|
+
edges.push({ from: resolved, to: qid });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const node = nodes.get(qid);
|
|
188
|
+
nodes.set(qid, { ...node, dependsOn: deps });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return { nodes, edges };
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=dag.js.map
|