@visulima/task-runner 1.0.0-alpha.2 → 1.0.0-alpha.20

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