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.
Files changed (130) hide show
  1. package/.claude-plugin/plugin.json +8 -0
  2. package/.codex-plugin/plugin.json +18 -0
  3. package/LICENSE +21 -0
  4. package/README.ja.md +144 -0
  5. package/README.ko.md +144 -0
  6. package/README.md +144 -0
  7. package/README.zh-CN.md +144 -0
  8. package/README.zh-TW.md +144 -0
  9. package/THIRD_PARTY_NOTICES.md +11 -0
  10. package/bin/cueline +14 -0
  11. package/config/routing.default.json +28 -0
  12. package/config/routing.schema.json +58 -0
  13. package/dist/src/api.d.ts +34 -0
  14. package/dist/src/api.js +153 -0
  15. package/dist/src/api.js.map +1 -0
  16. package/dist/src/browser/browser-adapter.d.ts +15 -0
  17. package/dist/src/browser/browser-adapter.js +2 -0
  18. package/dist/src/browser/browser-adapter.js.map +1 -0
  19. package/dist/src/browser/codex-iab/bootstrap.d.ts +66 -0
  20. package/dist/src/browser/codex-iab/bootstrap.js +41 -0
  21. package/dist/src/browser/codex-iab/bootstrap.js.map +1 -0
  22. package/dist/src/browser/codex-iab/chatgpt-client.d.ts +10 -0
  23. package/dist/src/browser/codex-iab/chatgpt-client.js +180 -0
  24. package/dist/src/browser/codex-iab/chatgpt-client.js.map +1 -0
  25. package/dist/src/browser/codex-iab/selectors.d.ts +3 -0
  26. package/dist/src/browser/codex-iab/selectors.js +17 -0
  27. package/dist/src/browser/codex-iab/selectors.js.map +1 -0
  28. package/dist/src/cli/main.d.ts +7 -0
  29. package/dist/src/cli/main.js +196 -0
  30. package/dist/src/cli/main.js.map +1 -0
  31. package/dist/src/cli/skill-links.d.ts +2 -0
  32. package/dist/src/cli/skill-links.js +64 -0
  33. package/dist/src/cli/skill-links.js.map +1 -0
  34. package/dist/src/core/controller-loop.d.ts +36 -0
  35. package/dist/src/core/controller-loop.js +292 -0
  36. package/dist/src/core/controller-loop.js.map +1 -0
  37. package/dist/src/core/errors.d.ts +10 -0
  38. package/dist/src/core/errors.js +18 -0
  39. package/dist/src/core/errors.js.map +1 -0
  40. package/dist/src/core/ids.d.ts +5 -0
  41. package/dist/src/core/ids.js +51 -0
  42. package/dist/src/core/ids.js.map +1 -0
  43. package/dist/src/core/runtime.d.ts +12 -0
  44. package/dist/src/core/runtime.js +35 -0
  45. package/dist/src/core/runtime.js.map +1 -0
  46. package/dist/src/core/state-machine.d.ts +28 -0
  47. package/dist/src/core/state-machine.js +99 -0
  48. package/dist/src/core/state-machine.js.map +1 -0
  49. package/dist/src/jobs/locks.d.ts +8 -0
  50. package/dist/src/jobs/locks.js +24 -0
  51. package/dist/src/jobs/locks.js.map +1 -0
  52. package/dist/src/jobs/status.d.ts +22 -0
  53. package/dist/src/jobs/status.js +53 -0
  54. package/dist/src/jobs/status.js.map +1 -0
  55. package/dist/src/jobs/supervisor.d.ts +22 -0
  56. package/dist/src/jobs/supervisor.js +96 -0
  57. package/dist/src/jobs/supervisor.js.map +1 -0
  58. package/dist/src/protocol/parse-command.d.ts +2 -0
  59. package/dist/src/protocol/parse-command.js +23 -0
  60. package/dist/src/protocol/parse-command.js.map +1 -0
  61. package/dist/src/protocol/types.d.ts +67 -0
  62. package/dist/src/protocol/types.js +2 -0
  63. package/dist/src/protocol/types.js.map +1 -0
  64. package/dist/src/protocol/validate-command.d.ts +2 -0
  65. package/dist/src/protocol/validate-command.js +181 -0
  66. package/dist/src/protocol/validate-command.js.map +1 -0
  67. package/dist/src/router/availability.d.ts +3 -0
  68. package/dist/src/router/availability.js +55 -0
  69. package/dist/src/router/availability.js.map +1 -0
  70. package/dist/src/router/config-loader.d.ts +3 -0
  71. package/dist/src/router/config-loader.js +92 -0
  72. package/dist/src/router/config-loader.js.map +1 -0
  73. package/dist/src/router/materialize.d.ts +8 -0
  74. package/dist/src/router/materialize.js +39 -0
  75. package/dist/src/router/materialize.js.map +1 -0
  76. package/dist/src/router/resolver.d.ts +6 -0
  77. package/dist/src/router/resolver.js +55 -0
  78. package/dist/src/router/resolver.js.map +1 -0
  79. package/dist/src/router/types.d.ts +24 -0
  80. package/dist/src/router/types.js +2 -0
  81. package/dist/src/router/types.js.map +1 -0
  82. package/dist/src/runners/process-runner.d.ts +15 -0
  83. package/dist/src/runners/process-runner.js +124 -0
  84. package/dist/src/runners/process-runner.js.map +1 -0
  85. package/dist/src/runners/registry.d.ts +17 -0
  86. package/dist/src/runners/registry.js +57 -0
  87. package/dist/src/runners/registry.js.map +1 -0
  88. package/dist/src/runners/runner-adapter.d.ts +32 -0
  89. package/dist/src/runners/runner-adapter.js +4 -0
  90. package/dist/src/runners/runner-adapter.js.map +1 -0
  91. package/dist/src/state/atomic-write.d.ts +1 -0
  92. package/dist/src/state/atomic-write.js +27 -0
  93. package/dist/src/state/atomic-write.js.map +1 -0
  94. package/dist/src/state/event-log.d.ts +8 -0
  95. package/dist/src/state/event-log.js +56 -0
  96. package/dist/src/state/event-log.js.map +1 -0
  97. package/dist/src/state/paths.d.ts +9 -0
  98. package/dist/src/state/paths.js +37 -0
  99. package/dist/src/state/paths.js.map +1 -0
  100. package/dist/src/state/store.d.ts +23 -0
  101. package/dist/src/state/store.js +108 -0
  102. package/dist/src/state/store.js.map +1 -0
  103. package/dist/src/version.d.ts +1 -0
  104. package/dist/src/version.js +2 -0
  105. package/dist/src/version.js.map +1 -0
  106. package/docs/architecture.md +83 -0
  107. package/docs/assets/README.md +51 -0
  108. package/docs/assets/cueline-banner-dark.svg +41 -0
  109. package/docs/assets/cueline-banner-light.svg +41 -0
  110. package/docs/assets/cueline-loop-en.svg +77 -0
  111. package/docs/assets/cueline-loop-ja.svg +77 -0
  112. package/docs/assets/cueline-loop-ko.svg +77 -0
  113. package/docs/assets/cueline-loop-zh-CN.svg +77 -0
  114. package/docs/assets/cueline-loop-zh-TW.svg +77 -0
  115. package/docs/assets/cueline-mark-dark.svg +10 -0
  116. package/docs/assets/cueline-mark-light.svg +10 -0
  117. package/docs/assets/cueline-wordmark-dark.svg +19 -0
  118. package/docs/assets/cueline-wordmark-light.svg +19 -0
  119. package/docs/compatibility.md +60 -0
  120. package/docs/controller-protocol.md +106 -0
  121. package/docs/provenance.md +10 -0
  122. package/docs/runner-contract.md +61 -0
  123. package/docs/state-and-recovery.md +67 -0
  124. package/evals/evals.json +41 -0
  125. package/install.sh +70 -0
  126. package/package.json +73 -0
  127. package/schemas/controller-command.schema.json +53 -0
  128. package/schemas/controller-observation.schema.json +32 -0
  129. package/skills/cueline/SKILL.md +80 -0
  130. package/skills/cueline/agents/openai.yaml +4 -0
