@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.
Files changed (199) hide show
  1. package/dist/auth/headless-detect.d.ts +14 -5
  2. package/dist/auth/headless-detect.js +50 -9
  3. package/dist/cli-banner.d.ts +6 -3
  4. package/dist/cli-banner.js +13 -5
  5. package/dist/cli.js +180 -48
  6. package/dist/commands/check-mode.d.ts +1 -1
  7. package/dist/commands/compile.js +31 -7
  8. package/dist/commands/diagnostics.js +14 -2
  9. package/dist/commands/doctor.d.ts +64 -0
  10. package/dist/commands/doctor.js +413 -0
  11. package/dist/commands/endpoints.js +6 -0
  12. package/dist/commands/fixture.js +13 -0
  13. package/dist/commands/hook.js +3 -2
  14. package/dist/commands/index.d.ts +13 -1
  15. package/dist/commands/index.js +10 -3
  16. package/dist/commands/init-host-os.d.ts +34 -0
  17. package/dist/commands/init-host-os.js +44 -0
  18. package/dist/commands/init.d.ts +4 -0
  19. package/dist/commands/init.js +160 -37
  20. package/dist/commands/local-trust-root.d.ts +6 -0
  21. package/dist/commands/local-trust-root.js +52 -0
  22. package/dist/commands/local.d.ts +65 -0
  23. package/dist/commands/local.js +206 -0
  24. package/dist/commands/login.d.ts +2 -0
  25. package/dist/commands/login.js +41 -1
  26. package/dist/commands/logout.js +7 -0
  27. package/dist/commands/notifications.d.ts +61 -0
  28. package/dist/commands/notifications.js +274 -0
  29. package/dist/commands/preview.js +9 -2
  30. package/dist/commands/run-banner.d.ts +31 -0
  31. package/dist/commands/run-banner.js +25 -0
  32. package/dist/commands/run-routed.d.ts +53 -0
  33. package/dist/commands/run-routed.js +185 -0
  34. package/dist/commands/run.d.ts +0 -17
  35. package/dist/commands/run.js +44 -58
  36. package/dist/commands/runs/artifacts/download.d.ts +24 -0
  37. package/dist/commands/runs/artifacts/download.js +154 -0
  38. package/dist/commands/runs/artifacts/list.d.ts +5 -0
  39. package/dist/commands/runs/artifacts/list.js +47 -0
  40. package/dist/commands/runs/cancel.js +2 -2
  41. package/dist/commands/runs/list.js +8 -2
  42. package/dist/commands/secrets-list.d.ts +2 -2
  43. package/dist/commands/secrets-list.js +4 -4
  44. package/dist/commands/types.d.ts +3 -3
  45. package/dist/commands/types.js +4 -4
  46. package/dist/commands/verify-attestation.d.ts +3 -1
  47. package/dist/commands/verify-attestation.js +18 -7
  48. package/dist/errors/formatter.d.ts +14 -1
  49. package/dist/errors/formatter.js +13 -3
  50. package/dist/errors/index.d.ts +2 -1
  51. package/dist/errors/index.js +3 -2
  52. package/dist/errors/source-location.d.ts +20 -0
  53. package/dist/errors/source-location.js +45 -0
  54. package/dist/execution/executor.js +32 -12
  55. package/dist/fixtures/defaults/index.js +2 -1
  56. package/dist/generators/secrets-dts.d.ts +9 -9
  57. package/dist/generators/secrets-dts.js +12 -12
  58. package/dist/llm-context/llms-architecture.txt +50 -21
  59. package/dist/llm-context/llms-cli.txt +2667 -1454
  60. package/dist/llm-context/llms-features.txt +484 -414
  61. package/dist/llm-context/llms-full.txt +6703 -4687
  62. package/dist/llm-context/llms-getting-started.txt +311 -55
  63. package/dist/llm-context/llms-patterns.txt +17 -29
  64. package/dist/llm-context/llms-providers.txt +10 -1
  65. package/dist/llm-context/llms-sdk-runtime.txt +2013 -0
  66. package/dist/llm-context/llms-sdk.txt +596 -2157
  67. package/dist/llm-context/llms.txt +31 -17
  68. package/dist/local-plane/orchestrator-process.d.ts +70 -0
  69. package/dist/local-plane/orchestrator-process.js +129 -0
  70. package/dist/local-plane/os-mismatch-hint.d.ts +16 -0
  71. package/dist/local-plane/os-mismatch-hint.js +34 -0
  72. package/dist/local-plane/paths.d.ts +39 -0
  73. package/dist/local-plane/paths.js +54 -0
  74. package/dist/local-plane/plane-liveness.d.ts +68 -0
  75. package/dist/local-plane/plane-liveness.js +145 -0
  76. package/dist/local-plane/plane-manager.d.ts +193 -0
  77. package/dist/local-plane/plane-manager.js +480 -0
  78. package/dist/local-plane/plane-seed.d.ts +76 -0
  79. package/dist/local-plane/plane-seed.js +59 -0
  80. package/dist/local-plane/plane-trigger.d.ts +71 -0
  81. package/dist/local-plane/plane-trigger.js +110 -0
  82. package/dist/local-plane/platform-attach.d.ts +53 -0
  83. package/dist/local-plane/platform-attach.js +107 -0
  84. package/dist/local-plane/port-holder.d.ts +78 -0
  85. package/dist/local-plane/port-holder.js +227 -0
  86. package/dist/local-plane/postgres.d.ts +56 -0
  87. package/dist/local-plane/postgres.js +187 -0
  88. package/dist/local-plane/resolve-plane.d.ts +37 -0
  89. package/dist/local-plane/resolve-plane.js +76 -0
  90. package/dist/local-plane/run-follow.d.ts +61 -0
  91. package/dist/local-plane/run-follow.js +151 -0
  92. package/dist/local-plane/scaler-config.d.ts +68 -0
  93. package/dist/local-plane/scaler-config.js +132 -0
  94. package/dist/local-plane/secret-seed.d.ts +59 -0
  95. package/dist/local-plane/secret-seed.js +112 -0
  96. package/dist/local-plane/source-provider.d.ts +43 -0
  97. package/dist/local-plane/source-provider.js +159 -0
  98. package/dist/local-plane/trusted-routing.d.ts +32 -0
  99. package/dist/local-plane/trusted-routing.js +47 -0
  100. package/dist/lockfile/generator.d.ts +14 -1
  101. package/dist/lockfile/generator.js +103 -37
  102. package/dist/lockfile/index.d.ts +3 -1
  103. package/dist/lockfile/index.js +3 -2
  104. package/dist/lockfile/purity-diagnostics.d.ts +31 -0
  105. package/dist/lockfile/purity-diagnostics.js +52 -0
  106. package/dist/postinstall.js +2 -1
  107. package/dist/remote/artifact-extract.d.ts +8 -0
  108. package/dist/remote/artifact-extract.js +58 -0
  109. package/dist/remote/config.d.ts +21 -3
  110. package/dist/remote/config.js +24 -5
  111. package/dist/remote/dashboard-client.d.ts +46 -9
  112. package/dist/remote/dashboard-client.js +54 -11
  113. package/dist/remote/fs-case.d.ts +21 -0
  114. package/dist/remote/fs-case.js +45 -0
  115. package/dist/remote/history.d.ts +9 -3
  116. package/dist/remote/history.js +79 -10
  117. package/dist/remote/local-repo-identity.d.ts +1 -1
  118. package/dist/remote/local-repo-identity.js +2 -1
  119. package/dist/remote/notifications-client.d.ts +149 -0
  120. package/dist/remote/notifications-client.js +103 -0
  121. package/dist/remote/oauth.d.ts +13 -0
  122. package/dist/remote/oauth.js +31 -5
  123. package/dist/remote/output/summary.js +76 -14
  124. package/dist/remote/platform-client.d.ts +1 -1
  125. package/dist/remote/render.js +30 -9
  126. package/dist/{local-executor → remote}/secret-loader.d.ts +1 -1
  127. package/dist/{local-executor → remote}/secret-loader.js +4 -4
  128. package/dist/remote/secret-upload.d.ts +1 -1
  129. package/dist/remote/secret-upload.js +2 -2
  130. package/dist/remote/uploader.d.ts +1 -1
  131. package/dist/remote/uploader.js +7 -6
  132. package/dist/templates/agents-md.d.ts +1 -1
  133. package/dist/templates/agents-md.js +8 -8
  134. package/dist/templates/index.d.ts +1 -1
  135. package/dist/templates/index.js +2 -2
  136. package/dist/templates/package-json.d.ts +15 -0
  137. package/dist/templates/package-json.js +26 -5
  138. package/dist/test-runner/dry-run.d.ts +2 -1
  139. package/dist/test-runner/dry-run.js +18 -1
  140. package/dist/test-runner/event-types.d.ts +2 -0
  141. package/dist/test-runner/event-types.js +3 -0
  142. package/dist/test-runner/job-executor.d.ts +1 -0
  143. package/dist/test-runner/job-executor.js +82 -44
  144. package/dist/test-runner/output-formatter.d.ts +5 -2
  145. package/dist/test-runner/output-formatter.js +6 -4
  146. package/dist/test-runner/payload-builder.d.ts +7 -1
  147. package/dist/test-runner/payload-builder.js +11 -2
  148. package/dist/test-runner/rule-evaluator.d.ts +7 -3
  149. package/dist/test-runner/rule-evaluator.js +13 -12
  150. package/dist/{local-executor → test-runner}/runs-on-display.js +1 -1
  151. package/dist/test-runner/step-context.d.ts +23 -5
  152. package/dist/test-runner/step-context.js +29 -119
  153. package/dist/types.d.ts +26 -7
  154. package/dist/types.js +4 -2
  155. package/dist/validation/index.d.ts +2 -0
  156. package/dist/validation/index.js +2 -1
  157. package/dist/validation/typecheck.d.ts +28 -0
  158. package/dist/validation/typecheck.js +110 -0
  159. package/dist/validation/validator.d.ts +9 -4
  160. package/dist/validation/validator.js +38 -44
  161. package/package.json +13 -13
  162. package/sbom.spdx.json +9311 -1528
  163. package/dist/assets/api-TJJVHrjC.json +0 -118
  164. package/dist/assets/descriptor-BTtjzN9L.json +0 -1382
  165. package/dist/assets/package-BpQF9kR8.json +0 -74
  166. package/dist/assets/package-Ceo2h27X.json +0 -89
  167. package/dist/assets/source_context-D0atuL28.json +0 -20
  168. package/dist/assets/type-BFqO8SCZ.json +0 -202
  169. package/dist/commands/cancel.d.ts +0 -22
  170. package/dist/commands/detect-package-manager.d.ts +0 -42
  171. package/dist/commands/held-run-resolve.d.ts +0 -50
  172. package/dist/commands/status.d.ts +0 -34
  173. package/dist/commands/test.d.ts +0 -88
  174. package/dist/errors/codes.d.ts +0 -25
  175. package/dist/local-executor/dag-scheduler.d.ts +0 -44
  176. package/dist/local-executor/dag-scheduler.js +0 -183
  177. package/dist/local-executor/index.d.ts +0 -23
  178. package/dist/local-executor/index.js +0 -378
  179. package/dist/local-executor/job-runner.d.ts +0 -47
  180. package/dist/local-executor/job-runner.js +0 -411
  181. package/dist/local-executor/materializer.d.ts +0 -44
  182. package/dist/local-executor/materializer.js +0 -132
  183. package/dist/local-executor/output-streamer.d.ts +0 -31
  184. package/dist/local-executor/output-streamer.js +0 -168
  185. package/dist/local-executor/payload-generator.d.ts +0 -16
  186. package/dist/local-executor/payload-generator.js +0 -138
  187. package/dist/local-executor/picker.d.ts +0 -33
  188. package/dist/local-executor/picker.js +0 -109
  189. package/dist/local-executor/to-event-payload.d.ts +0 -16
  190. package/dist/local-executor/to-event-payload.js +0 -21
  191. package/dist/local-executor/types.d.ts +0 -93
  192. package/dist/local-executor/types.js +0 -2
  193. package/dist/local-executor/workflow-lock.d.ts +0 -82
  194. package/dist/local-executor/workflow-lock.js +0 -0
  195. package/dist/package-F7UXSDHW.json +0 -74
  196. package/dist/remote/client.d.ts +0 -210
  197. package/dist/remote/observer.d.ts +0 -81
  198. package/dist/test-runner/summary.d.ts +0 -7
  199. /package/dist/{local-executor → test-runner}/runs-on-display.d.ts +0 -0
