@kici-dev/compiler 0.1.26 → 0.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-banner.d.ts +6 -3
- package/dist/cli-banner.js +13 -5
- package/dist/cli.js +66 -43
- package/dist/commands/check-mode.d.ts +1 -1
- package/dist/commands/compile.js +3 -3
- package/dist/commands/index.d.ts +5 -1
- package/dist/commands/index.js +6 -3
- package/dist/commands/init.js +1 -1
- 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 +39 -0
- package/dist/commands/local.js +137 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +40 -0
- package/dist/commands/logout.js +7 -0
- package/dist/commands/preview.js +1 -1
- 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 +176 -0
- package/dist/commands/run.d.ts +0 -17
- package/dist/commands/run.js +7 -50
- 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/execution/executor.js +7 -7
- package/dist/generators/secrets-dts.d.ts +9 -9
- package/dist/generators/secrets-dts.js +12 -12
- package/dist/llm-context/llms-architecture.txt +2 -2
- package/dist/llm-context/llms-cli.txt +674 -340
- package/dist/llm-context/llms-features.txt +334 -336
- package/dist/llm-context/llms-full.txt +1207 -892
- package/dist/llm-context/llms-getting-started.txt +7 -15
- package/dist/llm-context/llms-patterns.txt +5 -23
- package/dist/llm-context/llms-providers.txt +10 -1
- package/dist/llm-context/llms-sdk.txt +12 -12
- package/dist/llm-context/llms.txt +5 -5
- package/dist/local-executor/secret-loader.d.ts +1 -1
- package/dist/local-plane/orchestrator-process.d.ts +65 -0
- package/dist/local-plane/orchestrator-process.js +115 -0
- package/dist/local-plane/paths.d.ts +39 -0
- package/dist/local-plane/paths.js +54 -0
- package/dist/local-plane/plane-manager.d.ts +151 -0
- package/dist/local-plane/plane-manager.js +340 -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 +62 -0
- package/dist/local-plane/plane-trigger.js +97 -0
- package/dist/local-plane/platform-attach.d.ts +53 -0
- package/dist/local-plane/platform-attach.js +111 -0
- package/dist/local-plane/postgres.d.ts +44 -0
- package/dist/local-plane/postgres.js +165 -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 +41 -0
- package/dist/local-plane/run-follow.js +104 -0
- package/dist/local-plane/scaler-config.d.ts +54 -0
- package/dist/local-plane/scaler-config.js +114 -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 +165 -0
- package/dist/local-plane/trusted-routing.d.ts +32 -0
- package/dist/local-plane/trusted-routing.js +47 -0
- package/dist/lockfile/generator.js +7 -7
- package/dist/remote/dashboard-client.d.ts +7 -7
- package/dist/remote/dashboard-client.js +7 -7
- package/dist/remote/platform-client.d.ts +1 -1
- package/dist/remote/secret-loader.d.ts +18 -0
- 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 +2 -2
- package/dist/templates/agents-md.d.ts +1 -1
- package/dist/templates/agents-md.js +2 -2
- package/dist/templates/package-json.js +1 -1
- package/dist/test-runner/job-executor.js +1 -1
- package/dist/test-runner/runs-on-display.d.ts +9 -0
- package/dist/{local-executor → test-runner}/runs-on-display.js +1 -1
- package/dist/test-runner/step-context.d.ts +1 -1
- package/dist/test-runner/step-context.js +2 -2
- package/dist/types.d.ts +6 -6
- package/package.json +7 -4
- package/sbom.spdx.json +9884 -1459
- package/dist/local-executor/dag-scheduler.js +0 -183
- package/dist/local-executor/index.js +0 -378
- package/dist/local-executor/job-runner.js +0 -411
- package/dist/local-executor/materializer.js +0 -132
- package/dist/local-executor/output-streamer.js +0 -168
- package/dist/local-executor/payload-generator.js +0 -138
- package/dist/local-executor/picker.js +0 -109
- package/dist/local-executor/to-event-payload.js +0 -21
- package/dist/local-executor/types.js +0 -2
- package/dist/local-executor/workflow-lock.js +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/** Whether the plane runs offline (independent) or attached to the Platform (hybrid). */
|
|
2
|
+
export type PlaneMode = 'independent' | 'hybrid';
|
|
3
|
+
/**
|
|
4
|
+
* Attachment record for a hybrid plane: the Platform relay it dials, the org it
|
|
5
|
+
* is scoped to, and the Platform api_keys id of its minted orchestrator key (for
|
|
6
|
+
* later revoke). The orchestrator token itself is NEVER stored here — it lives
|
|
7
|
+
* in a sibling 0600 file so it can't land in a world-readable stamp.
|
|
8
|
+
*/
|
|
9
|
+
export interface PlaneAttachment {
|
|
10
|
+
/** Orchestrator KICI_PLATFORM_URL — a ws(s)://…/ws relay URL. */
|
|
11
|
+
platformWsUrl: string;
|
|
12
|
+
/** HTTPS Platform API base (provenance/verify context, key revoke). */
|
|
13
|
+
platformApiBase: string;
|
|
14
|
+
orgId: string;
|
|
15
|
+
/** Platform api_keys id of the minted orchestrator key. */
|
|
16
|
+
keyId: string;
|
|
17
|
+
}
|
|
18
|
+
/** On-disk record of the running plane, written on boot, read on status/down. */
|
|
19
|
+
export interface PlaneStamp {
|
|
20
|
+
orchestratorPid: number;
|
|
21
|
+
port: number;
|
|
22
|
+
pgKind: 'embedded' | 'podman';
|
|
23
|
+
kiciVersion: string;
|
|
24
|
+
stampVersion: number;
|
|
25
|
+
/** Offline (independent) vs attached (hybrid). Absent (legacy) reads as independent. */
|
|
26
|
+
mode?: PlaneMode;
|
|
27
|
+
}
|
|
28
|
+
export interface PlaneStatus {
|
|
29
|
+
running: boolean;
|
|
30
|
+
pid?: number;
|
|
31
|
+
port?: number;
|
|
32
|
+
pgKind?: 'embedded' | 'podman';
|
|
33
|
+
stampVersion?: number;
|
|
34
|
+
url?: string;
|
|
35
|
+
/** Bootstrap admin token the CLI presents to the plane's admin API. */
|
|
36
|
+
adminToken?: string;
|
|
37
|
+
/** Offline (independent) vs attached (hybrid). */
|
|
38
|
+
mode: PlaneMode;
|
|
39
|
+
/** Present iff mode === 'hybrid'. */
|
|
40
|
+
attachment?: PlaneAttachment;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read the plane's persisted Platform orchestrator token (mode 0600), or null
|
|
44
|
+
* when the plane is independent/offline. Kept out of the stamp so the token
|
|
45
|
+
* never lands in a world-readable file.
|
|
46
|
+
*/
|
|
47
|
+
export declare function readPlatformToken(): string | null;
|
|
48
|
+
/** Persist the Platform orchestrator token (mode 0600). */
|
|
49
|
+
export declare function writePlatformToken(token: string): void;
|
|
50
|
+
/** Remove the persisted Platform orchestrator token (on detach). */
|
|
51
|
+
export declare function clearPlatformToken(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Read the durable attachment record (survives `kici local down`), or null when
|
|
54
|
+
* the plane is not attached. This — not the running stamp — is the source of
|
|
55
|
+
* truth for "is this plane attached to the Platform".
|
|
56
|
+
*/
|
|
57
|
+
export declare function readAttachment(): PlaneAttachment | null;
|
|
58
|
+
/** Persist the durable attachment record (never the token). */
|
|
59
|
+
export declare function writeAttachment(attachment: PlaneAttachment): void;
|
|
60
|
+
/** Remove the durable attachment record (on detach). */
|
|
61
|
+
export declare function clearAttachment(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Read the plane's persisted bootstrap admin token, generating + persisting one
|
|
64
|
+
* (mode 0600) on first boot. A warm plane reuses the same token across CLI
|
|
65
|
+
* invocations so a running orchestrator keeps accepting the CLI's admin calls.
|
|
66
|
+
*/
|
|
67
|
+
export declare function readOrCreateAdminToken(): string;
|
|
68
|
+
/** Read the plane's admin token without generating one (null when absent). */
|
|
69
|
+
export declare function planeAdminToken(): string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Read the plane's local master secret key, generating + persisting a fresh 64
|
|
72
|
+
* hex-char key (mode 0600) on first boot. Stable across boots so DB-encrypted
|
|
73
|
+
* material stays decryptable. Freshly generated — never derived from any sops
|
|
74
|
+
* secret. Enables the orchestrator's secrets subsystem, which gates its admin
|
|
75
|
+
* API surface.
|
|
76
|
+
*/
|
|
77
|
+
export declare function readOrCreateSecretKey(): string;
|
|
78
|
+
/** Absolute path of the dev-signed identity's private JWK key file. */
|
|
79
|
+
export declare function devIdentityKeyFile(): string;
|
|
80
|
+
/** Absolute path of the dev-signed identity's public JWK (written by the plane orchestrator). */
|
|
81
|
+
export declare function devIdentityPublicJwkFile(): string;
|
|
82
|
+
/**
|
|
83
|
+
* Read the plane's dev-signed identity keypair, generating + persisting a fresh
|
|
84
|
+
* EC P-256 private JWK (mode 0600) on first boot. Stable across boots so a
|
|
85
|
+
* `kici local trust-root` export stays valid for previously-minted tokens.
|
|
86
|
+
* Freshly generated — NEVER derived from any sops secret or real key. Only the
|
|
87
|
+
* private key is written here; the plane orchestrator derives + writes the
|
|
88
|
+
* public JWK next to it. Returns the private-key file path passed to the
|
|
89
|
+
* orchestrator via KICI_DEV_IDENTITY_KEY_FILE.
|
|
90
|
+
*/
|
|
91
|
+
export declare function readOrCreateDevIdentity(): string;
|
|
92
|
+
/** Absolute path of the plane orchestrator's log file. */
|
|
93
|
+
export declare function planeLogPath(): string;
|
|
94
|
+
/** Read the plane's current status by probing `/ready` against the stamped port. */
|
|
95
|
+
export declare function planeStatus(): Promise<PlaneStatus>;
|
|
96
|
+
/** Attach parameters for a hybrid plane boot (see attachPlane). */
|
|
97
|
+
export interface PlaneUpAttach {
|
|
98
|
+
platformWsUrl: string;
|
|
99
|
+
platformToken: string;
|
|
100
|
+
platformApiBase: string;
|
|
101
|
+
orgId: string;
|
|
102
|
+
keyId: string;
|
|
103
|
+
}
|
|
104
|
+
export interface PlaneUpOptions {
|
|
105
|
+
/** Boot the plane hybrid (attached to the Platform) instead of independent. */
|
|
106
|
+
attach?: PlaneUpAttach;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Start (or reuse) the local dev plane. Idempotent: a healthy running plane
|
|
110
|
+
* **whose stamped mode matches the requested mode** is returned as-is; a mode
|
|
111
|
+
* change (independent↔hybrid) forces a teardown + reboot. Otherwise Postgres +
|
|
112
|
+
* the orchestrator are booted and a fresh stamp + pidfile are written. When
|
|
113
|
+
* `attach` is present the orchestrator boots hybrid against the Platform relay;
|
|
114
|
+
* otherwise it boots independent with the dev-signed identity.
|
|
115
|
+
*/
|
|
116
|
+
export declare function planeUp(opts?: PlaneUpOptions): Promise<PlaneStatus>;
|
|
117
|
+
/**
|
|
118
|
+
* Stop the local dev plane. Teardown is reconstructed from the stamp (the
|
|
119
|
+
* start-time Postgres handle does not survive a separate CLI invocation):
|
|
120
|
+
* SIGTERM the orchestrator, stop Postgres by backend kind, then clear the
|
|
121
|
+
* pidfile + stamp.
|
|
122
|
+
*/
|
|
123
|
+
export declare function planeDown(): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Poll until process `pid` has exited (SIGTERM already sent), escalating to
|
|
126
|
+
* SIGKILL if it outlives the grace period, so the caller can rely on the
|
|
127
|
+
* listening socket being released once this resolves. `process.kill(pid, 0)`
|
|
128
|
+
* throws `ESRCH` once the process is gone.
|
|
129
|
+
*/
|
|
130
|
+
export declare function waitForProcessExit(pid: number, graceMs?: number): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Attach the local dev plane to the hosted Platform: mint an org-scoped
|
|
133
|
+
* orchestrator key with the logged-in PAT, then (re)boot the plane hybrid
|
|
134
|
+
* against the Platform relay. Registration with the Platform is implicit — the
|
|
135
|
+
* hybrid orchestrator's PlatformClient sends `source.register` on WS auth.
|
|
136
|
+
*/
|
|
137
|
+
export declare function attachPlane(args: {
|
|
138
|
+
apiBase: string;
|
|
139
|
+
pat: string;
|
|
140
|
+
orgId: string;
|
|
141
|
+
}): Promise<PlaneStatus>;
|
|
142
|
+
/**
|
|
143
|
+
* Detach the local dev plane: best-effort revoke the minted orchestrator key on
|
|
144
|
+
* the Platform, clear the durable attachment + token, then reboot the plane
|
|
145
|
+
* independent (offline). The revoke is non-fatal — a detach must succeed even
|
|
146
|
+
* offline or with an expired PAT.
|
|
147
|
+
*/
|
|
148
|
+
export declare function detachPlane(opts?: {
|
|
149
|
+
pat?: string;
|
|
150
|
+
}): Promise<PlaneStatus>;
|
|
151
|
+
//# sourceMappingURL=plane-manager.d.ts.map
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { planePaths, planePorts } from "./paths.js";
|
|
3
|
+
import { orchestratorReady, spawnOrchestrator } from "./orchestrator-process.js";
|
|
4
|
+
import { startPlanePostgres, stopPlanePostgres } from "./postgres.js";
|
|
5
|
+
import { writeScalerConfig } from "./scaler-config.js";
|
|
6
|
+
import { derivePlatformWsUrl, mintOrchestratorKey, revokeOrchestratorKey } from "./platform-attach.js";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import { generateKeyPairSync, randomBytes } from "node:crypto";
|
|
10
|
+
//#region src/local-plane/plane-manager.ts
|
|
11
|
+
const kiciVersion = "0.1.27";
|
|
12
|
+
/**
|
|
13
|
+
* Read the plane's persisted Platform orchestrator token (mode 0600), or null
|
|
14
|
+
* when the plane is independent/offline. Kept out of the stamp so the token
|
|
15
|
+
* never lands in a world-readable file.
|
|
16
|
+
*/
|
|
17
|
+
function readPlatformToken() {
|
|
18
|
+
try {
|
|
19
|
+
return fs.readFileSync(planePaths().platformTokenFile, "utf-8").trim() || null;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Persist the Platform orchestrator token (mode 0600). */
|
|
25
|
+
function writePlatformToken(token) {
|
|
26
|
+
const { platformTokenFile, root } = planePaths();
|
|
27
|
+
fs.mkdirSync(root, { recursive: true });
|
|
28
|
+
fs.writeFileSync(platformTokenFile, token, { mode: 384 });
|
|
29
|
+
}
|
|
30
|
+
/** Remove the persisted Platform orchestrator token (on detach). */
|
|
31
|
+
function clearPlatformToken() {
|
|
32
|
+
fs.rmSync(planePaths().platformTokenFile, { force: true });
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Read the durable attachment record (survives `kici local down`), or null when
|
|
36
|
+
* the plane is not attached. This — not the running stamp — is the source of
|
|
37
|
+
* truth for "is this plane attached to the Platform".
|
|
38
|
+
*/
|
|
39
|
+
function readAttachment() {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(fs.readFileSync(planePaths().attachmentFile, "utf-8"));
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Persist the durable attachment record (never the token). */
|
|
47
|
+
function writeAttachment(attachment) {
|
|
48
|
+
const { attachmentFile, root } = planePaths();
|
|
49
|
+
fs.mkdirSync(root, { recursive: true });
|
|
50
|
+
fs.writeFileSync(attachmentFile, JSON.stringify(attachment, null, 2));
|
|
51
|
+
}
|
|
52
|
+
/** Remove the durable attachment record (on detach). */
|
|
53
|
+
function clearAttachment() {
|
|
54
|
+
fs.rmSync(planePaths().attachmentFile, { force: true });
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Read the plane's persisted bootstrap admin token, generating + persisting one
|
|
58
|
+
* (mode 0600) on first boot. A warm plane reuses the same token across CLI
|
|
59
|
+
* invocations so a running orchestrator keeps accepting the CLI's admin calls.
|
|
60
|
+
*/
|
|
61
|
+
function readOrCreateAdminToken() {
|
|
62
|
+
const { adminTokenFile, root } = planePaths();
|
|
63
|
+
try {
|
|
64
|
+
const existing = fs.readFileSync(adminTokenFile, "utf-8").trim();
|
|
65
|
+
if (existing) return existing;
|
|
66
|
+
} catch {}
|
|
67
|
+
fs.mkdirSync(root, { recursive: true });
|
|
68
|
+
const token = `kici-local-${randomBytes(24).toString("hex")}`;
|
|
69
|
+
fs.writeFileSync(adminTokenFile, token, { mode: 384 });
|
|
70
|
+
return token;
|
|
71
|
+
}
|
|
72
|
+
/** Read the plane's admin token without generating one (null when absent). */
|
|
73
|
+
function planeAdminToken() {
|
|
74
|
+
try {
|
|
75
|
+
return fs.readFileSync(planePaths().adminTokenFile, "utf-8").trim() || null;
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Read the plane's local master secret key, generating + persisting a fresh 64
|
|
82
|
+
* hex-char key (mode 0600) on first boot. Stable across boots so DB-encrypted
|
|
83
|
+
* material stays decryptable. Freshly generated — never derived from any sops
|
|
84
|
+
* secret. Enables the orchestrator's secrets subsystem, which gates its admin
|
|
85
|
+
* API surface.
|
|
86
|
+
*/
|
|
87
|
+
function readOrCreateSecretKey() {
|
|
88
|
+
const { secretKeyFile, root } = planePaths();
|
|
89
|
+
try {
|
|
90
|
+
const existing = fs.readFileSync(secretKeyFile, "utf-8").trim();
|
|
91
|
+
if (existing) return existing;
|
|
92
|
+
} catch {}
|
|
93
|
+
fs.mkdirSync(root, { recursive: true });
|
|
94
|
+
const key = randomBytes(32).toString("hex");
|
|
95
|
+
fs.writeFileSync(secretKeyFile, key, { mode: 384 });
|
|
96
|
+
return key;
|
|
97
|
+
}
|
|
98
|
+
/** Absolute path of the dev-signed identity's private JWK key file. */
|
|
99
|
+
function devIdentityKeyFile() {
|
|
100
|
+
return path.join(planePaths().devIdentityDir, "identity.jwk");
|
|
101
|
+
}
|
|
102
|
+
/** Absolute path of the dev-signed identity's public JWK (written by the plane orchestrator). */
|
|
103
|
+
function devIdentityPublicJwkFile() {
|
|
104
|
+
return path.join(planePaths().devIdentityDir, "identity.pub.jwk");
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Read the plane's dev-signed identity keypair, generating + persisting a fresh
|
|
108
|
+
* EC P-256 private JWK (mode 0600) on first boot. Stable across boots so a
|
|
109
|
+
* `kici local trust-root` export stays valid for previously-minted tokens.
|
|
110
|
+
* Freshly generated — NEVER derived from any sops secret or real key. Only the
|
|
111
|
+
* private key is written here; the plane orchestrator derives + writes the
|
|
112
|
+
* public JWK next to it. Returns the private-key file path passed to the
|
|
113
|
+
* orchestrator via KICI_DEV_IDENTITY_KEY_FILE.
|
|
114
|
+
*/
|
|
115
|
+
function readOrCreateDevIdentity() {
|
|
116
|
+
const keyFile = devIdentityKeyFile();
|
|
117
|
+
try {
|
|
118
|
+
if (fs.readFileSync(keyFile, "utf-8").trim()) return keyFile;
|
|
119
|
+
} catch {}
|
|
120
|
+
fs.mkdirSync(planePaths().devIdentityDir, {
|
|
121
|
+
recursive: true,
|
|
122
|
+
mode: 448
|
|
123
|
+
});
|
|
124
|
+
const { privateKey } = generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
125
|
+
const jwk = privateKey.export({ format: "jwk" });
|
|
126
|
+
fs.writeFileSync(keyFile, JSON.stringify(jwk, null, 2), { mode: 384 });
|
|
127
|
+
return keyFile;
|
|
128
|
+
}
|
|
129
|
+
function readStamp() {
|
|
130
|
+
try {
|
|
131
|
+
return JSON.parse(fs.readFileSync(planePaths().stampFile, "utf-8"));
|
|
132
|
+
} catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function writeStamp(stamp) {
|
|
137
|
+
const { root, stampFile, pidfile } = planePaths();
|
|
138
|
+
fs.mkdirSync(root, { recursive: true });
|
|
139
|
+
fs.writeFileSync(stampFile, JSON.stringify(stamp, null, 2));
|
|
140
|
+
fs.writeFileSync(pidfile, String(stamp.orchestratorPid));
|
|
141
|
+
}
|
|
142
|
+
function orchestratorUrl(port) {
|
|
143
|
+
return `http://127.0.0.1:${port}`;
|
|
144
|
+
}
|
|
145
|
+
/** Absolute path of the plane orchestrator's log file. */
|
|
146
|
+
function planeLogPath() {
|
|
147
|
+
return planePaths().logFile;
|
|
148
|
+
}
|
|
149
|
+
/** Read the plane's current status by probing `/ready` against the stamped port. */
|
|
150
|
+
async function planeStatus() {
|
|
151
|
+
const stamp = readStamp();
|
|
152
|
+
const port = stamp?.port ?? planePorts().orchestrator;
|
|
153
|
+
if (!await orchestratorReady(port)) return {
|
|
154
|
+
running: false,
|
|
155
|
+
mode: stamp?.mode ?? "independent",
|
|
156
|
+
attachment: readAttachment() ?? void 0
|
|
157
|
+
};
|
|
158
|
+
return {
|
|
159
|
+
running: true,
|
|
160
|
+
pid: stamp?.orchestratorPid,
|
|
161
|
+
port,
|
|
162
|
+
pgKind: stamp?.pgKind,
|
|
163
|
+
stampVersion: stamp?.stampVersion,
|
|
164
|
+
url: orchestratorUrl(port),
|
|
165
|
+
adminToken: planeAdminToken() ?? void 0,
|
|
166
|
+
mode: stamp?.mode ?? "independent",
|
|
167
|
+
attachment: readAttachment() ?? void 0
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Start (or reuse) the local dev plane. Idempotent: a healthy running plane
|
|
172
|
+
* **whose stamped mode matches the requested mode** is returned as-is; a mode
|
|
173
|
+
* change (independent↔hybrid) forces a teardown + reboot. Otherwise Postgres +
|
|
174
|
+
* the orchestrator are booted and a fresh stamp + pidfile are written. When
|
|
175
|
+
* `attach` is present the orchestrator boots hybrid against the Platform relay;
|
|
176
|
+
* otherwise it boots independent with the dev-signed identity.
|
|
177
|
+
*/
|
|
178
|
+
async function planeUp(opts = {}) {
|
|
179
|
+
const { orchestrator: port } = planePorts();
|
|
180
|
+
const existing = readStamp();
|
|
181
|
+
const requestedMode = opts.attach ? "hybrid" : "independent";
|
|
182
|
+
if (existing && existing.stampVersion !== 3) {
|
|
183
|
+
await planeDown();
|
|
184
|
+
fs.rmSync(planePaths().pgData, {
|
|
185
|
+
recursive: true,
|
|
186
|
+
force: true
|
|
187
|
+
});
|
|
188
|
+
} else if ((existing?.mode ?? "independent") !== requestedMode && await orchestratorReady(port)) await planeDown();
|
|
189
|
+
else if (await orchestratorReady(port)) return {
|
|
190
|
+
running: true,
|
|
191
|
+
pid: existing?.orchestratorPid,
|
|
192
|
+
port,
|
|
193
|
+
pgKind: existing?.pgKind,
|
|
194
|
+
stampVersion: existing?.stampVersion,
|
|
195
|
+
url: orchestratorUrl(port),
|
|
196
|
+
adminToken: planeAdminToken() ?? void 0,
|
|
197
|
+
mode: existing?.mode ?? "independent",
|
|
198
|
+
attachment: readAttachment() ?? void 0
|
|
199
|
+
};
|
|
200
|
+
const adminToken = readOrCreateAdminToken();
|
|
201
|
+
const secretKey = readOrCreateSecretKey();
|
|
202
|
+
const scalerConfigFile = writeScalerConfig(port);
|
|
203
|
+
const pg = await startPlanePostgres();
|
|
204
|
+
let orch;
|
|
205
|
+
if (opts.attach) {
|
|
206
|
+
writePlatformToken(opts.attach.platformToken);
|
|
207
|
+
writeAttachment({
|
|
208
|
+
platformWsUrl: opts.attach.platformWsUrl,
|
|
209
|
+
platformApiBase: opts.attach.platformApiBase,
|
|
210
|
+
orgId: opts.attach.orgId,
|
|
211
|
+
keyId: opts.attach.keyId
|
|
212
|
+
});
|
|
213
|
+
orch = await spawnOrchestrator(pg.url, {
|
|
214
|
+
adminToken,
|
|
215
|
+
secretKey,
|
|
216
|
+
scalerConfigFile,
|
|
217
|
+
attach: {
|
|
218
|
+
platformWsUrl: opts.attach.platformWsUrl,
|
|
219
|
+
platformToken: opts.attach.platformToken
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
} else {
|
|
223
|
+
const devIdentityKey = readOrCreateDevIdentity();
|
|
224
|
+
orch = await spawnOrchestrator(pg.url, {
|
|
225
|
+
adminToken,
|
|
226
|
+
secretKey,
|
|
227
|
+
scalerConfigFile,
|
|
228
|
+
devIdentityKeyFile: devIdentityKey
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
writeStamp({
|
|
232
|
+
orchestratorPid: orch.pid,
|
|
233
|
+
port: orch.port,
|
|
234
|
+
pgKind: pg.kind,
|
|
235
|
+
kiciVersion,
|
|
236
|
+
stampVersion: 3,
|
|
237
|
+
mode: requestedMode
|
|
238
|
+
});
|
|
239
|
+
return {
|
|
240
|
+
running: true,
|
|
241
|
+
pid: orch.pid,
|
|
242
|
+
port: orch.port,
|
|
243
|
+
pgKind: pg.kind,
|
|
244
|
+
stampVersion: 3,
|
|
245
|
+
url: orchestratorUrl(orch.port),
|
|
246
|
+
adminToken,
|
|
247
|
+
mode: requestedMode,
|
|
248
|
+
attachment: readAttachment() ?? void 0
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Stop the local dev plane. Teardown is reconstructed from the stamp (the
|
|
253
|
+
* start-time Postgres handle does not survive a separate CLI invocation):
|
|
254
|
+
* SIGTERM the orchestrator, stop Postgres by backend kind, then clear the
|
|
255
|
+
* pidfile + stamp.
|
|
256
|
+
*/
|
|
257
|
+
async function planeDown() {
|
|
258
|
+
const stamp = readStamp();
|
|
259
|
+
const { pidfile, stampFile } = planePaths();
|
|
260
|
+
if (stamp) {
|
|
261
|
+
try {
|
|
262
|
+
process.kill(stamp.orchestratorPid, "SIGTERM");
|
|
263
|
+
} catch {}
|
|
264
|
+
await waitForProcessExit(stamp.orchestratorPid);
|
|
265
|
+
await stopPlanePostgres(stamp.pgKind);
|
|
266
|
+
}
|
|
267
|
+
fs.rmSync(pidfile, { force: true });
|
|
268
|
+
fs.rmSync(stampFile, { force: true });
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Poll until process `pid` has exited (SIGTERM already sent), escalating to
|
|
272
|
+
* SIGKILL if it outlives the grace period, so the caller can rely on the
|
|
273
|
+
* listening socket being released once this resolves. `process.kill(pid, 0)`
|
|
274
|
+
* throws `ESRCH` once the process is gone.
|
|
275
|
+
*/
|
|
276
|
+
async function waitForProcessExit(pid, graceMs = 1e4) {
|
|
277
|
+
const deadline = Date.now() + graceMs;
|
|
278
|
+
let killed = false;
|
|
279
|
+
for (;;) {
|
|
280
|
+
try {
|
|
281
|
+
process.kill(pid, 0);
|
|
282
|
+
} catch {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (!killed && Date.now() > deadline) {
|
|
286
|
+
try {
|
|
287
|
+
process.kill(pid, "SIGKILL");
|
|
288
|
+
} catch {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
killed = true;
|
|
292
|
+
}
|
|
293
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Attach the local dev plane to the hosted Platform: mint an org-scoped
|
|
298
|
+
* orchestrator key with the logged-in PAT, then (re)boot the plane hybrid
|
|
299
|
+
* against the Platform relay. Registration with the Platform is implicit — the
|
|
300
|
+
* hybrid orchestrator's PlatformClient sends `source.register` on WS auth.
|
|
301
|
+
*/
|
|
302
|
+
async function attachPlane(args) {
|
|
303
|
+
const platformWsUrl = derivePlatformWsUrl(args.apiBase);
|
|
304
|
+
const minted = await mintOrchestratorKey({
|
|
305
|
+
apiBase: args.apiBase,
|
|
306
|
+
pat: args.pat,
|
|
307
|
+
orgId: args.orgId,
|
|
308
|
+
name: "local dev plane"
|
|
309
|
+
});
|
|
310
|
+
return planeUp({ attach: {
|
|
311
|
+
platformWsUrl,
|
|
312
|
+
platformToken: minted.key,
|
|
313
|
+
platformApiBase: args.apiBase,
|
|
314
|
+
orgId: args.orgId,
|
|
315
|
+
keyId: minted.keyId
|
|
316
|
+
} });
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Detach the local dev plane: best-effort revoke the minted orchestrator key on
|
|
320
|
+
* the Platform, clear the durable attachment + token, then reboot the plane
|
|
321
|
+
* independent (offline). The revoke is non-fatal — a detach must succeed even
|
|
322
|
+
* offline or with an expired PAT.
|
|
323
|
+
*/
|
|
324
|
+
async function detachPlane(opts = {}) {
|
|
325
|
+
const attachment = readAttachment();
|
|
326
|
+
if (attachment && opts.pat) await revokeOrchestratorKey({
|
|
327
|
+
apiBase: attachment.platformApiBase,
|
|
328
|
+
pat: opts.pat,
|
|
329
|
+
orgId: attachment.orgId,
|
|
330
|
+
keyId: attachment.keyId
|
|
331
|
+
}).catch(() => false);
|
|
332
|
+
clearAttachment();
|
|
333
|
+
clearPlatformToken();
|
|
334
|
+
await planeDown();
|
|
335
|
+
return planeUp();
|
|
336
|
+
}
|
|
337
|
+
//#endregion
|
|
338
|
+
export { attachPlane, clearAttachment, clearPlatformToken, detachPlane, devIdentityKeyFile, devIdentityPublicJwkFile, planeAdminToken, planeDown, planeLogPath, planeStatus, planeUp, readAttachment, readOrCreateAdminToken, readOrCreateDevIdentity, readOrCreateSecretKey, readPlatformToken, waitForProcessExit, writeAttachment, writePlatformToken };
|
|
339
|
+
|
|
340
|
+
//# sourceMappingURL=plane-manager.js.map
|
|
@@ -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,62 @@
|
|
|
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
|
+
/** POST the trigger request to the plane. Returns the HTTP status. */
|
|
49
|
+
export declare function sendLocalTrigger(planeUrl: string, req: LocalTriggerRequest): Promise<number>;
|
|
50
|
+
export interface TriggerRunOptions {
|
|
51
|
+
client?: RunDiscoveryClient;
|
|
52
|
+
pollIntervalMs?: number;
|
|
53
|
+
resendAfterMs?: number;
|
|
54
|
+
timeoutMs?: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Trigger the run and resolve its runId. Sends the synthetic push, then polls
|
|
58
|
+
* the admin runs list (`created_at > since`) for the newest run, resending the
|
|
59
|
+
* webhook past a grace window to absorb source hot-reload latency.
|
|
60
|
+
*/
|
|
61
|
+
export declare function triggerRun(planeUrl: string, adminToken: string, input: LocalTriggerInput, opts?: TriggerRunOptions): Promise<string>;
|
|
62
|
+
//# sourceMappingURL=plane-trigger.d.ts.map
|