@kici-dev/agent 0.1.21 → 0.1.23
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/api-intercept.d.ts +20 -0
- package/dist/bootstrap/ensure-init-runner.d.ts +39 -0
- package/dist/bootstrap/pre-boot-send.d.ts +25 -0
- package/dist/bootstrap/reach.d.ts +17 -0
- package/dist/bootstrap/ssh-exec.d.ts +58 -0
- package/dist/config.d.ts +2 -0
- package/dist/execution/job-runner.d.ts +22 -0
- package/dist/execution/reboot.d.ts +26 -0
- package/dist/execution/rule-evaluator.d.ts +4 -2
- package/dist/execution/sandbox/ipc-protocol.d.ts +24 -1
- package/dist/execution/sandbox/step-loop.d.ts +32 -6
- package/dist/execution/sandbox/workflow-runner.d.ts +17 -0
- package/dist/index.js +3 -1
- package/dist/server.js +486 -54
- package/dist/workflow-runner.js +234 -52
- package/package.json +5 -5
- package/sbom.spdx.json +62 -62
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-process interception of the bootstrap bring-up API methods.
|
|
3
|
+
*
|
|
4
|
+
* `ctx.kici.bootstrap.ensureInitRunner` / `preBootSend` are relayed from the
|
|
5
|
+
* workflow sandbox as `agent.api.request` IPC. The agent process intercepts
|
|
6
|
+
* those two methods HERE (rather than relaying them straight to the
|
|
7
|
+
* orchestrator) so the SSH transport — and the bring-up key / bootstrap token
|
|
8
|
+
* the orchestrator hands back — stay in the agent process, never reaching user
|
|
9
|
+
* workflow code. Every other method falls through to the orchestrator relay
|
|
10
|
+
* unchanged.
|
|
11
|
+
*/
|
|
12
|
+
import { type ApiTransport, type EnsureInitRunnerDeps } from './ensure-init-runner.js';
|
|
13
|
+
/**
|
|
14
|
+
* Wrap the orchestrator API transport so the two bootstrap methods are handled
|
|
15
|
+
* in-process (SSH transport here; privileged resolve relayed to the
|
|
16
|
+
* orchestrator). `relay` is the raw orchestrator transport (the WS
|
|
17
|
+
* `sendApiRequest`).
|
|
18
|
+
*/
|
|
19
|
+
export declare function withBootstrapInterception(relay: ApiTransport, deps?: EnsureInitRunnerDeps): (method: string, params?: Record<string, unknown>) => Promise<unknown>;
|
|
20
|
+
//# sourceMappingURL=api-intercept.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-side init-runner bring-up.
|
|
3
|
+
*
|
|
4
|
+
* Runs in the AGENT process (never the workflow sandbox), so the bring-up SSH
|
|
5
|
+
* key and bootstrap token the orchestrator hands back never reach user
|
|
6
|
+
* workflow code. The flow:
|
|
7
|
+
*
|
|
8
|
+
* 1. Call the orchestrator's privileged `kici.ensureInitRunner` handler — it
|
|
9
|
+
* gates on this agent's `kici:capability:ssh-transport` capability,
|
|
10
|
+
* resolves the target's reach + SSH key, mints a single-use bootstrap
|
|
11
|
+
* token, audits, and returns the material (or `{ broughtUp: false }` when
|
|
12
|
+
* the target already has a live agent).
|
|
13
|
+
* 2. Over SSH (ephemeral key, never on disk): drop a launcher onto the target
|
|
14
|
+
* that starts `kici-agent` with the bootstrap env (token + agent id +
|
|
15
|
+
* orchestrator URL + labels), and start it detached.
|
|
16
|
+
*
|
|
17
|
+
* The init-runner then connects → `auth.request` (bootstrap token) →
|
|
18
|
+
* `agent.register` auto-enroll as a temporary `kici:init` agent.
|
|
19
|
+
*/
|
|
20
|
+
import { type SshDeps } from './ssh-exec.js';
|
|
21
|
+
/** Transport that relays an API request to the orchestrator and awaits the result. */
|
|
22
|
+
export type ApiTransport = (method: string, params: Record<string, unknown>) => Promise<unknown>;
|
|
23
|
+
export interface EnsureInitRunnerDeps extends SshDeps {
|
|
24
|
+
/**
|
|
25
|
+
* The command used to start the init-runner on the target. Defaults to
|
|
26
|
+
* `kici-agent` (resolved on the target's PATH). Override for a rescue env
|
|
27
|
+
* that stages the binary at a fixed path.
|
|
28
|
+
*/
|
|
29
|
+
agentCommand?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Bring up a temporary init-runner on `targetAgentId`. Returns `{ broughtUp }`:
|
|
33
|
+
* false when the target already had a live agent (the orchestrator no-op'd),
|
|
34
|
+
* true when this call dropped + started the init-runner.
|
|
35
|
+
*/
|
|
36
|
+
export declare function ensureInitRunner(transport: ApiTransport, targetAgentId: string, deps?: EnsureInitRunnerDeps): Promise<{
|
|
37
|
+
broughtUp: boolean;
|
|
38
|
+
}>;
|
|
39
|
+
//# sourceMappingURL=ensure-init-runner.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-side pre-boot input send (dropbear / initramfs unlock).
|
|
3
|
+
*
|
|
4
|
+
* Runs in the AGENT process. Asks the orchestrator's privileged
|
|
5
|
+
* `kici.preBootSend` handler to gate on the `kici:capability:ssh-transport`
|
|
6
|
+
* capability, resolve the input secret (e.g. a LUKS passphrase), and audit;
|
|
7
|
+
* then pipes the resolved input to the target's pre-boot SSH endpoint
|
|
8
|
+
* (default dropbear port 2222, forced `cryptroot-unlock`). The unlock drops
|
|
9
|
+
* the session as the box boots — success is the send completing, not the SSH
|
|
10
|
+
* exit code; the caller composes a host-alive wait to confirm boot.
|
|
11
|
+
*/
|
|
12
|
+
import { type SshDeps } from './ssh-exec.js';
|
|
13
|
+
import type { ApiTransport } from './ensure-init-runner.js';
|
|
14
|
+
export interface PreBootSendOpts {
|
|
15
|
+
inputSecret: string;
|
|
16
|
+
port?: number;
|
|
17
|
+
command?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Ship a pre-boot input to the target's dropbear/initramfs SSH channel. The
|
|
21
|
+
* input plaintext is resolved server-side and never logged. Resolves once the
|
|
22
|
+
* send completes (the SSH session legitimately drops as the box boots).
|
|
23
|
+
*/
|
|
24
|
+
export declare function preBootSend(transport: ApiTransport, targetAgentId: string, opts: PreBootSendOpts, deps?: SshDeps): Promise<void>;
|
|
25
|
+
//# sourceMappingURL=pre-boot-send.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-facing host reach metadata: how to SSH to a target for bootstrap.
|
|
3
|
+
*
|
|
4
|
+
* This is the subset the agent's SSH helper needs — the connection
|
|
5
|
+
* coordinates only. It deliberately does NOT carry the `sshKeySecret` ref:
|
|
6
|
+
* the orchestrator resolves the scoped secret server-side and hands the agent
|
|
7
|
+
* the already-resolved private key separately, so the secret ref never reaches
|
|
8
|
+
* the agent. `sshPort` is the OS-sshd port for bring-up; a pre-boot dropbear
|
|
9
|
+
* port is supplied per-call via `SshExecOpts.port`.
|
|
10
|
+
*/
|
|
11
|
+
export interface HostReach {
|
|
12
|
+
agentId: string;
|
|
13
|
+
address: string | null;
|
|
14
|
+
sshUser: string | null;
|
|
15
|
+
sshPort: number | null;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=reach.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { HostReach } from './reach.js';
|
|
2
|
+
/** Host-key verification mode for the SSH connection. */
|
|
3
|
+
export type SshHostKeyMode = 'accept-new' | 'strict';
|
|
4
|
+
/** Result of an SSH invocation: exit code is reported, never thrown away. */
|
|
5
|
+
export interface SshResult {
|
|
6
|
+
exitCode: number;
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SshExecOpts {
|
|
11
|
+
/** Piped to the remote command's stdin (e.g. a LUKS passphrase to cryptroot-unlock). */
|
|
12
|
+
stdin?: string;
|
|
13
|
+
/** Override the default SSH port (22). E.g. 2222 for a dropbear initramfs prompt. */
|
|
14
|
+
port?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Host-key verification. Defaults to `accept-new` — the dropbear initramfs
|
|
17
|
+
* host key differs from the OS sshd key, so a pinned OS entry must not be
|
|
18
|
+
* reused. `strict` enforces a known_hosts match.
|
|
19
|
+
*/
|
|
20
|
+
hostKeyMode?: SshHostKeyMode;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Low-level process spawn boundary, injectable so unit tests can assert the
|
|
24
|
+
* exact argv / env / stdin without running a real `ssh`. Mirrors the relevant
|
|
25
|
+
* subset of `child_process.spawn`'s contract.
|
|
26
|
+
*/
|
|
27
|
+
export interface SpawnFn {
|
|
28
|
+
(command: string, args: string[], opts: {
|
|
29
|
+
env: NodeJS.ProcessEnv;
|
|
30
|
+
stdin?: string;
|
|
31
|
+
}): Promise<SshResult>;
|
|
32
|
+
}
|
|
33
|
+
/** Default spawn boundary backed by `node:child_process.spawn`. */
|
|
34
|
+
export declare const defaultSpawn: SpawnFn;
|
|
35
|
+
/** Deps for the SSH helper — the spawn boundary is injectable for tests. */
|
|
36
|
+
export interface SshDeps {
|
|
37
|
+
spawnFn?: SpawnFn;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Run a command on the target over SSH using an ephemeral, in-memory key.
|
|
41
|
+
*
|
|
42
|
+
* The key is loaded into a per-call ssh-agent (never written to disk) and the
|
|
43
|
+
* agent is killed in `finally`. The remote command's exit code / stdout /
|
|
44
|
+
* stderr are returned verbatim — a non-zero exit is reported, not swallowed
|
|
45
|
+
* (a pre-boot unlock legitimately drops the session, so the caller decides
|
|
46
|
+
* what a "success" looks like).
|
|
47
|
+
*/
|
|
48
|
+
export declare function sshExec(reach: HostReach, privateKey: string, command: string, opts?: SshExecOpts, deps?: SshDeps): Promise<SshResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Ship local bytes to a remote path over SSH (`ssh 'cat > path'`), using the
|
|
51
|
+
* same ephemeral-key discipline. Throws on a non-zero exit (a push must
|
|
52
|
+
* succeed end-to-end, unlike a pre-boot unlock).
|
|
53
|
+
*/
|
|
54
|
+
export declare function sshPush(reach: HostReach, privateKey: string, localBytes: string, remotePath: string, opts?: {
|
|
55
|
+
port?: number;
|
|
56
|
+
hostKeyMode?: SshHostKeyMode;
|
|
57
|
+
}, deps?: SshDeps): Promise<void>;
|
|
58
|
+
//# sourceMappingURL=ssh-exec.d.ts.map
|
package/dist/config.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
44
44
|
}>>;
|
|
45
45
|
otelExporterOtlpEndpoint: z.ZodOptional<z.ZodString>;
|
|
46
46
|
concurrencyWaitTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
47
|
+
isOrchestratorHost: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<boolean, string | undefined>>;
|
|
47
48
|
}, z.core.$strip>;
|
|
48
49
|
/**
|
|
49
50
|
* App configuration type. Includes computed agentId when not provided.
|
|
@@ -75,6 +76,7 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
|
75
76
|
scalerIdleTimeoutMs: number;
|
|
76
77
|
scalerPendingDispatchTimeoutMs: number;
|
|
77
78
|
concurrencyWaitTimeoutMs: number;
|
|
79
|
+
isOrchestratorHost: boolean;
|
|
78
80
|
agentId?: string | undefined;
|
|
79
81
|
agentToken?: string | undefined;
|
|
80
82
|
githubToken?: string | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentToOrchestratorMessage, JobDispatch } from '@kici-dev/engine';
|
|
2
2
|
import type { AppConfig } from '../config.js';
|
|
3
|
+
import { buildNeedsContext } from '@kici-dev/sdk';
|
|
3
4
|
import type { CacheRequestIpc, CacheResponseIpc, ProvenanceRequestIpc, ProvenanceResponseIpc, StepApprovalRequestIpc, StepApprovalResolvedIpc } from './sandbox/index.js';
|
|
4
5
|
/**
|
|
5
6
|
* Dependencies injected into JobRunner.
|
|
@@ -112,6 +113,15 @@ interface ActiveJob {
|
|
|
112
113
|
completionPromise: Promise<void>;
|
|
113
114
|
runId: string;
|
|
114
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Build the result-aware `ctx.needs` for a dynamic eval from its frozen upstream
|
|
118
|
+
* snapshot. Returns undefined for an event-only generator (no snapshot).
|
|
119
|
+
*/
|
|
120
|
+
export declare function buildEvalNeedsContext(config: {
|
|
121
|
+
resultAware?: boolean;
|
|
122
|
+
declaredNeeds?: readonly unknown[];
|
|
123
|
+
upstreamSnapshot?: import('@kici-dev/engine').UpstreamSnapshot;
|
|
124
|
+
}): ReturnType<typeof buildNeedsContext> | undefined;
|
|
115
125
|
/**
|
|
116
126
|
* Top-level job execution orchestrator for the agent.
|
|
117
127
|
*
|
|
@@ -205,6 +215,18 @@ export declare class JobRunner {
|
|
|
205
215
|
* failure diagnostics, and send the terminal `job.status` message.
|
|
206
216
|
*/
|
|
207
217
|
private reportExecutionResult;
|
|
218
|
+
/**
|
|
219
|
+
* Handle a bring-up job: bring up a temporary init-runner on a declared-but-
|
|
220
|
+
* un-agented host over SSH (fresh-box bootstrap convergence). The orchestrator
|
|
221
|
+
* dispatches this synthetic `__bringup__` job to an agent holding the
|
|
222
|
+
* `kici:capability:ssh-transport` capability; here we run the agent-side
|
|
223
|
+
* `ensureInitRunner` helper (the privileged resolve is relayed to the
|
|
224
|
+
* orchestrator, the SSH transport happens in this agent process — never
|
|
225
|
+
* reaching workflow code). No clone, no sandbox — the init-runner then
|
|
226
|
+
* connects under the target's agent id and the orchestrator's pinned-hold
|
|
227
|
+
* drains the target's bootstrap steps onto it.
|
|
228
|
+
*/
|
|
229
|
+
private handleBringupJob;
|
|
208
230
|
/**
|
|
209
231
|
* Handle a build-only job.
|
|
210
232
|
*
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-OS host reboot for the workflow-level `restartHost()` step.
|
|
3
|
+
*
|
|
4
|
+
* The agent runs ON the host it executes jobs for, so a `restartHost()` step
|
|
5
|
+
* reboots that host. `rebootCommandFor` is the pure OS→command mapping (kept
|
|
6
|
+
* pure for unit-testing); `issueReboot` spawns it detached with a tiny grace so
|
|
7
|
+
* the job's final flush completes before the box goes down.
|
|
8
|
+
*
|
|
9
|
+
* Rebooting needs host privilege (root/admin). If the primitive is denied, the
|
|
10
|
+
* spawn fails and the caller clears the orchestrator's reboot-pending flag and
|
|
11
|
+
* surfaces the error — the deadline sweep is the backstop.
|
|
12
|
+
*/
|
|
13
|
+
/** The OS reboot primitive for a Node platform string. */
|
|
14
|
+
export declare function rebootCommandFor(platform: NodeJS.Platform): {
|
|
15
|
+
cmd: string;
|
|
16
|
+
args: string[];
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Issue the OS reboot detached. Resolves once the child has been spawned (the
|
|
20
|
+
* box is going down; there is nothing to await). Rejects synchronously if the
|
|
21
|
+
* spawn itself fails (e.g. the binary is missing). A privilege denial usually
|
|
22
|
+
* surfaces as a non-zero exit AFTER spawn — logged, not thrown, because by then
|
|
23
|
+
* the box may already be on its way down.
|
|
24
|
+
*/
|
|
25
|
+
export declare function issueReboot(platform?: NodeJS.Platform): Promise<void>;
|
|
26
|
+
//# sourceMappingURL=reboot.d.ts.map
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import type { RuleContext } from '@kici-dev/sdk';
|
|
1
|
+
import type { RuleContext, FanoutPosition } from '@kici-dev/sdk';
|
|
2
2
|
/**
|
|
3
3
|
* Create RuleContext for agent-side rule evaluation.
|
|
4
4
|
*
|
|
5
5
|
* @param event - Event payload from the dispatch message
|
|
6
6
|
* @param changedFiles - List of files changed in this event
|
|
7
7
|
* @param env - Merged environment variables
|
|
8
|
+
* @param dispatchInputs - Operator dispatch inputs (`ctx.dispatchInputs`)
|
|
9
|
+
* @param fanout - Fan-out position (`ctx.fanout`); undefined on a non-fan-out job
|
|
8
10
|
*/
|
|
9
|
-
export declare function createRuleContext(event: Record<string, unknown>, changedFiles?: string[], env?: Record<string, string | undefined
|
|
11
|
+
export declare function createRuleContext(event: Record<string, unknown>, changedFiles?: string[], env?: Record<string, string | undefined>, dispatchInputs?: Readonly<Record<string, string | number | boolean | null>>, fanout?: FanoutPosition): RuleContext;
|
|
10
12
|
export { evaluateRules, type RuleEvaluationResult } from '@kici-dev/sdk';
|
|
11
13
|
//# sourceMappingURL=rule-evaluator.d.ts.map
|
|
@@ -193,8 +193,13 @@ export interface StepApprovalRequestIpc {
|
|
|
193
193
|
}>;
|
|
194
194
|
/** Human label for the gate. */
|
|
195
195
|
reason: string;
|
|
196
|
-
/** Per-gate timeout override (seconds) from the SDK `
|
|
196
|
+
/** Per-gate timeout override (seconds) from the SDK `approval.timeout`. */
|
|
197
197
|
timeoutSeconds?: number;
|
|
198
|
+
/** Computed drift payload, present only for `when: 'drift'` gates. */
|
|
199
|
+
payload?: {
|
|
200
|
+
summaryMarkdown: string;
|
|
201
|
+
drift: unknown;
|
|
202
|
+
};
|
|
198
203
|
}
|
|
199
204
|
/** Which provenance upload operation to relay. */
|
|
200
205
|
export type ProvenanceRequestOp = 'requestUploadUrl' | 'complete';
|
|
@@ -405,6 +410,20 @@ export interface JobExecutionRequest {
|
|
|
405
410
|
platform?: string;
|
|
406
411
|
arch?: string;
|
|
407
412
|
};
|
|
413
|
+
/**
|
|
414
|
+
* Operator-supplied, validated + coerced + defaulted workflow-dispatch inputs
|
|
415
|
+
* (from `kici run --input`), exposed to steps + rules as `ctx.dispatchInputs`.
|
|
416
|
+
* Absent for webhook runs.
|
|
417
|
+
*/
|
|
418
|
+
dispatchInputs?: Record<string, unknown>;
|
|
419
|
+
/**
|
|
420
|
+
* For a fan-out child (`runsOnAll` host or matrix combination): the 0-based
|
|
421
|
+
* deterministic position in the fan-out, assembled into `ctx.fanout`. Absent
|
|
422
|
+
* for non-fan-out jobs.
|
|
423
|
+
*/
|
|
424
|
+
fanoutIndex?: number;
|
|
425
|
+
/** For a fan-out child: the number of children in this fan-out. */
|
|
426
|
+
fanoutTotal?: number;
|
|
408
427
|
/** Secrets to merge into step environment (highest precedence). */
|
|
409
428
|
secrets?: Record<string, string>;
|
|
410
429
|
/** Namespaced secrets by context name for ctx.secrets['context-name'].KEY access. */
|
|
@@ -478,6 +497,10 @@ export interface JobExecutionRequest {
|
|
|
478
497
|
branch?: string;
|
|
479
498
|
/** Plain outputs from upstream jobs (keyed by job name, then by step name). For ctx.jobOutputs(). */
|
|
480
499
|
upstreamJobOutputs?: Record<string, Record<string, unknown>>;
|
|
500
|
+
/** Terminal status of each upstream job (keyed by job name; per-child for fan-out). For ctx.needs.<job>.status. */
|
|
501
|
+
upstreamJobStatuses?: Record<string, import('@kici-dev/engine').ExecutionJobStatus>;
|
|
502
|
+
/** This job's declared upstream needs (normalized lock edges) used to shape ctx.needs for steps. */
|
|
503
|
+
jobNeeds?: readonly unknown[];
|
|
481
504
|
/** Resolved private npm registries for `npm install` auth (token bytes already filled). */
|
|
482
505
|
npmRegistries?: ReadonlyArray<{
|
|
483
506
|
url: string;
|
|
@@ -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 } from '@kici-dev/sdk';
|
|
8
|
+
import type { Step, StepContext, HookInput, OutputsMap, StepSecretMountRecord, FanoutPosition } 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';
|
|
@@ -36,6 +36,10 @@ export interface StepLoopOptions {
|
|
|
36
36
|
event: Record<string, unknown>;
|
|
37
37
|
/** Environment variables for rule context. */
|
|
38
38
|
env: Record<string, string | undefined>;
|
|
39
|
+
/** Operator dispatch inputs for the rule context (`ctx.dispatchInputs`). */
|
|
40
|
+
dispatchInputs?: Readonly<Record<string, string | number | boolean | null>>;
|
|
41
|
+
/** Fan-out position for the rule context (`ctx.fanout`); undefined on a non-fan-out job. */
|
|
42
|
+
fanout?: FanoutPosition;
|
|
39
43
|
/** Job-level hooks. */
|
|
40
44
|
jobHooks?: JobHooks;
|
|
41
45
|
/**
|
|
@@ -90,11 +94,11 @@ export interface StepLoopOptions {
|
|
|
90
94
|
*/
|
|
91
95
|
afterStepApplyEnvFiles?: () => Promise<void>;
|
|
92
96
|
/**
|
|
93
|
-
* Block
|
|
94
|
-
* The runner sends the normalized requirement and awaits the
|
|
95
|
-
* agent keeps job heartbeats flowing during the wait so the
|
|
96
|
-
* reaped. Absent ⇒ approvals are not gated (CT / unit harnesses)
|
|
97
|
-
* run unconditionally.
|
|
97
|
+
* Block an `approval` step (`when: 'always'`) pending an orchestrator-side
|
|
98
|
+
* approval hold. The runner sends the normalized requirement and awaits the
|
|
99
|
+
* resolution; the agent keeps job heartbeats flowing during the wait so the
|
|
100
|
+
* agent isn't reaped. Absent ⇒ approvals are not gated (CT / unit harnesses)
|
|
101
|
+
* and steps run unconditionally.
|
|
98
102
|
*/
|
|
99
103
|
awaitStepApproval?: (req: {
|
|
100
104
|
stepIndex: number;
|
|
@@ -107,6 +111,28 @@ export interface StepLoopOptions {
|
|
|
107
111
|
reason: string;
|
|
108
112
|
timeoutSeconds?: number;
|
|
109
113
|
}) => Promise<StepApprovalResolution>;
|
|
114
|
+
/**
|
|
115
|
+
* Block an `approval: { when: 'drift' }` step mid-execution: after `check()`
|
|
116
|
+
* returns drift in apply mode, send a payload-bearing step-approval and await
|
|
117
|
+
* the resolution. The payload carries the computed drift (`summaryMarkdown` +
|
|
118
|
+
* structured `drift`) so the operator approves the actual diff. Absent ⇒ the
|
|
119
|
+
* drift gate is not enforced (CT / unit harnesses) and the step applies.
|
|
120
|
+
*/
|
|
121
|
+
awaitStepApprovalWithPayload?: (req: {
|
|
122
|
+
stepIndex: number;
|
|
123
|
+
stepName: string;
|
|
124
|
+
clauses: Array<{
|
|
125
|
+
team: string;
|
|
126
|
+
} | {
|
|
127
|
+
user: string;
|
|
128
|
+
}>;
|
|
129
|
+
reason: string;
|
|
130
|
+
timeoutSeconds?: number;
|
|
131
|
+
payload: {
|
|
132
|
+
summaryMarkdown: string;
|
|
133
|
+
drift: unknown;
|
|
134
|
+
};
|
|
135
|
+
}) => Promise<StepApprovalResolution>;
|
|
110
136
|
}
|
|
111
137
|
/** Outcome of an awaited step-level approval hold. */
|
|
112
138
|
export interface StepApprovalResolution {
|
|
@@ -13,10 +13,21 @@
|
|
|
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 { ExecutionJobStatus } from '@kici-dev/engine';
|
|
16
17
|
import type { StepContext } from '@kici-dev/sdk';
|
|
18
|
+
import type { NeedsContext, FanoutPosition } from '@kici-dev/sdk';
|
|
17
19
|
import type { OutputsMap, StepRefMap, TrackedStepSecrets } from '@kici-dev/sdk';
|
|
18
20
|
import type { RunnerToAgentMessage, JobExecutionRequest } from './ipc-protocol.js';
|
|
19
21
|
import { LogMasker } from './log-masker.js';
|
|
22
|
+
/**
|
|
23
|
+
* Build `ctx.needs` for a job's steps from the dispatch envelope. Reconstructs
|
|
24
|
+
* an {@link UpstreamSnapshot} from `upstreamJobOutputs` (flat per single job;
|
|
25
|
+
* `byMatrix` / `byHost` envelopes per fan-out) + `upstreamJobStatuses` (keyed by
|
|
26
|
+
* each upstream job/child name), then resolves the job's declared needs into the
|
|
27
|
+
* `{ result, status }` / ordered-array shape via the shared SDK builder. Returns
|
|
28
|
+
* undefined when the job declares no needs.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildStepNeedsContext(declaredNeeds: readonly unknown[] | undefined, upstreamJobOutputs: Record<string, Record<string, unknown>> | undefined, upstreamJobStatuses: Record<string, ExecutionJobStatus> | undefined): NeedsContext | undefined;
|
|
20
31
|
/**
|
|
21
32
|
* Create a StepContext natively inside the workflow runner.
|
|
22
33
|
*
|
|
@@ -25,6 +36,12 @@ import { LogMasker } from './log-masker.js';
|
|
|
25
36
|
* inside this process with full shell access.
|
|
26
37
|
*/
|
|
27
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): StepContext;
|
|
39
|
+
/**
|
|
40
|
+
* Derive the fan-out position (`ctx.fanout`) from a dispatch request. Returns
|
|
41
|
+
* `undefined` for a non-fan-out job (no `fanoutTotal`), so `ctx.fanout` is only
|
|
42
|
+
* set for `runsOnAll` host children and matrix combinations.
|
|
43
|
+
*/
|
|
44
|
+
export declare function deriveFanout(request: JobExecutionRequest): FanoutPosition | undefined;
|
|
28
45
|
/** Raw provider webhook body for ctx.rawPayload — nested in the envelope. */
|
|
29
46
|
export declare function rawPayloadFromEvent(event: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
|
|
30
47
|
//# sourceMappingURL=workflow-runner.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -86,7 +86,8 @@ const envDef = defineEnv({
|
|
|
86
86
|
scalerPendingDispatchTimeoutMs: z.coerce.number().default(6e4),
|
|
87
87
|
executionMode: ExecutionMode.optional(),
|
|
88
88
|
otelExporterOtlpEndpoint: z.string().optional(),
|
|
89
|
-
concurrencyWaitTimeoutMs: z.coerce.number().int().min(1e3).default(36e5)
|
|
89
|
+
concurrencyWaitTimeoutMs: z.coerce.number().int().min(1e3).default(36e5),
|
|
90
|
+
isOrchestratorHost: z.string().optional().transform((v) => v === "true")
|
|
90
91
|
}),
|
|
91
92
|
envMap: {
|
|
92
93
|
orchestratorUrl: "KICI_ORCHESTRATOR_URL",
|
|
@@ -97,6 +98,7 @@ const envDef = defineEnv({
|
|
|
97
98
|
port: "KICI_PORT",
|
|
98
99
|
logLevel: "KICI_LOG_LEVEL",
|
|
99
100
|
agentToken: "KICI_AGENT_TOKEN",
|
|
101
|
+
isOrchestratorHost: "KICI_AGENT_IS_ORCHESTRATOR_HOST",
|
|
100
102
|
githubToken: "KICI_GITHUB_TOKEN",
|
|
101
103
|
maxLogSizeBytes: "KICI_MAX_LOG_SIZE_BYTES",
|
|
102
104
|
defaultStepTimeoutMs: "KICI_DEFAULT_STEP_TIMEOUT_MS",
|