@@ -0,0 +1,151 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ import { ExecutionJobStatus, ExecutionRunStatus, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES } from "@kici-dev/engine";
3
+ import { AdminApiClient } from "@kici-dev/orchestrator";
4
+ //#region src/local-plane/run-follow.ts
5
+ /**
6
+ * Follow an offline routed run against the local plane by polling the
7
+ * orchestrator admin API (no SSE in independent mode). Streams step log lines as
8
+ * they arrive and resolves when the run reaches a terminal state.
9
+ */
10
+ /**
11
+ * Job states that mean the job did NOT succeed (failed / stale / cancelled /
12
+ * dropped / unroutable). Read only by the anti-false-green guard below, so a
13
+ * missing member lets a lagging `success` run header be reported verbatim even
14
+ * though a job settled badly.
15
+ */
16
+ const FAILED_JOB_STATES = /* @__PURE__ */ new Set([
17
+ ExecutionJobStatus.enum.failed,
18
+ ExecutionJobStatus.enum.timed_out_stale,
19
+ ExecutionJobStatus.enum.cancelled,
20
+ ExecutionJobStatus.enum.drift_dropped,
21
+ ExecutionJobStatus.enum.unroutable
22
+ ]);
23
+ /** Job states in which no agent has claimed the job yet. */
24
+ const UNCLAIMED_JOB_STATES = /* @__PURE__ */ new Set([ExecutionJobStatus.enum.pending, ExecutionJobStatus.enum.queued]);
25
+ /**
26
+ * How often the acceptance check re-fetches jobs. Deliberately far coarser than
27
+ * the poll interval: at 750 ms a two-minute window would cost ~160 extra admin
28
+ * requests to answer a question that changes once.
29
+ */
30
+ const ACCEPTANCE_CHECK_INTERVAL_MS = 5e3;
31
+ /** Default window for an agent to claim the run before the follow gives up. */
32
+ const DEFAULT_ACCEPTANCE_TIMEOUT_MS = 12e4;
33
+ /**
34
+ * Resolve the acceptance window, falling back to the default for any value that
35
+ * is not a positive number of milliseconds.
36
+ *
37
+ * A malformed override must never reach the deadline arithmetic: `Number('')`
38
+ * is `0` (every run fails instantly) and `Number('2 min')` is `NaN`, which makes
39
+ * `now > deadline` permanently false and silently restores the 900 s hang this
40
+ * window exists to prevent. Both failure modes are worse than ignoring a typo.
41
+ */
42
+ function resolveAcceptanceTimeoutMs(explicitMs, raw) {
43
+ if (explicitMs !== void 0) return explicitMs;
44
+ if (raw === void 0) return DEFAULT_ACCEPTANCE_TIMEOUT_MS;
45
+ const parsed = Number(raw);
46
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ACCEPTANCE_TIMEOUT_MS;
47
+ }
48
+ /** Poll a run to completion, streaming step logs. */
49
+ async function followRun(planeUrl, adminToken, runId, opts = {}) {
50
+ const client = opts.client ?? new AdminApiClient(planeUrl, adminToken);
51
+ const pollIntervalMs = opts.pollIntervalMs ?? 750;
52
+ const idleTimeoutMs = opts.idleTimeoutMs ?? opts.timeoutMs ?? 9e5;
53
+ const maxTotalMs = opts.maxTotalMs ?? 72e5;
54
+ const acceptanceTimeoutMs = resolveAcceptanceTimeoutMs(opts.acceptanceTimeoutMs, process.env.KICI_LOCAL_ACCEPTANCE_TIMEOUT_MS);
55
+ const cursors = /* @__PURE__ */ new Map();
56
+ const stream = !opts.quiet && opts.onLine ? opts.onLine : void 0;
57
+ const startedAt = Date.now();
58
+ let lastProgressAt = startedAt;
59
+ let lastStatus = "";
60
+ let accepted = false;
61
+ let acceptanceDeadline = startedAt + acceptanceTimeoutMs;
62
+ let lastAcceptanceCheck = 0;
63
+ for (;;) {
64
+ const now = Date.now();
65
+ if (now - lastProgressAt > idleTimeoutMs) throw new Error(`offline run: follow timed out (no progress for ${idleTimeoutMs}ms) for run ${runId}`);
66
+ if (now - startedAt > maxTotalMs) throw new Error(`offline run: follow exceeded its max window (${maxTotalMs}ms) for run ${runId}`);
67
+ const { run } = await client.get(`/api/v1/admin/runs/${runId}`);
68
+ if (!accepted && !TERMINAL_RUN_STATES.has(run.status)) {
69
+ if (run.status === ExecutionRunStatus.enum.held) acceptanceDeadline = now + acceptanceTimeoutMs;
70
+ else if (now - lastAcceptanceCheck >= ACCEPTANCE_CHECK_INTERVAL_MS) {
71
+ lastAcceptanceCheck = now;
72
+ const claimedJobs = await fetchJobs(client, runId);
73
+ accepted = claimedJobs.length > 0 && claimedJobs.some((j) => !UNCLAIMED_JOB_STATES.has(j.status));
74
+ }
75
+ if (!accepted && now > acceptanceDeadline) {
76
+ const seconds = Math.round(acceptanceTimeoutMs / 1e3);
77
+ throw new Error(`offline run: no agent picked up this run within ${seconds}s — no scaler label set matched the job's runsOn, or the agent failed to start.` + (opts.hintLogPath ? ` Plane log: ${opts.hintLogPath}` : ""));
78
+ }
79
+ }
80
+ let progressed = false;
81
+ if (run.status !== lastStatus) {
82
+ lastStatus = run.status;
83
+ progressed = true;
84
+ }
85
+ if (stream) {
86
+ if (await drainLogs(client, runId, cursors, stream) > 0) progressed = true;
87
+ }
88
+ if (progressed) lastProgressAt = now;
89
+ if (TERMINAL_RUN_STATES.has(run.status)) {
90
+ const jobs = await fetchJobs(client, runId);
91
+ const allTerminal = jobs.length > 0 && jobs.every((j) => TERMINAL_JOB_STATES.has(j.status));
92
+ const anyFailed = jobs.some((j) => FAILED_JOB_STATES.has(j.status));
93
+ if (allTerminal && !(run.status === "success" && anyFailed)) {
94
+ if (stream) await drainLogs(client, runId, cursors, stream);
95
+ return {
96
+ runId,
97
+ status: run.status,
98
+ jobs
99
+ };
100
+ }
101
+ }
102
+ await sleep(pollIntervalMs);
103
+ }
104
+ }
105
+ /** Fetch the run's jobs mapped to the summary shape. */
106
+ async function fetchJobs(client, runId) {
107
+ const { jobs } = await client.get(`/api/v1/admin/runs/${runId}/jobs`);
108
+ return jobs.map((j) => ({
109
+ name: j.jobName,
110
+ status: j.status,
111
+ durationMs: j.durationMs ?? void 0
112
+ }));
113
+ }
114
+ /**
115
+ * Stream any new step-log lines. Best-effort: a per-step fetch error never
116
+ * aborts the follow (logs are observability, not the terminal signal).
117
+ */
118
+ async function drainLogs(client, runId, cursors, onLine) {
119
+ let emitted = 0;
120
+ let jobs;
121
+ try {
122
+ jobs = (await client.get(`/api/v1/admin/runs/${runId}/jobs?includeSteps=true`)).jobs;
123
+ } catch {
124
+ return emitted;
125
+ }
126
+ for (const job of jobs) for (const step of job.steps ?? []) {
127
+ const key = `${job.jobId}:${step.stepIndex}`;
128
+ try {
129
+ let cursor = cursors.get(key);
130
+ for (;;) {
131
+ const qs = cursor ? `?cursor=${encodeURIComponent(cursor)}` : "";
132
+ const page = await client.get(`/api/v1/admin/runs/${runId}/jobs/${job.jobId}/steps/${step.stepIndex}/logs${qs}`);
133
+ for (const l of page.lines) {
134
+ onLine(l.value);
135
+ emitted++;
136
+ }
137
+ cursor = page.nextCursor ?? String(page.totalLines);
138
+ cursors.set(key, cursor);
139
+ if (!page.nextCursor) break;
140
+ }
141
+ } catch {}
142
+ }
143
+ return emitted;
144
+ }
145
+ function sleep(ms) {
146
+ return new Promise((r) => setTimeout(r, ms));
147
+ }
148
+ //#endregion
149
+ export { followRun, resolveAcceptanceTimeoutMs };
150
+
151
+ //# sourceMappingURL=run-follow.js.map
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Bare-metal scaler configuration for the local dev plane.
3
+ *
4
+ * A dispatched job needs an agent to run it. The plane orchestrator boots with
5
+ * a single bare-metal scaler pointed at a label set matching the default
6
+ * `runsOn` (`default`), so an offline `kici run --local` dispatch auto-spawns an
7
+ * ephemeral, one-job-then-exit agent on this machine. The spawned binary is a
8
+ * small executable wrapper that execs `node <@kici-dev/agent server entry>` —
9
+ * mirroring the shape proven by the `local-file-source` E2E, and avoiding a
10
+ * dependency on the (non-executable) shipped `kici-agent` script.
11
+ */
12
+ /** Resolve the built `@kici-dev/agent` server entry the bare-metal scaler runs. */
13
+ export declare function resolveAgentBinary(): string;
14
+ /**
15
+ * Write the executable wrapper the bare-metal scaler spawns. The scaler invokes
16
+ * `spawn(binaryPath, [])` directly, so the target must be an executable file;
17
+ * the shipped agent entry is a plain Node module without an exec bit, so this
18
+ * wrapper bridges the gap. Returns the wrapper path.
19
+ */
20
+ export declare function writeAgentWrapper(): string;
21
+ /**
22
+ * The non-reserved routing label a `kici run --local --trusted` run appends to
23
+ * every job's `runsOn` so the dispatch lands on the plane's trusted label set.
24
+ * `self-hosted` (not a `kici:`-prefixed reserved label) is also the `runsOn` the
25
+ * CI deploy/CDN workflows already declare, so those route to the trusted profile
26
+ * on this plane by construction.
27
+ */
28
+ export declare const TRUSTED_ROUTING_LABEL = "self-hosted";
29
+ /**
30
+ * The non-reserved routing label a `kici run --local --trusted --in-place` run
31
+ * appends (alongside `self-hosted`) so the dispatch lands on the plane's trusted
32
+ * **in-place** label set — the agent runs the operator's real working tree
33
+ * directly (no clone). This is the profile KiCI's own routed `deploy:stg` uses.
34
+ */
35
+ export declare const IN_PLACE_ROUTING_LABEL = "in-place";
36
+ /**
37
+ * Write the plane's bare-metal scaler YAML and return its path. TWO coexisting
38
+ * bare-metal scalers spawn the agent wrapper against the plane orchestrator's
39
+ * localhost WS endpoint — a **sandboxed** pool and a `self-hosted`-**tainted**
40
+ * TRUSTED pool:
41
+ *
42
+ * - `kici-local-bare-metal` (sandboxed) — one `default` label set, no taint. The
43
+ * credential-isolated profile a normal `kici run --local` dispatch (jobs with
44
+ * `runsOn: ['default']` or none) lands on. Its agents inherit no
45
+ * `mandatoryLabels`, so a bare `default` job routes here.
46
+ * - `kici-local-bare-metal-trusted` (TRUSTED) — carries `mandatoryLabels:
47
+ * ['self-hosted']`, so ONLY a job that explicitly requests `self-hosted`
48
+ * (i.e. a `kici run --local --trusted` run, whose lock patch appends the
49
+ * `self-hosted` routing label, or a `runsOn: self-hosted` workflow) is ever
50
+ * routed to it — at BOTH the scaler spawn matcher AND the orchestrator's
51
+ * agent-registry dispatch gate. Two label sets:
52
+ * - `default` + `self-hosted` — the trusted fleet profile
53
+ * (`KICI_TRUSTED_ENV=true`, bwrap off): steps run with the ambient host
54
+ * env passed through, minus the agent's own KiCI identity secrets.
55
+ * - `default` + `self-hosted` + `in-place` — the trusted **in-place**
56
+ * profile (adds `KICI_IN_PLACE=true`) a `kici run --local --trusted
57
+ * --in-place` run lands on: same trusted env, but the agent uses the
58
+ * operator's real working tree directly (no clone) — the profile KiCI's
59
+ * own routed `deploy:stg` uses.
60
+ *
61
+ * The `self-hosted` taint is the isolation guarantee: without it, an idle
62
+ * trusted agent (spawned for an earlier `--trusted` run and lingering for
63
+ * follow-up jobs) would satisfy the agent-registry's subset match for a plain
64
+ * `default` job and leak the ambient host env into a sandboxed run. The taint
65
+ * closes that at the dispatch layer, not just the spawn heuristic.
66
+ */
67
+ export declare function writeScalerConfig(orchestratorPort: number): string;
68
+ //# sourceMappingURL=scaler-config.d.ts.map
@@ -0,0 +1,132 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ import { planePaths } from "./paths.js";
3
+ import { createRequire } from "node:module";
4
+ import fs from "node:fs";
5
+ import { stringify } from "yaml";
6
+ //#region src/local-plane/scaler-config.ts
7
+ /**
8
+ * Bare-metal scaler configuration for the local dev plane.
9
+ *
10
+ * A dispatched job needs an agent to run it. The plane orchestrator boots with
11
+ * a single bare-metal scaler pointed at a label set matching the default
12
+ * `runsOn` (`default`), so an offline `kici run --local` dispatch auto-spawns an
13
+ * ephemeral, one-job-then-exit agent on this machine. The spawned binary is a
14
+ * small executable wrapper that execs `node <@kici-dev/agent server entry>` —
15
+ * mirroring the shape proven by the `local-file-source` E2E, and avoiding a
16
+ * dependency on the (non-executable) shipped `kici-agent` script.
17
+ */
18
+ /** Resolve the built `@kici-dev/agent` server entry the bare-metal scaler runs. */
19
+ function resolveAgentBinary() {
20
+ return createRequire(import.meta.url).resolve("@kici-dev/agent/server");
21
+ }
22
+ /**
23
+ * Write the executable wrapper the bare-metal scaler spawns. The scaler invokes
24
+ * `spawn(binaryPath, [])` directly, so the target must be an executable file;
25
+ * the shipped agent entry is a plain Node module without an exec bit, so this
26
+ * wrapper bridges the gap. Returns the wrapper path.
27
+ */
28
+ function writeAgentWrapper() {
29
+ const { agentWrapperFile, root } = planePaths();
30
+ fs.mkdirSync(root, { recursive: true });
31
+ const server = resolveAgentBinary();
32
+ fs.writeFileSync(agentWrapperFile, `#!/usr/bin/env bash\nexec node ${JSON.stringify(server)} "$@"\n`);
33
+ fs.chmodSync(agentWrapperFile, 493);
34
+ return agentWrapperFile;
35
+ }
36
+ /**
37
+ * The non-reserved routing label a `kici run --local --trusted` run appends to
38
+ * every job's `runsOn` so the dispatch lands on the plane's trusted label set.
39
+ * `self-hosted` (not a `kici:`-prefixed reserved label) is also the `runsOn` the
40
+ * CI deploy/CDN workflows already declare, so those route to the trusted profile
41
+ * on this plane by construction.
42
+ */
43
+ const TRUSTED_ROUTING_LABEL = "self-hosted";
44
+ /**
45
+ * The non-reserved routing label a `kici run --local --trusted --in-place` run
46
+ * appends (alongside `self-hosted`) so the dispatch lands on the plane's trusted
47
+ * **in-place** label set — the agent runs the operator's real working tree
48
+ * directly (no clone). This is the profile KiCI's own routed `deploy:stg` uses.
49
+ */
50
+ const IN_PLACE_ROUTING_LABEL = "in-place";
51
+ /**
52
+ * Write the plane's bare-metal scaler YAML and return its path. TWO coexisting
53
+ * bare-metal scalers spawn the agent wrapper against the plane orchestrator's
54
+ * localhost WS endpoint — a **sandboxed** pool and a `self-hosted`-**tainted**
55
+ * TRUSTED pool:
56
+ *
57
+ * - `kici-local-bare-metal` (sandboxed) — one `default` label set, no taint. The
58
+ * credential-isolated profile a normal `kici run --local` dispatch (jobs with
59
+ * `runsOn: ['default']` or none) lands on. Its agents inherit no
60
+ * `mandatoryLabels`, so a bare `default` job routes here.
61
+ * - `kici-local-bare-metal-trusted` (TRUSTED) — carries `mandatoryLabels:
62
+ * ['self-hosted']`, so ONLY a job that explicitly requests `self-hosted`
63
+ * (i.e. a `kici run --local --trusted` run, whose lock patch appends the
64
+ * `self-hosted` routing label, or a `runsOn: self-hosted` workflow) is ever
65
+ * routed to it — at BOTH the scaler spawn matcher AND the orchestrator's
66
+ * agent-registry dispatch gate. Two label sets:
67
+ * - `default` + `self-hosted` — the trusted fleet profile
68
+ * (`KICI_TRUSTED_ENV=true`, bwrap off): steps run with the ambient host
69
+ * env passed through, minus the agent's own KiCI identity secrets.
70
+ * - `default` + `self-hosted` + `in-place` — the trusted **in-place**
71
+ * profile (adds `KICI_IN_PLACE=true`) a `kici run --local --trusted
72
+ * --in-place` run lands on: same trusted env, but the agent uses the
73
+ * operator's real working tree directly (no clone) — the profile KiCI's
74
+ * own routed `deploy:stg` uses.
75
+ *
76
+ * The `self-hosted` taint is the isolation guarantee: without it, an idle
77
+ * trusted agent (spawned for an earlier `--trusted` run and lingering for
78
+ * follow-up jobs) would satisfy the agent-registry's subset match for a plain
79
+ * `default` job and leak the ambient host env into a sandboxed run. The taint
80
+ * closes that at the dispatch layer, not just the spawn heuristic.
81
+ */
82
+ function writeScalerConfig(orchestratorPort) {
83
+ const { scalerConfigFile, root } = planePaths();
84
+ fs.mkdirSync(root, { recursive: true });
85
+ const binaryPath = writeAgentWrapper();
86
+ const orchestratorUrl = `ws://127.0.0.1:${orchestratorPort}/ws`;
87
+ const yaml = stringify({
88
+ version: 1,
89
+ scalers: [{
90
+ name: "kici-local-bare-metal",
91
+ type: "bare-metal",
92
+ maxAgents: 10,
93
+ orchestratorUrl,
94
+ labelSets: [{
95
+ labels: ["default"],
96
+ binaryPath
97
+ }]
98
+ }, {
99
+ name: "kici-local-bare-metal-trusted",
100
+ type: "bare-metal",
101
+ maxAgents: 10,
102
+ orchestratorUrl,
103
+ mandatoryLabels: [TRUSTED_ROUTING_LABEL],
104
+ labelSets: [{
105
+ labels: ["default", TRUSTED_ROUTING_LABEL],
106
+ binaryPath,
107
+ env: {
108
+ KICI_TRUSTED_ENV: "true",
109
+ KICI_SANDBOX: "false"
110
+ }
111
+ }, {
112
+ labels: [
113
+ "default",
114
+ TRUSTED_ROUTING_LABEL,
115
+ IN_PLACE_ROUTING_LABEL
116
+ ],
117
+ binaryPath,
118
+ env: {
119
+ KICI_TRUSTED_ENV: "true",
120
+ KICI_SANDBOX: "false",
121
+ KICI_IN_PLACE: "true"
122
+ }
123
+ }]
124
+ }]
125
+ });
126
+ fs.writeFileSync(scalerConfigFile, yaml);
127
+ return scalerConfigFile;
128
+ }
129
+ //#endregion
130
+ export { IN_PLACE_ROUTING_LABEL, TRUSTED_ROUTING_LABEL, resolveAgentBinary, writeAgentWrapper, writeScalerConfig };
131
+
132
+ //# sourceMappingURL=scaler-config.js.map
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Seed a project's local `secrets.yaml` (and `.kici/.secrets` INI) contexts into
3
+ * the local dev plane's real `scoped_secrets` store, preserving context/scope
4
+ * structure so the plane's real `SecretResolver` honors scoping at dispatch —
5
+ * the routed local run resolves scoped secrets through the plane's real
6
+ * resolver rather than a flat merge.
7
+ *
8
+ * Local secret files are READ-ONLY here: values are only READ and transit into
9
+ * the local plane's Postgres via the admin secret API. Nothing is written back
10
+ * to a secret file, and nothing leaves this machine (the plane is local).
11
+ *
12
+ * Scope model: each `secrets.yaml` top-level key is a context name. The seeder
13
+ * creates a context of that name, binds it to a scope pattern equal to the
14
+ * context name, and stores each secret at that scope. So a workflow job bound to
15
+ * context `production` resolves `production`-scoped secrets through the resolver.
16
+ */
17
+ /** Minimal AdminApiClient surface the seeder needs (for test injection). */
18
+ export interface SecretSeedClient {
19
+ createContext(data: {
20
+ orgId: string;
21
+ name: string;
22
+ allowLocalExecution?: boolean;
23
+ }): Promise<{
24
+ envId: string;
25
+ created: boolean;
26
+ }>;
27
+ bindContext(data: {
28
+ orgId: string;
29
+ name: string;
30
+ scopePattern: string;
31
+ hostPattern?: string;
32
+ }): Promise<unknown>;
33
+ setSecret(orgId: string, scope: string, key: string, value: string): Promise<void>;
34
+ }
35
+ /** Result of a seed run: the contexts seeded and how many secret keys landed. */
36
+ export interface SeededSecrets {
37
+ contexts: string[];
38
+ secretCount: number;
39
+ }
40
+ /**
41
+ * Collect the per-context secrets from a project's `.kici/` directory,
42
+ * preserving context structure. Reads `.kici/.secrets` (INI sections) and
43
+ * `.kici/secrets.yaml` (top-level contexts). The yaml layer wins on key
44
+ * collisions within a context. Flat (context-less) secrets are ignored here —
45
+ * a routed job only resolves secrets for the contexts it binds.
46
+ */
47
+ export declare function loadLocalSecretContexts(kiciDir: string): Promise<Record<string, Record<string, string>>>;
48
+ /**
49
+ * Seed the local plane's `scoped_secrets` store from a project's `secrets.yaml`
50
+ * / `.secrets` contexts. Idempotent: `createContext` upserts, `bind` is a no-op
51
+ * when the scope is already bound, and `setSecret` overwrites in place. Returns
52
+ * the contexts + secret-key count seeded (never the values).
53
+ */
54
+ export declare function seedLocalSecrets(planeUrl: string, adminToken: string, opts: {
55
+ orgId: string;
56
+ kiciDir: string;
57
+ client?: SecretSeedClient;
58
+ }): Promise<SeededSecrets>;
59
+ //# sourceMappingURL=secret-seed.d.ts.map
@@ -0,0 +1,112 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ import { parseSecretsFile } from "../test-runner/secrets-file.js";
3
+ import path from "node:path";
4
+ import { readFile } from "node:fs/promises";
5
+ import { parse } from "yaml";
6
+ import { AdminApiClient } from "@kici-dev/orchestrator";
7
+ //#region src/local-plane/secret-seed.ts
8
+ /**
9
+ * Seed a project's local `secrets.yaml` (and `.kici/.secrets` INI) contexts into
10
+ * the local dev plane's real `scoped_secrets` store, preserving context/scope
11
+ * structure so the plane's real `SecretResolver` honors scoping at dispatch —
12
+ * the routed local run resolves scoped secrets through the plane's real
13
+ * resolver rather than a flat merge.
14
+ *
15
+ * Local secret files are READ-ONLY here: values are only READ and transit into
16
+ * the local plane's Postgres via the admin secret API. Nothing is written back
17
+ * to a secret file, and nothing leaves this machine (the plane is local).
18
+ *
19
+ * Scope model: each `secrets.yaml` top-level key is a context name. The seeder
20
+ * creates a context of that name, binds it to a scope pattern equal to the
21
+ * context name, and stores each secret at that scope. So a workflow job bound to
22
+ * context `production` resolves `production`-scoped secrets through the resolver.
23
+ */
24
+ /**
25
+ * Read a file, returning null when it does not exist.
26
+ */
27
+ async function readFileOrNull(filePath) {
28
+ try {
29
+ return await readFile(filePath, "utf-8");
30
+ } catch (err) {
31
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") return null;
32
+ throw err;
33
+ }
34
+ }
35
+ /**
36
+ * Parse `secrets.yaml`, PRESERVING its top-level context structure (a flat merge
37
+ * would collapse the contexts). Top-level keys are context names; values are
38
+ * key→value maps. Non-scalar leaf values are stringified.
39
+ */
40
+ function parseSecretsYamlContexts(content) {
41
+ const parsed = parse(content);
42
+ if (!parsed || typeof parsed !== "object") return {};
43
+ const out = {};
44
+ for (const [contextName, contextSecrets] of Object.entries(parsed)) if (contextSecrets && typeof contextSecrets === "object" && !Array.isArray(contextSecrets)) {
45
+ const map = {};
46
+ for (const [key, value] of Object.entries(contextSecrets)) if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") map[key] = String(value);
47
+ if (Object.keys(map).length > 0) out[contextName] = map;
48
+ }
49
+ return out;
50
+ }
51
+ /**
52
+ * Collect the per-context secrets from a project's `.kici/` directory,
53
+ * preserving context structure. Reads `.kici/.secrets` (INI sections) and
54
+ * `.kici/secrets.yaml` (top-level contexts). The yaml layer wins on key
55
+ * collisions within a context. Flat (context-less) secrets are ignored here —
56
+ * a routed job only resolves secrets for the contexts it binds.
57
+ */
58
+ async function loadLocalSecretContexts(kiciDir) {
59
+ const contexts = {};
60
+ const iniContent = await readFileOrNull(path.join(kiciDir, ".secrets"));
61
+ if (iniContent !== null) {
62
+ const parsed = parseSecretsFile(iniContent);
63
+ for (const [ctx, vals] of Object.entries(parsed.contexts)) contexts[ctx] = {
64
+ ...contexts[ctx] ?? {},
65
+ ...vals
66
+ };
67
+ }
68
+ const yamlContent = await readFileOrNull(path.join(kiciDir, "secrets.yaml"));
69
+ if (yamlContent !== null) for (const [ctx, vals] of Object.entries(parseSecretsYamlContexts(yamlContent))) contexts[ctx] = {
70
+ ...contexts[ctx] ?? {},
71
+ ...vals
72
+ };
73
+ return contexts;
74
+ }
75
+ /**
76
+ * Seed the local plane's `scoped_secrets` store from a project's `secrets.yaml`
77
+ * / `.secrets` contexts. Idempotent: `createContext` upserts, `bind` is a no-op
78
+ * when the scope is already bound, and `setSecret` overwrites in place. Returns
79
+ * the contexts + secret-key count seeded (never the values).
80
+ */
81
+ async function seedLocalSecrets(planeUrl, adminToken, opts) {
82
+ const client = opts.client ?? new AdminApiClient(planeUrl, adminToken);
83
+ const contexts = await loadLocalSecretContexts(opts.kiciDir);
84
+ const seeded = [];
85
+ let secretCount = 0;
86
+ for (const [contextName, secrets] of Object.entries(contexts)) {
87
+ await client.createContext({
88
+ orgId: opts.orgId,
89
+ name: contextName,
90
+ allowLocalExecution: true
91
+ });
92
+ await client.bindContext({
93
+ orgId: opts.orgId,
94
+ name: contextName,
95
+ scopePattern: contextName,
96
+ hostPattern: "**"
97
+ });
98
+ for (const [key, value] of Object.entries(secrets)) {
99
+ await client.setSecret(opts.orgId, contextName, key, value);
100
+ secretCount++;
101
+ }
102
+ seeded.push(contextName);
103
+ }
104
+ return {
105
+ contexts: seeded,
106
+ secretCount
107
+ };
108
+ }
109
+ //#endregion
110
+ export { loadLocalSecretContexts, seedLocalSecrets };
111
+
112
+ //# sourceMappingURL=secret-seed.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * LocalSourceProvider — resolve the workdir a `kici run --local` dispatch
3
+ * registers as a `file://` source for the plane.
4
+ *
5
+ * The plane orchestrator's local provider fetches the lock from
6
+ * `<repoBasePath>/.kici/kici.lock.json` and the ephemeral agent clones
7
+ * `file://<repoBasePath>` at a committed sha. So the resolved workdir must be a
8
+ * git repo whose HEAD carries the workflow + lock the run should execute.
9
+ *
10
+ * Two profiles:
11
+ * - **default (isolated):** an isolated tmp clone at HEAD with the local overlay
12
+ * (dirty + untracked files) applied and committed onto a `kici-local` branch,
13
+ * so the clone-by-sha the agent performs sees uncommitted work without
14
+ * touching the developer's tree.
15
+ * - **`--in-place`:** the repo root directly (ambient state, the profile the CI
16
+ * deploy jobs use). Cleanup is a no-op.
17
+ */
18
+ /** The `kici-local` branch the isolated profile commits its overlay onto. */
19
+ export declare const LOCAL_RUN_BRANCH = "kici-local";
20
+ /** A resolved workdir plus the git coordinates the plane trigger needs. */
21
+ export interface ResolvedWorkdir {
22
+ /** Absolute path registered as the local source `repoBasePath`. */
23
+ dir: string;
24
+ /** Git ref the synthetic push carries (`refs/heads/<branch>`). */
25
+ ref: string;
26
+ /** Committed HEAD sha the agent clones + checks out. */
27
+ sha: string;
28
+ /** Branch short name (matched by the workflow's push filter). */
29
+ branch: string;
30
+ /** Remove the tmp workdir (no-op for `--in-place`). */
31
+ cleanup: () => Promise<void>;
32
+ }
33
+ /**
34
+ * Resolve the workdir for an offline routed run.
35
+ *
36
+ * @param opts.inPlace - Use the repo root directly instead of an isolated clone.
37
+ * @param opts.repoRoot - The developer's repo root.
38
+ */
39
+ export declare function resolveWorkdir(opts: {
40
+ inPlace: boolean;
41
+ repoRoot: string;
42
+ }): Promise<ResolvedWorkdir>;
43
+ //# sourceMappingURL=source-provider.d.ts.map