@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,76 @@
1
+ /**
2
+ * Seed the local `file://` source the plane dispatches an offline routed run
3
+ * through. Idempotent: a stable `kici-local` source is created once and its
4
+ * `repoBasePath` is re-pointed at the current workdir on every run (the isolated
5
+ * profile materializes a fresh tmp clone each time). The org is the independent
6
+ * orchestrator's `__default__` sentinel — the orchestrator DB has no org table,
7
+ * so seeding an org is just passing this id.
8
+ */
9
+ /** Independent-mode default org sentinel used by the local plane. */
10
+ export declare const LOCAL_ORG_ID = "__default__";
11
+ /** Stable name of the plane's local source (reused + re-pointed across runs). */
12
+ export declare const LOCAL_SOURCE_NAME = "kici-local";
13
+ /** Minimal surface of AdminApiClient plane-seed needs (for test injection). */
14
+ export interface PlaneSeedClient {
15
+ listGenericSources(orgId: string, includeDeleted?: boolean): Promise<{
16
+ sources: Array<{
17
+ id: string;
18
+ name: string;
19
+ provider_type?: string;
20
+ }>;
21
+ }>;
22
+ createGenericSource(data: {
23
+ orgId: string;
24
+ name: string;
25
+ providerType?: string;
26
+ verificationMethod?: string;
27
+ localConfig?: {
28
+ repoBasePath: string;
29
+ cloneUrlBase?: string;
30
+ inPlace?: boolean;
31
+ };
32
+ }): Promise<{
33
+ source: {
34
+ id: string;
35
+ name: string;
36
+ };
37
+ }>;
38
+ updateGenericSource(id: string, data: {
39
+ localConfig?: {
40
+ repoBasePath: string;
41
+ cloneUrlBase?: string;
42
+ inPlace?: boolean;
43
+ } | null;
44
+ }): Promise<{
45
+ source: {
46
+ id: string;
47
+ name: string;
48
+ };
49
+ }>;
50
+ }
51
+ /** Result of seeding: the org + the (created or reused) source. */
52
+ export interface SeededSource {
53
+ orgId: string;
54
+ sourceId: string;
55
+ sourceName: string;
56
+ }
57
+ /**
58
+ * Ensure the plane has a local `file://` source pointed at `repoDir`. Creates
59
+ * the `kici-local` source on first use; on subsequent runs it re-points the
60
+ * existing source at the new workdir (idempotent reuse). Returns the org +
61
+ * source ids the trigger uses.
62
+ */
63
+ export declare function ensureLocalSource(planeUrl: string, adminToken: string, opts: {
64
+ repoDir: string;
65
+ orgId?: string;
66
+ name?: string;
67
+ /**
68
+ * In-place profile: `repoDir` is the operator's real working tree, so the
69
+ * plane skips the source-pack `__build__` job and each job runs the tree
70
+ * directly. Always written (true/false) so a subsequent isolated run resets
71
+ * a previously in-place source.
72
+ */
73
+ inPlace?: boolean;
74
+ client?: PlaneSeedClient;
75
+ }): Promise<SeededSource>;
76
+ //# sourceMappingURL=plane-seed.d.ts.map
@@ -0,0 +1,59 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ import { AdminApiClient } from "@kici-dev/orchestrator";
3
+ //#region src/local-plane/plane-seed.ts
4
+ /**
5
+ * Seed the local `file://` source the plane dispatches an offline routed run
6
+ * through. Idempotent: a stable `kici-local` source is created once and its
7
+ * `repoBasePath` is re-pointed at the current workdir on every run (the isolated
8
+ * profile materializes a fresh tmp clone each time). The org is the independent
9
+ * orchestrator's `__default__` sentinel — the orchestrator DB has no org table,
10
+ * so seeding an org is just passing this id.
11
+ */
12
+ /** Independent-mode default org sentinel used by the local plane. */
13
+ const LOCAL_ORG_ID = "__default__";
14
+ /** Stable name of the plane's local source (reused + re-pointed across runs). */
15
+ const LOCAL_SOURCE_NAME = "kici-local";
16
+ /**
17
+ * Ensure the plane has a local `file://` source pointed at `repoDir`. Creates
18
+ * the `kici-local` source on first use; on subsequent runs it re-points the
19
+ * existing source at the new workdir (idempotent reuse). Returns the org +
20
+ * source ids the trigger uses.
21
+ */
22
+ async function ensureLocalSource(planeUrl, adminToken, opts) {
23
+ const orgId = opts.orgId ?? "__default__";
24
+ const name = opts.name ?? "kici-local";
25
+ const inPlace = opts.inPlace === true;
26
+ const client = opts.client ?? new AdminApiClient(planeUrl, adminToken);
27
+ const { sources } = await client.listGenericSources(orgId);
28
+ const existing = sources.find((s) => s.name === name && s.provider_type === "local");
29
+ if (existing) {
30
+ const updated = await client.updateGenericSource(existing.id, { localConfig: {
31
+ repoBasePath: opts.repoDir,
32
+ inPlace
33
+ } });
34
+ return {
35
+ orgId,
36
+ sourceId: updated.source.id,
37
+ sourceName: updated.source.name
38
+ };
39
+ }
40
+ const created = await client.createGenericSource({
41
+ orgId,
42
+ name,
43
+ providerType: "local",
44
+ verificationMethod: "none",
45
+ localConfig: {
46
+ repoBasePath: opts.repoDir,
47
+ inPlace
48
+ }
49
+ });
50
+ return {
51
+ orgId,
52
+ sourceId: created.source.id,
53
+ sourceName: created.source.name
54
+ };
55
+ }
56
+ //#endregion
57
+ export { LOCAL_ORG_ID, LOCAL_SOURCE_NAME, ensureLocalSource };
58
+
59
+ //# sourceMappingURL=plane-seed.js.map
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Trigger an offline routed run against the local plane and discover its runId.
3
+ *
4
+ * A local repo has no forge to send a webhook, so this posts a GitHub-shaped
5
+ * synthetic push to the plane's provider-agnostic generic webhook route (the
6
+ * same wire shape the `kici-admin source trigger-local` path builds). The
7
+ * webhook is unauthenticated (the edge accepts it and returns 202); the run is
8
+ * created asynchronously, so this then polls the admin runs list — authenticated
9
+ * with the plane's bootstrap admin token — to resolve the created runId.
10
+ *
11
+ * The source's bundle hot-reload is debounced, so a first trigger can land
12
+ * before the plane has registered the (re-pointed) local source. This resends
13
+ * the webhook after a grace window until a run appears or the timeout elapses.
14
+ */
15
+ /** A minimal admin-read client (AdminApiClient.get) — injectable for tests. */
16
+ export interface RunDiscoveryClient {
17
+ get<T>(path: string): Promise<T>;
18
+ }
19
+ export interface LocalTriggerInput {
20
+ orgId: string;
21
+ sourceId: string;
22
+ repoFullName: string;
23
+ event: 'push' | 'pull_request' | 'dispatch';
24
+ ref: string;
25
+ sha: string;
26
+ defaultBranch: string;
27
+ /**
28
+ * Dispatch event action (`dispatch()` `types` matcher key). Only used when
29
+ * `event === 'dispatch'`; the local normalizer reads it from `payload.action`,
30
+ * so exactly one `types`-filtered workflow matches.
31
+ */
32
+ action?: string;
33
+ /** Dispatch `client_payload` (steps read `ctx.rawPayload.client_payload`). */
34
+ clientPayload?: unknown;
35
+ }
36
+ export interface LocalTriggerRequest {
37
+ path: string;
38
+ headers: Record<string, string>;
39
+ body: string;
40
+ }
41
+ /**
42
+ * Build the GitHub-shaped webhook request the plane's local provider normalizer
43
+ * expects. Mirrors `buildLocalTriggerRequest` in the orchestrator's
44
+ * `cli/commands/local-trigger.ts` (kept local to honour the compiler-only scope
45
+ * of this phase — no orchestrator export is added).
46
+ */
47
+ export declare function buildLocalTriggerRequest(input: LocalTriggerInput): LocalTriggerRequest;
48
+ /** Structured result of posting a local trigger to the plane. */
49
+ export interface LocalTriggerResponse {
50
+ status: number;
51
+ /** Routing-key-scoped delivery id echoed by the plane, or null if absent. */
52
+ deliveryId: string | null;
53
+ }
54
+ /** POST the trigger request to the plane. Returns the status + delivery id. */
55
+ export declare function sendLocalTrigger(planeUrl: string, req: LocalTriggerRequest): Promise<LocalTriggerResponse>;
56
+ export interface TriggerRunOptions {
57
+ client?: RunDiscoveryClient;
58
+ pollIntervalMs?: number;
59
+ resendAfterMs?: number;
60
+ timeoutMs?: number;
61
+ }
62
+ /**
63
+ * Trigger the run and resolve its runId. Sends the synthetic push, then polls
64
+ * the admin runs list filtered by this webhook's routing-key-scoped delivery id
65
+ * (`?deliveryId=`), resending the webhook past a grace window to absorb source
66
+ * hot-reload latency. Correlating on the exact delivery id — rather than the
67
+ * newest run in a time window — guarantees the follower attaches to the run
68
+ * THIS invocation created, even when another local run lands concurrently.
69
+ */
70
+ export declare function triggerRun(planeUrl: string, adminToken: string, input: LocalTriggerInput, opts?: TriggerRunOptions): Promise<string>;
71
+ //# sourceMappingURL=plane-trigger.d.ts.map
@@ -0,0 +1,110 @@
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 status + delivery id. */
49
+ async function sendLocalTrigger(planeUrl, req) {
50
+ const res = await fetch(`${planeUrl.replace(/\/$/, "")}${req.path}`, {
51
+ method: "POST",
52
+ headers: req.headers,
53
+ body: req.body
54
+ });
55
+ let deliveryId = null;
56
+ try {
57
+ const parsed = await res.json();
58
+ if (typeof parsed.deliveryId === "string") deliveryId = parsed.deliveryId;
59
+ } catch {}
60
+ return {
61
+ status: res.status,
62
+ deliveryId
63
+ };
64
+ }
65
+ /**
66
+ * Trigger the run and resolve its runId. Sends the synthetic push, then polls
67
+ * the admin runs list filtered by this webhook's routing-key-scoped delivery id
68
+ * (`?deliveryId=`), resending the webhook past a grace window to absorb source
69
+ * hot-reload latency. Correlating on the exact delivery id — rather than the
70
+ * newest run in a time window — guarantees the follower attaches to the run
71
+ * THIS invocation created, even when another local run lands concurrently.
72
+ */
73
+ async function triggerRun(planeUrl, adminToken, input, opts = {}) {
74
+ const client = opts.client ?? new AdminApiClient(planeUrl, adminToken);
75
+ const pollIntervalMs = opts.pollIntervalMs ?? 750;
76
+ const resendAfterMs = opts.resendAfterMs ?? 8e3;
77
+ const timeoutMs = opts.timeoutMs ?? 6e4;
78
+ const req = buildLocalTriggerRequest(input);
79
+ let deliveryId = (await sendLocalTrigger(planeUrl, req)).deliveryId;
80
+ let lastSend = Date.now();
81
+ const deadline = Date.now() + timeoutMs;
82
+ while (Date.now() < deadline) {
83
+ if (deliveryId) {
84
+ const runId = await findRunByDelivery(client, deliveryId);
85
+ if (runId) return runId;
86
+ }
87
+ if (Date.now() - lastSend > resendAfterMs) {
88
+ deliveryId ??= (await sendLocalTrigger(planeUrl, req)).deliveryId;
89
+ lastSend = Date.now();
90
+ }
91
+ await sleep(pollIntervalMs);
92
+ }
93
+ throw new Error("offline run: no run appeared after triggering the local plane");
94
+ }
95
+ /** Return the run created by this webhook delivery, or null when none yet. */
96
+ async function findRunByDelivery(client, deliveryId) {
97
+ const qs = new URLSearchParams({
98
+ deliveryId,
99
+ limit: "1"
100
+ });
101
+ const { runs } = await client.get(`/api/v1/admin/runs?${qs}`);
102
+ return runs[0]?.runId ?? null;
103
+ }
104
+ function sleep(ms) {
105
+ return new Promise((r) => setTimeout(r, ms));
106
+ }
107
+ //#endregion
108
+ export { buildLocalTriggerRequest, sendLocalTrigger, triggerRun };
109
+
110
+ //# 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,107 @@
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
+ return process.env.HOSTNAME || "kici-local";
103
+ }
104
+ //#endregion
105
+ export { derivePlatformWsUrl, mintOrchestratorKey, probePlatformReachable, revokeOrchestratorKey };
106
+
107
+ //# sourceMappingURL=platform-attach.js.map
@@ -0,0 +1,78 @@
1
+ /** Command runner, injectable so each platform branch is unit-testable anywhere. */
2
+ export type ExecFn = (cmd: string, args: string[]) => Promise<{
3
+ stdout: string;
4
+ exitCode: number;
5
+ }>;
6
+ /**
7
+ * Command line of process `pid`, or null when it cannot be determined.
8
+ *
9
+ * Linux exposes it as a NUL-separated `/proc` file; every other platform is
10
+ * asked through the same injectable `ExecFn` the port discovery above uses.
11
+ *
12
+ * **A null means "cannot tell", never "not that process."** Callers must treat
13
+ * it as an absence of evidence — the only safe reading when the answer decides
14
+ * whether to signal a pid.
15
+ */
16
+ export declare function processCommandLine(pid: number, opts?: {
17
+ exec?: ExecFn;
18
+ platform?: NodeJS.Platform;
19
+ readProc?: (path: string) => string;
20
+ }): Promise<string | null>;
21
+ /**
22
+ * Whether the port can be bound on loopback. Probes `127.0.0.1` because that is
23
+ * the address the CLI talks to; a listener bound to `0.0.0.0` still makes this
24
+ * bind fail with EADDRINUSE, so there is no false "free".
25
+ */
26
+ export declare function isPortFree(port: number): Promise<boolean>;
27
+ /**
28
+ * Parse `ss -lptnH` rows. The process name is deliberately NOT matched — a Node
29
+ * listener reports as `MainThread` — so only `pid=N` is extracted. A row for a
30
+ * socket this user does not own carries no `users:(…)` field at all and yields
31
+ * null, which is what sends the caller to the lsof fallback.
32
+ */
33
+ export declare function parseSsOutput(stdout: string, port: number): number | null;
34
+ /**
35
+ * Parse `lsof -Fpn` field output, which is a stateful stream: each `p<pid>` line
36
+ * precedes the `n<address>` lines belonging to that pid.
37
+ */
38
+ export declare function parseLsofOutput(stdout: string, port: number): number | null;
39
+ /** Parse `netstat -ano -p tcp`: LISTENING rows only, pid in the last column. */
40
+ export declare function parseNetstatOutput(stdout: string, port: number): number | null;
41
+ /**
42
+ * Pid of the process listening on `port`, or null when it cannot be determined.
43
+ * Each platform runs the UNFILTERED listing and matches the port here rather
44
+ * than passing a filter expression to the tool, whose syntax is the fragile
45
+ * part. On Linux a null parse falls through to lsof as well as a non-zero exit,
46
+ * because ss omits the owning-process field for sockets this user does not own.
47
+ */
48
+ export declare function findPortHolderPid(port: number, opts?: {
49
+ exec?: ExecFn;
50
+ platform?: NodeJS.Platform;
51
+ }): Promise<number | null>;
52
+ /** Signal sender, injectable so the escalation windows are unit-testable. */
53
+ export type KillFn = (pid: number, signal: NodeJS.Signals | 0) => void;
54
+ /**
55
+ * Poll until process `pid` has exited, sending SIGTERM first and escalating to
56
+ * SIGKILL past the grace window. `process.kill(pid, 0)` throws ESRCH once the
57
+ * process is gone. On Windows every signal terminates unconditionally, so the
58
+ * escalation collapses into a single hard kill there.
59
+ *
60
+ * Resolving does NOT prove the process died: a pid that survives SIGKILL (a
61
+ * zombie awaiting reaping, or a task wedged in uninterruptible I/O) is given
62
+ * `hardKillWaitMs` and then abandoned, because a teardown command that hangs
63
+ * forever is worse than one that reports the port is still held. Callers decide
64
+ * the outcome from the port itself — `waitForPortFree` — never from this
65
+ * resolving.
66
+ */
67
+ export declare function terminatePid(pid: number, graceMs?: number, opts?: {
68
+ hardKillWaitMs?: number;
69
+ kill?: KillFn;
70
+ }): Promise<void>;
71
+ /**
72
+ * Poll until the port is bindable. This, not `terminatePid` resolving, is what
73
+ * decides a teardown: `terminatePid` abandons a pid that survives SIGKILL, and
74
+ * even a process that did exit can leave its listening socket lingering a moment
75
+ * longer on some platforms.
76
+ */
77
+ export declare function waitForPortFree(port: number, timeoutMs?: number): Promise<boolean>;
78
+ //# sourceMappingURL=port-holder.d.ts.map