@kici-dev/compiler 0.1.26 → 0.1.27

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 (96) hide show
  1. package/dist/cli-banner.d.ts +6 -3
  2. package/dist/cli-banner.js +13 -5
  3. package/dist/cli.js +66 -43
  4. package/dist/commands/check-mode.d.ts +1 -1
  5. package/dist/commands/compile.js +3 -3
  6. package/dist/commands/index.d.ts +5 -1
  7. package/dist/commands/index.js +6 -3
  8. package/dist/commands/init.js +1 -1
  9. package/dist/commands/local-trust-root.d.ts +6 -0
  10. package/dist/commands/local-trust-root.js +52 -0
  11. package/dist/commands/local.d.ts +39 -0
  12. package/dist/commands/local.js +137 -0
  13. package/dist/commands/login.d.ts +2 -0
  14. package/dist/commands/login.js +40 -0
  15. package/dist/commands/logout.js +7 -0
  16. package/dist/commands/preview.js +1 -1
  17. package/dist/commands/run-banner.d.ts +31 -0
  18. package/dist/commands/run-banner.js +25 -0
  19. package/dist/commands/run-routed.d.ts +53 -0
  20. package/dist/commands/run-routed.js +176 -0
  21. package/dist/commands/run.d.ts +0 -17
  22. package/dist/commands/run.js +7 -50
  23. package/dist/commands/secrets-list.d.ts +2 -2
  24. package/dist/commands/secrets-list.js +4 -4
  25. package/dist/commands/types.d.ts +3 -3
  26. package/dist/commands/types.js +4 -4
  27. package/dist/execution/executor.js +7 -7
  28. package/dist/generators/secrets-dts.d.ts +9 -9
  29. package/dist/generators/secrets-dts.js +12 -12
  30. package/dist/llm-context/llms-architecture.txt +2 -2
  31. package/dist/llm-context/llms-cli.txt +674 -340
  32. package/dist/llm-context/llms-features.txt +334 -336
  33. package/dist/llm-context/llms-full.txt +1207 -892
  34. package/dist/llm-context/llms-getting-started.txt +7 -15
  35. package/dist/llm-context/llms-patterns.txt +5 -23
  36. package/dist/llm-context/llms-providers.txt +10 -1
  37. package/dist/llm-context/llms-sdk.txt +12 -12
  38. package/dist/llm-context/llms.txt +5 -5
  39. package/dist/local-executor/secret-loader.d.ts +1 -1
  40. package/dist/local-plane/orchestrator-process.d.ts +65 -0
  41. package/dist/local-plane/orchestrator-process.js +115 -0
  42. package/dist/local-plane/paths.d.ts +39 -0
  43. package/dist/local-plane/paths.js +54 -0
  44. package/dist/local-plane/plane-manager.d.ts +151 -0
  45. package/dist/local-plane/plane-manager.js +340 -0
  46. package/dist/local-plane/plane-seed.d.ts +76 -0
  47. package/dist/local-plane/plane-seed.js +59 -0
  48. package/dist/local-plane/plane-trigger.d.ts +62 -0
  49. package/dist/local-plane/plane-trigger.js +97 -0
  50. package/dist/local-plane/platform-attach.d.ts +53 -0
  51. package/dist/local-plane/platform-attach.js +111 -0
  52. package/dist/local-plane/postgres.d.ts +44 -0
  53. package/dist/local-plane/postgres.js +165 -0
  54. package/dist/local-plane/resolve-plane.d.ts +37 -0
  55. package/dist/local-plane/resolve-plane.js +76 -0
  56. package/dist/local-plane/run-follow.d.ts +41 -0
  57. package/dist/local-plane/run-follow.js +104 -0
  58. package/dist/local-plane/scaler-config.d.ts +54 -0
  59. package/dist/local-plane/scaler-config.js +114 -0
  60. package/dist/local-plane/secret-seed.d.ts +59 -0
  61. package/dist/local-plane/secret-seed.js +112 -0
  62. package/dist/local-plane/source-provider.d.ts +43 -0
  63. package/dist/local-plane/source-provider.js +165 -0
  64. package/dist/local-plane/trusted-routing.d.ts +32 -0
  65. package/dist/local-plane/trusted-routing.js +47 -0
  66. package/dist/lockfile/generator.js +7 -7
  67. package/dist/remote/dashboard-client.d.ts +7 -7
  68. package/dist/remote/dashboard-client.js +7 -7
  69. package/dist/remote/platform-client.d.ts +1 -1
  70. package/dist/remote/secret-loader.d.ts +18 -0
  71. package/dist/{local-executor → remote}/secret-loader.js +4 -4
  72. package/dist/remote/secret-upload.d.ts +1 -1
  73. package/dist/remote/secret-upload.js +2 -2
  74. package/dist/remote/uploader.d.ts +1 -1
  75. package/dist/remote/uploader.js +2 -2
  76. package/dist/templates/agents-md.d.ts +1 -1
  77. package/dist/templates/agents-md.js +2 -2
  78. package/dist/templates/package-json.js +1 -1
  79. package/dist/test-runner/job-executor.js +1 -1
  80. package/dist/test-runner/runs-on-display.d.ts +9 -0
  81. package/dist/{local-executor → test-runner}/runs-on-display.js +1 -1
  82. package/dist/test-runner/step-context.d.ts +1 -1
  83. package/dist/test-runner/step-context.js +2 -2
  84. package/dist/types.d.ts +6 -6
  85. package/package.json +7 -4
  86. package/sbom.spdx.json +9884 -1459
  87. package/dist/local-executor/dag-scheduler.js +0 -183
  88. package/dist/local-executor/index.js +0 -378
  89. package/dist/local-executor/job-runner.js +0 -411
  90. package/dist/local-executor/materializer.js +0 -132
  91. package/dist/local-executor/output-streamer.js +0 -168
  92. package/dist/local-executor/payload-generator.js +0 -138
  93. package/dist/local-executor/picker.js +0 -109
  94. package/dist/local-executor/to-event-payload.js +0 -21
  95. package/dist/local-executor/types.js +0 -2
  96. package/dist/local-executor/workflow-lock.js +0 -0
