@kici-dev/agent 0.0.0 → 0.1.2

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.
Files changed (50) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +1 -6
  3. package/dist/checkout/git-clone.d.ts +59 -0
  4. package/dist/checkout/ssh-auth.d.ts +34 -0
  5. package/dist/config.d.ts +109 -0
  6. package/dist/execution/console-capture.d.ts +35 -0
  7. package/dist/execution/dep-installer.d.ts +44 -0
  8. package/dist/execution/dep-packer.d.ts +25 -0
  9. package/dist/execution/dep-restore.d.ts +85 -0
  10. package/dist/execution/download.d.ts +29 -0
  11. package/dist/execution/dynamic-job-serializer.d.ts +51 -0
  12. package/dist/execution/hook-executor.d.ts +46 -0
  13. package/dist/execution/init-runner.d.ts +33 -0
  14. package/dist/execution/job-runner.d.ts +266 -0
  15. package/dist/execution/log-streamer.d.ts +126 -0
  16. package/dist/execution/npm-registry-config.d.ts +63 -0
  17. package/dist/execution/npm-resolver.d.ts +40 -0
  18. package/dist/execution/overlay-applier.d.ts +51 -0
  19. package/dist/execution/rule-evaluator.d.ts +11 -0
  20. package/dist/execution/sandbox/bare-metal-sandbox.d.ts +69 -0
  21. package/dist/execution/sandbox/container-sandbox.d.ts +100 -0
  22. package/dist/execution/sandbox/env-sanitizer.d.ts +43 -0
  23. package/dist/execution/sandbox/firecracker-sandbox.d.ts +65 -0
  24. package/dist/execution/sandbox/fork-runner.d.ts +94 -0
  25. package/dist/execution/sandbox/index.d.ts +14 -0
  26. package/dist/execution/sandbox/ipc-protocol.d.ts +311 -0
  27. package/dist/execution/sandbox/log-masker.d.ts +45 -0
  28. package/dist/execution/sandbox/secret-encryption.d.ts +37 -0
  29. package/dist/execution/sandbox/secret-merge.d.ts +18 -0
  30. package/dist/execution/sandbox/step-loop.d.ts +77 -0
  31. package/dist/execution/sandbox/types.d.ts +142 -0
  32. package/dist/execution/sandbox/workflow-runner.d.ts +17 -0
  33. package/dist/execution/source-packer.d.ts +18 -0
  34. package/dist/execution/source-restore.d.ts +23 -0
  35. package/dist/execution/timeout-util.d.ts +11 -0
  36. package/dist/execution/workflow-loader.d.ts +70 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +128 -0
  39. package/dist/metrics/metrics-reporter.d.ts +32 -0
  40. package/dist/metrics/prometheus.d.ts +95 -0
  41. package/dist/routes/health.d.ts +27 -0
  42. package/dist/server.d.ts +20 -0
  43. package/dist/server.js +5347 -0
  44. package/dist/workflow-runner.js +2978 -0
  45. package/dist/ws/event-buffer.d.ts +16 -0
  46. package/dist/ws/log-buffer.d.ts +15 -0
  47. package/dist/ws/orchestrator-client.d.ts +269 -0
  48. package/package.json +59 -7
  49. package/sbom.spdx.json +10125 -0
  50. package/index.js +0 -3
