@kici-dev/agent 0.4.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/download.d.ts +27 -2
- package/dist/execution/dynamic-job-serializer.d.ts +6 -2
- package/dist/execution/generator-context.d.ts +54 -0
- package/dist/execution/global-eval-runner.d.ts +92 -0
- package/dist/execution/global-workflow-env.d.ts +57 -0
- 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/init-runner.d.ts +60 -3
- package/dist/execution/job-runner.d.ts +146 -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 +17 -1
- package/dist/execution/sandbox/types.d.ts +55 -1
- package/dist/execution/sandbox/workflow-runner.d.ts +75 -4
- package/dist/execution/workflow-loader.d.ts +8 -1
- package/dist/idle-shutdown.d.ts +24 -0
- package/dist/index.js +221 -80
- package/dist/metrics/prometheus.d.ts +36 -10
- package/dist/server.js +3197 -462
- package/dist/workflow-runner-bundle.js +42207 -41104
- package/dist/workflow-runner.js +594 -196
- package/dist/ws/orchestrator-client.d.ts +95 -3
- package/package.json +10 -10
- package/sbom.spdx.json +464 -454
|
@@ -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
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Extracted for testability: the workflow-runner's main() handles IPC, clone, deps,
|
|
6
6
|
* module loading, and calls this loop for step execution with hooks.
|
|
7
7
|
*/
|
|
8
|
-
import type { Step, StepContext, HookInput, OutputsMap, StepSecretMountRecord, FanoutPosition } from '@kici-dev/sdk';
|
|
8
|
+
import type { Step, StepContext, HookInput, OutputsMap, StepSecretMountRecord, FanoutPosition, RepoInfo } from '@kici-dev/sdk';
|
|
9
9
|
import { CheckMode } from '@kici-dev/engine';
|
|
10
10
|
import type { RunnerToAgentMessage } from './ipc-protocol.js';
|
|
11
11
|
import type { SandboxStepResult } from './types.js';
|
|
@@ -101,8 +101,24 @@ export interface StepLoopOptions {
|
|
|
101
101
|
dispatchInputs?: Readonly<Record<string, string | number | boolean | null>>;
|
|
102
102
|
/** Fan-out position for the rule context (`ctx.fanout`); undefined on a non-fan-out job. */
|
|
103
103
|
fanout?: FanoutPosition;
|
|
104
|
+
/**
|
|
105
|
+
* The repo whose event triggered this run, for the step rule context
|
|
106
|
+
* (`ctx.sourceRepo`). Present for a global workflow, absent otherwise — a
|
|
107
|
+
* step rule reads the source tree through `.path` the same way a job rule
|
|
108
|
+
* and a generator do.
|
|
109
|
+
*/
|
|
110
|
+
sourceRepo?: RepoInfo;
|
|
111
|
+
/** The repo that registered the workflow, for the step rule context (`ctx.workflowRepo`). */
|
|
112
|
+
workflowRepo?: RepoInfo;
|
|
104
113
|
/** Job-level hooks. */
|
|
105
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;
|
|
106
122
|
/**
|
|
107
123
|
* Declarative cache phase dependencies (cache API + IPC + pseudo-step index
|
|
108
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
|
|
@@ -16,12 +16,24 @@
|
|
|
16
16
|
import { $ } from 'zx';
|
|
17
17
|
import { type TempScope } from '@kici-dev/core/tmp';
|
|
18
18
|
import { ExecutionJobStatus } from '@kici-dev/engine';
|
|
19
|
-
import type { Step, StepInput, StepContext } from '@kici-dev/sdk';
|
|
20
|
-
import type { NeedsContext, FanoutPosition, EventDefinition } from '@kici-dev/sdk';
|
|
19
|
+
import type { Step, StepInput, StepContext, RepoInfo, EventPayload } 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
|
+
import type { StepLoopOptions } from './step-loop.js';
|
|
25
|
+
import type { GeneratorRepoPair } from '../generator-context.js';
|
|
24
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>;
|
|
25
37
|
/**
|
|
26
38
|
* Build `ctx.needs` for a job's steps from the dispatch envelope. Reconstructs
|
|
27
39
|
* an {@link UpstreamSnapshot} from `upstreamJobOutputs` (flat per single job;
|
|
@@ -30,7 +42,7 @@ import { type RuleEvaluationResult } from '../rule-evaluator.js';
|
|
|
30
42
|
* `{ result, status }` / ordered-array shape via the shared SDK builder. Returns
|
|
31
43
|
* undefined when the job declares no needs.
|
|
32
44
|
*/
|
|
33
|
-
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;
|
|
34
46
|
/**
|
|
35
47
|
* Build a fresh zx `$` shell bound to the sandbox working directory and the
|
|
36
48
|
* sanitized environment (process.env was set by the parent via env-sanitizer
|
|
@@ -69,6 +81,16 @@ export declare function buildSandboxShell(cwd: string, stepIndex: number, masked
|
|
|
69
81
|
* emit overload); a definition resolves to its `.name`, a string passes through.
|
|
70
82
|
*/
|
|
71
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;
|
|
72
94
|
/**
|
|
73
95
|
* Sanitize a raw identifier into a valid temp label: lowercase, every
|
|
74
96
|
* non-`[a-z0-9-]` char to `-`, falling back to `'step'` when the result is
|
|
@@ -103,6 +125,42 @@ export declare function createSandboxStepContext(workDir: string, stepIndex: num
|
|
|
103
125
|
export declare function deriveFanout(request: JobExecutionRequest): FanoutPosition | undefined;
|
|
104
126
|
/** Raw provider webhook body for ctx.rawPayload — nested in the envelope. */
|
|
105
127
|
export declare function rawPayloadFromEvent(event: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
|
|
128
|
+
/**
|
|
129
|
+
* Build the argument the user's `concurrency.group(...)` function receives.
|
|
130
|
+
*
|
|
131
|
+
* The only inputs an author can scope a group by. `branch` alone does not
|
|
132
|
+
* separate one repository from another — an organization-wide workflow runs on
|
|
133
|
+
* events from many repositories, and their default branches share a name — so
|
|
134
|
+
* `event.sourceRepo` is what makes a per-source-repository group expressible.
|
|
135
|
+
* That is why the orchestrator writes the whole normalized envelope into every
|
|
136
|
+
* global job config: an empty `event` here silently collapses every repository
|
|
137
|
+
* into one group, and with `cancelInProgress` (the default) one repository's
|
|
138
|
+
* push then cancels another's in-flight run.
|
|
139
|
+
*
|
|
140
|
+
* Boundary cast: the wire `request.event` is untyped JSON that, per the unified
|
|
141
|
+
* event protocol, always carries the normalized event envelope.
|
|
142
|
+
*/
|
|
143
|
+
export declare function buildConcurrencyGroupContext(request: JobExecutionRequest): {
|
|
144
|
+
branch: string;
|
|
145
|
+
event: EventPayload;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Phase 5 — Inject env vars and build the `RepoInfo` pair that the generator,
|
|
149
|
+
* the job rules, and step contexts receive when the job is a global workflow.
|
|
150
|
+
* No-op for normal jobs.
|
|
151
|
+
*
|
|
152
|
+
* Runs at the head of phase 5, before anything that may read the source tree:
|
|
153
|
+
* the generator's re-evaluation (phase 5) and the job rules (phase 7) both take
|
|
154
|
+
* the returned pair, and both must see what the pre-dispatch evaluation saw.
|
|
155
|
+
*
|
|
156
|
+
* `sourceRepo.path` is this sandbox's own absolute path — the same repo lives at
|
|
157
|
+
* a different path in the evaluation that produced the job list. Read through
|
|
158
|
+
* it; never compare it or embed it in a job name.
|
|
159
|
+
*/
|
|
160
|
+
export declare function setupGlobalWorkflowEnv(request: JobExecutionRequest, isGlobal: boolean, workflowDir: string, sourceDir: string): {
|
|
161
|
+
workflowRepo: RepoInfo;
|
|
162
|
+
sourceRepo: RepoInfo;
|
|
163
|
+
} | undefined;
|
|
106
164
|
/**
|
|
107
165
|
* Mutable state threaded through {@link coerceStep} for one job normalization
|
|
108
166
|
* pass: the shared `step-N` counter, the bare-function → name ref map, and the
|
|
@@ -150,6 +208,19 @@ export declare function resolveChangedFilesForRules(request: JobExecutionRequest
|
|
|
150
208
|
export declare function buildJobRuleCompletion(ruleResult: RuleEvaluationResult, normalizedSteps: Step[]): (RunnerToAgentMessage & {
|
|
151
209
|
type: 'job.complete';
|
|
152
210
|
}) | null;
|
|
211
|
+
/**
|
|
212
|
+
* Build the inputs the step loop turns into every step rule's `RuleContext`.
|
|
213
|
+
*
|
|
214
|
+
* Shares its source with `maybeSkipJobOnRules` so a step rule and a job rule
|
|
215
|
+
* see the same world: the same event, env, dispatch inputs, fan-out position,
|
|
216
|
+
* and — for a global workflow — the same source / workflow repo pair. A step
|
|
217
|
+
* rule that received the pair as `undefined` while the job rule beside it
|
|
218
|
+
* received the real thing would read the same `RuleContext` type two ways.
|
|
219
|
+
*
|
|
220
|
+
* The pair is spread conditionally: a present-but-undefined `sourceRepo` reads
|
|
221
|
+
* as "declared" to a rule that guards on the key rather than the value.
|
|
222
|
+
*/
|
|
223
|
+
export declare function buildStepLoopRuleInputs(request: JobExecutionRequest, repos: GeneratorRepoPair | undefined): Pick<StepLoopOptions, 'event' | 'env' | 'dispatchInputs' | 'fanout' | 'sourceRepo' | 'workflowRepo'>;
|
|
153
224
|
/**
|
|
154
225
|
* Build the step loop's KICI_ENV/KICI_PATH callbacks with a per-step delta-file
|
|
155
226
|
* pair (keyed by step index). `beforeStepEnvFiles(stepIndex)` lazily creates the
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Node's normal ESM lookup against `.kici/node_modules/`.
|
|
8
8
|
*/
|
|
9
9
|
import type { Workflow, StepInput, DynamicJobFn, OutputsMap, StepRefMap } from '@kici-dev/sdk';
|
|
10
|
+
import { type GeneratorRepoPair } from './generator-context.js';
|
|
10
11
|
/**
|
|
11
12
|
* SDK output-map setters resolved from a specific `@kici-dev/sdk` instance.
|
|
12
13
|
* The agent uses these to wire the workflow module's OWN SDK copy (a different
|
|
@@ -99,7 +100,13 @@ export declare function extractStepsFromDynamicJob(workflow: Workflow, dynamicIn
|
|
|
99
100
|
/** Frozen upstream snapshot for a result-aware generator (rebuilds ctx.needs). */
|
|
100
101
|
upstreamSnapshot?: import('@kici-dev/engine').UpstreamSnapshot,
|
|
101
102
|
/** Declared upstream needs that shape ctx.needs. */
|
|
102
|
-
declaredNeeds?: readonly unknown[]
|
|
103
|
+
declaredNeeds?: readonly unknown[],
|
|
104
|
+
/**
|
|
105
|
+
* The source / workflow repo pair for a global workflow. Must match what the
|
|
106
|
+
* first evaluation saw, or a generator that reads the source tree produces a
|
|
107
|
+
* different job list here and the determinism check below fails the job.
|
|
108
|
+
*/
|
|
109
|
+
repos?: GeneratorRepoPair): Promise<{
|
|
103
110
|
steps: readonly StepInput[];
|
|
104
111
|
droppedJobs: string[];
|
|
105
112
|
}>;
|
|
@@ -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
|