@@ -0,0 +1,97 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ import { randomUUID } from "node:crypto";
3
+ import { AdminApiClient } from "@kici-dev/orchestrator";
4
+ //#region src/local-plane/plane-trigger.ts
5
+ /**
6
+ * Trigger an offline routed run against the local plane and discover its runId.
7
+ *
8
+ * A local repo has no forge to send a webhook, so this posts a GitHub-shaped
9
+ * synthetic push to the plane's provider-agnostic generic webhook route (the
10
+ * same wire shape the `kici-admin source trigger-local` path builds). The
11
+ * webhook is unauthenticated (the edge accepts it and returns 202); the run is
12
+ * created asynchronously, so this then polls the admin runs list — authenticated
13
+ * with the plane's bootstrap admin token — to resolve the created runId.
14
+ *
15
+ * The source's bundle hot-reload is debounced, so a first trigger can land
16
+ * before the plane has registered the (re-pointed) local source. This resends
17
+ * the webhook after a grace window until a run appears or the timeout elapses.
18
+ */
19
+ /**
20
+ * Build the GitHub-shaped webhook request the plane's local provider normalizer
21
+ * expects. Mirrors `buildLocalTriggerRequest` in the orchestrator's
22
+ * `cli/commands/local-trigger.ts` (kept local to honour the compiler-only scope
23
+ * of this phase — no orchestrator export is added).
24
+ */
25
+ function buildLocalTriggerRequest(input) {
26
+ const body = JSON.stringify({
27
+ ref: input.ref,
28
+ after: input.sha,
29
+ repository: {
30
+ full_name: input.repoFullName,
31
+ default_branch: input.defaultBranch
32
+ },
33
+ ...input.event === "dispatch" && {
34
+ action: input.action,
35
+ client_payload: input.clientPayload ?? {}
36
+ }
37
+ });
38
+ return {
39
+ path: `/webhook/${input.orgId}/generic/${input.sourceId}`,
40
+ headers: {
41
+ "content-type": "application/json",
42
+ "x-event-type": input.event,
43
+ "x-delivery-id": randomUUID()
44
+ },
45
+ body
46
+ };
47
+ }
48
+ /** POST the trigger request to the plane. Returns the HTTP status. */
49
+ async function sendLocalTrigger(planeUrl, req) {
50
+ return (await fetch(`${planeUrl.replace(/\/$/, "")}${req.path}`, {
51
+ method: "POST",
52
+ headers: req.headers,
53
+ body: req.body
54
+ })).status;
55
+ }
56
+ /**
57
+ * Trigger the run and resolve its runId. Sends the synthetic push, then polls
58
+ * the admin runs list (`created_at > since`) for the newest run, resending the
59
+ * webhook past a grace window to absorb source hot-reload latency.
60
+ */
61
+ async function triggerRun(planeUrl, adminToken, input, opts = {}) {
62
+ const client = opts.client ?? new AdminApiClient(planeUrl, adminToken);
63
+ const pollIntervalMs = opts.pollIntervalMs ?? 750;
64
+ const resendAfterMs = opts.resendAfterMs ?? 8e3;
65
+ const timeoutMs = opts.timeoutMs ?? 6e4;
66
+ const since = /* @__PURE__ */ new Date(Date.now() - 3e3);
67
+ const req = buildLocalTriggerRequest(input);
68
+ await sendLocalTrigger(planeUrl, req);
69
+ let lastSend = Date.now();
70
+ const deadline = Date.now() + timeoutMs;
71
+ while (Date.now() < deadline) {
72
+ const runId = await findLatestRunSince(client, since);
73
+ if (runId) return runId;
74
+ if (Date.now() - lastSend > resendAfterMs) {
75
+ await sendLocalTrigger(planeUrl, req);
76
+ lastSend = Date.now();
77
+ }
78
+ await sleep(pollIntervalMs);
79
+ }
80
+ throw new Error("offline run: no run appeared after triggering the local plane");
81
+ }
82
+ /** Return the newest run created after `since`, or null when none yet. */
83
+ async function findLatestRunSince(client, since) {
84
+ const qs = new URLSearchParams({
85
+ since: since.toISOString(),
86
+ limit: "5"
87
+ });
88
+ const { runs } = await client.get(`/api/v1/admin/runs?${qs}`);
89
+ return runs[0]?.runId ?? null;
90
+ }
91
+ function sleep(ms) {
92
+ return new Promise((r) => setTimeout(r, ms));
93
+ }
94
+ //#endregion
95
+ export { buildLocalTriggerRequest, sendLocalTrigger, triggerRun };
96
+
97
+ //# sourceMappingURL=plane-trigger.js.map
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Platform attach helpers for the local dev plane. Pure HTTP/URL helpers with no
3
+ * plane side-effects: derive the orchestrator WS relay URL from the CLI's HTTPS
4
+ * API base, mint/revoke an org-scoped orchestrator key via the Platform API
5
+ * using the logged-in personal access token, and probe Platform reachability.
6
+ *
7
+ * The minted key (prefix `kici_ok_`) is what the plane presents on WS auth when
8
+ * it boots hybrid. It is org-scoped and created under the calling user's own
9
+ * `api_keys:write` permission — attach never mints anything the user could not
10
+ * mint from the dashboard.
11
+ */
12
+ /**
13
+ * Derive the orchestrator WS relay URL from the CLI's HTTPS Platform API base.
14
+ * `https://…` → `wss://…`, `http://…` → `ws://…`, then ensure a single trailing
15
+ * `/ws` path segment (idempotent when the base already ends in `/ws`).
16
+ */
17
+ export declare function derivePlatformWsUrl(apiBase: string): string;
18
+ export interface MintedOrchestratorKey {
19
+ /** Raw orchestrator key (kici_ok_…) — present once. NEVER log this. */
20
+ key: string;
21
+ /** Platform api_keys id (for later revoke). */
22
+ keyId: string;
23
+ keyPrefix: string;
24
+ }
25
+ /**
26
+ * Mint an org-scoped orchestrator key via the Platform HTTP API using the PAT.
27
+ * `POST ${apiBase}/api/v1/orgs/${orgId}/orchestrator-keys`.
28
+ */
29
+ export declare function mintOrchestratorKey(args: {
30
+ apiBase: string;
31
+ pat: string;
32
+ orgId: string;
33
+ name?: string;
34
+ }): Promise<MintedOrchestratorKey>;
35
+ /**
36
+ * Best-effort revoke of a previously-minted orchestrator key on detach.
37
+ * `DELETE ${apiBase}/api/v1/orgs/${orgId}/orchestrator-keys/${keyId}`. Never
38
+ * throws — a failed revoke (offline, expired PAT) is non-fatal; the key is
39
+ * org-scoped and the local plane stops presenting it regardless.
40
+ */
41
+ export declare function revokeOrchestratorKey(args: {
42
+ apiBase: string;
43
+ pat: string;
44
+ orgId: string;
45
+ keyId: string;
46
+ }): Promise<boolean>;
47
+ /**
48
+ * Bounded reachability probe against the Platform API base. Returns true when
49
+ * the base responds (any HTTP status — a reachable Platform, even a 401/404 on
50
+ * `/health`, proves the network path). A timeout / network error is false.
51
+ */
52
+ export declare function probePlatformReachable(apiBase: string, timeoutMs?: number): Promise<boolean>;
53
+ //# sourceMappingURL=platform-attach.d.ts.map
@@ -0,0 +1,111 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ import { toErrorMessage } from "@kici-dev/core";
3
+ //#region src/local-plane/platform-attach.ts
4
+ /**
5
+ * Platform attach helpers for the local dev plane. Pure HTTP/URL helpers with no
6
+ * plane side-effects: derive the orchestrator WS relay URL from the CLI's HTTPS
7
+ * API base, mint/revoke an org-scoped orchestrator key via the Platform API
8
+ * using the logged-in personal access token, and probe Platform reachability.
9
+ *
10
+ * The minted key (prefix `kici_ok_`) is what the plane presents on WS auth when
11
+ * it boots hybrid. It is org-scoped and created under the calling user's own
12
+ * `api_keys:write` permission — attach never mints anything the user could not
13
+ * mint from the dashboard.
14
+ */
15
+ /**
16
+ * Derive the orchestrator WS relay URL from the CLI's HTTPS Platform API base.
17
+ * `https://…` → `wss://…`, `http://…` → `ws://…`, then ensure a single trailing
18
+ * `/ws` path segment (idempotent when the base already ends in `/ws`).
19
+ */
20
+ function derivePlatformWsUrl(apiBase) {
21
+ let url;
22
+ try {
23
+ url = new URL(apiBase);
24
+ } catch {
25
+ throw new Error(`Invalid Platform API base URL: ${apiBase}`);
26
+ }
27
+ if (url.protocol === "https:") url.protocol = "wss:";
28
+ else if (url.protocol === "http:") url.protocol = "ws:";
29
+ else throw new Error(`Platform API base must be http(s): ${apiBase}`);
30
+ const trimmed = url.pathname.replace(/\/+$/, "");
31
+ url.pathname = trimmed.endsWith("/ws") ? trimmed : `${trimmed}/ws`;
32
+ url.search = "";
33
+ url.hash = "";
34
+ return url.toString();
35
+ }
36
+ /**
37
+ * Mint an org-scoped orchestrator key via the Platform HTTP API using the PAT.
38
+ * `POST ${apiBase}/api/v1/orgs/${orgId}/orchestrator-keys`.
39
+ */
40
+ async function mintOrchestratorKey(args) {
41
+ const url = `${args.apiBase.replace(/\/+$/, "")}/api/v1/orgs/${encodeURIComponent(args.orgId)}/orchestrator-keys`;
42
+ let res;
43
+ try {
44
+ res = await fetch(url, {
45
+ method: "POST",
46
+ headers: {
47
+ Authorization: `Bearer ${args.pat}`,
48
+ "Content-Type": "application/json"
49
+ },
50
+ body: JSON.stringify({ name: args.name ?? `local dev plane (${orgHostLabel()})` })
51
+ });
52
+ } catch (err) {
53
+ throw new Error(`Could not reach the Platform to mint an orchestrator key: ${toErrorMessage(err)}`);
54
+ }
55
+ if (res.status === 401) throw new Error("Platform rejected the credentials (401). Run `kici login` to re-authenticate.");
56
+ if (res.status === 403) throw new Error(`Your account lacks permission to create orchestrator keys in org ${args.orgId} (needs the api_keys:write permission — an Owner can grant it).`);
57
+ if (!res.ok) throw new Error(`Platform returned ${res.status} minting the orchestrator key.`);
58
+ const body = await res.json();
59
+ if (!body.key || !body.id) throw new Error("Platform orchestrator-key response was missing the key or id.");
60
+ return {
61
+ key: body.key,
62
+ keyId: body.id,
63
+ keyPrefix: body.keyPrefix ?? "kici_ok_"
64
+ };
65
+ }
66
+ /**
67
+ * Best-effort revoke of a previously-minted orchestrator key on detach.
68
+ * `DELETE ${apiBase}/api/v1/orgs/${orgId}/orchestrator-keys/${keyId}`. Never
69
+ * throws — a failed revoke (offline, expired PAT) is non-fatal; the key is
70
+ * org-scoped and the local plane stops presenting it regardless.
71
+ */
72
+ async function revokeOrchestratorKey(args) {
73
+ const url = `${args.apiBase.replace(/\/+$/, "")}/api/v1/orgs/${encodeURIComponent(args.orgId)}/orchestrator-keys/${encodeURIComponent(args.keyId)}`;
74
+ try {
75
+ return (await fetch(url, {
76
+ method: "DELETE",
77
+ headers: { Authorization: `Bearer ${args.pat}` }
78
+ })).ok;
79
+ } catch {
80
+ return false;
81
+ }
82
+ }
83
+ /**
84
+ * Bounded reachability probe against the Platform API base. Returns true when
85
+ * the base responds (any HTTP status — a reachable Platform, even a 401/404 on
86
+ * `/health`, proves the network path). A timeout / network error is false.
87
+ */
88
+ async function probePlatformReachable(apiBase, timeoutMs = 4e3) {
89
+ const controller = new AbortController();
90
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
91
+ try {
92
+ const url = `${apiBase.replace(/\/+$/, "")}/health`;
93
+ return (await fetch(url, { signal: controller.signal })).status > 0;
94
+ } catch {
95
+ return false;
96
+ } finally {
97
+ clearTimeout(timer);
98
+ }
99
+ }
100
+ /** A short, non-sensitive label for the minted key name (the machine host). */
101
+ function orgHostLabel() {
102
+ try {
103
+ return process.env.HOSTNAME || "kici-local";
104
+ } catch {
105
+ return "kici-local";
106
+ }
107
+ }
108
+ //#endregion
109
+ export { derivePlatformWsUrl, mintOrchestratorKey, probePlatformReachable, revokeOrchestratorKey };
110
+
111
+ //# sourceMappingURL=platform-attach.js.map
@@ -0,0 +1,44 @@
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
+ /** Whether a podman binary is available on PATH. */
22
+ export declare function isPodmanAvailable(): Promise<boolean>;
23
+ /**
24
+ * Start a detached embedded postmaster via `pg_ctl` so it survives the exit of
25
+ * this CLI process (embedded-postgres's in-process server is killed by its own
26
+ * exit hook, so it cannot back a warm plane).
27
+ */
28
+ declare function defaultEmbeddedDaemon(port: number): Promise<void>;
29
+ /**
30
+ * Stop the plane's Postgres by backend kind. Handle-independent so a separate
31
+ * CLI invocation (`kici local down`) can tear down what `up` started.
32
+ */
33
+ export declare function stopPlanePostgres(kind: 'embedded' | 'podman'): Promise<void>;
34
+ /**
35
+ * Provision the local dev plane's Postgres. Prefers the zero-dependency
36
+ * `embedded-postgres` binary (daemonized via pg_ctl so it stays warm); falls
37
+ * back to a podman Postgres container when the embedded binary is unavailable
38
+ * (or when forced via `forcePodman` / `KICI_LOCAL_PG_MODE=podman`).
39
+ */
40
+ export declare function startPlanePostgres(opts?: {
41
+ forcePodman?: boolean;
42
+ }): Promise<PlanePgHandle>;
43
+ export {};
44
+ //# sourceMappingURL=postgres.d.ts.map
@@ -0,0 +1,165 @@
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 { spawn } from "node:child_process";
7
+ import os from "node:os";
8
+ import { $ } from "zx";
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
+ /** Whether a podman binary is available on PATH. */
39
+ async function isPodmanAvailable() {
40
+ try {
41
+ await $`podman --version`.quiet();
42
+ return true;
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+ /**
48
+ * Resolve the platform-specific `pg_ctl` binary bundled with embedded-postgres.
49
+ * Mirrors the package's own platform → binary-package mapping, resolving the
50
+ * binary package through embedded-postgres's module context (pnpm isolates it
51
+ * as a transitive dependency, so it is not directly resolvable here) and
52
+ * computing the `native/bin/pg_ctl` path from the resolved package directory.
53
+ */
54
+ function resolvePgCtl() {
55
+ const platform = os.platform();
56
+ const arch = os.arch();
57
+ const pkg = {
58
+ "darwin:arm64": "@embedded-postgres/darwin-arm64",
59
+ "darwin:x64": "@embedded-postgres/darwin-x64",
60
+ "linux:arm64": "@embedded-postgres/linux-arm64",
61
+ "linux:arm": "@embedded-postgres/linux-arm",
62
+ "linux:ia32": "@embedded-postgres/linux-ia32",
63
+ "linux:ppc64": "@embedded-postgres/linux-ppc64",
64
+ "linux:x64": "@embedded-postgres/linux-x64",
65
+ "win32:x64": "@embedded-postgres/windows-x64"
66
+ }[`${platform}:${arch}`];
67
+ if (!pkg) throw new Error(`unsupported platform for embedded Postgres: ${platform}/${arch}`);
68
+ const entry = createRequire(createRequire(import.meta.url).resolve("embedded-postgres")).resolve(pkg);
69
+ const binName = platform === "win32" ? "pg_ctl.exe" : "pg_ctl";
70
+ return path.resolve(path.dirname(entry), "..", "native", "bin", binName);
71
+ }
72
+ /**
73
+ * Initialise the embedded Postgres cluster + the `kici_local` database once. A
74
+ * short-lived in-process server is used only for the one-time bootstrap; the
75
+ * persistent postmaster is started separately (daemonized) so it outlives this
76
+ * CLI invocation.
77
+ */
78
+ async function ensureEmbeddedCluster(port) {
79
+ const { pgData } = planePaths();
80
+ if (fs.existsSync(path.join(pgData, "PG_VERSION"))) return;
81
+ const pg = new EmbeddedPostgres({
82
+ databaseDir: pgData,
83
+ port,
84
+ user: "kici",
85
+ password: "kici",
86
+ persistent: true
87
+ });
88
+ await pg.initialise();
89
+ await pg.start();
90
+ await pg.createDatabase("kici_local").catch(() => {});
91
+ await pg.stop();
92
+ }
93
+ /**
94
+ * Start a detached embedded postmaster via `pg_ctl` so it survives the exit of
95
+ * this CLI process (embedded-postgres's in-process server is killed by its own
96
+ * exit hook, so it cannot back a warm plane).
97
+ */
98
+ async function defaultEmbeddedDaemon(port) {
99
+ const { pgData, logFile } = planePaths();
100
+ await $`${resolvePgCtl()} -D ${pgData} -o ${`-p ${port}`} -l ${`${logFile}.pg`} -w start`.quiet();
101
+ }
102
+ /** Stop the detached embedded postmaster (handle-independent, reads the data dir). */
103
+ async function stopEmbeddedDaemon() {
104
+ const { pgData } = planePaths();
105
+ if (!fs.existsSync(path.join(pgData, "postmaster.pid"))) return;
106
+ await $`${resolvePgCtl()} -D ${pgData} stop -m fast`.quiet().catch(() => {});
107
+ }
108
+ /**
109
+ * Stop the plane's Postgres by backend kind. Handle-independent so a separate
110
+ * CLI invocation (`kici local down`) can tear down what `up` started.
111
+ */
112
+ async function stopPlanePostgres(kind) {
113
+ if (kind === "podman") await $`podman rm -f ${PLANE_PG_CONTAINER}`.quiet().catch(() => {});
114
+ else await stopEmbeddedDaemon();
115
+ }
116
+ /**
117
+ * Provision the local dev plane's Postgres. Prefers the zero-dependency
118
+ * `embedded-postgres` binary (daemonized via pg_ctl so it stays warm); falls
119
+ * back to a podman Postgres container when the embedded binary is unavailable
120
+ * (or when forced via `forcePodman` / `KICI_LOCAL_PG_MODE=podman`).
121
+ */
122
+ async function startPlanePostgres(opts = {}) {
123
+ const { postgres: port } = planePorts();
124
+ const url = `postgres://kici:kici@127.0.0.1:${port}/kici_local`;
125
+ if (!(opts.forcePodman || process.env.KICI_LOCAL_PG_MODE === "podman")) try {
126
+ await ensureEmbeddedCluster(port);
127
+ await embeddedDaemon(port);
128
+ return {
129
+ url,
130
+ kind: "embedded",
131
+ stop: () => stopEmbeddedDaemon()
132
+ };
133
+ } catch {}
134
+ spawn("podman", [
135
+ "run",
136
+ "-d",
137
+ "--replace",
138
+ "--name",
139
+ PLANE_PG_CONTAINER,
140
+ "-p",
141
+ `127.0.0.1:${port}:5432`,
142
+ "-e",
143
+ "POSTGRES_USER=kici",
144
+ "-e",
145
+ "POSTGRES_PASSWORD=kici",
146
+ "-e",
147
+ "POSTGRES_DB=kici_local",
148
+ PLANE_PG_IMAGE
149
+ ], {
150
+ stdio: "ignore",
151
+ detached: true
152
+ }).unref();
153
+ if (!await readyPoller(port)) throw new Error("local Postgres (podman) did not become ready");
154
+ return {
155
+ url,
156
+ kind: "podman",
157
+ stop: async () => {
158
+ await $`podman rm -f ${PLANE_PG_CONTAINER}`.quiet().catch(() => {});
159
+ }
160
+ };
161
+ }
162
+ //#endregion
163
+ export { PLANE_PG_CONTAINER, PLANE_PG_IMAGE, __setEmbeddedDaemonForTest, __setReadyPollerForTest, isPodmanAvailable, startPlanePostgres, stopPlanePostgres };
164
+
165
+ //# 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,41 @@
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
+ /** A minimal admin-read client (AdminApiClient.get) — injectable for tests. */
7
+ export interface RunFollowClient {
8
+ get<T>(path: string): Promise<T>;
9
+ }
10
+ /** Terminal outcome of a followed run. */
11
+ export interface RunOutcome {
12
+ runId: string;
13
+ status: string;
14
+ jobs: Array<{
15
+ name: string;
16
+ status: string;
17
+ durationMs?: number;
18
+ }>;
19
+ }
20
+ export interface FollowRunOptions {
21
+ onLine?: (line: string) => void;
22
+ quiet?: boolean;
23
+ client?: RunFollowClient;
24
+ pollIntervalMs?: number;
25
+ /**
26
+ * Idle window: the max time with NO observed progress (no new log line, no run
27
+ * status change) before the follow gives up. Resets on every progress tick, so
28
+ * a legitimately long run (e.g. a full `deploy:stg`, tens of minutes of image
29
+ * builds + host mutation) never times out while it is actively advancing —
30
+ * only a genuinely stalled run does. Default 15 min. `timeoutMs` is a
31
+ * back-compat alias.
32
+ */
33
+ idleTimeoutMs?: number;
34
+ /** @deprecated Back-compat alias for `idleTimeoutMs`. */
35
+ timeoutMs?: number;
36
+ /** Absolute cap regardless of progress (safety net against a runaway follow). Default 2 h. */
37
+ maxTotalMs?: number;
38
+ }
39
+ /** Poll a run to completion, streaming step logs. */
40
+ export declare function followRun(planeUrl: string, adminToken: string, runId: string, opts?: FollowRunOptions): Promise<RunOutcome>;
41
+ //# sourceMappingURL=run-follow.d.ts.map