@kici-dev/compiler 0.1.26 → 0.2.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/dist/auth/headless-detect.d.ts +14 -5
- package/dist/auth/headless-detect.js +50 -9
- package/dist/cli-banner.d.ts +6 -3
- package/dist/cli-banner.js +13 -5
- package/dist/cli.js +180 -48
- package/dist/commands/check-mode.d.ts +1 -1
- package/dist/commands/compile.js +31 -7
- package/dist/commands/diagnostics.js +14 -2
- package/dist/commands/doctor.d.ts +64 -0
- package/dist/commands/doctor.js +413 -0
- package/dist/commands/endpoints.js +6 -0
- package/dist/commands/fixture.js +13 -0
- package/dist/commands/hook.js +3 -2
- package/dist/commands/index.d.ts +13 -1
- package/dist/commands/index.js +10 -3
- package/dist/commands/init-host-os.d.ts +34 -0
- package/dist/commands/init-host-os.js +44 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +160 -37
- package/dist/commands/local-trust-root.d.ts +6 -0
- package/dist/commands/local-trust-root.js +52 -0
- package/dist/commands/local.d.ts +65 -0
- package/dist/commands/local.js +206 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +41 -1
- package/dist/commands/logout.js +7 -0
- package/dist/commands/notifications.d.ts +61 -0
- package/dist/commands/notifications.js +274 -0
- package/dist/commands/preview.js +9 -2
- package/dist/commands/run-banner.d.ts +31 -0
- package/dist/commands/run-banner.js +25 -0
- package/dist/commands/run-routed.d.ts +53 -0
- package/dist/commands/run-routed.js +185 -0
- package/dist/commands/run.d.ts +0 -17
- package/dist/commands/run.js +44 -58
- package/dist/commands/runs/artifacts/download.d.ts +24 -0
- package/dist/commands/runs/artifacts/download.js +154 -0
- package/dist/commands/runs/artifacts/list.d.ts +5 -0
- package/dist/commands/runs/artifacts/list.js +47 -0
- package/dist/commands/runs/cancel.js +2 -2
- package/dist/commands/runs/list.js +8 -2
- package/dist/commands/secrets-list.d.ts +2 -2
- package/dist/commands/secrets-list.js +4 -4
- package/dist/commands/types.d.ts +3 -3
- package/dist/commands/types.js +4 -4
- package/dist/commands/verify-attestation.d.ts +3 -1
- package/dist/commands/verify-attestation.js +18 -7
- package/dist/errors/formatter.d.ts +14 -1
- package/dist/errors/formatter.js +13 -3
- package/dist/errors/index.d.ts +2 -1
- package/dist/errors/index.js +3 -2
- package/dist/errors/source-location.d.ts +20 -0
- package/dist/errors/source-location.js +45 -0
- package/dist/execution/executor.js +32 -12
- package/dist/fixtures/defaults/index.js +2 -1
- package/dist/generators/secrets-dts.d.ts +9 -9
- package/dist/generators/secrets-dts.js +12 -12
- package/dist/llm-context/llms-architecture.txt +50 -21
- package/dist/llm-context/llms-cli.txt +2667 -1454
- package/dist/llm-context/llms-features.txt +484 -414
- package/dist/llm-context/llms-full.txt +6703 -4687
- package/dist/llm-context/llms-getting-started.txt +311 -55
- package/dist/llm-context/llms-patterns.txt +17 -29
- package/dist/llm-context/llms-providers.txt +10 -1
- package/dist/llm-context/llms-sdk-runtime.txt +2013 -0
- package/dist/llm-context/llms-sdk.txt +596 -2157
- package/dist/llm-context/llms.txt +31 -17
- package/dist/local-plane/orchestrator-process.d.ts +70 -0
- package/dist/local-plane/orchestrator-process.js +129 -0
- package/dist/local-plane/os-mismatch-hint.d.ts +16 -0
- package/dist/local-plane/os-mismatch-hint.js +34 -0
- package/dist/local-plane/paths.d.ts +39 -0
- package/dist/local-plane/paths.js +54 -0
- package/dist/local-plane/plane-liveness.d.ts +68 -0
- package/dist/local-plane/plane-liveness.js +145 -0
- package/dist/local-plane/plane-manager.d.ts +193 -0
- package/dist/local-plane/plane-manager.js +480 -0
- package/dist/local-plane/plane-seed.d.ts +76 -0
- package/dist/local-plane/plane-seed.js +59 -0
- package/dist/local-plane/plane-trigger.d.ts +71 -0
- package/dist/local-plane/plane-trigger.js +110 -0
- package/dist/local-plane/platform-attach.d.ts +53 -0
- package/dist/local-plane/platform-attach.js +107 -0
- package/dist/local-plane/port-holder.d.ts +78 -0
- package/dist/local-plane/port-holder.js +227 -0
- package/dist/local-plane/postgres.d.ts +56 -0
- package/dist/local-plane/postgres.js +187 -0
- package/dist/local-plane/resolve-plane.d.ts +37 -0
- package/dist/local-plane/resolve-plane.js +76 -0
- package/dist/local-plane/run-follow.d.ts +61 -0
- package/dist/local-plane/run-follow.js +151 -0
- package/dist/local-plane/scaler-config.d.ts +68 -0
- package/dist/local-plane/scaler-config.js +132 -0
- package/dist/local-plane/secret-seed.d.ts +59 -0
- package/dist/local-plane/secret-seed.js +112 -0
- package/dist/local-plane/source-provider.d.ts +43 -0
- package/dist/local-plane/source-provider.js +159 -0
- package/dist/local-plane/trusted-routing.d.ts +32 -0
- package/dist/local-plane/trusted-routing.js +47 -0
- package/dist/lockfile/generator.d.ts +14 -1
- package/dist/lockfile/generator.js +103 -37
- package/dist/lockfile/index.d.ts +3 -1
- package/dist/lockfile/index.js +3 -2
- package/dist/lockfile/purity-diagnostics.d.ts +31 -0
- package/dist/lockfile/purity-diagnostics.js +52 -0
- package/dist/postinstall.js +2 -1
- package/dist/remote/artifact-extract.d.ts +8 -0
- package/dist/remote/artifact-extract.js +58 -0
- package/dist/remote/config.d.ts +21 -3
- package/dist/remote/config.js +24 -5
- package/dist/remote/dashboard-client.d.ts +46 -9
- package/dist/remote/dashboard-client.js +54 -11
- package/dist/remote/fs-case.d.ts +21 -0
- package/dist/remote/fs-case.js +45 -0
- package/dist/remote/history.d.ts +9 -3
- package/dist/remote/history.js +79 -10
- package/dist/remote/local-repo-identity.d.ts +1 -1
- package/dist/remote/local-repo-identity.js +2 -1
- package/dist/remote/notifications-client.d.ts +149 -0
- package/dist/remote/notifications-client.js +103 -0
- package/dist/remote/oauth.d.ts +13 -0
- package/dist/remote/oauth.js +31 -5
- package/dist/remote/output/summary.js +76 -14
- package/dist/remote/platform-client.d.ts +1 -1
- package/dist/remote/render.js +30 -9
- package/dist/{local-executor → remote}/secret-loader.d.ts +1 -1
- package/dist/{local-executor → remote}/secret-loader.js +4 -4
- package/dist/remote/secret-upload.d.ts +1 -1
- package/dist/remote/secret-upload.js +2 -2
- package/dist/remote/uploader.d.ts +1 -1
- package/dist/remote/uploader.js +7 -6
- package/dist/templates/agents-md.d.ts +1 -1
- package/dist/templates/agents-md.js +8 -8
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.js +2 -2
- package/dist/templates/package-json.d.ts +15 -0
- package/dist/templates/package-json.js +26 -5
- package/dist/test-runner/dry-run.d.ts +2 -1
- package/dist/test-runner/dry-run.js +18 -1
- package/dist/test-runner/event-types.d.ts +2 -0
- package/dist/test-runner/event-types.js +3 -0
- package/dist/test-runner/job-executor.d.ts +1 -0
- package/dist/test-runner/job-executor.js +82 -44
- package/dist/test-runner/output-formatter.d.ts +5 -2
- package/dist/test-runner/output-formatter.js +6 -4
- package/dist/test-runner/payload-builder.d.ts +7 -1
- package/dist/test-runner/payload-builder.js +11 -2
- package/dist/test-runner/rule-evaluator.d.ts +7 -3
- package/dist/test-runner/rule-evaluator.js +13 -12
- package/dist/{local-executor → test-runner}/runs-on-display.js +1 -1
- package/dist/test-runner/step-context.d.ts +23 -5
- package/dist/test-runner/step-context.js +29 -119
- package/dist/types.d.ts +26 -7
- package/dist/types.js +4 -2
- package/dist/validation/index.d.ts +2 -0
- package/dist/validation/index.js +2 -1
- package/dist/validation/typecheck.d.ts +28 -0
- package/dist/validation/typecheck.js +110 -0
- package/dist/validation/validator.d.ts +9 -4
- package/dist/validation/validator.js +38 -44
- package/package.json +13 -13
- package/sbom.spdx.json +9311 -1528
- package/dist/assets/api-TJJVHrjC.json +0 -118
- package/dist/assets/descriptor-BTtjzN9L.json +0 -1382
- package/dist/assets/package-BpQF9kR8.json +0 -74
- package/dist/assets/package-Ceo2h27X.json +0 -89
- package/dist/assets/source_context-D0atuL28.json +0 -20
- package/dist/assets/type-BFqO8SCZ.json +0 -202
- package/dist/commands/cancel.d.ts +0 -22
- package/dist/commands/detect-package-manager.d.ts +0 -42
- package/dist/commands/held-run-resolve.d.ts +0 -50
- package/dist/commands/status.d.ts +0 -34
- package/dist/commands/test.d.ts +0 -88
- package/dist/errors/codes.d.ts +0 -25
- package/dist/local-executor/dag-scheduler.d.ts +0 -44
- package/dist/local-executor/dag-scheduler.js +0 -183
- package/dist/local-executor/index.d.ts +0 -23
- package/dist/local-executor/index.js +0 -378
- package/dist/local-executor/job-runner.d.ts +0 -47
- package/dist/local-executor/job-runner.js +0 -411
- package/dist/local-executor/materializer.d.ts +0 -44
- package/dist/local-executor/materializer.js +0 -132
- package/dist/local-executor/output-streamer.d.ts +0 -31
- package/dist/local-executor/output-streamer.js +0 -168
- package/dist/local-executor/payload-generator.d.ts +0 -16
- package/dist/local-executor/payload-generator.js +0 -138
- package/dist/local-executor/picker.d.ts +0 -33
- package/dist/local-executor/picker.js +0 -109
- package/dist/local-executor/to-event-payload.d.ts +0 -16
- package/dist/local-executor/to-event-payload.js +0 -21
- package/dist/local-executor/types.d.ts +0 -93
- package/dist/local-executor/types.js +0 -2
- package/dist/local-executor/workflow-lock.d.ts +0 -82
- package/dist/local-executor/workflow-lock.js +0 -0
- package/dist/package-F7UXSDHW.json +0 -74
- package/dist/remote/client.d.ts +0 -210
- package/dist/remote/observer.d.ts +0 -81
- package/dist/test-runner/summary.d.ts +0 -7
- /package/dist/{local-executor → test-runner}/runs-on-display.d.ts +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
//#region src/local-plane/port-holder.ts
|
|
6
|
+
/**
|
|
7
|
+
* OS-level ownership of the local dev plane's port.
|
|
8
|
+
*
|
|
9
|
+
* Answers two questions the plane's on-disk stamp cannot: is the port occupied
|
|
10
|
+
* at all, and which process occupies it. Every external command is reached
|
|
11
|
+
* through an injectable `ExecFn` so each platform branch has a real unit test on
|
|
12
|
+
* any host, and every failure degrades to `null` rather than throwing — a
|
|
13
|
+
* discovery failure must never break the command that asked.
|
|
14
|
+
*/
|
|
15
|
+
const defaultExec = (cmd, args) => new Promise((resolve) => {
|
|
16
|
+
execFile(cmd, args, {
|
|
17
|
+
timeout: 5e3,
|
|
18
|
+
maxBuffer: 8 * 1024 * 1024
|
|
19
|
+
}, (err, stdout) => {
|
|
20
|
+
const raw = err?.code;
|
|
21
|
+
resolve({
|
|
22
|
+
stdout: stdout ?? "",
|
|
23
|
+
exitCode: typeof raw === "number" ? raw : err ? 1 : 0
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Command line of process `pid`, or null when it cannot be determined.
|
|
29
|
+
*
|
|
30
|
+
* Linux exposes it as a NUL-separated `/proc` file; every other platform is
|
|
31
|
+
* asked through the same injectable `ExecFn` the port discovery above uses.
|
|
32
|
+
*
|
|
33
|
+
* **A null means "cannot tell", never "not that process."** Callers must treat
|
|
34
|
+
* it as an absence of evidence — the only safe reading when the answer decides
|
|
35
|
+
* whether to signal a pid.
|
|
36
|
+
*/
|
|
37
|
+
async function processCommandLine(pid, opts = {}) {
|
|
38
|
+
const platform = opts.platform ?? process.platform;
|
|
39
|
+
try {
|
|
40
|
+
if (platform === "linux") {
|
|
41
|
+
const joined = (opts.readProc ?? ((p) => fs.readFileSync(p, "utf-8")))(`/proc/${pid}/cmdline`).split("\0").filter(Boolean).join(" ").trim();
|
|
42
|
+
return joined === "" ? null : joined;
|
|
43
|
+
}
|
|
44
|
+
if (platform === "win32") return null;
|
|
45
|
+
const r = await (opts.exec ?? defaultExec)("ps", [
|
|
46
|
+
"-o",
|
|
47
|
+
"args=",
|
|
48
|
+
"-p",
|
|
49
|
+
String(pid)
|
|
50
|
+
]);
|
|
51
|
+
if (r.exitCode !== 0) return null;
|
|
52
|
+
const line = r.stdout.trim();
|
|
53
|
+
return line === "" ? null : line;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Whether the port can be bound on loopback. Probes `127.0.0.1` because that is
|
|
60
|
+
* the address the CLI talks to; a listener bound to `0.0.0.0` still makes this
|
|
61
|
+
* bind fail with EADDRINUSE, so there is no false "free".
|
|
62
|
+
*/
|
|
63
|
+
function isPortFree(port) {
|
|
64
|
+
return new Promise((resolve) => {
|
|
65
|
+
const probe = net.createServer();
|
|
66
|
+
probe.once("error", () => resolve(false));
|
|
67
|
+
probe.listen({
|
|
68
|
+
port,
|
|
69
|
+
host: "127.0.0.1",
|
|
70
|
+
exclusive: true
|
|
71
|
+
}, () => {
|
|
72
|
+
probe.close(() => resolve(true));
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Port of a socket address column. The port is the segment after the LAST
|
|
78
|
+
* colon, because an IPv6 address is written `[::]:5355`.
|
|
79
|
+
*/
|
|
80
|
+
function addressPort(addr) {
|
|
81
|
+
const i = addr.lastIndexOf(":");
|
|
82
|
+
if (i < 0) return null;
|
|
83
|
+
const n = Number(addr.slice(i + 1));
|
|
84
|
+
return Number.isInteger(n) ? n : null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Parse `ss -lptnH` rows. The process name is deliberately NOT matched — a Node
|
|
88
|
+
* listener reports as `MainThread` — so only `pid=N` is extracted. A row for a
|
|
89
|
+
* socket this user does not own carries no `users:(…)` field at all and yields
|
|
90
|
+
* null, which is what sends the caller to the lsof fallback.
|
|
91
|
+
*/
|
|
92
|
+
function parseSsOutput(stdout, port) {
|
|
93
|
+
for (const line of stdout.split("\n")) {
|
|
94
|
+
const cols = line.trim().split(/\s+/);
|
|
95
|
+
if (cols.length < 4) continue;
|
|
96
|
+
if (addressPort(cols[3]) !== port) continue;
|
|
97
|
+
const m = /pid=(\d+)/.exec(line);
|
|
98
|
+
return m ? Number(m[1]) : null;
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Parse `lsof -Fpn` field output, which is a stateful stream: each `p<pid>` line
|
|
104
|
+
* precedes the `n<address>` lines belonging to that pid.
|
|
105
|
+
*/
|
|
106
|
+
function parseLsofOutput(stdout, port) {
|
|
107
|
+
let current = null;
|
|
108
|
+
for (const line of stdout.split("\n")) if (line.startsWith("p")) {
|
|
109
|
+
const n = Number(line.slice(1));
|
|
110
|
+
current = Number.isInteger(n) ? n : null;
|
|
111
|
+
} else if (line.startsWith("n") && current !== null) {
|
|
112
|
+
if (addressPort(line.slice(1)) === port) return current;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
/** Parse `netstat -ano -p tcp`: LISTENING rows only, pid in the last column. */
|
|
117
|
+
function parseNetstatOutput(stdout, port) {
|
|
118
|
+
for (const line of stdout.split("\n")) {
|
|
119
|
+
const cols = line.trim().split(/\s+/);
|
|
120
|
+
if (cols.length < 5) continue;
|
|
121
|
+
if (!/^LISTENING$/i.test(cols[3])) continue;
|
|
122
|
+
if (addressPort(cols[1]) !== port) continue;
|
|
123
|
+
const pid = Number(cols[cols.length - 1]);
|
|
124
|
+
return Number.isInteger(pid) ? pid : null;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Pid of the process listening on `port`, or null when it cannot be determined.
|
|
130
|
+
* Each platform runs the UNFILTERED listing and matches the port here rather
|
|
131
|
+
* than passing a filter expression to the tool, whose syntax is the fragile
|
|
132
|
+
* part. On Linux a null parse falls through to lsof as well as a non-zero exit,
|
|
133
|
+
* because ss omits the owning-process field for sockets this user does not own.
|
|
134
|
+
*/
|
|
135
|
+
async function findPortHolderPid(port, opts = {}) {
|
|
136
|
+
const exec = opts.exec ?? defaultExec;
|
|
137
|
+
const platform = opts.platform ?? process.platform;
|
|
138
|
+
const viaLsof = async () => {
|
|
139
|
+
const r = await exec("lsof", [
|
|
140
|
+
"-nP",
|
|
141
|
+
"-iTCP",
|
|
142
|
+
"-sTCP:LISTEN",
|
|
143
|
+
"-Fpn"
|
|
144
|
+
]);
|
|
145
|
+
return r.exitCode === 0 ? parseLsofOutput(r.stdout, port) : null;
|
|
146
|
+
};
|
|
147
|
+
try {
|
|
148
|
+
if (platform === "win32") {
|
|
149
|
+
const r = await exec("netstat", [
|
|
150
|
+
"-ano",
|
|
151
|
+
"-p",
|
|
152
|
+
"tcp"
|
|
153
|
+
]);
|
|
154
|
+
return r.exitCode === 0 ? parseNetstatOutput(r.stdout, port) : null;
|
|
155
|
+
}
|
|
156
|
+
if (platform === "linux") {
|
|
157
|
+
const r = await exec("ss", ["-lptnH"]);
|
|
158
|
+
return (r.exitCode === 0 ? parseSsOutput(r.stdout, port) : null) ?? await viaLsof();
|
|
159
|
+
}
|
|
160
|
+
return await viaLsof();
|
|
161
|
+
} catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const defaultKill = (pid, signal) => process.kill(pid, signal);
|
|
166
|
+
/** How long to keep watching for the process to disappear after SIGKILL. */
|
|
167
|
+
const HARD_KILL_WAIT_MS = 5e3;
|
|
168
|
+
/**
|
|
169
|
+
* Poll until process `pid` has exited, sending SIGTERM first and escalating to
|
|
170
|
+
* SIGKILL past the grace window. `process.kill(pid, 0)` throws ESRCH once the
|
|
171
|
+
* process is gone. On Windows every signal terminates unconditionally, so the
|
|
172
|
+
* escalation collapses into a single hard kill there.
|
|
173
|
+
*
|
|
174
|
+
* Resolving does NOT prove the process died: a pid that survives SIGKILL (a
|
|
175
|
+
* zombie awaiting reaping, or a task wedged in uninterruptible I/O) is given
|
|
176
|
+
* `hardKillWaitMs` and then abandoned, because a teardown command that hangs
|
|
177
|
+
* forever is worse than one that reports the port is still held. Callers decide
|
|
178
|
+
* the outcome from the port itself — `waitForPortFree` — never from this
|
|
179
|
+
* resolving.
|
|
180
|
+
*/
|
|
181
|
+
async function terminatePid(pid, graceMs = 1e4, opts = {}) {
|
|
182
|
+
const kill = opts.kill ?? defaultKill;
|
|
183
|
+
const hardKillWaitMs = opts.hardKillWaitMs ?? HARD_KILL_WAIT_MS;
|
|
184
|
+
try {
|
|
185
|
+
kill(pid, "SIGTERM");
|
|
186
|
+
} catch {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
let deadline = Date.now() + graceMs;
|
|
190
|
+
let killed = false;
|
|
191
|
+
for (;;) {
|
|
192
|
+
try {
|
|
193
|
+
kill(pid, 0);
|
|
194
|
+
} catch {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (Date.now() > deadline) {
|
|
198
|
+
if (killed) return;
|
|
199
|
+
try {
|
|
200
|
+
kill(pid, "SIGKILL");
|
|
201
|
+
} catch {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
killed = true;
|
|
205
|
+
deadline = Date.now() + hardKillWaitMs;
|
|
206
|
+
}
|
|
207
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Poll until the port is bindable. This, not `terminatePid` resolving, is what
|
|
212
|
+
* decides a teardown: `terminatePid` abandons a pid that survives SIGKILL, and
|
|
213
|
+
* even a process that did exit can leave its listening socket lingering a moment
|
|
214
|
+
* longer on some platforms.
|
|
215
|
+
*/
|
|
216
|
+
async function waitForPortFree(port, timeoutMs = 5e3) {
|
|
217
|
+
const deadline = Date.now() + timeoutMs;
|
|
218
|
+
for (;;) {
|
|
219
|
+
if (await isPortFree(port)) return true;
|
|
220
|
+
if (Date.now() >= deadline) return false;
|
|
221
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
export { findPortHolderPid, isPortFree, parseLsofOutput, parseNetstatOutput, parseSsOutput, processCommandLine, terminatePid, waitForPortFree };
|
|
226
|
+
|
|
227
|
+
//# sourceMappingURL=port-holder.js.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Podman fallback Postgres image for the local dev plane. Pinned via
|
|
3
|
+
* `TS_CONST_PINS` in hack/containers-lock.ts (identity
|
|
4
|
+
* docker.io/library/postgres+alpine), so a lock bump rewrites it in place.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PLANE_PG_IMAGE = "docker.io/library/postgres:18.4-alpine";
|
|
7
|
+
/** Name of the fallback podman Postgres container. */
|
|
8
|
+
export declare const PLANE_PG_CONTAINER = "kici-local-postgres";
|
|
9
|
+
export type PlanePgHandle = {
|
|
10
|
+
url: string;
|
|
11
|
+
kind: 'embedded' | 'podman';
|
|
12
|
+
stop(): Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
declare let readyPoller: typeof defaultReadyPoller;
|
|
15
|
+
/** Test seam: override the podman readiness poller. */
|
|
16
|
+
export declare function __setReadyPollerForTest(fn: typeof readyPoller): void;
|
|
17
|
+
declare let embeddedDaemon: typeof defaultEmbeddedDaemon;
|
|
18
|
+
/** Test seam: override the embedded postmaster daemonizer. */
|
|
19
|
+
export declare function __setEmbeddedDaemonForTest(fn: typeof embeddedDaemon): void;
|
|
20
|
+
declare function defaultReadyPoller(_port: number): Promise<boolean>;
|
|
21
|
+
/**
|
|
22
|
+
* Whether this plane's embedded postmaster is already serving `port`.
|
|
23
|
+
*
|
|
24
|
+
* `pg_ctl start` fails outright against a running cluster, so a boot that
|
|
25
|
+
* reclaims the plane port while leaving PostgreSQL up — reclaiming another
|
|
26
|
+
* plane's orchestrator does exactly that, since only the port holder is
|
|
27
|
+
* signalled — would otherwise fall through to the Podman path and fail there.
|
|
28
|
+
*
|
|
29
|
+
* The port comes from the cluster's own `postmaster.pid` (line 4) rather than
|
|
30
|
+
* being assumed, so a postmaster left on a different port is not mistaken for
|
|
31
|
+
* one this plane can reuse.
|
|
32
|
+
*/
|
|
33
|
+
export declare function embeddedClusterIsServing(port: number): Promise<boolean>;
|
|
34
|
+
/**
|
|
35
|
+
* Start a detached embedded postmaster via `pg_ctl` so it survives the exit of
|
|
36
|
+
* this CLI process (embedded-postgres's in-process server is killed by its own
|
|
37
|
+
* exit hook, so it cannot back a warm plane). A cluster already serving this
|
|
38
|
+
* plane's port is reused as-is.
|
|
39
|
+
*/
|
|
40
|
+
declare function defaultEmbeddedDaemon(port: number): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Stop the plane's Postgres by backend kind. Handle-independent so a separate
|
|
43
|
+
* CLI invocation (`kici local down`) can tear down what `up` started.
|
|
44
|
+
*/
|
|
45
|
+
export declare function stopPlanePostgres(kind: 'embedded' | 'podman'): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Provision the local dev plane's Postgres. Prefers the zero-dependency
|
|
48
|
+
* `embedded-postgres` binary (daemonized via pg_ctl so it stays warm); falls
|
|
49
|
+
* back to a podman Postgres container when the embedded binary is unavailable
|
|
50
|
+
* (or when forced via `forcePodman` / `KICI_LOCAL_PG_MODE=podman`).
|
|
51
|
+
*/
|
|
52
|
+
export declare function startPlanePostgres(opts?: {
|
|
53
|
+
forcePodman?: boolean;
|
|
54
|
+
}): Promise<PlanePgHandle>;
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { planePaths, planePorts } from "./paths.js";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import { $ } from "zx";
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
import EmbeddedPostgres from "embedded-postgres";
|
|
10
|
+
//#region src/local-plane/postgres.ts
|
|
11
|
+
/**
|
|
12
|
+
* Podman fallback Postgres image for the local dev plane. Pinned via
|
|
13
|
+
* `TS_CONST_PINS` in hack/containers-lock.ts (identity
|
|
14
|
+
* docker.io/library/postgres+alpine), so a lock bump rewrites it in place.
|
|
15
|
+
*/
|
|
16
|
+
const PLANE_PG_IMAGE = "docker.io/library/postgres:18.4-alpine";
|
|
17
|
+
/** Name of the fallback podman Postgres container. */
|
|
18
|
+
const PLANE_PG_CONTAINER = "kici-local-postgres";
|
|
19
|
+
let readyPoller = defaultReadyPoller;
|
|
20
|
+
/** Test seam: override the podman readiness poller. */
|
|
21
|
+
function __setReadyPollerForTest(fn) {
|
|
22
|
+
readyPoller = fn;
|
|
23
|
+
}
|
|
24
|
+
let embeddedDaemon = defaultEmbeddedDaemon;
|
|
25
|
+
/** Test seam: override the embedded postmaster daemonizer. */
|
|
26
|
+
function __setEmbeddedDaemonForTest(fn) {
|
|
27
|
+
embeddedDaemon = fn;
|
|
28
|
+
}
|
|
29
|
+
async function defaultReadyPoller(_port) {
|
|
30
|
+
for (let i = 0; i < 60; i++) try {
|
|
31
|
+
await $`podman exec ${PLANE_PG_CONTAINER} pg_isready -U kici`.quiet();
|
|
32
|
+
return true;
|
|
33
|
+
} catch {
|
|
34
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the platform-specific `pg_ctl` binary bundled with embedded-postgres.
|
|
40
|
+
* Mirrors the package's own platform → binary-package mapping, resolving the
|
|
41
|
+
* binary package through embedded-postgres's module context (pnpm isolates it
|
|
42
|
+
* as a transitive dependency, so it is not directly resolvable here) and
|
|
43
|
+
* computing the `native/bin/pg_ctl` path from the resolved package directory.
|
|
44
|
+
*/
|
|
45
|
+
function resolvePgCtl() {
|
|
46
|
+
const platform = os.platform();
|
|
47
|
+
const arch = os.arch();
|
|
48
|
+
const pkg = {
|
|
49
|
+
"darwin:arm64": "@embedded-postgres/darwin-arm64",
|
|
50
|
+
"darwin:x64": "@embedded-postgres/darwin-x64",
|
|
51
|
+
"linux:arm64": "@embedded-postgres/linux-arm64",
|
|
52
|
+
"linux:arm": "@embedded-postgres/linux-arm",
|
|
53
|
+
"linux:ia32": "@embedded-postgres/linux-ia32",
|
|
54
|
+
"linux:ppc64": "@embedded-postgres/linux-ppc64",
|
|
55
|
+
"linux:x64": "@embedded-postgres/linux-x64",
|
|
56
|
+
"win32:x64": "@embedded-postgres/windows-x64"
|
|
57
|
+
}[`${platform}:${arch}`];
|
|
58
|
+
if (!pkg) throw new Error(`unsupported platform for embedded Postgres: ${platform}/${arch}`);
|
|
59
|
+
const entry = createRequire(createRequire(import.meta.url).resolve("embedded-postgres")).resolve(pkg);
|
|
60
|
+
const binName = platform === "win32" ? "pg_ctl.exe" : "pg_ctl";
|
|
61
|
+
return path.resolve(path.dirname(entry), "..", "native", "bin", binName);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Initialise the embedded Postgres cluster + the `kici_local` database once. A
|
|
65
|
+
* short-lived in-process server is used only for the one-time bootstrap; the
|
|
66
|
+
* persistent postmaster is started separately (daemonized) so it outlives this
|
|
67
|
+
* CLI invocation.
|
|
68
|
+
*/
|
|
69
|
+
async function ensureEmbeddedCluster(port) {
|
|
70
|
+
const { pgData } = planePaths();
|
|
71
|
+
if (fs.existsSync(path.join(pgData, "PG_VERSION"))) return;
|
|
72
|
+
const pg = new EmbeddedPostgres({
|
|
73
|
+
databaseDir: pgData,
|
|
74
|
+
port,
|
|
75
|
+
user: "kici",
|
|
76
|
+
password: "kici",
|
|
77
|
+
persistent: true
|
|
78
|
+
});
|
|
79
|
+
await pg.initialise();
|
|
80
|
+
await pg.start();
|
|
81
|
+
await pg.createDatabase("kici_local").catch(() => {});
|
|
82
|
+
await pg.stop();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Whether this plane's embedded postmaster is already serving `port`.
|
|
86
|
+
*
|
|
87
|
+
* `pg_ctl start` fails outright against a running cluster, so a boot that
|
|
88
|
+
* reclaims the plane port while leaving PostgreSQL up — reclaiming another
|
|
89
|
+
* plane's orchestrator does exactly that, since only the port holder is
|
|
90
|
+
* signalled — would otherwise fall through to the Podman path and fail there.
|
|
91
|
+
*
|
|
92
|
+
* The port comes from the cluster's own `postmaster.pid` (line 4) rather than
|
|
93
|
+
* being assumed, so a postmaster left on a different port is not mistaken for
|
|
94
|
+
* one this plane can reuse.
|
|
95
|
+
*/
|
|
96
|
+
async function embeddedClusterIsServing(port) {
|
|
97
|
+
const { pgData } = planePaths();
|
|
98
|
+
const pidFile = path.join(pgData, "postmaster.pid");
|
|
99
|
+
let runningPort;
|
|
100
|
+
try {
|
|
101
|
+
runningPort = Number(fs.readFileSync(pidFile, "utf-8").split("\n")[3]);
|
|
102
|
+
} catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (runningPort !== port) return false;
|
|
106
|
+
try {
|
|
107
|
+
await $`${resolvePgCtl()} -D ${pgData} status`.quiet();
|
|
108
|
+
return true;
|
|
109
|
+
} catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Start a detached embedded postmaster via `pg_ctl` so it survives the exit of
|
|
115
|
+
* this CLI process (embedded-postgres's in-process server is killed by its own
|
|
116
|
+
* exit hook, so it cannot back a warm plane). A cluster already serving this
|
|
117
|
+
* plane's port is reused as-is.
|
|
118
|
+
*/
|
|
119
|
+
async function defaultEmbeddedDaemon(port) {
|
|
120
|
+
const { pgData, logFile } = planePaths();
|
|
121
|
+
if (await embeddedClusterIsServing(port)) return;
|
|
122
|
+
await $`${resolvePgCtl()} -D ${pgData} -o ${`-p ${port}`} -l ${`${logFile}.pg`} -w start`.quiet();
|
|
123
|
+
}
|
|
124
|
+
/** Stop the detached embedded postmaster (handle-independent, reads the data dir). */
|
|
125
|
+
async function stopEmbeddedDaemon() {
|
|
126
|
+
const { pgData } = planePaths();
|
|
127
|
+
if (!fs.existsSync(path.join(pgData, "postmaster.pid"))) return;
|
|
128
|
+
await $`${resolvePgCtl()} -D ${pgData} stop -m fast`.quiet().catch(() => {});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Stop the plane's Postgres by backend kind. Handle-independent so a separate
|
|
132
|
+
* CLI invocation (`kici local down`) can tear down what `up` started.
|
|
133
|
+
*/
|
|
134
|
+
async function stopPlanePostgres(kind) {
|
|
135
|
+
if (kind === "podman") await $`podman rm -f ${PLANE_PG_CONTAINER}`.quiet().catch(() => {});
|
|
136
|
+
else await stopEmbeddedDaemon();
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Provision the local dev plane's Postgres. Prefers the zero-dependency
|
|
140
|
+
* `embedded-postgres` binary (daemonized via pg_ctl so it stays warm); falls
|
|
141
|
+
* back to a podman Postgres container when the embedded binary is unavailable
|
|
142
|
+
* (or when forced via `forcePodman` / `KICI_LOCAL_PG_MODE=podman`).
|
|
143
|
+
*/
|
|
144
|
+
async function startPlanePostgres(opts = {}) {
|
|
145
|
+
const { postgres: port } = planePorts();
|
|
146
|
+
const url = `postgres://kici:kici@127.0.0.1:${port}/kici_local`;
|
|
147
|
+
if (!(opts.forcePodman || process.env.KICI_LOCAL_PG_MODE === "podman")) try {
|
|
148
|
+
await ensureEmbeddedCluster(port);
|
|
149
|
+
await embeddedDaemon(port);
|
|
150
|
+
return {
|
|
151
|
+
url,
|
|
152
|
+
kind: "embedded",
|
|
153
|
+
stop: () => stopEmbeddedDaemon()
|
|
154
|
+
};
|
|
155
|
+
} catch {}
|
|
156
|
+
spawn("podman", [
|
|
157
|
+
"run",
|
|
158
|
+
"-d",
|
|
159
|
+
"--replace",
|
|
160
|
+
"--name",
|
|
161
|
+
PLANE_PG_CONTAINER,
|
|
162
|
+
"-p",
|
|
163
|
+
`127.0.0.1:${port}:5432`,
|
|
164
|
+
"-e",
|
|
165
|
+
"POSTGRES_USER=kici",
|
|
166
|
+
"-e",
|
|
167
|
+
"POSTGRES_PASSWORD=kici",
|
|
168
|
+
"-e",
|
|
169
|
+
"POSTGRES_DB=kici_local",
|
|
170
|
+
PLANE_PG_IMAGE
|
|
171
|
+
], {
|
|
172
|
+
stdio: "ignore",
|
|
173
|
+
detached: true
|
|
174
|
+
}).unref();
|
|
175
|
+
if (!await readyPoller(port)) throw new Error("local Postgres (podman) did not become ready");
|
|
176
|
+
return {
|
|
177
|
+
url,
|
|
178
|
+
kind: "podman",
|
|
179
|
+
stop: async () => {
|
|
180
|
+
await $`podman rm -f ${PLANE_PG_CONTAINER}`.quiet().catch(() => {});
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
export { PLANE_PG_CONTAINER, PLANE_PG_IMAGE, __setEmbeddedDaemonForTest, __setReadyPollerForTest, embeddedClusterIsServing, startPlanePostgres, stopPlanePostgres };
|
|
186
|
+
|
|
187
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plane auto-selection for `kici run --local` (design §5).
|
|
3
|
+
*
|
|
4
|
+
* Rules:
|
|
5
|
+
* - `--offline` → force the independent (offline) plane.
|
|
6
|
+
* - `--connected` → require an attached plane; hard-error if not attached, and
|
|
7
|
+
* hard-error if the Platform is unreachable (the user explicitly demanded it).
|
|
8
|
+
* - neither flag → attached + Platform reachable → hybrid; attached but the
|
|
9
|
+
* Platform is unreachable → fall back to the independent plane with a loud
|
|
10
|
+
* banner; not attached → offline.
|
|
11
|
+
*
|
|
12
|
+
* The fallback is the "mid-session Platform-unreachable" guarantee: a `--local`
|
|
13
|
+
* run never fails just because the Platform is unreachable (unless `--connected`).
|
|
14
|
+
* It re-boots/keeps the independent plane and continues on local secrets +
|
|
15
|
+
* dev-signed identity.
|
|
16
|
+
*/
|
|
17
|
+
import { type PlaneStatus } from './plane-manager.js';
|
|
18
|
+
export interface ResolvePlaneFlags {
|
|
19
|
+
offline?: boolean;
|
|
20
|
+
connected?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export type ResolvedPlane = {
|
|
23
|
+
kind: 'offline';
|
|
24
|
+
plane: PlaneStatus;
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'attached';
|
|
27
|
+
plane: PlaneStatus;
|
|
28
|
+
orgId: string;
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'fallback';
|
|
31
|
+
plane: PlaneStatus;
|
|
32
|
+
reason: string;
|
|
33
|
+
} | {
|
|
34
|
+
error: string;
|
|
35
|
+
};
|
|
36
|
+
export declare function resolvePlaneForRun(flags: ResolvePlaneFlags): Promise<ResolvedPlane>;
|
|
37
|
+
//# sourceMappingURL=resolve-plane.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { probePlatformReachable } from "./platform-attach.js";
|
|
3
|
+
import { planeUp, readAttachment, readPlatformToken } from "./plane-manager.js";
|
|
4
|
+
//#region src/local-plane/resolve-plane.ts
|
|
5
|
+
/**
|
|
6
|
+
* Plane auto-selection for `kici run --local` (design §5).
|
|
7
|
+
*
|
|
8
|
+
* Rules:
|
|
9
|
+
* - `--offline` → force the independent (offline) plane.
|
|
10
|
+
* - `--connected` → require an attached plane; hard-error if not attached, and
|
|
11
|
+
* hard-error if the Platform is unreachable (the user explicitly demanded it).
|
|
12
|
+
* - neither flag → attached + Platform reachable → hybrid; attached but the
|
|
13
|
+
* Platform is unreachable → fall back to the independent plane with a loud
|
|
14
|
+
* banner; not attached → offline.
|
|
15
|
+
*
|
|
16
|
+
* The fallback is the "mid-session Platform-unreachable" guarantee: a `--local`
|
|
17
|
+
* run never fails just because the Platform is unreachable (unless `--connected`).
|
|
18
|
+
* It re-boots/keeps the independent plane and continues on local secrets +
|
|
19
|
+
* dev-signed identity.
|
|
20
|
+
*/
|
|
21
|
+
/** Boot the hybrid plane from the durable attachment record + persisted token. */
|
|
22
|
+
async function bootAttached(attachment) {
|
|
23
|
+
const token = readPlatformToken();
|
|
24
|
+
if (!token) return { error: "The plane is attached but its Platform token is missing — run `kici local attach` to re-attach." };
|
|
25
|
+
return planeUp({ attach: {
|
|
26
|
+
platformWsUrl: attachment.platformWsUrl,
|
|
27
|
+
platformToken: token,
|
|
28
|
+
platformApiBase: attachment.platformApiBase,
|
|
29
|
+
orgId: attachment.orgId,
|
|
30
|
+
keyId: attachment.keyId
|
|
31
|
+
} });
|
|
32
|
+
}
|
|
33
|
+
async function resolvePlaneForRun(flags) {
|
|
34
|
+
const attachment = readAttachment();
|
|
35
|
+
if (flags.offline) return {
|
|
36
|
+
kind: "offline",
|
|
37
|
+
plane: await planeUp()
|
|
38
|
+
};
|
|
39
|
+
if (flags.connected) {
|
|
40
|
+
if (!attachment) return { error: "Not attached to the Platform. Run `kici local attach` first (or drop --connected)." };
|
|
41
|
+
if (!await probePlatformReachable(attachment.platformApiBase)) return { error: `Platform ${attachment.platformApiBase} is unreachable — cannot honor --connected.` };
|
|
42
|
+
const booted = await bootAttached(attachment);
|
|
43
|
+
if ("error" in booted) return booted;
|
|
44
|
+
return {
|
|
45
|
+
kind: "attached",
|
|
46
|
+
plane: booted,
|
|
47
|
+
orgId: attachment.orgId
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (!attachment) return {
|
|
51
|
+
kind: "offline",
|
|
52
|
+
plane: await planeUp()
|
|
53
|
+
};
|
|
54
|
+
if (await probePlatformReachable(attachment.platformApiBase)) {
|
|
55
|
+
const booted = await bootAttached(attachment);
|
|
56
|
+
if ("error" in booted) return {
|
|
57
|
+
kind: "fallback",
|
|
58
|
+
plane: await planeUp(),
|
|
59
|
+
reason: booted.error
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
kind: "attached",
|
|
63
|
+
plane: booted,
|
|
64
|
+
orgId: attachment.orgId
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
kind: "fallback",
|
|
69
|
+
plane: await planeUp(),
|
|
70
|
+
reason: "Platform unreachable"
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { resolvePlaneForRun };
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=resolve-plane.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Follow an offline routed run against the local plane by polling the
|
|
3
|
+
* orchestrator admin API (no SSE in independent mode). Streams step log lines as
|
|
4
|
+
* they arrive and resolves when the run reaches a terminal state.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the acceptance window, falling back to the default for any value that
|
|
8
|
+
* is not a positive number of milliseconds.
|
|
9
|
+
*
|
|
10
|
+
* A malformed override must never reach the deadline arithmetic: `Number('')`
|
|
11
|
+
* is `0` (every run fails instantly) and `Number('2 min')` is `NaN`, which makes
|
|
12
|
+
* `now > deadline` permanently false and silently restores the 900 s hang this
|
|
13
|
+
* window exists to prevent. Both failure modes are worse than ignoring a typo.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveAcceptanceTimeoutMs(explicitMs: number | undefined, raw: string | undefined): number;
|
|
16
|
+
/** A minimal admin-read client (AdminApiClient.get) — injectable for tests. */
|
|
17
|
+
export interface RunFollowClient {
|
|
18
|
+
get<T>(path: string): Promise<T>;
|
|
19
|
+
}
|
|
20
|
+
/** Terminal outcome of a followed run. */
|
|
21
|
+
export interface RunOutcome {
|
|
22
|
+
runId: string;
|
|
23
|
+
status: string;
|
|
24
|
+
jobs: Array<{
|
|
25
|
+
name: string;
|
|
26
|
+
status: string;
|
|
27
|
+
durationMs?: number;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
export interface FollowRunOptions {
|
|
31
|
+
onLine?: (line: string) => void;
|
|
32
|
+
quiet?: boolean;
|
|
33
|
+
client?: RunFollowClient;
|
|
34
|
+
pollIntervalMs?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Idle window: the max time with NO observed progress (no new log line, no run
|
|
37
|
+
* status change) before the follow gives up. Resets on every progress tick, so
|
|
38
|
+
* a legitimately long run (e.g. a full `deploy:stg`, tens of minutes of image
|
|
39
|
+
* builds + host mutation) never times out while it is actively advancing —
|
|
40
|
+
* only a genuinely stalled run does. Default 15 min. `timeoutMs` is a
|
|
41
|
+
* back-compat alias.
|
|
42
|
+
*/
|
|
43
|
+
idleTimeoutMs?: number;
|
|
44
|
+
/** @deprecated Back-compat alias for `idleTimeoutMs`. */
|
|
45
|
+
timeoutMs?: number;
|
|
46
|
+
/** Absolute cap regardless of progress (safety net against a runaway follow). Default 2 h. */
|
|
47
|
+
maxTotalMs?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Max time for the FIRST job to leave `pending`/`queued` — that is, for some
|
|
50
|
+
* agent to claim the run. A plane whose scaler matches no label set, or whose
|
|
51
|
+
* agent cannot start, never gets past this, so it fails in seconds instead of
|
|
52
|
+
* consuming the whole idle window. Default 2 min; override with
|
|
53
|
+
* `KICI_LOCAL_ACCEPTANCE_TIMEOUT_MS`.
|
|
54
|
+
*/
|
|
55
|
+
acceptanceTimeoutMs?: number;
|
|
56
|
+
/** Plane log path named in the acceptance-failure message. */
|
|
57
|
+
hintLogPath?: string;
|
|
58
|
+
}
|
|
59
|
+
/** Poll a run to completion, streaming step logs. */
|
|
60
|
+
export declare function followRun(planeUrl: string, adminToken: string, runId: string, opts?: FollowRunOptions): Promise<RunOutcome>;
|
|
61
|
+
//# sourceMappingURL=run-follow.d.ts.map
|