@kici-dev/orchestrator 0.1.8 → 0.1.10
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 +2 -0
- package/dist/config.d.ts +2 -0
- package/dist/data-dir.d.ts +30 -0
- package/dist/scaler/container-backend.d.ts +10 -4
- package/dist/scaler/manager.d.ts +13 -0
- package/dist/server.js +153 -60
- package/dist/standalone.js +153 -60
- package/dist/ws/agent-handler.d.ts +33 -0
- package/package.json +3 -3
- package/sbom.spdx.json +36 -36
package/dist/cli.js
CHANGED
|
@@ -6180,6 +6180,8 @@ var init_systemd = __esmMin((() => {
|
|
|
6180
6180
|
const execArgs = config.args?.length ? ` ${config.args.join(" ")}` : "";
|
|
6181
6181
|
lines.push(`ExecStart=${config.executablePath}${execArgs}`);
|
|
6182
6182
|
lines.push(`EnvironmentFile=${config.envFilePath}`);
|
|
6183
|
+
const execBinDir = path.dirname(config.executablePath);
|
|
6184
|
+
lines.push(`Environment=PATH=${execBinDir}:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`);
|
|
6183
6185
|
lines.push(`WorkingDirectory=${config.workingDirectory}`);
|
|
6184
6186
|
if (!config.isUserLevel && config.user) {
|
|
6185
6187
|
lines.push(`User=${config.user}`);
|
package/dist/config.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
57
57
|
cacheBuildTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
58
58
|
cacheMaxTarballBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
59
59
|
webhookPayloadDir: z.ZodOptional<z.ZodString>;
|
|
60
|
+
dataDir: z.ZodOptional<z.ZodString>;
|
|
60
61
|
scalerConfigPath: z.ZodOptional<z.ZodString>;
|
|
61
62
|
scalerConfigDir: z.ZodOptional<z.ZodString>;
|
|
62
63
|
machineLedgerDir: z.ZodOptional<z.ZodString>;
|
|
@@ -238,6 +239,7 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
|
238
239
|
cacheStorageFsBaseUrl?: string | undefined;
|
|
239
240
|
logStorageS3Bucket?: string | undefined;
|
|
240
241
|
webhookPayloadDir?: string | undefined;
|
|
242
|
+
dataDir?: string | undefined;
|
|
241
243
|
scalerConfigPath?: string | undefined;
|
|
242
244
|
scalerConfigDir?: string | undefined;
|
|
243
245
|
machineLedgerDir?: string | undefined;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the writable base directory for orchestrator-local data (execution
|
|
3
|
+
* log storage, cache).
|
|
4
|
+
*
|
|
5
|
+
* A system-level orchestrator owns `/var/lib/kici`; a user-level install
|
|
6
|
+
* (e.g. `kici-admin orchestrator install --user-level`) does not and cannot
|
|
7
|
+
* write there. Mirrors the scaler-ledger resolution in machine-ledger.ts so
|
|
8
|
+
* both pieces of orchestrator state degrade the same way: explicit override →
|
|
9
|
+
* `/var/lib/kici` if writable → XDG state dir → tmpdir.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Return the first candidate directory that can be created and written to.
|
|
13
|
+
*
|
|
14
|
+
* Each candidate is `mkdir -p`'d and probed with a sentinel write (removed
|
|
15
|
+
* immediately) so "exists but not writable" is caught the same as "cannot be
|
|
16
|
+
* created". Throws if none are usable.
|
|
17
|
+
*/
|
|
18
|
+
export declare function firstWritableDir(candidates: string[]): string;
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the orchestrator data root.
|
|
21
|
+
*
|
|
22
|
+
* 1. `explicit` (KICI_DATA_DIR) wins — created if missing.
|
|
23
|
+
* 2. `/var/lib/kici` if writable (system-level install).
|
|
24
|
+
* 3. `${XDG_STATE_HOME:-$HOME/.local/state}/kici` (user-level install).
|
|
25
|
+
* 4. `${tmpdir}/kici-data` (last resort, e.g. CI sandboxes).
|
|
26
|
+
*
|
|
27
|
+
* Callers append their own subdir (e.g. `${dataDir}/cache/logs`).
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveDataDir(explicit: string | undefined): string;
|
|
30
|
+
//# sourceMappingURL=data-dir.d.ts.map
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { type ToolRequirement } from '@kici-dev/shared';
|
|
10
10
|
import type { AgentTokenStore } from '../agent/token-store.js';
|
|
11
|
-
import type { ScalerBackend, ManagedAgent, LabelSetConfig, LogCapture, ResourceRequest, EffectiveLimits, ScalerEventCallback, ValidationResult } from './types.js';
|
|
11
|
+
import type { ScalerBackend, ManagedAgent, LabelSetConfig, LogCapture, ResourceRequest, EffectiveLimits, ScalerEventCallback, ValidationResult, ScalerEntry } from './types.js';
|
|
12
12
|
/**
|
|
13
13
|
* Result of runtime detection.
|
|
14
14
|
*/
|
|
@@ -93,10 +93,16 @@ export declare class ContainerScalerBackend implements ScalerBackend {
|
|
|
93
93
|
private ensureIsolatedNetwork;
|
|
94
94
|
/**
|
|
95
95
|
* Declare required tools for a container scaler entry.
|
|
96
|
-
*
|
|
97
|
-
*
|
|
96
|
+
*
|
|
97
|
+
* For the auto-detect case (no explicit socketPath / remote host) the
|
|
98
|
+
* orchestrator must have a local container runtime — docker OR podman — on
|
|
99
|
+
* PATH, otherwise the scaler cannot spawn agent containers. Declaring it
|
|
100
|
+
* here lets the startup tool-validation gate fail fast with a clear error
|
|
101
|
+
* instead of the first job hanging. When a socketPath or remote host is
|
|
102
|
+
* configured the binary need not be on PATH (the runtime may be remote), so
|
|
103
|
+
* reachability is validated later in create().
|
|
98
104
|
*/
|
|
99
|
-
static getRequiredTools(): ToolRequirement[];
|
|
105
|
+
static getRequiredTools(entry: ScalerEntry): ToolRequirement[];
|
|
100
106
|
/**
|
|
101
107
|
* Create a ContainerScalerBackend with auto-detected or configured socket.
|
|
102
108
|
* Throws if no container runtime is found and no host is configured.
|
package/dist/scaler/manager.d.ts
CHANGED
|
@@ -15,6 +15,19 @@ import type { ScalerStateStore, ScalerStateRecovery } from './scaler-state-store
|
|
|
15
15
|
* combined with the scaler's `defaults.resources`, applying the request<->limit
|
|
16
16
|
* mirroring rule. Caps aggregate `requests`; backends use `limits`.
|
|
17
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the orchestrator WebSocket URL a scaler-spawned agent should dial.
|
|
20
|
+
*
|
|
21
|
+
* 1. Per-scaler `orchestratorUrl` (scalers.yaml) wins — required for container
|
|
22
|
+
* agents (host.docker.internal / LAN IP) and Firecracker VMs (bridge gateway
|
|
23
|
+
* IP), which cannot reach the orchestrator over the host's loopback.
|
|
24
|
+
* 2. `KICI_ORCHESTRATOR_URL` env override.
|
|
25
|
+
* 3. Default `ws://127.0.0.1:<orchestrator-port>/ws` — for local (bare-metal)
|
|
26
|
+
* agents that share the host. The port is the orchestrator's own bind port
|
|
27
|
+
* (`KICI_PORT`, default 4000), NOT the agent's 8080 default; pointing local
|
|
28
|
+
* agents at 8080 leaves them unable to reach the orchestrator.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveScalerOrchestratorUrl(configUrl: string | undefined, envUrl: string | undefined, port: string | number | undefined): string;
|
|
18
31
|
export interface ResolvedResources {
|
|
19
32
|
requests: {
|
|
20
33
|
cpus: number;
|
package/dist/server.js
CHANGED
|
@@ -3,7 +3,7 @@ import { dirname as __cjs_dirname } from "node:path";
|
|
|
3
3
|
__cjs_dirname(__cjs_fileURLToPath(import.meta.url));
|
|
4
4
|
import "node:module";
|
|
5
5
|
import { BaseColdStore, RingBuffer, createDb, createHealthRoutes, createLogger, createMeter, createMetricsRoutes, createPool, createS3Client, encodeKeySegment, enrichRequestContext, formatDuration, getPrometheusExporter, getReconnectDelay, getRequestContext, guardStartup, initTelemetry, requestContext, serializeError, setServiceName, setupGracefulShutdown, sha256, toErrorMessage, validateRequiredTools } from "@kici-dev/shared";
|
|
6
|
-
import { ALLOWED_SYSTEM_VARS, AccessLogAction, AccessLogOutcome, AccessLogSource, AccessLogTargetType, ActorType, CheckRunConclusion, EVENT_LOG_PAYLOAD_CHUNK_BYTES, EventLogPayloadStreamError, EventLogSource, EventLogStatus, ExecutionJobStatus, ExecutionRunStatus, ExecutionStepStatus, KICI_AGENT_ENV_PREFIX, KNOWN_ROLES, MIN_PROTOCOL_VERSION, ORCH_CAPABILITIES, OrchRole, PROTOCOL_VERSION, PayloadOmittedReason, RegisterableTriggerType, SCHEMA_VERSION, ScalerBackendType, SourceSubtype, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES, WEBHOOK_RELAY_MAX_BODY_BYTES, WS_CLOSE_AGENT_AUTH_FAILED, WS_CLOSE_AUTH_TIMEOUT, WS_CLOSE_GOING_AWAY, WS_CLOSE_HEARTBEAT_TIMEOUT, WS_CLOSE_INVALID_MESSAGE, WS_CLOSE_PROTOCOL_ERROR, WS_CLOSE_RUN_NOT_FOUND, WS_CLOSE_UNAUTHORIZED, WS_MAX_PAYLOAD_BYTES, WsRateLimiter, accessLogWarmSqlCase, agentAuthRequestSchema, agentToOrchestratorMessageSchema, agentTypeLabel, createWorkflowDecision, dashboardRunDetailApiResponseSchema, dashboardStepLogsApiResponseSchema, deriveOsArchLabels, flattenActor, getAccessLogColdDays, getSecretAuditLogColdDays, isLockDynamicJobFn, isLockInlineValue, isLockStaticJob, joinRequestSchema, logPullPlatformToOrchSchema, matchAllWorkflows, matchScopePattern, minAccessLogWarmDays, minSecretAuditLogWarmDays, observeSubscribeSchema, peerAuthRequestSchema, peerFromPeerMessageSchema, peerHelloResponseSchema, peerHelloSchema, platformToOrchestratorMessageSchema, resolveRoleLabels, resolveSecretsForEnvironment, scalerLabel, secretAuditLogWarmSqlCase, shouldRecordAccess, shouldRecordSecretResolve, stringifyActor, stripScopePrefix, testEventSchema } from "@kici-dev/engine";
|
|
6
|
+
import { ALLOWED_SYSTEM_VARS, AccessLogAction, AccessLogOutcome, AccessLogSource, AccessLogTargetType, ActorType, CheckRunConclusion, EVENT_LOG_PAYLOAD_CHUNK_BYTES, EventLogPayloadStreamError, EventLogSource, EventLogStatus, ExecutionJobStatus, ExecutionRunStatus, ExecutionStepStatus, KICI_AGENT_ENV_PREFIX, KNOWN_ROLES, MIN_PROTOCOL_VERSION, ORCH_CAPABILITIES, OrchRole, PROTOCOL_VERSION, PayloadOmittedReason, RegisterableTriggerType, SCHEMA_VERSION, ScalerBackendType, SourceSubtype, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES, WEBHOOK_RELAY_MAX_BODY_BYTES, WS_CLOSE_AGENT_AUTH_FAILED, WS_CLOSE_AUTH_TIMEOUT, WS_CLOSE_GOING_AWAY, WS_CLOSE_HEARTBEAT_TIMEOUT, WS_CLOSE_INVALID_MESSAGE, WS_CLOSE_PROTOCOL_ERROR, WS_CLOSE_RUN_NOT_FOUND, WS_CLOSE_UNAUTHORIZED, WS_MAX_PAYLOAD_BYTES, WsRateLimiter, accessLogWarmSqlCase, agentAuthRequestSchema, agentToOrchestratorMessageSchema, agentTypeLabel, createWorkflowDecision, dashboardRunDetailApiResponseSchema, dashboardStepLogsApiResponseSchema, deriveOsArchLabels, flattenActor, getAccessLogColdDays, getSecretAuditLogColdDays, isLockDynamicJobFn, isLockInlineValue, isLockStaticJob, isSelfReportedLabel, joinRequestSchema, logPullPlatformToOrchSchema, matchAllWorkflows, matchScopePattern, minAccessLogWarmDays, minSecretAuditLogWarmDays, observeSubscribeSchema, peerAuthRequestSchema, peerFromPeerMessageSchema, peerHelloResponseSchema, peerHelloSchema, platformToOrchestratorMessageSchema, resolveRoleLabels, resolveSecretsForEnvironment, scalerAgentLabels, scalerLabel, secretAuditLogWarmSqlCase, shouldRecordAccess, shouldRecordSecretResolve, stringifyActor, stripScopePrefix, testEventSchema } from "@kici-dev/engine";
|
|
7
7
|
import { verifySignature } from "@kici-dev/engine/webhook/signature";
|
|
8
8
|
import { X509Certificate, createCipheriv, createDecipheriv, createHmac, createPrivateKey, createPublicKey, diffieHellman, generateKeyPairSync, hkdfSync, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
9
9
|
import os, { cpus, freemem, homedir, hostname, release, tmpdir, totalmem, uptime, userInfo, version } from "node:os";
|
|
@@ -18,7 +18,7 @@ import picomatch from "picomatch";
|
|
|
18
18
|
import vm from "node:vm";
|
|
19
19
|
import { EventEmitter } from "node:events";
|
|
20
20
|
import { DASHBOARD_WRITE_OPERATIONS_BY_NAME, dashboardWritePolicyMapSchema, isDashboardWriteOperationEnabled, resolveFullPolicyView, resolveFullPolicyView as resolveFullPolicyView$1 } from "@kici-dev/engine/protocol/dashboard-write-operations";
|
|
21
|
-
import { chmodSync, closeSync, createReadStream, createWriteStream, mkdtempSync, openSync, promises, readFileSync, rmSync, statSync, unlinkSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
|
|
21
|
+
import { chmodSync, closeSync, createReadStream, createWriteStream, mkdirSync, mkdtempSync, openSync, promises, readFileSync, rmSync, statSync, unlinkSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
|
|
22
22
|
import { Cron } from "croner";
|
|
23
23
|
import Vault from "hashi-vault-js";
|
|
24
24
|
import "pg";
|
|
@@ -421,6 +421,7 @@ var init_config$5 = __esmMin((() => {
|
|
|
421
421
|
cacheBuildTimeoutMs: z.coerce.number().default(6e5),
|
|
422
422
|
cacheMaxTarballBytes: z.coerce.number().default(524288e3),
|
|
423
423
|
webhookPayloadDir: z.string().optional(),
|
|
424
|
+
dataDir: z.string().optional(),
|
|
424
425
|
scalerConfigPath: z.string().optional(),
|
|
425
426
|
scalerConfigDir: z.string().optional(),
|
|
426
427
|
machineLedgerDir: z.string().optional(),
|
|
@@ -605,6 +606,7 @@ var init_config$5 = __esmMin((() => {
|
|
|
605
606
|
workerConcurrency: "KICI_WORKER_CONCURRENCY",
|
|
606
607
|
concurrencyWaitTimeoutMs: "KICI_CONCURRENCY_WAIT_TIMEOUT_MS",
|
|
607
608
|
webhookPayloadDir: "KICI_WEBHOOK_PAYLOAD_DIR",
|
|
609
|
+
dataDir: "KICI_DATA_DIR",
|
|
608
610
|
scalerConfigPath: "KICI_SCALER_CONFIG_PATH",
|
|
609
611
|
scalerConfigDir: "KICI_SCALER_CONFIG_DIR",
|
|
610
612
|
machineLedgerDir: "KICI_MACHINE_LEDGER_DIR",
|
|
@@ -15149,7 +15151,7 @@ var init_peer_client = __esmMin((() => {
|
|
|
15149
15151
|
init_peer_crypto();
|
|
15150
15152
|
init_peer_credentials();
|
|
15151
15153
|
logger$73 = createLogger({ prefix: "peer-client" });
|
|
15152
|
-
SOFTWARE_VERSION$1 = "0.1.
|
|
15154
|
+
SOFTWARE_VERSION$1 = "0.1.10";
|
|
15153
15155
|
PeerClient$1 = class {
|
|
15154
15156
|
ws = null;
|
|
15155
15157
|
_state = "disconnected";
|
|
@@ -16612,7 +16614,7 @@ var init_peer_handler = __esmMin((() => {
|
|
|
16612
16614
|
init_peer_crypto();
|
|
16613
16615
|
init_join_token();
|
|
16614
16616
|
logger$72 = createLogger({ prefix: "peer-handler" });
|
|
16615
|
-
SOFTWARE_VERSION = "0.1.
|
|
16617
|
+
SOFTWARE_VERSION = "0.1.10";
|
|
16616
16618
|
RATE_LIMIT_MAX = 5;
|
|
16617
16619
|
RATE_LIMIT_WINDOW_MS = 6e4;
|
|
16618
16620
|
}));
|
|
@@ -18172,6 +18174,59 @@ var init_global_workflow_policy = __esmMin((() => {
|
|
|
18172
18174
|
};
|
|
18173
18175
|
}));
|
|
18174
18176
|
//#endregion
|
|
18177
|
+
//#region src/data-dir.ts
|
|
18178
|
+
/**
|
|
18179
|
+
* Resolve the writable base directory for orchestrator-local data (execution
|
|
18180
|
+
* log storage, cache).
|
|
18181
|
+
*
|
|
18182
|
+
* A system-level orchestrator owns `/var/lib/kici`; a user-level install
|
|
18183
|
+
* (e.g. `kici-admin orchestrator install --user-level`) does not and cannot
|
|
18184
|
+
* write there. Mirrors the scaler-ledger resolution in machine-ledger.ts so
|
|
18185
|
+
* both pieces of orchestrator state degrade the same way: explicit override →
|
|
18186
|
+
* `/var/lib/kici` if writable → XDG state dir → tmpdir.
|
|
18187
|
+
*/
|
|
18188
|
+
/**
|
|
18189
|
+
* Return the first candidate directory that can be created and written to.
|
|
18190
|
+
*
|
|
18191
|
+
* Each candidate is `mkdir -p`'d and probed with a sentinel write (removed
|
|
18192
|
+
* immediately) so "exists but not writable" is caught the same as "cannot be
|
|
18193
|
+
* created". Throws if none are usable.
|
|
18194
|
+
*/
|
|
18195
|
+
function firstWritableDir(candidates) {
|
|
18196
|
+
for (const dir of candidates) try {
|
|
18197
|
+
mkdirSync(dir, { recursive: true });
|
|
18198
|
+
const sentinel = join(dir, `.write-probe-${process.pid}`);
|
|
18199
|
+
writeFileSync(sentinel, "probe");
|
|
18200
|
+
rmSync(sentinel, { force: true });
|
|
18201
|
+
return dir;
|
|
18202
|
+
} catch {
|
|
18203
|
+
continue;
|
|
18204
|
+
}
|
|
18205
|
+
throw new Error(`data-dir: no writable directory among candidates: ${candidates.join(", ")}`);
|
|
18206
|
+
}
|
|
18207
|
+
/**
|
|
18208
|
+
* Resolve the orchestrator data root.
|
|
18209
|
+
*
|
|
18210
|
+
* 1. `explicit` (KICI_DATA_DIR) wins — created if missing.
|
|
18211
|
+
* 2. `/var/lib/kici` if writable (system-level install).
|
|
18212
|
+
* 3. `${XDG_STATE_HOME:-$HOME/.local/state}/kici` (user-level install).
|
|
18213
|
+
* 4. `${tmpdir}/kici-data` (last resort, e.g. CI sandboxes).
|
|
18214
|
+
*
|
|
18215
|
+
* Callers append their own subdir (e.g. `${dataDir}/cache/logs`).
|
|
18216
|
+
*/
|
|
18217
|
+
function resolveDataDir(explicit) {
|
|
18218
|
+
if (explicit) {
|
|
18219
|
+
mkdirSync(explicit, { recursive: true });
|
|
18220
|
+
return explicit;
|
|
18221
|
+
}
|
|
18222
|
+
return firstWritableDir([
|
|
18223
|
+
"/var/lib/kici",
|
|
18224
|
+
join(process.env.XDG_STATE_HOME ?? join(homedir(), ".local", "state"), "kici"),
|
|
18225
|
+
join(tmpdir(), "kici-data")
|
|
18226
|
+
]);
|
|
18227
|
+
}
|
|
18228
|
+
var init_data_dir = __esmMin((() => {}));
|
|
18229
|
+
//#endregion
|
|
18175
18230
|
//#region src/config/reload.ts
|
|
18176
18231
|
/**
|
|
18177
18232
|
* Compute a list of top-level config fields that differ between old and new config.
|
|
@@ -24719,13 +24774,25 @@ var init_check_run_reporter = __esmMin((() => {
|
|
|
24719
24774
|
* returns `false` after closing the WS on the first violation, in which
|
|
24720
24775
|
* case the caller MUST stop processing.
|
|
24721
24776
|
*/
|
|
24777
|
+
/**
|
|
24778
|
+
* WebSocket close reasons are capped at 123 UTF-8 bytes (RFC 6455 §5.5.1).
|
|
24779
|
+
* A reason longer than that makes `ws.close()` throw a RangeError, which —
|
|
24780
|
+
* thrown from an async message handler — surfaces as an unhandled rejection
|
|
24781
|
+
* and skips the close entirely. Truncate on a byte boundary and drop any
|
|
24782
|
+
* trailing partial multi-byte char.
|
|
24783
|
+
*/
|
|
24784
|
+
function truncateCloseReason(reason) {
|
|
24785
|
+
const bytes = Buffer.from(reason, "utf-8");
|
|
24786
|
+
if (bytes.length <= 123) return reason;
|
|
24787
|
+
return bytes.subarray(0, 123).toString("utf-8").replace(/�+$/, "");
|
|
24788
|
+
}
|
|
24722
24789
|
function enforceRegisterAuthGates(authState, payload, ws, agentIdToTokenId) {
|
|
24723
24790
|
if (authState === void 0) return true;
|
|
24724
24791
|
const { tokenId, tokenLabels, tokenAgentType, tokenCreatedBy } = authState;
|
|
24725
24792
|
const { agentId, labels } = payload;
|
|
24726
24793
|
if (tokenLabels !== void 0 && tokenLabels !== null) {
|
|
24727
24794
|
const allowedSet = new Set(tokenLabels);
|
|
24728
|
-
const elevated = labels.filter((l) => !allowedSet.has(l));
|
|
24795
|
+
const elevated = labels.filter((l) => !allowedSet.has(l) && !isSelfReportedLabel(l));
|
|
24729
24796
|
if (elevated.length > 0) {
|
|
24730
24797
|
logger$59.warn("Agent register-time label-scope violation: wire labels exceed token-bound set", {
|
|
24731
24798
|
agentId,
|
|
@@ -24733,7 +24800,7 @@ function enforceRegisterAuthGates(authState, payload, ws, agentIdToTokenId) {
|
|
|
24733
24800
|
wireLabels: labels,
|
|
24734
24801
|
elevated
|
|
24735
24802
|
});
|
|
24736
|
-
ws.close(WS_CLOSE_AGENT_AUTH_FAILED, `Agent labels exceed token-bound scope: ${elevated.join(",")}`);
|
|
24803
|
+
ws.close(WS_CLOSE_AGENT_AUTH_FAILED, truncateCloseReason(`Agent labels exceed token-bound scope: ${elevated.join(",")}`));
|
|
24737
24804
|
return false;
|
|
24738
24805
|
}
|
|
24739
24806
|
}
|
|
@@ -24743,7 +24810,7 @@ function enforceRegisterAuthGates(authState, payload, ws, agentIdToTokenId) {
|
|
|
24743
24810
|
tokenCreatedBy,
|
|
24744
24811
|
tokenId
|
|
24745
24812
|
});
|
|
24746
|
-
ws.close(WS_CLOSE_AGENT_AUTH_FAILED, `Ephemeral token bound to a different agentId: expected ${tokenCreatedBy}, got ${agentId}`);
|
|
24813
|
+
ws.close(WS_CLOSE_AGENT_AUTH_FAILED, truncateCloseReason(`Ephemeral token bound to a different agentId: expected ${tokenCreatedBy}, got ${agentId}`));
|
|
24747
24814
|
return false;
|
|
24748
24815
|
}
|
|
24749
24816
|
if (tokenId !== void 0) {
|
|
@@ -34049,15 +34116,15 @@ var init_admin_config = __esmMin((() => {
|
|
|
34049
34116
|
function createHealthRoutes$1(deps = {}) {
|
|
34050
34117
|
return createHealthRoutes({
|
|
34051
34118
|
livenessInfo: () => ({
|
|
34052
|
-
version: "0.1.
|
|
34053
|
-
buildDate: "2026-05-
|
|
34054
|
-
buildCommit: "
|
|
34055
|
-
sdkVersion: "0.1.
|
|
34056
|
-
sdkBundleHash: "
|
|
34057
|
-
sharedVersion: "0.1.
|
|
34119
|
+
version: "0.1.10",
|
|
34120
|
+
buildDate: "2026-05-26T17:19:08.888Z",
|
|
34121
|
+
buildCommit: "6bf528b56",
|
|
34122
|
+
sdkVersion: "0.1.10",
|
|
34123
|
+
sdkBundleHash: "unknown",
|
|
34124
|
+
sharedVersion: "0.1.10",
|
|
34058
34125
|
sharedBundleHash: "36a23a454fd75ec35f258d39f254543ec0451c6b543fe3618ce427e6e6369aae",
|
|
34059
|
-
engineVersion: "0.1.
|
|
34060
|
-
engineBundleHash: "
|
|
34126
|
+
engineVersion: "0.1.10",
|
|
34127
|
+
engineBundleHash: "dff25bbd7602e853cec6e6e985c6c956ad773e245830273cd52950fff958d4bb"
|
|
34061
34128
|
}),
|
|
34062
34129
|
readinessCheck: deps.db ? async () => {
|
|
34063
34130
|
const checks = {};
|
|
@@ -34091,7 +34158,7 @@ function createCapabilitiesRoutes() {
|
|
|
34091
34158
|
const app = new Hono();
|
|
34092
34159
|
app.get("/api/v1/capabilities", (c) => {
|
|
34093
34160
|
const manifest = {
|
|
34094
|
-
orchestratorVersion: "0.1.
|
|
34161
|
+
orchestratorVersion: "0.1.10",
|
|
34095
34162
|
protocolVersion: PROTOCOL_VERSION,
|
|
34096
34163
|
minProtocolVersion: MIN_PROTOCOL_VERSION
|
|
34097
34164
|
};
|
|
@@ -36452,11 +36519,22 @@ var init_container_backend = __esmMin((() => {
|
|
|
36452
36519
|
}
|
|
36453
36520
|
/**
|
|
36454
36521
|
* Declare required tools for a container scaler entry.
|
|
36455
|
-
*
|
|
36456
|
-
*
|
|
36522
|
+
*
|
|
36523
|
+
* For the auto-detect case (no explicit socketPath / remote host) the
|
|
36524
|
+
* orchestrator must have a local container runtime — docker OR podman — on
|
|
36525
|
+
* PATH, otherwise the scaler cannot spawn agent containers. Declaring it
|
|
36526
|
+
* here lets the startup tool-validation gate fail fast with a clear error
|
|
36527
|
+
* instead of the first job hanging. When a socketPath or remote host is
|
|
36528
|
+
* configured the binary need not be on PATH (the runtime may be remote), so
|
|
36529
|
+
* reachability is validated later in create().
|
|
36457
36530
|
*/
|
|
36458
|
-
static getRequiredTools() {
|
|
36459
|
-
return [];
|
|
36531
|
+
static getRequiredTools(entry) {
|
|
36532
|
+
if (entry.host || entry.socketPath) return [];
|
|
36533
|
+
return [{
|
|
36534
|
+
type: "any-path-binary",
|
|
36535
|
+
names: ["docker", "podman"],
|
|
36536
|
+
reason: `container scaler "${entry.name}" needs a local container runtime to spawn agents. Install Docker or Podman, or set socketPath / host in scalers.yaml for a remote runtime.`
|
|
36537
|
+
}];
|
|
36460
36538
|
}
|
|
36461
36539
|
/**
|
|
36462
36540
|
* Create a ContainerScalerBackend with auto-detected or configured socket.
|
|
@@ -36517,17 +36595,13 @@ var init_container_backend = __esmMin((() => {
|
|
|
36517
36595
|
const stripped = key.slice(KICI_AGENT_ENV_PREFIX.length);
|
|
36518
36596
|
if (stripped.length > 0) agentEnvForwarded.push(`${stripped}=${value}`);
|
|
36519
36597
|
}
|
|
36598
|
+
const fullLabels = scalerAgentLabels(labelSet, this.type, this.name, this.roles);
|
|
36520
36599
|
let agentToken;
|
|
36521
|
-
if (this.tokenStore) agentToken = await this.tokenStore.createEphemeral(agentId,
|
|
36600
|
+
if (this.tokenStore) agentToken = await this.tokenStore.createEphemeral(agentId, fullLabels, this.tokenTtlMs);
|
|
36522
36601
|
const env = [
|
|
36523
36602
|
`KICI_ORCHESTRATOR_URL=${orchestratorUrl}`,
|
|
36524
36603
|
`KICI_AGENT_ID=${agentId}`,
|
|
36525
|
-
`KICI_LABELS=${
|
|
36526
|
-
...labelSet,
|
|
36527
|
-
`kici:agent:${this.type}`,
|
|
36528
|
-
`kici:scaler:${this.name}`,
|
|
36529
|
-
...resolveRoleLabels(this.roles)
|
|
36530
|
-
].join(",")}`,
|
|
36604
|
+
`KICI_LABELS=${fullLabels.join(",")}`,
|
|
36531
36605
|
`KICI_SCALER_MANAGED=1`,
|
|
36532
36606
|
`KICI_EXECUTION_MODE=bare-metal`,
|
|
36533
36607
|
...agentToken ? [`KICI_AGENT_TOKEN=${agentToken}`] : [],
|
|
@@ -36780,6 +36854,11 @@ var init_bare_metal_backend = __esmMin((() => {
|
|
|
36780
36854
|
mode: "executable",
|
|
36781
36855
|
reason: `agent binary for bare-metal scaler "${entry.name}"`
|
|
36782
36856
|
}));
|
|
36857
|
+
requirements.push({
|
|
36858
|
+
type: "path-binary",
|
|
36859
|
+
name: "node",
|
|
36860
|
+
reason: `bare-metal scaler "${entry.name}" spawns the kici-agent node script. node must be on the orchestrator's PATH (it is forwarded to spawned agents). Ensure the orchestrator service's PATH includes your node install (e.g. the systemd unit's Environment=PATH covers the mise/nvm node bin dir).`
|
|
36861
|
+
});
|
|
36783
36862
|
const sandboxViaGlobalEnv = process.env.KICI_AGENT_ENV_KICI_SANDBOX === "true";
|
|
36784
36863
|
const sandboxViaLabelSet = entry.labelSets.some((ls) => ls.env && ls.env.KICI_SANDBOX === "true");
|
|
36785
36864
|
if (sandboxViaGlobalEnv || sandboxViaLabelSet) requirements.push({
|
|
@@ -36818,15 +36897,11 @@ var init_bare_metal_backend = __esmMin((() => {
|
|
|
36818
36897
|
const strippedKey = key.slice(KICI_AGENT_ENV_PREFIX.length);
|
|
36819
36898
|
if (strippedKey.length > 0) env[strippedKey] = value;
|
|
36820
36899
|
}
|
|
36821
|
-
|
|
36900
|
+
const fullLabels = scalerAgentLabels(labelSet, this.type, this.name, this.roles);
|
|
36901
|
+
if (this.tokenStore) env.KICI_AGENT_TOKEN = await this.tokenStore.createEphemeral(agentId, fullLabels, this.tokenTtlMs);
|
|
36822
36902
|
env.KICI_ORCHESTRATOR_URL = orchestratorUrl;
|
|
36823
36903
|
env.KICI_AGENT_ID = agentId;
|
|
36824
|
-
env.KICI_LABELS =
|
|
36825
|
-
...labelSet,
|
|
36826
|
-
`kici:agent:${this.type}`,
|
|
36827
|
-
`kici:scaler:${this.name}`,
|
|
36828
|
-
...resolveRoleLabels(this.roles)
|
|
36829
|
-
].join(",");
|
|
36904
|
+
env.KICI_LABELS = fullLabels.join(",");
|
|
36830
36905
|
env.KICI_SCALER_MANAGED = "1";
|
|
36831
36906
|
env.KICI_EXECUTION_MODE = "bare-metal";
|
|
36832
36907
|
env.KICI_PORT = "0";
|
|
@@ -37576,18 +37651,14 @@ var init_firecracker_backend = __esmMin((() => {
|
|
|
37576
37651
|
if (chmodLoosenInterval) clearInterval(chmodLoosenInterval);
|
|
37577
37652
|
}
|
|
37578
37653
|
if (!ready) throw new Error(`Firecracker API socket not ready within 5s for agent ${agentId}`);
|
|
37654
|
+
const fullLabels = scalerAgentLabels(labelSet, this.type, this.name, this.roles);
|
|
37579
37655
|
let agentToken;
|
|
37580
|
-
if (this.tokenStore) agentToken = await this.tokenStore.createEphemeral(agentId,
|
|
37656
|
+
if (this.tokenStore) agentToken = await this.tokenStore.createEphemeral(agentId, fullLabels, this.tokenTtlMs);
|
|
37581
37657
|
const acceptedEnv = this.buildForwardedEnv(matchedLabelSet, agentId);
|
|
37582
37658
|
await api.putMmds({ latest: { "meta-data": {
|
|
37583
37659
|
"kici-orchestrator-url": orchestratorUrl,
|
|
37584
37660
|
"kici-agent-id": agentId,
|
|
37585
|
-
"kici-labels":
|
|
37586
|
-
...labelSet,
|
|
37587
|
-
`kici:agent:${this.type}`,
|
|
37588
|
-
`kici:scaler:${this.name}`,
|
|
37589
|
-
...resolveRoleLabels(this.roles)
|
|
37590
|
-
].join(","),
|
|
37661
|
+
"kici-labels": fullLabels.join(","),
|
|
37591
37662
|
"kici-scaler-managed": "1",
|
|
37592
37663
|
"kici-gateway-ip": this.gateway,
|
|
37593
37664
|
...agentToken ? { "kici-agent-token": agentToken } : {},
|
|
@@ -38664,6 +38735,29 @@ var init_machine_ledger = __esmMin((() => {
|
|
|
38664
38735
|
* and manages the agent lifecycle from spawn to destroy.
|
|
38665
38736
|
*/
|
|
38666
38737
|
/**
|
|
38738
|
+
* Resolved per-job resource amounts (cpus + bytes) for both `requests` and
|
|
38739
|
+
* `limits`. The scaler manager produces this from the job's declared resources
|
|
38740
|
+
* combined with the scaler's `defaults.resources`, applying the request<->limit
|
|
38741
|
+
* mirroring rule. Caps aggregate `requests`; backends use `limits`.
|
|
38742
|
+
*/
|
|
38743
|
+
/**
|
|
38744
|
+
* Resolve the orchestrator WebSocket URL a scaler-spawned agent should dial.
|
|
38745
|
+
*
|
|
38746
|
+
* 1. Per-scaler `orchestratorUrl` (scalers.yaml) wins — required for container
|
|
38747
|
+
* agents (host.docker.internal / LAN IP) and Firecracker VMs (bridge gateway
|
|
38748
|
+
* IP), which cannot reach the orchestrator over the host's loopback.
|
|
38749
|
+
* 2. `KICI_ORCHESTRATOR_URL` env override.
|
|
38750
|
+
* 3. Default `ws://127.0.0.1:<orchestrator-port>/ws` — for local (bare-metal)
|
|
38751
|
+
* agents that share the host. The port is the orchestrator's own bind port
|
|
38752
|
+
* (`KICI_PORT`, default 4000), NOT the agent's 8080 default; pointing local
|
|
38753
|
+
* agents at 8080 leaves them unable to reach the orchestrator.
|
|
38754
|
+
*/
|
|
38755
|
+
function resolveScalerOrchestratorUrl(configUrl, envUrl, port) {
|
|
38756
|
+
if (configUrl) return configUrl;
|
|
38757
|
+
if (envUrl) return envUrl;
|
|
38758
|
+
return `ws://127.0.0.1:${port ?? "4000"}/ws`;
|
|
38759
|
+
}
|
|
38760
|
+
/**
|
|
38667
38761
|
* Apply the request<->limit mirroring rule: if only one side is set, copy it
|
|
38668
38762
|
* to the other; if neither is set, return undefined; if both are set, leave them.
|
|
38669
38763
|
*
|
|
@@ -39575,9 +39669,7 @@ var init_manager = __esmMin((() => {
|
|
|
39575
39669
|
});
|
|
39576
39670
|
}
|
|
39577
39671
|
getOrchestratorUrl(backendName) {
|
|
39578
|
-
|
|
39579
|
-
if (configUrl) return configUrl;
|
|
39580
|
-
return process.env.KICI_ORCHESTRATOR_URL ?? "ws://localhost:8080/ws";
|
|
39672
|
+
return resolveScalerOrchestratorUrl(this.scalerUrls.get(backendName), process.env.KICI_ORCHESTRATOR_URL, process.env.KICI_PORT);
|
|
39581
39673
|
}
|
|
39582
39674
|
/**
|
|
39583
39675
|
* Create a per-agent event emitter closure to pass to backend.spawn().
|
|
@@ -47162,7 +47254,7 @@ async function initializeScaler(config, db, tokenStore) {
|
|
|
47162
47254
|
}
|
|
47163
47255
|
const toolErrors = validateRequiredTools(scalerConfig.scalers.flatMap((s) => {
|
|
47164
47256
|
switch (s.type) {
|
|
47165
|
-
case "container": return ContainerScalerBackend.getRequiredTools();
|
|
47257
|
+
case "container": return ContainerScalerBackend.getRequiredTools(s);
|
|
47166
47258
|
case "bare-metal": return BareMetalScalerBackend.getRequiredTools(s);
|
|
47167
47259
|
case "firecracker": return FirecrackerScalerBackend.getRequiredTools(s);
|
|
47168
47260
|
default: return [];
|
|
@@ -48154,7 +48246,7 @@ async function bootstrapOrchestrator$1(config, hooks, options) {
|
|
|
48154
48246
|
forcePathStyle: config.storage.forcePathStyle
|
|
48155
48247
|
} : {
|
|
48156
48248
|
type: "filesystem",
|
|
48157
|
-
basePath: (config.webhookPayloadDir ?? "/
|
|
48249
|
+
basePath: (config.webhookPayloadDir ?? resolveDataDir(config.dataDir) + "/cache") + "/logs"
|
|
48158
48250
|
});
|
|
48159
48251
|
const observerRegistry = new ObserverRegistry();
|
|
48160
48252
|
let executionTrackerRef = null;
|
|
@@ -48954,6 +49046,7 @@ async function bootstrapOrchestrator$1(config, hooks, options) {
|
|
|
48954
49046
|
}
|
|
48955
49047
|
var logger$2;
|
|
48956
49048
|
var init_orchestrator_core = __esmMin((() => {
|
|
49049
|
+
init_data_dir();
|
|
48957
49050
|
init_reload();
|
|
48958
49051
|
init_resolver();
|
|
48959
49052
|
init_client();
|
|
@@ -50226,14 +50319,14 @@ var init_worker_core = __esmMin((() => {
|
|
|
50226
50319
|
init_agent_handler();
|
|
50227
50320
|
init_worker_status();
|
|
50228
50321
|
init_agent_heartbeat();
|
|
50229
|
-
ORCHESTRATOR_VERSION$1 = "0.1.
|
|
50230
|
-
WORKER_BUILD_COMMIT = "
|
|
50231
|
-
WORKER_SDK_VERSION = "0.1.
|
|
50232
|
-
WORKER_SDK_BUNDLE_HASH = "
|
|
50233
|
-
WORKER_SHARED_VERSION = "0.1.
|
|
50322
|
+
ORCHESTRATOR_VERSION$1 = "0.1.10";
|
|
50323
|
+
WORKER_BUILD_COMMIT = "6bf528b56";
|
|
50324
|
+
WORKER_SDK_VERSION = "0.1.10";
|
|
50325
|
+
WORKER_SDK_BUNDLE_HASH = "unknown";
|
|
50326
|
+
WORKER_SHARED_VERSION = "0.1.10";
|
|
50234
50327
|
WORKER_SHARED_BUNDLE_HASH = "36a23a454fd75ec35f258d39f254543ec0451c6b543fe3618ce427e6e6369aae";
|
|
50235
|
-
WORKER_ENGINE_VERSION = "0.1.
|
|
50236
|
-
WORKER_ENGINE_BUNDLE_HASH = "
|
|
50328
|
+
WORKER_ENGINE_VERSION = "0.1.10";
|
|
50329
|
+
WORKER_ENGINE_BUNDLE_HASH = "dff25bbd7602e853cec6e6e985c6c956ad773e245830273cd52950fff958d4bb";
|
|
50237
50330
|
logger$1 = createLogger({ prefix: "worker" });
|
|
50238
50331
|
DRAIN_TIMEOUT_MS = 3e5;
|
|
50239
50332
|
}));
|
|
@@ -50254,14 +50347,14 @@ var init_worker_core = __esmMin((() => {
|
|
|
50254
50347
|
* Graceful shutdown in reverse order:
|
|
50255
50348
|
* Platform client -> agent WS -> heartbeat -> HTTP -> DB
|
|
50256
50349
|
*/
|
|
50257
|
-
const ORCHESTRATOR_VERSION = "0.1.
|
|
50258
|
-
const BUILD_COMMIT = "
|
|
50259
|
-
const SDK_VERSION = "0.1.
|
|
50260
|
-
const SDK_BUNDLE_HASH = "
|
|
50261
|
-
const SHARED_VERSION = "0.1.
|
|
50350
|
+
const ORCHESTRATOR_VERSION = "0.1.10";
|
|
50351
|
+
const BUILD_COMMIT = "6bf528b56";
|
|
50352
|
+
const SDK_VERSION = "0.1.10";
|
|
50353
|
+
const SDK_BUNDLE_HASH = "unknown";
|
|
50354
|
+
const SHARED_VERSION = "0.1.10";
|
|
50262
50355
|
const SHARED_BUNDLE_HASH = "36a23a454fd75ec35f258d39f254543ec0451c6b543fe3618ce427e6e6369aae";
|
|
50263
|
-
const ENGINE_VERSION = "0.1.
|
|
50264
|
-
const ENGINE_BUNDLE_HASH = "
|
|
50356
|
+
const ENGINE_VERSION = "0.1.10";
|
|
50357
|
+
const ENGINE_BUNDLE_HASH = "dff25bbd7602e853cec6e6e985c6c956ad773e245830273cd52950fff958d4bb";
|
|
50265
50358
|
const otelSdk = initTelemetry({
|
|
50266
50359
|
serviceName: "kici-orchestrator",
|
|
50267
50360
|
otlpEndpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT
|
package/dist/standalone.js
CHANGED
|
@@ -8,8 +8,8 @@ import { X509Certificate, createCipheriv, createDecipheriv, createHmac, createPr
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { LOGGER_ENV_VARS, defineEnv, validateUnknownKiciVars } from "@kici-dev/shared/env";
|
|
10
10
|
import WebSocket from "ws";
|
|
11
|
-
import { chmodSync, closeSync, createReadStream, createWriteStream, mkdtempSync, openSync, promises, readFileSync, rmSync, statSync, unlinkSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
|
|
12
|
-
import { ALLOWED_SYSTEM_VARS, AccessLogAction, AccessLogOutcome, AccessLogSource, AccessLogTargetType, ActorType, CheckRunConclusion, EventLogSource, EventLogStatus, ExecutionJobStatus, ExecutionRunStatus, ExecutionStepStatus, KICI_AGENT_ENV_PREFIX, KNOWN_ROLES, MIN_PROTOCOL_VERSION, PROTOCOL_VERSION, PayloadOmittedReason, RegisterableTriggerType, SCHEMA_VERSION, ScalerBackendType, SourceSubtype, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES, WS_CLOSE_AGENT_AUTH_FAILED, WS_CLOSE_AUTH_TIMEOUT, WS_CLOSE_GOING_AWAY, WS_CLOSE_HEARTBEAT_TIMEOUT, WS_CLOSE_INVALID_MESSAGE, WS_CLOSE_PROTOCOL_ERROR, WS_CLOSE_RUN_NOT_FOUND, WS_CLOSE_UNAUTHORIZED, WS_MAX_PAYLOAD_BYTES, WsRateLimiter, accessLogWarmSqlCase, agentAuthRequestSchema, agentToOrchestratorMessageSchema, agentTypeLabel, createWorkflowDecision, deriveOsArchLabels, flattenActor, getAccessLogColdDays, getSecretAuditLogColdDays, isLockDynamicJobFn, isLockInlineValue, isLockStaticJob, matchAllWorkflows, minAccessLogWarmDays, minSecretAuditLogWarmDays, observeSubscribeSchema, peerAuthRequestSchema, peerFromPeerMessageSchema, peerHelloResponseSchema, peerHelloSchema, resolveRoleLabels, scalerLabel, secretAuditLogWarmSqlCase, shouldRecordAccess, shouldRecordSecretResolve, testEventSchema } from "@kici-dev/engine";
|
|
11
|
+
import { chmodSync, closeSync, createReadStream, createWriteStream, mkdirSync, mkdtempSync, openSync, promises, readFileSync, rmSync, statSync, unlinkSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
|
|
12
|
+
import { ALLOWED_SYSTEM_VARS, AccessLogAction, AccessLogOutcome, AccessLogSource, AccessLogTargetType, ActorType, CheckRunConclusion, EventLogSource, EventLogStatus, ExecutionJobStatus, ExecutionRunStatus, ExecutionStepStatus, KICI_AGENT_ENV_PREFIX, KNOWN_ROLES, MIN_PROTOCOL_VERSION, PROTOCOL_VERSION, PayloadOmittedReason, RegisterableTriggerType, SCHEMA_VERSION, ScalerBackendType, SourceSubtype, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES, WS_CLOSE_AGENT_AUTH_FAILED, WS_CLOSE_AUTH_TIMEOUT, WS_CLOSE_GOING_AWAY, WS_CLOSE_HEARTBEAT_TIMEOUT, WS_CLOSE_INVALID_MESSAGE, WS_CLOSE_PROTOCOL_ERROR, WS_CLOSE_RUN_NOT_FOUND, WS_CLOSE_UNAUTHORIZED, WS_MAX_PAYLOAD_BYTES, WsRateLimiter, accessLogWarmSqlCase, agentAuthRequestSchema, agentToOrchestratorMessageSchema, agentTypeLabel, createWorkflowDecision, deriveOsArchLabels, flattenActor, getAccessLogColdDays, getSecretAuditLogColdDays, isLockDynamicJobFn, isLockInlineValue, isLockStaticJob, isSelfReportedLabel, matchAllWorkflows, minAccessLogWarmDays, minSecretAuditLogWarmDays, observeSubscribeSchema, peerAuthRequestSchema, peerFromPeerMessageSchema, peerHelloResponseSchema, peerHelloSchema, resolveRoleLabels, scalerAgentLabels, scalerLabel, secretAuditLogWarmSqlCase, shouldRecordAccess, shouldRecordSecretResolve, testEventSchema } from "@kici-dev/engine";
|
|
13
13
|
import { access, appendFile, chmod, constants, copyFile, link, mkdir, mkdtemp, open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
14
14
|
import { dirname, join, relative, resolve } from "node:path";
|
|
15
15
|
import { sql } from "kysely";
|
|
@@ -175,6 +175,7 @@ var init_config$5 = __esmMin((() => {
|
|
|
175
175
|
cacheBuildTimeoutMs: z.coerce.number().default(6e5),
|
|
176
176
|
cacheMaxTarballBytes: z.coerce.number().default(524288e3),
|
|
177
177
|
webhookPayloadDir: z.string().optional(),
|
|
178
|
+
dataDir: z.string().optional(),
|
|
178
179
|
scalerConfigPath: z.string().optional(),
|
|
179
180
|
scalerConfigDir: z.string().optional(),
|
|
180
181
|
machineLedgerDir: z.string().optional(),
|
|
@@ -359,6 +360,7 @@ var init_config$5 = __esmMin((() => {
|
|
|
359
360
|
workerConcurrency: "KICI_WORKER_CONCURRENCY",
|
|
360
361
|
concurrencyWaitTimeoutMs: "KICI_CONCURRENCY_WAIT_TIMEOUT_MS",
|
|
361
362
|
webhookPayloadDir: "KICI_WEBHOOK_PAYLOAD_DIR",
|
|
363
|
+
dataDir: "KICI_DATA_DIR",
|
|
362
364
|
scalerConfigPath: "KICI_SCALER_CONFIG_PATH",
|
|
363
365
|
scalerConfigDir: "KICI_SCALER_CONFIG_DIR",
|
|
364
366
|
machineLedgerDir: "KICI_MACHINE_LEDGER_DIR",
|
|
@@ -1038,7 +1040,7 @@ var init_peer_client = __esmMin((() => {
|
|
|
1038
1040
|
init_peer_crypto();
|
|
1039
1041
|
init_peer_credentials();
|
|
1040
1042
|
logger$85 = createLogger({ prefix: "peer-client" });
|
|
1041
|
-
SOFTWARE_VERSION$1 = "0.1.
|
|
1043
|
+
SOFTWARE_VERSION$1 = "0.1.10";
|
|
1042
1044
|
PeerClient$1 = class {
|
|
1043
1045
|
ws = null;
|
|
1044
1046
|
_state = "disconnected";
|
|
@@ -2501,7 +2503,7 @@ var init_peer_handler = __esmMin((() => {
|
|
|
2501
2503
|
init_peer_crypto();
|
|
2502
2504
|
init_join_token();
|
|
2503
2505
|
logger$84 = createLogger({ prefix: "peer-handler" });
|
|
2504
|
-
SOFTWARE_VERSION = "0.1.
|
|
2506
|
+
SOFTWARE_VERSION = "0.1.10";
|
|
2505
2507
|
RATE_LIMIT_MAX = 5;
|
|
2506
2508
|
RATE_LIMIT_WINDOW_MS = 6e4;
|
|
2507
2509
|
}));
|
|
@@ -3739,6 +3741,59 @@ var init_cluster = __esmMin((() => {
|
|
|
3739
3741
|
init_health_api();
|
|
3740
3742
|
}));
|
|
3741
3743
|
//#endregion
|
|
3744
|
+
//#region src/data-dir.ts
|
|
3745
|
+
/**
|
|
3746
|
+
* Resolve the writable base directory for orchestrator-local data (execution
|
|
3747
|
+
* log storage, cache).
|
|
3748
|
+
*
|
|
3749
|
+
* A system-level orchestrator owns `/var/lib/kici`; a user-level install
|
|
3750
|
+
* (e.g. `kici-admin orchestrator install --user-level`) does not and cannot
|
|
3751
|
+
* write there. Mirrors the scaler-ledger resolution in machine-ledger.ts so
|
|
3752
|
+
* both pieces of orchestrator state degrade the same way: explicit override →
|
|
3753
|
+
* `/var/lib/kici` if writable → XDG state dir → tmpdir.
|
|
3754
|
+
*/
|
|
3755
|
+
/**
|
|
3756
|
+
* Return the first candidate directory that can be created and written to.
|
|
3757
|
+
*
|
|
3758
|
+
* Each candidate is `mkdir -p`'d and probed with a sentinel write (removed
|
|
3759
|
+
* immediately) so "exists but not writable" is caught the same as "cannot be
|
|
3760
|
+
* created". Throws if none are usable.
|
|
3761
|
+
*/
|
|
3762
|
+
function firstWritableDir(candidates) {
|
|
3763
|
+
for (const dir of candidates) try {
|
|
3764
|
+
mkdirSync(dir, { recursive: true });
|
|
3765
|
+
const sentinel = join(dir, `.write-probe-${process.pid}`);
|
|
3766
|
+
writeFileSync(sentinel, "probe");
|
|
3767
|
+
rmSync(sentinel, { force: true });
|
|
3768
|
+
return dir;
|
|
3769
|
+
} catch {
|
|
3770
|
+
continue;
|
|
3771
|
+
}
|
|
3772
|
+
throw new Error(`data-dir: no writable directory among candidates: ${candidates.join(", ")}`);
|
|
3773
|
+
}
|
|
3774
|
+
/**
|
|
3775
|
+
* Resolve the orchestrator data root.
|
|
3776
|
+
*
|
|
3777
|
+
* 1. `explicit` (KICI_DATA_DIR) wins — created if missing.
|
|
3778
|
+
* 2. `/var/lib/kici` if writable (system-level install).
|
|
3779
|
+
* 3. `${XDG_STATE_HOME:-$HOME/.local/state}/kici` (user-level install).
|
|
3780
|
+
* 4. `${tmpdir}/kici-data` (last resort, e.g. CI sandboxes).
|
|
3781
|
+
*
|
|
3782
|
+
* Callers append their own subdir (e.g. `${dataDir}/cache/logs`).
|
|
3783
|
+
*/
|
|
3784
|
+
function resolveDataDir(explicit) {
|
|
3785
|
+
if (explicit) {
|
|
3786
|
+
mkdirSync(explicit, { recursive: true });
|
|
3787
|
+
return explicit;
|
|
3788
|
+
}
|
|
3789
|
+
return firstWritableDir([
|
|
3790
|
+
"/var/lib/kici",
|
|
3791
|
+
join(process.env.XDG_STATE_HOME ?? join(homedir(), ".local", "state"), "kici"),
|
|
3792
|
+
join(tmpdir(), "kici-data")
|
|
3793
|
+
]);
|
|
3794
|
+
}
|
|
3795
|
+
var init_data_dir = __esmMin((() => {}));
|
|
3796
|
+
//#endregion
|
|
3742
3797
|
//#region src/metrics/prometheus.ts
|
|
3743
3798
|
/** Set the current number of active agents. */
|
|
3744
3799
|
function setAgentsActive(value) {
|
|
@@ -10845,13 +10900,25 @@ var init_check_run_reporter = __esmMin((() => {
|
|
|
10845
10900
|
* returns `false` after closing the WS on the first violation, in which
|
|
10846
10901
|
* case the caller MUST stop processing.
|
|
10847
10902
|
*/
|
|
10903
|
+
/**
|
|
10904
|
+
* WebSocket close reasons are capped at 123 UTF-8 bytes (RFC 6455 §5.5.1).
|
|
10905
|
+
* A reason longer than that makes `ws.close()` throw a RangeError, which —
|
|
10906
|
+
* thrown from an async message handler — surfaces as an unhandled rejection
|
|
10907
|
+
* and skips the close entirely. Truncate on a byte boundary and drop any
|
|
10908
|
+
* trailing partial multi-byte char.
|
|
10909
|
+
*/
|
|
10910
|
+
function truncateCloseReason(reason) {
|
|
10911
|
+
const bytes = Buffer.from(reason, "utf-8");
|
|
10912
|
+
if (bytes.length <= 123) return reason;
|
|
10913
|
+
return bytes.subarray(0, 123).toString("utf-8").replace(/�+$/, "");
|
|
10914
|
+
}
|
|
10848
10915
|
function enforceRegisterAuthGates(authState, payload, ws, agentIdToTokenId) {
|
|
10849
10916
|
if (authState === void 0) return true;
|
|
10850
10917
|
const { tokenId, tokenLabels, tokenAgentType, tokenCreatedBy } = authState;
|
|
10851
10918
|
const { agentId, labels } = payload;
|
|
10852
10919
|
if (tokenLabels !== void 0 && tokenLabels !== null) {
|
|
10853
10920
|
const allowedSet = new Set(tokenLabels);
|
|
10854
|
-
const elevated = labels.filter((l) => !allowedSet.has(l));
|
|
10921
|
+
const elevated = labels.filter((l) => !allowedSet.has(l) && !isSelfReportedLabel(l));
|
|
10855
10922
|
if (elevated.length > 0) {
|
|
10856
10923
|
logger$69.warn("Agent register-time label-scope violation: wire labels exceed token-bound set", {
|
|
10857
10924
|
agentId,
|
|
@@ -10859,7 +10926,7 @@ function enforceRegisterAuthGates(authState, payload, ws, agentIdToTokenId) {
|
|
|
10859
10926
|
wireLabels: labels,
|
|
10860
10927
|
elevated
|
|
10861
10928
|
});
|
|
10862
|
-
ws.close(WS_CLOSE_AGENT_AUTH_FAILED, `Agent labels exceed token-bound scope: ${elevated.join(",")}`);
|
|
10929
|
+
ws.close(WS_CLOSE_AGENT_AUTH_FAILED, truncateCloseReason(`Agent labels exceed token-bound scope: ${elevated.join(",")}`));
|
|
10863
10930
|
return false;
|
|
10864
10931
|
}
|
|
10865
10932
|
}
|
|
@@ -10869,7 +10936,7 @@ function enforceRegisterAuthGates(authState, payload, ws, agentIdToTokenId) {
|
|
|
10869
10936
|
tokenCreatedBy,
|
|
10870
10937
|
tokenId
|
|
10871
10938
|
});
|
|
10872
|
-
ws.close(WS_CLOSE_AGENT_AUTH_FAILED, `Ephemeral token bound to a different agentId: expected ${tokenCreatedBy}, got ${agentId}`);
|
|
10939
|
+
ws.close(WS_CLOSE_AGENT_AUTH_FAILED, truncateCloseReason(`Ephemeral token bound to a different agentId: expected ${tokenCreatedBy}, got ${agentId}`));
|
|
10873
10940
|
return false;
|
|
10874
10941
|
}
|
|
10875
10942
|
if (tokenId !== void 0) {
|
|
@@ -21536,15 +21603,15 @@ var init_admin_config = __esmMin((() => {
|
|
|
21536
21603
|
function createHealthRoutes$1(deps = {}) {
|
|
21537
21604
|
return createHealthRoutes({
|
|
21538
21605
|
livenessInfo: () => ({
|
|
21539
|
-
version: "0.1.
|
|
21540
|
-
buildDate: "2026-05-
|
|
21541
|
-
buildCommit: "
|
|
21542
|
-
sdkVersion: "0.1.
|
|
21543
|
-
sdkBundleHash: "
|
|
21544
|
-
sharedVersion: "0.1.
|
|
21606
|
+
version: "0.1.10",
|
|
21607
|
+
buildDate: "2026-05-26T17:19:08.888Z",
|
|
21608
|
+
buildCommit: "6bf528b56",
|
|
21609
|
+
sdkVersion: "0.1.10",
|
|
21610
|
+
sdkBundleHash: "unknown",
|
|
21611
|
+
sharedVersion: "0.1.10",
|
|
21545
21612
|
sharedBundleHash: "36a23a454fd75ec35f258d39f254543ec0451c6b543fe3618ce427e6e6369aae",
|
|
21546
|
-
engineVersion: "0.1.
|
|
21547
|
-
engineBundleHash: "
|
|
21613
|
+
engineVersion: "0.1.10",
|
|
21614
|
+
engineBundleHash: "dff25bbd7602e853cec6e6e985c6c956ad773e245830273cd52950fff958d4bb"
|
|
21548
21615
|
}),
|
|
21549
21616
|
readinessCheck: deps.db ? async () => {
|
|
21550
21617
|
const checks = {};
|
|
@@ -21578,7 +21645,7 @@ function createCapabilitiesRoutes() {
|
|
|
21578
21645
|
const app = new Hono();
|
|
21579
21646
|
app.get("/api/v1/capabilities", (c) => {
|
|
21580
21647
|
const manifest = {
|
|
21581
|
-
orchestratorVersion: "0.1.
|
|
21648
|
+
orchestratorVersion: "0.1.10",
|
|
21582
21649
|
protocolVersion: PROTOCOL_VERSION,
|
|
21583
21650
|
minProtocolVersion: MIN_PROTOCOL_VERSION
|
|
21584
21651
|
};
|
|
@@ -28795,11 +28862,22 @@ var init_container_backend = __esmMin((() => {
|
|
|
28795
28862
|
}
|
|
28796
28863
|
/**
|
|
28797
28864
|
* Declare required tools for a container scaler entry.
|
|
28798
|
-
*
|
|
28799
|
-
*
|
|
28865
|
+
*
|
|
28866
|
+
* For the auto-detect case (no explicit socketPath / remote host) the
|
|
28867
|
+
* orchestrator must have a local container runtime — docker OR podman — on
|
|
28868
|
+
* PATH, otherwise the scaler cannot spawn agent containers. Declaring it
|
|
28869
|
+
* here lets the startup tool-validation gate fail fast with a clear error
|
|
28870
|
+
* instead of the first job hanging. When a socketPath or remote host is
|
|
28871
|
+
* configured the binary need not be on PATH (the runtime may be remote), so
|
|
28872
|
+
* reachability is validated later in create().
|
|
28800
28873
|
*/
|
|
28801
|
-
static getRequiredTools() {
|
|
28802
|
-
return [];
|
|
28874
|
+
static getRequiredTools(entry) {
|
|
28875
|
+
if (entry.host || entry.socketPath) return [];
|
|
28876
|
+
return [{
|
|
28877
|
+
type: "any-path-binary",
|
|
28878
|
+
names: ["docker", "podman"],
|
|
28879
|
+
reason: `container scaler "${entry.name}" needs a local container runtime to spawn agents. Install Docker or Podman, or set socketPath / host in scalers.yaml for a remote runtime.`
|
|
28880
|
+
}];
|
|
28803
28881
|
}
|
|
28804
28882
|
/**
|
|
28805
28883
|
* Create a ContainerScalerBackend with auto-detected or configured socket.
|
|
@@ -28860,17 +28938,13 @@ var init_container_backend = __esmMin((() => {
|
|
|
28860
28938
|
const stripped = key.slice(KICI_AGENT_ENV_PREFIX.length);
|
|
28861
28939
|
if (stripped.length > 0) agentEnvForwarded.push(`${stripped}=${value}`);
|
|
28862
28940
|
}
|
|
28941
|
+
const fullLabels = scalerAgentLabels(labelSet, this.type, this.name, this.roles);
|
|
28863
28942
|
let agentToken;
|
|
28864
|
-
if (this.tokenStore) agentToken = await this.tokenStore.createEphemeral(agentId,
|
|
28943
|
+
if (this.tokenStore) agentToken = await this.tokenStore.createEphemeral(agentId, fullLabels, this.tokenTtlMs);
|
|
28865
28944
|
const env = [
|
|
28866
28945
|
`KICI_ORCHESTRATOR_URL=${orchestratorUrl}`,
|
|
28867
28946
|
`KICI_AGENT_ID=${agentId}`,
|
|
28868
|
-
`KICI_LABELS=${
|
|
28869
|
-
...labelSet,
|
|
28870
|
-
`kici:agent:${this.type}`,
|
|
28871
|
-
`kici:scaler:${this.name}`,
|
|
28872
|
-
...resolveRoleLabels(this.roles)
|
|
28873
|
-
].join(",")}`,
|
|
28947
|
+
`KICI_LABELS=${fullLabels.join(",")}`,
|
|
28874
28948
|
`KICI_SCALER_MANAGED=1`,
|
|
28875
28949
|
`KICI_EXECUTION_MODE=bare-metal`,
|
|
28876
28950
|
...agentToken ? [`KICI_AGENT_TOKEN=${agentToken}`] : [],
|
|
@@ -29123,6 +29197,11 @@ var init_bare_metal_backend = __esmMin((() => {
|
|
|
29123
29197
|
mode: "executable",
|
|
29124
29198
|
reason: `agent binary for bare-metal scaler "${entry.name}"`
|
|
29125
29199
|
}));
|
|
29200
|
+
requirements.push({
|
|
29201
|
+
type: "path-binary",
|
|
29202
|
+
name: "node",
|
|
29203
|
+
reason: `bare-metal scaler "${entry.name}" spawns the kici-agent node script. node must be on the orchestrator's PATH (it is forwarded to spawned agents). Ensure the orchestrator service's PATH includes your node install (e.g. the systemd unit's Environment=PATH covers the mise/nvm node bin dir).`
|
|
29204
|
+
});
|
|
29126
29205
|
const sandboxViaGlobalEnv = process.env.KICI_AGENT_ENV_KICI_SANDBOX === "true";
|
|
29127
29206
|
const sandboxViaLabelSet = entry.labelSets.some((ls) => ls.env && ls.env.KICI_SANDBOX === "true");
|
|
29128
29207
|
if (sandboxViaGlobalEnv || sandboxViaLabelSet) requirements.push({
|
|
@@ -29161,15 +29240,11 @@ var init_bare_metal_backend = __esmMin((() => {
|
|
|
29161
29240
|
const strippedKey = key.slice(KICI_AGENT_ENV_PREFIX.length);
|
|
29162
29241
|
if (strippedKey.length > 0) env[strippedKey] = value;
|
|
29163
29242
|
}
|
|
29164
|
-
|
|
29243
|
+
const fullLabels = scalerAgentLabels(labelSet, this.type, this.name, this.roles);
|
|
29244
|
+
if (this.tokenStore) env.KICI_AGENT_TOKEN = await this.tokenStore.createEphemeral(agentId, fullLabels, this.tokenTtlMs);
|
|
29165
29245
|
env.KICI_ORCHESTRATOR_URL = orchestratorUrl;
|
|
29166
29246
|
env.KICI_AGENT_ID = agentId;
|
|
29167
|
-
env.KICI_LABELS =
|
|
29168
|
-
...labelSet,
|
|
29169
|
-
`kici:agent:${this.type}`,
|
|
29170
|
-
`kici:scaler:${this.name}`,
|
|
29171
|
-
...resolveRoleLabels(this.roles)
|
|
29172
|
-
].join(",");
|
|
29247
|
+
env.KICI_LABELS = fullLabels.join(",");
|
|
29173
29248
|
env.KICI_SCALER_MANAGED = "1";
|
|
29174
29249
|
env.KICI_EXECUTION_MODE = "bare-metal";
|
|
29175
29250
|
env.KICI_PORT = "0";
|
|
@@ -29919,18 +29994,14 @@ var init_firecracker_backend = __esmMin((() => {
|
|
|
29919
29994
|
if (chmodLoosenInterval) clearInterval(chmodLoosenInterval);
|
|
29920
29995
|
}
|
|
29921
29996
|
if (!ready) throw new Error(`Firecracker API socket not ready within 5s for agent ${agentId}`);
|
|
29997
|
+
const fullLabels = scalerAgentLabels(labelSet, this.type, this.name, this.roles);
|
|
29922
29998
|
let agentToken;
|
|
29923
|
-
if (this.tokenStore) agentToken = await this.tokenStore.createEphemeral(agentId,
|
|
29999
|
+
if (this.tokenStore) agentToken = await this.tokenStore.createEphemeral(agentId, fullLabels, this.tokenTtlMs);
|
|
29924
30000
|
const acceptedEnv = this.buildForwardedEnv(matchedLabelSet, agentId);
|
|
29925
30001
|
await api.putMmds({ latest: { "meta-data": {
|
|
29926
30002
|
"kici-orchestrator-url": orchestratorUrl,
|
|
29927
30003
|
"kici-agent-id": agentId,
|
|
29928
|
-
"kici-labels":
|
|
29929
|
-
...labelSet,
|
|
29930
|
-
`kici:agent:${this.type}`,
|
|
29931
|
-
`kici:scaler:${this.name}`,
|
|
29932
|
-
...resolveRoleLabels(this.roles)
|
|
29933
|
-
].join(","),
|
|
30004
|
+
"kici-labels": fullLabels.join(","),
|
|
29934
30005
|
"kici-scaler-managed": "1",
|
|
29935
30006
|
"kici-gateway-ip": this.gateway,
|
|
29936
30007
|
...agentToken ? { "kici-agent-token": agentToken } : {},
|
|
@@ -31007,6 +31078,29 @@ var init_machine_ledger = __esmMin((() => {
|
|
|
31007
31078
|
* and manages the agent lifecycle from spawn to destroy.
|
|
31008
31079
|
*/
|
|
31009
31080
|
/**
|
|
31081
|
+
* Resolved per-job resource amounts (cpus + bytes) for both `requests` and
|
|
31082
|
+
* `limits`. The scaler manager produces this from the job's declared resources
|
|
31083
|
+
* combined with the scaler's `defaults.resources`, applying the request<->limit
|
|
31084
|
+
* mirroring rule. Caps aggregate `requests`; backends use `limits`.
|
|
31085
|
+
*/
|
|
31086
|
+
/**
|
|
31087
|
+
* Resolve the orchestrator WebSocket URL a scaler-spawned agent should dial.
|
|
31088
|
+
*
|
|
31089
|
+
* 1. Per-scaler `orchestratorUrl` (scalers.yaml) wins — required for container
|
|
31090
|
+
* agents (host.docker.internal / LAN IP) and Firecracker VMs (bridge gateway
|
|
31091
|
+
* IP), which cannot reach the orchestrator over the host's loopback.
|
|
31092
|
+
* 2. `KICI_ORCHESTRATOR_URL` env override.
|
|
31093
|
+
* 3. Default `ws://127.0.0.1:<orchestrator-port>/ws` — for local (bare-metal)
|
|
31094
|
+
* agents that share the host. The port is the orchestrator's own bind port
|
|
31095
|
+
* (`KICI_PORT`, default 4000), NOT the agent's 8080 default; pointing local
|
|
31096
|
+
* agents at 8080 leaves them unable to reach the orchestrator.
|
|
31097
|
+
*/
|
|
31098
|
+
function resolveScalerOrchestratorUrl(configUrl, envUrl, port) {
|
|
31099
|
+
if (configUrl) return configUrl;
|
|
31100
|
+
if (envUrl) return envUrl;
|
|
31101
|
+
return `ws://127.0.0.1:${port ?? "4000"}/ws`;
|
|
31102
|
+
}
|
|
31103
|
+
/**
|
|
31010
31104
|
* Apply the request<->limit mirroring rule: if only one side is set, copy it
|
|
31011
31105
|
* to the other; if neither is set, return undefined; if both are set, leave them.
|
|
31012
31106
|
*
|
|
@@ -31918,9 +32012,7 @@ var init_manager = __esmMin((() => {
|
|
|
31918
32012
|
});
|
|
31919
32013
|
}
|
|
31920
32014
|
getOrchestratorUrl(backendName) {
|
|
31921
|
-
|
|
31922
|
-
if (configUrl) return configUrl;
|
|
31923
|
-
return process.env.KICI_ORCHESTRATOR_URL ?? "ws://localhost:8080/ws";
|
|
32015
|
+
return resolveScalerOrchestratorUrl(this.scalerUrls.get(backendName), process.env.KICI_ORCHESTRATOR_URL, process.env.KICI_PORT);
|
|
31924
32016
|
}
|
|
31925
32017
|
/**
|
|
31926
32018
|
* Create a per-agent event emitter closure to pass to backend.spawn().
|
|
@@ -40755,7 +40847,7 @@ async function initializeScaler(config, db, tokenStore) {
|
|
|
40755
40847
|
}
|
|
40756
40848
|
const toolErrors = validateRequiredTools(scalerConfig.scalers.flatMap((s) => {
|
|
40757
40849
|
switch (s.type) {
|
|
40758
|
-
case "container": return ContainerScalerBackend.getRequiredTools();
|
|
40850
|
+
case "container": return ContainerScalerBackend.getRequiredTools(s);
|
|
40759
40851
|
case "bare-metal": return BareMetalScalerBackend.getRequiredTools(s);
|
|
40760
40852
|
case "firecracker": return FirecrackerScalerBackend.getRequiredTools(s);
|
|
40761
40853
|
default: return [];
|
|
@@ -41747,7 +41839,7 @@ async function bootstrapOrchestrator$1(config, hooks, options) {
|
|
|
41747
41839
|
forcePathStyle: config.storage.forcePathStyle
|
|
41748
41840
|
} : {
|
|
41749
41841
|
type: "filesystem",
|
|
41750
|
-
basePath: (config.webhookPayloadDir ?? "/
|
|
41842
|
+
basePath: (config.webhookPayloadDir ?? resolveDataDir(config.dataDir) + "/cache") + "/logs"
|
|
41751
41843
|
});
|
|
41752
41844
|
const observerRegistry = new ObserverRegistry();
|
|
41753
41845
|
let executionTrackerRef = null;
|
|
@@ -42547,6 +42639,7 @@ async function bootstrapOrchestrator$1(config, hooks, options) {
|
|
|
42547
42639
|
}
|
|
42548
42640
|
var logger$2;
|
|
42549
42641
|
var init_orchestrator_core = __esmMin((() => {
|
|
42642
|
+
init_data_dir();
|
|
42550
42643
|
init_reload();
|
|
42551
42644
|
init_resolver();
|
|
42552
42645
|
init_client();
|
|
@@ -43705,14 +43798,14 @@ var init_worker_core = __esmMin((() => {
|
|
|
43705
43798
|
init_agent_handler();
|
|
43706
43799
|
init_worker_status();
|
|
43707
43800
|
init_agent_heartbeat();
|
|
43708
|
-
ORCHESTRATOR_VERSION$1 = "0.1.
|
|
43709
|
-
WORKER_BUILD_COMMIT = "
|
|
43710
|
-
WORKER_SDK_VERSION = "0.1.
|
|
43711
|
-
WORKER_SDK_BUNDLE_HASH = "
|
|
43712
|
-
WORKER_SHARED_VERSION = "0.1.
|
|
43801
|
+
ORCHESTRATOR_VERSION$1 = "0.1.10";
|
|
43802
|
+
WORKER_BUILD_COMMIT = "6bf528b56";
|
|
43803
|
+
WORKER_SDK_VERSION = "0.1.10";
|
|
43804
|
+
WORKER_SDK_BUNDLE_HASH = "unknown";
|
|
43805
|
+
WORKER_SHARED_VERSION = "0.1.10";
|
|
43713
43806
|
WORKER_SHARED_BUNDLE_HASH = "36a23a454fd75ec35f258d39f254543ec0451c6b543fe3618ce427e6e6369aae";
|
|
43714
|
-
WORKER_ENGINE_VERSION = "0.1.
|
|
43715
|
-
WORKER_ENGINE_BUNDLE_HASH = "
|
|
43807
|
+
WORKER_ENGINE_VERSION = "0.1.10";
|
|
43808
|
+
WORKER_ENGINE_BUNDLE_HASH = "dff25bbd7602e853cec6e6e985c6c956ad773e245830273cd52950fff958d4bb";
|
|
43716
43809
|
logger$1 = createLogger({ prefix: "worker" });
|
|
43717
43810
|
DRAIN_TIMEOUT_MS = 3e5;
|
|
43718
43811
|
}));
|
|
@@ -43736,14 +43829,14 @@ var init_worker_core = __esmMin((() => {
|
|
|
43736
43829
|
* Graceful shutdown:
|
|
43737
43830
|
* agent WS -> heartbeat -> HTTP -> DB
|
|
43738
43831
|
*/
|
|
43739
|
-
const ORCHESTRATOR_VERSION = "0.1.
|
|
43740
|
-
const BUILD_COMMIT = "
|
|
43741
|
-
const SDK_VERSION = "0.1.
|
|
43742
|
-
const SDK_BUNDLE_HASH = "
|
|
43743
|
-
const SHARED_VERSION = "0.1.
|
|
43832
|
+
const ORCHESTRATOR_VERSION = "0.1.10";
|
|
43833
|
+
const BUILD_COMMIT = "6bf528b56";
|
|
43834
|
+
const SDK_VERSION = "0.1.10";
|
|
43835
|
+
const SDK_BUNDLE_HASH = "unknown";
|
|
43836
|
+
const SHARED_VERSION = "0.1.10";
|
|
43744
43837
|
const SHARED_BUNDLE_HASH = "36a23a454fd75ec35f258d39f254543ec0451c6b543fe3618ce427e6e6369aae";
|
|
43745
|
-
const ENGINE_VERSION = "0.1.
|
|
43746
|
-
const ENGINE_BUNDLE_HASH = "
|
|
43838
|
+
const ENGINE_VERSION = "0.1.10";
|
|
43839
|
+
const ENGINE_BUNDLE_HASH = "dff25bbd7602e853cec6e6e985c6c956ad773e245830273cd52950fff958d4bb";
|
|
43747
43840
|
const otelSdk = initTelemetry({
|
|
43748
43841
|
serviceName: "kici-orchestrator",
|
|
43749
43842
|
otlpEndpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT
|
|
@@ -29,6 +29,39 @@ import type { PendingDynamicTracker } from '../cache/pending-dynamics.js';
|
|
|
29
29
|
import type { CacheStorage } from '../storage/types.js';
|
|
30
30
|
import type { AgentMetricsAggregator } from '../metrics/agent-metrics-aggregator.js';
|
|
31
31
|
import type { AgentApiRegistry } from './agent-api-registry.js';
|
|
32
|
+
/**
|
|
33
|
+
* Run the three token-bound authorization gates against a wire-supplied
|
|
34
|
+
* `agent.register` payload. The gates are identical at first register
|
|
35
|
+
* (Phase 2 / `pendingRegistration`) and on every subsequent re-register
|
|
36
|
+
* arriving on the same registered WS — every code path that mutates the
|
|
37
|
+
* registry's `agentId → entry` mapping for an authenticated WS MUST run
|
|
38
|
+
* them, otherwise the §5.3 / §5.1 invariants only hold for the very first
|
|
39
|
+
* register and a re-register can silently overwrite the authority.
|
|
40
|
+
*
|
|
41
|
+
* Gates:
|
|
42
|
+
* 1. Token-scope subset — wire labels MUST be a subset of
|
|
43
|
+
* `agent_tokens.labels` when that set is non-null. Closes the WS with
|
|
44
|
+
* `WS_CLOSE_AGENT_AUTH_FAILED` listing the elevated labels.
|
|
45
|
+
* 2. Ephemeral identity-binding — for `agent_type === 'ephemeral'`,
|
|
46
|
+
* wire `agentId` MUST equal `tokenCreatedBy` (the scaler-spawned
|
|
47
|
+
* agentId the token was issued for). Closes the WS with
|
|
48
|
+
* `WS_CLOSE_AGENT_AUTH_FAILED`.
|
|
49
|
+
* 3. Static-token agentId-collision — a different `tokenId` must not
|
|
50
|
+
* already claim the wire `agentId`. Closes the WS with
|
|
51
|
+
* `WS_CLOSE_INVALID_MESSAGE`.
|
|
52
|
+
*
|
|
53
|
+
* Returns `true` when every gate passed and the caller may proceed;
|
|
54
|
+
* returns `false` after closing the WS on the first violation, in which
|
|
55
|
+
* case the caller MUST stop processing.
|
|
56
|
+
*/
|
|
57
|
+
/**
|
|
58
|
+
* WebSocket close reasons are capped at 123 UTF-8 bytes (RFC 6455 §5.5.1).
|
|
59
|
+
* A reason longer than that makes `ws.close()` throw a RangeError, which —
|
|
60
|
+
* thrown from an async message handler — surfaces as an unhandled rejection
|
|
61
|
+
* and skips the close entirely. Truncate on a byte boundary and drop any
|
|
62
|
+
* trailing partial multi-byte char.
|
|
63
|
+
*/
|
|
64
|
+
export declare function truncateCloseReason(reason: string): string;
|
|
32
65
|
export interface AgentWsHandlerDeps {
|
|
33
66
|
registry: AgentRegistry;
|
|
34
67
|
dispatcher: Dispatcher;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kici-dev/orchestrator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Customer-deployable orchestrator for the KiCI CI/CD stack. Receives webhook events (direct or via Platform relay), matches triggers against the lock file, and dispatches jobs to connected agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kici",
|
|
@@ -82,8 +82,8 @@
|
|
|
82
82
|
"ws": "^8.20.0",
|
|
83
83
|
"yaml": "^2.8.3",
|
|
84
84
|
"zod": "^4.3.6",
|
|
85
|
-
"@kici-dev/
|
|
86
|
-
"@kici-dev/
|
|
85
|
+
"@kici-dev/shared": "0.1.10",
|
|
86
|
+
"@kici-dev/engine": "0.1.10"
|
|
87
87
|
},
|
|
88
88
|
"kici": {
|
|
89
89
|
"metrics": {
|
package/sbom.spdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"spdxVersion": "SPDX-2.3",
|
|
3
3
|
"dataLicense": "CC0-1.0",
|
|
4
4
|
"SPDXID": "SPDXRef-DOCUMENT",
|
|
5
|
-
"name": "@kici-dev/orchestrator@0.1.
|
|
6
|
-
"documentNamespace": "https://kici.dev/sbom/%40kici-dev%2Forchestrator/0.1.
|
|
5
|
+
"name": "@kici-dev/orchestrator@0.1.10",
|
|
6
|
+
"documentNamespace": "https://kici.dev/sbom/%40kici-dev%2Forchestrator/0.1.10/1e7943f5-2300-4aee-b905-0121c011771c",
|
|
7
7
|
"creationInfo": {
|
|
8
|
-
"created": "2026-05-
|
|
8
|
+
"created": "2026-05-26T17:26:36Z",
|
|
9
9
|
"creators": [
|
|
10
10
|
"Tool: kici-sbom-generator"
|
|
11
11
|
]
|
|
@@ -1477,9 +1477,9 @@
|
|
|
1477
1477
|
"homepage": "https://ericsmekens.github.io/jsep/tree/master/packages/regex#readme"
|
|
1478
1478
|
},
|
|
1479
1479
|
{
|
|
1480
|
-
"SPDXID": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
1480
|
+
"SPDXID": "SPDXRef-Package--kici-dev-engine-0.1.10",
|
|
1481
1481
|
"name": "@kici-dev/engine",
|
|
1482
|
-
"versionInfo": "0.1.
|
|
1482
|
+
"versionInfo": "0.1.10",
|
|
1483
1483
|
"downloadLocation": "NOASSERTION",
|
|
1484
1484
|
"filesAnalyzed": false,
|
|
1485
1485
|
"licenseConcluded": "NOASSERTION",
|
|
@@ -1490,7 +1490,7 @@
|
|
|
1490
1490
|
{
|
|
1491
1491
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
1492
1492
|
"referenceType": "purl",
|
|
1493
|
-
"referenceLocator": "pkg:npm/%40kici-dev/engine@0.1.
|
|
1493
|
+
"referenceLocator": "pkg:npm/%40kici-dev/engine@0.1.10"
|
|
1494
1494
|
}
|
|
1495
1495
|
],
|
|
1496
1496
|
"description": "Shared business logic for the KiCI CI/CD stack: protocol, triggers, state machine, and provider interfaces used by the Platform relay, orchestrator, and compiler.",
|
|
@@ -1499,7 +1499,7 @@
|
|
|
1499
1499
|
{
|
|
1500
1500
|
"SPDXID": "SPDXRef-RootPackage",
|
|
1501
1501
|
"name": "@kici-dev/orchestrator",
|
|
1502
|
-
"versionInfo": "0.1.
|
|
1502
|
+
"versionInfo": "0.1.10",
|
|
1503
1503
|
"downloadLocation": "NOASSERTION",
|
|
1504
1504
|
"filesAnalyzed": false,
|
|
1505
1505
|
"licenseConcluded": "NOASSERTION",
|
|
@@ -1510,16 +1510,16 @@
|
|
|
1510
1510
|
{
|
|
1511
1511
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
1512
1512
|
"referenceType": "purl",
|
|
1513
|
-
"referenceLocator": "pkg:npm/%40kici-dev/orchestrator@0.1.
|
|
1513
|
+
"referenceLocator": "pkg:npm/%40kici-dev/orchestrator@0.1.10"
|
|
1514
1514
|
}
|
|
1515
1515
|
],
|
|
1516
1516
|
"description": "Customer-deployable orchestrator for the KiCI CI/CD stack. Receives webhook events (direct or via Platform relay), matches triggers against the lock file, and dispatches jobs to connected agents.",
|
|
1517
1517
|
"homepage": "https://kici.dev"
|
|
1518
1518
|
},
|
|
1519
1519
|
{
|
|
1520
|
-
"SPDXID": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
1520
|
+
"SPDXID": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
1521
1521
|
"name": "@kici-dev/shared",
|
|
1522
|
-
"versionInfo": "0.1.
|
|
1522
|
+
"versionInfo": "0.1.10",
|
|
1523
1523
|
"downloadLocation": "NOASSERTION",
|
|
1524
1524
|
"filesAnalyzed": false,
|
|
1525
1525
|
"licenseConcluded": "NOASSERTION",
|
|
@@ -1530,7 +1530,7 @@
|
|
|
1530
1530
|
{
|
|
1531
1531
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
1532
1532
|
"referenceType": "purl",
|
|
1533
|
-
"referenceLocator": "pkg:npm/%40kici-dev/shared@0.1.
|
|
1533
|
+
"referenceLocator": "pkg:npm/%40kici-dev/shared@0.1.10"
|
|
1534
1534
|
}
|
|
1535
1535
|
],
|
|
1536
1536
|
"description": "Shared utilities for the KiCI CI/CD stack — logging, zx setup, crypto, telemetry, health and metrics routes. No business logic.",
|
|
@@ -10682,17 +10682,17 @@
|
|
|
10682
10682
|
"relationshipType": "DEPENDS_ON"
|
|
10683
10683
|
},
|
|
10684
10684
|
{
|
|
10685
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
10685
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.10",
|
|
10686
10686
|
"relatedSpdxElement": "SPDXRef-Package-jsonpath-plus-10.4.0",
|
|
10687
10687
|
"relationshipType": "DEPENDS_ON"
|
|
10688
10688
|
},
|
|
10689
10689
|
{
|
|
10690
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
10690
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.10",
|
|
10691
10691
|
"relatedSpdxElement": "SPDXRef-Package-picomatch-4.0.4",
|
|
10692
10692
|
"relationshipType": "DEPENDS_ON"
|
|
10693
10693
|
},
|
|
10694
10694
|
{
|
|
10695
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
10695
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.10",
|
|
10696
10696
|
"relatedSpdxElement": "SPDXRef-Package-zod-4.3.6",
|
|
10697
10697
|
"relationshipType": "DEPENDS_ON"
|
|
10698
10698
|
},
|
|
@@ -10728,12 +10728,12 @@
|
|
|
10728
10728
|
},
|
|
10729
10729
|
{
|
|
10730
10730
|
"spdxElementId": "SPDXRef-RootPackage",
|
|
10731
|
-
"relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
10731
|
+
"relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.10",
|
|
10732
10732
|
"relationshipType": "DEPENDS_ON"
|
|
10733
10733
|
},
|
|
10734
10734
|
{
|
|
10735
10735
|
"spdxElementId": "SPDXRef-RootPackage",
|
|
10736
|
-
"relatedSpdxElement": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10736
|
+
"relatedSpdxElement": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10737
10737
|
"relationshipType": "DEPENDS_ON"
|
|
10738
10738
|
},
|
|
10739
10739
|
{
|
|
@@ -10837,102 +10837,102 @@
|
|
|
10837
10837
|
"relationshipType": "DEPENDS_ON"
|
|
10838
10838
|
},
|
|
10839
10839
|
{
|
|
10840
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10840
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10841
10841
|
"relatedSpdxElement": "SPDXRef-Package--aws-sdk-client-s3-3.1038.0",
|
|
10842
10842
|
"relationshipType": "DEPENDS_ON"
|
|
10843
10843
|
},
|
|
10844
10844
|
{
|
|
10845
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10845
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10846
10846
|
"relatedSpdxElement": "SPDXRef-Package--opentelemetry-api-1.9.1",
|
|
10847
10847
|
"relationshipType": "DEPENDS_ON"
|
|
10848
10848
|
},
|
|
10849
10849
|
{
|
|
10850
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10850
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10851
10851
|
"relatedSpdxElement": "SPDXRef-Package--opentelemetry-exporter-metrics-otlp-http-0.217.0",
|
|
10852
10852
|
"relationshipType": "DEPENDS_ON"
|
|
10853
10853
|
},
|
|
10854
10854
|
{
|
|
10855
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10855
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10856
10856
|
"relatedSpdxElement": "SPDXRef-Package--opentelemetry-exporter-prometheus-0.217.0",
|
|
10857
10857
|
"relationshipType": "DEPENDS_ON"
|
|
10858
10858
|
},
|
|
10859
10859
|
{
|
|
10860
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10860
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10861
10861
|
"relatedSpdxElement": "SPDXRef-Package--opentelemetry-exporter-trace-otlp-http-0.217.0",
|
|
10862
10862
|
"relationshipType": "DEPENDS_ON"
|
|
10863
10863
|
},
|
|
10864
10864
|
{
|
|
10865
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10865
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10866
10866
|
"relatedSpdxElement": "SPDXRef-Package--opentelemetry-instrumentation-runtime-node-0.28.0",
|
|
10867
10867
|
"relationshipType": "DEPENDS_ON"
|
|
10868
10868
|
},
|
|
10869
10869
|
{
|
|
10870
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10870
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10871
10871
|
"relatedSpdxElement": "SPDXRef-Package--opentelemetry-resources-2.7.0",
|
|
10872
10872
|
"relationshipType": "DEPENDS_ON"
|
|
10873
10873
|
},
|
|
10874
10874
|
{
|
|
10875
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10875
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10876
10876
|
"relatedSpdxElement": "SPDXRef-Package--opentelemetry-sdk-node-0.217.0",
|
|
10877
10877
|
"relationshipType": "DEPENDS_ON"
|
|
10878
10878
|
},
|
|
10879
10879
|
{
|
|
10880
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10880
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10881
10881
|
"relatedSpdxElement": "SPDXRef-Package--opentelemetry-semantic-conventions-1.40.0",
|
|
10882
10882
|
"relationshipType": "DEPENDS_ON"
|
|
10883
10883
|
},
|
|
10884
10884
|
{
|
|
10885
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10885
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10886
10886
|
"relatedSpdxElement": "SPDXRef-Package-diff-7.0.0",
|
|
10887
10887
|
"relationshipType": "DEPENDS_ON"
|
|
10888
10888
|
},
|
|
10889
10889
|
{
|
|
10890
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10890
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10891
10891
|
"relatedSpdxElement": "SPDXRef-Package-hono-4.12.18",
|
|
10892
10892
|
"relationshipType": "DEPENDS_ON"
|
|
10893
10893
|
},
|
|
10894
10894
|
{
|
|
10895
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10895
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10896
10896
|
"relatedSpdxElement": "SPDXRef-Package-kysely-0.29.0",
|
|
10897
10897
|
"relationshipType": "DEPENDS_ON"
|
|
10898
10898
|
},
|
|
10899
10899
|
{
|
|
10900
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10900
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10901
10901
|
"relatedSpdxElement": "SPDXRef-Package-oxc-transform-0.128.0",
|
|
10902
10902
|
"relationshipType": "DEPENDS_ON"
|
|
10903
10903
|
},
|
|
10904
10904
|
{
|
|
10905
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10905
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10906
10906
|
"relatedSpdxElement": "SPDXRef-Package-pg-8.20.0",
|
|
10907
10907
|
"relationshipType": "DEPENDS_ON"
|
|
10908
10908
|
},
|
|
10909
10909
|
{
|
|
10910
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10910
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10911
10911
|
"relatedSpdxElement": "SPDXRef-Package-picocolors-1.1.1",
|
|
10912
10912
|
"relationshipType": "DEPENDS_ON"
|
|
10913
10913
|
},
|
|
10914
10914
|
{
|
|
10915
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10915
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10916
10916
|
"relatedSpdxElement": "SPDXRef-Package-winston-daily-rotate-file-5.0.0",
|
|
10917
10917
|
"relationshipType": "DEPENDS_ON"
|
|
10918
10918
|
},
|
|
10919
10919
|
{
|
|
10920
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10920
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10921
10921
|
"relatedSpdxElement": "SPDXRef-Package-winston-3.19.0",
|
|
10922
10922
|
"relationshipType": "DEPENDS_ON"
|
|
10923
10923
|
},
|
|
10924
10924
|
{
|
|
10925
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10925
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10926
10926
|
"relatedSpdxElement": "SPDXRef-Package-yaml-2.8.3",
|
|
10927
10927
|
"relationshipType": "DEPENDS_ON"
|
|
10928
10928
|
},
|
|
10929
10929
|
{
|
|
10930
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10930
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10931
10931
|
"relatedSpdxElement": "SPDXRef-Package-zod-4.3.6",
|
|
10932
10932
|
"relationshipType": "DEPENDS_ON"
|
|
10933
10933
|
},
|
|
10934
10934
|
{
|
|
10935
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.
|
|
10935
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.10",
|
|
10936
10936
|
"relatedSpdxElement": "SPDXRef-Package-zx-8.8.5",
|
|
10937
10937
|
"relationshipType": "DEPENDS_ON"
|
|
10938
10938
|
},
|