@kici-dev/agent 0.5.0 → 0.6.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/checkout/clone-job-repos.d.ts +53 -0
- package/dist/checkout/credential-helper-bin.d.ts +28 -0
- package/dist/checkout/credential-helper-host.d.ts +48 -0
- package/dist/checkout/credential-helper.d.ts +44 -0
- package/dist/checkout/git-clone.d.ts +26 -0
- package/dist/checkout/grant-table.d.ts +30 -0
- package/dist/checkout/job-git-credentials.d.ts +49 -0
- package/dist/checkout/write-elevation.d.ts +40 -0
- package/dist/config.d.ts +38 -0
- package/dist/container-ts-loader-hook.js +2047 -1814
- package/dist/execution/between-jobs-controller.d.ts +50 -0
- package/dist/execution/between-jobs-reset.d.ts +25 -0
- package/dist/execution/cleanup-rerun.d.ts +21 -0
- package/dist/execution/dynamic-job-serializer.d.ts +6 -2
- package/dist/execution/image-build/build-engine.d.ts +75 -0
- package/dist/execution/image-build/build-step.d.ts +57 -0
- package/dist/execution/image-build/resolve-build-spec.d.ts +41 -0
- package/dist/execution/image-build/runtime-facts.d.ts +31 -0
- package/dist/execution/job-runner.d.ts +48 -0
- package/dist/execution/sandbox/bare-metal-sandbox.d.ts +25 -1
- package/dist/execution/sandbox/container-sandbox.d.ts +97 -2
- package/dist/execution/sandbox/fork-runner.d.ts +55 -1
- package/dist/execution/sandbox/image-preflight.d.ts +38 -0
- package/dist/execution/sandbox/ipc-protocol.d.ts +96 -2
- package/dist/execution/sandbox/kici-runtime.d.ts +48 -0
- package/dist/execution/sandbox/step-loop.d.ts +7 -0
- package/dist/execution/sandbox/types.d.ts +55 -1
- package/dist/execution/sandbox/workflow-runner.d.ts +23 -3
- package/dist/idle-shutdown.d.ts +24 -0
- package/dist/index.js +133 -62
- package/dist/metrics/prometheus.d.ts +26 -0
- package/dist/server.js +2050 -312
- package/dist/workflow-runner-bundle.js +41961 -41319
- package/dist/workflow-runner.js +332 -136
- package/dist/ws/orchestrator-client.d.ts +95 -3
- package/package.json +10 -10
- package/sbom.spdx.json +460 -455
|
@@ -9,6 +9,21 @@ import { type ChildProcess } from 'node:child_process';
|
|
|
9
9
|
import type { JobDispatch } from '@kici-dev/engine';
|
|
10
10
|
import type { JobExecutionOptions, JobExecutionResult } from './types.js';
|
|
11
11
|
import type { JobExecutionRequest } from './ipc-protocol.js';
|
|
12
|
+
/**
|
|
13
|
+
* Best-effort SIGKILL/SIGTERM of an entire process group led by `pid`.
|
|
14
|
+
* `process.kill(-pid, signal)` targets the group whose leader is `pid` (the
|
|
15
|
+
* child was spawned `detached`, so its pid is its group id). Returns 1 when the
|
|
16
|
+
* group was signalled, 0 when it was already gone (ESRCH) or the pid is invalid.
|
|
17
|
+
* We cannot cheaply count members, so the caller treats a non-zero return as
|
|
18
|
+
* "reap attempted".
|
|
19
|
+
*/
|
|
20
|
+
export declare function killProcessGroup(pid: number | undefined, signal: NodeJS.Signals): number;
|
|
21
|
+
/**
|
|
22
|
+
* SIGTERM the job's process group, wait a short grace, then SIGKILL. No-op
|
|
23
|
+
* (returns 0) when the child was not spawned detached or has no pid. Returns the
|
|
24
|
+
* count of reap attempts that signalled a live group (0, 1, or 2).
|
|
25
|
+
*/
|
|
26
|
+
export declare function reapGroup(pid: number | undefined, detached: boolean, killFn?: (p: number | undefined, s: NodeJS.Signals) => number, sleepMs?: number): Promise<number>;
|
|
12
27
|
/** Options for creating a fork-based runner. */
|
|
13
28
|
interface ForkRunnerOptions {
|
|
14
29
|
/** Absolute path to the compiled workflow-runner.js. */
|
|
@@ -39,6 +54,18 @@ interface ForkRunnerOptions {
|
|
|
39
54
|
* Defaults to 30_000 (30 seconds).
|
|
40
55
|
*/
|
|
41
56
|
maxGracePeriodMs?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Spawn the runner child `detached` so it leads its own process group. Set by
|
|
59
|
+
* the bare-metal backend when orphan cleanup is on and bwrap is off, so the
|
|
60
|
+
* between-jobs phase can reap a backgrounded daemon that reparented to init.
|
|
61
|
+
* Ignored under bwrap (its PID namespace already contains the tree).
|
|
62
|
+
*/
|
|
63
|
+
detachProcessGroup?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Between-jobs out-of-band cleanup re-run: the runner reuses the preserved
|
|
66
|
+
* workdir and runs only the job's declared cleanup / onFailure hooks.
|
|
67
|
+
*/
|
|
68
|
+
cleanupOnly?: boolean;
|
|
42
69
|
}
|
|
43
70
|
/** State of a running fork-based child process. */
|
|
44
71
|
export interface ForkRunnerHandle {
|
|
@@ -59,13 +86,40 @@ export interface ForkRunnerHandle {
|
|
|
59
86
|
* Capped by the agent's maxGracePeriodMs.
|
|
60
87
|
*/
|
|
61
88
|
cancel: (force: boolean, gracePeriodMs?: number) => void;
|
|
89
|
+
/**
|
|
90
|
+
* Whether this runner leads its own detached process group (bare-metal,
|
|
91
|
+
* non-bwrap, orphan cleanup on). Drives whether `reap()` does anything.
|
|
92
|
+
*/
|
|
93
|
+
detached: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* True once the runner emitted `completion-hooks-done` (its onSuccess /
|
|
96
|
+
* onFailure / cleanup hooks ran). Stays false when the child exits before
|
|
97
|
+
* signalling, which is the between-jobs phase's cue to re-run declared
|
|
98
|
+
* cleanup out-of-band.
|
|
99
|
+
*/
|
|
100
|
+
completionHooksRan: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* True once the runner reported (via `hooks-declared`) that the job declares
|
|
103
|
+
* an `onFailure` / `cleanup` hook. Recorded early so it survives a later hard
|
|
104
|
+
* kill; gates whether the out-of-band cleanup re-run is attempted.
|
|
105
|
+
*/
|
|
106
|
+
declaresCleanup: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* SIGTERM → grace → SIGKILL the runner's process group, reaping any daemon it
|
|
109
|
+
* backgrounded. No-op (resolves 0) when the runner is not detached; returns
|
|
110
|
+
* the number of reap attempts that signalled a live group.
|
|
111
|
+
*/
|
|
112
|
+
reap: () => Promise<number>;
|
|
62
113
|
}
|
|
63
114
|
/**
|
|
64
115
|
* Build a JobExecutionRequest from a JobDispatch.
|
|
65
116
|
*
|
|
66
117
|
* Maps orchestrator dispatch fields to the subset needed by the workflow runner.
|
|
67
118
|
*/
|
|
68
|
-
export declare function buildRequest(dispatch: JobDispatch, workDir: string
|
|
119
|
+
export declare function buildRequest(dispatch: JobDispatch, workDir: string, extra?: {
|
|
120
|
+
cleanupOnly?: boolean;
|
|
121
|
+
credentialHelperPath?: string;
|
|
122
|
+
}): JobExecutionRequest;
|
|
69
123
|
/**
|
|
70
124
|
* Build bubblewrap (bwrap) arguments for namespace isolation.
|
|
71
125
|
*
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail a container job on an unusable image BEFORE the job starts.
|
|
3
|
+
*
|
|
4
|
+
* KiCI injects its own runtime (a pinned official glibc-2.17 Node) into the
|
|
5
|
+
* customer's image, so the image needs only a glibc and a shell. When it has
|
|
6
|
+
* neither, the container runtime's own error is close to useless — a musl image
|
|
7
|
+
* reports
|
|
8
|
+
*
|
|
9
|
+
* exec container process (missing dynamic library?) `/opt/kici/node/bin/node`:
|
|
10
|
+
* No such file or directory
|
|
11
|
+
*
|
|
12
|
+
* which names a file that plainly exists and says nothing about musl. The
|
|
13
|
+
* preflight turns that into a sentence an author can act on, and it fires
|
|
14
|
+
* before any step runs rather than partway through a job.
|
|
15
|
+
*
|
|
16
|
+
* glibc-only is the deliberate scope of this version; a musl runtime variant is
|
|
17
|
+
* a documented follow-up.
|
|
18
|
+
*/
|
|
19
|
+
import type Docker from 'dockerode';
|
|
20
|
+
/** What an image's rootfs says about whether we can run our runtime in it. */
|
|
21
|
+
export type ImageLibc = 'glibc' | 'musl' | 'static' | 'no-shell';
|
|
22
|
+
/** Every path the preflight stats in the image. */
|
|
23
|
+
export declare const PROBE_PATHS: readonly string[];
|
|
24
|
+
/**
|
|
25
|
+
* Classify an image from the subset of {@link PROBE_PATHS} that exist in it.
|
|
26
|
+
*
|
|
27
|
+
* Pure, so the decision table is testable without a container runtime.
|
|
28
|
+
*/
|
|
29
|
+
export declare function classifyImageLibc(presentPaths: readonly string[]): ImageLibc;
|
|
30
|
+
/**
|
|
31
|
+
* Throw unless `image` can host the injected runtime.
|
|
32
|
+
*
|
|
33
|
+
* Stats the probe paths through a created-but-never-started container, so a
|
|
34
|
+
* shell-less or musl image is diagnosed without executing anything in it —
|
|
35
|
+
* running a probe command would fail for the very reason we are testing for.
|
|
36
|
+
*/
|
|
37
|
+
export declare function assertImageRunnable(docker: Docker, image: string): Promise<void>;
|
|
38
|
+
//# sourceMappingURL=image-preflight.d.ts.map
|
|
@@ -119,6 +119,26 @@ interface JobCompleteMessage {
|
|
|
119
119
|
/** Names of sibling jobs dropped by DynamicJobFn re-evaluation drift. */
|
|
120
120
|
droppedJobs?: string[];
|
|
121
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Emitted once, right after the runner extracts the job's hooks, so the
|
|
124
|
+
* supervisor knows whether the job declares `onFailure` / `cleanup` even if the
|
|
125
|
+
* runner is later hard-killed before those hooks run. The between-jobs phase
|
|
126
|
+
* uses it to decide whether an out-of-band cleanup re-run is warranted.
|
|
127
|
+
*/
|
|
128
|
+
export interface HooksDeclaredMessage {
|
|
129
|
+
type: 'hooks-declared';
|
|
130
|
+
/** True when the job declares an `onFailure` or `cleanup` completion hook. */
|
|
131
|
+
declaresCleanup: boolean;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The runner's job-completion hooks (onSuccess / onFailure / cleanup) have run.
|
|
135
|
+
* Emitted once, whether or not a hook failed. Its absence when the child exits
|
|
136
|
+
* tells the supervisor the runner was killed before its declared cleanup ran,
|
|
137
|
+
* so the between-jobs phase re-runs that cleanup out-of-band.
|
|
138
|
+
*/
|
|
139
|
+
export interface CompletionHooksDoneMessage {
|
|
140
|
+
type: 'completion-hooks-done';
|
|
141
|
+
}
|
|
122
142
|
/** Request to emit a custom event from a workflow step (runner -> agent). */
|
|
123
143
|
export interface EventEmitRequest {
|
|
124
144
|
type: 'event.emit';
|
|
@@ -186,6 +206,31 @@ export interface CacheRequestIpc {
|
|
|
186
206
|
/** Tarball size in bytes (drives quota accounting). `completeSave` only. */
|
|
187
207
|
sizeBytes?: number;
|
|
188
208
|
}
|
|
209
|
+
/** Git write-grant operations (runner -> agent). */
|
|
210
|
+
export type GitGrantOp = 'elevate' | 'revoke';
|
|
211
|
+
/**
|
|
212
|
+
* Open or close a write window for one repository (runner -> agent).
|
|
213
|
+
*
|
|
214
|
+
* `withWrite` sends `elevate` before running its callback and `revoke` in a
|
|
215
|
+
* `finally`. The grant lives in the AGENT's grant table — the credential helper
|
|
216
|
+
* git spawns is a separate process and consults the agent, not the runner —
|
|
217
|
+
* which is why this is an agent-directed IPC call rather than an orchestrator
|
|
218
|
+
* one. Mirrors the {@link CacheRequestIpc} relay pattern.
|
|
219
|
+
*/
|
|
220
|
+
export interface GitGrantRequestIpc {
|
|
221
|
+
type: 'git.grant.request';
|
|
222
|
+
/** UUID for correlating the response. */
|
|
223
|
+
requestId: string;
|
|
224
|
+
op: GitGrantOp;
|
|
225
|
+
/** `owner/repo`. `elevate` only. */
|
|
226
|
+
repository?: string;
|
|
227
|
+
/** Permissions to request from the forge. `elevate` only. */
|
|
228
|
+
permissions?: Record<string, string>;
|
|
229
|
+
/** Named credential from the job's `gitCredentials` map. `elevate` only. */
|
|
230
|
+
credentialName?: string;
|
|
231
|
+
/** Grant id returned by a previous `elevate`. `revoke` only. */
|
|
232
|
+
grantId?: string;
|
|
233
|
+
}
|
|
189
234
|
/**
|
|
190
235
|
* Request a step-level approval hold (runner -> agent). The sandbox runner
|
|
191
236
|
* blocks the step loop before a `requireApproval` step; the agent relays this
|
|
@@ -275,7 +320,7 @@ export interface ArtifactRequestIpc {
|
|
|
275
320
|
/** Storage key echoed from the grant response. `completeUpload` only. */
|
|
276
321
|
storageKey?: string;
|
|
277
322
|
}
|
|
278
|
-
export type RunnerToAgentMessage = ReadyMessage | StepStartMessage | StepCompleteMessage | LogLineMessage | StepSecretMountMessage | JobCompleteMessage | EventEmitRequest | ConcurrencyReportMessage | AgentApiRequestIpc | CacheRequestIpc | ProvenanceRequestIpc | ArtifactRequestIpc | StepApprovalRequestIpc;
|
|
323
|
+
export type RunnerToAgentMessage = ReadyMessage | StepStartMessage | StepCompleteMessage | LogLineMessage | StepSecretMountMessage | JobCompleteMessage | HooksDeclaredMessage | CompletionHooksDoneMessage | EventEmitRequest | ConcurrencyReportMessage | AgentApiRequestIpc | CacheRequestIpc | ProvenanceRequestIpc | ArtifactRequestIpc | GitGrantRequestIpc | StepApprovalRequestIpc;
|
|
279
324
|
/** Instruct the workflow runner to execute a job. */
|
|
280
325
|
interface ExecuteMessage {
|
|
281
326
|
type: 'execute';
|
|
@@ -349,6 +394,30 @@ export interface CacheResponseIpc {
|
|
|
349
394
|
/** Error description (present when the relay or orchestrator failed). */
|
|
350
395
|
error?: string;
|
|
351
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Result of a git write-grant operation (agent -> runner).
|
|
399
|
+
*
|
|
400
|
+
* `granted` reports what the forge ACTUALLY allowed, never an echo of the
|
|
401
|
+
* request — a static credential comes back unscoped because an SSH key or PAT
|
|
402
|
+
* cannot be narrowed. `error` is set when the pre-flight found the grant
|
|
403
|
+
* narrower than requested, which fails the elevation before any git runs.
|
|
404
|
+
*/
|
|
405
|
+
export interface GitGrantResponseIpc {
|
|
406
|
+
type: 'git.grant.response';
|
|
407
|
+
/** Matches the original request's requestId. */
|
|
408
|
+
requestId: string;
|
|
409
|
+
/** Grant id to pass to a later `revoke`. `elevate` only, on success. */
|
|
410
|
+
grantId?: string;
|
|
411
|
+
/** What the credential can actually do. `elevate` only, on success. */
|
|
412
|
+
granted?: {
|
|
413
|
+
scoped: false;
|
|
414
|
+
} | {
|
|
415
|
+
scoped: true;
|
|
416
|
+
permissions: Record<string, string>;
|
|
417
|
+
};
|
|
418
|
+
/** Error description (present when elevation or revocation failed). */
|
|
419
|
+
error?: string;
|
|
420
|
+
}
|
|
352
421
|
/**
|
|
353
422
|
* Resolution of a step-level approval hold (agent -> runner). Relayed from the
|
|
354
423
|
* orchestrator's `step.approval-resolved` WS message. On `approved` the runner
|
|
@@ -423,7 +492,7 @@ export interface ArtifactResponseIpc {
|
|
|
423
492
|
/** Error description (present when the relay or orchestrator failed). */
|
|
424
493
|
error?: string;
|
|
425
494
|
}
|
|
426
|
-
export type AgentToRunnerMessage = ExecuteMessage | AbortMessage | EventEmitResponse | ConcurrencyAckMessage | AgentApiResponseIpc | CacheResponseIpc | ProvenanceResponseIpc | ArtifactResponseIpc | StepApprovalResolvedIpc;
|
|
495
|
+
export type AgentToRunnerMessage = ExecuteMessage | AbortMessage | EventEmitResponse | ConcurrencyAckMessage | AgentApiResponseIpc | CacheResponseIpc | ProvenanceResponseIpc | ArtifactResponseIpc | StepApprovalResolvedIpc | GitGrantResponseIpc;
|
|
427
496
|
/**
|
|
428
497
|
* All data the workflow runner needs to execute a job inside the sandbox.
|
|
429
498
|
*
|
|
@@ -470,6 +539,15 @@ export interface JobExecutionRequest {
|
|
|
470
539
|
workflowAuth?: GitAuthDispatch;
|
|
471
540
|
/** URL to a pre-packed `.kici/` source tarball (skip clone if present). */
|
|
472
541
|
sourceTarUrl?: string;
|
|
542
|
+
/**
|
|
543
|
+
* Absolute path to the agent's git credential helper, when one is available.
|
|
544
|
+
*
|
|
545
|
+
* The runner configures it on every clone it makes, so each later git network
|
|
546
|
+
* operation asks the agent for a freshly minted credential rather than
|
|
547
|
+
* relying on one captured at clone time. Absent for a container job, whose
|
|
548
|
+
* git has no route to the agent's socket — see the dual-mode container work.
|
|
549
|
+
*/
|
|
550
|
+
credentialHelperPath?: string;
|
|
473
551
|
/** SHA-256 hash of the source tarball bytes for integrity verification. */
|
|
474
552
|
sourceTarHash?: string;
|
|
475
553
|
/** URL to pre-built dependency tarball (skip install if present). */
|
|
@@ -568,6 +646,15 @@ export interface JobExecutionRequest {
|
|
|
568
646
|
* Defaults to `apply` when unset.
|
|
569
647
|
*/
|
|
570
648
|
checkMode?: CheckMode;
|
|
649
|
+
/**
|
|
650
|
+
* Between-jobs out-of-band cleanup re-run. When true, the runner reuses the
|
|
651
|
+
* preserved workdir (no clone, no deps, no concurrency / rule / step
|
|
652
|
+
* evaluation) and runs ONLY the job's `onFailure` + `cleanup` hooks with a
|
|
653
|
+
* synthesized failed outcome — the durable re-run for a job whose runner was
|
|
654
|
+
* hard-killed before its in-band completion hooks ran. Bare-metal / in-place
|
|
655
|
+
* only; the supervisor never sets it for a normally-finished job.
|
|
656
|
+
*/
|
|
657
|
+
cleanupOnly?: boolean;
|
|
571
658
|
/** When true, skip git clone -- use overlay tarball as complete workspace. */
|
|
572
659
|
fullRepo?: boolean;
|
|
573
660
|
/** URL to download the encrypted overlay tarball (test runs with uncommitted changes). */
|
|
@@ -612,6 +699,13 @@ export interface JobExecutionRequest {
|
|
|
612
699
|
upstreamJobStatuses?: Record<string, import('@kici-dev/engine').ExecutionJobStatus>;
|
|
613
700
|
/** This job's declared upstream needs (normalized lock edges) used to shape ctx.needs for steps. */
|
|
614
701
|
jobNeeds?: readonly unknown[];
|
|
702
|
+
/**
|
|
703
|
+
* Per-invoke-gate results for any upstream invoke gate this job `needs`, keyed
|
|
704
|
+
* by gate job name. Populated for a standard `run:`/step downstream job so
|
|
705
|
+
* `ctx.needs['<gate>'].result` is an `InvokeResult[]` rather than the fan-out
|
|
706
|
+
* group shape its proxy children would otherwise imply.
|
|
707
|
+
*/
|
|
708
|
+
upstreamInvokeResults?: Record<string, import('@kici-dev/engine').InvokeResult[]>;
|
|
615
709
|
/** Resolved private npm registries for `npm install` auth (token bytes already filled). */
|
|
616
710
|
npmRegistries?: ReadonlyArray<{
|
|
617
711
|
url: string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The KiCI-provisioned runtime a container job executes with.
|
|
3
|
+
*
|
|
4
|
+
* A container job used to require the customer's image to ship Node (to run the
|
|
5
|
+
* workflow runner) and git (to clone). That coupled every image a customer might
|
|
6
|
+
* name to KiCI's own toolchain. Instead KiCI provisions its own runtime — a
|
|
7
|
+
* pinned, official glibc-2.17 Node plus the runner bundle, both already inside
|
|
8
|
+
* the published `kici-agent` image — and mounts it read-only at `/opt/kici`.
|
|
9
|
+
* The image then needs only a glibc and a shell.
|
|
10
|
+
*
|
|
11
|
+
* This module is a pure descriptor on purpose: the container-runtime calls that
|
|
12
|
+
* act on it live in the spawn helper, so the launch contract stays unit-testable
|
|
13
|
+
* without a daemon.
|
|
14
|
+
*/
|
|
15
|
+
/** Architectures the published runtime covers. */
|
|
16
|
+
export type RuntimeArch = 'x64' | 'arm64';
|
|
17
|
+
/** Read-only mount point of the KiCI-provisioned runtime inside a job container. */
|
|
18
|
+
export declare const KICI_RUNTIME_MOUNT = "/opt/kici";
|
|
19
|
+
/** Mount point of the injected Node tree inside a job container. */
|
|
20
|
+
export declare const KICI_RUNTIME_NODE_DIR = "/opt/kici/node";
|
|
21
|
+
/** The injected node executable. Absolute — never resolved from the image's PATH. */
|
|
22
|
+
export declare const KICI_RUNTIME_NODE = "/opt/kici/node/bin/node";
|
|
23
|
+
/** How to materialize the runtime into a job container. */
|
|
24
|
+
export interface RuntimeSource {
|
|
25
|
+
arch: RuntimeArch;
|
|
26
|
+
/** Where the runtime lives inside the `kici-agent` image. */
|
|
27
|
+
sourceImagePath: string;
|
|
28
|
+
/** Where it is mounted inside the job container. */
|
|
29
|
+
mountPath: string;
|
|
30
|
+
/** Always read-only: a job must not be able to rewrite the runtime it runs under. */
|
|
31
|
+
readOnly: true;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Launch the runner with the injected node, never the image's own.
|
|
35
|
+
*
|
|
36
|
+
* The customer image is not required to ship Node, so a bare `node` would
|
|
37
|
+
* resolve to nothing (or, worse, to an unrelated build).
|
|
38
|
+
*/
|
|
39
|
+
export declare function runnerLaunchArgv(runnerMountPath: string): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Describe how to materialize `/opt/kici` for a job container on `arch`.
|
|
42
|
+
*
|
|
43
|
+
* Refuses an architecture the published runtime does not cover, rather than
|
|
44
|
+
* mounting a foreign-architecture binary that surfaces mid-job as a confusing
|
|
45
|
+
* `exec format error`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resolveRuntimeSource(arch: RuntimeArch): RuntimeSource;
|
|
48
|
+
//# sourceMappingURL=kici-runtime.d.ts.map
|
|
@@ -112,6 +112,13 @@ export interface StepLoopOptions {
|
|
|
112
112
|
workflowRepo?: RepoInfo;
|
|
113
113
|
/** Job-level hooks. */
|
|
114
114
|
jobHooks?: JobHooks;
|
|
115
|
+
/**
|
|
116
|
+
* Force the completion-hook sequence down the failed path (run `onFailure`
|
|
117
|
+
* then `cleanup`, not `onSuccess`) regardless of step outcomes. Set by the
|
|
118
|
+
* between-jobs out-of-band cleanup re-run, which runs no steps but must drive
|
|
119
|
+
* the declared cleanup as if the job had failed.
|
|
120
|
+
*/
|
|
121
|
+
forceInitialFailure?: boolean;
|
|
115
122
|
/**
|
|
116
123
|
* Declarative cache phase dependencies (cache API + IPC + pseudo-step index
|
|
117
124
|
* allocator). When set, each step's own `cache` specs are restored before the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
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';
|
|
2
|
+
import type { EventEmitRequest, EventEmitResponse, ConcurrencyReportMessage, ConcurrencyAckMessage, CacheRequestIpc, GitGrantRequestIpc, GitGrantResponseIpc, CacheResponseIpc, ProvenanceRequestIpc, ProvenanceResponseIpc, ArtifactRequestIpc, ArtifactResponseIpc, StepApprovalRequestIpc, StepApprovalResolvedIpc } from './ipc-protocol.js';
|
|
3
3
|
/**
|
|
4
4
|
* Common interface for all execution sandbox backends.
|
|
5
5
|
*
|
|
@@ -41,6 +41,28 @@ export interface ExecutionSandbox {
|
|
|
41
41
|
* - Firecracker: no-op (VM lifecycle managed by scaler)
|
|
42
42
|
*/
|
|
43
43
|
teardown(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* SIGTERM → grace → SIGKILL the finished job's process group, reaping any
|
|
46
|
+
* daemon a step backgrounded. Returns the number of reap attempts that
|
|
47
|
+
* signalled a live group. Only the bare-metal backend reaps a process group;
|
|
48
|
+
* others (which reap their whole tree on teardown) omit this.
|
|
49
|
+
*/
|
|
50
|
+
reap?(): Promise<number>;
|
|
51
|
+
/**
|
|
52
|
+
* Whether the runner signalled that its completion hooks ran before it exited.
|
|
53
|
+
* `false` means the runner was hard-killed before running declared cleanup —
|
|
54
|
+
* the between-jobs phase's cue to re-run it out-of-band. Absent ⇒ treated as
|
|
55
|
+
* `true` (no re-run) for backends that reap their whole tree.
|
|
56
|
+
*/
|
|
57
|
+
readonly completionHooksRan?: boolean;
|
|
58
|
+
/** Whether the job declares an `onFailure` / `cleanup` hook (bare-metal). */
|
|
59
|
+
readonly declaresCleanup?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Re-run the finished job's declared cleanup / onFailure hooks out-of-band,
|
|
62
|
+
* against the preserved workdir, in a fresh bounded child. Resolves on success
|
|
63
|
+
* and rejects on failure so the caller can time it out. Bare-metal only.
|
|
64
|
+
*/
|
|
65
|
+
runCleanupOnly?(workDir: string, signal: AbortSignal): Promise<void>;
|
|
44
66
|
}
|
|
45
67
|
/** Options for preparing the sandbox environment. */
|
|
46
68
|
export interface SandboxSetupOptions {
|
|
@@ -59,6 +81,19 @@ export interface SandboxSetupOptions {
|
|
|
59
81
|
* inside `executeJob`, so it ignores this field.
|
|
60
82
|
*/
|
|
61
83
|
extraReadOnlyBinds?: string[];
|
|
84
|
+
/**
|
|
85
|
+
* Populate the sandbox workspace from `workDir` instead of letting the runner
|
|
86
|
+
* clone inside it.
|
|
87
|
+
*
|
|
88
|
+
* Set when the AGENT already cloned on the host. The container backend packs
|
|
89
|
+
* `workDir` and copies it into the container's `/workspace` volume; the
|
|
90
|
+
* bare-metal backend already runs against `workDir` directly and ignores it.
|
|
91
|
+
*
|
|
92
|
+
* Cloning on the host is what lets a container image ship without git — and
|
|
93
|
+
* it puts clone-time credentials on the host, where the credential helper
|
|
94
|
+
* already works, instead of needing a route into a hardened container.
|
|
95
|
+
*/
|
|
96
|
+
workspaceFromHost?: boolean;
|
|
62
97
|
}
|
|
63
98
|
/** Options for executing a job inside the sandbox. */
|
|
64
99
|
export interface JobExecutionOptions {
|
|
@@ -101,6 +136,25 @@ export interface JobExecutionOptions {
|
|
|
101
136
|
* working — the runner falls back to a "not configured" cache response.
|
|
102
137
|
*/
|
|
103
138
|
onCacheRequest?: (request: CacheRequestIpc) => Promise<CacheResponseIpc>;
|
|
139
|
+
/**
|
|
140
|
+
* Callback for opening or closing a git write grant on behalf of the sandbox.
|
|
141
|
+
*
|
|
142
|
+
* The grant lives in the AGENT's grant table because the credential helper
|
|
143
|
+
* git spawns is a separate process that consults the agent, not the sandbox
|
|
144
|
+
* runner. Optional so harnesses that don't thread git credentials keep
|
|
145
|
+
* working — the runner falls back to a "not configured" error response.
|
|
146
|
+
*/
|
|
147
|
+
onGitGrantRequest?: (request: GitGrantRequestIpc) => Promise<GitGrantResponseIpc>;
|
|
148
|
+
/**
|
|
149
|
+
* Absolute path to the agent's git credential helper.
|
|
150
|
+
*
|
|
151
|
+
* Threaded into the execution request so the runner configures it on every
|
|
152
|
+
* clone. Set for the bare-metal (fork) backend, whose runner is a host
|
|
153
|
+
* process that can reach the agent's socket. Deliberately NOT set by the
|
|
154
|
+
* container backend: git runs inside the container and has no route to it —
|
|
155
|
+
* see the dual-mode container work.
|
|
156
|
+
*/
|
|
157
|
+
credentialHelperPath?: string;
|
|
104
158
|
/**
|
|
105
159
|
* Callback for relaying a provenance bundle upload request from the sandbox to
|
|
106
160
|
* the orchestrator. The sandbox runner sends `provenance.request` IPC; the
|
|
@@ -17,13 +17,23 @@ import { $ } from 'zx';
|
|
|
17
17
|
import { type TempScope } from '@kici-dev/core/tmp';
|
|
18
18
|
import { ExecutionJobStatus } from '@kici-dev/engine';
|
|
19
19
|
import type { Step, StepInput, StepContext, RepoInfo, EventPayload } from '@kici-dev/sdk';
|
|
20
|
-
import type { NeedsContext, FanoutPosition, EventDefinition } from '@kici-dev/sdk';
|
|
20
|
+
import type { NeedsContext, FanoutPosition, EventDefinition, InvokeResult } from '@kici-dev/sdk';
|
|
21
21
|
import type { OutputsMap, StepRefMap, TrackedStepSecrets } from '@kici-dev/sdk';
|
|
22
|
-
import type { RunnerToAgentMessage, JobExecutionRequest } from './ipc-protocol.js';
|
|
22
|
+
import type { RunnerToAgentMessage, JobExecutionRequest, GitGrantResponseIpc } from './ipc-protocol.js';
|
|
23
23
|
import { LogMasker } from './log-masker.js';
|
|
24
24
|
import type { StepLoopOptions } from './step-loop.js';
|
|
25
25
|
import type { GeneratorRepoPair } from '../generator-context.js';
|
|
26
26
|
import { type RuleEvaluationResult } from '../rule-evaluator.js';
|
|
27
|
+
/**
|
|
28
|
+
* Open a write window for one repository, run `fn`, and close it.
|
|
29
|
+
*
|
|
30
|
+
* The revoke rides a `finally`, so a throwing callback still closes the
|
|
31
|
+
* window — and the agent's TTL is the backstop if this process dies outright.
|
|
32
|
+
*/
|
|
33
|
+
export declare function withRepoWrite(repository: string, opts: {
|
|
34
|
+
permissions?: Record<string, string>;
|
|
35
|
+
credential?: string;
|
|
36
|
+
}, fn: () => Promise<void>, send: (msg: RunnerToAgentMessage) => void, wait: (requestId: string) => Promise<GitGrantResponseIpc>): Promise<void>;
|
|
27
37
|
/**
|
|
28
38
|
* Build `ctx.needs` for a job's steps from the dispatch envelope. Reconstructs
|
|
29
39
|
* an {@link UpstreamSnapshot} from `upstreamJobOutputs` (flat per single job;
|
|
@@ -32,7 +42,7 @@ import { type RuleEvaluationResult } from '../rule-evaluator.js';
|
|
|
32
42
|
* `{ result, status }` / ordered-array shape via the shared SDK builder. Returns
|
|
33
43
|
* undefined when the job declares no needs.
|
|
34
44
|
*/
|
|
35
|
-
export declare function buildStepNeedsContext(declaredNeeds: readonly unknown[] | undefined, upstreamJobOutputs: Record<string, Record<string, unknown>> | undefined, upstreamJobStatuses: Record<string, ExecutionJobStatus> | undefined): NeedsContext | undefined;
|
|
45
|
+
export declare function buildStepNeedsContext(declaredNeeds: readonly unknown[] | undefined, upstreamJobOutputs: Record<string, Record<string, unknown>> | undefined, upstreamJobStatuses: Record<string, ExecutionJobStatus> | undefined, upstreamInvokeResults?: Record<string, InvokeResult[]>): NeedsContext | undefined;
|
|
36
46
|
/**
|
|
37
47
|
* Build a fresh zx `$` shell bound to the sandbox working directory and the
|
|
38
48
|
* sanitized environment (process.env was set by the parent via env-sanitizer
|
|
@@ -71,6 +81,16 @@ export declare function buildSandboxShell(cwd: string, stepIndex: number, masked
|
|
|
71
81
|
* emit overload); a definition resolves to its `.name`, a string passes through.
|
|
72
82
|
*/
|
|
73
83
|
export declare function resolveEmitEventName(nameOrDefinition: string | EventDefinition): string;
|
|
84
|
+
/**
|
|
85
|
+
* Reject a user `ctx.emit` whose event name uses a reserved prefix: `kici.`
|
|
86
|
+
* (KiCI-internal system events — the event scaler's scale-up / scale-down) or
|
|
87
|
+
* `__` (the events the orchestrator mints for itself). A workflow step must
|
|
88
|
+
* forge neither — a `__` name is dispatched as a TRUSTED ref and skips the
|
|
89
|
+
* event-storm rate limiter, so emitting one would be a privilege escalation,
|
|
90
|
+
* not merely a naming collision. Throws a clear error at the emit call; the
|
|
91
|
+
* orchestrator enforces the same reservation authoritatively.
|
|
92
|
+
*/
|
|
93
|
+
export declare function assertUserEmittableEventName(eventName: string): void;
|
|
74
94
|
/**
|
|
75
95
|
* Sanitize a raw identifier into a valid temp label: lowercase, every
|
|
76
96
|
* non-`[a-z0-9-]` char to `-`, falling back to `'step'` when the result is
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a scaler-managed agent does about its idle-shutdown timer when
|
|
3
|
+
* `register.ack` arrives.
|
|
4
|
+
*
|
|
5
|
+
* This lives apart from `server.ts` so the rule has exactly one
|
|
6
|
+
* implementation: the server acts on the verdict, and the tests assert on the
|
|
7
|
+
* verdict. Importing `server.ts` starts the agent (it ends in a top-level
|
|
8
|
+
* `guardStartup`), so a function exported from there could only ever be tested
|
|
9
|
+
* by a copy of itself.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* - `none` — do nothing: either the agent is not scaler-managed, or it is busy.
|
|
13
|
+
* - `warm` — pre-spawned to wait for work; disarm any timer and stay up.
|
|
14
|
+
* - `pending-dispatch` — a bound job is on its way; arm the long safety timeout.
|
|
15
|
+
* - `idle` — arm the ordinary scaler-idle timer.
|
|
16
|
+
*/
|
|
17
|
+
export type IdleShutdownDecision = 'none' | 'warm' | 'pending-dispatch' | 'idle';
|
|
18
|
+
export declare function decideIdleShutdown(input: {
|
|
19
|
+
scalerManaged: boolean;
|
|
20
|
+
activeJobs: number;
|
|
21
|
+
pendingDispatch?: boolean;
|
|
22
|
+
warmPool?: boolean;
|
|
23
|
+
}): IdleShutdownDecision;
|
|
24
|
+
//# sourceMappingURL=idle-shutdown.d.ts.map
|