@kici-dev/orchestrator 0.7.0 → 0.8.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/cli.js +35 -17
- package/dist/providers/local/lock-file-fetcher.d.ts +8 -0
- package/dist/scaler/claim-store.d.ts +0 -2
- package/dist/scaler/event-backend.d.ts +7 -6
- package/dist/scaler/manager.d.ts +18 -2
- package/dist/scaler/scaler-state-store.d.ts +7 -0
- package/dist/scaler/types.d.ts +10 -0
- package/dist/scaler/warm-pool.d.ts +6 -4
- package/dist/security/identity-link.d.ts +1 -1
- package/dist/security/trust-directory-store.d.ts +1 -1
- package/dist/server.js +167 -76
- package/dist/standalone.js +161 -72
- package/dist/webhook/ingest-overflow-replayer.d.ts +1 -1
- package/dist/webhook/relay-reinject.d.ts +1 -1
- package/dist/worker/in-memory-job-queue.d.ts +9 -0
- package/dist/ws/fleet-agent-collector.d.ts +7 -0
- package/dist/ws/test-relay-handlers.d.ts +2 -0
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +53 -53
package/dist/cli.js
CHANGED
|
@@ -22056,7 +22056,7 @@ init_manifest();
|
|
|
22056
22056
|
const ALL_PLATFORMS = AgentPlatform.options;
|
|
22057
22057
|
/** The orchestrator's own version (single-version invariant) = the packaged agent version. */
|
|
22058
22058
|
function resolveKiciVersion() {
|
|
22059
|
-
return "0.
|
|
22059
|
+
return "0.8.0";
|
|
22060
22060
|
}
|
|
22061
22061
|
/** Parse the --platform value: default set | single | CSV | `all`. */
|
|
22062
22062
|
function parsePlatforms(raw) {
|
|
@@ -32216,6 +32216,9 @@ var ContainerScalerBackend = class ContainerScalerBackend {
|
|
|
32216
32216
|
state: "spawning"
|
|
32217
32217
|
};
|
|
32218
32218
|
this.agents.set(managed.id, managed);
|
|
32219
|
+
const assertStillTracked = () => {
|
|
32220
|
+
if (this.agents.get(managed.id) !== managed) throw new Error(`Agent ${agentId} was torn down while its container was being provisioned`);
|
|
32221
|
+
};
|
|
32219
32222
|
try {
|
|
32220
32223
|
const agentEnvForwarded = [];
|
|
32221
32224
|
for (const [key, value] of Object.entries(process.env)) if (key.startsWith(KICI_AGENT_ENV_PREFIX) && value !== void 0) {
|
|
@@ -32289,17 +32292,24 @@ var ContainerScalerBackend = class ContainerScalerBackend {
|
|
|
32289
32292
|
...this.networkIsolation && { NetworkingConfig: { EndpointsConfig: { ["kici-agent-net"]: {} } } }
|
|
32290
32293
|
});
|
|
32291
32294
|
createdContainer = container;
|
|
32295
|
+
managed.backendRef = container.id;
|
|
32296
|
+
this.containerToManaged.set(container.id, managed.id);
|
|
32297
|
+
assertStillTracked();
|
|
32292
32298
|
if (this.networkIsolation) emit(ScalerEventType.enum["scaler.network"], "configuring network isolation");
|
|
32293
32299
|
await container.start({ abortSignal: signal });
|
|
32300
|
+
assertStillTracked();
|
|
32294
32301
|
emit(ScalerEventType.enum["scaler.ready"], "container started");
|
|
32295
|
-
if (this.networkIsolation)
|
|
32296
|
-
|
|
32297
|
-
|
|
32298
|
-
|
|
32299
|
-
|
|
32300
|
-
|
|
32301
|
-
|
|
32302
|
-
|
|
32302
|
+
if (this.networkIsolation) {
|
|
32303
|
+
await this.applyIsolationRules({
|
|
32304
|
+
agentId,
|
|
32305
|
+
managedId: managed.id,
|
|
32306
|
+
containerId: container.id,
|
|
32307
|
+
networkPolicy: matchedLabelSet.networkPolicy,
|
|
32308
|
+
orchestratorUrl,
|
|
32309
|
+
...signal ? { signal } : {}
|
|
32310
|
+
});
|
|
32311
|
+
assertStillTracked();
|
|
32312
|
+
}
|
|
32303
32313
|
try {
|
|
32304
32314
|
const capture = await createContainerLogCapture(this.docker, container.id);
|
|
32305
32315
|
this.logCaptures.set(managed.id, capture);
|
|
@@ -32309,13 +32319,17 @@ var ContainerScalerBackend = class ContainerScalerBackend {
|
|
|
32309
32319
|
error: toErrorMessage(err)
|
|
32310
32320
|
});
|
|
32311
32321
|
}
|
|
32322
|
+
assertStillTracked();
|
|
32312
32323
|
managed.state = "running";
|
|
32313
|
-
managed.backendRef = container.id;
|
|
32314
|
-
this.containerToManaged.set(container.id, managed.id);
|
|
32315
32324
|
emit(ScalerEventType.enum["agent.connecting"], "waiting for agent WS registration");
|
|
32316
32325
|
return managed;
|
|
32317
32326
|
} catch (err) {
|
|
32318
|
-
const
|
|
32327
|
+
const capture = this.logCaptures.get(managed.id);
|
|
32328
|
+
const t = capture?.tail() ?? "";
|
|
32329
|
+
if (capture) {
|
|
32330
|
+
capture.close();
|
|
32331
|
+
this.logCaptures.delete(managed.id);
|
|
32332
|
+
}
|
|
32319
32333
|
const base = toErrorMessage(err);
|
|
32320
32334
|
emit(ScalerEventType.enum["scaler.failed"], t ? `${base}\n--- captured output ---\n${t}` : base);
|
|
32321
32335
|
const failedIp = this.containerIps.get(managed.id);
|
|
@@ -32325,10 +32339,13 @@ var ContainerScalerBackend = class ContainerScalerBackend {
|
|
|
32325
32339
|
} catch {}
|
|
32326
32340
|
this.containerIps.delete(managed.id);
|
|
32327
32341
|
}
|
|
32328
|
-
if (createdContainer)
|
|
32329
|
-
|
|
32330
|
-
|
|
32331
|
-
|
|
32342
|
+
if (createdContainer) {
|
|
32343
|
+
this.containerToManaged.delete(createdContainer.id);
|
|
32344
|
+
try {
|
|
32345
|
+
await createdContainer.remove({ force: true });
|
|
32346
|
+
} catch {}
|
|
32347
|
+
}
|
|
32348
|
+
if (this.agents.get(managed.id) === managed) this.agents.delete(managed.id);
|
|
32332
32349
|
throw err;
|
|
32333
32350
|
}
|
|
32334
32351
|
}
|
|
@@ -32365,6 +32382,7 @@ var ContainerScalerBackend = class ContainerScalerBackend {
|
|
|
32365
32382
|
capture.close();
|
|
32366
32383
|
this.logCaptures.delete(managedId);
|
|
32367
32384
|
}
|
|
32385
|
+
if (!managed.backendRef) return;
|
|
32368
32386
|
try {
|
|
32369
32387
|
const container = this.docker.getContainer(managed.backendRef);
|
|
32370
32388
|
try {
|
|
@@ -33025,7 +33043,7 @@ Token vocabulary:
|
|
|
33025
33043
|
* where no bundler substituted the constant — the same shape `server.ts` and
|
|
33026
33044
|
* `standalone.ts` use for `ORCHESTRATOR_VERSION`.
|
|
33027
33045
|
*/
|
|
33028
|
-
const CLI_VERSION = "0.
|
|
33046
|
+
const CLI_VERSION = "0.8.0";
|
|
33029
33047
|
/**
|
|
33030
33048
|
* Build the kici-admin Commander program with every command group registered.
|
|
33031
33049
|
* Exported so the surface registry can walk the real command tree without
|
|
@@ -15,6 +15,14 @@ import type { LockFileFetcher, LockFile } from '@kici-dev/engine';
|
|
|
15
15
|
export declare class LocalLockFileFetcher implements LockFileFetcher {
|
|
16
16
|
private readonly repoBasePath;
|
|
17
17
|
readonly provider: 'local';
|
|
18
|
+
/**
|
|
19
|
+
* Never cached: `ref` is ignored below, so a cache keyed on it would serve the
|
|
20
|
+
* first lock file read to every later trigger for the lifetime of the entry —
|
|
21
|
+
* a `kici run --local` dispatch always triggers at `HEAD`, and an in-place run
|
|
22
|
+
* carries its uncommitted edits under an unchanged sha. Reading the file from
|
|
23
|
+
* disk is what makes those edits reach the run.
|
|
24
|
+
*/
|
|
25
|
+
readonly cacheable: false;
|
|
18
26
|
/**
|
|
19
27
|
* @param repoBasePath - Base directory for the repo(s). When repoIdentifier
|
|
20
28
|
* starts with 'file://', it is stripped and used as-is. Otherwise
|
|
@@ -34,8 +34,6 @@ export interface ClaimSpec {
|
|
|
34
34
|
agentId: string;
|
|
35
35
|
/** Exact label set the ephemeral token is authorized for. */
|
|
36
36
|
labels: string[];
|
|
37
|
-
/** Mandatory (taint) labels the pool gates on, carried for the workflow. */
|
|
38
|
-
mandatoryLabels: string[];
|
|
39
37
|
/** TTL of the ephemeral agent token minted on claim (seconds). */
|
|
40
38
|
agentTokenTtlSeconds: number;
|
|
41
39
|
/** Orchestrator WS URL the provisioned agent connects back to. */
|
|
@@ -108,12 +108,13 @@ export declare class EventScalerBackend implements ScalerBackend {
|
|
|
108
108
|
*/
|
|
109
109
|
shutdownAll(): Promise<void>;
|
|
110
110
|
/**
|
|
111
|
-
* Apply the new config. The backend reads `roles`, `
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
111
|
+
* Apply the new config. The backend reads `roles`, `agentTokenTtlSeconds`
|
|
112
|
+
* and `provisioningTargets` off its entry at every spawn (and
|
|
113
|
+
* `mandatoryLabels` only when a spawn reaches it without a context), so the
|
|
114
|
+
* entry is replaced here — otherwise a reload that retargets the
|
|
115
|
+
* provisioning workflow, or changes the scaler's roles, would keep emitting
|
|
116
|
+
* scale-up events to the old workflow refs and minting agent labels from the
|
|
117
|
+
* old roles, while the manager's own routing gate used the new ones.
|
|
117
118
|
*/
|
|
118
119
|
reload(labelSets: LabelSetConfig[], opts?: {
|
|
119
120
|
maxAgents?: number;
|
package/dist/scaler/manager.d.ts
CHANGED
|
@@ -1281,10 +1281,25 @@ export declare class ScalerManager {
|
|
|
1281
1281
|
*/
|
|
1282
1282
|
getScalerContextForAgent(agentId: string): Record<string, unknown> | undefined;
|
|
1283
1283
|
/**
|
|
1284
|
-
* Start the
|
|
1285
|
-
*
|
|
1284
|
+
* Start the machine-pool ledger reaper and the retirement sweep that tears
|
|
1285
|
+
* down drained retiring backends, and publish the warm-pool gauges so each
|
|
1286
|
+
* pool's target is visible before its first fill.
|
|
1287
|
+
*
|
|
1288
|
+
* The warm pools themselves are NOT started here — see
|
|
1289
|
+
* {@link startWarmPools}, which the host calls once every dependency a fill
|
|
1290
|
+
* can reach is wired.
|
|
1286
1291
|
*/
|
|
1287
1292
|
start(): void;
|
|
1293
|
+
/**
|
|
1294
|
+
* Run the warm pools' first deficit pass and arm their periodic tick.
|
|
1295
|
+
*
|
|
1296
|
+
* Separate from {@link start} because the first pass spawns immediately,
|
|
1297
|
+
* and on an event backend a spawn emits through the event emitter — which
|
|
1298
|
+
* the coordinator assigns after the scaler is built. The coordinator calls
|
|
1299
|
+
* this once the emitter exists; the worker, which hosts no event backend,
|
|
1300
|
+
* calls it right after `start()`.
|
|
1301
|
+
*/
|
|
1302
|
+
startWarmPools(): void;
|
|
1288
1303
|
/**
|
|
1289
1304
|
* Provision/heal every backend's host prerequisites before spawning starts.
|
|
1290
1305
|
* Awaits each backend's optional ensureHostReady, catching per-backend so one
|
|
@@ -1385,6 +1400,7 @@ export declare class ScalerManager {
|
|
|
1385
1400
|
* coord boot or Raft leader switch. Reconstructs:
|
|
1386
1401
|
*
|
|
1387
1402
|
* - `spawningAgents` (with `boundJobId` preserved for eager-dispatch on register)
|
|
1403
|
+
* — event-backend rows only; a local-backend row is dropped, see below
|
|
1388
1404
|
* - `agentJobCorrelation` (so scaler-lifecycle events route correctly)
|
|
1389
1405
|
* - `reservations` + `perScalerUsage` (so the cap-check critical
|
|
1390
1406
|
* section reflects the cluster-wide truth, not the local empty
|
|
@@ -374,6 +374,13 @@ export declare class ScalerStateStore {
|
|
|
374
374
|
*/
|
|
375
375
|
export interface ScalerStateRecovery {
|
|
376
376
|
spawningAgentsRehydrated: number;
|
|
377
|
+
/**
|
|
378
|
+
* Spawn rows released during recovery because their backend is local
|
|
379
|
+
* (container / firecracker / bare-metal): such a spawn cannot outlive the
|
|
380
|
+
* process that started it, so nothing could ever register against it, and
|
|
381
|
+
* rehydrating it would hold its reservation until the stale prune.
|
|
382
|
+
*/
|
|
383
|
+
spawningDropped: number;
|
|
377
384
|
agentJobsRehydrated: number;
|
|
378
385
|
reservationsRehydrated: number;
|
|
379
386
|
/**
|
package/dist/scaler/types.d.ts
CHANGED
|
@@ -97,6 +97,16 @@ export interface SpawnContext {
|
|
|
97
97
|
* single source, and computing them twice is the defect this field closes.
|
|
98
98
|
*/
|
|
99
99
|
platformTaints?: readonly string[];
|
|
100
|
+
/**
|
|
101
|
+
* The full taint gate the spawned agent is registered under: the pool's
|
|
102
|
+
* configured `mandatoryLabels` plus every derived platform taint, resolved
|
|
103
|
+
* by `ScalerManager` for the label set this spawn belongs to — the same set
|
|
104
|
+
* the durable spawn row carries. A backend publishes it (an event scaler puts
|
|
105
|
+
* it on the scale-up payload) so a provisioning workflow sees the gate the
|
|
106
|
+
* agent will actually be held to. A backend must never derive it itself: the
|
|
107
|
+
* manager is the single source, exactly as for `platformTaints`.
|
|
108
|
+
*/
|
|
109
|
+
mandatoryLabels?: readonly string[];
|
|
100
110
|
}
|
|
101
111
|
/**
|
|
102
112
|
* Network policy controlling RFC1918 and internet access for agents in this
|
|
@@ -125,10 +125,12 @@ export declare class WarmPoolManager {
|
|
|
125
125
|
* Start the periodic tick: reap surplus agents past their idle timeout, then
|
|
126
126
|
* top the pools back up.
|
|
127
127
|
*
|
|
128
|
-
* The first pass runs immediately rather than a tick later.
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
128
|
+
* The first pass runs immediately rather than a tick later. The one caller
|
|
129
|
+
* is `ScalerManager.startWarmPools()`, which each host reaches only after
|
|
130
|
+
* `ensureHostsReady()` and — on the coordinator — after the event emitter
|
|
131
|
+
* is assigned, so spawning here is safe. Waiting a tick instead would leave
|
|
132
|
+
* the pool empty for 30 seconds after every restart, which is precisely the
|
|
133
|
+
* cold start it exists to remove.
|
|
132
134
|
*/
|
|
133
135
|
start(): void;
|
|
134
136
|
/**
|
|
@@ -40,7 +40,7 @@ export interface IdentityLink {
|
|
|
40
40
|
* the answer is unambiguous; when they disagree there is no way to tell here
|
|
41
41
|
* which provider serves the commit, so this answers undefined rather than
|
|
42
42
|
* naming the wrong account. Undefined is a safe answer at every call site: the
|
|
43
|
-
* attribution is
|
|
43
|
+
* attribution is omitted, never replaced by the raw subject id.
|
|
44
44
|
*/
|
|
45
45
|
export declare function resolveLinkedUsername(identityLinks: IdentityLink[], userId: string): string | undefined;
|
|
46
46
|
/**
|
|
@@ -64,7 +64,7 @@ import type { Database } from '../db/types.js';
|
|
|
64
64
|
* What it does not pin: the field *set*. A fourth directory field added to
|
|
65
65
|
* `trustPolicyUpdateSchema` is not picked up here, and `server.ts` hand-builds
|
|
66
66
|
* the value it passes to `upsertFromPlatform`, so nothing would fail — the new
|
|
67
|
-
* field would
|
|
67
|
+
* field would go unpersisted. Adding one means extending this object and
|
|
68
68
|
* that literal together.
|
|
69
69
|
*/
|
|
70
70
|
export declare const trustDirectorySchema: z.ZodObject<{
|