@kici-dev/compiler 0.1.26 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/headless-detect.d.ts +14 -5
- package/dist/auth/headless-detect.js +50 -9
- package/dist/cli-banner.d.ts +6 -3
- package/dist/cli-banner.js +13 -5
- package/dist/cli.js +180 -48
- package/dist/commands/check-mode.d.ts +1 -1
- package/dist/commands/compile.js +31 -7
- package/dist/commands/diagnostics.js +14 -2
- package/dist/commands/doctor.d.ts +64 -0
- package/dist/commands/doctor.js +413 -0
- package/dist/commands/endpoints.js +6 -0
- package/dist/commands/fixture.js +13 -0
- package/dist/commands/hook.js +3 -2
- package/dist/commands/index.d.ts +13 -1
- package/dist/commands/index.js +10 -3
- package/dist/commands/init-host-os.d.ts +34 -0
- package/dist/commands/init-host-os.js +44 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +160 -37
- package/dist/commands/local-trust-root.d.ts +6 -0
- package/dist/commands/local-trust-root.js +52 -0
- package/dist/commands/local.d.ts +65 -0
- package/dist/commands/local.js +206 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +41 -1
- package/dist/commands/logout.js +7 -0
- package/dist/commands/notifications.d.ts +61 -0
- package/dist/commands/notifications.js +274 -0
- package/dist/commands/preview.js +9 -2
- package/dist/commands/run-banner.d.ts +31 -0
- package/dist/commands/run-banner.js +25 -0
- package/dist/commands/run-routed.d.ts +53 -0
- package/dist/commands/run-routed.js +185 -0
- package/dist/commands/run.d.ts +0 -17
- package/dist/commands/run.js +44 -58
- package/dist/commands/runs/artifacts/download.d.ts +24 -0
- package/dist/commands/runs/artifacts/download.js +154 -0
- package/dist/commands/runs/artifacts/list.d.ts +5 -0
- package/dist/commands/runs/artifacts/list.js +47 -0
- package/dist/commands/runs/cancel.js +2 -2
- package/dist/commands/runs/list.js +8 -2
- package/dist/commands/secrets-list.d.ts +2 -2
- package/dist/commands/secrets-list.js +4 -4
- package/dist/commands/types.d.ts +3 -3
- package/dist/commands/types.js +4 -4
- package/dist/commands/verify-attestation.d.ts +3 -1
- package/dist/commands/verify-attestation.js +18 -7
- package/dist/errors/formatter.d.ts +14 -1
- package/dist/errors/formatter.js +13 -3
- package/dist/errors/index.d.ts +2 -1
- package/dist/errors/index.js +3 -2
- package/dist/errors/source-location.d.ts +20 -0
- package/dist/errors/source-location.js +45 -0
- package/dist/execution/executor.js +32 -12
- package/dist/fixtures/defaults/index.js +2 -1
- package/dist/generators/secrets-dts.d.ts +9 -9
- package/dist/generators/secrets-dts.js +12 -12
- package/dist/llm-context/llms-architecture.txt +50 -21
- package/dist/llm-context/llms-cli.txt +2667 -1454
- package/dist/llm-context/llms-features.txt +484 -414
- package/dist/llm-context/llms-full.txt +6703 -4687
- package/dist/llm-context/llms-getting-started.txt +311 -55
- package/dist/llm-context/llms-patterns.txt +17 -29
- package/dist/llm-context/llms-providers.txt +10 -1
- package/dist/llm-context/llms-sdk-runtime.txt +2013 -0
- package/dist/llm-context/llms-sdk.txt +596 -2157
- package/dist/llm-context/llms.txt +31 -17
- package/dist/local-plane/orchestrator-process.d.ts +70 -0
- package/dist/local-plane/orchestrator-process.js +129 -0
- package/dist/local-plane/os-mismatch-hint.d.ts +16 -0
- package/dist/local-plane/os-mismatch-hint.js +34 -0
- package/dist/local-plane/paths.d.ts +39 -0
- package/dist/local-plane/paths.js +54 -0
- package/dist/local-plane/plane-liveness.d.ts +68 -0
- package/dist/local-plane/plane-liveness.js +145 -0
- package/dist/local-plane/plane-manager.d.ts +193 -0
- package/dist/local-plane/plane-manager.js +480 -0
- package/dist/local-plane/plane-seed.d.ts +76 -0
- package/dist/local-plane/plane-seed.js +59 -0
- package/dist/local-plane/plane-trigger.d.ts +71 -0
- package/dist/local-plane/plane-trigger.js +110 -0
- package/dist/local-plane/platform-attach.d.ts +53 -0
- package/dist/local-plane/platform-attach.js +107 -0
- package/dist/local-plane/port-holder.d.ts +78 -0
- package/dist/local-plane/port-holder.js +227 -0
- package/dist/local-plane/postgres.d.ts +56 -0
- package/dist/local-plane/postgres.js +187 -0
- package/dist/local-plane/resolve-plane.d.ts +37 -0
- package/dist/local-plane/resolve-plane.js +76 -0
- package/dist/local-plane/run-follow.d.ts +61 -0
- package/dist/local-plane/run-follow.js +151 -0
- package/dist/local-plane/scaler-config.d.ts +68 -0
- package/dist/local-plane/scaler-config.js +132 -0
- package/dist/local-plane/secret-seed.d.ts +59 -0
- package/dist/local-plane/secret-seed.js +112 -0
- package/dist/local-plane/source-provider.d.ts +43 -0
- package/dist/local-plane/source-provider.js +159 -0
- package/dist/local-plane/trusted-routing.d.ts +32 -0
- package/dist/local-plane/trusted-routing.js +47 -0
- package/dist/lockfile/generator.d.ts +14 -1
- package/dist/lockfile/generator.js +103 -37
- package/dist/lockfile/index.d.ts +3 -1
- package/dist/lockfile/index.js +3 -2
- package/dist/lockfile/purity-diagnostics.d.ts +31 -0
- package/dist/lockfile/purity-diagnostics.js +52 -0
- package/dist/postinstall.js +2 -1
- package/dist/remote/artifact-extract.d.ts +8 -0
- package/dist/remote/artifact-extract.js +58 -0
- package/dist/remote/config.d.ts +21 -3
- package/dist/remote/config.js +24 -5
- package/dist/remote/dashboard-client.d.ts +46 -9
- package/dist/remote/dashboard-client.js +54 -11
- package/dist/remote/fs-case.d.ts +21 -0
- package/dist/remote/fs-case.js +45 -0
- package/dist/remote/history.d.ts +9 -3
- package/dist/remote/history.js +79 -10
- package/dist/remote/local-repo-identity.d.ts +1 -1
- package/dist/remote/local-repo-identity.js +2 -1
- package/dist/remote/notifications-client.d.ts +149 -0
- package/dist/remote/notifications-client.js +103 -0
- package/dist/remote/oauth.d.ts +13 -0
- package/dist/remote/oauth.js +31 -5
- package/dist/remote/output/summary.js +76 -14
- package/dist/remote/platform-client.d.ts +1 -1
- package/dist/remote/render.js +30 -9
- package/dist/{local-executor → remote}/secret-loader.d.ts +1 -1
- package/dist/{local-executor → remote}/secret-loader.js +4 -4
- package/dist/remote/secret-upload.d.ts +1 -1
- package/dist/remote/secret-upload.js +2 -2
- package/dist/remote/uploader.d.ts +1 -1
- package/dist/remote/uploader.js +7 -6
- package/dist/templates/agents-md.d.ts +1 -1
- package/dist/templates/agents-md.js +8 -8
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.js +2 -2
- package/dist/templates/package-json.d.ts +15 -0
- package/dist/templates/package-json.js +26 -5
- package/dist/test-runner/dry-run.d.ts +2 -1
- package/dist/test-runner/dry-run.js +18 -1
- package/dist/test-runner/event-types.d.ts +2 -0
- package/dist/test-runner/event-types.js +3 -0
- package/dist/test-runner/job-executor.d.ts +1 -0
- package/dist/test-runner/job-executor.js +82 -44
- package/dist/test-runner/output-formatter.d.ts +5 -2
- package/dist/test-runner/output-formatter.js +6 -4
- package/dist/test-runner/payload-builder.d.ts +7 -1
- package/dist/test-runner/payload-builder.js +11 -2
- package/dist/test-runner/rule-evaluator.d.ts +7 -3
- package/dist/test-runner/rule-evaluator.js +13 -12
- package/dist/{local-executor → test-runner}/runs-on-display.js +1 -1
- package/dist/test-runner/step-context.d.ts +23 -5
- package/dist/test-runner/step-context.js +29 -119
- package/dist/types.d.ts +26 -7
- package/dist/types.js +4 -2
- package/dist/validation/index.d.ts +2 -0
- package/dist/validation/index.js +2 -1
- package/dist/validation/typecheck.d.ts +28 -0
- package/dist/validation/typecheck.js +110 -0
- package/dist/validation/validator.d.ts +9 -4
- package/dist/validation/validator.js +38 -44
- package/package.json +13 -13
- package/sbom.spdx.json +9311 -1528
- package/dist/assets/api-TJJVHrjC.json +0 -118
- package/dist/assets/descriptor-BTtjzN9L.json +0 -1382
- package/dist/assets/package-BpQF9kR8.json +0 -74
- package/dist/assets/package-Ceo2h27X.json +0 -89
- package/dist/assets/source_context-D0atuL28.json +0 -20
- package/dist/assets/type-BFqO8SCZ.json +0 -202
- package/dist/commands/cancel.d.ts +0 -22
- package/dist/commands/detect-package-manager.d.ts +0 -42
- package/dist/commands/held-run-resolve.d.ts +0 -50
- package/dist/commands/status.d.ts +0 -34
- package/dist/commands/test.d.ts +0 -88
- package/dist/errors/codes.d.ts +0 -25
- package/dist/local-executor/dag-scheduler.d.ts +0 -44
- package/dist/local-executor/dag-scheduler.js +0 -183
- package/dist/local-executor/index.d.ts +0 -23
- package/dist/local-executor/index.js +0 -378
- package/dist/local-executor/job-runner.d.ts +0 -47
- package/dist/local-executor/job-runner.js +0 -411
- package/dist/local-executor/materializer.d.ts +0 -44
- package/dist/local-executor/materializer.js +0 -132
- package/dist/local-executor/output-streamer.d.ts +0 -31
- package/dist/local-executor/output-streamer.js +0 -168
- package/dist/local-executor/payload-generator.d.ts +0 -16
- package/dist/local-executor/payload-generator.js +0 -138
- package/dist/local-executor/picker.d.ts +0 -33
- package/dist/local-executor/picker.js +0 -109
- package/dist/local-executor/to-event-payload.d.ts +0 -16
- package/dist/local-executor/to-event-payload.js +0 -21
- package/dist/local-executor/types.d.ts +0 -93
- package/dist/local-executor/types.js +0 -2
- package/dist/local-executor/workflow-lock.d.ts +0 -82
- package/dist/local-executor/workflow-lock.js +0 -0
- package/dist/package-F7UXSDHW.json +0 -74
- package/dist/remote/client.d.ts +0 -210
- package/dist/remote/observer.d.ts +0 -81
- package/dist/test-runner/summary.d.ts +0 -7
- /package/dist/{local-executor → test-runner}/runs-on-display.d.ts +0 -0
|
@@ -8,19 +8,23 @@ The full markdown bundle of every page indexed here is available at https://docs
|
|
|
8
8
|
|
|
9
9
|
Each bundle below is a self-contained markdown file for one authoring task. Fetch only the one your task needs instead of the full bundle:
|
|
10
10
|
|
|
11
|
-
- [getting-started](https://docs.kici.dev/llms-getting-started.txt) (
|
|
11
|
+
- [getting-started](https://docs.kici.dev/llms-getting-started.txt) (51 KB) — Adopt KiCI: why it exists, how workflows execute, migrating from GitHub Actions, installing the SDK, and writing/compiling/testing your first workflow
|
|
12
12
|
- [patterns](https://docs.kici.dev/llms-patterns.txt) (49 KB) — Copy-paste workflow recipes: triggers, conditionals, matrix, scheduling, integrations
|
|
13
|
-
- [sdk](https://docs.kici.dev/llms-sdk.txt) (
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
13
|
+
- [sdk](https://docs.kici.dev/llms-sdk.txt) (110 KB) — Core authoring API: workflow/job/step factories, triggers, rules, matrix, dynamic jobs, cross-job outputs
|
|
14
|
+
- [sdk-runtime](https://docs.kici.dev/llms-sdk-runtime.txt) (112 KB) — Runtime and advanced authoring: step runtime context, event payloads, host fan-out, idempotent steps, caching, artifacts, waiting
|
|
15
|
+
- [cli](https://docs.kici.dev/llms-cli.txt) (185 KB) — Running the CLI: compile, test, run local/remote, auth, hooks, lock-file drift
|
|
16
|
+
- [features](https://docs.kici.dev/llms-features.txt) (135 KB) — Workflow features: concurrency, contexts, secrets, approvals, provenance, events
|
|
17
|
+
- [providers](https://docs.kici.dev/llms-providers.txt) (37 KB) — Connecting sources: GitHub App, universal-git (Forgejo/Gitea/GitLab), local file://
|
|
18
|
+
- [architecture](https://docs.kici.dev/llms-architecture.txt) (92 KB) — How the runtime works: three-tier relay model, data flows, configuration
|
|
18
19
|
|
|
19
20
|
## Getting started
|
|
20
21
|
|
|
21
|
-
- [User guide](https://docs.kici.dev/user/): Writing and testing CI/CD workflows in TypeScript
|
|
22
|
+
- [User guide](https://docs.kici.dev/user/): Writing and testing CI/CD workflows in TypeScript, running on infrastructure you control
|
|
23
|
+
- [How your workflow code executes](https://docs.kici.dev/user/execution-model/): When and where your workflow TypeScript runs — compile time, orchestrator time, and agent time
|
|
22
24
|
- [Getting started with workflows](https://docs.kici.dev/user/getting-started/): Install the SDK, write your first workflow, compile and test locally
|
|
25
|
+
- [Migrating from GitHub Actions](https://docs.kici.dev/user/migrating-from-github-actions/): Map GitHub Actions concepts to KiCI, translate a real workflow side-by-side, and see what has no equivalent yet
|
|
23
26
|
- [5-minute quickstart](https://docs.kici.dev/user/quickstart/): Stand up a KiCI orchestrator + agent and run your first workflow
|
|
27
|
+
- [Why KiCI](https://docs.kici.dev/user/why-kici/): The case for running CI on infrastructure you control, with typed TypeScript workflows you test locally
|
|
24
28
|
|
|
25
29
|
## Workflow patterns
|
|
26
30
|
|
|
@@ -31,40 +35,50 @@ Each bundle below is a self-contained markdown file for one authoring task. Fetc
|
|
|
31
35
|
- [Pattern reference](https://docs.kici.dev/user/patterns/reference/): Step context, examples repository, GitHub check run output — cross-cutting reference for all patterns
|
|
32
36
|
- [Scheduling & event patterns](https://docs.kici.dev/user/patterns/scheduling-and-events/): Nightly cron, workflow-complete-triggered deploys, custom event chaining
|
|
33
37
|
|
|
34
|
-
## SDK reference
|
|
38
|
+
## SDK reference: core
|
|
35
39
|
|
|
36
|
-
- [Caching](https://docs.kici.dev/user/sdk/caching/): Cache files and directories across runs with declarative job/step cache or the imperative ctx.cache API
|
|
37
40
|
- [SDK reference: core](https://docs.kici.dev/user/sdk/core/): Factory functions (workflow, job, step) and authoring patterns: needs, output chaining, dynamic groups
|
|
38
|
-
- [Event payload reference](https://docs.kici.dev/user/sdk/event-payloads/): Generated schema of the normalized event envelope passed to rules and dynamic functions.
|
|
39
|
-
- [SDK reference: idempotent](https://docs.kici.dev/user/sdk/idempotent/): Idempotent helpers for declarative check / apply patterns inside workflow steps
|
|
40
41
|
- [SDK reference: parallel](https://docs.kici.dev/user/sdk/parallel/): Run independent steps concurrently within a single job, each as its own observable step
|
|
41
42
|
- [SDK reference: rules, matrix, dynamic jobs](https://docs.kici.dev/user/sdk/rules-matrix-dynamic/): rule(), skip(), matrix builds (static + dynamic), and dynamicJob / dynamicGroup
|
|
43
|
+
- [SDK reference: triggers](https://docs.kici.dev/user/sdk/triggers/): GitHub event triggers, kiciEvent, workflowComplete, workflowsFailedBatch, jobComplete, genericWebhook, schedule, lifecycle
|
|
44
|
+
- [SDK reference: validation & events](https://docs.kici.dev/user/sdk/validation-events/): validateDag(), event definitions, and event emission
|
|
45
|
+
- [SDK reference](https://docs.kici.dev/user/sdk-reference/): Complete API reference for @kici-dev/sdk -- workflows, jobs, steps, triggers, rules, matrix, validation, runtime
|
|
46
|
+
|
|
47
|
+
## SDK reference: runtime and advanced
|
|
48
|
+
|
|
49
|
+
- [Artifacts](https://docs.kici.dev/user/sdk/artifacts/): Share named, durable build outputs between jobs of a run and download them from the run page with ctx.artifacts
|
|
50
|
+
- [Caching](https://docs.kici.dev/user/sdk/caching/): Cache files and directories across runs with declarative job/step cache or the imperative ctx.cache API
|
|
51
|
+
- [Event payload reference](https://docs.kici.dev/user/sdk/event-payloads/): Generated schema of the normalized event envelope passed to rules and dynamic functions.
|
|
52
|
+
- [SDK reference: idempotent](https://docs.kici.dev/user/sdk/idempotent/): Idempotent helpers for declarative check / apply patterns inside workflow steps
|
|
42
53
|
- [SDK reference: runsOnAll host fan-out](https://docs.kici.dev/user/sdk/runs-on-all/): Fan one job out to every matching connected host, one pinned execution per host
|
|
43
54
|
- [SDK reference: runtime](https://docs.kici.dev/user/sdk/runtime/): Types index, StepContext, secrets, fixtures
|
|
44
|
-
- [SDK reference:
|
|
45
|
-
- [SDK reference: validation & events](https://docs.kici.dev/user/sdk/validation-events/): validateDag(), event definitions, and event emission
|
|
55
|
+
- [SDK reference: temp directories](https://docs.kici.dev/user/sdk/temp-directories/): Allocate job-scoped scratch dirs and files with ctx.mktemp() / ctx.mktempFile()
|
|
46
56
|
- [SDK reference: waitFor](https://docs.kici.dev/user/sdk/wait-for/): Polling helpers for steps that pause until an external condition becomes true
|
|
47
|
-
- [SDK reference](https://docs.kici.dev/user/sdk-reference/): Complete API reference for @kici-dev/sdk -- workflows, jobs, steps, triggers, rules, matrix, validation, runtime
|
|
48
57
|
|
|
49
58
|
## CLI and authoring
|
|
50
59
|
|
|
51
60
|
- [Drive KiCI from your coding agent](https://docs.kici.dev/user/ai-agents/): Point a coding agent at KiCI's MCP server and let it trigger, read, and re-run your CI under your own identity.
|
|
52
61
|
- [CLI authentication](https://docs.kici.dev/user/cli-auth/): Authenticate the KiCI CLI with browser OAuth, device flow, or API key paste
|
|
53
|
-
- [CLI reference](https://docs.kici.dev/user/cli-reference/): All CLI commands: compile,
|
|
62
|
+
- [CLI reference](https://docs.kici.dev/user/cli-reference/): All CLI commands: compile, preview, local, fixture, types, workflows, hook, docs, run, runs, approve, reject, login, logout, init, org, pat, secrets, admin, orchestrators, endpoints, notifications, verify-attestation, diagnostics, doctor
|
|
63
|
+
- [Common failures](https://docs.kici.dev/user/common-failures/): Symptom-to-fix reference for the failures workflow authors hit most — no jobs dispatched, lock-file drift, missing webhooks, and agents that won't connect
|
|
54
64
|
- [Lifecycle hooks](https://docs.kici.dev/user/hooks/): SDK hook API for cancel, cleanup, success, failure, and step-level callbacks
|
|
55
65
|
- [Lock file and workflow drift](https://docs.kici.dev/user/lock-file-and-drift/): Keep the lock file in sync with your workflow source and avoid drift
|
|
56
66
|
- [Testing guide](https://docs.kici.dev/user/testing-guide/): Running remote test fixtures with kici run remote
|
|
57
67
|
- [Workflow patterns](https://docs.kici.dev/user/workflow-patterns/): Common patterns: monorepo builds, conditional jobs, dynamic matrices, generic webhooks, scheduling
|
|
68
|
+
- [kici: account & org](https://docs.kici.dev/user/cli/account-and-org/): Login, org selection, personal access tokens, secrets, and admin
|
|
69
|
+
- [kici: authoring & local dev](https://docs.kici.dev/user/cli/authoring-and-local/): Compile, preview, local execution, fixtures, types, workflows, hooks, and docs
|
|
70
|
+
- [kici: notifications & diagnostics](https://docs.kici.dev/user/cli/notifications-and-diagnostics/): Notification channels, attestation verification, and diagnostics
|
|
71
|
+
- [kici: runs & approvals](https://docs.kici.dev/user/cli/runs-and-approvals/): Run push and inspection plus approval / rejection of held runs
|
|
58
72
|
|
|
59
73
|
## Workflow features
|
|
60
74
|
|
|
61
75
|
- [Account and sign-in](https://docs.kici.dev/user/account-and-login/): How your KiCI account relates to sign-in methods, and how to change the way you sign in.
|
|
62
76
|
- [Approval gates](https://docs.kici.dev/user/approvals/): Pause a workflow for human sign-off at step, job, or workflow granularity with approval
|
|
63
77
|
- [Concurrency groups](https://docs.kici.dev/user/concurrency/): Control parallel execution with auto-cancel and queue modes
|
|
78
|
+
- [Contexts](https://docs.kici.dev/user/contexts/): Configure deployment contexts with variables, secrets, and protection rules
|
|
64
79
|
- [Dashboard](https://docs.kici.dev/user/dashboard/): Web UI for monitoring workflow runs, managing sources, secrets, and organization settings.
|
|
65
80
|
- [Dynamic values](https://docs.kici.dev/user/dynamic-values/)
|
|
66
|
-
- [Environment variables](https://docs.kici.dev/user/env-vars/):
|
|
67
|
-
- [Environments](https://docs.kici.dev/user/environments/): Configure deployment environments with variables, secrets, and protection rules
|
|
81
|
+
- [Environment variables](https://docs.kici.dev/user/env-vars/): Environment variable reference for the CLI
|
|
68
82
|
- [Event system](https://docs.kici.dev/user/events/): How KiCI's event model works -- event types, the registration model, event matching, and circuit breaker protection
|
|
69
83
|
- [Global workflows](https://docs.kici.dev/user/global-workflows/): Cross-repo workflows that run on events from any repo in the same org
|
|
70
84
|
- [Idempotent steps and check mode](https://docs.kici.dev/user/idempotent-steps/): Declare desired state with a step check facet, then run in apply or --check preview mode
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a host address the plane's co-located bare-metal agent can reach for
|
|
3
|
+
* cache-blob fetches. The orchestrator refuses to hand a scaler a loopback
|
|
4
|
+
* storage URL (it assumes remote agents), so the plane advertises its first
|
|
5
|
+
* non-internal IPv4 (still this machine — the agent is co-located). Falls back
|
|
6
|
+
* to the hostname when no external interface is present.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveAgentReachableHost(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the built `@kici-dev/orchestrator/standalone` entry the local plane
|
|
11
|
+
* launches in independent (offline) mode.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveStandaloneEntry(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the built `@kici-dev/orchestrator/server` entry the local plane
|
|
16
|
+
* launches in hybrid (Platform-attached) mode.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveServerEntry(): string;
|
|
19
|
+
/** Single `/ready` probe against the local orchestrator. */
|
|
20
|
+
export declare function orchestratorReady(port: number): Promise<boolean>;
|
|
21
|
+
/** Options threading the plane's admin token + bare-metal scaler into the boot. */
|
|
22
|
+
export interface SpawnOrchestratorOptions {
|
|
23
|
+
/** Bootstrap admin token the CLI presents to the plane's admin API. */
|
|
24
|
+
adminToken: string;
|
|
25
|
+
/**
|
|
26
|
+
* Local master secret key (64 hex chars). Enables the orchestrator's secrets
|
|
27
|
+
* subsystem, which is what gates the admin API routes the CLI drives.
|
|
28
|
+
*/
|
|
29
|
+
secretKey: string;
|
|
30
|
+
/** Path to the bare-metal scaler YAML (KICI_SCALER_CONFIG_PATH). */
|
|
31
|
+
scalerConfigFile: string;
|
|
32
|
+
/**
|
|
33
|
+
* Path to the dev-signed identity's EC P-256 private JWK (mode 0600), freshly
|
|
34
|
+
* generated by the plane. Enables the orchestrator's local OIDC mint +
|
|
35
|
+
* dev-signed provenance (issuer `kici-local`) in independent mode. Present in
|
|
36
|
+
* the independent boot; omitted (with `attach`) in the hybrid boot, which
|
|
37
|
+
* mints via the Platform relay instead.
|
|
38
|
+
*/
|
|
39
|
+
devIdentityKeyFile?: string;
|
|
40
|
+
/**
|
|
41
|
+
* When present, boot the orchestrator in HYBRID mode attached to the Platform
|
|
42
|
+
* relay instead of independent mode. OIDC + attestation then mint via the
|
|
43
|
+
* Platform (the local dev signer is never consulted — the orchestrator's
|
|
44
|
+
* `selectOidcMintRegistration` prefers the relay whenever a Platform
|
|
45
|
+
* connection exists).
|
|
46
|
+
*/
|
|
47
|
+
attach?: {
|
|
48
|
+
/** Orchestrator KICI_PLATFORM_URL — a ws(s)://…/ws relay URL. */
|
|
49
|
+
platformWsUrl: string;
|
|
50
|
+
/** Org-scoped orchestrator key (kici_ok_…) presented on WS auth. */
|
|
51
|
+
platformToken: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Spawn the orchestrator entry against the local Postgres, detached, with
|
|
56
|
+
* stdout/stderr redirected to the plane log, and return immediately. Boots with
|
|
57
|
+
* a bootstrap admin token (so the CLI can drive the admin API) and a bare-metal
|
|
58
|
+
* scaler (so a dispatched job auto-spawns an ephemeral agent).
|
|
59
|
+
*
|
|
60
|
+
* Returning before the process is ready is deliberate: it lets the caller record
|
|
61
|
+
* the pid on disk BEFORE the readiness wait, so a wait that fails or a CLI that
|
|
62
|
+
* dies mid-boot cannot leave a running orchestrator that nothing can identify.
|
|
63
|
+
*/
|
|
64
|
+
export declare function spawnOrchestratorProcess(databaseUrl: string, opts: SpawnOrchestratorOptions): {
|
|
65
|
+
pid: number;
|
|
66
|
+
port: number;
|
|
67
|
+
};
|
|
68
|
+
/** Poll `/ready` until the orchestrator serves, or throw once the attempts run out. */
|
|
69
|
+
export declare function awaitOrchestratorReady(port: number, attempts?: number, intervalMs?: number): Promise<void>;
|
|
70
|
+
//# sourceMappingURL=orchestrator-process.d.ts.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { planePaths, planePorts } from "./paths.js";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
//#region src/local-plane/orchestrator-process.ts
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a host address the plane's co-located bare-metal agent can reach for
|
|
10
|
+
* cache-blob fetches. The orchestrator refuses to hand a scaler a loopback
|
|
11
|
+
* storage URL (it assumes remote agents), so the plane advertises its first
|
|
12
|
+
* non-internal IPv4 (still this machine — the agent is co-located). Falls back
|
|
13
|
+
* to the hostname when no external interface is present.
|
|
14
|
+
*/
|
|
15
|
+
function resolveAgentReachableHost() {
|
|
16
|
+
for (const addrs of Object.values(os.networkInterfaces())) for (const a of addrs ?? []) if (a.family === "IPv4" && !a.internal) return a.address;
|
|
17
|
+
return os.hostname();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the built `@kici-dev/orchestrator/standalone` entry the local plane
|
|
21
|
+
* launches in independent (offline) mode.
|
|
22
|
+
*/
|
|
23
|
+
function resolveStandaloneEntry() {
|
|
24
|
+
return createRequire(import.meta.url).resolve("@kici-dev/orchestrator/standalone");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the built `@kici-dev/orchestrator/server` entry the local plane
|
|
28
|
+
* launches in hybrid (Platform-attached) mode.
|
|
29
|
+
*/
|
|
30
|
+
function resolveServerEntry() {
|
|
31
|
+
return createRequire(import.meta.url).resolve("@kici-dev/orchestrator/server");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Cap on one readiness probe. Generous enough for a server still warming up,
|
|
35
|
+
* bounded so a socket that accepts and then goes silent cannot stall the poll
|
|
36
|
+
* for the HTTP client's own multi-minute default — which would make
|
|
37
|
+
* `awaitOrchestratorReady`'s attempt budget meaningless.
|
|
38
|
+
*/
|
|
39
|
+
const READY_PROBE_TIMEOUT_MS = 5e3;
|
|
40
|
+
/** Single `/ready` probe against the local orchestrator. */
|
|
41
|
+
async function orchestratorReady(port) {
|
|
42
|
+
try {
|
|
43
|
+
return (await fetch(`http://127.0.0.1:${port}/ready`, { signal: AbortSignal.timeout(READY_PROBE_TIMEOUT_MS) })).status === 200;
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Spawn the orchestrator entry against the local Postgres, detached, with
|
|
50
|
+
* stdout/stderr redirected to the plane log, and return immediately. Boots with
|
|
51
|
+
* a bootstrap admin token (so the CLI can drive the admin API) and a bare-metal
|
|
52
|
+
* scaler (so a dispatched job auto-spawns an ephemeral agent).
|
|
53
|
+
*
|
|
54
|
+
* Returning before the process is ready is deliberate: it lets the caller record
|
|
55
|
+
* the pid on disk BEFORE the readiness wait, so a wait that fails or a CLI that
|
|
56
|
+
* dies mid-boot cannot leave a running orchestrator that nothing can identify.
|
|
57
|
+
*/
|
|
58
|
+
function spawnOrchestratorProcess(databaseUrl, opts) {
|
|
59
|
+
const { orchestrator: port } = planePorts();
|
|
60
|
+
const { logFile, root, cacheDir } = planePaths();
|
|
61
|
+
fs.mkdirSync(root, { recursive: true });
|
|
62
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
63
|
+
const out = fs.openSync(logFile, "a");
|
|
64
|
+
const baseEnv = {};
|
|
65
|
+
for (const [key, value] of Object.entries(process.env)) if (!key.startsWith("KICI_")) baseEnv[key] = value;
|
|
66
|
+
const commonEnv = {
|
|
67
|
+
...baseEnv,
|
|
68
|
+
KICI_PORT: String(port),
|
|
69
|
+
KICI_DATABASE_URL: databaseUrl,
|
|
70
|
+
KICI_AGENT_AUTH: "none",
|
|
71
|
+
KICI_AUTO_MIGRATE: "true",
|
|
72
|
+
KICI_DATA_DIR: root,
|
|
73
|
+
KICI_BOOTSTRAP_ADMIN_TOKEN: opts.adminToken,
|
|
74
|
+
KICI_SECRET_KEY: opts.secretKey,
|
|
75
|
+
KICI_SCALER_CONFIG_PATH: opts.scalerConfigFile,
|
|
76
|
+
KICI_WEBHOOK_PUBLIC_URL: `http://127.0.0.1:${port}`,
|
|
77
|
+
KICI_STORAGE_TYPE: "filesystem",
|
|
78
|
+
KICI_STORAGE_FS_PATH: cacheDir,
|
|
79
|
+
KICI_STORAGE_FS_BASE_URL: `http://${resolveAgentReachableHost()}:${port}`
|
|
80
|
+
};
|
|
81
|
+
let entry;
|
|
82
|
+
let modeEnv;
|
|
83
|
+
if (opts.attach) {
|
|
84
|
+
entry = resolveServerEntry();
|
|
85
|
+
modeEnv = {
|
|
86
|
+
KICI_MODE: "hybrid",
|
|
87
|
+
KICI_PLATFORM_URL: opts.attach.platformWsUrl,
|
|
88
|
+
KICI_PLATFORM_TOKEN: opts.attach.platformToken
|
|
89
|
+
};
|
|
90
|
+
} else {
|
|
91
|
+
entry = resolveStandaloneEntry();
|
|
92
|
+
modeEnv = {
|
|
93
|
+
KICI_MODE: "independent",
|
|
94
|
+
KICI_INDEPENDENT_SECRETS: "true",
|
|
95
|
+
KICI_INDEPENDENT_IDENTITY: "true",
|
|
96
|
+
KICI_DEV_IDENTITY_KEY_FILE: opts.devIdentityKeyFile,
|
|
97
|
+
KICI_PROVENANCE_ISSUER: "kici-local"
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const child = spawn(process.execPath, [entry], {
|
|
101
|
+
detached: true,
|
|
102
|
+
stdio: [
|
|
103
|
+
"ignore",
|
|
104
|
+
out,
|
|
105
|
+
out
|
|
106
|
+
],
|
|
107
|
+
env: {
|
|
108
|
+
...commonEnv,
|
|
109
|
+
...modeEnv
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
child.unref();
|
|
113
|
+
return {
|
|
114
|
+
pid: child.pid,
|
|
115
|
+
port
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** Poll `/ready` until the orchestrator serves, or throw once the attempts run out. */
|
|
119
|
+
async function awaitOrchestratorReady(port, attempts = 120, intervalMs = 500) {
|
|
120
|
+
for (let i = 0; i < attempts; i++) {
|
|
121
|
+
if (await orchestratorReady(port)) return;
|
|
122
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
123
|
+
}
|
|
124
|
+
throw new Error("local orchestrator did not become ready");
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
export { awaitOrchestratorReady, orchestratorReady, resolveAgentReachableHost, resolveServerEntry, resolveStandaloneEntry, spawnOrchestratorProcess };
|
|
128
|
+
|
|
129
|
+
//# sourceMappingURL=orchestrator-process.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect when a routed local run cannot match the host OS.
|
|
3
|
+
*
|
|
4
|
+
* The local dev plane satisfies the host's own `kici:os:*` label (the scaler
|
|
5
|
+
* injects the host's derived OS labels into its bare-metal label sets). A job
|
|
6
|
+
* that requests a DIFFERENT OS than the host — e.g. a workflow authored on Linux
|
|
7
|
+
* run on a Mac — matches no local backend and dispatches nothing. This surfaces
|
|
8
|
+
* that as an honest hint instead of a silent "nothing happened".
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* One hint per job whose `runsOn` requires a `kici:os:*` label the host does not
|
|
12
|
+
* provide. Empty when every job either omits an OS selector or requests the
|
|
13
|
+
* host's own OS.
|
|
14
|
+
*/
|
|
15
|
+
export declare function detectOsMismatchHints(lock: unknown, platform: string, arch: string): string[];
|
|
16
|
+
//# sourceMappingURL=os-mismatch-hint.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { deriveOsArchLabels } from "@kici-dev/engine";
|
|
3
|
+
//#region src/local-plane/os-mismatch-hint.ts
|
|
4
|
+
/**
|
|
5
|
+
* Detect when a routed local run cannot match the host OS.
|
|
6
|
+
*
|
|
7
|
+
* The local dev plane satisfies the host's own `kici:os:*` label (the scaler
|
|
8
|
+
* injects the host's derived OS labels into its bare-metal label sets). A job
|
|
9
|
+
* that requests a DIFFERENT OS than the host — e.g. a workflow authored on Linux
|
|
10
|
+
* run on a Mac — matches no local backend and dispatches nothing. This surfaces
|
|
11
|
+
* that as an honest hint instead of a silent "nothing happened".
|
|
12
|
+
*/
|
|
13
|
+
const OS_LABEL_PREFIX = "kici:os:";
|
|
14
|
+
/**
|
|
15
|
+
* One hint per job whose `runsOn` requires a `kici:os:*` label the host does not
|
|
16
|
+
* provide. Empty when every job either omits an OS selector or requests the
|
|
17
|
+
* host's own OS.
|
|
18
|
+
*/
|
|
19
|
+
function detectOsMismatchHints(lock, platform, arch) {
|
|
20
|
+
const hostOsLabels = new Set(deriveOsArchLabels(platform, arch).filter((l) => l.startsWith(OS_LABEL_PREFIX)));
|
|
21
|
+
const hostPrimary = [...hostOsLabels][0] ?? `${OS_LABEL_PREFIX}${platform}`;
|
|
22
|
+
const hints = [];
|
|
23
|
+
for (const workflow of lock.workflows ?? []) for (const job of workflow.jobs ?? []) {
|
|
24
|
+
const requestedOs = (job.runsOn ?? []).filter((m) => m.kind === "exact" && m.value?.startsWith(OS_LABEL_PREFIX)).map((m) => m.value);
|
|
25
|
+
if (requestedOs.length === 0) continue;
|
|
26
|
+
if (requestedOs.some((l) => hostOsLabels.has(l))) continue;
|
|
27
|
+
hints.push(`Job '${job.name ?? "job"}' wants ${requestedOs.join(", ")} but this host is ${platform} — nothing will dispatch locally. Retry with ${hostPrimary} or edit the job's runsOn in .kici/workflows/.`);
|
|
28
|
+
}
|
|
29
|
+
return hints;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { detectOsMismatchHints };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=os-mismatch-hint.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* On-disk layout version of the local dev plane. Bumped when the state-dir
|
|
3
|
+
* structure changes incompatibly, so a stamped plane from an older layout is
|
|
4
|
+
* recreated rather than reused.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PLANE_STAMP_VERSION = 3;
|
|
7
|
+
/**
|
|
8
|
+
* Root directory of the local dev plane's state, following the same
|
|
9
|
+
* `KICI_CONFIG_DIR` → `~/.kici` convention the rest of the CLI uses.
|
|
10
|
+
*/
|
|
11
|
+
export declare function planeRoot(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Absolute paths for every artefact the plane persists under its state root.
|
|
14
|
+
*/
|
|
15
|
+
export declare function planePaths(): {
|
|
16
|
+
root: string;
|
|
17
|
+
pgData: string;
|
|
18
|
+
pidfile: string;
|
|
19
|
+
stampFile: string;
|
|
20
|
+
logFile: string;
|
|
21
|
+
socketDir: string;
|
|
22
|
+
adminTokenFile: string;
|
|
23
|
+
platformTokenFile: string;
|
|
24
|
+
attachmentFile: string;
|
|
25
|
+
secretKeyFile: string;
|
|
26
|
+
scalerConfigFile: string;
|
|
27
|
+
agentWrapperFile: string;
|
|
28
|
+
devIdentityDir: string;
|
|
29
|
+
cacheDir: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Fixed localhost ports for the plane's orchestrator (HTTP+WS) and Postgres.
|
|
33
|
+
* Overridable via `KICI_LOCAL_ORCH_PORT` / `KICI_LOCAL_PG_PORT`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function planePorts(): {
|
|
36
|
+
orchestrator: number;
|
|
37
|
+
postgres: number;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
//#region src/local-plane/paths.ts
|
|
5
|
+
/**
|
|
6
|
+
* On-disk layout version of the local dev plane. Bumped when the state-dir
|
|
7
|
+
* structure changes incompatibly, so a stamped plane from an older layout is
|
|
8
|
+
* recreated rather than reused.
|
|
9
|
+
*/
|
|
10
|
+
const PLANE_STAMP_VERSION = 3;
|
|
11
|
+
/**
|
|
12
|
+
* Root directory of the local dev plane's state, following the same
|
|
13
|
+
* `KICI_CONFIG_DIR` → `~/.kici` convention the rest of the CLI uses.
|
|
14
|
+
*/
|
|
15
|
+
function planeRoot() {
|
|
16
|
+
const base = process.env.KICI_CONFIG_DIR ?? path.join(os.homedir(), ".kici");
|
|
17
|
+
return path.join(base, "local");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Absolute paths for every artefact the plane persists under its state root.
|
|
21
|
+
*/
|
|
22
|
+
function planePaths() {
|
|
23
|
+
const root = planeRoot();
|
|
24
|
+
return {
|
|
25
|
+
root,
|
|
26
|
+
pgData: path.join(root, "pgdata"),
|
|
27
|
+
pidfile: path.join(root, "plane.pid"),
|
|
28
|
+
stampFile: path.join(root, "stamp.json"),
|
|
29
|
+
logFile: path.join(root, "orchestrator.log"),
|
|
30
|
+
socketDir: path.join(root, "sock"),
|
|
31
|
+
adminTokenFile: path.join(root, "admin-token"),
|
|
32
|
+
platformTokenFile: path.join(root, "platform-token"),
|
|
33
|
+
attachmentFile: path.join(root, "attachment.json"),
|
|
34
|
+
secretKeyFile: path.join(root, "secret-key"),
|
|
35
|
+
scalerConfigFile: path.join(root, "scaler.yaml"),
|
|
36
|
+
agentWrapperFile: path.join(root, "agent-wrapper.sh"),
|
|
37
|
+
devIdentityDir: path.join(root, "dev-identity"),
|
|
38
|
+
cacheDir: path.join(root, "cache")
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Fixed localhost ports for the plane's orchestrator (HTTP+WS) and Postgres.
|
|
43
|
+
* Overridable via `KICI_LOCAL_ORCH_PORT` / `KICI_LOCAL_PG_PORT`.
|
|
44
|
+
*/
|
|
45
|
+
function planePorts() {
|
|
46
|
+
return {
|
|
47
|
+
orchestrator: Number(process.env.KICI_LOCAL_ORCH_PORT ?? 4319),
|
|
48
|
+
postgres: Number(process.env.KICI_LOCAL_PG_PORT ?? 45432)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { PLANE_STAMP_VERSION, planePaths, planePorts, planeRoot };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** The stamp fields classification needs. Structural, to avoid an import cycle with plane-manager. */
|
|
2
|
+
export interface StampIdentity {
|
|
3
|
+
orchestratorPid: number;
|
|
4
|
+
port: number;
|
|
5
|
+
}
|
|
6
|
+
/** The `/health` fields that identify a KiCI orchestrator. */
|
|
7
|
+
export interface KiciHealth {
|
|
8
|
+
uptime: number;
|
|
9
|
+
version?: string;
|
|
10
|
+
buildCommit?: string;
|
|
11
|
+
}
|
|
12
|
+
export type PlaneState = 'stopped' | 'ready' | 'unready' | 'foreign-kici' | 'foreign-unknown';
|
|
13
|
+
export type PlaneClassification = {
|
|
14
|
+
kind: 'free';
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'ours-ready';
|
|
17
|
+
pid: number;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'ours-unready';
|
|
20
|
+
pid: number;
|
|
21
|
+
checks: Record<string, boolean>;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'foreign-kici';
|
|
24
|
+
pid: number | null;
|
|
25
|
+
health: KiciHealth;
|
|
26
|
+
} | {
|
|
27
|
+
kind: 'foreign-unknown';
|
|
28
|
+
pid: number | null;
|
|
29
|
+
};
|
|
30
|
+
export interface PlaneProbes {
|
|
31
|
+
isPortFree(port: number): Promise<boolean>;
|
|
32
|
+
findPortHolderPid(port: number): Promise<number | null>;
|
|
33
|
+
/** The parsed `/health` body, or null when it did not answer in time. */
|
|
34
|
+
fetchHealth(port: number): Promise<unknown>;
|
|
35
|
+
fetchReady(port: number): Promise<{
|
|
36
|
+
ok: boolean;
|
|
37
|
+
checks: Record<string, boolean>;
|
|
38
|
+
}>;
|
|
39
|
+
isPidAlive(pid: number): boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface ReclaimResult {
|
|
42
|
+
freed: boolean;
|
|
43
|
+
killedPid?: number;
|
|
44
|
+
error?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Whether a `/health` body came from a KiCI orchestrator. The build-fingerprint
|
|
48
|
+
* fields are the discriminator: an `ok` status alone is far too common to
|
|
49
|
+
* justify signalling the process that produced it.
|
|
50
|
+
*/
|
|
51
|
+
export declare function isKiciOrchestratorHealth(body: unknown): body is KiciHealth;
|
|
52
|
+
/** The state label a classification reports to the operator. */
|
|
53
|
+
export declare function planeStateOf(c: PlaneClassification): PlaneState;
|
|
54
|
+
/** The holder pid a classification carries, when it has one. */
|
|
55
|
+
export declare function classificationPid(c: PlaneClassification): number | null;
|
|
56
|
+
/**
|
|
57
|
+
* Decide what, if anything, is holding the plane port and whether it is ours.
|
|
58
|
+
*
|
|
59
|
+
* `stamp` is the on-disk record of the plane this config dir booted; a null
|
|
60
|
+
* stamp means nothing here booted the holder, so any live holder is foreign.
|
|
61
|
+
*/
|
|
62
|
+
export declare function classifyPlane(port: number, stamp: StampIdentity | null, probes?: Partial<PlaneProbes>): Promise<PlaneClassification>;
|
|
63
|
+
/**
|
|
64
|
+
* Free the plane port, if it is ours or another KiCI plane orchestrator. A
|
|
65
|
+
* holder that does not identify as one is reported and left strictly alone.
|
|
66
|
+
*/
|
|
67
|
+
export declare function reclaimPlanePort(port: number, c: PlaneClassification): Promise<ReclaimResult>;
|
|
68
|
+
//# sourceMappingURL=plane-liveness.d.ts.map
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { findPortHolderPid, isPortFree, terminatePid, waitForPortFree } from "./port-holder.js";
|
|
3
|
+
//#region src/local-plane/plane-liveness.ts
|
|
4
|
+
/**
|
|
5
|
+
* One answer to "is a local dev plane running", shared by up, down, and status.
|
|
6
|
+
*
|
|
7
|
+
* Ownership is decided by pid identity — the process actually holding the port
|
|
8
|
+
* versus the pid the stamp names — not by whether a stamp file exists. That is
|
|
9
|
+
* what keeps the three commands consistent: a plane nobody stamped is foreign to
|
|
10
|
+
* all of them, rather than invisible to two and adoptable by the third.
|
|
11
|
+
*
|
|
12
|
+
* Every external effect arrives through `PlaneProbes`, so the policy here is
|
|
13
|
+
* exercised without a subprocess or a socket.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Cap on each loopback probe. The holder these probes exist to diagnose is
|
|
17
|
+
* frequently wedged, and a socket that accepts a connection but never answers
|
|
18
|
+
* would otherwise hang the CLI for the HTTP client's own multi-minute default —
|
|
19
|
+
* turning `kici local status` into the hang it is meant to explain. A holder
|
|
20
|
+
* that cannot answer loopback within this window is treated as not answering.
|
|
21
|
+
*/
|
|
22
|
+
const PROBE_TIMEOUT_MS = 2e3;
|
|
23
|
+
const defaultProbes = {
|
|
24
|
+
isPortFree,
|
|
25
|
+
findPortHolderPid,
|
|
26
|
+
async fetchHealth(port) {
|
|
27
|
+
try {
|
|
28
|
+
const r = await fetch(`http://127.0.0.1:${port}/health`, { signal: AbortSignal.timeout(PROBE_TIMEOUT_MS) });
|
|
29
|
+
if (!r.ok) return null;
|
|
30
|
+
return await r.json();
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
async fetchReady(port) {
|
|
36
|
+
try {
|
|
37
|
+
const r = await fetch(`http://127.0.0.1:${port}/ready`, { signal: AbortSignal.timeout(PROBE_TIMEOUT_MS) });
|
|
38
|
+
const body = await r.json().catch(() => ({}));
|
|
39
|
+
return {
|
|
40
|
+
ok: r.status === 200,
|
|
41
|
+
checks: body.checks ?? {}
|
|
42
|
+
};
|
|
43
|
+
} catch {
|
|
44
|
+
return {
|
|
45
|
+
ok: false,
|
|
46
|
+
checks: {}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
isPidAlive(pid) {
|
|
51
|
+
try {
|
|
52
|
+
process.kill(pid, 0);
|
|
53
|
+
return true;
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Whether a `/health` body came from a KiCI orchestrator. The build-fingerprint
|
|
61
|
+
* fields are the discriminator: an `ok` status alone is far too common to
|
|
62
|
+
* justify signalling the process that produced it.
|
|
63
|
+
*/
|
|
64
|
+
function isKiciOrchestratorHealth(body) {
|
|
65
|
+
if (typeof body !== "object" || body === null) return false;
|
|
66
|
+
const b = body;
|
|
67
|
+
return b.status === "ok" && typeof b.uptime === "number" && "sdkBundleHash" in b && "engineBundleHash" in b;
|
|
68
|
+
}
|
|
69
|
+
/** The state label a classification reports to the operator. */
|
|
70
|
+
function planeStateOf(c) {
|
|
71
|
+
switch (c.kind) {
|
|
72
|
+
case "free": return "stopped";
|
|
73
|
+
case "ours-ready": return "ready";
|
|
74
|
+
case "ours-unready": return "unready";
|
|
75
|
+
case "foreign-kici": return "foreign-kici";
|
|
76
|
+
case "foreign-unknown": return "foreign-unknown";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** The holder pid a classification carries, when it has one. */
|
|
80
|
+
function classificationPid(c) {
|
|
81
|
+
return "pid" in c ? c.pid : null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Decide what, if anything, is holding the plane port and whether it is ours.
|
|
85
|
+
*
|
|
86
|
+
* `stamp` is the on-disk record of the plane this config dir booted; a null
|
|
87
|
+
* stamp means nothing here booted the holder, so any live holder is foreign.
|
|
88
|
+
*/
|
|
89
|
+
async function classifyPlane(port, stamp, probes = {}) {
|
|
90
|
+
const p = {
|
|
91
|
+
...defaultProbes,
|
|
92
|
+
...probes
|
|
93
|
+
};
|
|
94
|
+
if (await p.isPortFree(port)) return { kind: "free" };
|
|
95
|
+
const holderPid = await p.findPortHolderPid(port);
|
|
96
|
+
if ((holderPid !== null ? stamp !== null && holderPid === stamp.orchestratorPid : stamp !== null && p.isPidAlive(stamp.orchestratorPid)) && stamp !== null) {
|
|
97
|
+
const ready = await p.fetchReady(port);
|
|
98
|
+
return ready.ok ? {
|
|
99
|
+
kind: "ours-ready",
|
|
100
|
+
pid: stamp.orchestratorPid
|
|
101
|
+
} : {
|
|
102
|
+
kind: "ours-unready",
|
|
103
|
+
pid: stamp.orchestratorPid,
|
|
104
|
+
checks: ready.checks
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const health = await p.fetchHealth(port);
|
|
108
|
+
return isKiciOrchestratorHealth(health) ? {
|
|
109
|
+
kind: "foreign-kici",
|
|
110
|
+
pid: holderPid,
|
|
111
|
+
health
|
|
112
|
+
} : {
|
|
113
|
+
kind: "foreign-unknown",
|
|
114
|
+
pid: holderPid
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Free the plane port, if it is ours or another KiCI plane orchestrator. A
|
|
119
|
+
* holder that does not identify as one is reported and left strictly alone.
|
|
120
|
+
*/
|
|
121
|
+
async function reclaimPlanePort(port, c) {
|
|
122
|
+
if (c.kind === "free") return { freed: true };
|
|
123
|
+
if (c.kind === "foreign-unknown") return {
|
|
124
|
+
freed: false,
|
|
125
|
+
error: `port ${port} is held by a process that is not a KiCI plane orchestrator${c.pid === null ? "" : ` (pid ${c.pid})`} — refusing to stop it`
|
|
126
|
+
};
|
|
127
|
+
const pid = classificationPid(c);
|
|
128
|
+
if (pid === null) return {
|
|
129
|
+
freed: false,
|
|
130
|
+
error: `port ${port} is held by a KiCI plane orchestrator whose pid could not be determined`
|
|
131
|
+
};
|
|
132
|
+
await terminatePid(pid);
|
|
133
|
+
return await waitForPortFree(port) ? {
|
|
134
|
+
freed: true,
|
|
135
|
+
killedPid: pid
|
|
136
|
+
} : {
|
|
137
|
+
freed: false,
|
|
138
|
+
killedPid: pid,
|
|
139
|
+
error: `port ${port} is still held after stopping pid ${pid}`
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
export { classificationPid, classifyPlane, isKiciOrchestratorHealth, planeStateOf, reclaimPlanePort };
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=plane-liveness.js.map
|