@kici-dev/agent 0.1.20 → 0.1.22
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/config.d.ts +6 -0
- package/dist/execution/job-runner.d.ts +10 -0
- package/dist/execution/reboot.d.ts +26 -0
- package/dist/execution/sandbox/ipc-protocol.d.ts +30 -3
- package/dist/execution/sandbox/step-loop.d.ts +33 -5
- package/dist/execution/sandbox/workflow-runner.d.ts +11 -0
- package/dist/index.js +7 -2
- package/dist/server.js +180 -59
- package/dist/workflow-runner.js +224 -47
- package/dist/ws/orchestrator-client.d.ts +7 -0
- package/package.json +5 -5
- package/sbom.spdx.json +71 -122
package/dist/config.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
10
10
|
orchestratorUrl: z.ZodString;
|
|
11
11
|
agentId: z.ZodOptional<z.ZodString>;
|
|
12
12
|
labels: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<string[], string>>;
|
|
13
|
+
properties: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<Record<string, string | number | boolean>, string>>;
|
|
13
14
|
roles: z.ZodPipe<z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<string[] | undefined, string | undefined>>, z.ZodTransform<string[] | undefined, string[] | undefined>>;
|
|
14
15
|
port: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
15
16
|
logLevel: z.ZodDefault<z.ZodEnum<{
|
|
@@ -43,6 +44,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
43
44
|
}>>;
|
|
44
45
|
otelExporterOtlpEndpoint: z.ZodOptional<z.ZodString>;
|
|
45
46
|
concurrencyWaitTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
47
|
+
isOrchestratorHost: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<boolean, string | undefined>>;
|
|
46
48
|
}, z.core.$strip>;
|
|
47
49
|
/**
|
|
48
50
|
* App configuration type. Includes computed agentId when not provided.
|
|
@@ -59,6 +61,7 @@ export type AppConfig = z.infer<typeof configSchema> & {
|
|
|
59
61
|
export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
60
62
|
orchestratorUrl: string;
|
|
61
63
|
labels: string[];
|
|
64
|
+
properties: Record<string, string | number | boolean>;
|
|
62
65
|
roles: string[] | undefined;
|
|
63
66
|
port: number;
|
|
64
67
|
logLevel: "error" | "debug" | "info" | "warn";
|
|
@@ -73,6 +76,7 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
|
73
76
|
scalerIdleTimeoutMs: number;
|
|
74
77
|
scalerPendingDispatchTimeoutMs: number;
|
|
75
78
|
concurrencyWaitTimeoutMs: number;
|
|
79
|
+
isOrchestratorHost: boolean;
|
|
76
80
|
agentId?: string | undefined;
|
|
77
81
|
agentToken?: string | undefined;
|
|
78
82
|
githubToken?: string | undefined;
|
|
@@ -86,6 +90,7 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
|
|
|
86
90
|
* - KICI_ORCHESTRATOR_URL (required)
|
|
87
91
|
* - KICI_AGENT_ID (optional, auto-generated from hostname-uuid8)
|
|
88
92
|
* - KICI_LABELS (comma-separated, e.g. "linux,docker"). Labels with 'kici-' prefix are reserved.
|
|
93
|
+
* - KICI_PROPERTIES (comma-separated key=value host-vars, e.g. "region=eu,cores=8,gpu=true"). Typed (bool/number/string), reported into the host roster.
|
|
89
94
|
* - KICI_ROLES (comma-separated agent roles, e.g. "builder,init-runner". undefined=all, empty=execution-only)
|
|
90
95
|
* - KICI_PORT (default: 8080)
|
|
91
96
|
* - KICI_LOG_LEVEL (default: info)
|
|
@@ -120,6 +125,7 @@ export declare function agentClientConnectionOptions(config: AppConfig): {
|
|
|
120
125
|
url: string;
|
|
121
126
|
agentId: string;
|
|
122
127
|
labels: string[];
|
|
128
|
+
properties: Record<string, string | number | boolean>;
|
|
123
129
|
scalerManaged: boolean;
|
|
124
130
|
token: string | undefined;
|
|
125
131
|
};
|
|
@@ -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
|
*
|
|
@@ -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,3 +1,4 @@
|
|
|
1
|
+
import type { CheckMode, CheckStepOutcome } from '@kici-dev/engine';
|
|
1
2
|
import type { SandboxStepResult } from './types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Structured clone auth. Wire-compatible with `gitAuthSchema` on the
|
|
@@ -24,11 +25,11 @@ interface StepStartMessage {
|
|
|
24
25
|
/** Distinguishes regular steps from hook executions (e.g., 'hook:onCancel', 'hook:cleanup'). Defaults to 'step'. */
|
|
25
26
|
step_type?: string;
|
|
26
27
|
}
|
|
27
|
-
/** A step has completed (success or
|
|
28
|
+
/** A step has completed (success, failure, or a check-mode skip). */
|
|
28
29
|
interface StepCompleteMessage {
|
|
29
30
|
type: 'step.complete';
|
|
30
31
|
stepIndex: number;
|
|
31
|
-
status: 'success' | 'failed';
|
|
32
|
+
status: 'success' | 'failed' | 'skipped';
|
|
32
33
|
durationMs: number;
|
|
33
34
|
error?: {
|
|
34
35
|
message: string;
|
|
@@ -47,6 +48,16 @@ interface StepCompleteMessage {
|
|
|
47
48
|
* pseudo-steps carry `{ cacheOutcome, key, matchedKey?, bytes? }` here.
|
|
48
49
|
*/
|
|
49
50
|
data?: Record<string, unknown>;
|
|
51
|
+
/**
|
|
52
|
+
* Idempotent per-step outcome (`CheckStepOutcome`). Present only when the run
|
|
53
|
+
* carried a check mode and the step has a `check` facet (or was a plain step
|
|
54
|
+
* skipped under check mode). Orthogonal to `status`.
|
|
55
|
+
*/
|
|
56
|
+
checkOutcome?: CheckStepOutcome;
|
|
57
|
+
/** Human-readable drift summary (`summarize(drift)`). Present when drift was detected. */
|
|
58
|
+
driftSummary?: string;
|
|
59
|
+
/** Structured drift value returned by `check()`. Present when drift was detected. */
|
|
60
|
+
drift?: unknown;
|
|
50
61
|
}
|
|
51
62
|
/** A single log line from step execution. */
|
|
52
63
|
interface LogLineMessage {
|
|
@@ -182,8 +193,13 @@ export interface StepApprovalRequestIpc {
|
|
|
182
193
|
}>;
|
|
183
194
|
/** Human label for the gate. */
|
|
184
195
|
reason: string;
|
|
185
|
-
/** Per-gate timeout override (seconds) from the SDK `
|
|
196
|
+
/** Per-gate timeout override (seconds) from the SDK `approval.timeout`. */
|
|
186
197
|
timeoutSeconds?: number;
|
|
198
|
+
/** Computed drift payload, present only for `when: 'drift'` gates. */
|
|
199
|
+
payload?: {
|
|
200
|
+
summaryMarkdown: string;
|
|
201
|
+
drift: unknown;
|
|
202
|
+
};
|
|
187
203
|
}
|
|
188
204
|
/** Which provenance upload operation to relay. */
|
|
189
205
|
export type ProvenanceRequestOp = 'requestUploadUrl' | 'complete';
|
|
@@ -426,6 +442,13 @@ export interface JobExecutionRequest {
|
|
|
426
442
|
checkout?: boolean;
|
|
427
443
|
/** Whether this job is part of a test run triggered by `kici test`. */
|
|
428
444
|
isTestRun?: boolean;
|
|
445
|
+
/**
|
|
446
|
+
* Run mode for idempotent steps (`apply` | `check` | `check-fail-on-drift`).
|
|
447
|
+
* Threaded from the dispatch event. In check / check-fail-on-drift mode the
|
|
448
|
+
* runner previews drift and never invokes a checked step's apply (`run`).
|
|
449
|
+
* Defaults to `apply` when unset.
|
|
450
|
+
*/
|
|
451
|
+
checkMode?: CheckMode;
|
|
429
452
|
/** When true, skip git clone -- use overlay tarball as complete workspace. */
|
|
430
453
|
fullRepo?: boolean;
|
|
431
454
|
/** URL to download the encrypted overlay tarball (test runs with uncommitted changes). */
|
|
@@ -460,6 +483,10 @@ export interface JobExecutionRequest {
|
|
|
460
483
|
branch?: string;
|
|
461
484
|
/** Plain outputs from upstream jobs (keyed by job name, then by step name). For ctx.jobOutputs(). */
|
|
462
485
|
upstreamJobOutputs?: Record<string, Record<string, unknown>>;
|
|
486
|
+
/** Terminal status of each upstream job (keyed by job name; per-child for fan-out). For ctx.needs.<job>.status. */
|
|
487
|
+
upstreamJobStatuses?: Record<string, import('@kici-dev/engine').ExecutionJobStatus>;
|
|
488
|
+
/** This job's declared upstream needs (normalized lock edges) used to shape ctx.needs for steps. */
|
|
489
|
+
jobNeeds?: readonly unknown[];
|
|
463
490
|
/** Resolved private npm registries for `npm install` auth (token bytes already filled). */
|
|
464
491
|
npmRegistries?: ReadonlyArray<{
|
|
465
492
|
url: string;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* module loading, and calls this loop for step execution with hooks.
|
|
7
7
|
*/
|
|
8
8
|
import type { Step, StepContext, HookInput, OutputsMap, StepSecretMountRecord } from '@kici-dev/sdk';
|
|
9
|
+
import { CheckMode } from '@kici-dev/engine';
|
|
9
10
|
import type { RunnerToAgentMessage } from './ipc-protocol.js';
|
|
10
11
|
import type { SandboxStepResult } from './types.js';
|
|
11
12
|
import { type CachePhaseDeps } from '../cache/index.js';
|
|
@@ -21,6 +22,11 @@ export interface JobHooks {
|
|
|
21
22
|
/** Options for the step execution loop. */
|
|
22
23
|
export interface StepLoopOptions {
|
|
23
24
|
steps: Step[];
|
|
25
|
+
/**
|
|
26
|
+
* Run mode for idempotent steps. `apply` (default) converges; `check` /
|
|
27
|
+
* `check-fail-on-drift` preview drift and never invoke a checked step's apply.
|
|
28
|
+
*/
|
|
29
|
+
checkMode?: CheckMode;
|
|
24
30
|
/** Factory that creates a StepContext for a given step index and name. */
|
|
25
31
|
createStepContext: (stepIndex: number, stepName: string) => StepContext;
|
|
26
32
|
sendIpc: (msg: RunnerToAgentMessage) => void;
|
|
@@ -84,11 +90,11 @@ export interface StepLoopOptions {
|
|
|
84
90
|
*/
|
|
85
91
|
afterStepApplyEnvFiles?: () => Promise<void>;
|
|
86
92
|
/**
|
|
87
|
-
* Block
|
|
88
|
-
* The runner sends the normalized requirement and awaits the
|
|
89
|
-
* agent keeps job heartbeats flowing during the wait so the
|
|
90
|
-
* reaped. Absent ⇒ approvals are not gated (CT / unit harnesses)
|
|
91
|
-
* run unconditionally.
|
|
93
|
+
* Block an `approval` step (`when: 'always'`) pending an orchestrator-side
|
|
94
|
+
* approval hold. The runner sends the normalized requirement and awaits the
|
|
95
|
+
* resolution; the agent keeps job heartbeats flowing during the wait so the
|
|
96
|
+
* agent isn't reaped. Absent ⇒ approvals are not gated (CT / unit harnesses)
|
|
97
|
+
* and steps run unconditionally.
|
|
92
98
|
*/
|
|
93
99
|
awaitStepApproval?: (req: {
|
|
94
100
|
stepIndex: number;
|
|
@@ -101,6 +107,28 @@ export interface StepLoopOptions {
|
|
|
101
107
|
reason: string;
|
|
102
108
|
timeoutSeconds?: number;
|
|
103
109
|
}) => Promise<StepApprovalResolution>;
|
|
110
|
+
/**
|
|
111
|
+
* Block an `approval: { when: 'drift' }` step mid-execution: after `check()`
|
|
112
|
+
* returns drift in apply mode, send a payload-bearing step-approval and await
|
|
113
|
+
* the resolution. The payload carries the computed drift (`summaryMarkdown` +
|
|
114
|
+
* structured `drift`) so the operator approves the actual diff. Absent ⇒ the
|
|
115
|
+
* drift gate is not enforced (CT / unit harnesses) and the step applies.
|
|
116
|
+
*/
|
|
117
|
+
awaitStepApprovalWithPayload?: (req: {
|
|
118
|
+
stepIndex: number;
|
|
119
|
+
stepName: string;
|
|
120
|
+
clauses: Array<{
|
|
121
|
+
team: string;
|
|
122
|
+
} | {
|
|
123
|
+
user: string;
|
|
124
|
+
}>;
|
|
125
|
+
reason: string;
|
|
126
|
+
timeoutSeconds?: number;
|
|
127
|
+
payload: {
|
|
128
|
+
summaryMarkdown: string;
|
|
129
|
+
drift: unknown;
|
|
130
|
+
};
|
|
131
|
+
}) => Promise<StepApprovalResolution>;
|
|
104
132
|
}
|
|
105
133
|
/** Outcome of an awaited step-level approval hold. */
|
|
106
134
|
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 } 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
|
*
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { homedir, hostname, tmpdir } from "node:os";
|
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { LOGGER_ENV_VARS, defineEnv, validateUnknownKiciVars } from "@kici-dev/shared/env";
|
|
6
|
-
import { KNOWN_ROLES, validateNoReservedLabels } from "@kici-dev/engine";
|
|
6
|
+
import { KNOWN_ROLES, parseHostPropertyAssignments, validateNoReservedLabels } from "@kici-dev/engine";
|
|
7
7
|
import { execFile } from "node:child_process";
|
|
8
8
|
import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, unlink, writeFile } from "node:fs/promises";
|
|
9
9
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
@@ -50,6 +50,7 @@ const envDef = defineEnv({
|
|
|
50
50
|
orchestratorUrl: z.string().url().min(1, "KICI_ORCHESTRATOR_URL is required"),
|
|
51
51
|
agentId: z.string().optional(),
|
|
52
52
|
labels: z.string().default("").transform((s) => s.split(",").filter(Boolean)),
|
|
53
|
+
properties: z.string().default("").transform((s) => parseHostPropertyAssignments(s.split(",").filter(Boolean))),
|
|
53
54
|
roles: z.string().optional().transform((s) => {
|
|
54
55
|
if (s === void 0) return void 0;
|
|
55
56
|
if (s === "") return [];
|
|
@@ -85,16 +86,19 @@ const envDef = defineEnv({
|
|
|
85
86
|
scalerPendingDispatchTimeoutMs: z.coerce.number().default(6e4),
|
|
86
87
|
executionMode: ExecutionMode.optional(),
|
|
87
88
|
otelExporterOtlpEndpoint: z.string().optional(),
|
|
88
|
-
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")
|
|
89
91
|
}),
|
|
90
92
|
envMap: {
|
|
91
93
|
orchestratorUrl: "KICI_ORCHESTRATOR_URL",
|
|
92
94
|
agentId: "KICI_AGENT_ID",
|
|
93
95
|
labels: "KICI_LABELS",
|
|
96
|
+
properties: "KICI_PROPERTIES",
|
|
94
97
|
roles: "KICI_ROLES",
|
|
95
98
|
port: "KICI_PORT",
|
|
96
99
|
logLevel: "KICI_LOG_LEVEL",
|
|
97
100
|
agentToken: "KICI_AGENT_TOKEN",
|
|
101
|
+
isOrchestratorHost: "KICI_AGENT_IS_ORCHESTRATOR_HOST",
|
|
98
102
|
githubToken: "KICI_GITHUB_TOKEN",
|
|
99
103
|
maxLogSizeBytes: "KICI_MAX_LOG_SIZE_BYTES",
|
|
100
104
|
defaultStepTimeoutMs: "KICI_DEFAULT_STEP_TIMEOUT_MS",
|
|
@@ -118,6 +122,7 @@ const envDef = defineEnv({
|
|
|
118
122
|
* - KICI_ORCHESTRATOR_URL (required)
|
|
119
123
|
* - KICI_AGENT_ID (optional, auto-generated from hostname-uuid8)
|
|
120
124
|
* - KICI_LABELS (comma-separated, e.g. "linux,docker"). Labels with 'kici-' prefix are reserved.
|
|
125
|
+
* - KICI_PROPERTIES (comma-separated key=value host-vars, e.g. "region=eu,cores=8,gpu=true"). Typed (bool/number/string), reported into the host roster.
|
|
121
126
|
* - KICI_ROLES (comma-separated agent roles, e.g. "builder,init-runner". undefined=all, empty=execution-only)
|
|
122
127
|
* - KICI_PORT (default: 8080)
|
|
123
128
|
* - KICI_LOG_LEVEL (default: info)
|