@visulima/task-runner 0.0.1 → 1.0.0-alpha.10

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 (58) hide show
  1. package/CHANGELOG.md +212 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +307 -29
  4. package/dist/index.d.ts +3166 -0
  5. package/dist/index.js +1 -0
  6. package/dist/packem_chunks/index.js +11 -0
  7. package/dist/packem_shared/Cache-C540ZPYk.js +2 -0
  8. package/dist/packem_shared/CompositeLifeCycle-D0zWvAXJ.js +1 -0
  9. package/dist/packem_shared/FileAccessTracker-DSNf03JW.js +47 -0
  10. package/dist/packem_shared/FingerprintManager-CYW2EwLc.js +2 -0
  11. package/dist/packem_shared/HttpRemoteCache-Ch-_9ejF.js +1 -0
  12. package/dist/packem_shared/INPUT_URI_SCHEMES-Csrd0tlg.js +1 -0
  13. package/dist/packem_shared/IncrementalFileHasher-jtLxMBKy.js +1 -0
  14. package/dist/packem_shared/LogReporter-BUPWiXAq.js +13 -0
  15. package/dist/packem_shared/ReapiRemoteCache-B3uQuVqP.js +251 -0
  16. package/dist/packem_shared/TaskOrchestrator-BgfOpjuB.js +2 -0
  17. package/dist/packem_shared/TerminalBuffer-BtZy7TpT.js +3 -0
  18. package/dist/packem_shared/TrackedTaskExecutor-D3-LNT_d.js +2 -0
  19. package/dist/packem_shared/V2_ROOT-injxWBrl.js +1 -0
  20. package/dist/packem_shared/actionDigestForTaskHash-BOL4fZ9v.js +1 -0
  21. package/dist/packem_shared/archive-CDfGy5Lm.js +1 -0
  22. package/dist/packem_shared/buildForwardDependencyMap-w1FVPFdv.js +3 -0
  23. package/dist/packem_shared/collectFiles-W4bnBRpb.js +1 -0
  24. package/dist/packem_shared/computeTaskHash-C2Iua2DL.js +1 -0
  25. package/dist/packem_shared/containsBlob-DBWgvkM_.js +1 -0
  26. package/dist/packem_shared/createInputHandler-CkDCpPYy.js +1 -0
  27. package/dist/packem_shared/createTaskGraph-CEYYI-DL.js +1 -0
  28. package/dist/packem_shared/defaultTaskRunner-CGbD4ahu.js +2 -0
  29. package/dist/packem_shared/detectFrameworks-WVZJOPgN.js +1 -0
  30. package/dist/packem_shared/detectScriptShell-CaTDk5cW.js +1 -0
  31. package/dist/packem_shared/digestBuffer-g11aCaDx.js +1 -0
  32. package/dist/packem_shared/enforceProjectConstraints-X49n3bVL.js +1 -0
  33. package/dist/packem_shared/expandArguments-4mab7-Ds.js +1 -0
  34. package/dist/packem_shared/expandShortcut-BErNHNXZ.js +1 -0
  35. package/dist/packem_shared/expandTokensInString-Cyx0qSFA.js +1 -0
  36. package/dist/packem_shared/expandWildcard-DE0dOOZF.js +1 -0
  37. package/dist/packem_shared/extractPackageName-BeL6Gc3a.js +1 -0
  38. package/dist/packem_shared/findCycle-BY8-jmzB.js +1 -0
  39. package/dist/packem_shared/formatTimingTable-CP3rsDwf.js +7 -0
  40. package/dist/packem_shared/generateRunSummary-ep21OCUT.js +1 -0
  41. package/dist/packem_shared/getCurrentBranch-D-qoZByx.js +1 -0
  42. package/dist/packem_shared/getMainWorktreeRoot-DRN_i8jA.js +1 -0
  43. package/dist/packem_shared/isNativeAvailable-BOavFPX1.js +1 -0
  44. package/dist/packem_shared/parseCommands-b1K2vIxj.js +1 -0
  45. package/dist/packem_shared/parsePartition-uzPNgtPp.js +1 -0
  46. package/dist/packem_shared/projectGraphToDot-K5A_CRoW.js +250 -0
  47. package/dist/packem_shared/resolveCacheMode-XhD7mg7G.js +1 -0
  48. package/dist/packem_shared/resolveOutputs-BBjdaraf.js +1 -0
  49. package/dist/packem_shared/runConcurrentFallback-CShJ7HUp.js +3 -0
  50. package/dist/packem_shared/runConcurrently-B471CUHO.js +1 -0
  51. package/dist/packem_shared/runTeardown-DBBpBAyb.js +1 -0
  52. package/dist/packem_shared/shell-quote-CksLqyXK.js +1 -0
  53. package/dist/packem_shared/stripQuotes-jkZb0CL9.js +1 -0
  54. package/dist/packem_shared/toChromeTrace-DxN5NQIU.js +1 -0
  55. package/dist/packem_shared/utils-BH2W5Wml.js +1 -0
  56. package/dist/packem_shared/withRestart-CWO6BKv_.js +1 -0
  57. package/index.js +775 -0
  58. package/package.json +86 -7