@@ -0,0 +1,266 @@
1
+ import type { AgentToOrchestratorMessage, JobDispatch } from '@kici-dev/engine';
2
+ import type { AppConfig } from '../config.js';
3
+ /**
4
+ * Dependencies injected into JobRunner.
5
+ */
6
+ export interface JobRunnerDeps {
7
+ /** Send function for WS messages (buffered) */
8
+ send: (msg: AgentToOrchestratorMessage) => void;
9
+ /** Send direct function (bypasses buffer, for protocol messages) */
10
+ sendDirect: (msg: AgentToOrchestratorMessage) => void;
11
+ /** Agent config */
12
+ config: AppConfig;
13
+ /** Request a pre-signed S3 upload URL from the orchestrator via WS request-response. */
14
+ requestUploadUrl: (jobId: string, cacheType: 'source' | 'deps', key: {
15
+ contentHash?: string;
16
+ lockfileHash?: string;
17
+ platform: string;
18
+ arch: string;
19
+ }) => Promise<string>;
20
+ /** Notify orchestrator that an S3 upload is complete (for metadata initialization). */
21
+ sendUploadComplete: (jobId: string, cacheType: 'source' | 'deps', key: {
22
+ contentHash?: string;
23
+ lockfileHash?: string;
24
+ platform: string;
25
+ arch: string;
26
+ depsHash?: string;
27
+ }) => void;
28
+ /**
29
+ * Send an event.emit WS message to the orchestrator and await the response.
30
+ * Used to relay ctx.emit() from the sandbox through the WS connection.
31
+ */
32
+ sendEventEmit: (jobId: string, requestId: string, eventName: string, payload: Record<string, unknown>, target?: {
33
+ repos?: string[];
34
+ }) => Promise<{
35
+ requestId: string;
36
+ deliveryId?: string;
37
+ error?: string;
38
+ }>;
39
+ /** Get WS send buffer size in bytes. Used by LogStreamer for backpressure detection. */
40
+ getBufferedAmount?: () => number;
41
+ /** Register a one-time callback for the WS 'drain' event. */
42
+ onDrain?: (callback: () => void) => void;
43
+ /**
44
+ * Send a job.context message to the orchestrator with execution environment details.
45
+ */
46
+ sendJobContext: (runId: string, jobId: string, context: {
47
+ envVars?: Array<{
48
+ name: string;
49
+ value: string;
50
+ category: 'system' | 'user' | 'inherited' | 'secret';
51
+ }>;
52
+ runtime?: {
53
+ nodeVersion?: string;
54
+ os?: string;
55
+ arch?: string;
56
+ };
57
+ sandboxType?: string;
58
+ labels?: string[];
59
+ workingDirectory?: string;
60
+ gitRef?: string;
61
+ }) => void;
62
+ /**
63
+ * Send a run.event message to the orchestrator for infrastructure lifecycle tracking.
64
+ */
65
+ sendRunEvent: (runId: string, eventType: string, opts?: {
66
+ jobId?: string;
67
+ metadata?: Record<string, unknown>;
68
+ durationMs?: number;
69
+ }) => void;
70
+ /**
71
+ * Send a job.concurrency.report WS message and wait for job.concurrency.ack.
72
+ * Returns the orchestrator's ack with action (proceed/wait/cancel).
73
+ */
74
+ sendConcurrencyReport: (runId: string, jobId: string, group: string) => Promise<{
75
+ action: 'proceed' | 'wait' | 'cancel';
76
+ reason?: string;
77
+ }>;
78
+ /**
79
+ * Send an agent.api.request WS message and await the response.
80
+ * Used to relay kici.* API calls from the sandbox through the WS connection.
81
+ * Optional for backward compatibility.
82
+ */
83
+ sendApiRequest?: (method: string, params?: Record<string, unknown>) => Promise<unknown>;
84
+ }
85
+ interface ActiveJob {
86
+ abortController: AbortController;
87
+ completionPromise: Promise<void>;
88
+ runId: string;
89
+ }
90
+ /**
91
+ * Top-level job execution orchestrator for the agent.
92
+ *
93
+ * When a `job.dispatch` is received, the runner:
94
+ * 1. Creates a temp work directory
95
+ * 2. Selects the appropriate sandbox backend (container, bare-metal, firecracker)
96
+ * 3. Delegates execution to the sandbox (clone, compile, run steps)
97
+ * 4. Wires sandbox IPC callbacks to the WS message pipeline
98
+ * 5. Reports status (running -> success/failed/cancelled/skipped)
99
+ * 6. Cleans up work directory and sandbox
100
+ *
101
+ * Customer code runs in an isolated child process -- NEVER in the agent's V8 isolate.
102
+ * Build jobs still run in-process (they don't execute customer workflow steps).
103
+ */
104
+ export declare class JobRunner {
105
+ private readonly send;
106
+ private readonly sendDirect;
107
+ private readonly config;
108
+ private readonly requestUploadUrl;
109
+ private readonly sendUploadComplete;
110
+ private readonly sendEventEmit;
111
+ private readonly getBufferedAmount?;
112
+ private readonly onDrain?;
113
+ private readonly _sendJobContext;
114
+ private readonly _sendRunEvent;
115
+ private readonly _sendConcurrencyReport;
116
+ private readonly _sendApiRequest?;
117
+ /** Tracks running jobs for concurrency and cancellation */
118
+ readonly activeJobs: Map<string, ActiveJob>;
119
+ /** Active sandbox for the current job (used for abort). */
120
+ private activeSandbox;
121
+ constructor(deps: JobRunnerDeps);
122
+ /**
123
+ * Execute a dispatched job through its full lifecycle.
124
+ *
125
+ * Creates a temp directory, delegates to the appropriate sandbox,
126
+ * reports status, and cleans up.
127
+ */
128
+ execute(dispatch: JobDispatch): Promise<void>;
129
+ /**
130
+ * Cancel a running job by signaling its abort controller
131
+ * and aborting the active sandbox.
132
+ *
133
+ * @param force When true, force-cancel (SIGKILL, skip hooks). When false, graceful cancel.
134
+ */
135
+ cancel(jobId: string, reason: string, _force?: boolean): void;
136
+ /**
137
+ * Internal job execution pipeline.
138
+ *
139
+ * For execution jobs: delegates to an ExecutionSandbox (customer code in
140
+ * isolated child process). For build-only jobs: runs in-process (no customer
141
+ * workflow steps).
142
+ */
143
+ private runJob;
144
+ /**
145
+ * Route init-only / dynamicJobFn / build-only jobs to their dedicated handlers.
146
+ *
147
+ * Returns `true` when one of the special handlers ran (caller must early-return);
148
+ * `false` when the job is a standard execution job that should hit the sandbox path.
149
+ */
150
+ private dispatchSpecialJobType;
151
+ /**
152
+ * Run a standard execution job through its full sandbox lifecycle.
153
+ *
154
+ * Heartbeat timer + try/finally wrap sandbox creation, setup, execution, and
155
+ * teardown. Errors during execution are caught and reported as a failed job
156
+ * status; the sandbox is always torn down in the finally block.
157
+ */
158
+ private executeStandardJob;
159
+ /**
160
+ * Determine execution mode, build sanitized env, create + setup the sandbox,
161
+ * and emit the job.context message.
162
+ *
163
+ * Returns `null` if the abort signal fires before / during setup (the caller
164
+ * has already received a `cancelled` status via `sendJobStatus`).
165
+ */
166
+ private setupSandboxForExecution;
167
+ /**
168
+ * Drive `sandbox.executeJob` with IPC callbacks wired to the WS pipeline.
169
+ *
170
+ * Lazily creates per-step LogStreamers, forwards step + log + event-emit +
171
+ * concurrency-report + api-request messages, and emits the
172
+ * `agent.execution.start` / `agent.execution.end` lifecycle events.
173
+ */
174
+ private runSandboxExecution;
175
+ /**
176
+ * Tear down log streamers, record step Prometheus metrics, log sandbox
177
+ * failure diagnostics, and send the terminal `job.status` message.
178
+ */
179
+ private reportExecutionResult;
180
+ /**
181
+ * Handle a build-only job.
182
+ *
183
+ * Build jobs install dependencies, pack them into a tarball,
184
+ * and optionally compile the workflow bundle. They report
185
+ * status back to the orchestrator but do not execute workflow steps.
186
+ */
187
+ private handleBuildJob;
188
+ /**
189
+ * Phase 1 of build: clone the source repo (with Prometheus timing) and apply
190
+ * an overlay tarball if the dispatch carries one (test runs with
191
+ * uncommitted changes).
192
+ */
193
+ private cloneAndApplyOverlay;
194
+ /**
195
+ * Phase 2 of build: install dependencies locally if needed for the build,
196
+ * and (when the orchestrator has flagged the dep cache as stale) pack
197
+ * `.kici/node_modules/` into a tarball and upload it to the deps cache.
198
+ */
199
+ private packAndUploadDeps;
200
+ /**
201
+ * Phase 3 of build: verify the cloned workflow source against the lock
202
+ * file's expected `contentHash`, pack `.kici/` into a tarball, and upload
203
+ * it to the source cache.
204
+ */
205
+ private packAndUploadSource;
206
+ /**
207
+ * Handle an init-only job.
208
+ *
209
+ * Init jobs evaluate dynamic functions (environment, env, concurrencyGroup)
210
+ * from a compiled workflow bundle and report the resolved values back to the
211
+ * orchestrator via job.status data payload. They do not execute workflow steps.
212
+ *
213
+ * A synthetic step 0 "init" LogStreamer carries console.log / structured log
214
+ * output so operators get the same visibility into init jobs that they get
215
+ * for regular steps. Module top-level code and each dynamic field function
216
+ * run inside a runCaptured scope so their console.* writes land on this log.
217
+ */
218
+ private handleInitJob;
219
+ /**
220
+ * Handle DynamicJobFn evaluation jobs.
221
+ *
222
+ * Loads the workflow bundle, extracts the DynamicJobFn by index, calls it
223
+ * with a DynamicJobContext, serializes the returned Job[] to LockJob[],
224
+ * and sends the result back to the orchestrator.
225
+ */
226
+ private handleDynamicJobFn;
227
+ /**
228
+ * Create the appropriate sandbox backend based on execution mode.
229
+ */
230
+ private createSandbox;
231
+ /**
232
+ * Collect relevant environment variables for the job.context message.
233
+ *
234
+ * Returns KICI_* system vars (visible) and user-defined workflow vars.
235
+ * Secret values are masked as '***'. Full process.env is NOT sent
236
+ * to avoid leaking host configuration.
237
+ */
238
+ private collectEnvVars;
239
+ /**
240
+ * Emit a run.event message to the orchestrator for infrastructure lifecycle tracking.
241
+ */
242
+ private emitRunEvent;
243
+ /**
244
+ * Create a LogStreamer for a synthetic step (build, evaluate, etc.).
245
+ */
246
+ private createStepStreamer;
247
+ /**
248
+ * Send a job.status message to the orchestrator.
249
+ *
250
+ * When secretOutputs are provided (encrypted envelopes from the sandbox),
251
+ * they are included as a top-level field on the WS message (not nested in data).
252
+ */
253
+ private sendJobStatus;
254
+ /**
255
+ * Send a step.status message to the orchestrator.
256
+ *
257
+ * @param logBytesStreamed total raw stream bytes accumulated by this step's
258
+ * LogStreamer. Set on terminal step states so the orchestrator can
259
+ * accumulate per-job and per-run totals for the operator-side
260
+ * `kici_org_log_bytes` capacity-planning gauge. Undefined for the
261
+ * `running` transition.
262
+ */
263
+ private sendStepStatus;
264
+ }
265
+ export {};
266
+ //# sourceMappingURL=job-runner.d.ts.map
@@ -0,0 +1,126 @@
1
+ import type { AgentLogChunk } from '@kici-dev/engine';
2
+ interface LogStreamerOptions {
3
+ /** Callback to send log.chunk messages */
4
+ send: (msg: AgentLogChunk) => void;
5
+ /** Run ID for log.chunk messages */
6
+ runId: string;
7
+ /** Job ID for log.chunk messages */
8
+ jobId: string;
9
+ /** Step index for log.chunk messages */
10
+ stepIndex: number;
11
+ /** Max log bytes per step (default: 10MB). Truncates after this limit. */
12
+ maxLogSizeBytes?: number;
13
+ /** Flush interval in ms (default: 100ms) */
14
+ flushIntervalMs?: number;
15
+ /** Flush line threshold (default: 50 lines) */
16
+ flushLineThreshold?: number;
17
+ /** Get current WS send buffer size in bytes. When provided, enables backpressure. */
18
+ getBufferedAmount?: () => number;
19
+ /** Backpressure mode: 'pause' stops the source stream, 'drop' discards lines. Default: 'pause'. */
20
+ backpressureMode?: 'pause' | 'drop';
21
+ /** Callback invoked when backpressure is detected (pause mode) -- caller should pause child stdout. */
22
+ onBackpressure?: () => void;
23
+ /** Callback invoked when backpressure clears (pause mode) -- caller should resume child stdout. */
24
+ onBackpressureClear?: () => void;
25
+ /** Register a one-time drain event listener on the WS. */
26
+ onWsDrain?: (callback: () => void) => void;
27
+ }
28
+ /** WS send buffer threshold in bytes before backpressure kicks in (1MB). */
29
+ export declare const BACKPRESSURE_THRESHOLD = 1048576;
30
+ /**
31
+ * Batches step output lines and flushes them as log.chunk messages.
32
+ *
33
+ * Optimizes WS delivery by collecting lines and flushing either:
34
+ * - When the line count reaches the flush threshold (default: 50 lines)
35
+ * - After a timer fires (default: 100ms)
36
+ *
37
+ * Enforces a maximum total log size per step. After exceeding the limit,
38
+ * a truncation notice is sent and further lines are silently dropped.
39
+ *
40
+ * Supports sender-side backpressure with two modes:
41
+ * - **pause**: Pauses the child process stdout via callback, resumes on WS drain event.
42
+ * Includes a 30s safety timeout that temporarily switches to drop mode to prevent deadlocks.
43
+ * - **drop**: Discards buffered lines when backpressure is detected, emits a count marker on resume.
44
+ */
45
+ export declare class LogStreamer {
46
+ private buffer;
47
+ private flushTimer;
48
+ private totalBytes;
49
+ private truncated;
50
+ /** Number of lines dropped due to backpressure (drop mode). */
51
+ private droppedCount;
52
+ /** Whether we are currently in a backpressured state (pause mode). */
53
+ private paused;
54
+ /** Whether drop mode is currently shedding lines. Tracked separately from
55
+ * `paused` so the Prometheus `kici_agent_log_backpressure_active` gauge
56
+ * reflects the actual shedding state (not just pause mode). */
57
+ private dropping;
58
+ /** Safety timeout timer for pause mode. */
59
+ private pauseSafetyTimer;
60
+ private readonly send;
61
+ private readonly runId;
62
+ private readonly jobId;
63
+ private readonly stepIndex;
64
+ private readonly maxLogSizeBytes;
65
+ private readonly flushIntervalMs;
66
+ private readonly flushLineThreshold;
67
+ private readonly getBufferedAmount?;
68
+ private readonly backpressureMode;
69
+ private readonly onBackpressure?;
70
+ private readonly onBackpressureClear?;
71
+ private readonly onWsDrain?;
72
+ constructor(options: LogStreamerOptions);
73
+ /**
74
+ * Add a line to the buffer. Triggers flush if threshold reached,
75
+ * otherwise schedules a timer-based flush.
76
+ */
77
+ addLine(line: string): void;
78
+ /**
79
+ * Flush buffered lines as a log.chunk message.
80
+ * No-op if buffer is empty.
81
+ *
82
+ * When backpressure is enabled (getBufferedAmount provided), checks the WS
83
+ * send buffer before sending:
84
+ * - **drop mode**: Discards buffered lines and increments droppedCount.
85
+ * - **pause mode**: Signals the caller to pause the child process, registers
86
+ * a drain listener to resume, and sets a 30s safety timeout.
87
+ */
88
+ flush(): void;
89
+ /**
90
+ * Flush remaining buffer and clean up. Call at end of step.
91
+ */
92
+ destroy(): void;
93
+ /**
94
+ * Total bytes tracked across all lines added.
95
+ */
96
+ getTotalBytes(): number;
97
+ /**
98
+ * Number of lines dropped due to backpressure.
99
+ * Exposed for testing.
100
+ */
101
+ getDroppedCount(): number;
102
+ /**
103
+ * Whether backpressure pause is currently active.
104
+ * Exposed for testing.
105
+ */
106
+ isPaused(): boolean;
107
+ /**
108
+ * Build the lines array, prepending a drop marker if lines were dropped.
109
+ */
110
+ private buildLinesWithDropMarker;
111
+ /**
112
+ * Force send all buffered lines, bypassing backpressure checks.
113
+ * Used during destroy() to ensure remaining data is sent.
114
+ */
115
+ private forceSend;
116
+ /**
117
+ * Register a WS drain listener that resumes sending after backpressure clears.
118
+ * Includes a 30s safety timeout that temporarily switches to drop mode.
119
+ */
120
+ private registerDrainResume;
121
+ private clearPauseSafetyTimer;
122
+ private scheduleFlush;
123
+ private clearTimer;
124
+ }
125
+ export {};
126
+ //# sourceMappingURL=log-streamer.d.ts.map
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Apply private-npm-registry auth to a workflow's `.kici/.npmrc` for the
3
+ * lifetime of one `npm install` invocation, then restore the file on cleanup.
4
+ *
5
+ * Why a closure-cleanup pattern (mirrors `setupSshAuth.cleanup()` in
6
+ * `packages/agent/src/checkout/git-clone.ts`): the customer's committed
7
+ * `.kici/.npmrc` may carry literal `${VAR}` placeholders or an unrelated
8
+ * scope mapping. We must NOT clobber it permanently — we just want to
9
+ * append the agent-managed registry/auth lines for this single install,
10
+ * then revert.
11
+ *
12
+ * Token bytes never end up in the on-disk `.npmrc`. Each registry's token
13
+ * is exposed as a job-scoped env var (`KICI_NPM_TOKEN_${jobIdShort}_<i>`)
14
+ * and the on-disk auth line carries the env var reference (`${VAR}`). npm
15
+ * substitutes at read time. The job-scoped nonce makes the env var name
16
+ * unguessable from outside the install subprocess.
17
+ *
18
+ * Merge order: customer-committed `.npmrc` lines come FIRST, agent-generated
19
+ * lines come LAST. npm's last-wins semantics make the agent's line shadow
20
+ * any literal `_authToken=...` the customer accidentally committed for a
21
+ * registry KiCI manages — refuses to let a committed secret beat a managed
22
+ * one.
23
+ *
24
+ * `installEnvSecrets` is a separate channel for customers who prefer the
25
+ * "commit a `.kici/.npmrc` with `${MY_TOKEN}` and supply MY_TOKEN as a
26
+ * scoped secret" pattern (Option C in the design doc). Each entry becomes
27
+ * an env var on the install subprocess; the customer's existing `.npmrc`
28
+ * uses it as `${MY_TOKEN}`.
29
+ */
30
+ /** Registry spec carried on the dispatch message (token already resolved). */
31
+ export interface NpmRegistrySpec {
32
+ url: string;
33
+ scope?: string;
34
+ alwaysAuth: boolean;
35
+ token: string;
36
+ }
37
+ export interface ApplyNpmRegistryConfigArgs {
38
+ /** Absolute path to the workflow's `.kici/` directory. */
39
+ kiciDir: string;
40
+ /** Resolved registries from the orchestrator. Empty/undefined = no-op. */
41
+ npmRegistries: readonly NpmRegistrySpec[] | undefined;
42
+ /** Bare-name resolved secrets to project as install env vars. */
43
+ installEnvSecrets: Record<string, string> | undefined;
44
+ /** Short (8 char) job-scoped nonce — used as suffix on synthesized env-var names. */
45
+ jobIdShort: string;
46
+ }
47
+ export interface ApplyNpmRegistryConfigResult {
48
+ /** Env vars to merge into the install subprocess (token vars + installEnvSecrets). */
49
+ extraEnv: Record<string, string>;
50
+ /** Token bytes the caller MUST mask out of stdout/stderr before logging. */
51
+ tokensForRedaction: string[];
52
+ /** Restore `.kici/.npmrc` to its pre-call state. Idempotent; never throws. */
53
+ cleanup: () => Promise<void>;
54
+ }
55
+ /**
56
+ * Apply the merged `.npmrc` and return env + redaction + cleanup. Caller
57
+ * runs the npm install with `extraEnv` merged in, then awaits cleanup()
58
+ * inside the install's `finally`.
59
+ */
60
+ export declare function applyNpmRegistryConfig(args: ApplyNpmRegistryConfigArgs): Promise<ApplyNpmRegistryConfigResult>;
61
+ /** Mask every token in `tokensForRedaction` out of `input` before logging. */
62
+ export declare function redactNpmOutput(input: string, tokens: readonly string[]): string;
63
+ //# sourceMappingURL=npm-registry-config.d.ts.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Resolve the npm CLI path relative to the running Node.js binary.
3
+ *
4
+ * Used both at startup (builder role readiness check) and at install time
5
+ * (dep-installer). Centralizes the resolution logic so it stays consistent.
6
+ *
7
+ * Resolution strategy:
8
+ * 1. Check standard Node.js layout paths relative to process.execPath
9
+ * 2. Fall back to bare 'npm' on PATH (development environments)
10
+ */
11
+ /** Result of npm resolution. */
12
+ interface NpmResolution {
13
+ /** Absolute path to npm-cli.js, or undefined if using bare 'npm' from PATH. */
14
+ npmCliPath: string | undefined;
15
+ /** The Node.js executable path (process.execPath). */
16
+ nodeExe: string;
17
+ /** Directory containing the Node.js binary. */
18
+ nodeDir: string;
19
+ }
20
+ /**
21
+ * Resolve the npm CLI path from the current Node.js binary.
22
+ *
23
+ * Checks standard Node.js distribution layout paths:
24
+ * - {nodeDir}/../lib/node_modules/npm/bin/npm-cli.js (Linux/macOS installed)
25
+ * - {nodeDir}/node_modules/npm/bin/npm-cli.js (Windows / some layouts)
26
+ *
27
+ * Returns undefined npmCliPath if neither is found (caller can fall back to PATH).
28
+ */
29
+ export declare function resolveNpm(): NpmResolution;
30
+ /**
31
+ * Verify that npm is usable by running `npm --version`.
32
+ *
33
+ * Called at agent startup when the builder role is active.
34
+ * Throws a descriptive error if npm cannot be executed.
35
+ *
36
+ * @returns The npm version string (e.g., "10.8.1")
37
+ */
38
+ export declare function verifyNpmAvailable(): string;
39
+ export {};
40
+ //# sourceMappingURL=npm-resolver.d.ts.map
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Agent-side overlay application.
3
+ *
4
+ * Downloads an encrypted tarball uploaded by the CLI, decrypts it using
5
+ * X25519 ECDH shared secret, verifies file checksums from the manifest,
6
+ * and applies the overlay (file additions/modifications + deletions)
7
+ * on top of the cloned repository.
8
+ *
9
+ * Wire format: [12-byte IV][16-byte auth tag][ciphertext]
10
+ * Same encryption scheme as packages/compiler/src/remote/encryption.ts.
11
+ */
12
+ /**
13
+ * Configuration for applying an overlay to a cloned repo.
14
+ */
15
+ interface OverlayConfig {
16
+ /** URL to download the encrypted tarball from (S3 pre-signed URL) */
17
+ tarballUrl: string;
18
+ /** Base64-encoded CLI ephemeral public key (DER/SPKI format) */
19
+ cliPublicKey: string;
20
+ /** Base64-encoded orchestrator ephemeral private key (DER/PKCS8 format) */
21
+ orchestratorPrivateKey: string;
22
+ /** Path to the cloned repository directory */
23
+ repoDir: string;
24
+ }
25
+ /**
26
+ * Result of applying an overlay.
27
+ */
28
+ interface OverlayResult {
29
+ /** Number of files copied/overwritten in the repo */
30
+ filesApplied: number;
31
+ /** Number of files deleted from the repo */
32
+ filesDeleted: number;
33
+ /** Whether all checksums were verified successfully */
34
+ verified: boolean;
35
+ }
36
+ /**
37
+ * Apply an overlay tarball to a cloned repository.
38
+ *
39
+ * Flow:
40
+ * 1. Download encrypted tarball from tarballUrl
41
+ * 2. Derive ECDH shared secret from orchestratorPrivateKey + cliPublicKey
42
+ * 3. Decrypt tarball using AES-256-GCM
43
+ * 4. Extract tar.gz to temp directory
44
+ * 5. Read manifest.json and verify checksums
45
+ * 6. Copy files to repoDir preserving directory structure
46
+ * 7. Apply deletions from manifest
47
+ * 8. Clean up temp files
48
+ */
49
+ export declare function applyOverlay(config: OverlayConfig): Promise<OverlayResult>;
50
+ export {};
51
+ //# sourceMappingURL=overlay-applier.d.ts.map
@@ -0,0 +1,11 @@
1
+ import type { RuleContext } from '@kici-dev/sdk';
2
+ /**
3
+ * Create RuleContext for agent-side rule evaluation.
4
+ *
5
+ * @param event - Event payload from the dispatch message
6
+ * @param changedFiles - List of files changed in this event
7
+ * @param env - Merged environment variables
8
+ */
9
+ export declare function createRuleContext(event: Record<string, unknown>, changedFiles?: string[], env?: Record<string, string | undefined>): RuleContext;
10
+ export { evaluateRules, type RuleEvaluationResult } from '@kici-dev/sdk';
11
+ //# sourceMappingURL=rule-evaluator.d.ts.map
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Bare-metal execution sandbox.
3
+ *
4
+ * Uses child_process.fork() with sanitized environment and optional bubblewrap
5
+ * (bwrap) namespace isolation. The workflow runner runs as a separate Node.js
6
+ * process with only explicitly allowed environment variables.
7
+ *
8
+ * Security model:
9
+ * - Environment sanitization: only ALLOWED_SYSTEM_VARS + user env + secrets
10
+ * - Optional bwrap: PID/IPC namespace isolation, read-only system mounts
11
+ * - Network isolation via --unshare-net when bwrap is enabled (loopback only)
12
+ *
13
+ * Without bwrap (sandbox=false), the runner process has full filesystem and
14
+ * network access. This mode provides credential isolation only and should
15
+ * be used in trusted environments.
16
+ */
17
+ import type { ExecutionSandbox, SandboxSetupOptions, JobExecutionOptions, JobExecutionResult } from './types.js';
18
+ /** Configuration options for BareMetalSandbox. */
19
+ interface BareMetalSandboxOptions {
20
+ /** Absolute path to the compiled workflow-runner.js entry point. */
21
+ runnerPath: string;
22
+ /** Whether to use bubblewrap (bwrap) for namespace isolation. */
23
+ sandbox: boolean;
24
+ /**
25
+ * Network mode when sandbox=true.
26
+ * - 'isolated' (default): bwrap --unshare-net (loopback only).
27
+ * - 'host': keep the host network namespace so workflows can talk to npm,
28
+ * git, package registries, etc.
29
+ * Ignored when sandbox=false.
30
+ */
31
+ sandboxNetwork?: 'isolated' | 'host';
32
+ /** Pre-sanitized environment variables (system allowlist + user env). */
33
+ env: Record<string, string>;
34
+ }
35
+ /**
36
+ * Bare-metal execution sandbox implementation.
37
+ *
38
+ * Forks the workflow runner as a child process with sanitized environment.
39
+ * Optionally wraps execution in bubblewrap for PID/IPC/filesystem isolation.
40
+ */
41
+ export declare class BareMetalSandbox implements ExecutionSandbox {
42
+ private readonly runnerPath;
43
+ private readonly useBwrap;
44
+ private readonly sandboxNetwork;
45
+ private readonly env;
46
+ private runner;
47
+ private workDir;
48
+ constructor(options: BareMetalSandboxOptions);
49
+ /**
50
+ * Validate that the runner path exists and bwrap is available (if needed).
51
+ */
52
+ setup(options: SandboxSetupOptions): Promise<void>;
53
+ /**
54
+ * Execute a job by forking the workflow runner with sanitized environment.
55
+ */
56
+ executeJob(options: JobExecutionOptions): Promise<JobExecutionResult>;
57
+ /**
58
+ * Abort the running job.
59
+ *
60
+ * Sends abort IPC message, then SIGTERM after 10s, SIGKILL after 15s.
61
+ */
62
+ abort(): Promise<void>;
63
+ /**
64
+ * Clean up the child process if still running.
65
+ */
66
+ teardown(): Promise<void>;
67
+ }
68
+ export {};
69
+ //# sourceMappingURL=bare-metal-sandbox.d.ts.map