@vzn/vx 0.0.16 → 0.0.21
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/LICENSE +1 -1
- package/README.md +135 -124
- package/index.ts +10 -0
- package/launcher.mjs +10 -11
- package/package.json +11 -7
- package/src/bin.ts +14 -4
- package/src/cache/archive.ts +659 -0
- package/src/cache/cache.ts +769 -1101
- package/src/cache/chained-cache.ts +194 -0
- package/src/cache/config-evals.ts +89 -0
- package/src/cache/file-hashes.ts +262 -0
- package/src/cache/git-inputs.ts +637 -0
- package/src/cache/index.ts +24 -5
- package/src/cache/inputs.ts +373 -456
- package/src/cache/layer.ts +604 -0
- package/src/cache/layered-cache.ts +96 -43
- package/src/cache/output-index.ts +224 -0
- package/src/cache/policy.ts +84 -0
- package/src/cache/run-history.ts +147 -0
- package/src/cache/tar-stream.ts +358 -0
- package/src/cache/zstd.ts +160 -0
- package/src/cli/cache.ts +75 -21
- package/src/cli/completions.ts +104 -0
- package/src/cli/core-alias.ts +25 -0
- package/src/cli/help.ts +195 -128
- package/src/cli/index.ts +116 -46
- package/src/cli/info.ts +122 -63
- package/src/cli/init.ts +75 -0
- package/src/cli/last.ts +172 -0
- package/src/cli/lock.ts +2 -1
- package/src/cli/plan-format.ts +72 -6
- package/src/cli/plugin-commands.ts +103 -0
- package/src/cli/run.ts +245 -271
- package/src/cli/select.ts +332 -0
- package/src/cli/show.ts +144 -69
- package/src/cli/upgrade.ts +9 -7
- package/src/cli/watch.ts +858 -110
- package/src/cli/why.ts +223 -0
- package/src/cli/workspace-config.ts +126 -0
- package/src/config.ts +187 -150
- package/src/exec/env.ts +18 -1
- package/src/exec/executor.ts +224 -0
- package/src/exec/index.ts +24 -2
- package/src/exec/local-executor.ts +41 -0
- package/src/exec/runner.ts +166 -40
- package/src/exec/sandbox-binds.ts +182 -0
- package/src/exec/sandbox-paths.ts +50 -0
- package/src/exec/sandbox-runtime.ts +642 -260
- package/src/exec/sandbox-violations.ts +280 -0
- package/src/graph/index.ts +4 -10
- package/src/graph/scheduler.ts +232 -203
- package/src/graph/task-graph.ts +222 -8
- package/src/index.ts +146 -153
- package/src/orchestrator/admission.ts +137 -0
- package/src/orchestrator/deferred-outputs.ts +147 -0
- package/src/orchestrator/doctor.ts +255 -0
- package/src/orchestrator/download-policy.ts +171 -0
- package/src/orchestrator/events.ts +72 -8
- package/src/orchestrator/execute-task.ts +374 -549
- package/src/orchestrator/failure-mode.ts +202 -0
- package/src/orchestrator/framed-output.ts +126 -69
- package/src/orchestrator/history.ts +126 -59
- package/src/orchestrator/hit-restore.ts +221 -0
- package/src/orchestrator/index.ts +77 -93
- package/src/orchestrator/local-shortcircuit.ts +22 -0
- package/src/orchestrator/lockfile-claim.ts +281 -0
- package/src/orchestrator/logger.ts +208 -35
- package/src/orchestrator/metrics.ts +102 -1388
- package/src/orchestrator/miss-save.ts +169 -0
- package/src/orchestrator/options.ts +55 -34
- package/src/orchestrator/persistent.ts +79 -0
- package/src/orchestrator/placement.ts +182 -0
- package/src/orchestrator/plan.ts +144 -1
- package/src/orchestrator/plugin-host.ts +357 -120
- package/src/orchestrator/plugin.ts +298 -58
- package/src/orchestrator/prepare.ts +202 -106
- package/src/orchestrator/projects.ts +259 -0
- package/src/orchestrator/remote-prefetch.ts +12 -8
- package/src/orchestrator/run-artifacts.ts +63 -16
- package/src/orchestrator/run-context.ts +167 -29
- package/src/orchestrator/run-records.ts +149 -0
- package/src/orchestrator/run-report.ts +74 -47
- package/src/orchestrator/run.ts +534 -417
- package/src/orchestrator/sandbox-request.ts +243 -0
- package/src/orchestrator/save-lane.ts +47 -0
- package/src/orchestrator/signals.ts +111 -0
- package/src/orchestrator/stable-keys.ts +129 -31
- package/src/orchestrator/status-line.ts +49 -9
- package/src/orchestrator/summary.ts +90 -26
- package/src/orchestrator/tally.ts +78 -35
- package/src/orchestrator/task-hash.ts +111 -15
- package/src/orchestrator/task-log-buffer.ts +288 -0
- package/src/orchestrator/telemetry-host.ts +34 -6
- package/src/orchestrator/telemetry.ts +182 -36
- package/src/orchestrator/upstream.ts +72 -31
- package/src/util/cgroup.ts +160 -0
- package/src/util/edit-distance.ts +70 -0
- package/src/util/errors.ts +15 -1
- package/src/util/index.ts +15 -2
- package/src/util/num.ts +37 -0
- package/src/util/paths.ts +64 -0
- package/src/util/settle.ts +67 -0
- package/src/util/size.ts +5 -1
- package/src/util/tail.ts +66 -0
- package/src/util/timing.ts +63 -0
- package/src/util/verbs.ts +39 -0
- package/src/workspace/affected.ts +250 -23
- package/src/workspace/config-cache.ts +313 -0
- package/src/workspace/config-eval.ts +217 -0
- package/src/workspace/config-imports.ts +162 -0
- package/src/workspace/config-schema.ts +837 -0
- package/src/workspace/filter.ts +65 -10
- package/src/workspace/fingerprint.ts +53 -4
- package/src/workspace/index.ts +42 -3
- package/src/workspace/lockfile.ts +25 -9
- package/src/workspace/migrate-scripts.ts +134 -0
- package/src/workspace/migration.ts +314 -0
- package/src/workspace/nested-dirs.ts +21 -7
- package/src/workspace/package-graph.ts +114 -41
- package/src/workspace/project-loader.ts +224 -450
- package/src/workspace/workspace.ts +298 -72
- package/src/cache/cas-backend.ts +0 -97
- package/src/cache/digest.ts +0 -47
- package/src/cache/tar.ts +0 -343
- package/src/cli/backend.ts +0 -23
- package/src/cli/mcp-rpc.ts +0 -292
- package/src/cli/mcp.ts +0 -92
- package/src/cli/migrate-nx.ts +0 -450
- package/src/cli/migrate-turbo.ts +0 -364
- package/src/cli/migrate.ts +0 -219
- package/src/orchestrator/devframe-surface.ts +0 -74
- package/src/orchestrator/predict.ts +0 -106
- package/src/orchestrator/protocol.ts +0 -156
- package/src/orchestrator/resources.ts +0 -59
- package/src/orchestrator/run-state.ts +0 -145
- package/src/orchestrator/verify.ts +0 -164
- package/src/orchestrator/wire-render.ts +0 -92
- package/src/orchestrator/wire.ts +0 -212
package/src/config.ts
CHANGED
|
@@ -14,23 +14,29 @@ export interface WorkspaceConfig {
|
|
|
14
14
|
*/
|
|
15
15
|
timeout?: number
|
|
16
16
|
/**
|
|
17
|
-
* Plugins registered for this workspace
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* Plugins registered for this workspace, consulted in this order once
|
|
18
|
+
* per `vx run`: each fills any of the pipeline stages and capabilities
|
|
19
|
+
* below, and core's own executor and cache store sit at the tail of
|
|
20
|
+
* every list. See `docs/design/pipeline-2026-09.md`.
|
|
20
21
|
*/
|
|
21
22
|
plugins?: readonly Plugin[]
|
|
22
|
-
/**
|
|
23
|
-
* Opt in to history-aware predictive scheduling. When `true` and
|
|
24
|
-
* `cache.db` has prior runs, the scheduler picks the next ready
|
|
25
|
-
* task by expected remaining critical-path duration (HistoryTable
|
|
26
|
-
* p50) instead of the static reverse-deps count.
|
|
27
|
-
*/
|
|
28
|
-
predictive?: boolean
|
|
29
23
|
}
|
|
30
24
|
|
|
31
25
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
26
|
+
* The runtime brand `definePlugin` stamps on a plugin: the name of the
|
|
27
|
+
* package it was defined in. The workspace loader accepts a plugin only
|
|
28
|
+
* when this is present and equals `name`, so a plugin's name is its
|
|
29
|
+
* package name and nothing else — no field to set, no override. A
|
|
30
|
+
* registry symbol on purpose: a plugin package imports its own copy of
|
|
31
|
+
* `@vzn/vx` beside a compiled binary's, and the two copies must stamp
|
|
32
|
+
* and check the same key.
|
|
33
|
+
*/
|
|
34
|
+
export const PLUGIN_PACKAGE: unique symbol = Symbol.for('vx.plugin.package')
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Structural plugin shape — any subset of the pipeline stages
|
|
38
|
+
* (`config`/`project`/`graph`/`key`/`schedule`), run-level capabilities
|
|
39
|
+
* (`cache`/`executor`/`telemetry`) and CLI `commands`, plus optional
|
|
34
40
|
* `setup`/`teardown`. The fully-typed `VxPlugin` lives in
|
|
35
41
|
* `src/orchestrator/plugin.ts` and is what users import; this is a
|
|
36
42
|
* re-declaration with opaque function types so `config.ts` stays a leaf
|
|
@@ -38,15 +44,60 @@ export interface WorkspaceConfig {
|
|
|
38
44
|
* before consulting capabilities.
|
|
39
45
|
*/
|
|
40
46
|
export interface Plugin {
|
|
47
|
+
/** The package name — set by `definePlugin`, never by the plugin. */
|
|
41
48
|
readonly name: string
|
|
42
|
-
|
|
49
|
+
config?(workspace: unknown, ctx: unknown): unknown
|
|
50
|
+
project?(config: unknown, ctx: unknown): unknown
|
|
51
|
+
graph?(nodes: unknown, ctx: unknown): unknown
|
|
52
|
+
key?(task: unknown, ctx: unknown): unknown
|
|
53
|
+
schedule?(nodes: unknown, ctx: unknown): unknown
|
|
54
|
+
admit?(task: unknown, ctx: unknown): unknown
|
|
55
|
+
readonly commands?: Readonly<Record<string, unknown>>
|
|
43
56
|
cache?(ctx: unknown): unknown
|
|
57
|
+
executor?(ctx: unknown): unknown
|
|
44
58
|
telemetry?(ctx: unknown): unknown
|
|
45
|
-
eventSink?(ctx: unknown): unknown
|
|
46
59
|
setup?(ctx: unknown): void | Promise<void>
|
|
47
60
|
teardown?(): void | Promise<void>
|
|
61
|
+
readonly fingerprint?: unknown
|
|
48
62
|
}
|
|
49
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Every hook a plugin may fill, in pipeline order — THE list. The loader's
|
|
66
|
+
* "must be a function" and "at least one of" checks, the host's stage gate
|
|
67
|
+
* and `vx info`'s seam column all read it, so a stage added here is a stage
|
|
68
|
+
* everywhere: `admit` reached `vx info` a day late (2026-09-12) because
|
|
69
|
+
* that column kept its own copy, and the loader kept a third. The type
|
|
70
|
+
* pin below refuses a list that drifts from `Plugin`'s keys either way.
|
|
71
|
+
*/
|
|
72
|
+
export const PLUGIN_HOOKS = [
|
|
73
|
+
'config',
|
|
74
|
+
'project',
|
|
75
|
+
'graph',
|
|
76
|
+
'key',
|
|
77
|
+
'fingerprint',
|
|
78
|
+
'schedule',
|
|
79
|
+
'admit',
|
|
80
|
+
'executor',
|
|
81
|
+
'cache',
|
|
82
|
+
'telemetry',
|
|
83
|
+
'setup',
|
|
84
|
+
'commands',
|
|
85
|
+
'teardown',
|
|
86
|
+
] as const
|
|
87
|
+
export type PluginHook = (typeof PLUGIN_HOOKS)[number]
|
|
88
|
+
/** The hooks that are functions; `commands` and `fingerprint` are objects. */
|
|
89
|
+
export const PLUGIN_FUNCTION_HOOKS = PLUGIN_HOOKS.filter(
|
|
90
|
+
(h): h is Exclude<PluginHook, 'commands' | 'fingerprint'> =>
|
|
91
|
+
h !== 'commands' && h !== 'fingerprint',
|
|
92
|
+
)
|
|
93
|
+
type PluginHookKeys = Exclude<keyof Plugin, 'name'>
|
|
94
|
+
type HooksNotListed = Exclude<PluginHookKeys, PluginHook>
|
|
95
|
+
type HooksNotOnPlugin = Exclude<PluginHook, PluginHookKeys>
|
|
96
|
+
// Both `never`, or this line does not compile: the list and the type agree.
|
|
97
|
+
const _pluginHooksMatch: [HooksNotListed, HooksNotOnPlugin] extends [never, never] ? true : never =
|
|
98
|
+
true
|
|
99
|
+
void _pluginHooksMatch
|
|
100
|
+
|
|
50
101
|
export interface ProjectConfig {
|
|
51
102
|
/** Tasks declared by this project, keyed by task name. */
|
|
52
103
|
tasks?: Record<string, TaskConfig>
|
|
@@ -57,7 +108,13 @@ export interface TaskConfig {
|
|
|
57
108
|
* Optional one-line description of what this task does. Surfaced in
|
|
58
109
|
* the interactive task picker and the `--dry` text view so users
|
|
59
110
|
* can grep for what each task does without opening every config.
|
|
60
|
-
*
|
|
111
|
+
*
|
|
112
|
+
* No effect on scheduling or execution. It DOES participate in the
|
|
113
|
+
* cache key, because the key hashes the whole resolved task config
|
|
114
|
+
* (`docs/caching.md`, step 5) — deliberate: editing a description
|
|
115
|
+
* isn't a correctness change, but a one-off re-run costs little and
|
|
116
|
+
* carving exceptions out of the resolved object is what invites
|
|
117
|
+
* stale hits.
|
|
61
118
|
*/
|
|
62
119
|
description?: string
|
|
63
120
|
/**
|
|
@@ -89,128 +146,120 @@ export interface TaskConfig {
|
|
|
89
146
|
* runs.
|
|
90
147
|
*/
|
|
91
148
|
cache?: CacheConfig
|
|
92
|
-
/**
|
|
93
|
-
* Sandbox configuration. **Sandbox is opt-in per task.**
|
|
94
|
-
* - omitted entirely → task runs unsandboxed.
|
|
95
|
-
* - `sandbox: {}` → opts in with the minimum baseline (only
|
|
96
|
-
* resolved inputs readable, only static-prefix of outputs
|
|
97
|
-
* writable, network blocked).
|
|
98
|
-
* - `sandbox: { ... }` → opts in with the explicit options below.
|
|
99
|
-
*
|
|
100
|
-
* No inheritance, no workspace defaults, no built-in escapes. The
|
|
101
|
-
* sandbox sees STRICTLY the union of resolved `cache.inputs.files`
|
|
102
|
-
* + this block's `allowRead` (for reads) and the static prefix of
|
|
103
|
-
* `cache.outputs.files` + this block's `allowWrite` (for writes).
|
|
104
|
-
* If a task needs `node_modules` or `/tmp`, declare them here
|
|
105
|
-
* explicitly.
|
|
106
|
-
*
|
|
107
|
-
* **Policy: fail on violation.** If the sandbox detects an
|
|
108
|
-
* undeclared read/write (macOS log monitor) or the child returns
|
|
109
|
-
* non-zero because it couldn't reach a path it expected (Linux
|
|
110
|
-
* structural deny), the task fails. No cache is written for a
|
|
111
|
-
* failed task.
|
|
112
|
-
*
|
|
113
|
-
* Activation requires `exec` (group tasks have no command to wrap)
|
|
114
|
-
* and is silently skipped for persistent tasks (dev servers need
|
|
115
|
-
* unrestricted network + an indefinite process).
|
|
116
|
-
*/
|
|
117
|
-
sandbox?: SandboxConfig
|
|
118
149
|
}
|
|
119
150
|
|
|
120
151
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
152
|
+
* What a sandboxed task may do, declared as CAPABILITIES.
|
|
153
|
+
*
|
|
154
|
+
* One shape for the whole policy. You say what the task is allowed to
|
|
155
|
+
* touch; vx decides how each capability is realised — some become sandbox
|
|
156
|
+
* runtime config, some become rules in the OS policy, and one the platform
|
|
157
|
+
* cannot express is an error rather than a silent no-op. None of that
|
|
158
|
+
* reaches the config.
|
|
124
159
|
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* - tilde paths → expanded against the user's home (`~/.npmrc`)
|
|
160
|
+
* The baseline is always the task's own declarations: it may read its
|
|
161
|
+
* resolved `cache.inputs.files`, write the prefixes of its
|
|
162
|
+
* `cache.outputs.files`, and reach nothing else. Everything here is on top
|
|
163
|
+
* of that.
|
|
130
164
|
*
|
|
131
|
-
*
|
|
165
|
+
* Paths are project-relative, absolute, or `~`-expanded — and are path
|
|
166
|
+
* PREFIXES, never globs (a glob cannot be enforced by a mount).
|
|
132
167
|
*/
|
|
133
168
|
export interface SandboxConfig {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
* Additional read-allowed paths beyond resolved `cache.inputs.files`.
|
|
137
|
-
* These are unioned with the declared inputs to form the complete
|
|
138
|
-
* allowRead set passed to the sandbox.
|
|
139
|
-
*/
|
|
140
|
-
allowRead?: string[]
|
|
141
|
-
/**
|
|
142
|
-
* Additional write-allowed paths beyond the static prefix of
|
|
143
|
-
* `cache.outputs.files`. Tasks with no declared outputs and no extra
|
|
144
|
-
* `allowWrite` can write to nowhere — typical for `lint`/`typecheck`.
|
|
145
|
-
*/
|
|
146
|
-
allowWrite?: string[]
|
|
147
|
-
/**
|
|
148
|
-
* Permit writes to `.git/config` files. Default `false` — most build
|
|
149
|
-
* tools should not be reconfiguring git.
|
|
150
|
-
*/
|
|
151
|
-
allowGitConfig?: boolean
|
|
152
|
-
|
|
153
|
-
// === Network ===
|
|
154
|
-
/**
|
|
155
|
-
* Network policy.
|
|
156
|
-
* - `false` (default) — block all outbound traffic.
|
|
157
|
-
* - `true` — allow all outbound traffic.
|
|
158
|
-
* - object — fine-grained control (domains / sockets / etc.).
|
|
159
|
-
*/
|
|
160
|
-
network?: boolean | SandboxNetworkConfig
|
|
161
|
-
|
|
162
|
-
// === Process behavior ===
|
|
169
|
+
/** Capabilities granted on top of the task's own declarations. */
|
|
170
|
+
allow?: SandboxGrants
|
|
163
171
|
/**
|
|
164
|
-
*
|
|
165
|
-
*
|
|
172
|
+
* Capabilities taken away. Evaluated before `allow`, so a domain in both
|
|
173
|
+
* is denied.
|
|
166
174
|
*/
|
|
167
|
-
|
|
175
|
+
deny?: SandboxDenials
|
|
168
176
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
177
|
+
* Denials to leave out of the report, in the same shape as `allow`.
|
|
178
|
+
*
|
|
179
|
+
* This does NOT permit anything: the OS still denies the operation, and
|
|
180
|
+
* a command that cannot proceed without it still fails. It says "I know,
|
|
181
|
+
* and it does not affect this task's output" — for a denial you cannot
|
|
182
|
+
* grant away and do not want failing the run.
|
|
183
|
+
*
|
|
184
|
+
* Unlike `allow`, the values are GLOB PATTERNS, and they work the same
|
|
185
|
+
* on every platform: `allow` has to be expressible by the OS sandbox
|
|
186
|
+
* (bwrap mounts a path; it cannot mount a pattern), while `ignore` only
|
|
187
|
+
* has to match a line vx already holds. Relative patterns anchor at the
|
|
188
|
+
* project dir.
|
|
189
|
+
*
|
|
190
|
+
* ```ts
|
|
191
|
+
* ignore: { write: ['*.bun-build'] } // bun --compile stages a random
|
|
192
|
+
* // temp name in the cwd
|
|
193
|
+
* ```
|
|
171
194
|
*/
|
|
172
|
-
|
|
195
|
+
ignore?: SandboxGrants
|
|
173
196
|
/**
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
197
|
+
* Accept a weaker sandbox when vx itself already runs inside one
|
|
198
|
+
* (a container, another sandbox). Without it, nesting fails fast rather
|
|
199
|
+
* than pretending to confine.
|
|
177
200
|
*/
|
|
178
|
-
|
|
201
|
+
weakerWhenNested?: boolean
|
|
202
|
+
/** Accept weaker network isolation (host proxy) for lower overhead. */
|
|
203
|
+
weakerNetworkIsolation?: boolean
|
|
204
|
+
}
|
|
179
205
|
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
206
|
+
export interface SandboxGrants {
|
|
207
|
+
/** Readable path prefixes, beyond the resolved `cache.inputs.files`. */
|
|
208
|
+
read?: string[]
|
|
209
|
+
/** Writable path prefixes, beyond the `cache.outputs.files` prefixes. */
|
|
210
|
+
write?: string[]
|
|
211
|
+
/**
|
|
212
|
+
* Reachable domains. `true` allows all, `['*.example.com']` a pattern.
|
|
213
|
+
* Omitted means no network at all.
|
|
214
|
+
*/
|
|
215
|
+
network?: true | string[]
|
|
216
|
+
/**
|
|
217
|
+
* Kernel information the task may query, by info type — the name that
|
|
218
|
+
* appears in a denial, e.g. `vfs.disk-space` (every Bun and git process
|
|
219
|
+
* probes free space at startup).
|
|
220
|
+
*/
|
|
221
|
+
systemInfo?: string[]
|
|
222
|
+
/** Unix socket paths the task may connect to. `true` allows any. */
|
|
223
|
+
unixSockets?: true | string[]
|
|
224
|
+
/**
|
|
225
|
+
* Bind and reach localhost ports — a test that boots a server for
|
|
226
|
+
* itself. A port LIST additionally makes those ports reachable from
|
|
227
|
+
* outside the sandbox (a dev server another task or the developer
|
|
228
|
+
* talks to): on Linux, where a sandboxed task lives in its own network
|
|
229
|
+
* namespace, each listed port is bridged out to the host's loopback;
|
|
230
|
+
* on macOS the host already sees them and the list means `true`.
|
|
231
|
+
*/
|
|
232
|
+
localBinding?: boolean | number[]
|
|
233
|
+
/** macOS: Mach service names the task may look up. */
|
|
234
|
+
machLookup?: string[]
|
|
235
|
+
/** Acquire a TTY. Rare outside interactive tools. */
|
|
236
|
+
pty?: boolean
|
|
237
|
+
/** Write to `.git/config`. Most build tools should not. */
|
|
238
|
+
gitConfig?: boolean
|
|
188
239
|
}
|
|
189
240
|
|
|
190
|
-
export interface
|
|
191
|
-
/**
|
|
192
|
-
|
|
193
|
-
* `*` (allow all).
|
|
194
|
-
*/
|
|
195
|
-
allowedDomains?: string[]
|
|
196
|
-
/** Domains explicitly blocked, evaluated before allowedDomains. */
|
|
197
|
-
deniedDomains?: string[]
|
|
198
|
-
/** Unix socket paths the task may connect to. */
|
|
199
|
-
allowUnixSockets?: string[]
|
|
200
|
-
/** Allow any Unix socket. Use with care. */
|
|
201
|
-
allowAllUnixSockets?: boolean
|
|
202
|
-
/**
|
|
203
|
-
* Allow binding to local ports (e.g. a test that boots a server on
|
|
204
|
-
* localhost for itself to query).
|
|
205
|
-
*/
|
|
206
|
-
allowLocalBinding?: boolean
|
|
207
|
-
/** macOS only. Mach service names the task may look up. */
|
|
208
|
-
allowMachLookup?: string[]
|
|
241
|
+
export interface SandboxDenials {
|
|
242
|
+
/** Domains blocked even if `allow.network` would admit them. */
|
|
243
|
+
network?: string[]
|
|
209
244
|
}
|
|
210
245
|
|
|
211
246
|
export interface ExecConfig {
|
|
212
247
|
/** Shell command to run, from the project's directory. */
|
|
213
248
|
command: string
|
|
249
|
+
/**
|
|
250
|
+
* `false` pins the task to this machine: it is never offered to a remote
|
|
251
|
+
* executor (it talks to a local daemon, the network, Docker, …). Default
|
|
252
|
+
* `true`. Tasks that are persistent, or depend on one, are pinned
|
|
253
|
+
* regardless — a worker cannot reach a port on the submitter.
|
|
254
|
+
*
|
|
255
|
+
* `'only'` is the inverse pin: the task exists to produce a REMOTE input
|
|
256
|
+
* tree (the canonical case is `pnpm install` feeding remote workers) and is
|
|
257
|
+
* a NO-OP on this machine — never executed locally, its declared outputs
|
|
258
|
+
* never cleaned or restored on this disk. With no remote executor declared
|
|
259
|
+
* the task succeeds without running and dependents use whatever ambient
|
|
260
|
+
* state the machine has, exactly as they did before the field existed.
|
|
261
|
+
*/
|
|
262
|
+
remote?: boolean | 'only'
|
|
214
263
|
/** Environment exposed to the child process. */
|
|
215
264
|
env?: ExecEnv
|
|
216
265
|
/**
|
|
@@ -239,16 +288,6 @@ export interface ExecConfig {
|
|
|
239
288
|
* default, including `retries: 0`.
|
|
240
289
|
*/
|
|
241
290
|
retries?: number
|
|
242
|
-
/**
|
|
243
|
-
* Resource RESERVATIONS for admission control — NOT enforcement: vx
|
|
244
|
-
* does not cgroup-limit, nice, or kill the task; it only decides what
|
|
245
|
-
* to co-schedule so concurrent reservations never exceed the CPU /
|
|
246
|
-
* memory budget. Each axis defaults to 0 = reserve nothing: the task
|
|
247
|
-
* runs subject only to the concurrency-count limit. A pure scheduling
|
|
248
|
-
* hint — the whole object is stripped from the cache key, so tuning a
|
|
249
|
-
* reservation never invalidates a cached result.
|
|
250
|
-
*/
|
|
251
|
-
resources?: ResourcesConfig
|
|
252
291
|
/**
|
|
253
292
|
* Long-running / continuous task (dev server, watcher, daemon).
|
|
254
293
|
* When present, the task is spawned but the runner does NOT wait
|
|
@@ -258,32 +297,29 @@ export interface ExecConfig {
|
|
|
258
297
|
* on ready, not on exit. The orchestrator SIGTERMs every persistent
|
|
259
298
|
* subprocess once the rest of the graph has finished.
|
|
260
299
|
*
|
|
261
|
-
* `cache` is
|
|
262
|
-
*
|
|
263
|
-
* of "done".
|
|
300
|
+
* `cache` is rejected on a persistent task — there's no exit code
|
|
301
|
+
* to cache and the work continues after vx's run notion of "done".
|
|
264
302
|
*/
|
|
265
303
|
persistent?: PersistentConfig
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Per-task resource reservation (see `ExecConfig.resources`). Grouped so
|
|
270
|
-
* a future axis slots in without new top-level `exec` fields; the loader
|
|
271
|
-
* rejects unknown keys.
|
|
272
|
-
*/
|
|
273
|
-
export interface ResourcesConfig {
|
|
274
304
|
/**
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
305
|
+
* Confine this task's process: what it may read, write and reach.
|
|
306
|
+
* Nothing is confined unless this is present.
|
|
307
|
+
*
|
|
308
|
+
* It sits in `exec` because it constrains the same spawn `env` and
|
|
309
|
+
* `resources` do — the environment the child gets, the CPU and memory it
|
|
310
|
+
* gets, the filesystem and network it gets. A group task has no process,
|
|
311
|
+
* so the field cannot be reached from one.
|
|
312
|
+
*
|
|
313
|
+
* **Policy: fail on violation.** An undeclared touch of the project's own
|
|
314
|
+
* files fails the task, and a failed task is never cached. Reaching
|
|
315
|
+
* OUTSIDE the project is denied but not reported — that is the wall
|
|
316
|
+
* doing its job, not a finding.
|
|
317
|
+
*
|
|
318
|
+
* A persistent task is confined the same way but never REPORTED on: the
|
|
319
|
+
* violation report reads the trace after the process exits, and a server
|
|
320
|
+
* exits at teardown. Enforced, not reported.
|
|
285
321
|
*/
|
|
286
|
-
|
|
322
|
+
sandbox?: SandboxConfig
|
|
287
323
|
}
|
|
288
324
|
|
|
289
325
|
export interface PersistentConfig {
|
|
@@ -337,8 +373,9 @@ export interface CacheOutputs {
|
|
|
337
373
|
* files anywhere in the workspace, including inside other projects'
|
|
338
374
|
* directories. Deliberate escape hatch — prefer project-relative
|
|
339
375
|
* `files` whenever the task can write inside its own dir. Two tasks
|
|
340
|
-
*
|
|
341
|
-
* vx
|
|
376
|
+
* whose workspace outputs provably overlap are refused at graph build,
|
|
377
|
+
* like overlapping `files` (vx cleans declared outputs before a run
|
|
378
|
+
* and before a restore, so the second would delete the first's).
|
|
342
379
|
*/
|
|
343
380
|
workspaceFiles?: string[]
|
|
344
381
|
}
|
package/src/exec/env.ts
CHANGED
|
@@ -8,7 +8,13 @@ import path from 'node:path'
|
|
|
8
8
|
// 3. define: explicit name=value pairs from the task config.
|
|
9
9
|
// 4. binPaths prepended to PATH (after all the above resolve PATH).
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Exported so the schema doc's enumeration of it can be PINNED against the
|
|
13
|
+
* real list: a hand-copied allowlist is how the two drift, and a reader
|
|
14
|
+
* asking "what does my build script actually see?" is asking a
|
|
15
|
+
* security-shaped question that a stale list answers wrongly.
|
|
16
|
+
*/
|
|
17
|
+
export const ESSENTIAL_ENV: readonly string[] = [
|
|
12
18
|
'PATH',
|
|
13
19
|
'HOME',
|
|
14
20
|
'SHELL',
|
|
@@ -36,6 +42,17 @@ const ESSENTIAL_ENV: readonly string[] = [
|
|
|
36
42
|
'PATHEXT',
|
|
37
43
|
]
|
|
38
44
|
|
|
45
|
+
/**
|
|
46
|
+
* The two variables vx sets on every task it spawns, over the layers
|
|
47
|
+
* above: the workspace root the run is in and the `project#task` running.
|
|
48
|
+
* `run()` reads them back so a task that shells out to `vx run` in its OWN
|
|
49
|
+
* workspace is refused before it forks without bound (a different
|
|
50
|
+
* workspace — a fixture, a benchmark — is fine). Not in the allowlist:
|
|
51
|
+
* they come from this run, never from the parent environment.
|
|
52
|
+
*/
|
|
53
|
+
export const VX_RUN_WORKSPACE_ENV = 'VX_RUN_WORKSPACE'
|
|
54
|
+
export const VX_RUN_TASK_ENV = 'VX_RUN_TASK'
|
|
55
|
+
|
|
39
56
|
export interface BuildEnvOptions {
|
|
40
57
|
passThrough: readonly string[]
|
|
41
58
|
define: Readonly<Record<string, string>>
|