@@ -0,0 +1,22 @@
1
+ import type { JobExecution, JobResult, JobResultStatus } from "../runners/runner-adapter.js";
2
+ export type JobStatusKind = "running" | JobResultStatus;
3
+ export interface JobStatus {
4
+ jobId: string;
5
+ execution: JobExecution;
6
+ status: JobStatusKind;
7
+ startedAt: string;
8
+ finishedAt?: string;
9
+ result?: JobResult;
10
+ error?: string;
11
+ }
12
+ /**
13
+ * Stores one atomically replaced JSON status file per job. The write format is
14
+ * deliberately small so foreground and background work share the same view.
15
+ */
16
+ export declare class JobStatusStore {
17
+ #private;
18
+ constructor(rootDirectory: string);
19
+ write(status: JobStatus): Promise<void>;
20
+ read(jobId: string): Promise<JobStatus | undefined>;
21
+ pathFor(jobId: string): string;
22
+ }
@@ -0,0 +1,53 @@
1
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { CueLineError } from "../core/errors.js";
4
+ import { runtimePidTag } from "../core/runtime.js";
5
+ function assertSafeJobId(jobId) {
6
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(jobId)) {
7
+ throw new CueLineError("JOB_ID_INVALID", "job id contains unsupported path characters", {
8
+ details: { jobId },
9
+ });
10
+ }
11
+ }
12
+ /**
13
+ * Stores one atomically replaced JSON status file per job. The write format is
14
+ * deliberately small so foreground and background work share the same view.
15
+ */
16
+ export class JobStatusStore {
17
+ #directory;
18
+ constructor(rootDirectory) {
19
+ this.#directory = path.resolve(rootDirectory, "jobs");
20
+ }
21
+ async write(status) {
22
+ assertSafeJobId(status.jobId);
23
+ await mkdir(this.#directory, { recursive: true });
24
+ const target = this.pathFor(status.jobId);
25
+ const temporary = `${target}.${runtimePidTag()}.${Date.now()}.tmp`;
26
+ await writeFile(temporary, `${JSON.stringify(status)}\n`, "utf8");
27
+ await rename(temporary, target);
28
+ }
29
+ async read(jobId) {
30
+ assertSafeJobId(jobId);
31
+ try {
32
+ const source = await readFile(this.pathFor(jobId), "utf8");
33
+ return JSON.parse(source);
34
+ }
35
+ catch (error) {
36
+ if (isNotFound(error)) {
37
+ return undefined;
38
+ }
39
+ throw new CueLineError("JOB_STATUS_READ_FAILED", `unable to read job status: ${jobId}`, {
40
+ cause: error,
41
+ details: { jobId },
42
+ });
43
+ }
44
+ }
45
+ pathFor(jobId) {
46
+ assertSafeJobId(jobId);
47
+ return path.join(this.#directory, `${jobId}.json`);
48
+ }
49
+ }
50
+ function isNotFound(error) {
51
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
52
+ }
53
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/jobs/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAenD,SAAS,eAAe,CAAC,KAAa;IACpC,IAAI,CAAC,6BAA6B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,YAAY,CAAC,gBAAgB,EAAE,6CAA6C,EAAE;YACtF,OAAO,EAAE,EAAE,KAAK,EAAE;SACnB,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,cAAc;IAChB,UAAU,CAAS;IAE5B,YAAY,aAAqB;QAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,MAAiB;QAC3B,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,GAAG,MAAM,IAAI,aAAa,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;QACnE,MAAM,SAAS,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAClE,MAAM,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAa;QACtB,eAAe,CAAC,KAAK,CAAC,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;YAC3D,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAc,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,MAAM,IAAI,YAAY,CAAC,wBAAwB,EAAE,8BAA8B,KAAK,EAAE,EAAE;gBACtF,KAAK,EAAE,KAAK;gBACZ,OAAO,EAAE,EAAE,KAAK,EAAE;aACnB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,CAAC,KAAa;QACnB,eAAe,CAAC,KAAK,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC;IACrD,CAAC;CACF;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AACnG,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { type RunnerAdapter, type RunnerSpec } from "../runners/runner-adapter.js";
2
+ import { JobLocks } from "./locks.js";
3
+ import { JobStatusStore, type JobStatus } from "./status.js";
4
+ export interface JobSupervisorOptions {
5
+ statusStore: JobStatusStore;
6
+ locks?: JobLocks;
7
+ }
8
+ /**
9
+ * Starts each job at most once. A background call returns its persisted
10
+ * running status; foreground calls wait for that exact same execution.
11
+ */
12
+ export declare class JobSupervisor {
13
+ #private;
14
+ private readonly runner;
15
+ private readonly options;
16
+ constructor(runner: RunnerAdapter, options: JobSupervisorOptions);
17
+ start(spec: RunnerSpec): Promise<JobStatus>;
18
+ waitForCompletion(jobId: string): Promise<JobStatus>;
19
+ inspect(jobId: string): Promise<JobStatus>;
20
+ private completeOnce;
21
+ private withResult;
22
+ }
@@ -0,0 +1,96 @@
1
+ import { CueLineError } from "../core/errors.js";
2
+ import { executionFor } from "../runners/runner-adapter.js";
3
+ import { JobLocks } from "./locks.js";
4
+ import { JobStatusStore } from "./status.js";
5
+ function errorText(error) {
6
+ return error instanceof Error ? error.message : String(error);
7
+ }
8
+ /**
9
+ * Starts each job at most once. A background call returns its persisted
10
+ * running status; foreground calls wait for that exact same execution.
11
+ */
12
+ export class JobSupervisor {
13
+ runner;
14
+ options;
15
+ #locks;
16
+ #completions = new Map();
17
+ constructor(runner, options) {
18
+ this.runner = runner;
19
+ this.options = options;
20
+ this.#locks = options.locks ?? new JobLocks();
21
+ }
22
+ async start(spec) {
23
+ const lock = this.#locks.acquire(spec.jobId);
24
+ const running = {
25
+ jobId: spec.jobId,
26
+ execution: executionFor(spec),
27
+ status: "running",
28
+ startedAt: new Date().toISOString(),
29
+ };
30
+ try {
31
+ await this.options.statusStore.write(running);
32
+ }
33
+ catch (error) {
34
+ lock.release();
35
+ throw error;
36
+ }
37
+ const completion = this.completeOnce(spec, running, lock.release);
38
+ this.#completions.set(spec.jobId, completion);
39
+ if (spec.background === true) {
40
+ void completion.catch(() => undefined);
41
+ return running;
42
+ }
43
+ return completion;
44
+ }
45
+ async waitForCompletion(jobId) {
46
+ const completion = this.#completions.get(jobId);
47
+ if (completion !== undefined) {
48
+ return completion;
49
+ }
50
+ const persisted = await this.options.statusStore.read(jobId);
51
+ if (persisted !== undefined) {
52
+ return persisted;
53
+ }
54
+ throw new CueLineError("JOB_NOT_FOUND", `no job status exists for: ${jobId}`, { details: { jobId } });
55
+ }
56
+ async inspect(jobId) {
57
+ const persisted = await this.options.statusStore.read(jobId);
58
+ if (persisted !== undefined)
59
+ return persisted;
60
+ throw new CueLineError("JOB_NOT_FOUND", `no job status exists for: ${jobId}`, {
61
+ details: { jobId },
62
+ });
63
+ }
64
+ async completeOnce(spec, running, release) {
65
+ let terminal;
66
+ try {
67
+ const result = await this.runner.run(spec);
68
+ terminal = this.withResult(running, result);
69
+ }
70
+ catch (error) {
71
+ terminal = {
72
+ ...running,
73
+ status: "failed",
74
+ finishedAt: new Date().toISOString(),
75
+ error: errorText(error),
76
+ };
77
+ }
78
+ try {
79
+ await this.options.statusStore.write(terminal);
80
+ return terminal;
81
+ }
82
+ finally {
83
+ this.#completions.delete(spec.jobId);
84
+ release();
85
+ }
86
+ }
87
+ withResult(running, result) {
88
+ return {
89
+ ...running,
90
+ status: result.status,
91
+ finishedAt: result.finishedAt,
92
+ result,
93
+ };
94
+ }
95
+ }
96
+ //# sourceMappingURL=supervisor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../../../src/jobs/supervisor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ControllerCommand, ExpectedControllerIdentity } from "./types.js";
2
+ export declare function parseControllerCommand(text: string, expected: ExpectedControllerIdentity): ControllerCommand;
@@ -0,0 +1,23 @@
1
+ import { CueLineError } from "../core/errors.js";
2
+ import { validateControllerCommand } from "./validate-command.js";
3
+ const CONTROL_ENVELOPE = /<CueLineControl>([\s\S]*?)<\/CueLineControl>/g;
4
+ export function parseControllerCommand(text, expected) {
5
+ let body;
6
+ for (const match of text.matchAll(CONTROL_ENVELOPE)) {
7
+ body = match[1];
8
+ }
9
+ if (body === undefined) {
10
+ throw new CueLineError("CONTROL_ENVELOPE_MISSING", "No complete <CueLineControl> envelope was found.");
11
+ }
12
+ let parsed;
13
+ try {
14
+ parsed = JSON.parse(body.trim());
15
+ }
16
+ catch (error) {
17
+ throw new CueLineError("CONTROL_JSON_INVALID", "Control envelope contains invalid JSON.", {
18
+ cause: error,
19
+ });
20
+ }
21
+ return validateControllerCommand(parsed, expected);
22
+ }
23
+ //# sourceMappingURL=parse-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-command.js","sourceRoot":"","sources":["../../../src/protocol/parse-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAElE,MAAM,gBAAgB,GAAG,+CAA+C,CAAC;AAEzE,MAAM,UAAU,sBAAsB,CACpC,IAAY,EACZ,QAAoC;IAEpC,IAAI,IAAwB,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACpD,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,YAAY,CACpB,0BAA0B,EAC1B,kDAAkD,CACnD,CAAC;IACJ,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,YAAY,CAAC,sBAAsB,EAAE,yCAAyC,EAAE;YACxF,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;IACD,OAAO,yBAAyB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,67 @@
1
+ export declare const CUELINE_PROTOCOL: "cueline/0.1";
2
+ export type JobMode = "advise" | "work";
3
+ export type ControllerAction = "dispatch" | "wait" | "inspect" | "complete" | "blocked";
4
+ export interface ExpectedControllerIdentity {
5
+ runId: string;
6
+ round: number;
7
+ requestId: string;
8
+ }
9
+ export interface ControllerJobSpec {
10
+ job_key: string;
11
+ lane: string;
12
+ mode: JobMode;
13
+ task: string;
14
+ required?: boolean;
15
+ timeout_ms?: number;
16
+ runner?: string;
17
+ workdir?: string;
18
+ background?: boolean;
19
+ }
20
+ interface ControllerCommandBase {
21
+ protocol: typeof CUELINE_PROTOCOL;
22
+ run_id: string;
23
+ round: number;
24
+ request_id: string;
25
+ action: ControllerAction;
26
+ }
27
+ export interface DispatchCommand extends ControllerCommandBase {
28
+ action: "dispatch";
29
+ jobs: ControllerJobSpec[];
30
+ }
31
+ export interface WaitCommand extends ControllerCommandBase {
32
+ action: "wait";
33
+ job_ids?: string[];
34
+ wait_ms?: number;
35
+ }
36
+ export interface InspectCommand extends ControllerCommandBase {
37
+ action: "inspect";
38
+ job_ids?: string[];
39
+ }
40
+ export interface CompleteCommand extends ControllerCommandBase {
41
+ action: "complete";
42
+ final_delivery_text: string;
43
+ }
44
+ export interface BlockedCommand extends ControllerCommandBase {
45
+ action: "blocked";
46
+ reason: string;
47
+ final_delivery_text?: string;
48
+ }
49
+ export type ControllerCommand = DispatchCommand | WaitCommand | InspectCommand | CompleteCommand | BlockedCommand;
50
+ export interface JobObservation {
51
+ job_id: string;
52
+ job_key: string;
53
+ required: boolean;
54
+ status: "pending" | "running" | "succeeded" | "failed" | "timed_out" | "ambiguous";
55
+ output?: string;
56
+ error?: string;
57
+ }
58
+ export interface ControllerObservation {
59
+ protocol: typeof CUELINE_PROTOCOL;
60
+ run_id: string;
61
+ round: number;
62
+ request_id: string;
63
+ user_request: string;
64
+ jobs: JobObservation[];
65
+ notices: string[];
66
+ }
67
+ export {};
@@ -0,0 +1,2 @@
1
+ export const CUELINE_PROTOCOL = "cueline/0.1";
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/protocol/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAsB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type ControllerCommand, type ExpectedControllerIdentity } from "./types.js";
2
+ export declare function validateControllerCommand(value: unknown, expected: ExpectedControllerIdentity): ControllerCommand;
@@ -0,0 +1,181 @@
1
+ import { CueLineError } from "../core/errors.js";
2
+ import { CUELINE_PROTOCOL, } from "./types.js";
3
+ const JOB_KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
4
+ const LANE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
5
+ function isRecord(value) {
6
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7
+ }
8
+ function fail(message, details) {
9
+ throw new CueLineError("CONTROL_COMMAND_INVALID", message, { details });
10
+ }
11
+ function requiredString(record, key) {
12
+ const value = record[key];
13
+ if (typeof value !== "string" || value.trim() === "") {
14
+ return fail(`'${key}' must be a non-empty string.`, { key });
15
+ }
16
+ return value;
17
+ }
18
+ function optionalString(record, key) {
19
+ const value = record[key];
20
+ if (value === undefined) {
21
+ return undefined;
22
+ }
23
+ if (typeof value !== "string" || value.trim() === "") {
24
+ return fail(`'${key}' must be a non-empty string when provided.`, { key });
25
+ }
26
+ return value;
27
+ }
28
+ function optionalBoolean(record, key) {
29
+ const value = record[key];
30
+ if (value === undefined) {
31
+ return undefined;
32
+ }
33
+ if (typeof value !== "boolean") {
34
+ return fail(`'${key}' must be a boolean when provided.`, { key });
35
+ }
36
+ return value;
37
+ }
38
+ function optionalPositiveInteger(record, key, maximum) {
39
+ const value = record[key];
40
+ if (value === undefined) {
41
+ return undefined;
42
+ }
43
+ if (!Number.isSafeInteger(value) || value < 1 || value > maximum) {
44
+ return fail(`'${key}' must be an integer from 1 to ${maximum}.`, { key });
45
+ }
46
+ return value;
47
+ }
48
+ function optionalStringArray(record, key) {
49
+ const value = record[key];
50
+ if (value === undefined) {
51
+ return undefined;
52
+ }
53
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item === "")) {
54
+ return fail(`'${key}' must be an array of non-empty strings.`, { key });
55
+ }
56
+ return [...value];
57
+ }
58
+ function validateJob(value) {
59
+ if (!isRecord(value)) {
60
+ return fail("Each dispatch job must be an object.");
61
+ }
62
+ const jobKey = requiredString(value, "job_key");
63
+ if (!JOB_KEY_PATTERN.test(jobKey)) {
64
+ return fail("'job_key' contains unsupported characters.", { jobKey });
65
+ }
66
+ const lane = requiredString(value, "lane");
67
+ if (!LANE_PATTERN.test(lane)) {
68
+ return fail("'lane' contains unsupported characters.", { lane });
69
+ }
70
+ const mode = requiredString(value, "mode");
71
+ if (mode !== "advise" && mode !== "work") {
72
+ throw new CueLineError("CONTROL_MODE_INVALID", `Unsupported job mode '${mode}'.`, {
73
+ details: { mode },
74
+ });
75
+ }
76
+ const job = {
77
+ job_key: jobKey,
78
+ lane,
79
+ mode,
80
+ task: requiredString(value, "task"),
81
+ };
82
+ const required = optionalBoolean(value, "required");
83
+ const timeout = optionalPositiveInteger(value, "timeout_ms", 86_400_000);
84
+ const runner = optionalString(value, "runner");
85
+ const workdir = optionalString(value, "workdir");
86
+ const background = optionalBoolean(value, "background");
87
+ if (required !== undefined)
88
+ job.required = required;
89
+ if (timeout !== undefined)
90
+ job.timeout_ms = timeout;
91
+ if (runner !== undefined)
92
+ job.runner = runner;
93
+ if (workdir !== undefined)
94
+ job.workdir = workdir;
95
+ if (background !== undefined)
96
+ job.background = background;
97
+ return job;
98
+ }
99
+ function validateIdentity(record, expected) {
100
+ if (record.run_id !== expected.runId ||
101
+ record.round !== expected.round ||
102
+ record.request_id !== expected.requestId) {
103
+ throw new CueLineError("CONTROL_ID_MISMATCH", "Controller command identity does not match the pending request.", {
104
+ details: {
105
+ expected,
106
+ received: {
107
+ runId: record.run_id,
108
+ round: record.round,
109
+ requestId: record.request_id,
110
+ },
111
+ },
112
+ });
113
+ }
114
+ }
115
+ export function validateControllerCommand(value, expected) {
116
+ if (!isRecord(value)) {
117
+ return fail("Controller command must be a JSON object.");
118
+ }
119
+ if (value.protocol !== CUELINE_PROTOCOL) {
120
+ return fail(`'protocol' must be '${CUELINE_PROTOCOL}'.`);
121
+ }
122
+ validateIdentity(value, expected);
123
+ const action = requiredString(value, "action");
124
+ const base = {
125
+ protocol: CUELINE_PROTOCOL,
126
+ run_id: expected.runId,
127
+ round: expected.round,
128
+ request_id: expected.requestId,
129
+ };
130
+ if (action === "dispatch") {
131
+ if (!Array.isArray(value.jobs) || value.jobs.length === 0) {
132
+ return fail("A dispatch command requires at least one job.");
133
+ }
134
+ const jobs = value.jobs.map(validateJob);
135
+ const seen = new Set();
136
+ for (const job of jobs) {
137
+ if (seen.has(job.job_key)) {
138
+ throw new CueLineError("CONTROL_DUPLICATE_JOB_KEY", `Duplicate job_key '${job.job_key}'.`, { details: { jobKey: job.job_key } });
139
+ }
140
+ seen.add(job.job_key);
141
+ }
142
+ return { ...base, action, jobs };
143
+ }
144
+ if (action === "wait") {
145
+ const command = { ...base, action };
146
+ const jobIds = optionalStringArray(value, "job_ids");
147
+ const waitMs = optionalPositiveInteger(value, "wait_ms", 300_000);
148
+ if (jobIds !== undefined)
149
+ command.job_ids = jobIds;
150
+ if (waitMs !== undefined)
151
+ command.wait_ms = waitMs;
152
+ return command;
153
+ }
154
+ if (action === "inspect") {
155
+ const command = { ...base, action };
156
+ const jobIds = optionalStringArray(value, "job_ids");
157
+ if (jobIds !== undefined)
158
+ command.job_ids = jobIds;
159
+ return command;
160
+ }
161
+ if (action === "complete") {
162
+ return {
163
+ ...base,
164
+ action,
165
+ final_delivery_text: requiredString(value, "final_delivery_text"),
166
+ };
167
+ }
168
+ if (action === "blocked") {
169
+ const command = {
170
+ ...base,
171
+ action,
172
+ reason: requiredString(value, "reason"),
173
+ };
174
+ const finalText = optionalString(value, "final_delivery_text");
175
+ if (finalText !== undefined)
176
+ command.final_delivery_text = finalText;
177
+ return command;
178
+ }
179
+ return fail(`Unsupported controller action '${action}'.`, { action });
180
+ }
181
+ //# sourceMappingURL=validate-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-command.js","sourceRoot":"","sources":["../../../src/protocol/validate-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EACL,gBAAgB,GASjB,MAAM,YAAY,CAAC;AAEpB,MAAM,eAAe,GAAG,mCAAmC,CAAC;AAC5D,MAAM,YAAY,GAAG,mCAAmC,CAAC;AAEzD,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,IAAI,CAAC,OAAe,EAAE,OAAiB;IAC9C,MAAM,IAAI,YAAY,CAAC,yBAAyB,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED,SAAS,cAAc,CAAC,MAA+B,EAAE,GAAW;IAClE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,OAAO,IAAI,CAAC,IAAI,GAAG,+BAA+B,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,cAAc,CAAC,MAA+B,EAAE,GAAW;IAClE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,OAAO,IAAI,CAAC,IAAI,GAAG,6CAA6C,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,eAAe,CAAC,MAA+B,EAAE,GAAW;IACnE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC,IAAI,GAAG,oCAAoC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,uBAAuB,CAC9B,MAA+B,EAC/B,GAAW,EACX,OAAe;IAEf,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAK,KAAgB,GAAG,CAAC,IAAK,KAAgB,GAAG,OAAO,EAAE,CAAC;QACzF,OAAO,IAAI,CAAC,IAAI,GAAG,kCAAkC,OAAO,GAAG,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,KAAe,CAAC;AACzB,CAAC;AAED,SAAS,mBAAmB,CAAC,MAA+B,EAAE,GAAW;IACvE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE,CAAC,EAAE,CAAC;QAC3F,OAAO,IAAI,CAAC,IAAI,GAAG,0CAA0C,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAa,CAAC;AAChC,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC,sCAAsC,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAChD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,4CAA4C,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC,yCAAyC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3C,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACzC,MAAM,IAAI,YAAY,CAAC,sBAAsB,EAAE,yBAAyB,IAAI,IAAI,EAAE;YAChF,OAAO,EAAE,EAAE,IAAI,EAAE;SAClB,CAAC,CAAC;IACL,CAAC;IAED,MAAM,GAAG,GAAsB;QAC7B,OAAO,EAAE,MAAM;QACf,IAAI;QACJ,IAAI;QACJ,IAAI,EAAE,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC;KACpC,CAAC;IACF,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,uBAAuB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,eAAe,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IACxD,IAAI,QAAQ,KAAK,SAAS;QAAE,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACpD,IAAI,OAAO,KAAK,SAAS;QAAE,GAAG,CAAC,UAAU,GAAG,OAAO,CAAC;IACpD,IAAI,MAAM,KAAK,SAAS;QAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;IAC9C,IAAI,OAAO,KAAK,SAAS;QAAE,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC;IACjD,IAAI,UAAU,KAAK,SAAS;QAAE,GAAG,CAAC,UAAU,GAAG,UAAU,CAAC;IAC1D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,gBAAgB,CACvB,MAA+B,EAC/B,QAAoC;IAEpC,IACE,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,KAAK;QAChC,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,KAAK;QAC/B,MAAM,CAAC,UAAU,KAAK,QAAQ,CAAC,SAAS,EACxC,CAAC;QACD,MAAM,IAAI,YAAY,CACpB,qBAAqB,EACrB,iEAAiE,EACjE;YACE,OAAO,EAAE;gBACP,QAAQ;gBACR,QAAQ,EAAE;oBACR,KAAK,EAAE,MAAM,CAAC,MAAM;oBACpB,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,SAAS,EAAE,MAAM,CAAC,UAAU;iBAC7B;aACF;SACF,CACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,KAAc,EACd,QAAoC;IAEpC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC,2CAA2C,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,KAAK,gBAAgB,EAAE,CAAC;QACxC,OAAO,IAAI,CAAC,uBAAuB,gBAAgB,IAAI,CAAC,CAAC;IAC3D,CAAC;IACD,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAElC,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG;QACX,QAAQ,EAAE,gBAAgB;QAC1B,MAAM,EAAE,QAAQ,CAAC,KAAK;QACtB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,UAAU,EAAE,QAAQ,CAAC,SAAS;KAC/B,CAAC;IAEF,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC,+CAA+C,CAAC,CAAC;QAC/D,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACzC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,YAAY,CACpB,2BAA2B,EAC3B,sBAAsB,GAAG,CAAC,OAAO,IAAI,EACrC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,EAAE,CACrC,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,EAA4B,CAAC;IAC7D,CAAC;IAED,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,MAAM,OAAO,GAAgB,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC;QACjD,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,uBAAuB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAClE,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC;QACnD,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC;QACnD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,OAAO,GAAmB,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QACrD,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC;QACnD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAC1B,OAAO;YACL,GAAG,IAAI;YACP,MAAM;YACN,mBAAmB,EAAE,cAAc,CAAC,KAAK,EAAE,qBAAqB,CAAC;SACxC,CAAC;IAC9B,CAAC;IAED,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,OAAO,GAAmB;YAC9B,GAAG,IAAI;YACP,MAAM;YACN,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC;SACxC,CAAC;QACF,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;QAC/D,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;QACrE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,IAAI,CAAC,kCAAkC,MAAM,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AACxE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CandidateAvailabilityChecker } from "./types.js";
2
+ export declare function findExecutable(command: string, environment?: NodeJS.ProcessEnv, cwd?: string): string | undefined;
3
+ export declare function executableAvailability(environment?: NodeJS.ProcessEnv, cwd?: string): CandidateAvailabilityChecker;
@@ -0,0 +1,55 @@
1
+ import { constants, accessSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { runtimeCwd, runtimeEnvironment, runtimePlatform } from "../core/runtime.js";
4
+ function executableNames(command, environment) {
5
+ if (runtimePlatform() !== "win32" || path.extname(command) !== "") {
6
+ return [command];
7
+ }
8
+ const extensions = (environment.PATHEXT ?? ".EXE;.CMD;.BAT;.COM")
9
+ .split(";")
10
+ .filter(Boolean);
11
+ return [command, ...extensions.map((extension) => `${command}${extension.toLowerCase()}`)];
12
+ }
13
+ function canExecute(filePath) {
14
+ try {
15
+ accessSync(filePath, runtimePlatform() === "win32" ? constants.F_OK : constants.X_OK);
16
+ return true;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ export function findExecutable(command, environment = runtimeEnvironment(), cwd = runtimeCwd()) {
23
+ if (command.trim() === "")
24
+ return undefined;
25
+ if (command.includes(path.sep) || (path.sep === "\\" && command.includes("/"))) {
26
+ const candidate = path.resolve(cwd, command);
27
+ return canExecute(candidate) ? candidate : undefined;
28
+ }
29
+ const directories = (environment.PATH ?? "").split(path.delimiter).filter(Boolean);
30
+ for (const directory of directories) {
31
+ for (const name of executableNames(command, environment)) {
32
+ const candidate = path.join(directory, name);
33
+ if (canExecute(candidate))
34
+ return candidate;
35
+ }
36
+ }
37
+ return undefined;
38
+ }
39
+ export function executableAvailability(environment = runtimeEnvironment(), cwd = runtimeCwd()) {
40
+ const cache = new Map();
41
+ return {
42
+ isAvailable(candidate) {
43
+ const executable = candidate.argv[0];
44
+ if (executable === undefined)
45
+ return false;
46
+ const cached = cache.get(executable);
47
+ if (cached !== undefined)
48
+ return cached;
49
+ const available = findExecutable(executable, environment, cwd) !== undefined;
50
+ cache.set(executable, available);
51
+ return available;
52
+ },
53
+ };
54
+ }
55
+ //# sourceMappingURL=availability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"availability.js","sourceRoot":"","sources":["../../../src/router/availability.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { RoutingConfig } from "./types.js";
2
+ export declare function parseRoutingConfig(value: unknown): RoutingConfig;
3
+ export declare function loadRoutingConfig(filePath: string): Promise<RoutingConfig>;