@vzn/vx 0.0.16 → 0.0.18
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/README.md +66 -89
- package/index.ts +10 -0
- package/launcher.mjs +10 -11
- package/package.json +19 -5
- package/plugins/local-cache/index.ts +2 -0
- package/plugins/local-executor/index.ts +2 -0
- package/plugins/schedule-history/index.ts +2 -0
- package/src/bin.ts +2 -2
- package/src/cache/archive.ts +557 -0
- package/src/cache/cache.ts +929 -250
- package/src/cache/cas-backend.ts +20 -2
- package/src/cache/chained-cache.ts +202 -0
- package/src/cache/digest.ts +0 -15
- package/src/cache/index.ts +13 -3
- package/src/cache/inputs.ts +445 -41
- package/src/cache/layered-cache.ts +88 -30
- package/src/cache/tar-stream.ts +358 -0
- package/src/cli/cache.ts +36 -10
- package/src/cli/help.ts +167 -128
- package/src/cli/index.ts +74 -43
- package/src/cli/info.ts +30 -0
- package/src/cli/last.ts +140 -0
- package/src/cli/migrate-emit.ts +21 -0
- package/src/cli/migrate-nx.ts +23 -0
- package/src/cli/migrate-scripts.ts +127 -0
- package/src/cli/migrate-turbo.ts +43 -6
- package/src/cli/migrate.ts +119 -33
- package/src/cli/plan-format.ts +58 -3
- package/src/cli/plugin-commands.ts +96 -0
- package/src/cli/prune.ts +291 -0
- package/src/cli/run.ts +272 -80
- package/src/cli/upgrade.ts +7 -6
- package/src/cli/watch.ts +259 -61
- package/src/cli/why.ts +193 -0
- package/src/config.ts +40 -23
- package/src/exec/env.ts +7 -1
- package/src/exec/executor.ts +239 -0
- package/src/exec/index.ts +19 -1
- package/src/exec/runner.ts +116 -29
- package/src/exec/sandbox-runtime.ts +194 -24
- package/src/graph/index.ts +3 -1
- package/src/graph/scheduler.ts +92 -18
- package/src/graph/task-graph.ts +220 -6
- package/src/index.ts +82 -84
- package/src/orchestrator/deferred-outputs.ts +144 -0
- package/src/orchestrator/download-policy.ts +171 -0
- package/src/orchestrator/events.ts +30 -0
- package/src/orchestrator/execute-task.ts +328 -94
- package/src/orchestrator/failure-mode.ts +61 -0
- package/src/orchestrator/framed-output.ts +87 -35
- package/src/orchestrator/history.ts +78 -31
- package/src/orchestrator/index.ts +43 -90
- package/src/orchestrator/local-shortcircuit.ts +22 -0
- package/src/orchestrator/logger.ts +196 -14
- package/src/orchestrator/metrics.ts +57 -1376
- package/src/orchestrator/missing-plugin.ts +9 -0
- package/src/orchestrator/options.ts +22 -4
- package/src/orchestrator/plan.ts +144 -1
- package/src/orchestrator/plugin-host.ts +199 -119
- package/src/orchestrator/plugin.ts +144 -57
- package/src/orchestrator/prepare.ts +220 -69
- package/src/orchestrator/remote-prefetch.ts +12 -8
- package/src/orchestrator/resources.ts +20 -34
- package/src/orchestrator/run-artifacts.ts +41 -16
- package/src/orchestrator/run-context.ts +167 -29
- package/src/orchestrator/run-report.ts +73 -46
- package/src/orchestrator/run.ts +590 -122
- package/src/orchestrator/stable-keys.ts +74 -33
- package/src/orchestrator/status-line.ts +47 -8
- package/src/orchestrator/summary.ts +22 -1
- package/src/orchestrator/tally.ts +78 -35
- package/src/orchestrator/task-hash.ts +105 -11
- package/src/orchestrator/task-log-buffer.ts +291 -0
- package/src/orchestrator/telemetry-host.ts +24 -6
- package/src/orchestrator/telemetry.ts +181 -21
- package/src/orchestrator/upstream.ts +72 -31
- package/src/orchestrator/verify.ts +12 -2
- package/src/plugins/local-cache/index.ts +11 -0
- package/src/plugins/local-executor/index.ts +45 -0
- package/src/plugins/schedule-history/index.ts +109 -0
- package/src/util/edit-distance.ts +21 -0
- package/src/util/errors.ts +15 -1
- package/src/util/index.ts +7 -2
- package/src/util/num.ts +37 -0
- package/src/util/paths.ts +43 -0
- package/src/util/settle.ts +48 -0
- package/src/util/size.ts +5 -1
- package/src/util/tail.ts +66 -0
- package/src/util/timing.ts +63 -0
- package/src/workspace/affected.ts +142 -15
- package/src/workspace/config-cache.ts +304 -0
- package/src/workspace/config-eval.ts +210 -0
- package/src/workspace/config-imports.ts +161 -0
- package/src/workspace/filter.ts +22 -4
- package/src/workspace/fingerprint.ts +16 -1
- package/src/workspace/index.ts +15 -3
- package/src/workspace/lockfile.ts +13 -2
- package/src/workspace/nested-dirs.ts +21 -7
- package/src/workspace/package-graph.ts +23 -20
- package/src/workspace/project-loader.ts +503 -59
- package/src/workspace/workspace.ts +213 -72
- 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/orchestrator/devframe-surface.ts +0 -74
- package/src/orchestrator/predict.ts +0 -106
- package/src/orchestrator/protocol.ts +0 -156
- package/src/orchestrator/run-state.ts +0 -145
- package/src/orchestrator/wire-render.ts +0 -92
- package/src/orchestrator/wire.ts +0 -212
|
@@ -1,64 +1,131 @@
|
|
|
1
|
-
// Plugin API —
|
|
1
|
+
// Plugin API — the ONE `VxPlugin` contract (docs/design/pipeline-2026-09.md).
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// receives a PluginContext with the bus + workspace metadata and can
|
|
7
|
-
// install lifecycle hooks. Plugins observe; they do not redirect.
|
|
3
|
+
// Users register plugins via defineWorkspace({ plugins }). Behaviour
|
|
4
|
+
// capabilities (`cache`, `executor`) return objects core calls INTO;
|
|
5
|
+
// observe capabilities (`telemetry`, `setup` on the bus) can only read.
|
|
8
6
|
//
|
|
9
|
-
// Crash isolation: a plugin that throws
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// plugin that throws inside a hook is logged + the plugin is disabled
|
|
13
|
-
// for the remainder of the run (mirrors the deleted Observer's
|
|
14
|
-
// makeSafeObserver pattern; surfaces are isolated from execution).
|
|
15
|
-
//
|
|
16
|
-
// Hook return values: today every hook is fire-and-forget (the bus IS
|
|
17
|
-
// the message pipe; hooks just give users an ergonomic place to read
|
|
18
|
-
// it). One write-capable hook is reserved for a future iteration —
|
|
19
|
-
// onCacheLookup returning { skip: true } — but not in v1.
|
|
7
|
+
// Crash isolation: a plugin that throws in a load-bearing hook aborts the
|
|
8
|
+
// run with a clean UserError naming plugin + hook. A plugin that throws
|
|
9
|
+
// inside a bus hook is logged and disabled for the remainder of the run.
|
|
20
10
|
|
|
21
11
|
import type { Cache, CacheLayer, CachePolicy } from '../cache/index.js'
|
|
12
|
+
import type { ProjectConfig, WorkspaceConfig } from '../config.js'
|
|
13
|
+
import type { TaskExecutor } from '../exec/index.js'
|
|
22
14
|
import type { TaskNode, TaskOutcome } from '../graph/index.js'
|
|
23
15
|
import { UserError } from '../util/index.js'
|
|
24
|
-
import type { EventBus, RunStartInfo
|
|
25
|
-
import type { RunBackend, RunRequest } from './protocol.js'
|
|
16
|
+
import type { EventBus, RunStartInfo } from './events.js'
|
|
26
17
|
import type { TelemetryContext, TelemetrySink } from './telemetry.js'
|
|
27
18
|
|
|
28
19
|
/**
|
|
29
|
-
* A vx plugin. Contributes any subset of
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* vx.workspace.ts via
|
|
20
|
+
* A vx plugin. Contributes any subset of the run-level capabilities —
|
|
21
|
+
* where work runs (executor), which cache is used (cache), who
|
|
22
|
+
* observes the run (telemetry). It never changes WHAT a task is (the
|
|
23
|
+
* command string — principle #3), only where and how that command is
|
|
24
|
+
* executed. Registered explicitly in vx.workspace.ts via
|
|
25
|
+
* defineWorkspace({ plugins: [...] }). No auto-discovery.
|
|
34
26
|
*
|
|
35
27
|
* The old observe-only `Plugin` (`{ name, setup(ctx) }`) is a subset of
|
|
36
28
|
* this shape: a plugin with only `setup` installs and runs exactly as
|
|
37
|
-
* before via `installPlugins`. The
|
|
38
|
-
* `plugin-host.ts
|
|
29
|
+
* before via `installPlugins`. The capabilities are consulted by
|
|
30
|
+
* `plugin-host.ts`; core's own executor and cache are plugins too
|
|
31
|
+
* (src/plugins/), declared by the workspace — there is no fallback
|
|
32
|
+
* outside the list.
|
|
39
33
|
*/
|
|
40
34
|
export interface VxPlugin {
|
|
41
35
|
/** Stable identifier, convention `'org/name'`. Used in errors + precedence logs. */
|
|
42
36
|
readonly name: string
|
|
43
37
|
|
|
44
|
-
// ---
|
|
38
|
+
// --- PIPELINE stages (shape the run before it executes — opt-in) ----------
|
|
39
|
+
//
|
|
40
|
+
// Each receives the object core is about to use and edits it IN PLACE;
|
|
41
|
+
// core re-validates after the last plugin, so a plugin cannot produce what
|
|
42
|
+
// the loader would refuse from a user. Declaration order is the order.
|
|
43
|
+
// Whatever a stage changes reaches the cache key by construction: the key
|
|
44
|
+
// hashes the task config AFTER `project` ran. See
|
|
45
|
+
// docs/design/pipeline-2026-09.md.
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The workspace config, before anything is derived from it (concurrency,
|
|
49
|
+
* cacheDir, timeout, …). `plugins` is already fixed by the time this runs.
|
|
50
|
+
*/
|
|
51
|
+
config?(workspace: WorkspaceConfig, ctx: WorkspaceHookContext): void | Promise<void>
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* One project's validated config, right after it loaded and before the
|
|
55
|
+
* graph is built. Add, remove or edit tasks. Runs for every loaded project
|
|
56
|
+
* on every run — a config's cached evaluation is the user's file, and the
|
|
57
|
+
* hook is applied on top of it live.
|
|
58
|
+
*/
|
|
59
|
+
project?(config: ProjectConfig, ctx: ProjectHookContext): void | Promise<void>
|
|
45
60
|
|
|
46
61
|
/**
|
|
47
|
-
*
|
|
48
|
-
* or
|
|
49
|
-
*
|
|
50
|
-
* plugin's
|
|
62
|
+
* The task graph, after `dependsOn` expansion and before scheduling. Add
|
|
63
|
+
* or drop edges (`node.deps`), mark tasks requested, adjust resources. A
|
|
64
|
+
* dep naming a task that is not in the graph, or a cycle, is refused with
|
|
65
|
+
* the plugin's name.
|
|
51
66
|
*/
|
|
52
|
-
|
|
67
|
+
graph?(nodes: Map<string, TaskNode>, ctx: GraphHookContext): void | Promise<void>
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Extra cache-key material for one task: a `{ name: value }` record that
|
|
71
|
+
* is folded into the key (and named in `vx why`) — a tool version, a
|
|
72
|
+
* feature flag, anything the declared inputs cannot see. Runs once per
|
|
73
|
+
* task per run, before any key is derived. Return undefined to add
|
|
74
|
+
* nothing. Values must be deterministic for the same inputs or the key
|
|
75
|
+
* never hits.
|
|
76
|
+
*/
|
|
77
|
+
key?(
|
|
78
|
+
task: TaskNode,
|
|
79
|
+
ctx: KeyHookContext,
|
|
80
|
+
):
|
|
81
|
+
| Readonly<Record<string, string>>
|
|
82
|
+
| undefined
|
|
83
|
+
| Promise<Readonly<Record<string, string>> | undefined>
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Scheduling priorities: task id → weight, higher runs first among READY
|
|
87
|
+
* tasks (merged over the structural baseline, which stays the tie-break).
|
|
88
|
+
* Runs once, after the graph is final. A later plugin's weight for a task
|
|
89
|
+
* overrides an earlier one's. Return undefined to leave the baseline.
|
|
90
|
+
*/
|
|
91
|
+
schedule?(
|
|
92
|
+
nodes: ReadonlyMap<string, TaskNode>,
|
|
93
|
+
ctx: ScheduleHookContext,
|
|
94
|
+
): ReadonlyMap<string, number> | undefined | Promise<ReadonlyMap<string, number> | undefined>
|
|
95
|
+
|
|
96
|
+
// --- CLI verbs (opt-in) ---------------------------------------------------
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Verbs this plugin adds to the `vx` CLI, keyed by name. Consulted only
|
|
100
|
+
* for a verb core does not know — core's own verbs always win — and only
|
|
101
|
+
* when the cwd is inside a workspace that declares the plugin. `vx help`
|
|
102
|
+
* lists them under "Plugin commands". A verb's exit code is the process
|
|
103
|
+
* exit code; a thrown `UserError` prints cleanly, like core's own.
|
|
104
|
+
*/
|
|
105
|
+
readonly commands?: Readonly<Record<string, PluginCommand>>
|
|
106
|
+
|
|
107
|
+
// --- BEHAVIOR capabilities (change WHAT/HOW work runs — opt-in) -----------
|
|
53
108
|
|
|
54
109
|
/**
|
|
55
110
|
* Contribute a cache layer. Returns a CacheLayer wrapping (or replacing)
|
|
56
111
|
* the local Cache, or undefined to decline. Consulted ONCE per prepareRun.
|
|
57
|
-
* Precedence: first non-undefined plugin cache wins
|
|
58
|
-
*
|
|
112
|
+
* Precedence: first non-undefined plugin cache wins, in declaration
|
|
113
|
+
* order. `@vzn/vx/plugins/local-cache` hands back the bare local Cache;
|
|
114
|
+
* a list with no provider fails the run before any task starts.
|
|
59
115
|
*/
|
|
60
116
|
cache?(ctx: CacheContext): CacheLayer | undefined | Promise<CacheLayer | undefined>
|
|
61
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Contribute a task executor — WHERE one task's command runs. Consulted
|
|
120
|
+
* ONCE per run; every contributed executor is kept, in declaration order,
|
|
121
|
+
* and per task the first whose `accepts()` passes executes it. Nothing is
|
|
122
|
+
* appended: a workspace declares `localExecutorPlugin()` (from
|
|
123
|
+
* `@vzn/vx/plugins/local-executor`) like any other, and a run with no
|
|
124
|
+
* executor at all fails before any task runs. Persistent tasks never
|
|
125
|
+
* reach an executor (local by construction).
|
|
126
|
+
*/
|
|
127
|
+
executor?(ctx: ExecutorContext): TaskExecutor | undefined | Promise<TaskExecutor | undefined>
|
|
128
|
+
|
|
62
129
|
// --- OBSERVE-ONLY capability (cannot change behavior — by construction) ---
|
|
63
130
|
|
|
64
131
|
/**
|
|
@@ -78,15 +145,6 @@ export interface VxPlugin {
|
|
|
78
145
|
| undefined
|
|
79
146
|
| Promise<TelemetrySink | TelemetrySink[] | undefined>
|
|
80
147
|
|
|
81
|
-
/**
|
|
82
|
-
* @deprecated Prefer `telemetry`. Contribute an event sink — a consumer of
|
|
83
|
-
* the serializable WireEvent stream, subscribed for the whole run via
|
|
84
|
-
* wireForwarder. Fire-and-forget; a throwing sink is isolated and cannot
|
|
85
|
-
* break the run. Kept as a back-compat path; `telemetry` is the canonical,
|
|
86
|
-
* analytics-shaped export contract.
|
|
87
|
-
*/
|
|
88
|
-
eventSink?(ctx: EventSinkContext): EventSink | undefined | Promise<EventSink | undefined>
|
|
89
|
-
|
|
90
148
|
/**
|
|
91
149
|
* Optional one-time setup before any capability is consulted (validate the
|
|
92
150
|
* workspace, open a connection, read a token). Throwing aborts the run with
|
|
@@ -98,12 +156,6 @@ export interface VxPlugin {
|
|
|
98
156
|
teardown?(): void | Promise<void>
|
|
99
157
|
}
|
|
100
158
|
|
|
101
|
-
/** A WireEvent consumer. onEvent is fire-and-forget; flush is awaited at teardown. */
|
|
102
|
-
export interface EventSink {
|
|
103
|
-
onEvent(event: WireEvent): void
|
|
104
|
-
flush?(): Promise<void>
|
|
105
|
-
}
|
|
106
|
-
|
|
107
159
|
/** Shared, read-only context every capability factory receives. */
|
|
108
160
|
interface BaseContext {
|
|
109
161
|
readonly workspaceRoot: string
|
|
@@ -113,11 +165,41 @@ interface BaseContext {
|
|
|
113
165
|
}
|
|
114
166
|
|
|
115
167
|
export interface PluginSetupContext extends BaseContext {}
|
|
116
|
-
export interface EventSinkContext extends BaseContext {}
|
|
117
168
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
169
|
+
/** One CLI verb contributed by a plugin. */
|
|
170
|
+
export interface PluginCommand {
|
|
171
|
+
/** One line for `vx help`. */
|
|
172
|
+
readonly description: string
|
|
173
|
+
run(argv: readonly string[], ctx: CommandContext): number | Promise<number>
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface CommandContext extends BaseContext {}
|
|
177
|
+
|
|
178
|
+
/** `config` runs before the cache dir is known — it may be what the hook changes. */
|
|
179
|
+
export interface WorkspaceHookContext {
|
|
180
|
+
readonly workspaceRoot: string
|
|
181
|
+
warn(message: string): void
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface ProjectHookContext extends BaseContext {
|
|
185
|
+
/** The package name (`package.json#name`). */
|
|
186
|
+
readonly name: string
|
|
187
|
+
/** The project's directory, absolute. */
|
|
188
|
+
readonly dir: string
|
|
189
|
+
/** The parsed `package.json`, read-only. */
|
|
190
|
+
readonly packageJson: Readonly<Record<string, unknown>>
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface GraphHookContext extends BaseContext {
|
|
194
|
+
/** Task ids the user asked for (the rest were pulled in by `dependsOn`). */
|
|
195
|
+
readonly requested: readonly string[]
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface KeyHookContext extends BaseContext {}
|
|
199
|
+
|
|
200
|
+
export interface ScheduleHookContext extends BaseContext {
|
|
201
|
+
/** The run's local cache handle — its `dbHandle()` holds the run history a policy can learn from. */
|
|
202
|
+
readonly localCache: Cache
|
|
121
203
|
}
|
|
122
204
|
|
|
123
205
|
export interface CacheContext extends BaseContext {
|
|
@@ -127,6 +209,11 @@ export interface CacheContext extends BaseContext {
|
|
|
127
209
|
readonly policy: CachePolicy
|
|
128
210
|
}
|
|
129
211
|
|
|
212
|
+
export interface ExecutorContext extends BaseContext {
|
|
213
|
+
/** The run's worker count — an executor that paces itself reads it here. */
|
|
214
|
+
readonly concurrency: number
|
|
215
|
+
}
|
|
216
|
+
|
|
130
217
|
export interface PluginContext {
|
|
131
218
|
/** Where the workspace lives on disk. */
|
|
132
219
|
readonly workspaceRoot: string
|
|
@@ -170,7 +257,7 @@ export interface Plugin {
|
|
|
170
257
|
* be installed synchronously inside setup() so no events are missed.
|
|
171
258
|
*
|
|
172
259
|
* OPTIONAL: a capability-only plugin (one that contributes
|
|
173
|
-
* `
|
|
260
|
+
* `cache`/`executor`/`telemetry` but no `setup`) is simply skipped by
|
|
174
261
|
* `installPlugins` — its capabilities are consulted by `plugin-host.ts`.
|
|
175
262
|
*/
|
|
176
263
|
setup?(ctx: PluginContext): void | Promise<void>
|
|
@@ -195,8 +282,8 @@ export interface InstallPluginsArgs {
|
|
|
195
282
|
* hooks are subscribed before the next plugin's setup runs. Throws if any
|
|
196
283
|
* plugin's setup() throws (the run cannot start with a broken plugin).
|
|
197
284
|
*
|
|
198
|
-
* A plugin without a `setup` is a capability-only plugin (
|
|
199
|
-
* /
|
|
285
|
+
* A plugin without a `setup` is a capability-only plugin (cache
|
|
286
|
+
* / executor / telemetry) — skipped here; those capabilities are consulted by
|
|
200
287
|
* `plugin-host.ts`.
|
|
201
288
|
*/
|
|
202
289
|
export async function installPlugins(args: InstallPluginsArgs): Promise<() => void> {
|
|
@@ -209,7 +296,7 @@ export async function installPlugins(args: InstallPluginsArgs): Promise<() => vo
|
|
|
209
296
|
if (typeof plugin.name !== 'string' || plugin.name.length === 0) {
|
|
210
297
|
throw new UserError('plugin missing `name` field')
|
|
211
298
|
}
|
|
212
|
-
// No setup → a capability-only plugin (
|
|
299
|
+
// No setup → a capability-only plugin (cache / executor / telemetry),
|
|
213
300
|
// consulted by plugin-host.ts; skip the hook install. A setup that's
|
|
214
301
|
// present but not callable is a real authoring error — reject it.
|
|
215
302
|
if (plugin.setup === undefined) continue
|