@@ -0,0 +1,3166 @@
1
+ import { Readable, Writable } from 'node:stream';
2
+ /**
3
+ * Represents a file access recorded during task execution.
4
+ *
5
+ * Write-intent accesses (`"write"`) are emitted when a task opens a file
6
+ * with `O_WRONLY`/`O_RDWR`/`O_CREAT`/`O_TRUNC` flags (strace) or calls
7
+ * `writeFile`/`appendFile`/`unlink`/`rename` (preload script).
8
+ * The orchestrator uses the overlap of reads and writes to the same
9
+ * workspace path to detect self-modifying tasks and skip caching.
10
+ */
11
+ interface FileAccess {
12
+ /** The absolute path of the file */
13
+ path: string;
14
+ /** The type of access */
15
+ type: "missing" | "read" | "readdir" | "stat" | "write";
16
+ }
17
+ /**
18
+ * Result of tracking file accesses during a command execution.
19
+ */
20
+ interface TrackingResult {
21
+ /** All file accesses recorded */
22
+ accesses: FileAccess[];
23
+ /** The command exit code */
24
+ code: number;
25
+ /** The command stdout + stderr output */
26
+ output: string;
27
+ }
28
+ /**
29
+ * Tracks which files a child process accesses during execution.
30
+ *
31
+ * Uses `strace` on Linux to intercept syscalls (open, openat, stat, lstat, access, getdents).
32
+ * Falls back to no tracking on unsupported platforms.
33
+ */
34
+ declare class FileAccessTracker {
35
+ #private;
36
+ constructor(workspaceRoot: string, excludePatterns?: RegExp[]);
37
+ /**
38
+ * Returns true if file access tracking is supported on the current platform.
39
+ */
40
+ isSupported(): boolean;
41
+ /**
42
+ * Runs a command and tracks all file system accesses.
43
+ * On unsupported platforms, runs the command without tracking.
44
+ */
45
+ track(command: string, options?: {
46
+ cwd?: string;
47
+ env?: Record<string, string | undefined>;
48
+ }): Promise<TrackingResult>;
49
+ /**
50
+ * Kills all active child processes. Called on abort/signal to prevent orphans.
51
+ */
52
+ killAll(): void;
53
+ }
54
+ /**
55
+ * Generates a preload script that can be used with NODE_OPTIONS to
56
+ * track file accesses in Node.js child processes.
57
+ *
58
+ * This is an alternative to strace that works cross-platform for Node.js processes.
59
+ */
60
+ declare const generatePreloadScript: (outputPath: string) => string;
61
+ /**
62
+ * Represents a stored fingerprint for a task execution.
63
+ */
64
+ interface TaskFingerprint {
65
+ /** Hash of the command arguments */
66
+ commandHash: string;
67
+ /** Directory listings recorded during execution (path -> sorted entries) */
68
+ directoryListings: Record<string, string[]>;
69
+ /** Hashes of fingerprinted environment variables */
70
+ envHashes: Record<string, string>;
71
+ /** Content hashes of files that were read during execution */
72
+ fileHashes: Record<string, string>;
73
+ /** Paths of files that were probed but didn't exist (ENOENT) */
74
+ missingFiles: string[];
75
+ /**
76
+ * Workspace-relative paths that were both read **and** written
77
+ * during execution. Populated when the tracker emits
78
+ * {@link FileAccess} entries with `"write"` type. The orchestrator
79
+ * uses a non-empty value here to skip caching a self-modifying
80
+ * task, whose fingerprint would otherwise capture post-write state
81
+ * and trigger false cache hits.
82
+ */
83
+ modifiedInputs?: string[];
84
+ }
85
+ /**
86
+ * Describes why a cache miss occurred.
87
+ */
88
+ interface CacheMissReason {
89
+ currentHash?: string;
90
+ detail: string;
91
+ previousHash?: string;
92
+ type: "file-changed" | "file-created" | "file-deleted" | "directory-changed" | "env-changed" | "args-changed" | "no-fingerprint";
93
+ }
94
+ /**
95
+ * Manages task fingerprints for auto-detection caching.
96
+ *
97
+ * Records which files a task accesses during execution, stores content
98
+ * hashes, and on subsequent runs checks if any accessed file has changed.
99
+ */
100
+ declare class FingerprintManager {
101
+ #private;
102
+ constructor(workspaceRoot: string);
103
+ createFingerprint(accesses: FileAccess[], command: string, args: Record<string, unknown>, envVariables: Record<string, string | undefined>, envPatterns?: string[], untrackedEnvVariables?: string[]): Promise<TaskFingerprint>;
104
+ /**
105
+ * Validates a stored fingerprint against the current state.
106
+ * Returns null if valid (cache hit), or an array of reasons (cache miss).
107
+ *
108
+ * Does NOT use the file hash cache — validation must see current disk state.
109
+ */
110
+ validate(fingerprint: TaskFingerprint): Promise<CacheMissReason[] | undefined>;
111
+ validateCommand(fingerprint: TaskFingerprint, command: string, args: Record<string, unknown>): CacheMissReason | undefined;
112
+ formatMissReasons(reasons: CacheMissReason[]): string;
113
+ }
114
+ /**
115
+ * Content-Addressable Storage digest. Mirrors REAPI's `Digest` message:
116
+ * a content hash and the size in bytes of the addressed blob.
117
+ *
118
+ * The hash is the lowercase hex sha256 of the blob's raw bytes. Size is
119
+ * the byte length of those bytes. Together they uniquely identify a CAS
120
+ * entry across HTTP and gRPC (REAPI) backends.
121
+ */
122
+ interface CasDigest {
123
+ /** Lowercase hex sha256 of the blob bytes. */
124
+ hash: string;
125
+ /** Size of the blob bytes, in bytes. */
126
+ sizeBytes: number;
127
+ }
128
+ /**
129
+ * Result of executing a single Action. Mirrors REAPI's `ActionResult`
130
+ * shape, with a vis-specific `fingerprint` extension for auto-fingerprint
131
+ * mode. Persisted as JSON locally; serialized as a protobuf over gRPC.
132
+ *
133
+ * Output paths are workspace-relative.
134
+ */
135
+ interface ActionResult {
136
+ /** Process exit code from the cached run. */
137
+ exitCode: number;
138
+ /** Optional vis-specific fingerprint (auto-fingerprint mode). */
139
+ fingerprint?: TaskFingerprint;
140
+ /**
141
+ * Per-output-directory entries. The `treeDigest` points to a CAS
142
+ * blob that is itself a serialized REAPI `Tree` message describing
143
+ * the directory hierarchy and its file digests.
144
+ */
145
+ outputDirectories: ReadonlyArray<{
146
+ path: string;
147
+ treeDigest: CasDigest;
148
+ }>;
149
+ /** Per-output-file entries. Workspace-relative paths. */
150
+ outputFiles: ReadonlyArray<{
151
+ digest: CasDigest;
152
+ isExecutable: boolean;
153
+ path: string;
154
+ }>;
155
+ /**
156
+ * CAS digest of the captured stdout/stderr stream. Optional because
157
+ * tasks with no output emit nothing. Most tasks emit at least
158
+ * "Done in Xs", so dedup across tasks is real.
159
+ */
160
+ stdoutDigest?: CasDigest;
161
+ }
162
+ /**
163
+ * Lazy handle for a CAS blob. Backends call `open()` only when they're
164
+ * ready to stream the bytes — keeps memory flat for multi-hundred-MB
165
+ * artifacts and avoids buffering blobs the server reports as already
166
+ * present (REAPI `FindMissingBlobs`).
167
+ */
168
+ interface BlobSource {
169
+ digest: CasDigest;
170
+ open: () => Promise<NodeJS.ReadableStream>;
171
+ }
172
+ /**
173
+ * Canonical cache-mode enum, replacing the original `read` / `write`
174
+ * boolean pair. One flag, three values, no implicit env-detection.
175
+ *
176
+ * - `"read"`: Pull cache hits, never push. Default for local dev when
177
+ * developers shouldn't poison the shared cache.
178
+ * - `"write"`: Push results, never read. Useful for refill / warm-up
179
+ * jobs that should always re-execute and re-upload.
180
+ * - `"readwrite"`: Both. Default for CI.
181
+ */
182
+ type CacheMode = "read" | "readwrite" | "write";
183
+ /**
184
+ * Compression algorithm used for artifact tarballs on the wire.
185
+ *
186
+ * - `"gzip"` (default): tar+gzip, matches Turborepo's protocol format
187
+ * and stays interop-safe with existing remote cache servers.
188
+ * - `"brotli"`: tar + Node brotli (BROTLI_MODE_TEXT, quality 4) — a
189
+ * solid ratio/speed trade-off for source-tree tarballs. Both upload
190
+ * and download sides must agree; switching invalidates existing
191
+ * remote entries (they will simply re-populate on next run).
192
+ *
193
+ * HTTP-only — REAPI servers negotiate compression via the
194
+ * `Capabilities` RPC + `grpc-encoding` metadata on the wire.
195
+ */
196
+ type RemoteCacheCompression = "brotli" | "gzip";
197
+ /**
198
+ * HMAC signing configuration for the HTTP backend.
199
+ *
200
+ * When set, every upload carries an `X-Artifact-Signature` header
201
+ * containing the HMAC-SHA256 digest of `hash | body`. On download,
202
+ * the client recomputes the HMAC and rejects any artifact whose
203
+ * signature doesn't match (constant-time comparison). REAPI servers
204
+ * do not consume this — REAPI integrity rides on sha256
205
+ * content-addressing instead.
206
+ */
207
+ interface RemoteCacheSigning {
208
+ /** Shared secret. Must be at least 16 characters. */
209
+ secret: string;
210
+ /**
211
+ * Reject downloads whose signature doesn't match or is missing.
212
+ * Set to `true` once every upload on your server is signed.
213
+ * @default false
214
+ */
215
+ verifyOnDownload?: boolean;
216
+ }
217
+ /**
218
+ * Canonical remote-cache configuration consumed by
219
+ * `createRemoteCacheBackend`. Both `HttpRemoteCache` and
220
+ * `ReapiRemoteCache` accept this shape directly — backend-specific
221
+ * fields (e.g. `signing`, `compression` for HTTP; `bearerToken`,
222
+ * `instanceName` for REAPI) are read by the corresponding constructor
223
+ * and ignored by the other.
224
+ *
225
+ * Living in `backends/types.ts` rather than each backend file keeps a
226
+ * single source of truth for `TaskRunnerOptions.remoteCache` and the
227
+ * `vis-config` typed surface.
228
+ */
229
+ interface RemoteCacheOptions {
230
+ /**
231
+ * Opt out of the REAPI safety check that refuses to send a bearer
232
+ * token over cleartext gRPC. Required only when the connection
233
+ * terminates inside a trusted boundary (loopback dev cache, mesh
234
+ * mTLS sidecar that strips/re-encrypts on the next hop). Default
235
+ * `false` — production callers should reach for `grpcs://` first.
236
+ *
237
+ * REAPI-only.
238
+ */
239
+ allowInsecureBearer?: boolean;
240
+ /**
241
+ * Wire-protocol selector. `"http"` is the Turborepo-compatible
242
+ * single-tarball cache; `"reapi"` switches to the Bazel Remote
243
+ * Execution API gRPC client, unlocking `bazel-remote`,
244
+ * BuildBuddy, BuildBarn, EngFlow as drop-in backends.
245
+ * @default "http"
246
+ */
247
+ backend?: "http" | "reapi";
248
+ /**
249
+ * Bearer token sent in REAPI's `authorization: Bearer {token}`
250
+ * gRPC metadata header. REAPI-only — HTTP backend uses `token`.
251
+ */
252
+ bearerToken?: string;
253
+ /** HTTP-only: tarball compression on the wire. @default "gzip" */
254
+ compression?: RemoteCacheCompression;
255
+ /**
256
+ * REAPI-only `instance_name` for multi-tenant servers (a single
257
+ * gRPC endpoint can host multiple logical caches keyed on the
258
+ * `instance_name` prefix).
259
+ */
260
+ instanceName?: string;
261
+ /**
262
+ * Local CAS root used by the per-blob {@link RemoteCacheBackend}
263
+ * methods. The HTTP wire format is still single-tarball; on
264
+ * retrieve the bridge extracts blobs into this root so a follow-up
265
+ * `fetchBlob` is just a local read.
266
+ */
267
+ localCasRoot?: string;
268
+ /**
269
+ * Canonical cache mode flag.
270
+ *
271
+ * - `"read"`: Pull cache hits, never push. Sensible default for
272
+ * local dev so a developer doesn't poison the shared cache while
273
+ * their workspace is dirty.
274
+ * - `"write"`: Push results, never read. Useful for refill /
275
+ * warm-up jobs that should always re-execute and re-upload.
276
+ * - `"readwrite"`: Both. Default for CI.
277
+ * @default "readwrite"
278
+ */
279
+ mode?: CacheMode;
280
+ /**
281
+ * Called when a fire-and-forget upload fails. Uploads are
282
+ * non-blocking, so without this hook errors are silently dropped.
283
+ * Provide it to log or report upload failures.
284
+ */
285
+ onUploadError?: (hash: string, error: unknown) => void;
286
+ /** HTTP-only: HMAC-SHA256 signing for upload integrity. */
287
+ signing?: RemoteCacheSigning;
288
+ /** Team / namespace for cache isolation. */
289
+ teamId?: string;
290
+ /** Per-call request timeout in milliseconds. @default 30000 */
291
+ timeout?: number;
292
+ /**
293
+ * HTTP authentication token sent as `Authorization: Bearer …`.
294
+ * For REAPI, use `bearerToken` instead — same wire mechanic, but
295
+ * a separate field so REAPI's cleartext-bearer guard can distinguish
296
+ * "user explicitly opted into a gRPC token" from "user set HTTP
297
+ * auth and accidentally selected the REAPI backend".
298
+ */
299
+ token?: string;
300
+ /**
301
+ * Cache server URL.
302
+ *
303
+ * - HTTP: `https://cache.example.com`
304
+ * - REAPI: `grpcs://host:port` (TLS, recommended) or `grpc://host:port`
305
+ * (cleartext — bearer tokens are refused unless `allowInsecureBearer`).
306
+ */
307
+ url: string;
308
+ }
309
+ /**
310
+ * Pluggable backend boundary for the remote cache. Implementations:
311
+ * - `HttpRemoteCache`: Turborepo wire-protocol-compatible HTTP cache.
312
+ * Bridges the single-tarball protocol to per-blob semantics by
313
+ * synthesizing an `ActionResult` from the extracted tarball contents.
314
+ * - `ReapiRemoteCache`: Bazel Remote Execution API gRPC client.
315
+ * Speaks `ActionCache` + `ContentAddressableStorage` services
316
+ * natively, unlocking bazel-remote / BuildBuddy / BuildBarn / EngFlow.
317
+ */
318
+ interface RemoteCacheBackend {
319
+ /**
320
+ * Release any persistent resources held by the backend (gRPC
321
+ * channels, HTTP keep-alive agents). Safe to call multiple times
322
+ * and safe to call when no work has been issued. Implementations
323
+ * must not throw — close failures are best-effort.
324
+ */
325
+ close: () => Promise<void>;
326
+ /**
327
+ * HEAD-equivalent existence check. REAPI: `GetActionResult` with
328
+ * `inline_*` fields disabled. HTTP: `HEAD /v8/artifacts/{hash}`.
329
+ */
330
+ containsAction: (actionDigest: CasDigest) => Promise<boolean>;
331
+ /**
332
+ * Stream a single CAS blob to disk. Returns `true` on success.
333
+ * Used to materialize output files referenced by an `ActionResult`.
334
+ */
335
+ fetchBlob: (digest: CasDigest, destinationPath: string) => Promise<boolean>;
336
+ /**
337
+ * Look up an Action's cached result. Resolves to `null` on miss.
338
+ * Implementations are responsible for fetching any CAS blobs the
339
+ * caller needs to materialize the outputs (or returning enough
340
+ * metadata for the caller to fetch them via `fetchBlob`).
341
+ */
342
+ retrieveAction: (actionDigest: CasDigest) => Promise<ActionResult | null>;
343
+ /**
344
+ * Persist an `ActionResult` and any blobs it references. Backends
345
+ * must enforce blobs-before-AC ordering on the wire so a partial
346
+ * failure cannot leave a cached result pointing at missing bytes.
347
+ */
348
+ storeAction: (actionDigest: CasDigest, result: ActionResult, blobs: ReadonlyArray<BlobSource>) => Promise<boolean>;
349
+ }
350
+ /**
351
+ * A predicate clause that can match a single value, a list of values
352
+ * (any-of), or — for env vars only — a `{ name, equals?, exists? }`
353
+ * triplet. `not.*` mirrors of every clause provide a negative form
354
+ * without having to wrap the whole `when` in a compound expression.
355
+ *
356
+ * Examples:
357
+ * `os: "linux"` — runs only on linux
358
+ * `os: ["linux", "darwin"]` — runs on linux or macOS
359
+ * `not.os: "windows"` — runs everywhere except windows
360
+ * `env: "CI"` — runs only when `process.env.CI` is truthy
361
+ * `env: { name: "NODE_ENV", equals: "production" }`
362
+ * `branch: ["main", "alpha"]`
363
+ * `ci: true` — runs only inside CI
364
+ */
365
+ interface WhenCondition {
366
+ /** Match against the current git branch (HEAD). */
367
+ branch?: string | string[];
368
+ /**
369
+ * Run only when invoked inside CI when `true`, only outside CI
370
+ * when `false`. Detects CI via the `CI` env var (the convention
371
+ * GitHub Actions, GitLab, CircleCI, Jenkins, etc. all share).
372
+ */
373
+ ci?: boolean;
374
+ /**
375
+ * Match against environment variables. A bare string asserts the
376
+ * variable is set and non-empty; the object form lets you match
377
+ * an exact value or just assert presence/absence.
378
+ */
379
+ env?: EnvMatcher | EnvMatcher[];
380
+ /** Negative mirrors. A task runs only when *all* `not.*` clauses fail. */
381
+ not?: {
382
+ branch?: string | string[];
383
+ ci?: boolean;
384
+ env?: EnvMatcher | EnvMatcher[];
385
+ os?: NodePlatform | NodePlatform[];
386
+ };
387
+ /**
388
+ * Match `process.platform` (`"linux" | "darwin" | "win32" | "freebsd"
389
+ * | "openbsd" | "sunos" | "aix"`). Pass `"windows"` as an alias for
390
+ * `"win32"` — easier to remember and matches what people type.
391
+ */
392
+ os?: NodePlatform | NodePlatform[];
393
+ }
394
+ /**
395
+ * An environment-variable match. The string form is shorthand for
396
+ * `{ name, exists: true }` (set + non-empty). The object form
397
+ * supports either presence assertions or exact-value matching.
398
+ */
399
+ type EnvMatcher = string | {
400
+ /** Match this exact value. Mutually exclusive with `exists`. */
401
+ equals?: string;
402
+ /** Assert the variable is set & non-empty (`true`) or unset/empty (`false`). */
403
+ exists?: boolean;
404
+ /** Variable name. */
405
+ name: string;
406
+ };
407
+ /** Aliased platform list — `"windows"` is sugar for Node's `"win32"`. */
408
+ type NodePlatform = "aix" | "darwin" | "freebsd" | "linux" | "openbsd" | "sunos" | "windows" | "win32";
409
+ /**
410
+ * Inputs for {@link evaluateWhen}. Pulled into a struct so tests can
411
+ * inject deterministic values and CLIs can reuse the same env/branch
412
+ * lookup across many task evaluations.
413
+ */
414
+ interface WhenContext {
415
+ /** Current git branch. Pass an empty string when not in a repo. */
416
+ branch?: string;
417
+ /** Whether we're inside CI. Defaults to `process.env.CI` truthy check. */
418
+ ci?: boolean;
419
+ /** Environment variables to match against. Defaults to `process.env`. */
420
+ env?: Record<string, string | undefined>;
421
+ /** Platform string. Defaults to `process.platform`. */
422
+ platform?: NodePlatform;
423
+ }
424
+ declare const getCurrentBranch: (cwd: string) => string;
425
+ /** Test-only escape hatch — clears every cached branch result. */
426
+ declare const resetBranchCache: () => void;
427
+ /**
428
+ * Evaluates a {@link WhenCondition} against the supplied context (or the
429
+ * live process state when omitted). Returns `true` when the task should
430
+ * run, `false` when every positive clause matches and every `not.*`
431
+ * clause is also satisfied.
432
+ *
433
+ * Semantics:
434
+ * - All positive clauses must match (AND).
435
+ * - All `not.*` clauses must not match (AND).
436
+ * - Within a single clause, an array means any-of (OR).
437
+ * - An empty/undefined `when` always returns `true`.
438
+ */
439
+ declare const evaluateWhen: (when: WhenCondition | undefined, context?: WhenContext) => boolean;
440
+ /**
441
+ * Returns a short human-readable explanation of why a `when` clause
442
+ * skipped a task. Used by lifecycle handlers to print diagnostics.
443
+ * Returns an empty string when the condition evaluates to true.
444
+ */
445
+ declare const explainWhen: (when: WhenCondition | undefined, context?: WhenContext) => string;
446
+ /**
447
+ * Represents a target that a task executes against.
448
+ */
449
+ interface TaskTarget {
450
+ /** Optional configuration name */
451
+ configuration?: string;
452
+ /** The project name */
453
+ project: string;
454
+ /** The target name (e.g., "build", "test", "lint") */
455
+ target: string;
456
+ }
457
+ /**
458
+ * Represents a single task to be executed.
459
+ */
460
+ /**
461
+ * Scheduling priority hint. The scheduler picks higher-priority tasks
462
+ * out of the ready-queue first; ties fall through to the graph-derived
463
+ * signals (dependent count, project depth).
464
+ *
465
+ * Use `"high"` for long-running leaves you want to kick off early
466
+ * (integration tests, e2e suites) so their wall-clock overlaps with
467
+ * the main build phase. Use `"low"` for work you don't mind deferring
468
+ * (linters, coverage uploads) when faster tasks contend for slots.
469
+ */
470
+ type TaskPriority = "high" | "low" | "normal";
471
+ /**
472
+ * Workspace-level concurrency caps. Maps a group name to the maximum
473
+ * number of in-flight tasks across every target that opts into the
474
+ * group via {@link TargetConfiguration.concurrencyGroup}. Independent
475
+ * of `--parallel`: the global cap still bounds total in-flight tasks,
476
+ * and group caps further bound the named subset. Values `<= 0` are
477
+ * ignored.
478
+ */
479
+ type ConcurrencyGroups = Record<string, number>;
480
+ /**
481
+ * A single entry in a task's `outputs` list.
482
+ *
483
+ * - `string` — a literal path *or* a glob pattern relative to the
484
+ * workspace root. Prefix with `!` to exclude files the positive
485
+ * patterns would otherwise include (e.g. `"!dist/cache/**"`).
486
+ * - `{ auto: true }` — use whatever files the task actually wrote
487
+ * during execution (resolved from the file-access tracker). Lets
488
+ * authors who don't know their exact output layout cache results
489
+ * without listing every path. Silently behaves as "no outputs" when
490
+ * tracking isn't active for this task.
491
+ */
492
+ type OutputSpec = string | {
493
+ auto: true;
494
+ };
495
+ interface Task {
496
+ /**
497
+ * When `true`, this task runs after the main task graph completes
498
+ * — even if upstream tasks failed or the run was interrupted.
499
+ * Used for cleanup, teardown, or notification tasks. Carried over
500
+ * from {@link TargetConfiguration.always} at task graph creation.
501
+ */
502
+ always?: boolean;
503
+ /** Whether this task is eligible for caching */
504
+ cache?: boolean;
505
+ /**
506
+ * When `false`, exit-0 runs whose output matched any
507
+ * {@link Task.warningPattern} are NOT written to the cache. Defaults
508
+ * to `true` — warnings are recorded on the result but don't suppress
509
+ * caching, matching the rushstack#1402 / Theme P expectation that a
510
+ * succeeded-with-warnings build is still incremental on the next run.
511
+ *
512
+ * Carried over from {@link TargetConfiguration.cacheOnWarning}.
513
+ */
514
+ cacheOnWarning?: boolean;
515
+ /**
516
+ * Per-task overrides for cache restore fidelity. Carried over
517
+ * from {@link TargetConfiguration.cacheRestore}. When absent, the
518
+ * runner uses faithful defaults (mtime + mode preserved).
519
+ */
520
+ cacheRestore?: CacheRestoreOptions;
521
+ /** Hash of the task inputs for caching */
522
+ hash?: string;
523
+ /** Detailed hash information */
524
+ hashDetails?: TaskHashDetails;
525
+ /** Unique identifier for the task, typically "project:target:configuration" */
526
+ id: string;
527
+ /**
528
+ * Maximum in-flight instances allowed for this task's target name.
529
+ * Carried over from {@link TargetConfiguration.maxConcurrent}.
530
+ */
531
+ maxConcurrent?: number;
532
+ /**
533
+ * Workspace-level concurrency group. Carried over from
534
+ * {@link TargetConfiguration.concurrencyGroup}; the scheduler maps
535
+ * the name to a numeric cap via
536
+ * {@link TaskRunnerOptions.concurrencyGroups}.
537
+ */
538
+ concurrencyGroup?: string;
539
+ /**
540
+ * Output patterns this task produces. Each entry is either a
541
+ * literal path, a glob (`"dist/**"`), a negative glob
542
+ * (`"!dist/cache/**"`), or `{ auto: true }` to pick up whatever
543
+ * files the task wrote during execution.
544
+ */
545
+ outputs: OutputSpec[];
546
+ /** Overrides/extra options passed to the task */
547
+ overrides: Record<string, unknown>;
548
+ /** Whether this task supports parallel execution */
549
+ parallelism?: boolean;
550
+ /**
551
+ * Explicit scheduling priority. Outranks the scheduler's
552
+ * graph-derived heuristics. Defaults to `"normal"` when absent.
553
+ */
554
+ priority?: TaskPriority;
555
+ /** The project root directory */
556
+ projectRoot?: string;
557
+ /** The target this task executes */
558
+ target: TaskTarget;
559
+ /**
560
+ * Regex strings (JavaScript flavor, anchored as the user writes them)
561
+ * that classify a successful task's terminal output as "succeeded
562
+ * with warnings". When any pattern matches, {@link TaskResult.hadWarnings}
563
+ * is set on the result. Combined with {@link Task.cacheOnWarning} this
564
+ * controls whether the run still seeds the cache.
565
+ *
566
+ * Carried over from {@link TargetConfiguration.warningPattern}.
567
+ */
568
+ warningPattern?: string[];
569
+ /**
570
+ * Predicate that gates execution. Evaluated by the orchestrator
571
+ * just before the task is launched; tasks whose `when` returns
572
+ * `false` are marked `"skipped"` without ever invoking the
573
+ * executor. Carried over from {@link TargetConfiguration.when}.
574
+ */
575
+ when?: WhenCondition;
576
+ }
577
+ /**
578
+ * Represents detailed hash information for a task.
579
+ */
580
+ interface TaskHashDetails {
581
+ /** The command hash */
582
+ command: string;
583
+ /** Hash of implicit dependencies */
584
+ implicitDeps?: Record<string, string>;
585
+ /** Hashes of input files */
586
+ nodes: Record<string, string>;
587
+ /** Hash of runtime values */
588
+ runtime?: Record<string, string>;
589
+ }
590
+ /**
591
+ * Represents a directed acyclic graph of tasks.
592
+ */
593
+ interface TaskGraph {
594
+ /** Dependencies for each task */
595
+ dependencies: Record<string, string[]>;
596
+ /** Top-level tasks that no other task depends on (the final outputs to run) */
597
+ roots: string[];
598
+ /** All tasks in the graph, keyed by task ID */
599
+ tasks: Record<string, Task>;
600
+ }
601
+ /**
602
+ * Possible states of a task after execution.
603
+ */
604
+ type TaskStatus = "success" | "failure" | "skipped" | "local-cache" | "local-cache-kept-existing" | "remote-cache";
605
+ /**
606
+ * Result of executing a task.
607
+ */
608
+ interface TaskResult {
609
+ /** The exit code, if applicable */
610
+ code?: number;
611
+ /**
612
+ * Set when auto-fingerprint tracking was attempted for this task but
613
+ * returned zero workspace accesses — usually a static binary on
614
+ * macOS/Windows, where the Node preload can't attach. Caching is
615
+ * skipped to avoid persisting an empty fingerprint that would
616
+ * produce false cache hits on every subsequent run.
617
+ */
618
+ emptyFingerprint?: boolean;
619
+ /** The end time of the task */
620
+ endTime?: number;
621
+ /**
622
+ * Set when the task exited 0 and at least one configured
623
+ * {@link Task.warningPattern} matched the terminal output. Surfaced
624
+ * to lifecycle reporters and the run summary so users can see that
625
+ * a "green" build still emitted warnings, and gates the optional
626
+ * `cacheOnWarning: false` cache-suppression path.
627
+ */
628
+ hadWarnings?: boolean;
629
+ /**
630
+ * Set when the task modified one or more of its own tracked input
631
+ * files during execution. Caching is skipped in this case — the
632
+ * fingerprint captured before the run would mismatch the post-run
633
+ * contents and produce false cache hits on subsequent runs.
634
+ */
635
+ selfModified?: boolean;
636
+ /** The start time of the task */
637
+ startTime?: number;
638
+ status: TaskStatus;
639
+ task: Task;
640
+ /** The terminal output produced during execution */
641
+ terminalOutput?: string;
642
+ }
643
+ /**
644
+ * Map of task results, keyed by task ID.
645
+ */
646
+ type TaskResults = Map<string, TaskResult>;
647
+ /**
648
+ * Configuration for a project in the workspace.
649
+ */
650
+ interface ProjectConfiguration {
651
+ /** Implicit dependencies on other projects */
652
+ implicitDependencies?: string[];
653
+ /**
654
+ * Project layer in the dependency hierarchy. Used by
655
+ * `enforceLayerRelationships` to ensure projects only depend on
656
+ * projects at the same or lower layer.
657
+ *
658
+ * Hierarchy (lowest → highest):
659
+ * `configuration < library < scaffolding < tool < automation < application`
660
+ */
661
+ layer?: "application" | "automation" | "configuration" | "library" | "scaffolding" | "tool";
662
+ /** The project type */
663
+ projectType?: "library" | "application";
664
+ /** The root directory of the project, relative to workspace root */
665
+ root: string;
666
+ /** The source root directory */
667
+ sourceRoot?: string;
668
+ /** Tags for filtering */
669
+ tags?: string[];
670
+ /** Named targets and their configurations */
671
+ targets?: Record<string, TargetConfiguration>;
672
+ }
673
+ /**
674
+ * Per-target controls over how the cache rehydrates outputs.
675
+ *
676
+ * Both default to `true` — faithful restoration is the contract:
677
+ * cached outputs come back with the same mtime and mode bits the
678
+ * task originally produced. Override only when downstream tooling
679
+ * needs the opposite (e.g. a bundler that uses "newer than source"
680
+ * heuristics, or a CI step that compares mtimes against a deploy
681
+ * artifact).
682
+ */
683
+ interface CacheRestoreOptions {
684
+ /**
685
+ * Restore each file's modification time from the captured tar
686
+ * header (second precision). When `false`, restored files take
687
+ * the current wall-clock time, matching the legacy behaviour.
688
+ */
689
+ preserveMtime?: boolean;
690
+ /**
691
+ * Restore each file's POSIX mode bits (rwx triplets, low 12
692
+ * bits). When `false`, restored files take the process umask.
693
+ * Only meaningful on POSIX hosts; Windows ignores tar mode.
694
+ */
695
+ preservePerms?: boolean;
696
+ }
697
+ /**
698
+ * Configuration for a target within a project.
699
+ */
700
+ interface TargetConfiguration {
701
+ /**
702
+ * When `true`, this target runs after the main task graph
703
+ * completes — even if upstream tasks failed. Useful for cleanup,
704
+ * teardown, notifications, or anything that should fire
705
+ * regardless of build outcome. Always-tasks are not part of the
706
+ * normal dependency graph and never block other tasks.
707
+ */
708
+ always?: boolean;
709
+ /** Whether this target is cacheable */
710
+ cache?: boolean;
711
+ /**
712
+ * When `false`, exit-0 runs whose terminal output matched any
713
+ * {@link TargetConfiguration.warningPattern} are not seeded into the
714
+ * cache. Defaults to `true` — warnings are surfaced on the result
715
+ * (`hadWarnings: true`) but caching still happens, matching the
716
+ * "succeeded with warnings is incremental" behaviour rush, lage and
717
+ * wireit users have repeatedly asked for.
718
+ */
719
+ cacheOnWarning?: boolean;
720
+ /**
721
+ * Fine-grained controls over how cached outputs are rehydrated.
722
+ * See {@link CacheRestoreOptions}. Both fields default to `true`
723
+ * (faithful restore); flip individually when downstream tooling
724
+ * needs the legacy "now"-stamped behaviour.
725
+ */
726
+ cacheRestore?: CacheRestoreOptions;
727
+ /** The command to run (alternative to executor) */
728
+ command?: string;
729
+ /** Named configurations (e.g., "production", "development") */
730
+ configurations?: Record<string, Record<string, unknown>>;
731
+ /**
732
+ * Workspace-level concurrency group this target opts into. The
733
+ * scheduler caps the *combined* in-flight count of every task whose
734
+ * target carries the same group name to the value declared in
735
+ * {@link TaskRunnerOptions.concurrencyGroups}. Use this when several
736
+ * targets share an external resource (a single Postgres,
737
+ * a developer's Docker daemon, an account-wide deploy lock) so the
738
+ * cap follows the resource, not any single target name.
739
+ *
740
+ * Targets that name a group not declared in `concurrencyGroups` run
741
+ * uncapped — a typo shouldn't deadlock the graph. Caps don't apply
742
+ * to tasks marked `always: true`; those run in a separate
743
+ * finalisation phase outside the scheduler.
744
+ */
745
+ concurrencyGroup?: string;
746
+ /** Other targets this target depends on */
747
+ dependsOn?: (string | TargetDependencyConfig)[];
748
+ /** The executor/command to run */
749
+ executor?: string;
750
+ /** Input patterns for cache invalidation */
751
+ inputs?: (string | InputDefinition)[];
752
+ /** Options passed to the executor */
753
+ options?: Record<string, unknown>;
754
+ /**
755
+ * Maximum number of in-flight instances of this target across the
756
+ * whole graph. When set, the scheduler refuses to start an
757
+ * additional task whose `target.target` equals this target's name
758
+ * once the running count reaches `maxConcurrent`. Independent of
759
+ * `--parallel`: the global cap still bounds total in-flight tasks,
760
+ * and `maxConcurrent` further bounds the per-target subset.
761
+ *
762
+ * Use for tests/deploys that share an external resource (DB, port,
763
+ * mock server). Set to `1` to serialize. Values `<= 0` are ignored.
764
+ *
765
+ * If multiple projects declare different values for the same
766
+ * target name, the runner uses the smallest declared cap. Caps
767
+ * don't apply to tasks marked `always: true`; those run in a
768
+ * separate finalisation phase outside the scheduler.
769
+ */
770
+ maxConcurrent?: number;
771
+ /** Output patterns produced by this target */
772
+ outputs?: string[];
773
+ /** Whether this target supports parallel execution */
774
+ parallelism?: boolean;
775
+ /**
776
+ * Regex source string(s) that mark a successful task as having
777
+ * emitted warnings. The orchestrator scans the task's combined
778
+ * terminal output after a 0-exit and, on first match, sets
779
+ * `hadWarnings` on the result. Combine with `cacheOnWarning: false`
780
+ * to skip caching for warning-tainted runs. Both bare strings and
781
+ * arrays are accepted; arrays are tested in order.
782
+ *
783
+ * ```ts
784
+ * warningPattern: ["\\bwarning\\b", "TS\\d{4}"]
785
+ * ```
786
+ */
787
+ warningPattern?: string | string[];
788
+ /**
789
+ * Predicate that gates execution. When the condition evaluates
790
+ * to `false` for the current environment, the task is skipped
791
+ * (marked `"skipped"`, not failed). Combine with `os`, `env`,
792
+ * `branch`, and `ci` clauses for granular control:
793
+ *
794
+ * ```ts
795
+ * when: { os: "linux", ci: true, env: "DEPLOY_TOKEN" }
796
+ * ```
797
+ */
798
+ when?: WhenCondition;
799
+ }
800
+ /**
801
+ * Defines a dependency on another target.
802
+ */
803
+ interface TargetDependencyConfig {
804
+ /** Whether this is a dependency on the same project */
805
+ dependencies?: boolean;
806
+ /** Params to pass through */
807
+ params?: "forward" | "ignore";
808
+ /** The project name (if different from the current project) */
809
+ projects?: string | string[];
810
+ /** The target name */
811
+ target: string;
812
+ }
813
+ /**
814
+ * Defines an input for cache invalidation.
815
+ */
816
+ type InputDefinition = FileSetInput | RuntimeInput | EnvironmentInput | ExternalDependencyInput;
817
+ /**
818
+ * Controls how a glob pattern is anchored.
819
+ * - "package" (default): pattern is resolved relative to the project root
820
+ * - "workspace": pattern is resolved relative to the workspace root
821
+ */
822
+ type FileSetBase = "package" | "workspace";
823
+ /**
824
+ * An input based on file patterns.
825
+ *
826
+ * `fileset` may be a bare glob string (package-root relative) or an object
827
+ * form `{ pattern, base }` to anchor explicitly to the workspace root.
828
+ * Negation (`!` prefix) works in both forms.
829
+ */
830
+ interface FileSetInput {
831
+ fileset: FileSetPattern | string;
832
+ }
833
+ /**
834
+ * Object form of a fileset pattern, for anchoring to the workspace root.
835
+ */
836
+ interface FileSetPattern {
837
+ /** Anchor for the pattern. */
838
+ base: FileSetBase;
839
+ /** Glob pattern (may start with `!` for negation). */
840
+ pattern: string;
841
+ }
842
+ /**
843
+ * An input based on a runtime command.
844
+ */
845
+ interface RuntimeInput {
846
+ runtime: string;
847
+ }
848
+ /**
849
+ * An input based on environment variables.
850
+ */
851
+ interface EnvironmentInput {
852
+ env: string;
853
+ }
854
+ /**
855
+ * An input based on external dependency versions.
856
+ */
857
+ interface ExternalDependencyInput {
858
+ externalDependencies: string[];
859
+ }
860
+ /**
861
+ * Defines tag-based dependency rules.
862
+ * Each key is a tag name. The value is the list of tags that a dependency
863
+ * must have at least one of, when the source project has that tag.
864
+ *
865
+ * Example: `{ "frontend": ["shared", "frontend"] }` means a project tagged
866
+ * "frontend" can only depend on projects tagged "shared" or "frontend".
867
+ */
868
+ type TagRelationships = Record<string, string[]>;
869
+ /**
870
+ * Defines project-type-based dependency rules.
871
+ */
872
+ interface TypeBoundaries {
873
+ /**
874
+ * Custom rules mapping project types to allowed dependency types.
875
+ * Example: `{ "application": ["library"] }` means applications can only
876
+ * depend on libraries.
877
+ */
878
+ allowedDependencyTypes?: Record<string, string[]>;
879
+ /**
880
+ * When true, no project may depend on an "application" type project.
881
+ * Applications are typically deployment targets, not libraries.
882
+ * @default true
883
+ */
884
+ enforceApplicationBoundary?: boolean;
885
+ }
886
+ /**
887
+ * Rules based on the dependency kind (dependencies vs devDependencies vs peerDependencies).
888
+ */
889
+ interface DependencyKindRules {
890
+ /**
891
+ * When true, a "library" project must not have workspace-internal devDependencies
892
+ * on other libraries that are also in its production dependencies.
893
+ * Prevents publishing libraries that silently rely on dev-only workspace packages.
894
+ * @default false
895
+ */
896
+ noDevDependencyOnProductionDep?: boolean;
897
+ /**
898
+ * When true, production `dependencies` must not point to "application" type projects.
899
+ * devDependencies on applications are allowed (e.g., for testing).
900
+ * @default false
901
+ */
902
+ noProductionDependencyOnApplication?: boolean;
903
+ }
904
+ /**
905
+ * Configuration for project constraint enforcement.
906
+ */
907
+ interface ConstraintsConfig {
908
+ /** Rules based on the dependency kind (static vs devDependency vs peerDependency) */
909
+ dependencyKindRules?: DependencyKindRules;
910
+ /**
911
+ * When true, projects can only depend on projects at the same or
912
+ * lower layer in the hierarchy:
913
+ *
914
+ * configuration < library < scaffolding < tool < automation < application
915
+ *
916
+ * Projects without an explicit `layer` are unconstrained.
917
+ * @default false
918
+ */
919
+ enforceLayerRelationships?: boolean;
920
+ /** Tag-based dependency rules */
921
+ tagRelationships?: TagRelationships;
922
+ /** Project-type-based dependency rules */
923
+ typeBoundaries?: TypeBoundaries;
924
+ }
925
+ /**
926
+ * A single constraint violation detected in the project graph.
927
+ */
928
+ interface ConstraintViolation {
929
+ /** The project being depended on */
930
+ dependencyProject: string;
931
+ /** Human-readable description of the violation */
932
+ message: string;
933
+ /** The type of rule that was violated */
934
+ rule: "dependency-kind" | "layer-relationship" | "tag-relationship" | "type-boundary";
935
+ /** The project that has the invalid dependency */
936
+ sourceProject: string;
937
+ }
938
+ /**
939
+ * Controls how far to traverse the dependency graph in a given direction.
940
+ * - "none": Don't traverse — only directly changed projects are included.
941
+ * - "direct": Include only immediate neighbors (one hop).
942
+ * - "deep": Include all transitive neighbors (full chain).
943
+ */
944
+ type AffectedScope = "deep" | "direct" | "none";
945
+ /**
946
+ * Workspace configuration containing all project configurations.
947
+ */
948
+ interface WorkspaceConfiguration {
949
+ /** All projects in the workspace, keyed by project name */
950
+ projects: Record<string, ProjectConfiguration>;
951
+ }
952
+ /**
953
+ * The kind of dependency relationship between projects.
954
+ * - "static": Production dependency (from `dependencies` in package.json)
955
+ * - "dynamic": Dynamically resolved dependency (e.g., lazy imports)
956
+ * - "implicit": Declared via `implicitDependencies` in project config
957
+ * - "devDependency": Development-only dependency (from `devDependencies`)
958
+ * - "peerDependency": Peer dependency (from `peerDependencies`)
959
+ */
960
+ type DependencyType = "devDependency" | "dynamic" | "implicit" | "peerDependency" | "static";
961
+ /**
962
+ * A dependency relationship in the project graph.
963
+ */
964
+ interface ProjectGraphDependency {
965
+ /** The source project */
966
+ source: string;
967
+ /** The target project */
968
+ target: string;
969
+ /** The type of dependency */
970
+ type: DependencyType;
971
+ }
972
+ /**
973
+ * A node in the project graph.
974
+ */
975
+ interface ProjectGraphProjectNode {
976
+ /** The project configuration */
977
+ data: ProjectConfiguration;
978
+ /** The project name */
979
+ name: string;
980
+ /** The type of project */
981
+ type: "library" | "application";
982
+ }
983
+ /**
984
+ * The project graph represents the dependency relationships between projects.
985
+ */
986
+ interface ProjectGraph {
987
+ /** All dependency relationships */
988
+ dependencies: Record<string, ProjectGraphDependency[]>;
989
+ /** All project nodes, keyed by project name */
990
+ nodes: Record<string, ProjectGraphProjectNode>;
991
+ }
992
+ /**
993
+ * Named input definitions that can be referenced by name.
994
+ */
995
+ interface NamedInputs {
996
+ [name: string]: (string | InputDefinition)[];
997
+ }
998
+ /**
999
+ * Configuration for the task runner.
1000
+ */
1001
+ interface TaskRunnerOptions {
1002
+ /**
1003
+ * Scan each task's resolved command text for `$VAR`/`${VAR}`
1004
+ * references and automatically fingerprint those env vars. Catches
1005
+ * tasks like `curl ${VERCEL_URL}/api` where the user forgot to
1006
+ * declare the reference in `envVars`/`globalEnv`.
1007
+ * @default false
1008
+ */
1009
+ autoEnvVars?: boolean;
1010
+ /**
1011
+ * Enable auto-fingerprinting mode (Vite Task-style).
1012
+ * When enabled, the task runner automatically tracks which files
1013
+ * a task accesses during execution and uses that for cache invalidation
1014
+ * instead of requiring manual input configuration.
1015
+ *
1016
+ * Falls back to explicit inputs (Nx-style) when file tracking
1017
+ * is not supported on the current platform.
1018
+ * @default false
1019
+ */
1020
+ autoFingerprint?: boolean;
1021
+ /**
1022
+ * Whether to show cache miss diagnostics (why a cache miss occurred).
1023
+ * @default false
1024
+ */
1025
+ cacheDiagnostics?: boolean;
1026
+ /** Directory for storing cache */
1027
+ cacheDirectory?: string;
1028
+ /**
1029
+ * Dry-run mode: compute hashes and check cache but don't execute tasks.
1030
+ * Useful for debugging cache hits/misses.
1031
+ * @default false
1032
+ */
1033
+ dryRun?: boolean;
1034
+ /** Custom environment variables to include in hash */
1035
+ envVars?: string[];
1036
+ /**
1037
+ * Environment variable patterns to include in auto-fingerprint.
1038
+ * Supports wildcard patterns like "VITE_*".
1039
+ * Only used when autoFingerprint is enabled.
1040
+ */
1041
+ fingerprintEnvPatterns?: string[];
1042
+ /**
1043
+ * Enable framework environment variable inference.
1044
+ * When true, automatically detects common frontend frameworks and includes
1045
+ * their public env var prefixes in the task hash:
1046
+ * - Next.js: NEXT_PUBLIC_*
1047
+ * - Vite: VITE_*
1048
+ * - Create React App: REACT_APP_*
1049
+ * - Gatsby: GATSBY_*
1050
+ * - Nuxt: NUXT_PUBLIC_*
1051
+ * - Expo: EXPO_PUBLIC_*
1052
+ *
1053
+ * Matches Turborepo's framework inference behavior.
1054
+ * @default false
1055
+ */
1056
+ frameworkInference?: boolean;
1057
+ /**
1058
+ * Global environment variables that invalidate ALL task hashes.
1059
+ * Matches Turborepo's `globalEnv`.
1060
+ */
1061
+ globalEnv?: string[];
1062
+ /**
1063
+ * Global input files that invalidate ALL task hashes when changed.
1064
+ * These are workspace-root-relative paths.
1065
+ *
1066
+ * Default: ["package-lock.json", "pnpm-lock.yaml", "yarn.lock",
1067
+ * "tsconfig.base.json", ".env"]
1068
+ *
1069
+ * When any global input changes, every task's hash changes,
1070
+ * forcing a full rebuild. This matches Turborepo's `globalDependencies`.
1071
+ */
1072
+ globalInputs?: string[];
1073
+ /**
1074
+ * When `true`, file hashes are cached in a persistent mtime+size
1075
+ * indexed snapshot under `node_modules/.cache/task-runner/`. On
1076
+ * subsequent runs, unchanged files skip content re-reads — cuts
1077
+ * cold-cache fingerprint time dramatically on large workspaces.
1078
+ *
1079
+ * Changed or new files still get full content hashing. Safe to
1080
+ * leave on by default; overhead when nothing matches is a single
1081
+ * `stat` call per file.
1082
+ * @default false
1083
+ */
1084
+ incrementalFileHashing?: boolean;
1085
+ /**
1086
+ * Workspace-level concurrency group caps. Maps a group name to the
1087
+ * maximum number of in-flight tasks across every target whose
1088
+ * `concurrencyGroup` field equals the same name. Independent of
1089
+ * `parallel`: the global cap still bounds total tasks, and group
1090
+ * caps further bound the named subset.
1091
+ *
1092
+ * Common shape — one cap per shared resource:
1093
+ *
1094
+ * ```ts
1095
+ * concurrencyGroups: {
1096
+ * "db-bound": 2, // any target hitting the local Postgres
1097
+ * "deploy-lock": 1, // any deploy target
1098
+ * }
1099
+ * ```
1100
+ *
1101
+ * Targets opt in via {@link TargetConfiguration.concurrencyGroup}.
1102
+ * Values `<= 0` are ignored.
1103
+ */
1104
+ concurrencyGroups?: ConcurrencyGroups;
1105
+ /** Maximum age of cache entries in milliseconds */
1106
+ maxCacheAge?: number;
1107
+ /** Maximum cache size (e.g., "1GB") */
1108
+ maxCacheSize?: string;
1109
+ /** Named inputs for cache invalidation */
1110
+ namedInputs?: NamedInputs;
1111
+ /**
1112
+ * When `true`, the cache directory is partitioned by a hash of
1113
+ * the resolved `globalEnv` values. Changing a globalEnv var moves
1114
+ * cache writes into a new namespace; rolling it back reuses the
1115
+ * old namespace and its hits. Without this option, any globalEnv
1116
+ * change silently busts every cached entry.
1117
+ *
1118
+ * Keep disabled when globalEnv is stable across runs — the extra
1119
+ * path depth offers no value, and misconfigured namespaces can
1120
+ * hide stale hits.
1121
+ * @default false
1122
+ */
1123
+ namespaceByGlobalEnv?: boolean;
1124
+ /** Maximum number of parallel tasks */
1125
+ parallel?: number | boolean;
1126
+ /**
1127
+ * Remote cache configuration.
1128
+ * When configured, the task runner checks the remote cache after a
1129
+ * local miss and uploads results after execution. See
1130
+ * {@link RemoteCacheOptions} for the full HTTP and REAPI surface.
1131
+ */
1132
+ remoteCache?: RemoteCacheOptions;
1133
+ /** Whether to skip cache reads */
1134
+ skipNxCache?: boolean;
1135
+ /**
1136
+ * Enable smart lockfile hashing.
1137
+ * Instead of hashing the entire lockfile (which busts ALL caches),
1138
+ * only hash the resolved versions of each package's actual dependencies.
1139
+ * This matches Turborepo's smart lockfile hashing behavior.
1140
+ * @default false
1141
+ */
1142
+ smartLockfileHashing?: boolean;
1143
+ /**
1144
+ * Generate a detailed JSON run summary after execution.
1145
+ * When true, writes a summary file to `.task-runner/runs/` containing
1146
+ * all task inputs, outputs, hashes, timings, and cache status.
1147
+ *
1148
+ * Useful for debugging cache misses and comparing runs.
1149
+ * Matches Turborepo's `--summarize` flag.
1150
+ * @default false
1151
+ */
1152
+ summarize?: boolean;
1153
+ /** Target-specific default configurations */
1154
+ targetDefaults?: Record<string, Partial<TargetConfiguration>>;
1155
+ /**
1156
+ * Environment variables that should be passed to tasks but NOT
1157
+ * included in the cache fingerprint. Useful for CI-specific vars.
1158
+ * Only used when autoFingerprint is enabled.
1159
+ */
1160
+ untrackedEnvVars?: string[];
1161
+ }
1162
+ /**
1163
+ * Options for executing a task.
1164
+ */
1165
+ interface TaskExecutionOptions {
1166
+ /** Whether to capture output */
1167
+ captureOutput?: boolean;
1168
+ /** The working directory */
1169
+ cwd?: string;
1170
+ /** Environment variables */
1171
+ env?: Record<string, string>;
1172
+ /** Stream output as it arrives */
1173
+ streamOutput?: boolean;
1174
+ }
1175
+ /**
1176
+ * A task executor function.
1177
+ */
1178
+ type TaskExecutor = (task: Task, options: TaskExecutionOptions) => Promise<{
1179
+ code: number;
1180
+ terminalOutput: string;
1181
+ }>;
1182
+ /**
1183
+ * The function type for a task runner.
1184
+ */
1185
+ type TasksRunner = (tasks: Task[], options: TaskRunnerOptions, context: TaskRunnerContext) => Promise<TaskResults>;
1186
+ /**
1187
+ * Context provided to the task runner.
1188
+ */
1189
+ interface TaskRunnerContext {
1190
+ /** Lifecycle hooks */
1191
+ lifeCycle: LifeCycleInterface;
1192
+ /** The project graph */
1193
+ projectGraph: ProjectGraph;
1194
+ /** The task executor */
1195
+ taskExecutor: TaskExecutor;
1196
+ /** The task graph */
1197
+ taskGraph: TaskGraph;
1198
+ /** The workspace root directory */
1199
+ workspaceRoot: string;
1200
+ }
1201
+ /**
1202
+ * Input for a concurrent command -- either a string or a config object.
1203
+ */
1204
+ type ConcurrentCommandInput = string | {
1205
+ command: string;
1206
+ cwd?: string;
1207
+ env?: Record<string, string>;
1208
+ name?: string; /** Initial PTY dimensions (only used when stdin is "pty"). */
1209
+ ptySize?: {
1210
+ cols: number;
1211
+ rows: number;
1212
+ };
1213
+ stdin?: "inherit" | "null" | "pipe" | "pty";
1214
+ };
1215
+ /**
1216
+ * Configuration for a single command to run concurrently.
1217
+ */
1218
+ interface ConcurrentCommandConfig {
1219
+ /** The command string to execute (passed to shell). */
1220
+ command: string;
1221
+ /** Working directory for the command. */
1222
+ cwd?: string;
1223
+ /** Additional environment variables merged with process env. */
1224
+ env?: Record<string, string>;
1225
+ /** Human-readable name for this command (used in prefixes/logs). */
1226
+ name?: string;
1227
+ /**
1228
+ * Initial PTY dimensions. Only used when stdin is "pty".
1229
+ * Defaults to 80x24 if not specified.
1230
+ */
1231
+ ptySize?: {
1232
+ cols: number;
1233
+ rows: number;
1234
+ };
1235
+ /** Whether to use shell execution (default: true). */
1236
+ shell?: boolean;
1237
+ /**
1238
+ * Stdin mode for the child process.
1239
+ * - "null" (default): stdin closed, child cannot read input
1240
+ * - "pipe": stdin is piped, can be written to programmatically
1241
+ * - "inherit": child inherits parent's stdin (for interactive commands like vite dev)
1242
+ * - "pty": child runs inside a pseudo-terminal (isatty() returns true, enables interactive prompts)
1243
+ */
1244
+ stdin?: "inherit" | "null" | "pipe" | "pty";
1245
+ }
1246
+ /**
1247
+ * Options controlling the concurrent runner behavior.
1248
+ */
1249
+ interface ConcurrentRunnerOptions {
1250
+ /** Conditions under which to kill other processes: "success", "failure". */
1251
+ killOthers?: ("failure" | "success")[];
1252
+ /** Signal to send when killing processes (default: "SIGTERM"). */
1253
+ killSignal?: string;
1254
+ /** Milliseconds to wait after kill signal before sending SIGKILL (default: 5000). */
1255
+ killTimeout?: number;
1256
+ /** Maximum number of processes to run simultaneously. 0 = unlimited. */
1257
+ maxProcesses?: number;
1258
+ /** Callback for real-time process events. */
1259
+ onEvent?: (event: ProcessEvent) => void;
1260
+ /** Restart options for failed commands. */
1261
+ restart?: {
1262
+ /** Delay between restarts in ms. "exponential" for 2^attempt * 1000ms. */
1263
+ delay?: number | "exponential";
1264
+ /** Maximum restart attempts per command. 0 = no restarts. -1 = infinite. */
1265
+ tries: number;
1266
+ };
1267
+ /**
1268
+ * Custom shell path for command execution.
1269
+ * Overrides the platform default (/bin/sh on Unix, cmd.exe on Windows).
1270
+ * Automatically detected from npm's `script-shell` config if not set.
1271
+ */
1272
+ shellPath?: string;
1273
+ /** Success condition: "first", "last", "all", or "command-&lt;name|index>". */
1274
+ successCondition?: string;
1275
+ /** Commands to run sequentially after all processes complete. */
1276
+ teardown?: string[];
1277
+ /** Working directory for teardown commands. */
1278
+ teardownCwd?: string;
1279
+ /** Print a timing summary table after completion. Default: false. */
1280
+ timings?: boolean;
1281
+ }
1282
+ /**
1283
+ * An event emitted during concurrent execution.
1284
+ */
1285
+ interface ProcessEvent {
1286
+ /** Command name (for close events). */
1287
+ commandName?: string;
1288
+ /** Duration in milliseconds (for close events). */
1289
+ durationMs?: number;
1290
+ /** Exit code (for close events). */
1291
+ exitCode?: number;
1292
+ /** Index of the command that produced this event. */
1293
+ index: number;
1294
+ /** Kill the child process/PTY. Only present on "started" events. */
1295
+ kill?: (signal?: string) => void;
1296
+ /** Whether the process was killed (for close events). */
1297
+ killed?: boolean;
1298
+ /** Event type: "stdout", "stderr", "close", "error", "started". */
1299
+ kind: "close" | "error" | "started" | "stderr" | "stdout";
1300
+ /** Error message (for error events). */
1301
+ message?: string;
1302
+ /** Resize the child's PTY. Only present on "started" events with stdin "pty". */
1303
+ resize?: (cols: number, rows: number) => void;
1304
+ /** Text content (for stdout/stderr events). */
1305
+ text?: string;
1306
+ /** Write data to the child's stdin (pipe) or PTY. Only present on "started" events. */
1307
+ write?: (data: string) => void;
1308
+ }
1309
+ /**
1310
+ * Result of a close event for a single command.
1311
+ */
1312
+ interface ConcurrentCloseEvent {
1313
+ /** The command string that was executed. */
1314
+ command: string;
1315
+ /** Duration in milliseconds. */
1316
+ durationMs: number;
1317
+ /** Exit code. -1 if killed by signal. */
1318
+ exitCode: number;
1319
+ /** Index of the command. */
1320
+ index: number;
1321
+ /** Whether the process was forcefully killed. */
1322
+ killed: boolean;
1323
+ /** The command name (if provided). */
1324
+ name?: string;
1325
+ }
1326
+ /**
1327
+ * Overall result of a concurrent run.
1328
+ */
1329
+ interface ConcurrentRunResult {
1330
+ /** Close events for all commands, in completion order. */
1331
+ closeEvents: ConcurrentCloseEvent[];
1332
+ /** Whether the run succeeded according to the success condition. */
1333
+ success: boolean;
1334
+ }
1335
+ /**
1336
+ * Interface for lifecycle event handlers.
1337
+ */
1338
+ interface LifeCycleInterface {
1339
+ endCommand?: () => void;
1340
+ endTasks?: (taskResults: TaskResult[]) => void;
1341
+ /**
1342
+ * Called when a running task emits data on stderr, in the order the
1343
+ * data arrives. Executors that support streaming output (`vis run`'s
1344
+ * concurrent executor) forward chunks here verbatim. Executors that
1345
+ * only buffer full output (e.g. the simple test executor) skip this
1346
+ * hook — rely on `printTaskTerminalOutput` for the final dump.
1347
+ *
1348
+ * Handlers should be non-blocking; the orchestrator doesn't await.
1349
+ */
1350
+ onTaskStderr?: (task: Task, chunk: string) => void;
1351
+ /**
1352
+ * Called when a running task emits data on stdout, in the order the
1353
+ * data arrives. See {@link LifeCycleInterface.onTaskStderr} for
1354
+ * semantics — same contract, different stream.
1355
+ */
1356
+ onTaskStdout?: (task: Task, chunk: string) => void;
1357
+ /** Called when a cache miss occurs with diagnostic information */
1358
+ printCacheMiss?: (task: Task, reasons: string) => void;
1359
+ /**
1360
+ * Called when caching was skipped because auto-fingerprint tracking
1361
+ * came back empty — a signal that the tracker (strace or Node
1362
+ * preload) couldn't observe the task's file access, typically
1363
+ * because it's a static binary on a platform without strace.
1364
+ * `reason` is a short human-readable diagnostic.
1365
+ */
1366
+ printEmptyFingerprintWarning?: (task: Task, reason: string) => void;
1367
+ /**
1368
+ * Called when caching is skipped because the task modified one or
1369
+ * more of its own tracked inputs. `modifiedFiles` lists the
1370
+ * workspace-relative paths that changed between pre- and
1371
+ * post-execution hashes.
1372
+ */
1373
+ printSelfModifyingSkip?: (task: Task, modifiedFiles: string[]) => void;
1374
+ printTaskTerminalOutput?: (task: Task, status: TaskStatus, terminalOutput: string) => void;
1375
+ /**
1376
+ * Called when a task is skipped because its `when` clause did not
1377
+ * match the current environment. `reason` is a short
1378
+ * human-readable diagnostic produced by {@link import("./when-condition").explainWhen}.
1379
+ *
1380
+ * Co-fires with `printTaskTerminalOutput` (status `"skipped"`,
1381
+ * output `"Skipped: [reason]"`). Pick one to render — implementing
1382
+ * both will double-report the skip.
1383
+ */
1384
+ printWhenSkip?: (task: Task, reason: string) => void;
1385
+ scheduleTask?: (task: Task) => void;
1386
+ startCommand?: () => void;
1387
+ startTasks?: (tasks: Task[]) => void;
1388
+ }
1389
+ /**
1390
+ * Options for determining affected projects.
1391
+ */
1392
+ interface AffectedOptions {
1393
+ /** The base ref to compare against (default: "main") */
1394
+ base?: string;
1395
+ /**
1396
+ * Control how far downstream (dependents of changed projects) to include.
1397
+ * @default "deep"
1398
+ */
1399
+ downstream?: AffectedScope;
1400
+ /** The head ref to compare (default: "HEAD") */
1401
+ head?: string;
1402
+ /** Project graph for dependency resolution */
1403
+ projectGraph: ProjectGraph;
1404
+ /** All project configurations keyed by name */
1405
+ projects: Record<string, ProjectConfiguration>;
1406
+ /**
1407
+ * Control how far upstream (dependencies of changed projects) to include.
1408
+ * @default "none"
1409
+ */
1410
+ upstream?: AffectedScope;
1411
+ /** The workspace root directory */
1412
+ workspaceRoot: string;
1413
+ }
1414
+ /**
1415
+ * Result of affected detection.
1416
+ */
1417
+ interface AffectedResult {
1418
+ /** All affected projects (union of changed, downstream, and upstream) */
1419
+ affectedProjects: string[];
1420
+ /** Files that changed between base and head */
1421
+ changedFiles: string[];
1422
+ /** Projects that were directly changed */
1423
+ changedProjects: string[];
1424
+ /** Projects affected because they depend on changed projects */
1425
+ downstreamProjects: string[];
1426
+ /** Projects that changed projects depend on */
1427
+ upstreamProjects: string[];
1428
+ }
1429
+ /**
1430
+ * Builds a map from each project to the set of projects that depend on it (reverse/downstream).
1431
+ */
1432
+ declare const buildReverseDependencyMap: (projectGraph: ProjectGraph) => Map<string, Set<string>>;
1433
+ /**
1434
+ * Builds a map from each project to the set of projects it depends on (forward/upstream).
1435
+ */
1436
+ declare const buildForwardDependencyMap: (projectGraph: ProjectGraph) => Map<string, Set<string>>;
1437
+ /**
1438
+ * Expands a set of changed projects based on upstream/downstream scope settings.
1439
+ * Returns a new set containing all affected projects.
1440
+ */
1441
+ declare const expandAffected: (changedProjects: Set<string>, projectGraph: ProjectGraph, options: {
1442
+ downstream: AffectedScope;
1443
+ upstream: AffectedScope;
1444
+ }) => {
1445
+ affected: Set<string>;
1446
+ downstream: Set<string>;
1447
+ upstream: Set<string>;
1448
+ };
1449
+ /**
1450
+ * Gets the list of files changed between two git refs.
1451
+ * Uses execFile with argument arrays to prevent command injection.
1452
+ */
1453
+ declare const getChangedFiles: (workspaceRoot: string, base: string, head: string) => Promise<string[]>;
1454
+ /**
1455
+ * Determines which projects are affected by changes between two git refs.
1456
+ *
1457
+ * Uses `git diff` to find changed files, maps them to projects based on
1458
+ * project roots, then walks the project dependency graph to find all
1459
+ * transitively affected projects.
1460
+ *
1461
+ * This is the same strategy used by `nx affected` and `turbo run --filter=[base...]`.
1462
+ */
1463
+ declare const getAffectedProjects: (options: AffectedOptions) => Promise<AffectedResult>;
1464
+ /**
1465
+ * Filters tasks to only include those that are affected by changes.
1466
+ */
1467
+ declare const filterAffectedTasks: (taskIds: string[], affectedProjects: Set<string>) => string[];
1468
+ /**
1469
+ * Resolves the canonical {@link CacheMode}. `mode` defaults to
1470
+ * `"readwrite"` when unset — the safe choice for CI and the most
1471
+ * common config in dev. Kept as a separate helper so vis-side code
1472
+ * (CLI flag merge, doctor probes) can reuse the resolution rule.
1473
+ */
1474
+ declare const resolveCacheMode: (options: {
1475
+ mode?: CacheMode;
1476
+ }) => CacheMode;
1477
+ /**
1478
+ * Construct the configured remote cache backend. Selects between the
1479
+ * Turborepo-compatible HTTP client and the Bazel REAPI gRPC client
1480
+ * via `options.backend`.
1481
+ */
1482
+ declare const createRemoteCacheBackend: (options: RemoteCacheOptions) => RemoteCacheBackend;
1483
+ /**
1484
+ * Derive a stable {@link CasDigest} from the orchestrator's task hash
1485
+ * (xxh3-128, 32 hex chars). REAPI servers reject non-sha256 digests on
1486
+ * the wire, so we sha256 a namespaced key. The HTTP backend uses the
1487
+ * resulting digest as the artifact URL component.
1488
+ *
1489
+ * `sizeBytes` is set to `0` because this digest identifies an *action*
1490
+ * (an `ActionResult` keyed in the AC), not a stored CAS blob. REAPI
1491
+ * `Digest` size_bytes only carries meaning for blobs in the CAS — for
1492
+ * action keys it's ignored by the server. Setting it to the byte
1493
+ * length of the prehash key would be semantically wrong: the server
1494
+ * would believe a blob of that exact length exists at this hash, and
1495
+ * a downstream `BatchReadBlobs` against this digest would mismatch.
1496
+ */
1497
+ declare const actionDigestForTaskHash: (taskHash: string) => CasDigest;
1498
+ /**
1499
+ * Probe whether a cached entry exists on the remote backend, keyed by
1500
+ * the orchestrator's task hash. Resolves `false` on any wire failure —
1501
+ * the orchestrator treats existence checks as best-effort.
1502
+ */
1503
+ declare const containsByTaskHash: (backend: RemoteCacheBackend, taskHash: string) => Promise<boolean>;
1504
+ /**
1505
+ * Download the cached entry for `taskHash` and extract it into
1506
+ * `{localCacheDirectory}/{taskHash}/`. Returns `true` only when the
1507
+ * directory has been fully populated; partial-extract failures clean
1508
+ * up after themselves so the local cache never observes a half-populated
1509
+ * entry.
1510
+ */
1511
+ declare const retrieveByTaskHash: (backend: RemoteCacheBackend, taskHash: string, localCacheDirectory: string) => Promise<boolean>;
1512
+ /**
1513
+ * Tar `{localCacheDirectory}/{taskHash}/` and upload as a single CAS
1514
+ * blob via {@link RemoteCacheBackend.storeAction}. Skips the upload
1515
+ * unless `.commit` is present so we never publish a half-written entry.
1516
+ *
1517
+ * The lazy {@link BlobSource.open} returns a fresh `createReadStream`
1518
+ * each call so REAPI's `FindMissingBlobs` → `BatchUpdateBlobs` →
1519
+ * fallback `Write` flow can re-read the file as many times as it needs
1520
+ * to without requiring the bridge to buffer the bytes in memory.
1521
+ *
1522
+ * Bridge-local failures (`createTarGz`, `digestFile`) are surfaced
1523
+ * through `onUploadError` when provided, then swallowed so the
1524
+ * fire-and-forget call site stays non-throwing. A missing `.commit`
1525
+ * marker is *not* an error — it's the normal "skip this upload" path.
1526
+ */
1527
+ declare const storeByTaskHash: (backend: RemoteCacheBackend, taskHash: string, localCacheDirectory: string, onUploadError?: (hash: string, error: unknown) => void) => Promise<boolean>;
1528
+ /**
1529
+ * HTTP-based remote cache compatible with the Turborepo remote cache protocol.
1530
+ *
1531
+ * Protocol:
1532
+ * - GET /v8/artifacts/{hash}?teamId={team} -> retrieve cached artifact (gzipped tar)
1533
+ * - PUT /v8/artifacts/{hash}?teamId={team} -> store artifact
1534
+ * - POST /v8/artifacts/events -> analytics (optional)
1535
+ *
1536
+ * Authentication via `Authorization: Bearer {token}` header.
1537
+ *
1538
+ * The cache entry is a gzipped tarball containing the cache directory contents
1539
+ * (code, terminalOutput, fingerprint.json, outputs/, .commit).
1540
+ */
1541
+ declare class HttpRemoteCache implements RemoteCacheBackend {
1542
+ #private;
1543
+ constructor(options: RemoteCacheOptions);
1544
+ /**
1545
+ * No-op. The HTTP backend uses Node's global `fetch`, which has no
1546
+ * persistent connection to release. Implemented to satisfy the
1547
+ * {@link RemoteCacheBackend.close} contract uniformly.
1548
+ */
1549
+ close(): Promise<void>;
1550
+ /**
1551
+ * {@link RemoteCacheBackend.containsAction}: HEAD on the artifact URL.
1552
+ * Resolves `false` on any wire failure — existence checks are best
1553
+ * effort and never block the caller.
1554
+ */
1555
+ containsAction(actionDigest: CasDigest): Promise<boolean>;
1556
+ /**
1557
+ * {@link RemoteCacheBackend.fetchBlob}: streams a blob out of the
1558
+ * local CAS that was hydrated by a previous {@link retrieveAction}
1559
+ * call. The HTTP wire ships one tarball per action, so per-blob
1560
+ * fetches are local-only — there's no remote endpoint to call.
1561
+ */
1562
+ fetchBlob(digest: CasDigest, destinationPath: string): Promise<boolean>;
1563
+ /**
1564
+ * {@link RemoteCacheBackend.retrieveAction}: GETs the artifact at
1565
+ * `/v8/artifacts/{actionDigest.hash}`, ingests the response bytes
1566
+ * as a single CAS blob in the local store, and synthesises an
1567
+ * {@link ActionResult} that points at that blob. Resolves to `null`
1568
+ * on a 404 / signature mismatch / missing local CAS root.
1569
+ */
1570
+ retrieveAction(actionDigest: CasDigest): Promise<ActionResult | null>;
1571
+ /**
1572
+ * {@link RemoteCacheBackend.storeAction}: takes the single blob
1573
+ * referenced by `result.outputFiles[0]`, streams its bytes as the
1574
+ * PUT body, and signs the body when a signing secret is configured.
1575
+ * Per-digest in-flight dedup means parallel writers racing on the
1576
+ * same action upload exactly once.
1577
+ */
1578
+ storeAction(actionDigest: CasDigest, result: ActionResult, blobs: ReadonlyArray<BlobSource>): Promise<boolean>;
1579
+ }
1580
+ /**
1581
+ * REAPI-specific options accepted by {@link ReapiRemoteCache}. A type
1582
+ * alias rather than its own interface so callers can hand the same
1583
+ * object to the backend factory or to the constructor directly without
1584
+ * rewriting field names. HTTP-only fields (`signing`, `compression`)
1585
+ * are silently ignored here.
1586
+ */
1587
+ type ReapiRemoteCacheOptions = RemoteCacheOptions;
1588
+ /**
1589
+ * Bazel REAPI gRPC backend. Implements {@link RemoteCacheBackend} over
1590
+ * `ContentAddressableStorage` + `ActionCache` + `Capabilities` +
1591
+ * `google.bytestream.ByteStream`. Battle-tested REAPI servers
1592
+ * (`bazel-remote`, BuildBuddy, BuildBarn, EngFlow) become drop-in
1593
+ * backends with no per-server adapter.
1594
+ *
1595
+ * Wire flow:
1596
+ * - retrieveAction → GetActionResult → fetchBlob (BatchReadBlobs|Read)
1597
+ * - storeAction → FindMissingBlobs → BatchUpdateBlobs|Write → UpdateActionResult
1598
+ */
1599
+ declare class ReapiRemoteCache implements RemoteCacheBackend {
1600
+ #private;
1601
+ constructor(options: ReapiRemoteCacheOptions);
1602
+ /**
1603
+ * Close all gRPC channels held by this backend. Idempotent — safe
1604
+ * to call multiple times, and safe to call before any RPC was
1605
+ * issued. If `#getClients` is currently in flight we await its
1606
+ * resolution so the underlying channels are observable to close.
1607
+ */
1608
+ close(): Promise<void>;
1609
+ /**
1610
+ * Diagnostic probe — fetches the server's `Capabilities` RPC response
1611
+ * (or the cached value, if a previous call already negotiated). Used
1612
+ * by `vis cache doctor` to surface what the server advertises without
1613
+ * forcing the operator to issue a real CAS RPC.
1614
+ *
1615
+ * Bypasses the read/write mode gate intentionally: a probe must work
1616
+ * even on a cache configured `mode: "write"` so the operator can
1617
+ * verify the connection regardless of how the runner uses it.
1618
+ */
1619
+ probeCapabilities(): Promise<{
1620
+ digestFunctions: ReadonlyArray<string>;
1621
+ maxBatchTotalSizeBytes: number;
1622
+ }>;
1623
+ containsAction(actionDigest: CasDigest): Promise<boolean>;
1624
+ fetchBlob(digest: CasDigest, destinationPath: string): Promise<boolean>;
1625
+ retrieveAction(actionDigest: CasDigest): Promise<ActionResult | null>;
1626
+ storeAction(actionDigest: CasDigest, result: ActionResult, blobs: ReadonlyArray<BlobSource>): Promise<boolean>;
1627
+ }
1628
+ /**
1629
+ * Per-call control over restore fidelity. Both default to `true` —
1630
+ * the cache's job is to recreate exactly what was captured, and any
1631
+ * deviation is opt-in. Callers (e.g. vis target config) can flip
1632
+ * either off when downstream tooling needs a "fresh" mtime/mode.
1633
+ */
1634
+ interface ExtractOptions {
1635
+ preserveMtime?: boolean;
1636
+ preservePerms?: boolean;
1637
+ }
1638
+ /**
1639
+ * Represents a cached task result.
1640
+ */
1641
+ interface CachedResult {
1642
+ /** The exit code of the original task execution */
1643
+ code: number;
1644
+ /** The auto-fingerprint data, if auto-fingerprinting was used */
1645
+ fingerprint?: TaskFingerprint;
1646
+ /** The hash that was used as the cache key */
1647
+ hash: string;
1648
+ /** The terminal output of the original task execution */
1649
+ terminalOutput: string;
1650
+ }
1651
+ /**
1652
+ * Options for creating a Cache instance.
1653
+ */
1654
+ interface CacheOptions {
1655
+ /** The cache directory (defaults to `{workspaceRoot}/.task-runner-cache`) */
1656
+ cacheDirectory?: string;
1657
+ /**
1658
+ * Optional isolation namespace appended to the cache directory
1659
+ * as `&lt;cacheDir>/ns/&lt;namespace>`. When the caller derives the
1660
+ * namespace from the resolved global-env fingerprint, flipping an
1661
+ * env var sends writes into a new namespace while keeping the old
1662
+ * namespace intact — rolling the env back restores the old hits.
1663
+ *
1664
+ * Filesystem-safe segment; callers are responsible for sanitising
1665
+ * (slashes/colons would break path resolution).
1666
+ */
1667
+ cacheNamespace?: string;
1668
+ /** Maximum age of cache entries in milliseconds (default: 7 days) */
1669
+ maxCacheAge?: number;
1670
+ /** Maximum cache size (e.g., "500MB", "1GB") */
1671
+ maxCacheSize?: string;
1672
+ /** The workspace root directory */
1673
+ workspaceRoot: string;
1674
+ }
1675
+ /**
1676
+ * Directory name (relative to `workspaceRoot`) where the task runner writes
1677
+ * its cache by default. Exported so callers that manage the cache from the
1678
+ * outside — e.g. a CLI `cache clean` command — can reach the same default
1679
+ * without hard-coding the literal.
1680
+ */
1681
+ declare const DEFAULT_CACHE_DIRECTORY_NAME = ".task-runner-cache";
1682
+ /**
1683
+ * Parses a human-readable cache size string into bytes.
1684
+ * Delegates to @visulima/humanizer's parseBytes with base-2 (1024) multipliers.
1685
+ */
1686
+ declare const parseCacheSize: (sizeString: string) => number;
1687
+ /**
1688
+ * Formats a byte count into a human-readable string.
1689
+ * Delegates to @visulima/humanizer's formatBytes with base-2 (1024) multipliers.
1690
+ */
1691
+ declare const formatCacheSize: (bytes: number) => string;
1692
+ /**
1693
+ * Local file-based cache for task results.
1694
+ *
1695
+ * Cache structure:
1696
+ * ```
1697
+ * .task-runner-cache/
1698
+ * &lt;hash&gt;/
1699
+ * outputs/ (Archived build outputs)
1700
+ * code (Exit code)
1701
+ * terminalOutput (Captured terminal output)
1702
+ * fingerprint.json (Auto-fingerprint data, optional)
1703
+ * .commit (Marker indicating complete cache entry)
1704
+ * .task-index.json (Task ID -> hash mapping for auto-fingerprint)
1705
+ * ```
1706
+ *
1707
+ * Atomicity: Cache entries are written to a temporary directory first,
1708
+ * then renamed into place. The `.commit` marker ensures readers only
1709
+ * see complete entries.
1710
+ */
1711
+ declare class Cache {
1712
+ #private;
1713
+ constructor(options: CacheOptions);
1714
+ /**
1715
+ * Gets the cache directory path.
1716
+ */
1717
+ get cacheDirectory(): string;
1718
+ /**
1719
+ * Root for v2 CAS-shaped reads/writes. Backend implementations
1720
+ * (HTTP today, REAPI gRPC next) take this path so they can
1721
+ * hydrate fetched blobs into the same CAS the local cache reads
1722
+ * from. Equal to {@link cacheDirectory} — `v2/` lives under that.
1723
+ */
1724
+ get casRoot(): string;
1725
+ /**
1726
+ * Read a v2 {@link ActionResult} by action digest. Resolves to
1727
+ * `null` on miss. The orchestrator is expected to follow up with
1728
+ * {@link materializeOutputs} to place the referenced blobs into
1729
+ * the workspace.
1730
+ */
1731
+ getActionResult(actionDigest: CasDigest): Promise<ActionResult | null>;
1732
+ /**
1733
+ * Look up an action digest by the legacy task hash. Returns
1734
+ * `null` when the bridge file isn't present — caller falls
1735
+ * through to the legacy `&lt;hash>/` layout (or executes the task).
1736
+ */
1737
+ resolveActionDigestForTaskHash(taskHash: string): Promise<string | null>;
1738
+ /**
1739
+ * Persist a v2 entry: writes the AC JSON, copies referenced
1740
+ * blobs into the CAS via the lazy {@link BlobSource} handles,
1741
+ * and binds the task hash → action digest redirect last so a
1742
+ * partial failure can't surface a half-written entry.
1743
+ */
1744
+ putActionResult(taskHash: string, actionDigest: CasDigest, result: ActionResult, blobs: ReadonlyArray<BlobSource>): Promise<void>;
1745
+ /**
1746
+ * Materialize an action's outputs into the workspace. Streams
1747
+ * each referenced blob from the local CAS to its workspace path.
1748
+ * Returns `false` when any blob is missing — caller treats that
1749
+ * as a cache miss and re-executes.
1750
+ */
1751
+ materializeOutputs(result: ActionResult, workspaceRoot: string): Promise<boolean>;
1752
+ /**
1753
+ * Retrieves a cached result for the given task hash.
1754
+ * Returns undefined if no valid cache entry exists.
1755
+ */
1756
+ get(hash: string): Promise<CachedResult | undefined>;
1757
+ /**
1758
+ * Stores a task result in the cache.
1759
+ *
1760
+ * Uses atomic write: builds the entry in a temporary directory,
1761
+ * then renames into place to avoid partial reads by concurrent processes.
1762
+ *
1763
+ * `outputs` accepts the richer `OutputSpec[]` shape — glob
1764
+ * patterns, negative globs, and `{ auto: true }` entries. Pass
1765
+ * `autoWrites` alongside `{ auto: true }` so the resolver knows
1766
+ * which files the task actually wrote; otherwise auto entries
1767
+ * contribute nothing.
1768
+ */
1769
+ put(hash: string, terminalOutput: string, outputs: OutputSpec[], code: number, fingerprint?: TaskFingerprint, autoWrites?: ReadonlyArray<string>): Promise<void>;
1770
+ /**
1771
+ * Restores cached outputs from the compressed `outputs.tar.br`
1772
+ * archive. Returns `true` either when the archive was extracted
1773
+ * successfully OR when the entry simply has no outputs to restore.
1774
+ *
1775
+ * The restore flow stages into a temp directory, then swaps each
1776
+ * top-level entry into place (see {@link restoreOutputsCompressed})
1777
+ * so a mid-restore failure never destroys the user's working tree.
1778
+ * The `outputs` parameter is no longer consulted at restore time —
1779
+ * the archive is authoritative, and top-level entries in the
1780
+ * extracted staging become the set of swap roots. Still accepted
1781
+ * for backward compat.
1782
+ */
1783
+ restoreOutputs(hash: string, _outputs?: OutputSpec[], options?: ExtractOptions): Promise<boolean>;
1784
+ /**
1785
+ * Retrieves the most recent cached result for a task by its ID.
1786
+ * Used in auto-fingerprint mode where the hash is derived from
1787
+ * tracked file accesses rather than computed upfront.
1788
+ */
1789
+ getByTaskId(taskId: string): Promise<CachedResult | undefined>;
1790
+ /**
1791
+ * Stores the mapping from task ID to cache hash.
1792
+ * Uses a write queue to serialize concurrent writes and prevent lost updates.
1793
+ * Each write is atomic (temp file + rename).
1794
+ */
1795
+ setTaskIndex(taskId: string, hash: string): Promise<void>;
1796
+ /**
1797
+ * Removes old cache entries that exceed the maximum age,
1798
+ * and enforces the maximum cache size by evicting oldest entries.
1799
+ */
1800
+ removeOldEntries(): Promise<void>;
1801
+ /**
1802
+ * Clears the entire cache.
1803
+ */
1804
+ clear(): Promise<void>;
1805
+ }
1806
+ /**
1807
+ * Compute the sha256 digest of a buffer's bytes. Lowercase hex, matching
1808
+ * REAPI's `Digest.hash` format. Used for synchronous payloads (small
1809
+ * AC metadata, action proto bytes) where streaming isn't worth it.
1810
+ */
1811
+ declare const digestBuffer: (bytes: Buffer) => CasDigest;
1812
+ /**
1813
+ * Compute the sha256 digest of a file's contents by streaming. Avoids
1814
+ * loading multi-hundred-MB outputs into memory. Returns `undefined`
1815
+ * when the file can't be opened (caller decides whether that's fatal).
1816
+ */
1817
+ declare const digestFile: (filePath: string) => Promise<CasDigest | undefined>;
1818
+ /**
1819
+ * v2 layout root inside the cache directory. Coexists with the legacy
1820
+ * `&lt;cacheDir>/&lt;hash>/` entries until the migration window closes.
1821
+ */
1822
+ declare const V2_ROOT = "v2";
1823
+ /**
1824
+ * Sub-roots under `v2/`. CAS holds raw blob bytes, AC holds JSON
1825
+ * `ActionResult` entries, the task-hash index bridges xxh3 task hashes
1826
+ * to sha256 action digests, tmp stages atomic renames.
1827
+ */
1828
+ declare const V2_CAS = "cas";
1829
+ declare const V2_AC = "ac";
1830
+ declare const V2_INDEX = "task-hash-index";
1831
+ declare const V2_TMP = "tmp";
1832
+ /**
1833
+ * Resolve the on-disk path for a CAS blob. `&lt;root>/v2/cas/&lt;aa>/&lt;hash>`.
1834
+ */
1835
+ declare const casBlobPath: (root: string, hash: string) => string;
1836
+ /**
1837
+ * Resolve the on-disk path for an Action Cache entry. AC entries are
1838
+ * JSON, suffix kept off-disk to match REAPI semantics (the action
1839
+ * digest is the file name).
1840
+ */
1841
+ declare const acEntryPath: (root: string, actionHash: string) => string;
1842
+ /**
1843
+ * Resolve the path for the task-hash → action-digest redirect. 64-byte
1844
+ * file containing the action digest hex; lets `Cache.get(taskHash)`
1845
+ * jump to the AC entry without recomputing the action proto.
1846
+ */
1847
+ declare const taskHashIndexPath: (root: string, taskHash: string) => string;
1848
+ /**
1849
+ * Returns `true` if the CAS blob already exists on disk for the given
1850
+ * digest. Caller uses this for `FindMissingBlobs`-style elision and to
1851
+ * avoid re-uploading bytes the local cache already holds.
1852
+ */
1853
+ declare const containsBlob: (root: string, digest: CasDigest) => Promise<boolean>;
1854
+ /**
1855
+ * Stream a CAS blob from a source path into the store. The blob is
1856
+ * staged under `v2/tmp/` and renamed into `v2/cas/&lt;aa>/&lt;digest>` after
1857
+ * the bytes land. Idempotent: a concurrent writer racing on the same
1858
+ * digest results in two POSIX renames over byte-identical content,
1859
+ * which is atomic per POSIX. On Windows we treat `EEXIST` as success.
1860
+ *
1861
+ * `digest.hash` is trusted — the caller is responsible for computing
1862
+ * the sha256 of the source file first. Re-hashing here would double
1863
+ * the IO on every put.
1864
+ */
1865
+ declare const putBlobFromFile: (root: string, digest: CasDigest, sourcePath: string) => Promise<void>;
1866
+ /**
1867
+ * Same as {@link putBlobFromFile} but for in-memory bytes. Used for
1868
+ * tiny payloads (the AC entry's stdout digest, tree protos) where
1869
+ * streaming would be more code than it's worth.
1870
+ */
1871
+ declare const putBlobFromBytes: (root: string, digest: CasDigest, bytes: Buffer) => Promise<void>;
1872
+ /**
1873
+ * Materialize a CAS blob to a destination path. Streams; safe for
1874
+ * large outputs. Returns `false` if the blob isn't in the local store.
1875
+ */
1876
+ declare const fetchBlobToFile: (root: string, digest: CasDigest, destinationPath: string) => Promise<boolean>;
1877
+ /**
1878
+ * Verify a blob's bytes match its expected digest. Used during legacy
1879
+ * → v2 migration where we trust nothing — sha256 every staged file
1880
+ * before the rename so a bit-flipped legacy artifact doesn't poison
1881
+ * the new CAS.
1882
+ */
1883
+ declare const verifyBlob: (filePath: string, expected: CasDigest) => Promise<boolean>;
1884
+ /**
1885
+ * Bump mtime/atime on a blob. Drives mark-and-sweep GC: the sweeper
1886
+ * evicts blobs whose mtime is older than `maxCacheAge` AND not
1887
+ * referenced by any AC entry. Touching on hit means LRU tracks real
1888
+ * usage rather than write-time.
1889
+ */
1890
+ declare const touchBlob: (root: string, digest: CasDigest) => Promise<void>;
1891
+ /**
1892
+ * Summary of a single task execution.
1893
+ */
1894
+ interface TaskSummary {
1895
+ /** Whether the task was cacheable */
1896
+ cacheable: boolean;
1897
+ /** Cache status */
1898
+ cacheStatus: "HIT" | "MISS" | "REMOTE_HIT" | "SKIPPED";
1899
+ /** Dependencies on other tasks */
1900
+ dependencies: string[];
1901
+ /** Duration in milliseconds */
1902
+ duration: number | undefined;
1903
+ /** End time (ISO 8601) */
1904
+ endTime: string | undefined;
1905
+ /** Exit code */
1906
+ exitCode: number | undefined;
1907
+ /** The computed cache hash */
1908
+ hash: string | undefined;
1909
+ /** Detailed hash information */
1910
+ hashDetails: TaskHashDetails | undefined;
1911
+ /** The task's declared outputs (glob patterns, literals, or `{ auto: true }`). */
1912
+ outputs: OutputSpec[];
1913
+ /** Start time (ISO 8601) */
1914
+ startTime: string | undefined;
1915
+ /** The task target */
1916
+ target: {
1917
+ configuration?: string;
1918
+ project: string;
1919
+ target: string;
1920
+ };
1921
+ /** The task ID (e.g., "app:build") */
1922
+ taskId: string;
1923
+ }
1924
+ /**
1925
+ * Complete summary of a task runner execution.
1926
+ */
1927
+ interface RunSummary {
1928
+ /** Total duration in milliseconds */
1929
+ duration: number;
1930
+ /** Run end time (ISO 8601) */
1931
+ endTime: string;
1932
+ /** Environment info */
1933
+ environment: {
1934
+ /** Architecture */
1935
+ arch: string;
1936
+ /** Node.js version */
1937
+ nodeVersion: string;
1938
+ /** Platform */
1939
+ platform: string;
1940
+ };
1941
+ /** Unique run ID */
1942
+ id: string;
1943
+ /** Run start time (ISO 8601) */
1944
+ startTime: string;
1945
+ /** Overall execution statistics */
1946
+ stats: {
1947
+ /** Number of cached tasks (local + remote) */
1948
+ cached: number;
1949
+ /** Number of failed tasks */
1950
+ failed: number;
1951
+ /** Number of skipped tasks */
1952
+ skipped: number;
1953
+ /** Number of successful tasks */
1954
+ succeeded: number;
1955
+ /** Total number of tasks */
1956
+ total: number;
1957
+ };
1958
+ /** The task graph used for this run */
1959
+ taskGraph: {
1960
+ dependencies: Record<string, string[]>;
1961
+ roots: string[];
1962
+ };
1963
+ /** Summary of each task */
1964
+ tasks: TaskSummary[];
1965
+ }
1966
+ /**
1967
+ * Generates a run summary from task results.
1968
+ */
1969
+ declare const generateRunSummary: (results: TaskResults, taskGraph: TaskGraph, startTime: number) => RunSummary;
1970
+ /**
1971
+ * Writes the run summary to a JSON file in the `.task-runner/runs/` directory.
1972
+ * @param summary The run summary to write
1973
+ * @param workspaceRoot The workspace root directory
1974
+ * @returns The path to the written summary file
1975
+ */
1976
+ declare const writeRunSummary: (summary: RunSummary, workspaceRoot: string) => Promise<string>;
1977
+ /**
1978
+ * Path where the most-recent run summary is persisted.
1979
+ * Consumers (e.g. CLIs exposing `--last-details`) read this file
1980
+ * to replay or render the previous run without re-executing.
1981
+ */
1982
+ declare const getLastRunSummaryPath: (workspaceRoot: string) => string;
1983
+ /**
1984
+ * Persists `summary` as the most-recent run summary at
1985
+ * `.task-runner/last-summary.json`, overwriting any previous entry.
1986
+ *
1987
+ * This is the companion to {@link readLastRunSummary} and powers
1988
+ * CLI surfaces that display "last run" details without re-running tasks.
1989
+ * @returns The path to the written summary file
1990
+ */
1991
+ declare const writeLastRunSummary: (summary: RunSummary, workspaceRoot: string) => Promise<string>;
1992
+ /**
1993
+ * Reads the most-recent run summary written by {@link writeLastRunSummary}.
1994
+ * Returns `undefined` when no previous run has been recorded or the file
1995
+ * cannot be parsed — callers should render an informational message
1996
+ * instead of treating this as an error.
1997
+ */
1998
+ declare const readLastRunSummary: (workspaceRoot: string) => Promise<RunSummary | undefined>;
1999
+ /**
2000
+ * A single event in the Chrome Tracing JSON format. Chrome's
2001
+ * chrome://tracing viewer and Perfetto both accept an array of these.
2002
+ *
2003
+ * Fields track the subset we actually emit:
2004
+ * - `ph: "X"` — "complete" span (has duration)
2005
+ * - `ph: "s"` / `ph: "f"` — flow start / finish (connects two spans)
2006
+ *
2007
+ * See the Chrome Trace Event Format spec on docs.google.com
2008
+ * (document id: 1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU)
2009
+ * for the full specification.
2010
+ */
2011
+ interface ChromeTraceEvent {
2012
+ args?: Record<string, unknown>;
2013
+ /** Event category — grouped in the viewer's search/filter UI. */
2014
+ cat: string;
2015
+ /** Duration in microseconds — only set for `ph: "X"` events. */
2016
+ dur?: number;
2017
+ /** Flow ID — used to draw an arrow from flow-start to flow-finish. */
2018
+ id?: number;
2019
+ /** Human label shown on the timeline. */
2020
+ name: string;
2021
+ /**
2022
+ * Event phase:
2023
+ * - `"X"` — complete (span with duration)
2024
+ * - `"s"` — flow start
2025
+ * - `"f"` — flow finish
2026
+ * - `"M"` — metadata
2027
+ */
2028
+ ph: "f" | "M" | "s" | "X";
2029
+ pid: number;
2030
+ tid: number;
2031
+ /** Timestamp in microseconds. */
2032
+ ts: number;
2033
+ }
2034
+ /**
2035
+ * Converts a {@link RunSummary} into a Chrome Tracing event list that
2036
+ * renders as a gantt chart in chrome://tracing or Perfetto.
2037
+ *
2038
+ * Each task becomes a `"X"` span; dependency edges become flow arrows
2039
+ * from the dependency's finish to the dependent task's start.
2040
+ * Parallel tasks are assigned synthetic `tid` values (lane 0, 1, 2, …)
2041
+ * based on the smallest free lane at the task's start time, so the
2042
+ * timeline clearly shows concurrency without requiring real thread IDs.
2043
+ */
2044
+ declare const toChromeTrace: (summary: RunSummary) => ChromeTraceEvent[];
2045
+ /**
2046
+ * Writes a Chrome Tracing JSON file at `outputPath`. Consumers (e.g.
2047
+ * a CLI `--profile out.json` flag) call this after the run completes
2048
+ * with a RunSummary produced by the task orchestrator.
2049
+ */
2050
+ declare const writeChromeTrace: (summary: RunSummary, outputPath: string) => Promise<void>;
2051
+ /**
2052
+ * Context for token interpolation.
2053
+ *
2054
+ * Currently the only first-class token group is `affected` (and its alias
2055
+ * `changed_files`). Pass `affected.files` (workspace-relative paths) and
2056
+ * the renderer will substitute it into command strings such as
2057
+ * `eslint ${affected.files}` or `prettier ${changed_files | flag '--file'}`.
2058
+ *
2059
+ * Optional `projectRoot` makes paths relative to a single project root,
2060
+ * stripping the leading prefix and the immediately following `/`. Files
2061
+ * outside the root are dropped — token interpolation only emits paths
2062
+ * the task can actually act on.
2063
+ */
2064
+ interface TokenContext {
2065
+ /** Affected/changed files, relative to workspace root. */
2066
+ affectedFiles?: string[];
2067
+ /**
2068
+ * When set, paths are rewritten relative to this project root and
2069
+ * files outside the root are filtered out.
2070
+ */
2071
+ projectRoot?: string;
2072
+ }
2073
+ /**
2074
+ * Expands token references in a single command string.
2075
+ *
2076
+ * Supported tokens:
2077
+ * `${affected.files}` — space-joined, shell-quoted paths
2078
+ * `${changed_files}` — alias of `affected.files`
2079
+ * `${affected.files | flag '--file'}` — `--file path1 --file path2 ...`
2080
+ *
2081
+ * Unknown tokens are left in place — they may be environment-variable
2082
+ * references the shell will expand at runtime, and silently dropping
2083
+ * them would mask bugs in user commands.
2084
+ *
2085
+ * Escape with a leading backslash (`\${affected.files}`) to emit the
2086
+ * literal token without expansion. Note: the regex consumes at most
2087
+ * one leading backslash, so `\\${...}` collapses to `\${...}` rather
2088
+ * than producing a literal backslash followed by the literal token.
2089
+ * Use a different surrounding quoting scheme if you need a real
2090
+ * backslash adjacent to a token.
2091
+ */
2092
+ declare const expandTokensInString: (command: string, context: TokenContext) => string;
2093
+ /**
2094
+ * Pipeline-friendly variant of {@link expandTokensInString} that takes
2095
+ * and returns a `ConcurrentCommandConfig`. Plays the same role as
2096
+ * {@link import("./expand-arguments").expandArguments} so it can slot
2097
+ * into {@link import("./index").parseCommands} as a normal step.
2098
+ */
2099
+ declare const expandTokens: (config: ConcurrentCommandConfig, context: TokenContext) => ConcurrentCommandConfig;
2100
+ /**
2101
+ * Expands argument placeholders in command strings.
2102
+ *
2103
+ * Placeholders:
2104
+ * {1}, {2}, ... -> specific positional argument
2105
+ * {@} -> all arguments, individually quoted
2106
+ * {*} -> all arguments as a single quoted string
2107
+ * \{1} -> literal {1} (escaped)
2108
+ */
2109
+ declare const expandArguments: (config: ConcurrentCommandConfig, additionalArguments: string[]) => ConcurrentCommandConfig;
2110
+ /**
2111
+ * Expands package manager shortcuts into full commands.
2112
+ *
2113
+ * This parser transforms shorthand notation into proper package manager
2114
+ * invocations. No user input is involved -- command strings originate
2115
+ * from the calling code which reads package.json scripts.
2116
+ *
2117
+ * Examples:
2118
+ * npm:build -> npm run build
2119
+ * pnpm:test -> pnpm run test
2120
+ * node:script -> node --run script
2121
+ * deno:task -> deno task task
2122
+ */
2123
+ declare const expandShortcut: (config: ConcurrentCommandConfig) => ConcurrentCommandConfig;
2124
+ /**
2125
+ * Expands wildcard patterns in package manager "run" commands.
2126
+ *
2127
+ * Reads scripts from package.json and matches against the wildcard pattern.
2128
+ * Returns one ConcurrentCommandConfig per matching script.
2129
+ *
2130
+ * Example: "npm run watch-*" with scripts { "watch-js": "...", "watch-css": "..." }
2131
+ * -> ["npm run watch-js", "npm run watch-css"]
2132
+ *
2133
+ * No user input is involved -- patterns come from the calling code.
2134
+ */
2135
+ declare const expandWildcard: (config: ConcurrentCommandConfig) => ConcurrentCommandConfig | ConcurrentCommandConfig[];
2136
+ /**
2137
+ * Removes surrounding quotes from a command string.
2138
+ * Handles both single and double quotes.
2139
+ */
2140
+ declare const stripQuotes: (config: ConcurrentCommandConfig) => ConcurrentCommandConfig;
2141
+ interface ParseCommandsOptions {
2142
+ /** Additional arguments for placeholder expansion ({1}, {@}, {*}). */
2143
+ additionalArguments?: string[];
2144
+ /**
2145
+ * Token interpolation context. When supplied, `${affected.files}`
2146
+ * and `${changed_files | flag '--file'}` references in command
2147
+ * strings are expanded before argument placeholder substitution.
2148
+ */
2149
+ tokens?: TokenContext;
2150
+ }
2151
+ /**
2152
+ * Parse and expand command inputs through the full pipeline:
2153
+ * 1. Normalize string inputs to config objects
2154
+ * 2. Strip surrounding quotes
2155
+ * 3. Expand package manager shortcuts (npm:build -> npm run build)
2156
+ * 4. Expand wildcard patterns (npm run watch-* -> multiple commands)
2157
+ * 5. Expand token references (${affected.files}, ${changed_files | flag '...'})
2158
+ * 6. Expand argument placeholders ({1}, {@}, {*})
2159
+ */
2160
+ declare const parseCommands: (inputs: ConcurrentCommandInput[], options?: ParseCommandsOptions) => ConcurrentCommandConfig[];
2161
+ /**
2162
+ * Run commands concurrently with output streaming and process management.
2163
+ *
2164
+ * Automatically uses the native Rust addon for performance when available,
2165
+ * falling back to a pure JavaScript implementation.
2166
+ *
2167
+ * Supports flow controllers:
2168
+ * - `restart`: retry failed commands with configurable delay/backoff
2169
+ * - `teardown`: run cleanup commands after all processes complete
2170
+ * - `timings`: print a timing summary table
2171
+ * @param commands Array of command strings or config objects
2172
+ * @param options Runner options (maxProcesses, killOthers, restart, teardown, etc.)
2173
+ * @returns Promise resolving to the run result with close events and success status
2174
+ */
2175
+ declare const runConcurrently: (commands: ConcurrentCommandInput[], options?: ConcurrentRunnerOptions) => Promise<ConcurrentRunResult>;
2176
+ /**
2177
+ * Run commands concurrently using pure JavaScript (child_process.spawn).
2178
+ * This is the fallback when the native Rust addon is unavailable.
2179
+ */
2180
+ declare const runConcurrentFallback: (commands: ConcurrentCommandConfig[], options: ConcurrentRunnerOptions) => Promise<ConcurrentRunResult>;
2181
+ /**
2182
+ * The default task runner implementation.
2183
+ *
2184
+ * Runs tasks with caching, scheduling, and lifecycle support.
2185
+ * Supports two caching modes:
2186
+ *
2187
+ * 1. **Nx-style** (default): Explicit input declarations with upfront hash computation.
2188
+ * 2. **Auto-fingerprint** (Vite Task-style): Set `autoFingerprint: true` to automatically
2189
+ * track file accesses and use them for cache invalidation.
2190
+ * @example
2191
+ * ```ts
2192
+ * import { defaultTaskRunner } from "@visulima/task-runner";
2193
+ *
2194
+ * // Nx-style (explicit inputs)
2195
+ * const results = await defaultTaskRunner(tasks, options, context);
2196
+ *
2197
+ * // Vite Task-style (auto-fingerprinting)
2198
+ * const results = await defaultTaskRunner(tasks, {
2199
+ * ...options,
2200
+ * autoFingerprint: true,
2201
+ * fingerprintEnvPatterns: ["VITE_*", "NODE_ENV"],
2202
+ * cacheDiagnostics: true,
2203
+ * }, context);
2204
+ *
2205
+ * // With remote cache
2206
+ * const results = await defaultTaskRunner(tasks, {
2207
+ * ...options,
2208
+ * remoteCache: {
2209
+ * url: "https://cache.example.com",
2210
+ * token: process.env.CACHE_TOKEN,
2211
+ * teamId: "my-team",
2212
+ * },
2213
+ * }, context);
2214
+ *
2215
+ * // Dry-run (inspect hashes without executing)
2216
+ * const results = await defaultTaskRunner(tasks, {
2217
+ * ...options,
2218
+ * dryRun: true,
2219
+ * }, context);
2220
+ * ```
2221
+ */
2222
+ declare const defaultTaskRunner: (_tasks: Task[], options: TaskRunnerOptions, context: TaskRunnerContext) => Promise<TaskResults>;
2223
+ /**
2224
+ * Detect the npm script-shell configuration.
2225
+ *
2226
+ * Returns the shell path if configured, or undefined to use platform defaults.
2227
+ * The result is cached after the first call.
2228
+ */
2229
+ declare const detectScriptShell: () => string | undefined;
2230
+ interface InputHandlerOptions {
2231
+ /** Default command index to route unprefixed input to. Default: 0. */
2232
+ defaultTarget?: number;
2233
+ /** Stream to read input from. Default: process.stdin. */
2234
+ inputStream?: Readable;
2235
+ /** Whether to pause the input stream when all processes finish. Default: true. */
2236
+ pauseOnFinish?: boolean;
2237
+ }
2238
+ interface CommandStdin {
2239
+ index: number;
2240
+ name?: string;
2241
+ stdin: Writable;
2242
+ }
2243
+ /**
2244
+ * Creates an input handler that routes stdin to child processes.
2245
+ * @param commands Map of command index/name to their stdin streams
2246
+ * @param options Input handler configuration
2247
+ * @returns cleanup function to call when done
2248
+ */
2249
+ declare const createInputHandler: (commands: CommandStdin[], options?: InputHandlerOptions) => (() => void);
2250
+ /**
2251
+ * Generate a timing summary table string from close events.
2252
+ * @param closeEvents Close events from the concurrent run (in completion order)
2253
+ * @returns Formatted table string
2254
+ */
2255
+ declare const formatTimingTable: (closeEvents: ConcurrentCloseEvent[]) => string;
2256
+ /**
2257
+ * Print timing summary to a writable stream.
2258
+ * @param closeEvents Close events from the concurrent run
2259
+ * @param output Output stream (default: process.stdout)
2260
+ */
2261
+ declare const logTimings: (closeEvents: ConcurrentCloseEvent[], output?: NodeJS.WritableStream) => void;
2262
+ interface RestartOptions {
2263
+ /** Delay between restarts in milliseconds. "exponential" for 2^attempt * 1000ms. */
2264
+ delay: number | "exponential";
2265
+ /** Maximum number of restart attempts per command. 0 = no restarts. -1 = infinite. */
2266
+ tries: number;
2267
+ }
2268
+ /**
2269
+ * Wraps a runner function to add restart-on-failure behavior.
2270
+ * @param runFn The underlying runner function (runConcurrently or runConcurrentFallback)
2271
+ * @param commands The original command configs
2272
+ * @param options Runner options
2273
+ * @param restartOptions Restart-specific options
2274
+ */
2275
+ declare const withRestart: (runFunction: (commands: ConcurrentCommandConfig[], options: ConcurrentRunnerOptions) => Promise<ConcurrentRunResult>, commands: ConcurrentCommandConfig[], options: ConcurrentRunnerOptions, restartOptions: RestartOptions) => Promise<ConcurrentRunResult>;
2276
+ interface TeardownOptions {
2277
+ /** Commands to run in sequence after all processes complete. */
2278
+ commands: string[];
2279
+ /** Working directory for teardown commands. */
2280
+ cwd?: string;
2281
+ }
2282
+ /**
2283
+ * Run teardown commands sequentially.
2284
+ * Each command runs in the shell with inherited stdio.
2285
+ * If a command fails, subsequent commands are still attempted.
2286
+ * @returns Array of exit codes for each teardown command
2287
+ */
2288
+ declare const runTeardown: (options: TeardownOptions) => Promise<number[]>;
2289
+ /**
2290
+ * Detected framework information.
2291
+ */
2292
+ interface DetectedFramework {
2293
+ /** The env var prefix(es) that should be included in task hashes */
2294
+ envPrefixes: string[];
2295
+ /** The framework name */
2296
+ name: string;
2297
+ }
2298
+ /**
2299
+ * Detects frameworks used in a project by inspecting its package.json dependencies.
2300
+ * @param packageJsonPath Absolute path to the package.json file
2301
+ * @returns Array of detected frameworks with their env prefixes
2302
+ */
2303
+ declare const detectFrameworks: (packageJsonPath: string) => Promise<DetectedFramework[]>;
2304
+ /**
2305
+ * Detects frameworks across all projects in a workspace and returns
2306
+ * the env var patterns that should be included in task hashes.
2307
+ * @param workspaceRoot The workspace root directory
2308
+ * @param projects Map of project name to project configuration with root paths
2309
+ * @returns Array of env var wildcard patterns (e.g., ["NEXT_PUBLIC_*", "VITE_*"])
2310
+ */
2311
+ declare const inferFrameworkEnvPatterns: (workspaceRoot: string, projects: Record<string, {
2312
+ root: string;
2313
+ }>) => Promise<string[]>;
2314
+ /**
2315
+ * For a specific project, detects frameworks and returns the matching
2316
+ * env vars from the current environment.
2317
+ * @param packageJsonPath Absolute path to the project's package.json
2318
+ * @param env The current environment variables
2319
+ * @returns Map of env var name to value for matching framework env vars
2320
+ */
2321
+ declare const getFrameworkEnvVariables: (packageJsonPath: string, env?: Record<string, string | undefined>) => Promise<Record<string, string>>;
2322
+ /**
2323
+ * Graph visualization output formats.
2324
+ */
2325
+ type GraphFormat = "dot" | "json" | "html" | "ascii";
2326
+ /**
2327
+ * Options for graph visualization.
2328
+ */
2329
+ interface GraphVisualizerOptions {
2330
+ /** Show only affected/filtered tasks (highlight subset) */
2331
+ focusedTasks?: string[];
2332
+ /** Group tasks by project (default: true) */
2333
+ groupByProject?: boolean;
2334
+ /** Show task status colors (requires results) */
2335
+ taskStatuses?: Map<string, "success" | "failure" | "skipped" | "local-cache" | "local-cache-kept-existing" | "remote-cache" | "running" | "pending">;
2336
+ }
2337
+ /**
2338
+ * Exports a task graph in DOT format for Graphviz rendering.
2339
+ * @example
2340
+ * ```ts
2341
+ * const dot = toGraphvizDot(taskGraph);
2342
+ * // Render: dot -Tsvg -o graph.svg <<< "$dot"
2343
+ * ```
2344
+ */
2345
+ declare const toGraphvizDot: (taskGraph: TaskGraph, options?: GraphVisualizerOptions) => string;
2346
+ /**
2347
+ * Exports the task graph as a JSON object suitable for visualization tools.
2348
+ */
2349
+ interface GraphJson {
2350
+ edges: {
2351
+ source: string;
2352
+ target: string;
2353
+ }[];
2354
+ nodes: {
2355
+ configuration?: string;
2356
+ id: string;
2357
+ project: string;
2358
+ status?: string;
2359
+ target: string;
2360
+ }[];
2361
+ roots: string[];
2362
+ }
2363
+ declare const toGraphJson: (taskGraph: TaskGraph, taskStatuses?: Map<string, string>) => {
2364
+ edges: GraphJson["edges"];
2365
+ nodes: GraphJson["nodes"];
2366
+ roots: string[];
2367
+ };
2368
+ /**
2369
+ * Generates a self-contained HTML file with an interactive task graph visualization.
2370
+ * Uses a simple force-directed layout with SVG rendering (no external dependencies).
2371
+ */
2372
+ declare const toGraphHtml: (taskGraph: TaskGraph, options?: GraphVisualizerOptions) => string;
2373
+ /**
2374
+ * Renders the task graph as ASCII art for terminal display.
2375
+ * @example
2376
+ * ```
2377
+ * Task Graph (6 tasks, 5 dependencies)
2378
+ *
2379
+ * app:build
2380
+ * ├── lib-a:build
2381
+ * │ └── lib-core:build
2382
+ * └── lib-b:build
2383
+ * └── lib-core:build (*)
2384
+ *
2385
+ * (*) = already shown above
2386
+ * ```
2387
+ */
2388
+ declare const toGraphAscii: (taskGraph: TaskGraph, options?: GraphVisualizerOptions) => string;
2389
+ /**
2390
+ * Exports a project graph in DOT format.
2391
+ */
2392
+ declare const projectGraphToDot: (projectGraph: ProjectGraph) => string;
2393
+ /**
2394
+ * Incremental file hasher that only re-hashes files that have changed
2395
+ * since the last run, based on mtime comparison.
2396
+ *
2397
+ * This is the key performance optimization used by Nx's daemon and
2398
+ * Turborepo's daemon — on subsequent runs, only files whose mtime
2399
+ * changed need to be re-read and re-hashed.
2400
+ *
2401
+ * The snapshot (path → { mtime, hash }) is kept in memory and can
2402
+ * optionally be persisted to disk for cross-process reuse.
2403
+ */
2404
+ interface FileSnapshot {
2405
+ /** xxh3-128 hash of file contents */
2406
+ hash: string;
2407
+ /** Last modification time in milliseconds */
2408
+ mtimeMs: number;
2409
+ /** File size in bytes (fast pre-check) */
2410
+ size: number;
2411
+ }
2412
+ interface IncrementalHasherOptions {
2413
+ /** Directories to skip (default: node_modules, .git, dist, coverage, .cache) */
2414
+ ignoredDirs?: Set<string>;
2415
+ /** File to persist the snapshot to (for cross-run reuse) */
2416
+ snapshotPath?: string;
2417
+ workspaceRoot: string;
2418
+ }
2419
+ declare class IncrementalFileHasher {
2420
+ #private;
2421
+ constructor(options: IncrementalHasherOptions);
2422
+ /**
2423
+ * Loads the snapshot from disk if available.
2424
+ * Called automatically on first use.
2425
+ */
2426
+ load(): Promise<void>;
2427
+ /**
2428
+ * Persists the current snapshot to disk for cross-run reuse.
2429
+ */
2430
+ save(): Promise<void>;
2431
+ /**
2432
+ * Incrementally hashes all files in a directory.
2433
+ *
2434
+ * Only files whose mtime or size changed since the last snapshot
2435
+ * are re-read and re-hashed. Unchanged files reuse the cached hash.
2436
+ *
2437
+ * Returns a map of relative paths → hashes.
2438
+ */
2439
+ hashDirectory(directoryPath: string): Promise<Record<string, string>>;
2440
+ /**
2441
+ * Returns how many files are in the snapshot (for diagnostics).
2442
+ */
2443
+ get snapshotSize(): number;
2444
+ /**
2445
+ * Clears the in-memory snapshot.
2446
+ */
2447
+ clear(): void;
2448
+ /**
2449
+ * Looks up the cached hash for `absolutePath` when its mtime and
2450
+ * size match the snapshot; returns `undefined` otherwise. Callers
2451
+ * that already have a `stat` result (e.g. `InProcessTaskHasher`)
2452
+ * skip an extra syscall by passing it through directly.
2453
+ *
2454
+ * The snapshot is considered loaded on first call — lazy-load is
2455
+ * synchronous-friendly here because the caller already performed
2456
+ * an async `stat` before calling this method.
2457
+ */
2458
+ getSnapshotHash(absolutePath: string, mtimeMs: number, size: number): string | undefined;
2459
+ /**
2460
+ * Writes a fresh snapshot entry after the caller has computed the
2461
+ * hash. Pairs with {@link IncrementalFileHasher.getSnapshotHash}
2462
+ * — after a miss, the caller hashes the file and records the
2463
+ * result here so the next run can reuse it.
2464
+ */
2465
+ recordSnapshot(absolutePath: string, hash: string, mtimeMs: number, size: number): void;
2466
+ }
2467
+ /**
2468
+ * Combines multiple lifecycle handlers into one.
2469
+ * Each event is forwarded to all registered handlers.
2470
+ */
2471
+ declare class CompositeLifeCycle implements LifeCycleInterface {
2472
+ #private;
2473
+ constructor(lifeCycles: LifeCycleInterface[]);
2474
+ startCommand(): void;
2475
+ endCommand(): void;
2476
+ scheduleTask(task: Task): void;
2477
+ startTasks(tasks: Task[]): void;
2478
+ endTasks(taskResults: TaskResult[]): void;
2479
+ printTaskTerminalOutput(task: Task, status: TaskStatus, terminalOutput: string): void;
2480
+ printCacheMiss(task: Task, reasons: string): void;
2481
+ onTaskStdout(task: Task, chunk: string): void;
2482
+ onTaskStderr(task: Task, chunk: string): void;
2483
+ }
2484
+ /**
2485
+ * A lifecycle handler that logs task progress to the console.
2486
+ */
2487
+ declare class ConsoleLifeCycle implements LifeCycleInterface {
2488
+ #private;
2489
+ constructor(verbose?: boolean);
2490
+ startCommand(): void;
2491
+ endCommand(): void;
2492
+ scheduleTask(task: Task): void;
2493
+ startTasks(tasks: Task[]): void;
2494
+ endTasks(taskResults: TaskResult[]): void;
2495
+ printTaskTerminalOutput(_task: Task, _status: TaskStatus, terminalOutput: string): void;
2496
+ printCacheMiss(task: Task, reasons: string): void;
2497
+ }
2498
+ /**
2499
+ * A no-op lifecycle handler. Useful as a default.
2500
+ */
2501
+ declare class EmptyLifeCycle implements LifeCycleInterface {}
2502
+ /**
2503
+ * Resolved dependency entry from a lockfile.
2504
+ */
2505
+ interface ResolvedDependency {
2506
+ /** The package name */
2507
+ name: string;
2508
+ /** The resolved version */
2509
+ version: string;
2510
+ }
2511
+ /**
2512
+ * Result of parsing a lockfile for a specific package.
2513
+ */
2514
+ interface PackageLockfileHash {
2515
+ /** The resolved dependencies that were included in the hash */
2516
+ dependencies: ResolvedDependency[];
2517
+ /** Hash of the resolved dependencies relevant to this package */
2518
+ hash: string;
2519
+ }
2520
+ /**
2521
+ * Extracts a package name from a node_modules path.
2522
+ * E.g., "node_modules/@scope/name" -> "@scope/name",
2523
+ * "node_modules/name" -> "name",
2524
+ * "node_modules/.package-lock.json" -> undefined.
2525
+ */
2526
+ declare const extractPackageName: (path: string) => string | undefined;
2527
+ /**
2528
+ * Parses package-lock.json (npm v2/v3 format) to extract resolved versions.
2529
+ * The v2/v3 format uses a flat "packages" map with paths like "node_modules/pkg-name".
2530
+ */
2531
+ declare const parseNpmLockfile: (content: string) => Map<string, string>;
2532
+ /**
2533
+ * Parses pnpm-lock.yaml to extract resolved versions.
2534
+ * Uses a lightweight regex-based parser to avoid a YAML dependency.
2535
+ */
2536
+ declare const parsePnpmLockfile: (content: string) => Map<string, string>;
2537
+ /**
2538
+ * Parses yarn.lock to extract resolved versions.
2539
+ * Works with both Yarn Classic (v1) and Berry (v2+) formats.
2540
+ */
2541
+ declare const parseYarnLockfile: (content: string) => Map<string, string>;
2542
+ /**
2543
+ * Smart lockfile hasher that only hashes the resolved versions
2544
+ * of a package's actual dependencies, not the entire lockfile.
2545
+ *
2546
+ * This matches Turborepo's smart lockfile hashing behavior:
2547
+ * changing the lockfile only busts cache for affected packages.
2548
+ *
2549
+ * Supports:
2550
+ * - package-lock.json (npm v2/v3)
2551
+ * - pnpm-lock.yaml (pnpm)
2552
+ * - yarn.lock (Yarn Classic + Berry)
2553
+ */
2554
+ declare class LockfileHasher {
2555
+ #private;
2556
+ constructor(workspaceRoot: string);
2557
+ /**
2558
+ * Computes a hash based only on the resolved dependency versions
2559
+ * relevant to a specific package.
2560
+ * @param packageJsonPath Path to the package.json (relative to workspace root)
2561
+ * @returns Hash of the relevant lockfile entries, or undefined if no lockfile found
2562
+ */
2563
+ hashForPackage(packageJsonPath: string): Promise<PackageLockfileHash | undefined>;
2564
+ /**
2565
+ * Returns the type of lockfile detected, or undefined if none found.
2566
+ */
2567
+ get lockfileType(): "npm" | "pnpm" | "yarn" | undefined;
2568
+ /**
2569
+ * Clears the cached lockfile data.
2570
+ */
2571
+ clearCache(): void;
2572
+ }
2573
+ /**
2574
+ * Output formatting mode for task terminal output.
2575
+ *
2576
+ * - `interleaved` **(default)**: emit each task's buffered output as-is
2577
+ * — lines from parallel tasks may intermix when streamed live.
2578
+ * - `labeled`: prefix every line with `[project#target]` so parallel
2579
+ * tasks remain distinguishable.
2580
+ * - `grouped`: buffer each task's output and print it as a single block
2581
+ * bracketed by `── project#target ──` header and blank-line footer.
2582
+ *
2583
+ * Matches the three modes exposed by vite-task's `--log` flag.
2584
+ */
2585
+ type LogMode = "grouped" | "interleaved" | "labeled";
2586
+ /**
2587
+ * A lifecycle handler that renders task terminal output per {@link LogMode}.
2588
+ *
2589
+ * Operates on the buffered `printTaskTerminalOutput` signal the orchestrator
2590
+ * emits at task-completion. Line-by-line streaming is the consumer's
2591
+ * responsibility — a streaming reporter can wrap this one and emit buffered
2592
+ * output at the end of each task regardless of streaming choices.
2593
+ */
2594
+ declare class LogReporter implements LifeCycleInterface {
2595
+ #private;
2596
+ constructor(mode: LogMode, write?: (chunk: string) => void);
2597
+ printTaskTerminalOutput(task: Task, _status: TaskStatus, terminalOutput: string): void;
2598
+ endTasks(_taskResults: TaskResult[]): void;
2599
+ }
2600
+ /**
2601
+ * Convenience factory matching vite-task's `createLogReporter(mode)` surface.
2602
+ * Consumers that already compose their own lifecycle handlers can instantiate
2603
+ * {@link LogReporter} directly.
2604
+ */
2605
+ declare const createLogReporter: (mode: LogMode, write?: (chunk: string) => void) => LogReporter;
2606
+ interface NativeFileHash {
2607
+ hash: string;
2608
+ path: string;
2609
+ }
2610
+ interface NativeTaskHashDetails {
2611
+ command: string;
2612
+ implicit_deps?: string[][];
2613
+ nodes: string[][];
2614
+ runtime?: string[][];
2615
+ }
2616
+ interface NativeTaskGraph {
2617
+ edges: string[][];
2618
+ task_ids: string[];
2619
+ }
2620
+ interface NativeCycleResult {
2621
+ cycle: string[];
2622
+ has_cycle: boolean;
2623
+ }
2624
+ interface NativeConcurrentCommandConfig {
2625
+ command: string;
2626
+ cwd?: string;
2627
+ env?: Record<string, string>;
2628
+ name?: string;
2629
+ shell?: boolean;
2630
+ stdin?: string;
2631
+ }
2632
+ interface NativeConcurrentRunnerOptions {
2633
+ killOthers?: string[];
2634
+ killSignal?: string;
2635
+ killTimeout?: number;
2636
+ maxProcesses?: number;
2637
+ shellPath?: string;
2638
+ successCondition?: string;
2639
+ }
2640
+ interface NativeProcessEvent {
2641
+ commandName?: string;
2642
+ durationMs?: number;
2643
+ exitCode?: number;
2644
+ index: number;
2645
+ killed?: boolean;
2646
+ kind: string;
2647
+ message?: string;
2648
+ text?: string;
2649
+ }
2650
+ interface NativeConcurrentCloseEvent {
2651
+ command: string;
2652
+ durationMs: number;
2653
+ exitCode: number;
2654
+ index: number;
2655
+ killed: boolean;
2656
+ name?: string;
2657
+ }
2658
+ interface NativeConcurrentRunResult {
2659
+ closeEvents: NativeConcurrentCloseEvent[];
2660
+ success: boolean;
2661
+ }
2662
+ interface NativeBindings {
2663
+ collectFiles: (directory: string) => string[];
2664
+ computeTaskHash: (details: NativeTaskHashDetails) => string;
2665
+ findAllCycles: (graph: NativeTaskGraph) => string[][];
2666
+ findBackEdges: (graph: NativeTaskGraph) => string[][];
2667
+ findCycle: (graph: NativeTaskGraph) => NativeCycleResult;
2668
+ getDependentTasks: (graph: NativeTaskGraph, taskId: string) => string[];
2669
+ getMainWorktreeRoot: (workspaceRoot: string) => string | undefined | null;
2670
+ getTransitiveDeps: (graph: NativeTaskGraph, taskId: string) => string[];
2671
+ hashCommand: (project: string, target: string, configuration: string | undefined, overridesJson: string) => string;
2672
+ hashEnvVar: (name: string, value: string) => string;
2673
+ hashFile: (filePath: string) => string;
2674
+ hashFilesBatch: (filePaths: string[], workspaceRoot: string) => NativeFileHash[];
2675
+ hashFilesInDirectory: (directory: string, workspaceRoot: string) => NativeFileHash[];
2676
+ hashString: (input: string) => string;
2677
+ hashStrings: (inputs: string[]) => string;
2678
+ isLinkedWorktree: (workspaceRoot: string) => boolean;
2679
+ resetWorktreeCache: () => void;
2680
+ runConcurrent: (commands: NativeConcurrentCommandConfig[], options: NativeConcurrentRunnerOptions, onEvent: (event: NativeProcessEvent) => void) => Promise<NativeConcurrentRunResult>;
2681
+ runConcurrentBatch: (commands: NativeConcurrentCommandConfig[], options: NativeConcurrentRunnerOptions) => Promise<NativeConcurrentRunResult>;
2682
+ topologicalSort: (graph: NativeTaskGraph) => string[];
2683
+ }
2684
+ /**
2685
+ * Attempts to load the native addon. Returns undefined if unavailable.
2686
+ * The result is cached after the first attempt.
2687
+ *
2688
+ * napi v3 outputs the .node file to the package root as
2689
+ * `task-runner-native.&lt;platform>.node`. The napi-generated index.js
2690
+ * handles platform detection automatically.
2691
+ *
2692
+ * Uses createRequire because the napi-generated index.js is CJS.
2693
+ */
2694
+ declare const loadNativeBindings: () => NativeBindings | undefined;
2695
+ /**
2696
+ * Returns true if the native addon is loaded and available.
2697
+ */
2698
+ declare const isNativeAvailable: () => boolean;
2699
+ /**
2700
+ * Expands a task's `OutputSpec[]` into the concrete file list to
2701
+ * archive:
2702
+ *
2703
+ * - literal paths → kept as-is (the archiver recursively copies
2704
+ * directories, so `"dist"` captures its whole tree);
2705
+ * - positive globs → expanded via `fs.glob`, filtered to files only;
2706
+ * - negatives (`!pattern`) → applied to the combined result;
2707
+ * - `{ auto: true }` → pulls in `autoWrites` entries that fall inside
2708
+ * the workspace.
2709
+ *
2710
+ * Returns deduped, sorted workspace-relative paths so archives are
2711
+ * byte-reproducible across invocations.
2712
+ *
2713
+ * Silent degradation: missing literal paths, empty glob matches, and
2714
+ * `{ auto: true }` without tracked writes all contribute nothing
2715
+ * rather than throwing.
2716
+ */
2717
+ declare const resolveOutputs: (workspaceRoot: string, outputs: OutputSpec[] | undefined, autoWrites?: ReadonlyArray<string>) => Promise<string[]>;
2718
+ /**
2719
+ * URI schemes recognized by {@link parseInputUri}. Each maps to one of the
2720
+ * existing structured `InputDefinition` shapes — the URI form is a sugar
2721
+ * over the object form so the same hash semantics apply unchanged.
2722
+ *
2723
+ * - `file://&lt;path>` and `glob://&lt;pattern>` → {@link FileSetInput}
2724
+ * (both produce a fileset; the split is purely documentary so a reader
2725
+ * can tell at a glance whether the author meant a single path or a glob).
2726
+ * - `env://&lt;NAME>` → {@link EnvironmentInput}.
2727
+ * - `func://&lt;command>` → {@link RuntimeInput} (runtime command output).
2728
+ * - `dep://&lt;a,b,c>` → {@link ExternalDependencyInput} (comma-separated names).
2729
+ *
2730
+ * `{projectRoot}` and `{workspaceRoot}` tokens are honored inside `file://`
2731
+ * and `glob://` bodies, matching the bare-string form. Negation works for
2732
+ * filesets only (`!file://...`, `!glob://...`); attempting to negate the
2733
+ * other schemes throws because there is no semantic for "not this env var".
2734
+ */
2735
+ declare const INPUT_URI_SCHEMES: readonly ["file", "glob", "env", "func", "dep"];
2736
+ type InputUriScheme = (typeof INPUT_URI_SCHEMES)[number];
2737
+ /**
2738
+ * Thrown when a string carries a URI-shaped prefix but the scheme is
2739
+ * unrecognized or the body violates a scheme-specific constraint
2740
+ * (e.g. negating a non-fileset scheme).
2741
+ *
2742
+ * Surfaces as a config-load error rather than degrading silently into a
2743
+ * fileset glob — silent fallback would let typos like `gob://**` mask
2744
+ * cache-correctness bugs that only show up at hash-divergence time.
2745
+ */
2746
+ declare class InvalidInputUriError extends Error {
2747
+ constructor(message: string);
2748
+ }
2749
+ /**
2750
+ * Parses a URI-prefixed input string into its structured `InputDefinition`.
2751
+ * Returns `undefined` for strings that don't carry a URI scheme — callers
2752
+ * fall back to existing handling (named-input lookup, bare globs).
2753
+ */
2754
+ declare const parseInputUri: (input: string) => InputDefinition | undefined;
2755
+ /**
2756
+ * Cheap predicate used by callers that only need to know whether a string
2757
+ * looks like a URI — saves them re-parsing when they want to short-circuit
2758
+ * other handling (e.g. file-group lookup) before delegating to
2759
+ * {@link parseInputUri} downstream.
2760
+ */
2761
+ declare const looksLikeInputUri: (input: string) => boolean;
2762
+ /**
2763
+ * Enforces project dependency constraints on a project graph.
2764
+ * @param projectGraph The workspace project graph to validate.
2765
+ * @param constraints The constraint rules to enforce.
2766
+ * @returns Array of violations found. Empty means all constraints pass.
2767
+ */
2768
+ declare const enforceProjectConstraints: (projectGraph: ProjectGraph, constraints: ConstraintsConfig) => ConstraintViolation[];
2769
+ interface CreateTaskGraphOptions {
2770
+ /** The project graph */
2771
+ projectGraph: ProjectGraph;
2772
+ /** Target default configurations */
2773
+ targetDefaults?: Record<string, Partial<TargetConfiguration>>;
2774
+ /** The workspace configuration */
2775
+ workspace: WorkspaceConfiguration;
2776
+ }
2777
+ /**
2778
+ * Creates a unique task ID from a target.
2779
+ */
2780
+ declare const getTaskId: (target: TaskTarget) => string;
2781
+ /**
2782
+ * Parses a task ID into its component parts.
2783
+ */
2784
+ declare const parseTaskId: (taskId: string) => TaskTarget;
2785
+ /**
2786
+ * Creates a task graph from a list of tasks, resolving all dependencies.
2787
+ */
2788
+ declare const createTaskGraph: (initialTasks: Task[], options: CreateTaskGraphOptions) => TaskGraph;
2789
+ /**
2790
+ * Finds a single cycle in the task graph, if one exists.
2791
+ * Returns the cycle as an array of task IDs, or null if no cycle exists.
2792
+ */
2793
+ declare const findCycle: (taskGraph: TaskGraph) => string[] | undefined;
2794
+ /**
2795
+ * Finds all cycles in the task graph.
2796
+ */
2797
+ declare const findCycles: (taskGraph: TaskGraph) => string[][];
2798
+ /**
2799
+ * Walks the task graph in topological order (dependencies before dependents),
2800
+ * calling the callback for each task.
2801
+ *
2802
+ * Note: If the graph contains cycles, tasks involved in cycles will not be visited.
2803
+ * Use `findCycle` to detect cycles before walking if complete traversal is required.
2804
+ */
2805
+ declare const walkTaskGraph: (taskGraph: TaskGraph, callback: (taskId: string) => void) => void;
2806
+ /**
2807
+ * Returns a reversed copy of the task graph (edges point in the opposite direction).
2808
+ */
2809
+ declare const reverseTaskGraph: (taskGraph: TaskGraph) => TaskGraph;
2810
+ /**
2811
+ * Returns the leaf tasks (tasks with no dependencies of their own).
2812
+ */
2813
+ declare const getLeafTasks: (taskGraph: TaskGraph) => string[];
2814
+ /**
2815
+ * Removes edges that form cycles, making the graph acyclic.
2816
+ * Returns a new task graph without the cycle-forming edges.
2817
+ */
2818
+ declare const makeAcyclic: (taskGraph: TaskGraph) => TaskGraph;
2819
+ /**
2820
+ * Gets all tasks that depend on the given task (directly or transitively).
2821
+ */
2822
+ declare const getDependentTasks: (taskGraph: TaskGraph, taskId: string) => string[];
2823
+ /**
2824
+ * Gets all tasks that the given task depends on (directly or transitively).
2825
+ */
2826
+ declare const getTransitiveDependencies: (taskGraph: TaskGraph, taskId: string) => string[];
2827
+ /**
2828
+ * Interface for task hashers.
2829
+ */
2830
+ interface TaskHasher {
2831
+ hashTask: (task: Task) => Promise<TaskHashDetails>;
2832
+ /**
2833
+ * Rehashes a single file bypassing any in-memory cache. Optional to keep
2834
+ * external/custom hashers backward compatible; the orchestrator skips
2835
+ * self-modifying-task detection when the implementation is absent.
2836
+ */
2837
+ rehashFile?: (filePath: string) => Promise<string | undefined>;
2838
+ }
2839
+ /**
2840
+ * Options for creating an InProcessTaskHasher.
2841
+ */
2842
+ interface TaskHasherOptions {
2843
+ /**
2844
+ * When true, scan each task's resolved command for `$VAR`/`${VAR}`
2845
+ * references and auto-fingerprint them. Catches the common case of
2846
+ * a script reading `$VERCEL_URL` or `${NEXT_PUBLIC_API}` without
2847
+ * the user remembering to declare it in `envVars`/`globalEnv`.
2848
+ * @default false
2849
+ */
2850
+ autoEnvVars?: boolean;
2851
+ /** Additional environment variables to include in hash */
2852
+ envVars?: string[];
2853
+ /**
2854
+ * Enable framework environment variable inference.
2855
+ * When true, auto-detects frameworks and includes their public
2856
+ * env var prefixes in the task hash.
2857
+ * @default false
2858
+ */
2859
+ frameworkInference?: boolean;
2860
+ /**
2861
+ * Global environment variables that invalidate all task hashes.
2862
+ */
2863
+ globalEnv?: string[];
2864
+ /**
2865
+ * Global input files that invalidate all task hashes when changed.
2866
+ * These are workspace-root-relative paths (e.g., "pnpm-lock.yaml").
2867
+ */
2868
+ globalInputs?: string[];
2869
+ /**
2870
+ * Optional persistent mtime/size-indexed file snapshot. When set,
2871
+ * `#hashFile` consults the snapshot first and only re-reads file
2872
+ * contents when the file's mtime or size has changed since the
2873
+ * previous run. Cuts cold-cache fingerprint time dramatically on
2874
+ * large workspaces where most source files don't change run-to-run.
2875
+ *
2876
+ * The caller is responsible for `load()`ing the snapshot before
2877
+ * using the hasher and `save()`ing it after the run completes.
2878
+ */
2879
+ incrementalHasher?: IncrementalFileHasher;
2880
+ /** Named input definitions */
2881
+ namedInputs?: NamedInputs;
2882
+ /** Project configurations keyed by project name */
2883
+ projects: Record<string, ProjectConfiguration>;
2884
+ /**
2885
+ * Enable smart lockfile hashing.
2886
+ * When true, instead of hashing the entire lockfile, only the resolved
2887
+ * versions of a package's actual dependencies are hashed.
2888
+ * This means changing the lockfile only busts cache for affected packages.
2889
+ *
2890
+ * Matches Turborepo's smart lockfile hashing behavior.
2891
+ * @default false
2892
+ */
2893
+ smartLockfileHashing?: boolean;
2894
+ /** Target default configurations */
2895
+ targetDefaults?: Record<string, Partial<TargetConfiguration>>;
2896
+ /** The workspace root directory */
2897
+ workspaceRoot: string;
2898
+ }
2899
+ /**
2900
+ * Computes hashes for tasks based on their inputs.
2901
+ * Used to determine if a cached result can be reused.
2902
+ */
2903
+ declare class InProcessTaskHasher implements TaskHasher {
2904
+ #private;
2905
+ constructor(options: TaskHasherOptions);
2906
+ hashTask(task: Task): Promise<TaskHashDetails>;
2907
+ clearCache(): void;
2908
+ /**
2909
+ * Reads `filePath` fresh and returns its content hash, bypassing the
2910
+ * in-memory cache used during the initial `hashTask` pass.
2911
+ *
2912
+ * Used to detect tasks that modify their own tracked inputs: compare
2913
+ * a pre-execution hash (from `task.hashDetails.nodes`) against the
2914
+ * post-execution result of this method.
2915
+ * @param filePath Absolute path to the file.
2916
+ * @returns The fresh xxh3 hash, or `undefined` if the file cannot be read.
2917
+ */
2918
+ rehashFile(filePath: string): Promise<string | undefined>;
2919
+ }
2920
+ /**
2921
+ * Computes the final hash for a task from its hash details.
2922
+ * Uses native Rust xxh3-128 when available, otherwise pure TS xxh3-ts.
2923
+ * Both produce identical xxh3-128 hashes, ensuring cache compatibility
2924
+ * regardless of whether the native addon is loaded.
2925
+ */
2926
+ declare const computeTaskHash: (hashDetails: TaskHashDetails) => string;
2927
+ /**
2928
+ * Options for partitioning tasks across CI runners.
2929
+ */
2930
+ interface PartitionOptions {
2931
+ /** 1-based partition index (e.g., 1 for the first partition) */
2932
+ index: number;
2933
+ /** Total number of partitions */
2934
+ total: number;
2935
+ }
2936
+ /**
2937
+ * Parses a partition string like "1/4" into PartitionOptions.
2938
+ * Also supports the VIS_PARTITION environment variable as fallback.
2939
+ */
2940
+ declare const parsePartition: (value?: string) => PartitionOptions | undefined;
2941
+ /**
2942
+ * Manages the scheduling order of tasks based on dependencies,
2943
+ * parallelism constraints, and estimated execution times.
2944
+ */
2945
+ declare class TaskScheduler {
2946
+ #private;
2947
+ /**
2948
+ * Partitions a list of tasks for distributed CI execution.
2949
+ * Tasks are sorted by ID for deterministic distribution, then split
2950
+ * using ceiling division so partitions differ by at most one task.
2951
+ * @param tasks The full list of tasks to partition
2952
+ * @param partition The partition configuration (1-based index and total)
2953
+ * @returns The subset of tasks assigned to this partition
2954
+ */
2955
+ static partitionTasks(tasks: Task[], partition: PartitionOptions): Task[];
2956
+ constructor(taskGraph: TaskGraph, projectGraph: ProjectGraph, maxParallel?: number, concurrencyGroups?: ConcurrencyGroups);
2957
+ /**
2958
+ * Returns the next batch of tasks that are ready to execute.
2959
+ */
2960
+ getNextBatch(): Task[];
2961
+ startTask(taskId: string): void;
2962
+ completeTask(taskId: string): void;
2963
+ isComplete(): boolean;
2964
+ get remainingCount(): number;
2965
+ get runningCount(): number;
2966
+ }
2967
+ /**
2968
+ * Options for the TaskOrchestrator.
2969
+ */
2970
+ interface TaskOrchestratorOptions {
2971
+ /**
2972
+ * Tasks marked `always: true` to run after the main task graph
2973
+ * completes. Run sequentially, in declaration order, even if the
2974
+ * main run failed or was aborted (SIGINT skips them — that's an
2975
+ * explicit user request to stop). Skipped if their `when` clause
2976
+ * doesn't match.
2977
+ */
2978
+ alwaysTasks?: Task[];
2979
+ autoFingerprint?: boolean;
2980
+ cache: Cache;
2981
+ cacheDiagnostics?: boolean;
2982
+ captureOutput?: boolean;
2983
+ dryRun?: boolean;
2984
+ fingerprintEnvPatterns?: string[];
2985
+ lifeCycle: LifeCycleInterface;
2986
+ /**
2987
+ * Surfaces bridge-local upload pipeline failures (tar / digest)
2988
+ * for fire-and-forget remote-cache writes. Wire-level errors are
2989
+ * already reported by the backend's own `onUploadError`; this
2990
+ * fills the gap for steps the backend never sees.
2991
+ */
2992
+ onRemoteUploadError?: (hash: string, error: unknown) => void;
2993
+ remoteCache?: RemoteCacheBackend;
2994
+ resolveCommand?: (task: Task) => string | undefined;
2995
+ scheduler: TaskScheduler;
2996
+ skipCache?: boolean;
2997
+ summarize?: boolean;
2998
+ taskExecutor: TaskExecutor;
2999
+ taskGraph?: TaskGraph;
3000
+ taskHasher: TaskHasher;
3001
+ untrackedEnvVars?: string[];
3002
+ /**
3003
+ * Context used to evaluate per-task `when` conditions. Defaults
3004
+ * to the live process state — `process.env`, `process.platform`,
3005
+ * git branch read from `workspaceRoot`. Override in tests.
3006
+ */
3007
+ whenContext?: WhenContext;
3008
+ workspaceRoot: string;
3009
+ }
3010
+ /**
3011
+ * Orchestrates the execution of tasks, handling caching,
3012
+ * scheduling, and lifecycle events.
3013
+ */
3014
+ declare class TaskOrchestrator {
3015
+ #private;
3016
+ constructor(options: TaskOrchestratorOptions);
3017
+ run(): Promise<TaskResults>;
3018
+ }
3019
+ /**
3020
+ * Minimal virtual terminal buffer that processes ANSI escape sequences
3021
+ * for cursor movement and line erasure. This allows PTY output from
3022
+ * interactive tools (inquirer, etc.) to render correctly by updating
3023
+ * lines in place rather than always appending.
3024
+ *
3025
+ * Supported sequences:
3026
+ * - \r carriage return (cursor to column 0)
3027
+ * - \n line feed (new line)
3028
+ * - \x1b[nA cursor up n lines
3029
+ * - \x1b[nB cursor down n lines
3030
+ * - \x1b[nC cursor forward n columns
3031
+ * - \x1b[nD cursor back n columns
3032
+ * - \x1b[nG cursor to column n
3033
+ * - \x1b[r;cH cursor position
3034
+ * - \x1b[K erase from cursor to end of line (0K, 1K, 2K)
3035
+ * - \x1b[J erase from cursor to end of display (0J, 1J, 2J)
3036
+ * - \x1b[...m SGR (colors/styles) — passed through into output
3037
+ */
3038
+ declare class TerminalBuffer {
3039
+ #private;
3040
+ constructor(maxBytes?: number);
3041
+ /**
3042
+ * Process raw PTY output data.
3043
+ */
3044
+ write(data: string): void;
3045
+ /** Get the current buffer content as a string. */
3046
+ toString(): string;
3047
+ }
3048
+ /**
3049
+ * Result of a tracked task execution.
3050
+ */
3051
+ interface TrackedExecutionResult {
3052
+ /** File accesses recorded during execution */
3053
+ accesses: FileAccess[];
3054
+ /** The command exit code */
3055
+ code: number;
3056
+ /** The command stdout + stderr output */
3057
+ terminalOutput: string;
3058
+ }
3059
+ /**
3060
+ * A task executor that tracks file accesses during command execution.
3061
+ *
3062
+ * Tracking strategies (in priority order):
3063
+ * 1. **Linux**: strace-based syscall interception (most complete)
3064
+ * 2. **macOS/Windows**: Node.js preload script that patches `fs` module
3065
+ * (works for Node.js processes, not native binaries)
3066
+ * 3. **Fallback**: No tracking (accesses array will be empty)
3067
+ */
3068
+ declare class TrackedTaskExecutor {
3069
+ #private;
3070
+ constructor(workspaceRoot: string);
3071
+ /**
3072
+ * Returns true if file access tracking is supported on the current platform.
3073
+ * strace tracking (Linux) or preload script (any Node.js process).
3074
+ */
3075
+ get isTrackingSupported(): boolean;
3076
+ /**
3077
+ * Returns true if the platform supports full syscall-level tracking (strace).
3078
+ */
3079
+ get isStraceSupported(): boolean;
3080
+ /**
3081
+ * Executes a task command and tracks all file system accesses.
3082
+ *
3083
+ * On Linux, uses strace for comprehensive tracking.
3084
+ * On other platforms, uses a Node.js preload script (for Node processes).
3085
+ */
3086
+ execute(task: Task, options: TaskExecutionOptions, command: string): Promise<TrackedExecutionResult>;
3087
+ /**
3088
+ * Kills all active child processes. Called on abort/signal to prevent orphans.
3089
+ */
3090
+ killAll(): void;
3091
+ }
3092
+ /**
3093
+ * Hashes a file's content using xxh3-128.
3094
+ * Returns undefined if the file cannot be read.
3095
+ */
3096
+ declare const hashFile: (filePath: string) => Promise<string | undefined>;
3097
+ /**
3098
+ * Hashes one or more string values using xxh3-128.
3099
+ */
3100
+ declare const hashStrings: (...values: string[]) => string;
3101
+ /**
3102
+ * Sorts an object's keys for deterministic serialization.
3103
+ */
3104
+ declare const sortObjectKeys: (object: Record<string, unknown>) => Record<string, unknown>;
3105
+ /**
3106
+ * Recursively collects all file paths in a directory,
3107
+ * skipping directories in the ignored set.
3108
+ *
3109
+ * Tracks visited real paths to prevent infinite loops from symlink cycles.
3110
+ */
3111
+ declare const collectFiles: (directory: string, ignoredDirectories: Set<string>, visitedRealPaths?: Set<string>) => Promise<string[]>;
3112
+ /**
3113
+ * Resolves the working directory for a task.
3114
+ */
3115
+ declare const resolveTaskCwd: (workspaceRoot: string, task: Task) => string;
3116
+ /**
3117
+ * Creates a failure TaskResult from an error.
3118
+ */
3119
+ declare const createFailureResult: (task: Task, error: unknown, startTime: number) => TaskResult;
3120
+ declare const readPackageDeps: (packageJsonPath: string, options?: {
3121
+ optional?: boolean;
3122
+ peer?: boolean;
3123
+ }) => Promise<Set<string> | undefined>;
3124
+ /**
3125
+ * Generates a unique ID for temporary files/directories using
3126
+ * `crypto.randomUUID()`. Used for staging-path names that briefly
3127
+ * coexist on disk during atomic writes — collisions would clobber a
3128
+ * concurrent writer's staging directory, so the extra entropy over
3129
+ * `Date.now() + Math.random()` is worth the cycles. UUID v4 from
3130
+ * Node's crypto module is itself uniformly random, making this
3131
+ * cheaper than the previous string concat.
3132
+ */
3133
+ declare const uniqueId: () => string;
3134
+ /**
3135
+ * Returns the absolute path to the *main* git worktree root when
3136
+ * `workspaceRoot` is a linked worktree, or `undefined` for primary
3137
+ * checkouts and non-git directories.
3138
+ *
3139
+ * Result is memoized for the lifetime of the process — worktree topology
3140
+ * does not change at runtime, so the second call is a hash lookup.
3141
+ *
3142
+ * Detection logic:
3143
+ * 1. If `{workspaceRoot}/.git` is a *directory*, this is a primary checkout
3144
+ * (or vanilla repo). Returns `undefined`.
3145
+ * 2. If `{workspaceRoot}/.git` is a *file* (gitlink), resolves to the parent
3146
+ * of `git rev-parse --git-common-dir` — that is the main worktree root.
3147
+ * 3. On any error (missing git binary, shallow CI checkout, etc.), returns
3148
+ * `undefined` so the caller falls back to the workspace-local cache.
3149
+ * @param workspaceRoot Absolute path to the candidate workspace root.
3150
+ * @returns The main worktree root, or `undefined` if not a linked worktree.
3151
+ */
3152
+ declare const getMainWorktreeRoot: (workspaceRoot: string) => string | undefined;
3153
+ /**
3154
+ * Returns `true` when `{workspaceRoot}/.git` is a regular file (the gitlink
3155
+ * pointer used by `git worktree add`), `false` otherwise. Cheap pre-flight
3156
+ * before invoking `git rev-parse`.
3157
+ * @param workspaceRoot Absolute path to the candidate workspace root.
3158
+ */
3159
+ declare const isLinkedWorktree: (workspaceRoot: string) => boolean;
3160
+ /**
3161
+ * Clears the in-process detection cache. Tests must call this between
3162
+ * scenarios because the cache key is the canonicalized workspace path —
3163
+ * recreating a fixture at the same path would otherwise leak stale results.
3164
+ */
3165
+ declare const resetWorktreeCache: () => void;
3166
+ export { type ActionResult, type AffectedOptions, type AffectedResult, type AffectedScope, type BlobSource, Cache, type CacheMissReason, type CacheMode, type CacheOptions, type CacheRestoreOptions, type CachedResult, type CasDigest, type ChromeTraceEvent, CompositeLifeCycle, type ConcurrencyGroups, type ConcurrentCloseEvent, type ConcurrentCommandConfig, type ConcurrentCommandInput, type ConcurrentRunResult, type ConcurrentRunnerOptions, ConsoleLifeCycle, type ConstraintViolation, type ConstraintsConfig, DEFAULT_CACHE_DIRECTORY_NAME, type DependencyKindRules, type DependencyType, type DetectedFramework, EmptyLifeCycle, type EnvMatcher, type EnvironmentInput, type ExternalDependencyInput, type FileAccess, FileAccessTracker, type FileSetBase, type FileSetInput, type FileSetPattern, type FileSnapshot, FingerprintManager, type GraphFormat, type GraphJson, type GraphVisualizerOptions, HttpRemoteCache, INPUT_URI_SCHEMES, InProcessTaskHasher, IncrementalFileHasher, type IncrementalHasherOptions, type InputDefinition, type InputHandlerOptions, type InputUriScheme, InvalidInputUriError, type LifeCycleInterface, LockfileHasher, type LogMode, LogReporter, type NamedInputs, type NodePlatform, type OutputSpec, type PackageLockfileHash, type ParseCommandsOptions, type PartitionOptions, type ProcessEvent, type ProjectConfiguration, type ProjectGraph, type ProjectGraphDependency, type ProjectGraphProjectNode, ReapiRemoteCache, type ReapiRemoteCacheOptions, type RemoteCacheBackend, type RemoteCacheCompression, type RemoteCacheOptions, type RemoteCacheSigning, type ResolvedDependency, type RestartOptions, type RunSummary, type RuntimeInput, type TagRelationships, type TargetConfiguration, type TargetDependencyConfig, type Task, type TaskExecutionOptions, type TaskExecutor, type TaskFingerprint, type TaskGraph, type TaskHashDetails, type TaskHasher, type TaskHasherOptions, TaskOrchestrator, type TaskOrchestratorOptions, type TaskPriority, type TaskResult, type TaskResults, type TaskRunnerContext, type TaskRunnerOptions, TaskScheduler, type TaskStatus, type TaskSummary, type TaskTarget, type TasksRunner, type TeardownOptions, TerminalBuffer, type TokenContext, type TrackedExecutionResult, TrackedTaskExecutor, type TrackingResult, type TypeBoundaries, V2_AC, V2_CAS, V2_INDEX, V2_ROOT, V2_TMP, type WhenCondition, type WhenContext, type WorkspaceConfiguration, acEntryPath, actionDigestForTaskHash, buildForwardDependencyMap, buildReverseDependencyMap, casBlobPath, collectFiles, computeTaskHash, containsBlob, containsByTaskHash, createFailureResult, createInputHandler, createLogReporter, createRemoteCacheBackend, createTaskGraph, defaultTaskRunner, detectFrameworks, detectScriptShell, digestBuffer, digestFile, enforceProjectConstraints, evaluateWhen, expandAffected, expandArguments, expandShortcut, expandTokens, expandTokensInString, expandWildcard, explainWhen, extractPackageName, fetchBlobToFile, filterAffectedTasks, findCycle, findCycles, formatCacheSize, formatTimingTable, generatePreloadScript, generateRunSummary, getAffectedProjects, getChangedFiles, getCurrentBranch, getDependentTasks, getFrameworkEnvVariables, getLastRunSummaryPath, getLeafTasks, getMainWorktreeRoot, getTaskId, getTransitiveDependencies, hashFile, hashStrings, inferFrameworkEnvPatterns, isLinkedWorktree, isNativeAvailable, loadNativeBindings, logTimings, looksLikeInputUri, makeAcyclic, parseCacheSize, parseCommands, parseInputUri, parseNpmLockfile, parsePartition, parsePnpmLockfile, parseTaskId, parseYarnLockfile, projectGraphToDot, putBlobFromBytes, putBlobFromFile, readLastRunSummary, readPackageDeps, resetBranchCache, resetWorktreeCache, resolveCacheMode, resolveOutputs, resolveTaskCwd, retrieveByTaskHash, reverseTaskGraph, runConcurrentFallback, runConcurrently, runTeardown, sortObjectKeys, storeByTaskHash, stripQuotes, taskHashIndexPath, toChromeTrace, toGraphAscii, toGraphHtml, toGraphJson, toGraphvizDot, touchBlob, uniqueId, verifyBlob, walkTaskGraph, withRestart, writeChromeTrace, writeLastRunSummary, writeRunSummary };