@kici-dev/agent 0.1.26 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap/ensure-init-runner.d.ts +23 -22
- package/dist/bootstrap/payload-source.d.ts +32 -0
- package/dist/bootstrap/probe-platform.d.ts +21 -0
- package/dist/bootstrap/restage-agent.d.ts +43 -0
- package/dist/bootstrap/run-restage.d.ts +12 -0
- package/dist/bootstrap/s3-payload-source.d.ts +35 -0
- package/dist/bootstrap/ssh-exec.d.ts +14 -0
- package/dist/bootstrap/stage-agent-payload.d.ts +41 -0
- package/dist/checkout/changed-files.d.ts +34 -0
- package/dist/config.d.ts +42 -14
- package/dist/container-ts-loader-hook.js +147710 -0
- package/dist/execution/artifacts/artifact-engine.d.ts +51 -0
- package/dist/execution/dep-installer.d.ts +3 -3
- package/dist/execution/init-runner.d.ts +4 -4
- package/dist/execution/job-runner.d.ts +41 -6
- package/dist/execution/log-streamer.d.ts +17 -2
- package/dist/execution/rule-evaluator.d.ts +1 -12
- package/dist/execution/sandbox/container-hardening.d.ts +80 -0
- package/dist/execution/sandbox/container-sandbox.d.ts +54 -0
- package/dist/execution/sandbox/container-ts-loader-hook.d.ts +26 -0
- package/dist/execution/sandbox/env-sanitizer.d.ts +12 -3
- package/dist/execution/sandbox/fork-runner.d.ts +14 -0
- package/dist/execution/sandbox/index.d.ts +2 -1
- package/dist/execution/sandbox/ipc-protocol.d.ts +82 -6
- package/dist/execution/sandbox/step-loop.d.ts +4 -0
- package/dist/execution/sandbox/types.d.ts +25 -4
- package/dist/execution/sandbox/workflow-runner.d.ts +111 -5
- package/dist/execution/streaming-zx-log.d.ts +38 -0
- package/dist/execution/tmp-gc.d.ts +22 -7
- package/dist/execution/workflow-loader.d.ts +32 -1
- package/dist/index.js +50 -45
- package/dist/provenance/statement-builder.d.ts +3 -2
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1360 -193
- package/dist/workflow-runner-bundle.js +215393 -0
- package/dist/workflow-runner.js +937 -256
- package/dist/ws/orchestrator-client.d.ts +105 -1
- package/package.json +14 -12
- package/sbom.spdx.json +1092 -1723
|
@@ -208,6 +208,10 @@ interface StepLoopResult {
|
|
|
208
208
|
stepResults: SandboxStepResult[];
|
|
209
209
|
failureReason?: string;
|
|
210
210
|
}
|
|
211
|
+
/** Most lines of a failing step's error message that reach the run log. */
|
|
212
|
+
export declare const STEP_FAILURE_LOG_MAX_LINES = 100;
|
|
213
|
+
/** Most characters of a failing step's error message that reach the run log. */
|
|
214
|
+
export declare const STEP_FAILURE_LOG_MAX_CHARS = 8192;
|
|
211
215
|
/**
|
|
212
216
|
* Per-step iteration outcome returned by `runStepIteration`.
|
|
213
217
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { JobDispatch } from '@kici-dev/engine';
|
|
2
|
-
import type { EventEmitRequest, EventEmitResponse, ConcurrencyReportMessage, ConcurrencyAckMessage, CacheRequestIpc, CacheResponseIpc, ProvenanceRequestIpc, ProvenanceResponseIpc, StepApprovalRequestIpc, StepApprovalResolvedIpc } from './ipc-protocol.js';
|
|
1
|
+
import type { JobDispatch, LogStream } from '@kici-dev/engine';
|
|
2
|
+
import type { EventEmitRequest, EventEmitResponse, ConcurrencyReportMessage, ConcurrencyAckMessage, CacheRequestIpc, CacheResponseIpc, ProvenanceRequestIpc, ProvenanceResponseIpc, ArtifactRequestIpc, ArtifactResponseIpc, StepApprovalRequestIpc, StepApprovalResolvedIpc } from './ipc-protocol.js';
|
|
3
3
|
/**
|
|
4
4
|
* Common interface for all execution sandbox backends.
|
|
5
5
|
*
|
|
@@ -50,6 +50,15 @@ export interface SandboxSetupOptions {
|
|
|
50
50
|
workDir: string;
|
|
51
51
|
/** Sanitized environment variables (user env + secrets, NO agent credentials). */
|
|
52
52
|
env: Record<string, string>;
|
|
53
|
+
/**
|
|
54
|
+
* Extra read-only host paths to expose in the sandbox beyond the workspace +
|
|
55
|
+
* runner — the `file://` clone-source dir(s) so the in-sandbox `git clone`
|
|
56
|
+
* can read a local source. Derived from the dispatch `repoUrl` by the
|
|
57
|
+
* job-runner (empty for https/ssh remotes). The container backend binds each
|
|
58
|
+
* as `<dir>:<dir>:ro`; the bare-metal backend derives its own equivalent
|
|
59
|
+
* inside `executeJob`, so it ignores this field.
|
|
60
|
+
*/
|
|
61
|
+
extraReadOnlyBinds?: string[];
|
|
53
62
|
}
|
|
54
63
|
/** Options for executing a job inside the sandbox. */
|
|
55
64
|
export interface JobExecutionOptions {
|
|
@@ -57,8 +66,11 @@ export interface JobExecutionOptions {
|
|
|
57
66
|
dispatch: JobDispatch;
|
|
58
67
|
/** Callback for real-time step status updates (start, success, failed). */
|
|
59
68
|
onStepStatus: (stepIndex: number, name: string, state: string, data?: Record<string, unknown>) => void;
|
|
60
|
-
/**
|
|
61
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Callback for real-time log line forwarding. `stream` names the subprocess
|
|
71
|
+
* stream the line came from; absent means stdout.
|
|
72
|
+
*/
|
|
73
|
+
onLogLine: (stepIndex: number, line: string, stream?: LogStream) => void;
|
|
62
74
|
/** Abort signal for cancellation. */
|
|
63
75
|
signal: AbortSignal;
|
|
64
76
|
/**
|
|
@@ -97,6 +109,15 @@ export interface JobExecutionOptions {
|
|
|
97
109
|
* working — the runner falls back to a "not configured" error response.
|
|
98
110
|
*/
|
|
99
111
|
onProvenanceRequest?: (request: ProvenanceRequestIpc) => Promise<ProvenanceResponseIpc>;
|
|
112
|
+
/**
|
|
113
|
+
* Callback for relaying a user-facing artifact request from the sandbox to the
|
|
114
|
+
* orchestrator. The sandbox runner sends `artifacts.request` IPC; the agent
|
|
115
|
+
* wraps it in the matching `artifacts.upload.*` / `artifacts.download.*` WS
|
|
116
|
+
* message and forwards to the orchestrator. Optional so harnesses that don't
|
|
117
|
+
* thread artifacts keep working — the runner falls back to a "not configured"
|
|
118
|
+
* error response.
|
|
119
|
+
*/
|
|
120
|
+
onArtifactRequest?: (request: ArtifactRequestIpc) => Promise<ArtifactResponseIpc>;
|
|
100
121
|
/**
|
|
101
122
|
* Callback for relaying a step-level approval request from the sandbox to the
|
|
102
123
|
* orchestrator. The sandbox runner sends `approval.request` IPC; the agent
|
|
@@ -13,12 +13,15 @@
|
|
|
13
13
|
* This file is compiled alongside the agent by rolldown (existing build), but
|
|
14
14
|
* runs as a SEPARATE process spawned by the sandbox backend.
|
|
15
15
|
*/
|
|
16
|
+
import { $ } from 'zx';
|
|
17
|
+
import { type TempScope } from '@kici-dev/core/tmp';
|
|
16
18
|
import { ExecutionJobStatus } from '@kici-dev/engine';
|
|
17
|
-
import type { StepContext } from '@kici-dev/sdk';
|
|
18
|
-
import type { NeedsContext, FanoutPosition } from '@kici-dev/sdk';
|
|
19
|
+
import type { Step, StepInput, StepContext } from '@kici-dev/sdk';
|
|
20
|
+
import type { NeedsContext, FanoutPosition, EventDefinition } from '@kici-dev/sdk';
|
|
19
21
|
import type { OutputsMap, StepRefMap, TrackedStepSecrets } from '@kici-dev/sdk';
|
|
20
22
|
import type { RunnerToAgentMessage, JobExecutionRequest } from './ipc-protocol.js';
|
|
21
23
|
import { LogMasker } from './log-masker.js';
|
|
24
|
+
import { type RuleEvaluationResult } from '../rule-evaluator.js';
|
|
22
25
|
/**
|
|
23
26
|
* Build `ctx.needs` for a job's steps from the dispatch envelope. Reconstructs
|
|
24
27
|
* an {@link UpstreamSnapshot} from `upstreamJobOutputs` (flat per single job;
|
|
@@ -28,6 +31,62 @@ import { LogMasker } from './log-masker.js';
|
|
|
28
31
|
* undefined when the job declares no needs.
|
|
29
32
|
*/
|
|
30
33
|
export declare function buildStepNeedsContext(declaredNeeds: readonly unknown[] | undefined, upstreamJobOutputs: Record<string, Record<string, unknown>> | undefined, upstreamJobStatuses: Record<string, ExecutionJobStatus> | undefined): NeedsContext | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Build a fresh zx `$` shell bound to the sandbox working directory and the
|
|
36
|
+
* sanitized environment (process.env was set by the parent via env-sanitizer
|
|
37
|
+
* before spawning this process). This is the single shell-construction code
|
|
38
|
+
* path shared by step execution (`createSandboxStepContext`) and the per-job
|
|
39
|
+
* init phase (`runInitPhase`), so init commands run through the identical shell
|
|
40
|
+
* steps use — same cwd, same live sanitized env, same masked log streaming.
|
|
41
|
+
*
|
|
42
|
+
* The shell binds `process.env` by reference (not a copy), so a same-step
|
|
43
|
+
* ctx.setEnv / ctx.addPath mutation — which applyEnvDelta writes to live
|
|
44
|
+
* process.env — is visible to that step's own subprocesses, matching the
|
|
45
|
+
* "visible to this step" SDK contract.
|
|
46
|
+
*
|
|
47
|
+
* Intercept zx subprocess output via the log callback: zx does NOT write child
|
|
48
|
+
* stdout/stderr to process.stdout — it pipes to an internal VoidStream and only
|
|
49
|
+
* calls $.log() with { kind: 'stdout'|'stderr', verbose }. We override the log
|
|
50
|
+
* function (`makeStreamingZxLog`) to capture both kinds and send them as masked
|
|
51
|
+
* IPC log.line messages tagged with `stepIndex`.
|
|
52
|
+
*
|
|
53
|
+
* The shell is built with `verbose: true` so ordinary subprocess output is
|
|
54
|
+
* flagged `verbose: true` (captured) while a step that opts into
|
|
55
|
+
* `$({ quiet: true })` — e.g. a `sops -d` credential decrypt — is flagged
|
|
56
|
+
* `verbose: false` and dropped by `makeStreamingZxLog`. That gate is what keeps
|
|
57
|
+
* a decrypted secret out of the streamed run log; a `verbose: false` base would
|
|
58
|
+
* flag ordinary output `verbose: false` too and drop every line.
|
|
59
|
+
*
|
|
60
|
+
* IMPORTANT: The log function must be passed in the zx$() config, not set on the
|
|
61
|
+
* returned function. zx$() returns a plain function (not the proxy $), so setting
|
|
62
|
+
* step$.log would only set it on the function object and NOT propagate to the
|
|
63
|
+
* AsyncLocalStorage store that zx uses for ProcessPromise snapshots.
|
|
64
|
+
*/
|
|
65
|
+
export declare function buildSandboxShell(cwd: string, stepIndex: number, maskedSendFn: (msg: RunnerToAgentMessage) => void): typeof $;
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the on-the-wire event name for `ctx.emit`. Accepts either an ad-hoc
|
|
68
|
+
* event-name string or a `defineEvent()` definition object (passed by the typed
|
|
69
|
+
* emit overload); a definition resolves to its `.name`, a string passes through.
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveEmitEventName(nameOrDefinition: string | EventDefinition): string;
|
|
72
|
+
/**
|
|
73
|
+
* Sanitize a raw identifier into a valid temp label: lowercase, every
|
|
74
|
+
* non-`[a-z0-9-]` char to `-`, falling back to `'step'` when the result is
|
|
75
|
+
* empty. Applied to both the caller-supplied `ctx.mktemp(label)` and the
|
|
76
|
+
* default step-id label so a friendly-but-irregular label never rejects at the
|
|
77
|
+
* scope. Mirrors the SDK test builder's `sanitizeTempLabel`.
|
|
78
|
+
*/
|
|
79
|
+
export declare function sanitizeTempLabel(raw: string): string;
|
|
80
|
+
/**
|
|
81
|
+
* Drain the job-scoped temp allocator once at job end.
|
|
82
|
+
*
|
|
83
|
+
* Called after the step loop and cancel-path hooks, immediately before the
|
|
84
|
+
* terminal `job.complete` emit, so a single call reclaims every `ctx.mktemp` /
|
|
85
|
+
* `ctx.mktempFile` allocation on success, failure, and cancel/timeout alike. A
|
|
86
|
+
* cleanup failure must NEVER change the job's terminal status, so any error is
|
|
87
|
+
* swallowed into `warn` rather than thrown out of `main()`.
|
|
88
|
+
*/
|
|
89
|
+
export declare function drainJobTempScope(scope: TempScope, warn: (message: string) => void): Promise<void>;
|
|
31
90
|
/**
|
|
32
91
|
* Create a StepContext natively inside the workflow runner.
|
|
33
92
|
*
|
|
@@ -35,7 +94,7 @@ export declare function buildStepNeedsContext(declaredNeeds: readonly unknown[]
|
|
|
35
94
|
* NOT serialized across the process boundary. This means zx $ runs natively
|
|
36
95
|
* inside this process with full shell access.
|
|
37
96
|
*/
|
|
38
|
-
export declare function createSandboxStepContext(workDir: string, stepIndex: number, stepName: string, request: JobExecutionRequest, maskedSendFn: (msg: RunnerToAgentMessage) => void, outputsMap: OutputsMap, refMap: StepRefMap, operatorSecretKeys: Set<string>, secretOutputs: Map<string, string>, jobOutputsMap: OutputsMap, secrets: TrackedStepSecrets, masker: LogMasker, signal: AbortSignal): StepContext;
|
|
97
|
+
export declare function createSandboxStepContext(workDir: string, stepIndex: number, stepName: string, request: JobExecutionRequest, maskedSendFn: (msg: RunnerToAgentMessage) => void, outputsMap: OutputsMap, refMap: StepRefMap, operatorSecretKeys: Set<string>, secretOutputs: Map<string, string>, jobOutputsMap: OutputsMap, secrets: TrackedStepSecrets, masker: LogMasker, signal: AbortSignal, jobTempScope: TempScope): StepContext;
|
|
39
98
|
/**
|
|
40
99
|
* Derive the fan-out position (`ctx.fanout`) from a dispatch request. Returns
|
|
41
100
|
* `undefined` for a non-fan-out job (no `fanoutTotal`), so `ctx.fanout` is only
|
|
@@ -44,12 +103,59 @@ export declare function createSandboxStepContext(workDir: string, stepIndex: num
|
|
|
44
103
|
export declare function deriveFanout(request: JobExecutionRequest): FanoutPosition | undefined;
|
|
45
104
|
/** Raw provider webhook body for ctx.rawPayload — nested in the envelope. */
|
|
46
105
|
export declare function rawPayloadFromEvent(event: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
|
|
106
|
+
/**
|
|
107
|
+
* Mutable state threaded through {@link coerceStep} for one job normalization
|
|
108
|
+
* pass: the shared `step-N` counter, the bare-function → name ref map, and the
|
|
109
|
+
* set of step objects this pass has already auto-named.
|
|
110
|
+
*/
|
|
111
|
+
export interface CoerceState {
|
|
112
|
+
counter: number;
|
|
113
|
+
refMap: StepRefMap;
|
|
114
|
+
autoNamed: WeakSet<object>;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Coerce one raw entry (bare function or Step) into a normalized Step, naming
|
|
118
|
+
* anonymous steps `step-N` with a counter shared across the whole flattened
|
|
119
|
+
* sequence (parallel children inline) — matching the compiler's transformSteps
|
|
120
|
+
* enumeration so the flat-stepIndex invariant holds agent ↔ orchestrator.
|
|
121
|
+
*
|
|
122
|
+
* Unnamed steps are named by MUTATING the original object so the SDK's
|
|
123
|
+
* late-bound `.result` proxy (which reads `step.name` at access time) resolves.
|
|
124
|
+
* A step object reused twice in one pass gets a clone with a fresh name on the
|
|
125
|
+
* repeat encounter — mirroring the compiler's per-occurrence naming so lock
|
|
126
|
+
* parity holds; the original keeps its first binding.
|
|
127
|
+
*/
|
|
128
|
+
export declare function coerceStep(stepOrFn: StepInput, state: CoerceState): Step<any>;
|
|
129
|
+
/**
|
|
130
|
+
* Resolve `event.changedFiles` for rule evaluation before job/step rules run.
|
|
131
|
+
* Ground truth is the agent's own clone (`computeChangedFiles`); the
|
|
132
|
+
* orchestrator's already-fetched list (status `'fetched'`) is a free fast-path.
|
|
133
|
+
* Only runs when a rule could read `ctx.changedFiles` — rule-less jobs skip the
|
|
134
|
+
* git cost. Diff-less events (schedule/tag/manual) resolve to `'unavailable'`.
|
|
135
|
+
*/
|
|
136
|
+
export declare function resolveChangedFilesForRules(request: JobExecutionRequest, workDir: string, hasRules: boolean): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Decide the terminal `job.complete` for job-level rule evaluation, or `null`
|
|
139
|
+
* when the job should proceed to step execution.
|
|
140
|
+
*
|
|
141
|
+
* - `allPassed` → `null` (run the steps).
|
|
142
|
+
* - a rule's `check()` **threw** (`evaluationError`) → FAIL: the gate could not
|
|
143
|
+
* be evaluated, so treating "couldn't decide" as "don't run" would be a false
|
|
144
|
+
* green. Report `status: failed` with the error surfaced.
|
|
145
|
+
* - a rule cleanly returned `false` → clean skip: `status: success` with every
|
|
146
|
+
* step marked skipped (a gate that evaluated and said "don't run").
|
|
147
|
+
*
|
|
148
|
+
* Pure and exported so the decision is unit-testable without `process.exit`.
|
|
149
|
+
*/
|
|
150
|
+
export declare function buildJobRuleCompletion(ruleResult: RuleEvaluationResult, normalizedSteps: Step[]): (RunnerToAgentMessage & {
|
|
151
|
+
type: 'job.complete';
|
|
152
|
+
}) | null;
|
|
47
153
|
/**
|
|
48
154
|
* Build the step loop's KICI_ENV/KICI_PATH callbacks with a per-step delta-file
|
|
49
155
|
* pair (keyed by step index). `beforeStepEnvFiles(stepIndex)` lazily creates the
|
|
50
156
|
* step's pair and points the runner's process.env at it (each step's zx $
|
|
51
|
-
*
|
|
52
|
-
*
|
|
157
|
+
* reads live process.env at spawn, which happens AFTER this before-hook, so
|
|
158
|
+
* the shell sees them; the pre-fork env allowlist does not
|
|
53
159
|
* re-filter runtime-set vars). `afterStepApplyEnvFiles(stepIndex)` applies that
|
|
54
160
|
* step's delta and releases the pair.
|
|
55
161
|
*
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { LogStream } from '@kici-dev/engine';
|
|
2
|
+
/**
|
|
3
|
+
* Shared factory for the zx `log` callback that streams a subprocess's
|
|
4
|
+
* stdout/stderr into the captured/streamed run log, line by line, via `emit`.
|
|
5
|
+
*
|
|
6
|
+
* zx does NOT write child stdout/stderr to `process.stdout`; it pipes the
|
|
7
|
+
* child stdio to an internal VoidStream and surfaces each chunk through the
|
|
8
|
+
* shell's `log` callback as `{ kind: 'stdout' | 'stderr', data, verbose }`.
|
|
9
|
+
* The `verbose` flag is zx's per-invocation quiet/verbose decision:
|
|
10
|
+
*
|
|
11
|
+
* - stdout entries: `verbose = !piped && (snapshot.verbose && !snapshot.quiet)`
|
|
12
|
+
* - stderr entries: `verbose = !snapshot.quiet`
|
|
13
|
+
*
|
|
14
|
+
* so a step that opts into `$({ quiet: true })` (e.g. a `sops -d` decrypt of a
|
|
15
|
+
* credential) produces `verbose: false` entries. This factory HONORS that flag
|
|
16
|
+
* — it skips `verbose: false` entries — which is what makes `{ quiet: true }`
|
|
17
|
+
* actually suppress sensitive output from the run log. Without the gate, a
|
|
18
|
+
* decrypted-secret line leaks into the persisted/streamed log (zx's own default
|
|
19
|
+
* log function gates on the same flag: `if (!entry.verbose) return`).
|
|
20
|
+
*
|
|
21
|
+
* IMPORTANT: the shell that installs this callback MUST be constructed with
|
|
22
|
+
* `verbose: true`. With `verbose: true` zx flags ordinary (non-quiet)
|
|
23
|
+
* subprocess output `verbose: true` (captured) and a `{ quiet: true }` call
|
|
24
|
+
* `verbose: false` (suppressed). A `verbose: false` base would flag ordinary
|
|
25
|
+
* output `verbose: false` too, and this gate would then drop every line.
|
|
26
|
+
*
|
|
27
|
+
* The returned callback owns one line buffer PER STREAM, so partial chunks are
|
|
28
|
+
* coalesced into whole lines before `emit` is called. The buffers are separate
|
|
29
|
+
* because the two streams are independent pipes: a stdout chunk ending mid-line
|
|
30
|
+
* and a stderr chunk arriving next would otherwise concatenate into a single
|
|
31
|
+
* spliced line attributed to whichever kind completed it.
|
|
32
|
+
*
|
|
33
|
+
* `emit` receives the originating stream alongside the line so a diagnostic
|
|
34
|
+
* written to stderr stays distinguishable from ordinary progress output all the
|
|
35
|
+
* way to the persisted run log.
|
|
36
|
+
*/
|
|
37
|
+
export declare function makeStreamingZxLog(emit: (line: string, stream: LogStream) => void): (entry: unknown) => void;
|
|
38
|
+
//# sourceMappingURL=streaming-zx-log.d.ts.map
|
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Startup garbage collection for this agent's own temp-directory families.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* Bare job workdirs (`kici-<6 random chars>`, see job-runner.ts), labeled
|
|
5
|
+
* allocator dirs (`kici-<label>-<6 random chars>`, minted by the global temp
|
|
6
|
+
* allocator `@kici-dev/core/tmp`, which guarantees every allocation carries
|
|
7
|
+
* the `kici-` prefix), and isolated pnpm stores (`kici-pnpm-store-*`, see
|
|
8
|
+
* dep-installer.ts) clean themselves up in `finally` blocks — but a hard
|
|
9
|
+
* process death (SIGKILL, OOM kill) skips those, and on a long-lived
|
|
10
|
+
* bare-metal agent the leftovers then accumulate forever. Collecting anything
|
|
11
|
+
* older than a day at startup is safe on shared hosts: no job lives remotely
|
|
12
|
+
* that long (job timeouts are minutes), so a concurrent agent's in-flight
|
|
13
|
+
* dirs are never eligible.
|
|
14
|
+
*
|
|
15
|
+
* The deterministic persistent caches (`kici-agent-payloads`, `kici-data`,
|
|
16
|
+
* `kici-scaler-ledger`) also live directly under the temp root and can be far
|
|
17
|
+
* older than a day, so they are explicitly excluded — `kici-scaler-ledger`
|
|
18
|
+
* even structurally matches the allocator pattern (`ledger` is a 6-char label
|
|
19
|
+
* suffix), which a regex alone cannot distinguish.
|
|
11
20
|
*/
|
|
12
21
|
/**
|
|
13
22
|
* Collect this agent's stale temp dirs. `base` is overridable for tests;
|
|
14
23
|
* production callers use the default temp root. Never throws.
|
|
24
|
+
*
|
|
25
|
+
* The default base is `kiciTmpBase()`, not the OS temp root, so it scans the
|
|
26
|
+
* exact directory the agent now writes payloads/clones/pnpm stores into: the
|
|
27
|
+
* global temp allocator and the payload cache both honor `KICI_TMPDIR` via the
|
|
28
|
+
* same helper. If the GC scanned a different root, stale-temp reaping would
|
|
29
|
+
* silently stop working whenever `KICI_TMPDIR` is set.
|
|
15
30
|
*/
|
|
16
31
|
export declare function gcStaleAgentTmpDirs(base?: string): Promise<string[]>;
|
|
17
32
|
//# sourceMappingURL=tmp-gc.d.ts.map
|
|
@@ -6,7 +6,37 @@
|
|
|
6
6
|
* no Rolldown step at runtime. `@kici-dev/sdk` and host-repo deps resolve via
|
|
7
7
|
* Node's normal ESM lookup against `.kici/node_modules/`.
|
|
8
8
|
*/
|
|
9
|
-
import type { Workflow, StepInput, DynamicJobFn } from '@kici-dev/sdk';
|
|
9
|
+
import type { Workflow, StepInput, DynamicJobFn, OutputsMap, StepRefMap } from '@kici-dev/sdk';
|
|
10
|
+
/**
|
|
11
|
+
* SDK output-map setters resolved from a specific `@kici-dev/sdk` instance.
|
|
12
|
+
* The agent uses these to wire the workflow module's OWN SDK copy (a different
|
|
13
|
+
* physical module than the agent's bundled SDK) to the same output maps the
|
|
14
|
+
* step loop mutates, so within-job `.result` proxies — which read that copy's
|
|
15
|
+
* module-global `_stepOutputsMap` — resolve at access time.
|
|
16
|
+
*/
|
|
17
|
+
export interface SdkOutputSetters {
|
|
18
|
+
setStepOutputsMap: (map: OutputsMap) => void;
|
|
19
|
+
setStepRefMap: (map: StepRefMap) => void;
|
|
20
|
+
setJobOutputsMap: (map: OutputsMap) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the `@kici-dev/sdk` instance the workflow module itself imports.
|
|
24
|
+
*
|
|
25
|
+
* The workflow's `.result` proxies read the module-global step-outputs map of
|
|
26
|
+
* whichever SDK copy the workflow file resolves — which is generally a
|
|
27
|
+
* different physical module than the agent's bundled SDK (the workflow is
|
|
28
|
+
* imported from the cloned source tree and resolves its deps against that
|
|
29
|
+
* tree's `node_modules`). Resolving via `createRequire(workflowFilePath)` walks
|
|
30
|
+
* `node_modules` from the workflow file exactly the way the workflow's own
|
|
31
|
+
* `import '@kici-dev/sdk'` does — including any hoisted copy — so the returned
|
|
32
|
+
* setters mutate the SAME module-global map object the proxies read. Node caches
|
|
33
|
+
* ESM modules by resolved URL, so importing that path yields the workflow's live
|
|
34
|
+
* singleton, not a fresh copy.
|
|
35
|
+
*
|
|
36
|
+
* Falls back to the agent's bundled setters when resolution fails (mirrors
|
|
37
|
+
* `resolveSdkSetters` in the compiler's test runner).
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveWorkflowSdkSetters(workflowFilePath: string): Promise<SdkOutputSetters>;
|
|
10
40
|
/**
|
|
11
41
|
* Compile schema version — must match `@kici-dev/compiler` lockfile/hasher.ts.
|
|
12
42
|
* Mixed into the content hash so compilation-approach changes produce different
|
|
@@ -33,6 +63,7 @@ export declare function ensureLoaderHookRegistered(): void;
|
|
|
33
63
|
*/
|
|
34
64
|
export declare function loadWorkflowSource(workDir: string, sourceFile: string, expectedContentHash?: string, resolvedHashFiles?: string[]): Promise<{
|
|
35
65
|
module: Record<string, unknown>;
|
|
66
|
+
sdkSetters: SdkOutputSetters;
|
|
36
67
|
}>;
|
|
37
68
|
/**
|
|
38
69
|
* Extract a workflow by name from a module's exports.
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,8 @@ import { z } from "zod";
|
|
|
5
5
|
import { LOGGER_ENV_VARS, defineEnv, validateUnknownKiciVars } from "@kici-dev/shared/env";
|
|
6
6
|
import { KNOWN_ROLES, parseHostPropertyAssignments, validateNoReservedLabels } from "@kici-dev/engine";
|
|
7
7
|
import { execFile } from "node:child_process";
|
|
8
|
-
import { access, cp, lstat, mkdir,
|
|
8
|
+
import { access, cp, lstat, mkdir, readFile, readdir, realpath, rename, rm, unlink, writeFile } from "node:fs/promises";
|
|
9
|
+
import { makeTempDir } from "@kici-dev/core/tmp";
|
|
9
10
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
10
11
|
import { promisify } from "node:util";
|
|
11
12
|
import { createLogger, sha256, toErrorMessage } from "@kici-dev/shared";
|
|
@@ -15,6 +16,7 @@ import { parse, stringify } from "yaml";
|
|
|
15
16
|
import { Readable, Transform } from "node:stream";
|
|
16
17
|
import { pipeline } from "node:stream/promises";
|
|
17
18
|
import { createGunzip } from "node:zlib";
|
|
19
|
+
import { HOME_ANCHOR, REPO_ANCHOR } from "@kici-dev/core";
|
|
18
20
|
import { c, x } from "tar";
|
|
19
21
|
import https from "node:https";
|
|
20
22
|
import http from "node:http";
|
|
@@ -85,8 +87,18 @@ const envDef = defineEnv({
|
|
|
85
87
|
dockerKeepFailed: z.string().default("false").transform((s) => s === "true"),
|
|
86
88
|
jobHeartbeatIntervalMs: z.coerce.number().default(6e4),
|
|
87
89
|
backpressureMode: z.enum(["pause", "drop"]).default("pause"),
|
|
90
|
+
agentPayloadDir: z.string().optional(),
|
|
91
|
+
agentCommand: z.string().optional(),
|
|
88
92
|
sandbox: z.string().default("false").transform((s) => s === "true"),
|
|
93
|
+
trustedEnv: z.string().default("false").transform((s) => s === "true"),
|
|
94
|
+
inPlace: z.string().default("false").transform((s) => s === "true"),
|
|
89
95
|
sandboxNetwork: z.enum(["isolated", "host"]).default("isolated"),
|
|
96
|
+
sandboxHardened: z.string().default("true").transform((s) => s !== "false"),
|
|
97
|
+
sandboxReadonlyRootfs: z.string().default("false").transform((s) => s === "true"),
|
|
98
|
+
sandboxUser: z.string().optional(),
|
|
99
|
+
sandboxPidsLimit: z.coerce.number().int().positive().default(512),
|
|
100
|
+
sandboxMemoryBytes: z.coerce.number().int().positive().default(2 * 1024 * 1024 * 1024),
|
|
101
|
+
sandboxNanoCpus: z.coerce.number().int().positive().default(2 * 1e9),
|
|
90
102
|
scalerManaged: z.string().optional().transform((s) => s === "1"),
|
|
91
103
|
scalerIdleTimeoutMs: z.coerce.number().default(5e3),
|
|
92
104
|
scalerPendingDispatchTimeoutMs: z.coerce.number().default(6e4),
|
|
@@ -111,8 +123,18 @@ const envDef = defineEnv({
|
|
|
111
123
|
dockerKeepFailed: "KICI_DOCKER_KEEP_FAILED",
|
|
112
124
|
jobHeartbeatIntervalMs: "KICI_JOB_HEARTBEAT_INTERVAL_MS",
|
|
113
125
|
backpressureMode: "KICI_BACKPRESSURE_MODE",
|
|
126
|
+
agentPayloadDir: "KICI_AGENT_PAYLOAD_DIR",
|
|
127
|
+
agentCommand: "KICI_AGENT_COMMAND",
|
|
114
128
|
sandbox: "KICI_SANDBOX",
|
|
129
|
+
trustedEnv: "KICI_TRUSTED_ENV",
|
|
130
|
+
inPlace: "KICI_IN_PLACE",
|
|
115
131
|
sandboxNetwork: "KICI_SANDBOX_NETWORK",
|
|
132
|
+
sandboxHardened: "KICI_SANDBOX_HARDENED",
|
|
133
|
+
sandboxReadonlyRootfs: "KICI_SANDBOX_READONLY_ROOTFS",
|
|
134
|
+
sandboxUser: "KICI_SANDBOX_USER",
|
|
135
|
+
sandboxPidsLimit: "KICI_SANDBOX_PIDS_LIMIT",
|
|
136
|
+
sandboxMemoryBytes: "KICI_SANDBOX_MEMORY_BYTES",
|
|
137
|
+
sandboxNanoCpus: "KICI_SANDBOX_NANO_CPUS",
|
|
116
138
|
scalerManaged: "KICI_SCALER_MANAGED",
|
|
117
139
|
scalerIdleTimeoutMs: "KICI_SCALER_IDLE_TIMEOUT",
|
|
118
140
|
scalerPendingDispatchTimeoutMs: "KICI_SCALER_PENDING_DISPATCH_TIMEOUT",
|
|
@@ -140,7 +162,15 @@ const envDef = defineEnv({
|
|
|
140
162
|
* - KICI_JOB_HEARTBEAT_INTERVAL_MS (default: 60000)
|
|
141
163
|
* - KICI_BACKPRESSURE_MODE (default: pause, options: pause | drop)
|
|
142
164
|
* - KICI_SANDBOX (default: false) — enable bubblewrap (bwrap) namespace isolation for bare-metal execution
|
|
143
|
-
* -
|
|
165
|
+
* - KICI_TRUSTED_ENV (default: false) — trusted fleet-agent profile: pass the ambient host env (minus the agent's own KiCI identity secrets) through to steps
|
|
166
|
+
* - KICI_IN_PLACE (default: false) — in-place no-clone profile: for a file:// source, use the real repo path as workDir and skip the clone (the routed deploy:stg profile)
|
|
167
|
+
* - KICI_SANDBOX_NETWORK (default: isolated, options: isolated | host) — sandbox network posture for BOTH backends: the bwrap network namespace (when sandbox=true) and the container job network. `host` shares the host network (container backend also binds host /etc/hosts read-only for name resolution); applies to the container backend under the default hardened posture (KICI_SANDBOX_HARDENED=true)
|
|
168
|
+
* - KICI_SANDBOX_HARDENED (default: true) — hardened-by-default job containers (CapDrop ALL, no-new-privileges, cgroup caps, tmpfs /tmp); set false to roll back to the legacy unhardened posture
|
|
169
|
+
* - KICI_SANDBOX_READONLY_ROOTFS (default: false) — opt-in read-only container rootfs (/tmp stays a writable tmpfs)
|
|
170
|
+
* - KICI_SANDBOX_USER (optional) — container user override (uid, uid:gid, or name); honors the image user when unset
|
|
171
|
+
* - KICI_SANDBOX_PIDS_LIMIT (default: 512) — max PIDs in the job container cgroup
|
|
172
|
+
* - KICI_SANDBOX_MEMORY_BYTES (default: 2 GiB) — memory cap in bytes for the job container cgroup
|
|
173
|
+
* - KICI_SANDBOX_NANO_CPUS (default: 2 CPUs) — CPU cap in nano-CPUs for the job container cgroup
|
|
144
174
|
* - KICI_SCALER_MANAGED (set to "1" by the orchestrator's auto-scaler — agent self-shuts down on idle)
|
|
145
175
|
* - KICI_SCALER_IDLE_TIMEOUT (ms, default 5000) — how long a scaler-managed agent waits before shutdown after going idle
|
|
146
176
|
* - KICI_SCALER_PENDING_DISPATCH_TIMEOUT (ms, default 60000) — extended idle window when register.ack signals a queued bound job
|
|
@@ -149,7 +179,7 @@ const envDef = defineEnv({
|
|
|
149
179
|
*/
|
|
150
180
|
function loadConfig() {
|
|
151
181
|
const data = envDef.parse();
|
|
152
|
-
if (!data.scalerManaged) validateNoReservedLabels(data.labels, "KICI_LABELS");
|
|
182
|
+
if (!data.scalerManaged && !data.agentToken) validateNoReservedLabels(data.labels, "KICI_LABELS");
|
|
153
183
|
validateUnknownKiciVars([...envDef.listKnownEnvVars(), ...LOGGER_ENV_VARS]);
|
|
154
184
|
return {
|
|
155
185
|
...data,
|
|
@@ -358,7 +388,7 @@ async function applyYarnrcBerryConfig(args) {
|
|
|
358
388
|
const hasPrivateRegistry = registries.length > 0 || Object.keys(installEnvSecrets).length > 0;
|
|
359
389
|
const yarnrcPath = join(args.kiciDir, ".yarnrc.yml");
|
|
360
390
|
const { raw: original, doc } = await readOriginalYarnrc(yarnrcPath);
|
|
361
|
-
const cacheFolder = await
|
|
391
|
+
const { path: cacheFolder, cleanup: cleanupCache } = await makeTempDir("yarn-berry-cache");
|
|
362
392
|
const merged = {
|
|
363
393
|
...doc,
|
|
364
394
|
nodeLinker: "node-modules",
|
|
@@ -395,10 +425,7 @@ async function applyYarnrcBerryConfig(args) {
|
|
|
395
425
|
if (original === null) await unlink(yarnrcPath).catch(() => {});
|
|
396
426
|
else await writeFile(yarnrcPath, original, { encoding: "utf8" });
|
|
397
427
|
} catch {}
|
|
398
|
-
await
|
|
399
|
-
recursive: true,
|
|
400
|
-
force: true
|
|
401
|
-
}).catch(() => {});
|
|
428
|
+
await cleanupCache().catch(() => {});
|
|
402
429
|
};
|
|
403
430
|
return {
|
|
404
431
|
extraEnv: {
|
|
@@ -750,9 +777,9 @@ async function detectKiciYarnFlavor(repoRoot, kiciDir) {
|
|
|
750
777
|
* Install `.kici/` dependencies inline with the repo's package manager.
|
|
751
778
|
*
|
|
752
779
|
* Falls back to this when the dep cache is unavailable or a download fails.
|
|
753
|
-
* The install runs with an isolated cache/store directory (
|
|
754
|
-
* `
|
|
755
|
-
* is removed after installation.
|
|
780
|
+
* The install runs with an isolated cache/store directory (allocated under the
|
|
781
|
+
* global temp base, which honors `KICI_TMPDIR`) to prevent cache poisoning
|
|
782
|
+
* between build jobs; the directory is removed after installation.
|
|
756
783
|
*
|
|
757
784
|
* If `opts.npmRegistries` / `opts.installEnvSecrets` is provided, a job-scoped
|
|
758
785
|
* `.kici/.npmrc` overlay is synthesized for the install, restored in `finally`,
|
|
@@ -841,7 +868,7 @@ function envWithNodeOnPath(extraEnv, nodeDir) {
|
|
|
841
868
|
/** Run `npm install` in `.kici/` with an isolated cache directory. */
|
|
842
869
|
async function runNpmInstall(args) {
|
|
843
870
|
const { npmCliPath, nodeExe, nodeDir } = resolveNpm();
|
|
844
|
-
const cacheDir = await
|
|
871
|
+
const { path: cacheDir, cleanup } = await makeTempDir("npm-cache");
|
|
845
872
|
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir);
|
|
846
873
|
const buildArgs = (...prefix) => {
|
|
847
874
|
const a = [
|
|
@@ -866,10 +893,7 @@ async function runNpmInstall(args) {
|
|
|
866
893
|
maxBuffer: INSTALL_MAX_BUFFER
|
|
867
894
|
});
|
|
868
895
|
} finally {
|
|
869
|
-
await
|
|
870
|
-
recursive: true,
|
|
871
|
-
force: true
|
|
872
|
-
}).catch(() => {});
|
|
896
|
+
await cleanup().catch(() => {});
|
|
873
897
|
}
|
|
874
898
|
}
|
|
875
899
|
/**
|
|
@@ -883,7 +907,7 @@ async function runNpmInstall(args) {
|
|
|
883
907
|
async function runPnpmInstall(args) {
|
|
884
908
|
await assertPnpmAvailable();
|
|
885
909
|
const { nodeDir } = resolveNpm();
|
|
886
|
-
const storeDir = await
|
|
910
|
+
const { path: storeDir, cleanup } = await makeTempDir("pnpm-store");
|
|
887
911
|
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir);
|
|
888
912
|
const argv = [
|
|
889
913
|
"install",
|
|
@@ -903,10 +927,7 @@ async function runPnpmInstall(args) {
|
|
|
903
927
|
maxBuffer: INSTALL_MAX_BUFFER
|
|
904
928
|
});
|
|
905
929
|
} finally {
|
|
906
|
-
await
|
|
907
|
-
recursive: true,
|
|
908
|
-
force: true
|
|
909
|
-
}).catch(() => {});
|
|
930
|
+
await cleanup().catch(() => {});
|
|
910
931
|
}
|
|
911
932
|
}
|
|
912
933
|
/** Pure: argv for `yarn install` with an isolated cache folder. */
|
|
@@ -932,7 +953,7 @@ function buildYarnInstallArgs(cacheDir, hasPrivateRegistry) {
|
|
|
932
953
|
async function runYarnInstall(args) {
|
|
933
954
|
await assertYarnAvailable();
|
|
934
955
|
const { nodeDir } = resolveNpm();
|
|
935
|
-
const cacheDir = await
|
|
956
|
+
const { path: cacheDir, cleanup } = await makeTempDir("yarn-cache");
|
|
936
957
|
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir);
|
|
937
958
|
const argv = buildYarnInstallArgs(cacheDir, args.hasPrivateRegistry);
|
|
938
959
|
try {
|
|
@@ -944,10 +965,7 @@ async function runYarnInstall(args) {
|
|
|
944
965
|
maxBuffer: INSTALL_MAX_BUFFER
|
|
945
966
|
});
|
|
946
967
|
} finally {
|
|
947
|
-
await
|
|
948
|
-
recursive: true,
|
|
949
|
-
force: true
|
|
950
|
-
}).catch(() => {});
|
|
968
|
+
await cleanup().catch(() => {});
|
|
951
969
|
}
|
|
952
970
|
}
|
|
953
971
|
/** Pure: argv for a berry `yarn install`. Cache + linker live in .yarnrc.yml. */
|
|
@@ -1218,10 +1236,6 @@ var init_download = __esmMin((() => {
|
|
|
1218
1236
|
const logger = createLogger({ prefix: "cache-engine" });
|
|
1219
1237
|
/** Download timeout for a presigned cache GET: 5 minutes. */
|
|
1220
1238
|
const DOWNLOAD_TIMEOUT_MS = 300 * 1e3;
|
|
1221
|
-
/** Anchor prefix for repo-root-relative cache entries inside the tar. */
|
|
1222
|
-
const REPO_ANCHOR = "__repo__";
|
|
1223
|
-
/** Anchor prefix for home-relative (`~`) cache entries inside the tar. */
|
|
1224
|
-
const HOME_ANCHOR = "__home__";
|
|
1225
1239
|
/**
|
|
1226
1240
|
* Resolve a cache path. `~`-prefixed -> home root; otherwise repo-root-relative.
|
|
1227
1241
|
* Rejects absolute paths and `..` escapes so a workflow cannot read or clobber
|
|
@@ -1262,7 +1276,7 @@ function anchorEntries(workDir, paths, roots) {
|
|
|
1262
1276
|
*/
|
|
1263
1277
|
async function packCachePaths(workDir, paths, roots) {
|
|
1264
1278
|
const entries = anchorEntries(workDir, paths, roots);
|
|
1265
|
-
const staging = await
|
|
1279
|
+
const { path: staging, cleanup } = await makeTempDir("cache-pack");
|
|
1266
1280
|
try {
|
|
1267
1281
|
const topLevel = /* @__PURE__ */ new Set();
|
|
1268
1282
|
for (const e of entries) {
|
|
@@ -1293,10 +1307,7 @@ async function packCachePaths(workDir, paths, roots) {
|
|
|
1293
1307
|
hash
|
|
1294
1308
|
};
|
|
1295
1309
|
} finally {
|
|
1296
|
-
await
|
|
1297
|
-
recursive: true,
|
|
1298
|
-
force: true
|
|
1299
|
-
});
|
|
1310
|
+
await cleanup();
|
|
1300
1311
|
}
|
|
1301
1312
|
}
|
|
1302
1313
|
/** Move the extracted `__repo__` / `__home__` groups from a scratch dir into place. */
|
|
@@ -1327,7 +1338,7 @@ async function extractCacheTarball(tarball, workDir, expectedHash, roots) {
|
|
|
1327
1338
|
if (actual !== expectedHash) throw new Error(`Cache tarball checksum mismatch: expected ${expectedHash}, got ${actual}`);
|
|
1328
1339
|
const home = roots?.home ?? homedir();
|
|
1329
1340
|
await mkdir(workDir, { recursive: true });
|
|
1330
|
-
const scratch = await
|
|
1341
|
+
const { path: scratch, cleanup } = await makeTempDir("cache-extract");
|
|
1331
1342
|
try {
|
|
1332
1343
|
await new Promise((res, rej) => {
|
|
1333
1344
|
Readable.from(tarball).pipe(x({
|
|
@@ -1337,10 +1348,7 @@ async function extractCacheTarball(tarball, workDir, expectedHash, roots) {
|
|
|
1337
1348
|
});
|
|
1338
1349
|
await moveAnchoredGroups(scratch, workDir, home);
|
|
1339
1350
|
} finally {
|
|
1340
|
-
await
|
|
1341
|
-
recursive: true,
|
|
1342
|
-
force: true
|
|
1343
|
-
});
|
|
1351
|
+
await cleanup();
|
|
1344
1352
|
}
|
|
1345
1353
|
}
|
|
1346
1354
|
/**
|
|
@@ -1359,17 +1367,14 @@ async function downloadAndExtractCache(url, workDir, expectedHash, roots) {
|
|
|
1359
1367
|
cb(null, chunk);
|
|
1360
1368
|
} });
|
|
1361
1369
|
await mkdir(workDir, { recursive: true });
|
|
1362
|
-
const scratch = await
|
|
1370
|
+
const { path: scratch, cleanup } = await makeTempDir("cache-extract");
|
|
1363
1371
|
try {
|
|
1364
1372
|
await pipeline(Readable.fromWeb(response.body), hashTransform, createGunzip(), x({ cwd: scratch }));
|
|
1365
1373
|
const digest = hash.digest("hex");
|
|
1366
1374
|
if (digest !== expectedHash) throw new Error(`Cache tarball checksum mismatch on download: expected ${expectedHash}, got ${digest}`);
|
|
1367
1375
|
await moveAnchoredGroups(scratch, workDir, home);
|
|
1368
1376
|
} finally {
|
|
1369
|
-
await
|
|
1370
|
-
recursive: true,
|
|
1371
|
-
force: true
|
|
1372
|
-
});
|
|
1377
|
+
await cleanup();
|
|
1373
1378
|
}
|
|
1374
1379
|
}
|
|
1375
1380
|
/** Build the imperative `ctx.cache` API bound to a workDir + transport. */
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Build a SLSA v1.0 in-toto provenance statement from the server-truth identity
|
|
3
3
|
* token claims plus the caller-supplied subject. The build context comes
|
|
4
|
-
* entirely from the JWT claims (
|
|
4
|
+
* entirely from the JWT claims (minted server-side by the orchestrator,
|
|
5
|
+
* unforgeable), so the
|
|
5
6
|
* statement's identity equals the token's identity by construction.
|
|
6
7
|
*/
|
|
7
8
|
import { type KiciProvenanceStatement } from '@kici-dev/engine/provenance/schema';
|
|
8
9
|
import type { SourceOrigin } from '@kici-dev/engine';
|
|
9
|
-
/** The KiCI identity-token claims the builder reads (
|
|
10
|
+
/** The KiCI identity-token claims the builder reads (minter server-truth). */
|
|
10
11
|
export interface ProvenanceTokenClaims {
|
|
11
12
|
iss: string;
|
|
12
13
|
repository?: string | null;
|
package/dist/server.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Startup sequence:
|
|
5
5
|
* 1. Load config
|
|
6
6
|
* 2. Create logger
|
|
7
|
-
* 3. Create JobRunner with send
|
|
7
|
+
* 3. Create JobRunner with send callbacks
|
|
8
8
|
* 4. Create OrchestratorClient with dispatch and cancel handlers
|
|
9
9
|
* 5. Add WS log transport (if not scaler-managed)
|
|
10
10
|
* 6. Connect OrchestratorClient
|