cueline 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +8 -0
- package/.codex-plugin/plugin.json +18 -0
- package/LICENSE +21 -0
- package/README.ja.md +144 -0
- package/README.ko.md +144 -0
- package/README.md +144 -0
- package/README.zh-CN.md +144 -0
- package/README.zh-TW.md +144 -0
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/bin/cueline +14 -0
- package/config/routing.default.json +28 -0
- package/config/routing.schema.json +58 -0
- package/dist/src/api.d.ts +34 -0
- package/dist/src/api.js +153 -0
- package/dist/src/api.js.map +1 -0
- package/dist/src/browser/browser-adapter.d.ts +15 -0
- package/dist/src/browser/browser-adapter.js +2 -0
- package/dist/src/browser/browser-adapter.js.map +1 -0
- package/dist/src/browser/codex-iab/bootstrap.d.ts +66 -0
- package/dist/src/browser/codex-iab/bootstrap.js +41 -0
- package/dist/src/browser/codex-iab/bootstrap.js.map +1 -0
- package/dist/src/browser/codex-iab/chatgpt-client.d.ts +10 -0
- package/dist/src/browser/codex-iab/chatgpt-client.js +180 -0
- package/dist/src/browser/codex-iab/chatgpt-client.js.map +1 -0
- package/dist/src/browser/codex-iab/selectors.d.ts +3 -0
- package/dist/src/browser/codex-iab/selectors.js +17 -0
- package/dist/src/browser/codex-iab/selectors.js.map +1 -0
- package/dist/src/cli/main.d.ts +7 -0
- package/dist/src/cli/main.js +196 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/skill-links.d.ts +2 -0
- package/dist/src/cli/skill-links.js +64 -0
- package/dist/src/cli/skill-links.js.map +1 -0
- package/dist/src/core/controller-loop.d.ts +36 -0
- package/dist/src/core/controller-loop.js +292 -0
- package/dist/src/core/controller-loop.js.map +1 -0
- package/dist/src/core/errors.d.ts +10 -0
- package/dist/src/core/errors.js +18 -0
- package/dist/src/core/errors.js.map +1 -0
- package/dist/src/core/ids.d.ts +5 -0
- package/dist/src/core/ids.js +51 -0
- package/dist/src/core/ids.js.map +1 -0
- package/dist/src/core/runtime.d.ts +12 -0
- package/dist/src/core/runtime.js +35 -0
- package/dist/src/core/runtime.js.map +1 -0
- package/dist/src/core/state-machine.d.ts +28 -0
- package/dist/src/core/state-machine.js +99 -0
- package/dist/src/core/state-machine.js.map +1 -0
- package/dist/src/jobs/locks.d.ts +8 -0
- package/dist/src/jobs/locks.js +24 -0
- package/dist/src/jobs/locks.js.map +1 -0
- package/dist/src/jobs/status.d.ts +22 -0
- package/dist/src/jobs/status.js +53 -0
- package/dist/src/jobs/status.js.map +1 -0
- package/dist/src/jobs/supervisor.d.ts +22 -0
- package/dist/src/jobs/supervisor.js +96 -0
- package/dist/src/jobs/supervisor.js.map +1 -0
- package/dist/src/protocol/parse-command.d.ts +2 -0
- package/dist/src/protocol/parse-command.js +23 -0
- package/dist/src/protocol/parse-command.js.map +1 -0
- package/dist/src/protocol/types.d.ts +67 -0
- package/dist/src/protocol/types.js +2 -0
- package/dist/src/protocol/types.js.map +1 -0
- package/dist/src/protocol/validate-command.d.ts +2 -0
- package/dist/src/protocol/validate-command.js +181 -0
- package/dist/src/protocol/validate-command.js.map +1 -0
- package/dist/src/router/availability.d.ts +3 -0
- package/dist/src/router/availability.js +55 -0
- package/dist/src/router/availability.js.map +1 -0
- package/dist/src/router/config-loader.d.ts +3 -0
- package/dist/src/router/config-loader.js +92 -0
- package/dist/src/router/config-loader.js.map +1 -0
- package/dist/src/router/materialize.d.ts +8 -0
- package/dist/src/router/materialize.js +39 -0
- package/dist/src/router/materialize.js.map +1 -0
- package/dist/src/router/resolver.d.ts +6 -0
- package/dist/src/router/resolver.js +55 -0
- package/dist/src/router/resolver.js.map +1 -0
- package/dist/src/router/types.d.ts +24 -0
- package/dist/src/router/types.js +2 -0
- package/dist/src/router/types.js.map +1 -0
- package/dist/src/runners/process-runner.d.ts +15 -0
- package/dist/src/runners/process-runner.js +124 -0
- package/dist/src/runners/process-runner.js.map +1 -0
- package/dist/src/runners/registry.d.ts +17 -0
- package/dist/src/runners/registry.js +57 -0
- package/dist/src/runners/registry.js.map +1 -0
- package/dist/src/runners/runner-adapter.d.ts +32 -0
- package/dist/src/runners/runner-adapter.js +4 -0
- package/dist/src/runners/runner-adapter.js.map +1 -0
- package/dist/src/state/atomic-write.d.ts +1 -0
- package/dist/src/state/atomic-write.js +27 -0
- package/dist/src/state/atomic-write.js.map +1 -0
- package/dist/src/state/event-log.d.ts +8 -0
- package/dist/src/state/event-log.js +56 -0
- package/dist/src/state/event-log.js.map +1 -0
- package/dist/src/state/paths.d.ts +9 -0
- package/dist/src/state/paths.js +37 -0
- package/dist/src/state/paths.js.map +1 -0
- package/dist/src/state/store.d.ts +23 -0
- package/dist/src/state/store.js +108 -0
- package/dist/src/state/store.js.map +1 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +2 -0
- package/dist/src/version.js.map +1 -0
- package/docs/architecture.md +83 -0
- package/docs/assets/README.md +51 -0
- package/docs/assets/cueline-banner-dark.svg +41 -0
- package/docs/assets/cueline-banner-light.svg +41 -0
- package/docs/assets/cueline-loop-en.svg +77 -0
- package/docs/assets/cueline-loop-ja.svg +77 -0
- package/docs/assets/cueline-loop-ko.svg +77 -0
- package/docs/assets/cueline-loop-zh-CN.svg +77 -0
- package/docs/assets/cueline-loop-zh-TW.svg +77 -0
- package/docs/assets/cueline-mark-dark.svg +10 -0
- package/docs/assets/cueline-mark-light.svg +10 -0
- package/docs/assets/cueline-wordmark-dark.svg +19 -0
- package/docs/assets/cueline-wordmark-light.svg +19 -0
- package/docs/compatibility.md +60 -0
- package/docs/controller-protocol.md +106 -0
- package/docs/provenance.md +10 -0
- package/docs/runner-contract.md +61 -0
- package/docs/state-and-recovery.md +67 -0
- package/evals/evals.json +41 -0
- package/install.sh +70 -0
- package/package.json +73 -0
- package/schemas/controller-command.schema.json +53 -0
- package/schemas/controller-observation.schema.json +32 -0
- package/skills/cueline/SKILL.md +80 -0
- package/skills/cueline/agents/openai.yaml +4 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { CueLineError } from "../core/errors.js";
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
function invalid(message, details) {
|
|
7
|
+
return new CueLineError("ROUTING_CONFIG_INVALID", message, { details });
|
|
8
|
+
}
|
|
9
|
+
function parseCandidate(value, lane, index) {
|
|
10
|
+
if (!isRecord(value)) {
|
|
11
|
+
throw invalid("route candidate must be an object", { lane, index });
|
|
12
|
+
}
|
|
13
|
+
const { id, argv, task_input, enabled } = value;
|
|
14
|
+
if (typeof id !== "string" || id.trim().length === 0) {
|
|
15
|
+
throw invalid("route candidate id must be a non-empty string", { lane, index });
|
|
16
|
+
}
|
|
17
|
+
if (!Array.isArray(argv) || argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
|
|
18
|
+
throw invalid("route candidate argv must contain non-empty strings", { lane, index });
|
|
19
|
+
}
|
|
20
|
+
if (enabled !== undefined && typeof enabled !== "boolean") {
|
|
21
|
+
throw invalid("route candidate enabled must be a boolean", { lane, index });
|
|
22
|
+
}
|
|
23
|
+
if (task_input !== undefined && task_input !== "argv" && task_input !== "stdin") {
|
|
24
|
+
throw invalid("route candidate task_input must be argv or stdin", { lane, index });
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
id,
|
|
28
|
+
argv: [...argv],
|
|
29
|
+
...(task_input === undefined ? {} : { task_input }),
|
|
30
|
+
...(enabled === undefined ? {} : { enabled }),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function parseLane(value, lane) {
|
|
34
|
+
if (!isRecord(value)) {
|
|
35
|
+
throw invalid("lane configuration must be an object", { lane });
|
|
36
|
+
}
|
|
37
|
+
const { enabled, candidates } = value;
|
|
38
|
+
if (typeof enabled !== "boolean") {
|
|
39
|
+
throw invalid("lane enabled must be a boolean", { lane });
|
|
40
|
+
}
|
|
41
|
+
if (!Array.isArray(candidates) || candidates.length === 0) {
|
|
42
|
+
throw invalid("lane candidates must be a non-empty array", { lane });
|
|
43
|
+
}
|
|
44
|
+
const parsedCandidates = candidates.map((candidate, index) => parseCandidate(candidate, lane, index));
|
|
45
|
+
const identifiers = new Set();
|
|
46
|
+
for (const candidate of parsedCandidates) {
|
|
47
|
+
if (identifiers.has(candidate.id)) {
|
|
48
|
+
throw invalid("lane candidate ids must be unique", { lane, id: candidate.id });
|
|
49
|
+
}
|
|
50
|
+
identifiers.add(candidate.id);
|
|
51
|
+
}
|
|
52
|
+
return { enabled, candidates: parsedCandidates };
|
|
53
|
+
}
|
|
54
|
+
export function parseRoutingConfig(value) {
|
|
55
|
+
if (!isRecord(value)) {
|
|
56
|
+
throw invalid("routing configuration must be an object");
|
|
57
|
+
}
|
|
58
|
+
if (value.version !== 1) {
|
|
59
|
+
throw invalid("routing configuration version must be 1", { version: value.version });
|
|
60
|
+
}
|
|
61
|
+
if (!isRecord(value.lanes) || Object.keys(value.lanes).length === 0) {
|
|
62
|
+
throw invalid("routing configuration must define at least one lane");
|
|
63
|
+
}
|
|
64
|
+
const lanes = {};
|
|
65
|
+
for (const [lane, laneValue] of Object.entries(value.lanes)) {
|
|
66
|
+
if (lane.trim().length === 0) {
|
|
67
|
+
throw invalid("lane name must be non-empty");
|
|
68
|
+
}
|
|
69
|
+
lanes[lane] = parseLane(laneValue, lane);
|
|
70
|
+
}
|
|
71
|
+
return { version: 1, lanes };
|
|
72
|
+
}
|
|
73
|
+
export async function loadRoutingConfig(filePath) {
|
|
74
|
+
let source;
|
|
75
|
+
try {
|
|
76
|
+
source = await readFile(filePath, "utf8");
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
throw new CueLineError("ROUTING_CONFIG_READ_FAILED", `unable to read routing config: ${filePath}`, {
|
|
80
|
+
cause: error,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
let parsed;
|
|
84
|
+
try {
|
|
85
|
+
parsed = JSON.parse(source);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
throw new CueLineError("ROUTING_CONFIG_INVALID", "routing config is not valid JSON", { cause: error });
|
|
89
|
+
}
|
|
90
|
+
return parseRoutingConfig(parsed);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=config-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../../src/router/config-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ControllerJobSpec } from "../protocol/types.js";
|
|
2
|
+
import type { RunnerSpec } from "../runners/runner-adapter.js";
|
|
3
|
+
import type { ResolvedRoute } from "./types.js";
|
|
4
|
+
export interface MaterializeRunnerOptions {
|
|
5
|
+
cwd?: string;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function materializeRunnerSpec(jobId: string, job: ControllerJobSpec, route: ResolvedRoute, options?: MaterializeRunnerOptions): RunnerSpec;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { CueLineError } from "../core/errors.js";
|
|
2
|
+
import { runtimeCwd } from "../core/runtime.js";
|
|
3
|
+
const TOKEN_PATTERN = /\{(job_id|lane|mode|sandbox|task|workdir)\}/g;
|
|
4
|
+
export function materializeRunnerSpec(jobId, job, route, options = {}) {
|
|
5
|
+
const cwd = job.workdir ?? options.cwd ?? runtimeCwd();
|
|
6
|
+
const taskInput = route.candidate.task_input ?? "argv";
|
|
7
|
+
let placedTask = false;
|
|
8
|
+
const values = {
|
|
9
|
+
job_id: jobId,
|
|
10
|
+
lane: route.lane,
|
|
11
|
+
mode: job.mode,
|
|
12
|
+
sandbox: job.mode === "advise" ? "read-only" : "workspace-write",
|
|
13
|
+
task: job.task,
|
|
14
|
+
workdir: cwd,
|
|
15
|
+
};
|
|
16
|
+
const argv = route.candidate.argv.map((part) => part.replace(TOKEN_PATTERN, (_match, token) => {
|
|
17
|
+
if (token === "task")
|
|
18
|
+
placedTask = true;
|
|
19
|
+
return values[token] ?? "";
|
|
20
|
+
}));
|
|
21
|
+
if (taskInput === "argv" && !placedTask) {
|
|
22
|
+
throw new CueLineError("ROUTE_TEMPLATE_TASK_MISSING", `runner '${route.candidate.id}' uses argv input but has no {task} placeholder`, { details: { lane: route.lane, candidate: route.candidate.id } });
|
|
23
|
+
}
|
|
24
|
+
if (taskInput === "stdin" && placedTask) {
|
|
25
|
+
throw new CueLineError("ROUTE_TEMPLATE_TASK_DUPLICATE", `runner '${route.candidate.id}' cannot use both stdin and a {task} placeholder`, { details: { lane: route.lane, candidate: route.candidate.id } });
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
jobId,
|
|
29
|
+
argv,
|
|
30
|
+
...(taskInput === "stdin" ? { stdin: job.task } : {}),
|
|
31
|
+
mode: job.mode,
|
|
32
|
+
timeoutMs: job.timeout_ms ?? options.timeoutMs ?? 10 * 60 * 1_000,
|
|
33
|
+
lane: route.lane,
|
|
34
|
+
task: job.task,
|
|
35
|
+
cwd,
|
|
36
|
+
...(job.background === undefined ? {} : { background: job.background }),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=materialize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materialize.js","sourceRoot":"","sources":["../../../src/router/materialize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAUhD,MAAM,aAAa,GAAG,8CAA8C,CAAC;AAErE,MAAM,UAAU,qBAAqB,CACnC,KAAa,EACb,GAAsB,EACtB,KAAoB,EACpB,OAAO,GAA6B,EAAE;IAEtC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,CAAC;IACvD,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,UAAU,IAAI,MAAM,CAAC;IACvD,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,MAAM,MAAM,GAA2B;QACrC,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB;QAChE,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG;KACb,CAAC;IACF,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC7C,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,KAAa,EAAE,EAAE;QACpD,IAAI,KAAK,KAAK,MAAM;YAAE,UAAU,GAAG,IAAI,CAAC;QACxC,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC,CAAC,CACH,CAAC;IAEF,IAAI,SAAS,KAAK,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACxC,MAAM,IAAI,YAAY,CACpB,6BAA6B,EAC7B,WAAW,KAAK,CAAC,SAAS,CAAC,EAAE,iDAAiD,EAC9E,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CACjE,CAAC;IACJ,CAAC;IACD,IAAI,SAAS,KAAK,OAAO,IAAI,UAAU,EAAE,CAAC;QACxC,MAAM,IAAI,YAAY,CACpB,+BAA+B,EAC/B,WAAW,KAAK,CAAC,SAAS,CAAC,EAAE,kDAAkD,EAC/E,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CACjE,CAAC;IACJ,CAAC;IAED,OAAO;QACL,KAAK;QACL,IAAI;QACJ,GAAG,CAAC,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,SAAS,EAAE,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,SAAS,IAAI,EAAE,GAAG,EAAE,GAAG,KAAK;QACjE,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,GAAG;QACH,GAAG,CAAC,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC;KACxE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ResolvedRoute, RouteAvailability, RoutingConfig } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Chooses a candidate before any runner is invoked. This function never spawns
|
|
4
|
+
* a process and therefore cannot perform post-spawn fallback.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveRoute(lane: string, config: RoutingConfig, availability: RouteAvailability, requestedCandidateId?: string): ResolvedRoute;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { CueLineError } from "../core/errors.js";
|
|
2
|
+
function isAvailabilityChecker(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && "isAvailable" in value;
|
|
4
|
+
}
|
|
5
|
+
function isAvailable(availability, candidate, lane) {
|
|
6
|
+
if (typeof availability === "function") {
|
|
7
|
+
return availability(candidate, lane);
|
|
8
|
+
}
|
|
9
|
+
if (isAvailabilityChecker(availability)) {
|
|
10
|
+
return availability.isAvailable(candidate, lane);
|
|
11
|
+
}
|
|
12
|
+
return availability[candidate.id] === true;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Chooses a candidate before any runner is invoked. This function never spawns
|
|
16
|
+
* a process and therefore cannot perform post-spawn fallback.
|
|
17
|
+
*/
|
|
18
|
+
export function resolveRoute(lane, config, availability, requestedCandidateId) {
|
|
19
|
+
const laneConfig = config.lanes[lane];
|
|
20
|
+
if (laneConfig === undefined) {
|
|
21
|
+
throw new CueLineError("ROUTE_LANE_UNKNOWN", `unknown routing lane: ${lane}`, { details: { lane } });
|
|
22
|
+
}
|
|
23
|
+
if (!laneConfig.enabled) {
|
|
24
|
+
throw new CueLineError("ROUTE_LANE_DISABLED", `routing lane is disabled: ${lane}`, {
|
|
25
|
+
details: { lane },
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
if (requestedCandidateId !== undefined) {
|
|
29
|
+
const candidateIndex = laneConfig.candidates.findIndex((candidate) => candidate.id === requestedCandidateId);
|
|
30
|
+
if (candidateIndex < 0) {
|
|
31
|
+
throw new CueLineError("ROUTE_RUNNER_UNKNOWN", `runner '${requestedCandidateId}' is not configured for lane: ${lane}`, { details: { lane, runner: requestedCandidateId } });
|
|
32
|
+
}
|
|
33
|
+
const candidate = laneConfig.candidates[candidateIndex];
|
|
34
|
+
if (candidate === undefined || candidate.enabled === false) {
|
|
35
|
+
throw new CueLineError("ROUTE_RUNNER_DISABLED", `runner '${requestedCandidateId}' is disabled for lane: ${lane}`, { details: { lane, runner: requestedCandidateId } });
|
|
36
|
+
}
|
|
37
|
+
if (!isAvailable(availability, candidate, lane)) {
|
|
38
|
+
throw new CueLineError("ROUTE_RUNNER_UNAVAILABLE", `runner '${requestedCandidateId}' is unavailable for lane: ${lane}`, { details: { lane, runner: requestedCandidateId } });
|
|
39
|
+
}
|
|
40
|
+
return { lane, candidate, candidateIndex };
|
|
41
|
+
}
|
|
42
|
+
for (let candidateIndex = 0; candidateIndex < laneConfig.candidates.length; candidateIndex += 1) {
|
|
43
|
+
const candidate = laneConfig.candidates[candidateIndex];
|
|
44
|
+
if (candidate === undefined || candidate.enabled === false) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (isAvailable(availability, candidate, lane)) {
|
|
48
|
+
return { lane, candidate, candidateIndex };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
throw new CueLineError("ROUTE_NO_CANDIDATE", `no available candidate for routing lane: ${lane}`, {
|
|
52
|
+
details: { lane },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../../src/router/resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface RouteCandidate {
|
|
2
|
+
id: string;
|
|
3
|
+
argv: readonly string[];
|
|
4
|
+
task_input?: "argv" | "stdin";
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface LaneConfig {
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
candidates: readonly RouteCandidate[];
|
|
10
|
+
}
|
|
11
|
+
export interface RoutingConfig {
|
|
12
|
+
version: 1;
|
|
13
|
+
lanes: Readonly<Record<string, LaneConfig>>;
|
|
14
|
+
}
|
|
15
|
+
export type CandidateAvailability = (candidate: RouteCandidate, lane: string) => boolean;
|
|
16
|
+
export interface CandidateAvailabilityChecker {
|
|
17
|
+
isAvailable(candidate: RouteCandidate, lane: string): boolean;
|
|
18
|
+
}
|
|
19
|
+
export type RouteAvailability = CandidateAvailability | CandidateAvailabilityChecker | Readonly<Record<string, boolean | undefined>>;
|
|
20
|
+
export interface ResolvedRoute {
|
|
21
|
+
lane: string;
|
|
22
|
+
candidate: RouteCandidate;
|
|
23
|
+
candidateIndex: number;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/router/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { JobResult, RunnerAdapter, RunnerSpec } from "./runner-adapter.js";
|
|
2
|
+
import { RunnerRegistry } from "./registry.js";
|
|
3
|
+
export interface ProcessRunnerOptions {
|
|
4
|
+
environment?: NodeJS.ProcessEnv;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Executes exactly one registered argv. Candidate fallback belongs to the
|
|
8
|
+
* router and is intentionally unavailable here after a process starts.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ProcessRunner implements RunnerAdapter {
|
|
11
|
+
#private;
|
|
12
|
+
private readonly registry;
|
|
13
|
+
constructor(registry: RunnerRegistry, options?: ProcessRunnerOptions);
|
|
14
|
+
run(spec: RunnerSpec): Promise<JobResult>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { CueLineError } from "../core/errors.js";
|
|
3
|
+
import { runtimeEnvironment } from "../core/runtime.js";
|
|
4
|
+
import { RunnerRegistry } from "./registry.js";
|
|
5
|
+
function combineOutput(stdout, stderr) {
|
|
6
|
+
if (stdout.length === 0) {
|
|
7
|
+
return stderr;
|
|
8
|
+
}
|
|
9
|
+
if (stderr.length === 0) {
|
|
10
|
+
return stdout;
|
|
11
|
+
}
|
|
12
|
+
return stdout.endsWith("\n") ? `${stdout}${stderr}` : `${stdout}\n${stderr}`;
|
|
13
|
+
}
|
|
14
|
+
function errorText(error) {
|
|
15
|
+
return error instanceof Error ? error.message : String(error);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Executes exactly one registered argv. Candidate fallback belongs to the
|
|
19
|
+
* router and is intentionally unavailable here after a process starts.
|
|
20
|
+
*/
|
|
21
|
+
export class ProcessRunner {
|
|
22
|
+
registry;
|
|
23
|
+
#environment;
|
|
24
|
+
constructor(registry, options = {}) {
|
|
25
|
+
this.registry = registry;
|
|
26
|
+
this.#environment = { ...(options.environment ?? runtimeEnvironment()) };
|
|
27
|
+
}
|
|
28
|
+
async run(spec) {
|
|
29
|
+
if (this.#environment.CUELINE_DEPTH !== undefined || spec.env?.CUELINE_DEPTH !== undefined) {
|
|
30
|
+
throw new CueLineError("NESTED_ROUTING_REJECTED", "nested CueLine routing is not allowed");
|
|
31
|
+
}
|
|
32
|
+
if (!Number.isFinite(spec.timeoutMs) || spec.timeoutMs <= 0) {
|
|
33
|
+
throw new CueLineError("PROCESS_TIMEOUT_INVALID", "process timeout must be a positive finite number", {
|
|
34
|
+
details: { timeoutMs: spec.timeoutMs },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const registered = this.registry.requireArgv(spec.argv);
|
|
38
|
+
const executable = spec.argv[0];
|
|
39
|
+
if (executable === undefined) {
|
|
40
|
+
throw new CueLineError("RUNNER_ARGV_INVALID", "argv must begin with a registered executable");
|
|
41
|
+
}
|
|
42
|
+
if (registered.executable !== executable) {
|
|
43
|
+
throw new CueLineError("RUNNER_EXECUTABLE_UNREGISTERED", "argv executable registration did not match");
|
|
44
|
+
}
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const startedAt = new Date().toISOString();
|
|
47
|
+
const environment = {
|
|
48
|
+
...this.#environment,
|
|
49
|
+
...spec.env,
|
|
50
|
+
CUELINE_DEPTH: "1",
|
|
51
|
+
};
|
|
52
|
+
let stdout = "";
|
|
53
|
+
let stderr = "";
|
|
54
|
+
let timedOut = false;
|
|
55
|
+
let settled = false;
|
|
56
|
+
let timeoutTimer;
|
|
57
|
+
let forceKillTimer;
|
|
58
|
+
const finish = (status, exitCode) => {
|
|
59
|
+
if (settled) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
settled = true;
|
|
63
|
+
if (timeoutTimer !== undefined) {
|
|
64
|
+
clearTimeout(timeoutTimer);
|
|
65
|
+
}
|
|
66
|
+
if (forceKillTimer !== undefined) {
|
|
67
|
+
clearTimeout(forceKillTimer);
|
|
68
|
+
}
|
|
69
|
+
const output = combineOutput(stdout, stderr);
|
|
70
|
+
resolve({
|
|
71
|
+
status,
|
|
72
|
+
exitCode,
|
|
73
|
+
stdout,
|
|
74
|
+
stderr,
|
|
75
|
+
output,
|
|
76
|
+
emptyOutput: output.length === 0,
|
|
77
|
+
timedOut,
|
|
78
|
+
ambiguousSideEffects: spec.mode === "work" && status !== "succeeded",
|
|
79
|
+
retryable: false,
|
|
80
|
+
startedAt,
|
|
81
|
+
finishedAt: new Date().toISOString(),
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
let child;
|
|
85
|
+
try {
|
|
86
|
+
child = spawn(executable, [...spec.argv.slice(1)], {
|
|
87
|
+
cwd: spec.cwd,
|
|
88
|
+
env: environment,
|
|
89
|
+
shell: false,
|
|
90
|
+
stdio: [spec.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
stderr = errorText(error);
|
|
95
|
+
finish("failed", null);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
timeoutTimer = setTimeout(() => {
|
|
99
|
+
timedOut = true;
|
|
100
|
+
child.kill("SIGTERM");
|
|
101
|
+
forceKillTimer = setTimeout(() => child.kill("SIGKILL"), 250);
|
|
102
|
+
}, spec.timeoutMs);
|
|
103
|
+
child.stdout?.setEncoding("utf8");
|
|
104
|
+
child.stdout?.on("data", (chunk) => {
|
|
105
|
+
stdout += chunk.toString();
|
|
106
|
+
});
|
|
107
|
+
child.stderr?.setEncoding("utf8");
|
|
108
|
+
child.stderr?.on("data", (chunk) => {
|
|
109
|
+
stderr += chunk.toString();
|
|
110
|
+
});
|
|
111
|
+
if (spec.stdin !== undefined) {
|
|
112
|
+
child.stdin?.end(spec.stdin, "utf8");
|
|
113
|
+
}
|
|
114
|
+
child.once("error", (error) => {
|
|
115
|
+
stderr += errorText(error);
|
|
116
|
+
finish("failed", null);
|
|
117
|
+
});
|
|
118
|
+
child.once("close", (exitCode) => {
|
|
119
|
+
finish(timedOut ? "timed_out" : exitCode === 0 ? "succeeded" : "failed", exitCode);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=process-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-runner.js","sourceRoot":"","sources":["../../../src/runners/process-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface RegisteredExecutable {
|
|
2
|
+
id: string;
|
|
3
|
+
executable: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Explicit allow-list for argv[0]. Registering an executable never grants
|
|
7
|
+
* permission to invoke a shell or to substitute a different argv[0].
|
|
8
|
+
*/
|
|
9
|
+
export declare class RunnerRegistry {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(entries?: Iterable<RegisteredExecutable>);
|
|
12
|
+
register(entry: RegisteredExecutable): this;
|
|
13
|
+
get(id: string): RegisteredExecutable | undefined;
|
|
14
|
+
hasExecutable(executable: string): boolean;
|
|
15
|
+
requireExecutable(executable: string): RegisteredExecutable;
|
|
16
|
+
requireArgv(argv: readonly string[]): RegisteredExecutable;
|
|
17
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { CueLineError } from "../core/errors.js";
|
|
2
|
+
function validateEntry(entry) {
|
|
3
|
+
if (entry.id.trim().length === 0) {
|
|
4
|
+
throw new CueLineError("RUNNER_REGISTRY_INVALID", "registered runner id must be non-empty");
|
|
5
|
+
}
|
|
6
|
+
if (entry.executable.trim().length === 0) {
|
|
7
|
+
throw new CueLineError("RUNNER_REGISTRY_INVALID", "registered executable must be non-empty", {
|
|
8
|
+
details: { id: entry.id },
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Explicit allow-list for argv[0]. Registering an executable never grants
|
|
14
|
+
* permission to invoke a shell or to substitute a different argv[0].
|
|
15
|
+
*/
|
|
16
|
+
export class RunnerRegistry {
|
|
17
|
+
#byId = new Map();
|
|
18
|
+
#byExecutable = new Map();
|
|
19
|
+
constructor(entries = []) {
|
|
20
|
+
for (const entry of entries) {
|
|
21
|
+
this.register(entry);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
register(entry) {
|
|
25
|
+
validateEntry(entry);
|
|
26
|
+
if (this.#byId.has(entry.id) || this.#byExecutable.has(entry.executable)) {
|
|
27
|
+
throw new CueLineError("RUNNER_REGISTRY_DUPLICATE", "runner executable is already registered", {
|
|
28
|
+
details: { id: entry.id, executable: entry.executable },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const registered = { ...entry };
|
|
32
|
+
this.#byId.set(registered.id, registered);
|
|
33
|
+
this.#byExecutable.set(registered.executable, registered);
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
get(id) {
|
|
37
|
+
return this.#byId.get(id);
|
|
38
|
+
}
|
|
39
|
+
hasExecutable(executable) {
|
|
40
|
+
return this.#byExecutable.has(executable);
|
|
41
|
+
}
|
|
42
|
+
requireExecutable(executable) {
|
|
43
|
+
const registered = this.#byExecutable.get(executable);
|
|
44
|
+
if (registered === undefined) {
|
|
45
|
+
throw new CueLineError("RUNNER_EXECUTABLE_UNREGISTERED", `argv executable is not pre-registered: ${executable}`, { details: { executable } });
|
|
46
|
+
}
|
|
47
|
+
return registered;
|
|
48
|
+
}
|
|
49
|
+
requireArgv(argv) {
|
|
50
|
+
const executable = argv[0];
|
|
51
|
+
if (typeof executable !== "string" || executable.length === 0) {
|
|
52
|
+
throw new CueLineError("RUNNER_ARGV_INVALID", "argv must begin with a non-empty executable");
|
|
53
|
+
}
|
|
54
|
+
return this.requireExecutable(executable);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/runners/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAOjD,SAAS,aAAa,CAAC,KAA2B;IAChD,IAAI,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,YAAY,CAAC,yBAAyB,EAAE,wCAAwC,CAAC,CAAC;IAC9F,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,YAAY,CAAC,yBAAyB,EAAE,yCAAyC,EAAE;YAC3F,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE;SAC1B,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,cAAc;IAChB,KAAK,GAAG,IAAI,GAAG,EAAgC,CAAC;IAChD,aAAa,GAAG,IAAI,GAAG,EAAgC,CAAC;IAEjE,YAAY,OAAO,GAAmC,EAAE;QACtD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,KAA2B;QAClC,aAAa,CAAC,KAAK,CAAC,CAAC;QACrB,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;YACzE,MAAM,IAAI,YAAY,CAAC,2BAA2B,EAAE,yCAAyC,EAAE;gBAC7F,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE;aACxD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,UAAU,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAED,aAAa,CAAC,UAAkB;QAC9B,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC5C,CAAC;IAED,iBAAiB,CAAC,UAAkB;QAClC,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACtD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,YAAY,CACpB,gCAAgC,EAChC,0CAA0C,UAAU,EAAE,EACtD,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,EAAE,CAC5B,CAAC;QACJ,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,WAAW,CAAC,IAAuB;QACjC,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9D,MAAM,IAAI,YAAY,CAAC,qBAAqB,EAAE,6CAA6C,CAAC,CAAC;QAC/F,CAAC;QACD,OAAO,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAC5C,CAAC;CACF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { JobMode } from "../protocol/types.js";
|
|
2
|
+
export type JobExecution = "foreground" | "background";
|
|
3
|
+
export type JobResultStatus = "succeeded" | "failed" | "timed_out" | "ambiguous";
|
|
4
|
+
export interface RunnerSpec {
|
|
5
|
+
jobId: string;
|
|
6
|
+
argv: readonly string[];
|
|
7
|
+
stdin?: string;
|
|
8
|
+
mode: JobMode;
|
|
9
|
+
timeoutMs: number;
|
|
10
|
+
background?: boolean;
|
|
11
|
+
lane?: string;
|
|
12
|
+
task?: string;
|
|
13
|
+
cwd?: string;
|
|
14
|
+
env?: NodeJS.ProcessEnv;
|
|
15
|
+
}
|
|
16
|
+
export interface JobResult {
|
|
17
|
+
status: JobResultStatus;
|
|
18
|
+
exitCode: number | null;
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
output: string;
|
|
22
|
+
emptyOutput: boolean;
|
|
23
|
+
timedOut: boolean;
|
|
24
|
+
ambiguousSideEffects: boolean;
|
|
25
|
+
retryable: false;
|
|
26
|
+
startedAt: string;
|
|
27
|
+
finishedAt: string;
|
|
28
|
+
}
|
|
29
|
+
export interface RunnerAdapter {
|
|
30
|
+
run(spec: RunnerSpec): Promise<JobResult>;
|
|
31
|
+
}
|
|
32
|
+
export declare function executionFor(spec: Pick<RunnerSpec, "background">): JobExecution;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner-adapter.js","sourceRoot":"","sources":["../../../src/runners/runner-adapter.ts"],"names":[],"mappings":"AAoCA,MAAM,UAAU,YAAY,CAAC,IAAoC;IAC/D,OAAO,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function atomicWriteJson(target: string, value: unknown): Promise<void>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, open, rename, unlink } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { canonicalJson } from "../core/ids.js";
|
|
5
|
+
import { runtimePidTag } from "../core/runtime.js";
|
|
6
|
+
export async function atomicWriteJson(target, value) {
|
|
7
|
+
const directory = path.dirname(target);
|
|
8
|
+
await mkdir(directory, { recursive: true });
|
|
9
|
+
const temporary = path.join(directory, `.${path.basename(target)}.${runtimePidTag()}.${randomUUID()}.tmp`);
|
|
10
|
+
let handle;
|
|
11
|
+
try {
|
|
12
|
+
handle = await open(temporary, "wx", 0o600);
|
|
13
|
+
await handle.writeFile(`${canonicalJson(value)}\n`, "utf8");
|
|
14
|
+
await handle.sync();
|
|
15
|
+
await handle.close();
|
|
16
|
+
handle = undefined;
|
|
17
|
+
await rename(temporary, target);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (handle !== undefined) {
|
|
21
|
+
await handle.close().catch(() => undefined);
|
|
22
|
+
}
|
|
23
|
+
await unlink(temporary).catch(() => undefined);
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=atomic-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../../../src/state/atomic-write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,KAAc;IAClE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CACzB,SAAS,EACT,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,aAAa,EAAE,IAAI,UAAU,EAAE,MAAM,CACnE,CAAC;IACF,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC5C,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5D,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,GAAG,SAAS,CAAC;QACnB,MAAM,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC9C,CAAC;QACD,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC/C,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface RunEvent {
|
|
2
|
+
sequence: number;
|
|
3
|
+
timestamp: string;
|
|
4
|
+
type: string;
|
|
5
|
+
payload: unknown;
|
|
6
|
+
}
|
|
7
|
+
export declare function appendEvent(file: string, event: RunEvent): Promise<void>;
|
|
8
|
+
export declare function readEvents(file: string): Promise<RunEvent[]>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { mkdir, open, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { canonicalJson } from "../core/ids.js";
|
|
4
|
+
function validateEvent(value, expectedSequence) {
|
|
5
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
6
|
+
throw new Error(`EVENT_LOG_INVALID: event ${expectedSequence} is not an object`);
|
|
7
|
+
}
|
|
8
|
+
const record = value;
|
|
9
|
+
if (record.sequence !== expectedSequence ||
|
|
10
|
+
typeof record.timestamp !== "string" ||
|
|
11
|
+
typeof record.type !== "string" ||
|
|
12
|
+
record.type === "") {
|
|
13
|
+
throw new Error(`EVENT_LOG_INVALID: malformed event ${expectedSequence}`);
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
sequence: expectedSequence,
|
|
17
|
+
timestamp: record.timestamp,
|
|
18
|
+
type: record.type,
|
|
19
|
+
payload: record.payload,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export async function appendEvent(file, event) {
|
|
23
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
24
|
+
const handle = await open(file, "a", 0o600);
|
|
25
|
+
try {
|
|
26
|
+
await handle.writeFile(`${canonicalJson(event)}\n`, "utf8");
|
|
27
|
+
await handle.sync();
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
await handle.close();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function readEvents(file) {
|
|
34
|
+
let contents;
|
|
35
|
+
try {
|
|
36
|
+
contents = await readFile(file, "utf8");
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (error.code === "ENOENT") {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
const lines = contents.split("\n").filter((line) => line.trim() !== "");
|
|
45
|
+
return lines.map((line, index) => {
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = JSON.parse(line);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new Error(`EVENT_LOG_INVALID: invalid JSON at line ${index + 1}`, { cause: error });
|
|
52
|
+
}
|
|
53
|
+
return validateEvent(parsed, index + 1);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=event-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-log.js","sourceRoot":"","sources":["../../../src/state/event-log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAS/C,SAAS,aAAa,CAAC,KAAc,EAAE,gBAAwB;IAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,4BAA4B,gBAAgB,mBAAmB,CAAC,CAAC;IACnF,CAAC;IACD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,IACE,MAAM,CAAC,QAAQ,KAAK,gBAAgB;QACpC,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;QACpC,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;QAC/B,MAAM,CAAC,IAAI,KAAK,EAAE,EAClB,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,sCAAsC,gBAAgB,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,gBAAgB;QAC1B,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,KAAe;IAC7D,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5D,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACxE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC/B,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,2CAA2C,KAAK,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5F,CAAC;QACD,OAAO,aAAa,CAAC,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC"}
|