@visulima/task-runner 1.0.0-alpha.8 → 1.0.0-alpha.9

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 (40) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +3 -1
  3. package/dist/index.d.ts +791 -205
  4. package/dist/index.js +28 -20
  5. package/dist/packem_chunks/index.js +5593 -0
  6. package/dist/packem_shared/{Cache-CWaX_c8U.js → Cache-CbhoA268.js} +151 -10
  7. package/dist/packem_shared/{FileAccessTracker-CQ5Ot7Hd.js → FileAccessTracker-D8zIURPU.js} +1 -1
  8. package/dist/packem_shared/{FingerprintManager-CV7U4f4f.js → FingerprintManager-78DjwWQ4.js} +1 -1
  9. package/dist/packem_shared/HttpRemoteCache-BTXUBH7t.js +290 -0
  10. package/dist/packem_shared/INPUT_URI_SCHEMES-DRm76YI5.js +69 -0
  11. package/dist/packem_shared/{IncrementalFileHasher-BRS76-mb.js → IncrementalFileHasher-BBhVK491.js} +1 -1
  12. package/dist/packem_shared/ReapiRemoteCache-vgRxDMmu.js +1012 -0
  13. package/dist/packem_shared/{TaskOrchestrator-UCMHCx8c.js → TaskOrchestrator-CdRaQhTO.js} +34 -11
  14. package/dist/packem_shared/{TrackedTaskExecutor-CFPpQfXF.js → TrackedTaskExecutor-CWSMfHAW.js} +2 -2
  15. package/dist/packem_shared/V2_ROOT-DKBLxKo4.js +14 -0
  16. package/dist/packem_shared/actionDigestForTaskHash-BRE-9MT6.js +121 -0
  17. package/dist/packem_shared/archive-CnggHWb-.js +152 -0
  18. package/dist/packem_shared/{collectFiles-ClXHnHhg.js → collectFiles-cc1gokGU.js} +2 -1
  19. package/dist/packem_shared/{computeTaskHash-B5APHW7e.js → computeTaskHash-DHoBJ_-V.js} +10 -4
  20. package/dist/packem_shared/containsBlob-CwGB0a_q.js +125 -0
  21. package/dist/packem_shared/{createTaskGraph-B5YrfAMx.js → createTaskGraph-Bwl4hwAf.js} +17 -0
  22. package/dist/packem_shared/{defaultTaskRunner-DzR0ld8F.js → defaultTaskRunner-BaX4ZbFv.js} +24 -13
  23. package/dist/packem_shared/{detectFrameworks-CeFzKE6J.js → detectFrameworks-D7nyTc-o.js} +1 -1
  24. package/dist/packem_shared/{detectScriptShell-CR-xXKA4.js → detectScriptShell-CzxCM9-t.js} +1 -1
  25. package/dist/packem_shared/digestBuffer-CPdI2E1d.js +48 -0
  26. package/dist/packem_shared/{expandArguments-0AwD2BIA.js → expandArguments-Ba-hHYff.js} +2 -1
  27. package/dist/packem_shared/{expandTokensInString-C03AGAjh.js → expandTokensInString-Bb7nYehP.js} +2 -1
  28. package/dist/packem_shared/{extractPackageName-CbVNW-dr.js → extractPackageName-CMHjqGj_.js} +1 -1
  29. package/dist/packem_shared/{generateRunSummary-BE1jnQ3H.js → generateRunSummary-Bah7CFay.js} +1 -1
  30. package/dist/packem_shared/{getCurrentBranch-DsKPDoVj.js → getCurrentBranch-DVNikt0P.js} +11 -8
  31. package/dist/packem_shared/getMainWorktreeRoot-iBqToQJ4.js +114 -0
  32. package/dist/packem_shared/{parseCommands-CJ16ohOB.js → parseCommands-DDdIxaH5.js} +3 -3
  33. package/dist/packem_shared/resolveCacheMode-CsmHT_0o.js +21 -0
  34. package/dist/packem_shared/{runConcurrently-CmfC4r-f.js → runConcurrently-BCGQ9fJl.js} +1 -1
  35. package/dist/packem_shared/shell-quote-DWJJbt21.js +3 -0
  36. package/dist/packem_shared/{utils-zO0ZRgtf.js → utils-Bmnj-H2J.js} +4 -1
  37. package/index.js +52 -52
  38. package/package.json +23 -10
  39. package/dist/packem_shared/RemoteCache-DSU3lc87.js +0 -219
  40. package/dist/packem_shared/archive-UQHAnZUa.js +0 -102
package/dist/index.d.ts CHANGED
@@ -1,5 +1,353 @@
1
1
  import { Readable, Writable } from 'node:stream';
2
2
  /**
3
+ * Represents a file access recorded during task execution.
4
+ *
5
+ * Write-intent accesses (`"write"`) are emitted when a task opens a file
6
+ * with `O_WRONLY`/`O_RDWR`/`O_CREAT`/`O_TRUNC` flags (strace) or calls
7
+ * `writeFile`/`appendFile`/`unlink`/`rename` (preload script).
8
+ * The orchestrator uses the overlap of reads and writes to the same
9
+ * workspace path to detect self-modifying tasks and skip caching.
10
+ */
11
+ interface FileAccess {
12
+ /** The absolute path of the file */
13
+ path: string;
14
+ /** The type of access */
15
+ type: "missing" | "read" | "readdir" | "stat" | "write";
16
+ }
17
+ /**
18
+ * Result of tracking file accesses during a command execution.
19
+ */
20
+ interface TrackingResult {
21
+ /** All file accesses recorded */
22
+ accesses: FileAccess[];
23
+ /** The command exit code */
24
+ code: number;
25
+ /** The command stdout + stderr output */
26
+ output: string;
27
+ }
28
+ /**
29
+ * Tracks which files a child process accesses during execution.
30
+ *
31
+ * Uses `strace` on Linux to intercept syscalls (open, openat, stat, lstat, access, getdents).
32
+ * Falls back to no tracking on unsupported platforms.
33
+ */
34
+ declare class FileAccessTracker {
35
+ #private;
36
+ constructor(workspaceRoot: string, excludePatterns?: RegExp[]);
37
+ /**
38
+ * Returns true if file access tracking is supported on the current platform.
39
+ */
40
+ isSupported(): boolean;
41
+ /**
42
+ * Runs a command and tracks all file system accesses.
43
+ * On unsupported platforms, runs the command without tracking.
44
+ */
45
+ track(command: string, options?: {
46
+ cwd?: string;
47
+ env?: Record<string, string | undefined>;
48
+ }): Promise<TrackingResult>;
49
+ /**
50
+ * Kills all active child processes. Called on abort/signal to prevent orphans.
51
+ */
52
+ killAll(): void;
53
+ }
54
+ /**
55
+ * Generates a preload script that can be used with NODE_OPTIONS to
56
+ * track file accesses in Node.js child processes.
57
+ *
58
+ * This is an alternative to strace that works cross-platform for Node.js processes.
59
+ */
60
+ declare const generatePreloadScript: (outputPath: string) => string;
61
+ /**
62
+ * Represents a stored fingerprint for a task execution.
63
+ */
64
+ interface TaskFingerprint {
65
+ /** Hash of the command arguments */
66
+ commandHash: string;
67
+ /** Directory listings recorded during execution (path -> sorted entries) */
68
+ directoryListings: Record<string, string[]>;
69
+ /** Hashes of fingerprinted environment variables */
70
+ envHashes: Record<string, string>;
71
+ /** Content hashes of files that were read during execution */
72
+ fileHashes: Record<string, string>;
73
+ /** Paths of files that were probed but didn't exist (ENOENT) */
74
+ missingFiles: string[];
75
+ /**
76
+ * Workspace-relative paths that were both read **and** written
77
+ * during execution. Populated when the tracker emits
78
+ * {@link FileAccess} entries with `"write"` type. The orchestrator
79
+ * uses a non-empty value here to skip caching a self-modifying
80
+ * task, whose fingerprint would otherwise capture post-write state
81
+ * and trigger false cache hits.
82
+ */
83
+ modifiedInputs?: string[];
84
+ }
85
+ /**
86
+ * Describes why a cache miss occurred.
87
+ */
88
+ interface CacheMissReason {
89
+ currentHash?: string;
90
+ detail: string;
91
+ previousHash?: string;
92
+ type: "file-changed" | "file-created" | "file-deleted" | "directory-changed" | "env-changed" | "args-changed" | "no-fingerprint";
93
+ }
94
+ /**
95
+ * Manages task fingerprints for auto-detection caching.
96
+ *
97
+ * Records which files a task accesses during execution, stores content
98
+ * hashes, and on subsequent runs checks if any accessed file has changed.
99
+ */
100
+ declare class FingerprintManager {
101
+ #private;
102
+ constructor(workspaceRoot: string);
103
+ createFingerprint(accesses: FileAccess[], command: string, args: Record<string, unknown>, envVariables: Record<string, string | undefined>, envPatterns?: string[], untrackedEnvVariables?: string[]): Promise<TaskFingerprint>;
104
+ /**
105
+ * Validates a stored fingerprint against the current state.
106
+ * Returns null if valid (cache hit), or an array of reasons (cache miss).
107
+ *
108
+ * Does NOT use the file hash cache — validation must see current disk state.
109
+ */
110
+ validate(fingerprint: TaskFingerprint): Promise<CacheMissReason[] | undefined>;
111
+ validateCommand(fingerprint: TaskFingerprint, command: string, args: Record<string, unknown>): CacheMissReason | undefined;
112
+ formatMissReasons(reasons: CacheMissReason[]): string;
113
+ }
114
+ /**
115
+ * Content-Addressable Storage digest. Mirrors REAPI's `Digest` message:
116
+ * a content hash and the size in bytes of the addressed blob.
117
+ *
118
+ * The hash is the lowercase hex sha256 of the blob's raw bytes. Size is
119
+ * the byte length of those bytes. Together they uniquely identify a CAS
120
+ * entry across HTTP and gRPC (REAPI) backends.
121
+ */
122
+ interface CasDigest {
123
+ /** Lowercase hex sha256 of the blob bytes. */
124
+ hash: string;
125
+ /** Size of the blob bytes, in bytes. */
126
+ sizeBytes: number;
127
+ }
128
+ /**
129
+ * Result of executing a single Action. Mirrors REAPI's `ActionResult`
130
+ * shape, with a vis-specific `fingerprint` extension for auto-fingerprint
131
+ * mode. Persisted as JSON locally; serialized as a protobuf over gRPC.
132
+ *
133
+ * Output paths are workspace-relative.
134
+ */
135
+ interface ActionResult {
136
+ /** Process exit code from the cached run. */
137
+ exitCode: number;
138
+ /** Optional vis-specific fingerprint (auto-fingerprint mode). */
139
+ fingerprint?: TaskFingerprint;
140
+ /**
141
+ * Per-output-directory entries. The `treeDigest` points to a CAS
142
+ * blob that is itself a serialized REAPI `Tree` message describing
143
+ * the directory hierarchy and its file digests.
144
+ */
145
+ outputDirectories: ReadonlyArray<{
146
+ path: string;
147
+ treeDigest: CasDigest;
148
+ }>;
149
+ /** Per-output-file entries. Workspace-relative paths. */
150
+ outputFiles: ReadonlyArray<{
151
+ digest: CasDigest;
152
+ isExecutable: boolean;
153
+ path: string;
154
+ }>;
155
+ /**
156
+ * CAS digest of the captured stdout/stderr stream. Optional because
157
+ * tasks with no output emit nothing. Most tasks emit at least
158
+ * "Done in Xs", so dedup across tasks is real.
159
+ */
160
+ stdoutDigest?: CasDigest;
161
+ }
162
+ /**
163
+ * Lazy handle for a CAS blob. Backends call `open()` only when they're
164
+ * ready to stream the bytes — keeps memory flat for multi-hundred-MB
165
+ * artifacts and avoids buffering blobs the server reports as already
166
+ * present (REAPI `FindMissingBlobs`).
167
+ */
168
+ interface BlobSource {
169
+ digest: CasDigest;
170
+ open: () => Promise<NodeJS.ReadableStream>;
171
+ }
172
+ /**
173
+ * Canonical cache-mode enum, replacing the original `read` / `write`
174
+ * boolean pair. One flag, three values, no implicit env-detection.
175
+ *
176
+ * - `"read"`: Pull cache hits, never push. Default for local dev when
177
+ * developers shouldn't poison the shared cache.
178
+ * - `"write"`: Push results, never read. Useful for refill / warm-up
179
+ * jobs that should always re-execute and re-upload.
180
+ * - `"readwrite"`: Both. Default for CI.
181
+ */
182
+ type CacheMode = "read" | "readwrite" | "write";
183
+ /**
184
+ * Compression algorithm used for artifact tarballs on the wire.
185
+ *
186
+ * - `"gzip"` (default): tar+gzip, matches Turborepo's protocol format
187
+ * and stays interop-safe with existing remote cache servers.
188
+ * - `"brotli"`: tar + Node brotli (BROTLI_MODE_TEXT, quality 4) — a
189
+ * solid ratio/speed trade-off for source-tree tarballs. Both upload
190
+ * and download sides must agree; switching invalidates existing
191
+ * remote entries (they will simply re-populate on next run).
192
+ *
193
+ * HTTP-only — REAPI servers negotiate compression via the
194
+ * `Capabilities` RPC + `grpc-encoding` metadata on the wire.
195
+ */
196
+ type RemoteCacheCompression = "brotli" | "gzip";
197
+ /**
198
+ * HMAC signing configuration for the HTTP backend.
199
+ *
200
+ * When set, every upload carries an `X-Artifact-Signature` header
201
+ * containing the HMAC-SHA256 digest of `hash | body`. On download,
202
+ * the client recomputes the HMAC and rejects any artifact whose
203
+ * signature doesn't match (constant-time comparison). REAPI servers
204
+ * do not consume this — REAPI integrity rides on sha256
205
+ * content-addressing instead.
206
+ */
207
+ interface RemoteCacheSigning {
208
+ /** Shared secret. Must be at least 16 characters. */
209
+ secret: string;
210
+ /**
211
+ * Reject downloads whose signature doesn't match or is missing.
212
+ * Set to `true` once every upload on your server is signed.
213
+ * @default false
214
+ */
215
+ verifyOnDownload?: boolean;
216
+ }
217
+ /**
218
+ * Canonical remote-cache configuration consumed by
219
+ * `createRemoteCacheBackend`. Both `HttpRemoteCache` and
220
+ * `ReapiRemoteCache` accept this shape directly — backend-specific
221
+ * fields (e.g. `signing`, `compression` for HTTP; `bearerToken`,
222
+ * `instanceName` for REAPI) are read by the corresponding constructor
223
+ * and ignored by the other.
224
+ *
225
+ * Living in `backends/types.ts` rather than each backend file keeps a
226
+ * single source of truth for `TaskRunnerOptions.remoteCache` and the
227
+ * `vis-config` typed surface.
228
+ */
229
+ interface RemoteCacheOptions {
230
+ /**
231
+ * Opt out of the REAPI safety check that refuses to send a bearer
232
+ * token over cleartext gRPC. Required only when the connection
233
+ * terminates inside a trusted boundary (loopback dev cache, mesh
234
+ * mTLS sidecar that strips/re-encrypts on the next hop). Default
235
+ * `false` — production callers should reach for `grpcs://` first.
236
+ *
237
+ * REAPI-only.
238
+ */
239
+ allowInsecureBearer?: boolean;
240
+ /**
241
+ * Wire-protocol selector. `"http"` is the Turborepo-compatible
242
+ * single-tarball cache; `"reapi"` switches to the Bazel Remote
243
+ * Execution API gRPC client, unlocking `bazel-remote`,
244
+ * BuildBuddy, BuildBarn, EngFlow as drop-in backends.
245
+ * @default "http"
246
+ */
247
+ backend?: "http" | "reapi";
248
+ /**
249
+ * Bearer token sent in REAPI's `authorization: Bearer {token}`
250
+ * gRPC metadata header. REAPI-only — HTTP backend uses `token`.
251
+ */
252
+ bearerToken?: string;
253
+ /** HTTP-only: tarball compression on the wire. @default "gzip" */
254
+ compression?: RemoteCacheCompression;
255
+ /**
256
+ * REAPI-only `instance_name` for multi-tenant servers (a single
257
+ * gRPC endpoint can host multiple logical caches keyed on the
258
+ * `instance_name` prefix).
259
+ */
260
+ instanceName?: string;
261
+ /**
262
+ * Local CAS root used by the per-blob {@link RemoteCacheBackend}
263
+ * methods. The HTTP wire format is still single-tarball; on
264
+ * retrieve the bridge extracts blobs into this root so a follow-up
265
+ * `fetchBlob` is just a local read.
266
+ */
267
+ localCasRoot?: string;
268
+ /**
269
+ * Canonical cache mode flag.
270
+ *
271
+ * - `"read"`: Pull cache hits, never push. Sensible default for
272
+ * local dev so a developer doesn't poison the shared cache while
273
+ * their workspace is dirty.
274
+ * - `"write"`: Push results, never read. Useful for refill /
275
+ * warm-up jobs that should always re-execute and re-upload.
276
+ * - `"readwrite"`: Both. Default for CI.
277
+ * @default "readwrite"
278
+ */
279
+ mode?: CacheMode;
280
+ /**
281
+ * Called when a fire-and-forget upload fails. Uploads are
282
+ * non-blocking, so without this hook errors are silently dropped.
283
+ * Provide it to log or report upload failures.
284
+ */
285
+ onUploadError?: (hash: string, error: unknown) => void;
286
+ /** HTTP-only: HMAC-SHA256 signing for upload integrity. */
287
+ signing?: RemoteCacheSigning;
288
+ /** Team / namespace for cache isolation. */
289
+ teamId?: string;
290
+ /** Per-call request timeout in milliseconds. @default 30000 */
291
+ timeout?: number;
292
+ /**
293
+ * HTTP authentication token sent as `Authorization: Bearer …`.
294
+ * For REAPI, use `bearerToken` instead — same wire mechanic, but
295
+ * a separate field so REAPI's cleartext-bearer guard can distinguish
296
+ * "user explicitly opted into a gRPC token" from "user set HTTP
297
+ * auth and accidentally selected the REAPI backend".
298
+ */
299
+ token?: string;
300
+ /**
301
+ * Cache server URL.
302
+ *
303
+ * - HTTP: `https://cache.example.com`
304
+ * - REAPI: `grpcs://host:port` (TLS, recommended) or `grpc://host:port`
305
+ * (cleartext — bearer tokens are refused unless `allowInsecureBearer`).
306
+ */
307
+ url: string;
308
+ }
309
+ /**
310
+ * Pluggable backend boundary for the remote cache. Implementations:
311
+ * - `HttpRemoteCache`: Turborepo wire-protocol-compatible HTTP cache.
312
+ * Bridges the single-tarball protocol to per-blob semantics by
313
+ * synthesizing an `ActionResult` from the extracted tarball contents.
314
+ * - `ReapiRemoteCache`: Bazel Remote Execution API gRPC client.
315
+ * Speaks `ActionCache` + `ContentAddressableStorage` services
316
+ * natively, unlocking bazel-remote / BuildBuddy / BuildBarn / EngFlow.
317
+ */
318
+ interface RemoteCacheBackend {
319
+ /**
320
+ * Release any persistent resources held by the backend (gRPC
321
+ * channels, HTTP keep-alive agents). Safe to call multiple times
322
+ * and safe to call when no work has been issued. Implementations
323
+ * must not throw — close failures are best-effort.
324
+ */
325
+ close: () => Promise<void>;
326
+ /**
327
+ * HEAD-equivalent existence check. REAPI: `GetActionResult` with
328
+ * `inline_*` fields disabled. HTTP: `HEAD /v8/artifacts/{hash}`.
329
+ */
330
+ containsAction: (actionDigest: CasDigest) => Promise<boolean>;
331
+ /**
332
+ * Stream a single CAS blob to disk. Returns `true` on success.
333
+ * Used to materialize output files referenced by an `ActionResult`.
334
+ */
335
+ fetchBlob: (digest: CasDigest, destinationPath: string) => Promise<boolean>;
336
+ /**
337
+ * Look up an Action's cached result. Resolves to `null` on miss.
338
+ * Implementations are responsible for fetching any CAS blobs the
339
+ * caller needs to materialize the outputs (or returning enough
340
+ * metadata for the caller to fetch them via `fetchBlob`).
341
+ */
342
+ retrieveAction: (actionDigest: CasDigest) => Promise<ActionResult | null>;
343
+ /**
344
+ * Persist an `ActionResult` and any blobs it references. Backends
345
+ * must enforce blobs-before-AC ordering on the wire so a partial
346
+ * failure cannot leave a cached result pointing at missing bytes.
347
+ */
348
+ storeAction: (actionDigest: CasDigest, result: ActionResult, blobs: ReadonlyArray<BlobSource>) => Promise<boolean>;
349
+ }
350
+ /**
3
351
  * A predicate clause that can match a single value, a list of values
4
352
  * (any-of), or — for env vars only — a `{ name, equals?, exists? }`
5
353
  * triplet. `not.*` mirrors of every clause provide a negative form
@@ -145,6 +493,22 @@ interface Task {
145
493
  always?: boolean;
146
494
  /** Whether this task is eligible for caching */
147
495
  cache?: boolean;
496
+ /**
497
+ * When `false`, exit-0 runs whose output matched any
498
+ * {@link Task.warningPattern} are NOT written to the cache. Defaults
499
+ * to `true` — warnings are recorded on the result but don't suppress
500
+ * caching, matching the rushstack#1402 / Theme P expectation that a
501
+ * succeeded-with-warnings build is still incremental on the next run.
502
+ *
503
+ * Carried over from {@link TargetConfiguration.cacheOnWarning}.
504
+ */
505
+ cacheOnWarning?: boolean;
506
+ /**
507
+ * Per-task overrides for cache restore fidelity. Carried over
508
+ * from {@link TargetConfiguration.cacheRestore}. When absent, the
509
+ * runner uses faithful defaults (mtime + mode preserved).
510
+ */
511
+ cacheRestore?: CacheRestoreOptions;
148
512
  /** Hash of the task inputs for caching */
149
513
  hash?: string;
150
514
  /** Detailed hash information */
@@ -172,6 +536,16 @@ interface Task {
172
536
  /** The target this task executes */
173
537
  target: TaskTarget;
174
538
  /**
539
+ * Regex strings (JavaScript flavor, anchored as the user writes them)
540
+ * that classify a successful task's terminal output as "succeeded
541
+ * with warnings". When any pattern matches, {@link TaskResult.hadWarnings}
542
+ * is set on the result. Combined with {@link Task.cacheOnWarning} this
543
+ * controls whether the run still seeds the cache.
544
+ *
545
+ * Carried over from {@link TargetConfiguration.warningPattern}.
546
+ */
547
+ warningPattern?: string[];
548
+ /**
175
549
  * Predicate that gates execution. Evaluated by the orchestrator
176
550
  * just before the task is launched; tasks whose `when` returns
177
551
  * `false` are marked `"skipped"` without ever invoking the
@@ -224,6 +598,14 @@ interface TaskResult {
224
598
  /** The end time of the task */
225
599
  endTime?: number;
226
600
  /**
601
+ * Set when the task exited 0 and at least one configured
602
+ * {@link Task.warningPattern} matched the terminal output. Surfaced
603
+ * to lifecycle reporters and the run summary so users can see that
604
+ * a "green" build still emitted warnings, and gates the optional
605
+ * `cacheOnWarning: false` cache-suppression path.
606
+ */
607
+ hadWarnings?: boolean;
608
+ /**
227
609
  * Set when the task modified one or more of its own tracked input
228
610
  * files during execution. Caching is skipped in this case — the
229
611
  * fingerprint captured before the run would mismatch the post-run
@@ -268,6 +650,30 @@ interface ProjectConfiguration {
268
650
  targets?: Record<string, TargetConfiguration>;
269
651
  }
270
652
  /**
653
+ * Per-target controls over how the cache rehydrates outputs.
654
+ *
655
+ * Both default to `true` — faithful restoration is the contract:
656
+ * cached outputs come back with the same mtime and mode bits the
657
+ * task originally produced. Override only when downstream tooling
658
+ * needs the opposite (e.g. a bundler that uses "newer than source"
659
+ * heuristics, or a CI step that compares mtimes against a deploy
660
+ * artifact).
661
+ */
662
+ interface CacheRestoreOptions {
663
+ /**
664
+ * Restore each file's modification time from the captured tar
665
+ * header (second precision). When `false`, restored files take
666
+ * the current wall-clock time, matching the legacy behaviour.
667
+ */
668
+ preserveMtime?: boolean;
669
+ /**
670
+ * Restore each file's POSIX mode bits (rwx triplets, low 12
671
+ * bits). When `false`, restored files take the process umask.
672
+ * Only meaningful on POSIX hosts; Windows ignores tar mode.
673
+ */
674
+ preservePerms?: boolean;
675
+ }
676
+ /**
271
677
  * Configuration for a target within a project.
272
678
  */
273
679
  interface TargetConfiguration {
@@ -281,6 +687,22 @@ interface TargetConfiguration {
281
687
  always?: boolean;
282
688
  /** Whether this target is cacheable */
283
689
  cache?: boolean;
690
+ /**
691
+ * When `false`, exit-0 runs whose terminal output matched any
692
+ * {@link TargetConfiguration.warningPattern} are not seeded into the
693
+ * cache. Defaults to `true` — warnings are surfaced on the result
694
+ * (`hadWarnings: true`) but caching still happens, matching the
695
+ * "succeeded with warnings is incremental" behaviour rush, lage and
696
+ * wireit users have repeatedly asked for.
697
+ */
698
+ cacheOnWarning?: boolean;
699
+ /**
700
+ * Fine-grained controls over how cached outputs are rehydrated.
701
+ * See {@link CacheRestoreOptions}. Both fields default to `true`
702
+ * (faithful restore); flip individually when downstream tooling
703
+ * needs the legacy "now"-stamped behaviour.
704
+ */
705
+ cacheRestore?: CacheRestoreOptions;
284
706
  /** The command to run (alternative to executor) */
285
707
  command?: string;
286
708
  /** Named configurations (e.g., "production", "development") */
@@ -298,6 +720,19 @@ interface TargetConfiguration {
298
720
  /** Whether this target supports parallel execution */
299
721
  parallelism?: boolean;
300
722
  /**
723
+ * Regex source string(s) that mark a successful task as having
724
+ * emitted warnings. The orchestrator scans the task's combined
725
+ * terminal output after a 0-exit and, on first match, sets
726
+ * `hadWarnings` on the result. Combine with `cacheOnWarning: false`
727
+ * to skip caching for warning-tainted runs. Both bare strings and
728
+ * arrays are accepted; arrays are tested in order.
729
+ *
730
+ * ```ts
731
+ * warningPattern: ["\\bwarning\\b", "TS\\d{4}"]
732
+ * ```
733
+ */
734
+ warningPattern?: string | string[];
735
+ /**
301
736
  * Predicate that gates execution. When the condition evaluates
302
737
  * to `false` for the current environment, the task is skipped
303
738
  * (marked `"skipped"`, not failed). Combine with `os`, `env`,
@@ -617,27 +1052,11 @@ interface TaskRunnerOptions {
617
1052
  parallel?: number | boolean;
618
1053
  /**
619
1054
  * Remote cache configuration.
620
- * When configured, the task runner will check the remote cache
621
- * after a local cache miss, and upload results after execution.
622
- */
623
- remoteCache?: {
624
- /**
625
- * Called when a fire-and-forget upload fails.
626
- * Since uploads are non-blocking, errors are silently swallowed by default.
627
- * Provide this callback to log or report upload failures.
628
- */
629
- onUploadError?: (hash: string, error: unknown) => void;
630
- /** Enable remote reads (default: true) */
631
- read?: boolean;
632
- /** Team/namespace for cache isolation */
633
- teamId?: string;
634
- /** Authentication token */
635
- token?: string;
636
- /** Remote cache server URL */
637
- url: string;
638
- /** Enable remote writes (default: true) */
639
- write?: boolean;
640
- };
1055
+ * When configured, the task runner checks the remote cache after a
1056
+ * local miss and uploads results after execution. See
1057
+ * {@link RemoteCacheOptions} for the full HTTP and REAPI surface.
1058
+ */
1059
+ remoteCache?: RemoteCacheOptions;
641
1060
  /** Whether to skip cache reads */
642
1061
  skipNxCache?: boolean;
643
1062
  /**
@@ -974,116 +1393,174 @@ declare const getAffectedProjects: (options: AffectedOptions) => Promise<Affecte
974
1393
  */
975
1394
  declare const filterAffectedTasks: (taskIds: string[], affectedProjects: Set<string>) => string[];
976
1395
  /**
977
- * Represents a file access recorded during task execution.
1396
+ * Resolves the canonical {@link CacheMode}. `mode` defaults to
1397
+ * `"readwrite"` when unset — the safe choice for CI and the most
1398
+ * common config in dev. Kept as a separate helper so vis-side code
1399
+ * (CLI flag merge, doctor probes) can reuse the resolution rule.
1400
+ */
1401
+ declare const resolveCacheMode: (options: {
1402
+ mode?: CacheMode;
1403
+ }) => CacheMode;
1404
+ /**
1405
+ * Construct the configured remote cache backend. Selects between the
1406
+ * Turborepo-compatible HTTP client and the Bazel REAPI gRPC client
1407
+ * via `options.backend`.
1408
+ */
1409
+ declare const createRemoteCacheBackend: (options: RemoteCacheOptions) => RemoteCacheBackend;
1410
+ /**
1411
+ * Derive a stable {@link CasDigest} from the orchestrator's task hash
1412
+ * (xxh3-128, 32 hex chars). REAPI servers reject non-sha256 digests on
1413
+ * the wire, so we sha256 a namespaced key. The HTTP backend uses the
1414
+ * resulting digest as the artifact URL component.
978
1415
  *
979
- * Write-intent accesses (`"write"`) are emitted when a task opens a file
980
- * with `O_WRONLY`/`O_RDWR`/`O_CREAT`/`O_TRUNC` flags (strace) or calls
981
- * `writeFile`/`appendFile`/`unlink`/`rename` (preload script).
982
- * The orchestrator uses the overlap of reads and writes to the same
983
- * workspace path to detect self-modifying tasks and skip caching.
1416
+ * `sizeBytes` is set to `0` because this digest identifies an *action*
1417
+ * (an `ActionResult` keyed in the AC), not a stored CAS blob. REAPI
1418
+ * `Digest` size_bytes only carries meaning for blobs in the CAS — for
1419
+ * action keys it's ignored by the server. Setting it to the byte
1420
+ * length of the prehash key would be semantically wrong: the server
1421
+ * would believe a blob of that exact length exists at this hash, and
1422
+ * a downstream `BatchReadBlobs` against this digest would mismatch.
984
1423
  */
985
- interface FileAccess {
986
- /** The absolute path of the file */
987
- path: string;
988
- /** The type of access */
989
- type: "missing" | "read" | "readdir" | "stat" | "write";
990
- }
1424
+ declare const actionDigestForTaskHash: (taskHash: string) => CasDigest;
991
1425
  /**
992
- * Result of tracking file accesses during a command execution.
1426
+ * Probe whether a cached entry exists on the remote backend, keyed by
1427
+ * the orchestrator's task hash. Resolves `false` on any wire failure —
1428
+ * the orchestrator treats existence checks as best-effort.
993
1429
  */
994
- interface TrackingResult {
995
- /** All file accesses recorded */
996
- accesses: FileAccess[];
997
- /** The command exit code */
998
- code: number;
999
- /** The command stdout + stderr output */
1000
- output: string;
1001
- }
1430
+ declare const containsByTaskHash: (backend: RemoteCacheBackend, taskHash: string) => Promise<boolean>;
1002
1431
  /**
1003
- * Tracks which files a child process accesses during execution.
1432
+ * Download the cached entry for `taskHash` and extract it into
1433
+ * `{localCacheDirectory}/{taskHash}/`. Returns `true` only when the
1434
+ * directory has been fully populated; partial-extract failures clean
1435
+ * up after themselves so the local cache never observes a half-populated
1436
+ * entry.
1437
+ */
1438
+ declare const retrieveByTaskHash: (backend: RemoteCacheBackend, taskHash: string, localCacheDirectory: string) => Promise<boolean>;
1439
+ /**
1440
+ * Tar `{localCacheDirectory}/{taskHash}/` and upload as a single CAS
1441
+ * blob via {@link RemoteCacheBackend.storeAction}. Skips the upload
1442
+ * unless `.commit` is present so we never publish a half-written entry.
1004
1443
  *
1005
- * Uses `strace` on Linux to intercept syscalls (open, openat, stat, lstat, access, getdents).
1006
- * Falls back to no tracking on unsupported platforms.
1444
+ * The lazy {@link BlobSource.open} returns a fresh `createReadStream`
1445
+ * each call so REAPI's `FindMissingBlobs` `BatchUpdateBlobs`
1446
+ * fallback `Write` flow can re-read the file as many times as it needs
1447
+ * to without requiring the bridge to buffer the bytes in memory.
1448
+ *
1449
+ * Bridge-local failures (`createTarGz`, `digestFile`) are surfaced
1450
+ * through `onUploadError` when provided, then swallowed so the
1451
+ * fire-and-forget call site stays non-throwing. A missing `.commit`
1452
+ * marker is *not* an error — it's the normal "skip this upload" path.
1007
1453
  */
1008
- declare class FileAccessTracker {
1454
+ declare const storeByTaskHash: (backend: RemoteCacheBackend, taskHash: string, localCacheDirectory: string, onUploadError?: (hash: string, error: unknown) => void) => Promise<boolean>;
1455
+ /**
1456
+ * HTTP-based remote cache compatible with the Turborepo remote cache protocol.
1457
+ *
1458
+ * Protocol:
1459
+ * - GET /v8/artifacts/{hash}?teamId={team} -> retrieve cached artifact (gzipped tar)
1460
+ * - PUT /v8/artifacts/{hash}?teamId={team} -> store artifact
1461
+ * - POST /v8/artifacts/events -> analytics (optional)
1462
+ *
1463
+ * Authentication via `Authorization: Bearer {token}` header.
1464
+ *
1465
+ * The cache entry is a gzipped tarball containing the cache directory contents
1466
+ * (code, terminalOutput, fingerprint.json, outputs/, .commit).
1467
+ */
1468
+ declare class HttpRemoteCache implements RemoteCacheBackend {
1009
1469
  #private;
1010
- constructor(workspaceRoot: string, excludePatterns?: RegExp[]);
1470
+ constructor(options: RemoteCacheOptions);
1011
1471
  /**
1012
- * Returns true if file access tracking is supported on the current platform.
1472
+ * No-op. The HTTP backend uses Node's global `fetch`, which has no
1473
+ * persistent connection to release. Implemented to satisfy the
1474
+ * {@link RemoteCacheBackend.close} contract uniformly.
1013
1475
  */
1014
- isSupported(): boolean;
1476
+ close(): Promise<void>;
1015
1477
  /**
1016
- * Runs a command and tracks all file system accesses.
1017
- * On unsupported platforms, runs the command without tracking.
1478
+ * {@link RemoteCacheBackend.containsAction}: HEAD on the artifact URL.
1479
+ * Resolves `false` on any wire failure existence checks are best
1480
+ * effort and never block the caller.
1018
1481
  */
1019
- track(command: string, options?: {
1020
- cwd?: string;
1021
- env?: Record<string, string | undefined>;
1022
- }): Promise<TrackingResult>;
1482
+ containsAction(actionDigest: CasDigest): Promise<boolean>;
1023
1483
  /**
1024
- * Kills all active child processes. Called on abort/signal to prevent orphans.
1484
+ * {@link RemoteCacheBackend.fetchBlob}: streams a blob out of the
1485
+ * local CAS that was hydrated by a previous {@link retrieveAction}
1486
+ * call. The HTTP wire ships one tarball per action, so per-blob
1487
+ * fetches are local-only — there's no remote endpoint to call.
1025
1488
  */
1026
- killAll(): void;
1027
- }
1028
- /**
1029
- * Generates a preload script that can be used with NODE_OPTIONS to
1030
- * track file accesses in Node.js child processes.
1031
- *
1032
- * This is an alternative to strace that works cross-platform for Node.js processes.
1033
- */
1034
- declare const generatePreloadScript: (outputPath: string) => string;
1035
- /**
1036
- * Represents a stored fingerprint for a task execution.
1037
- */
1038
- interface TaskFingerprint {
1039
- /** Hash of the command arguments */
1040
- commandHash: string;
1041
- /** Directory listings recorded during execution (path -> sorted entries) */
1042
- directoryListings: Record<string, string[]>;
1043
- /** Hashes of fingerprinted environment variables */
1044
- envHashes: Record<string, string>;
1045
- /** Content hashes of files that were read during execution */
1046
- fileHashes: Record<string, string>;
1047
- /** Paths of files that were probed but didn't exist (ENOENT) */
1048
- missingFiles: string[];
1489
+ fetchBlob(digest: CasDigest, destinationPath: string): Promise<boolean>;
1049
1490
  /**
1050
- * Workspace-relative paths that were both read **and** written
1051
- * during execution. Populated when the tracker emits
1052
- * {@link FileAccess} entries with `"write"` type. The orchestrator
1053
- * uses a non-empty value here to skip caching a self-modifying
1054
- * task, whose fingerprint would otherwise capture post-write state
1055
- * and trigger false cache hits.
1491
+ * {@link RemoteCacheBackend.retrieveAction}: GETs the artifact at
1492
+ * `/v8/artifacts/{actionDigest.hash}`, ingests the response bytes
1493
+ * as a single CAS blob in the local store, and synthesises an
1494
+ * {@link ActionResult} that points at that blob. Resolves to `null`
1495
+ * on a 404 / signature mismatch / missing local CAS root.
1056
1496
  */
1057
- modifiedInputs?: string[];
1497
+ retrieveAction(actionDigest: CasDigest): Promise<ActionResult | null>;
1498
+ /**
1499
+ * {@link RemoteCacheBackend.storeAction}: takes the single blob
1500
+ * referenced by `result.outputFiles[0]`, streams its bytes as the
1501
+ * PUT body, and signs the body when a signing secret is configured.
1502
+ * Per-digest in-flight dedup means parallel writers racing on the
1503
+ * same action upload exactly once.
1504
+ */
1505
+ storeAction(actionDigest: CasDigest, result: ActionResult, blobs: ReadonlyArray<BlobSource>): Promise<boolean>;
1058
1506
  }
1059
1507
  /**
1060
- * Describes why a cache miss occurred.
1508
+ * REAPI-specific options accepted by {@link ReapiRemoteCache}. A type
1509
+ * alias rather than its own interface so callers can hand the same
1510
+ * object to the backend factory or to the constructor directly without
1511
+ * rewriting field names. HTTP-only fields (`signing`, `compression`)
1512
+ * are silently ignored here.
1061
1513
  */
1062
- interface CacheMissReason {
1063
- currentHash?: string;
1064
- detail: string;
1065
- previousHash?: string;
1066
- type: "file-changed" | "file-created" | "file-deleted" | "directory-changed" | "env-changed" | "args-changed" | "no-fingerprint";
1067
- }
1514
+ type ReapiRemoteCacheOptions = RemoteCacheOptions;
1068
1515
  /**
1069
- * Manages task fingerprints for auto-detection caching.
1516
+ * Bazel REAPI gRPC backend. Implements {@link RemoteCacheBackend} over
1517
+ * `ContentAddressableStorage` + `ActionCache` + `Capabilities` +
1518
+ * `google.bytestream.ByteStream`. Battle-tested REAPI servers
1519
+ * (`bazel-remote`, BuildBuddy, BuildBarn, EngFlow) become drop-in
1520
+ * backends with no per-server adapter.
1070
1521
  *
1071
- * Records which files a task accesses during execution, stores content
1072
- * hashes, and on subsequent runs checks if any accessed file has changed.
1522
+ * Wire flow:
1523
+ * - retrieveAction GetActionResult fetchBlob (BatchReadBlobs|Read)
1524
+ * - storeAction → FindMissingBlobs → BatchUpdateBlobs|Write → UpdateActionResult
1073
1525
  */
1074
- declare class FingerprintManager {
1526
+ declare class ReapiRemoteCache implements RemoteCacheBackend {
1075
1527
  #private;
1076
- constructor(workspaceRoot: string);
1077
- createFingerprint(accesses: FileAccess[], command: string, args: Record<string, unknown>, envVariables: Record<string, string | undefined>, envPatterns?: string[], untrackedEnvVariables?: string[]): Promise<TaskFingerprint>;
1528
+ constructor(options: ReapiRemoteCacheOptions);
1078
1529
  /**
1079
- * Validates a stored fingerprint against the current state.
1080
- * Returns null if valid (cache hit), or an array of reasons (cache miss).
1530
+ * Close all gRPC channels held by this backend. Idempotent — safe
1531
+ * to call multiple times, and safe to call before any RPC was
1532
+ * issued. If `#getClients` is currently in flight we await its
1533
+ * resolution so the underlying channels are observable to close.
1534
+ */
1535
+ close(): Promise<void>;
1536
+ /**
1537
+ * Diagnostic probe — fetches the server's `Capabilities` RPC response
1538
+ * (or the cached value, if a previous call already negotiated). Used
1539
+ * by `vis cache doctor` to surface what the server advertises without
1540
+ * forcing the operator to issue a real CAS RPC.
1081
1541
  *
1082
- * Does NOT use the file hash cache validation must see current disk state.
1542
+ * Bypasses the read/write mode gate intentionally: a probe must work
1543
+ * even on a cache configured `mode: "write"` so the operator can
1544
+ * verify the connection regardless of how the runner uses it.
1083
1545
  */
1084
- validate(fingerprint: TaskFingerprint): Promise<CacheMissReason[] | undefined>;
1085
- validateCommand(fingerprint: TaskFingerprint, command: string, args: Record<string, unknown>): CacheMissReason | undefined;
1086
- formatMissReasons(reasons: CacheMissReason[]): string;
1546
+ probeCapabilities(): Promise<{
1547
+ digestFunctions: ReadonlyArray<string>;
1548
+ maxBatchTotalSizeBytes: number;
1549
+ }>;
1550
+ containsAction(actionDigest: CasDigest): Promise<boolean>;
1551
+ fetchBlob(digest: CasDigest, destinationPath: string): Promise<boolean>;
1552
+ retrieveAction(actionDigest: CasDigest): Promise<ActionResult | null>;
1553
+ storeAction(actionDigest: CasDigest, result: ActionResult, blobs: ReadonlyArray<BlobSource>): Promise<boolean>;
1554
+ }
1555
+ /**
1556
+ * Per-call control over restore fidelity. Both default to `true` —
1557
+ * the cache's job is to recreate exactly what was captured, and any
1558
+ * deviation is opt-in. Callers (e.g. vis target config) can flip
1559
+ * either off when downstream tooling needs a "fresh" mtime/mode.
1560
+ */
1561
+ interface ExtractOptions {
1562
+ preserveMtime?: boolean;
1563
+ preservePerms?: boolean;
1087
1564
  }
1088
1565
  /**
1089
1566
  * Represents a cached task result.
@@ -1166,6 +1643,40 @@ declare class Cache {
1166
1643
  */
1167
1644
  get cacheDirectory(): string;
1168
1645
  /**
1646
+ * Root for v2 CAS-shaped reads/writes. Backend implementations
1647
+ * (HTTP today, REAPI gRPC next) take this path so they can
1648
+ * hydrate fetched blobs into the same CAS the local cache reads
1649
+ * from. Equal to {@link cacheDirectory} — `v2/` lives under that.
1650
+ */
1651
+ get casRoot(): string;
1652
+ /**
1653
+ * Read a v2 {@link ActionResult} by action digest. Resolves to
1654
+ * `null` on miss. The orchestrator is expected to follow up with
1655
+ * {@link materializeOutputs} to place the referenced blobs into
1656
+ * the workspace.
1657
+ */
1658
+ getActionResult(actionDigest: CasDigest): Promise<ActionResult | null>;
1659
+ /**
1660
+ * Look up an action digest by the legacy task hash. Returns
1661
+ * `null` when the bridge file isn't present — caller falls
1662
+ * through to the legacy `&lt;hash>/` layout (or executes the task).
1663
+ */
1664
+ resolveActionDigestForTaskHash(taskHash: string): Promise<string | null>;
1665
+ /**
1666
+ * Persist a v2 entry: writes the AC JSON, copies referenced
1667
+ * blobs into the CAS via the lazy {@link BlobSource} handles,
1668
+ * and binds the task hash → action digest redirect last so a
1669
+ * partial failure can't surface a half-written entry.
1670
+ */
1671
+ putActionResult(taskHash: string, actionDigest: CasDigest, result: ActionResult, blobs: ReadonlyArray<BlobSource>): Promise<void>;
1672
+ /**
1673
+ * Materialize an action's outputs into the workspace. Streams
1674
+ * each referenced blob from the local CAS to its workspace path.
1675
+ * Returns `false` when any blob is missing — caller treats that
1676
+ * as a cache miss and re-executes.
1677
+ */
1678
+ materializeOutputs(result: ActionResult, workspaceRoot: string): Promise<boolean>;
1679
+ /**
1169
1680
  * Retrieves a cached result for the given task hash.
1170
1681
  * Returns undefined if no valid cache entry exists.
1171
1682
  */
@@ -1196,7 +1707,7 @@ declare class Cache {
1196
1707
  * extracted staging become the set of swap roots. Still accepted
1197
1708
  * for backward compat.
1198
1709
  */
1199
- restoreOutputs(hash: string, _outputs?: OutputSpec[]): Promise<boolean>;
1710
+ restoreOutputs(hash: string, _outputs?: OutputSpec[], options?: ExtractOptions): Promise<boolean>;
1200
1711
  /**
1201
1712
  * Retrieves the most recent cached result for a task by its ID.
1202
1713
  * Used in auto-fingerprint mode where the hash is derived from
@@ -1220,6 +1731,91 @@ declare class Cache {
1220
1731
  clear(): Promise<void>;
1221
1732
  }
1222
1733
  /**
1734
+ * Compute the sha256 digest of a buffer's bytes. Lowercase hex, matching
1735
+ * REAPI's `Digest.hash` format. Used for synchronous payloads (small
1736
+ * AC metadata, action proto bytes) where streaming isn't worth it.
1737
+ */
1738
+ declare const digestBuffer: (bytes: Buffer) => CasDigest;
1739
+ /**
1740
+ * Compute the sha256 digest of a file's contents by streaming. Avoids
1741
+ * loading multi-hundred-MB outputs into memory. Returns `undefined`
1742
+ * when the file can't be opened (caller decides whether that's fatal).
1743
+ */
1744
+ declare const digestFile: (filePath: string) => Promise<CasDigest | undefined>;
1745
+ /**
1746
+ * v2 layout root inside the cache directory. Coexists with the legacy
1747
+ * `&lt;cacheDir>/&lt;hash>/` entries until the migration window closes.
1748
+ */
1749
+ declare const V2_ROOT = "v2";
1750
+ /**
1751
+ * Sub-roots under `v2/`. CAS holds raw blob bytes, AC holds JSON
1752
+ * `ActionResult` entries, the task-hash index bridges xxh3 task hashes
1753
+ * to sha256 action digests, tmp stages atomic renames.
1754
+ */
1755
+ declare const V2_CAS = "cas";
1756
+ declare const V2_AC = "ac";
1757
+ declare const V2_INDEX = "task-hash-index";
1758
+ declare const V2_TMP = "tmp";
1759
+ /**
1760
+ * Resolve the on-disk path for a CAS blob. `&lt;root>/v2/cas/&lt;aa>/&lt;hash>`.
1761
+ */
1762
+ declare const casBlobPath: (root: string, hash: string) => string;
1763
+ /**
1764
+ * Resolve the on-disk path for an Action Cache entry. AC entries are
1765
+ * JSON, suffix kept off-disk to match REAPI semantics (the action
1766
+ * digest is the file name).
1767
+ */
1768
+ declare const acEntryPath: (root: string, actionHash: string) => string;
1769
+ /**
1770
+ * Resolve the path for the task-hash → action-digest redirect. 64-byte
1771
+ * file containing the action digest hex; lets `Cache.get(taskHash)`
1772
+ * jump to the AC entry without recomputing the action proto.
1773
+ */
1774
+ declare const taskHashIndexPath: (root: string, taskHash: string) => string;
1775
+ /**
1776
+ * Returns `true` if the CAS blob already exists on disk for the given
1777
+ * digest. Caller uses this for `FindMissingBlobs`-style elision and to
1778
+ * avoid re-uploading bytes the local cache already holds.
1779
+ */
1780
+ declare const containsBlob: (root: string, digest: CasDigest) => Promise<boolean>;
1781
+ /**
1782
+ * Stream a CAS blob from a source path into the store. The blob is
1783
+ * staged under `v2/tmp/` and renamed into `v2/cas/&lt;aa>/&lt;digest>` after
1784
+ * the bytes land. Idempotent: a concurrent writer racing on the same
1785
+ * digest results in two POSIX renames over byte-identical content,
1786
+ * which is atomic per POSIX. On Windows we treat `EEXIST` as success.
1787
+ *
1788
+ * `digest.hash` is trusted — the caller is responsible for computing
1789
+ * the sha256 of the source file first. Re-hashing here would double
1790
+ * the IO on every put.
1791
+ */
1792
+ declare const putBlobFromFile: (root: string, digest: CasDigest, sourcePath: string) => Promise<void>;
1793
+ /**
1794
+ * Same as {@link putBlobFromFile} but for in-memory bytes. Used for
1795
+ * tiny payloads (the AC entry's stdout digest, tree protos) where
1796
+ * streaming would be more code than it's worth.
1797
+ */
1798
+ declare const putBlobFromBytes: (root: string, digest: CasDigest, bytes: Buffer) => Promise<void>;
1799
+ /**
1800
+ * Materialize a CAS blob to a destination path. Streams; safe for
1801
+ * large outputs. Returns `false` if the blob isn't in the local store.
1802
+ */
1803
+ declare const fetchBlobToFile: (root: string, digest: CasDigest, destinationPath: string) => Promise<boolean>;
1804
+ /**
1805
+ * Verify a blob's bytes match its expected digest. Used during legacy
1806
+ * → v2 migration where we trust nothing — sha256 every staged file
1807
+ * before the rename so a bit-flipped legacy artifact doesn't poison
1808
+ * the new CAS.
1809
+ */
1810
+ declare const verifyBlob: (filePath: string, expected: CasDigest) => Promise<boolean>;
1811
+ /**
1812
+ * Bump mtime/atime on a blob. Drives mark-and-sweep GC: the sweeper
1813
+ * evicts blobs whose mtime is older than `maxCacheAge` AND not
1814
+ * referenced by any AC entry. Touching on hit means LRU tracks real
1815
+ * usage rather than write-time.
1816
+ */
1817
+ declare const touchBlob: (root: string, digest: CasDigest) => Promise<void>;
1818
+ /**
1223
1819
  * Summary of a single task execution.
1224
1820
  */
1225
1821
  interface TaskSummary {
@@ -2047,110 +2643,56 @@ declare const isNativeAvailable: () => boolean;
2047
2643
  */
2048
2644
  declare const resolveOutputs: (workspaceRoot: string, outputs: OutputSpec[] | undefined, autoWrites?: ReadonlyArray<string>) => Promise<string[]>;
2049
2645
  /**
2050
- * Enforces project dependency constraints on a project graph.
2051
- * @param projectGraph The workspace project graph to validate.
2052
- * @param constraints The constraint rules to enforce.
2053
- * @returns Array of violations found. Empty means all constraints pass.
2054
- */
2055
- declare const enforceProjectConstraints: (projectGraph: ProjectGraph, constraints: ConstraintsConfig) => ConstraintViolation[];
2056
- /**
2057
- * Compression algorithm used for artifact tarballs on the wire.
2058
- * - `"gzip"` (default): tar+gzip, matches Turborepo's protocol format
2059
- * and stays interop-safe with existing remote cache servers.
2060
- * - `"brotli"`: tar + Node brotli (BROTLI_MODE_TEXT, quality 4) — a
2061
- * solid ratio/speed trade-off for source-tree tarballs. Both upload
2062
- * and download sides must agree; switching invalidates existing
2063
- * remote entries (they will simply re-populate on next run).
2646
+ * URI schemes recognized by {@link parseInputUri}. Each maps to one of the
2647
+ * existing structured `InputDefinition` shapes the URI form is a sugar
2648
+ * over the object form so the same hash semantics apply unchanged.
2649
+ *
2650
+ * - `file://&lt;path>` and `glob://&lt;pattern>` → {@link FileSetInput}
2651
+ * (both produce a fileset; the split is purely documentary so a reader
2652
+ * can tell at a glance whether the author meant a single path or a glob).
2653
+ * - `env://&lt;NAME>` {@link EnvironmentInput}.
2654
+ * - `func://&lt;command>` {@link RuntimeInput} (runtime command output).
2655
+ * - `dep://&lt;a,b,c>` {@link ExternalDependencyInput} (comma-separated names).
2656
+ *
2657
+ * `{projectRoot}` and `{workspaceRoot}` tokens are honored inside `file://`
2658
+ * and `glob://` bodies, matching the bare-string form. Negation works for
2659
+ * filesets only (`!file://...`, `!glob://...`); attempting to negate the
2660
+ * other schemes throws because there is no semantic for "not this env var".
2064
2661
  */
2065
- type RemoteCacheCompression = "brotli" | "gzip";
2662
+ declare const INPUT_URI_SCHEMES: readonly ["file", "glob", "env", "func", "dep"];
2663
+ type InputUriScheme = (typeof INPUT_URI_SCHEMES)[number];
2066
2664
  /**
2067
- * HMAC signing configuration for cache integrity.
2665
+ * Thrown when a string carries a URI-shaped prefix but the scheme is
2666
+ * unrecognized or the body violates a scheme-specific constraint
2667
+ * (e.g. negating a non-fileset scheme).
2068
2668
  *
2069
- * When set, every upload carries an `X-Artifact-Signature` header
2070
- * containing the HMAC-SHA256 digest of `hash | body`. On download,
2071
- * the client recomputes the HMAC and rejects any artifact whose
2072
- * signature doesn't match using a constant-time comparison.
2073
- *
2074
- * Prevents cache poisoning in shared team environments where a
2075
- * compromised cache server (or a MITM) could inject malicious
2076
- * artifacts. Unsigned entries (written before signing was enabled)
2077
- * are accepted only when `verifyOnDownload === false` — the default.
2669
+ * Surfaces as a config-load error rather than degrading silently into a
2670
+ * fileset glob silent fallback would let typos like `gob://**` mask
2671
+ * cache-correctness bugs that only show up at hash-divergence time.
2078
2672
  */
2079
- interface RemoteCacheSigning {
2080
- /** Shared secret. Must be at least 16 characters. */
2081
- secret: string;
2082
- /**
2083
- * Reject downloads whose signature doesn't match or is missing.
2084
- * Set to `true` once every upload on your server is signed.
2085
- * @default false
2086
- */
2087
- verifyOnDownload?: boolean;
2673
+ declare class InvalidInputUriError extends Error {
2674
+ constructor(message: string);
2088
2675
  }
2089
2676
  /**
2090
- * Options for the remote cache.
2677
+ * Parses a URI-prefixed input string into its structured `InputDefinition`.
2678
+ * Returns `undefined` for strings that don't carry a URI scheme — callers
2679
+ * fall back to existing handling (named-input lookup, bare globs).
2091
2680
  */
2092
- interface RemoteCacheOptions {
2093
- /**
2094
- * Compression format for artifact tarballs. Defaults to `"gzip"`
2095
- * for Turborepo protocol compatibility.
2096
- */
2097
- compression?: RemoteCacheCompression;
2098
- /**
2099
- * Called when a fire-and-forget upload fails.
2100
- * Since uploads are non-blocking, errors are silently swallowed by default.
2101
- * Provide this callback to log or report upload failures.
2102
- */
2103
- onUploadError?: (hash: string, error: unknown) => void;
2104
- /** Whether to enable remote cache reads */
2105
- read?: boolean;
2106
- /**
2107
- * HMAC-SHA256 signing for upload integrity. When set, every
2108
- * uploaded artifact carries an `X-Artifact-Signature` header;
2109
- * downloads with `verifyOnDownload: true` reject unsigned or
2110
- * tampered payloads.
2111
- */
2112
- signing?: RemoteCacheSigning;
2113
- /** Team ID or namespace for cache isolation */
2114
- teamId?: string;
2115
- /** Request timeout in milliseconds (default: 30000) */
2116
- timeout?: number;
2117
- /** Authentication token for the remote cache */
2118
- token?: string;
2119
- /** Remote cache server URL (e.g., "https://cache.example.com") */
2120
- url: string;
2121
- /** Whether to enable remote cache writes */
2122
- write?: boolean;
2123
- }
2681
+ declare const parseInputUri: (input: string) => InputDefinition | undefined;
2124
2682
  /**
2125
- * HTTP-based remote cache compatible with the Turborepo remote cache protocol.
2126
- *
2127
- * Protocol:
2128
- * - GET /v8/artifacts/{hash}?teamId={team} -> retrieve cached artifact (gzipped tar)
2129
- * - PUT /v8/artifacts/{hash}?teamId={team} -> store artifact
2130
- * - POST /v8/artifacts/events -> analytics (optional)
2131
- *
2132
- * Authentication via `Authorization: Bearer {token}` header.
2133
- *
2134
- * The cache entry is a gzipped tarball containing the cache directory contents
2135
- * (code, terminalOutput, fingerprint.json, outputs/, .commit).
2683
+ * Cheap predicate used by callers that only need to know whether a string
2684
+ * looks like a URI — saves them re-parsing when they want to short-circuit
2685
+ * other handling (e.g. file-group lookup) before delegating to
2686
+ * {@link parseInputUri} downstream.
2136
2687
  */
2137
- declare class RemoteCache {
2138
- #private;
2139
- constructor(options: RemoteCacheOptions);
2140
- /**
2141
- * Retrieves a cached artifact from the remote cache.
2142
- * Returns the local path to the extracted cache entry, or undefined if not found.
2143
- */
2144
- retrieve(hash: string, localCacheDirectory: string): Promise<boolean>;
2145
- /**
2146
- * Stores a cache entry in the remote cache.
2147
- */
2148
- store(hash: string, localCacheDirectory: string): Promise<boolean>;
2149
- /**
2150
- * Checks if an artifact exists in the remote cache without downloading it.
2151
- */
2152
- exists(hash: string): Promise<boolean>;
2153
- }
2688
+ declare const looksLikeInputUri: (input: string) => boolean;
2689
+ /**
2690
+ * Enforces project dependency constraints on a project graph.
2691
+ * @param projectGraph The workspace project graph to validate.
2692
+ * @param constraints The constraint rules to enforce.
2693
+ * @returns Array of violations found. Empty means all constraints pass.
2694
+ */
2695
+ declare const enforceProjectConstraints: (projectGraph: ProjectGraph, constraints: ConstraintsConfig) => ConstraintViolation[];
2154
2696
  interface CreateTaskGraphOptions {
2155
2697
  /** The project graph */
2156
2698
  projectGraph: ProjectGraph;
@@ -2368,7 +2910,14 @@ interface TaskOrchestratorOptions {
2368
2910
  dryRun?: boolean;
2369
2911
  fingerprintEnvPatterns?: string[];
2370
2912
  lifeCycle: LifeCycleInterface;
2371
- remoteCache?: RemoteCache;
2913
+ /**
2914
+ * Surfaces bridge-local upload pipeline failures (tar / digest)
2915
+ * for fire-and-forget remote-cache writes. Wire-level errors are
2916
+ * already reported by the backend's own `onUploadError`; this
2917
+ * fills the gap for steps the backend never sees.
2918
+ */
2919
+ onRemoteUploadError?: (hash: string, error: unknown) => void;
2920
+ remoteCache?: RemoteCacheBackend;
2372
2921
  resolveCommand?: (task: Task) => string | undefined;
2373
2922
  scheduler: TaskScheduler;
2374
2923
  skipCache?: boolean;
@@ -2500,8 +3049,45 @@ declare const readPackageDeps: (packageJsonPath: string, options?: {
2500
3049
  peer?: boolean;
2501
3050
  }) => Promise<Set<string> | undefined>;
2502
3051
  /**
2503
- * Generates a unique ID for temporary files/directories.
2504
- * Not cryptographically secure — for cache entry naming only.
3052
+ * Generates a unique ID for temporary files/directories using
3053
+ * `crypto.randomUUID()`. Used for staging-path names that briefly
3054
+ * coexist on disk during atomic writes — collisions would clobber a
3055
+ * concurrent writer's staging directory, so the extra entropy over
3056
+ * `Date.now() + Math.random()` is worth the cycles. UUID v4 from
3057
+ * Node's crypto module is itself uniformly random, making this
3058
+ * cheaper than the previous string concat.
2505
3059
  */
2506
3060
  declare const uniqueId: () => string;
2507
- export { type AffectedOptions, type AffectedResult, type AffectedScope, Cache, type CacheMissReason, type CacheOptions, type CachedResult, type ChromeTraceEvent, CompositeLifeCycle, type ConcurrentCloseEvent, type ConcurrentCommandConfig, type ConcurrentCommandInput, type ConcurrentRunResult, type ConcurrentRunnerOptions, ConsoleLifeCycle, type ConstraintViolation, type ConstraintsConfig, DEFAULT_CACHE_DIRECTORY_NAME, type DependencyKindRules, type DependencyType, type DetectedFramework, EmptyLifeCycle, type EnvMatcher, type EnvironmentInput, type ExternalDependencyInput, type FileAccess, FileAccessTracker, type FileSetBase, type FileSetInput, type FileSetPattern, type FileSnapshot, FingerprintManager, type GraphFormat, type GraphJson, type GraphVisualizerOptions, InProcessTaskHasher, IncrementalFileHasher, type IncrementalHasherOptions, type InputDefinition, type InputHandlerOptions, 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, RemoteCache, type RemoteCacheCompression, type RemoteCacheOptions, type RemoteCacheSigning, type ResolvedDependency, type RestartOptions, type RunSummary, type RuntimeInput, type TagRelationships, type TargetConfiguration, type TargetDependencyConfig, type Task, type TaskExecutionOptions, type TaskExecutor, type TaskFingerprint, type TaskGraph, type TaskHashDetails, type TaskHasher, type TaskHasherOptions, TaskOrchestrator, type TaskOrchestratorOptions, type TaskPriority, type TaskResult, type TaskResults, type TaskRunnerContext, type TaskRunnerOptions, TaskScheduler, type TaskStatus, type TaskSummary, type TaskTarget, type TasksRunner, type TeardownOptions, TerminalBuffer, type TokenContext, type TrackedExecutionResult, TrackedTaskExecutor, type TrackingResult, type TypeBoundaries, type WhenCondition, type WhenContext, type WorkspaceConfiguration, buildForwardDependencyMap, buildReverseDependencyMap, collectFiles, computeTaskHash, createFailureResult, createInputHandler, createLogReporter, createTaskGraph, defaultTaskRunner, detectFrameworks, detectScriptShell, enforceProjectConstraints, evaluateWhen, expandAffected, expandArguments, expandShortcut, expandTokens, expandTokensInString, expandWildcard, explainWhen, extractPackageName, filterAffectedTasks, findCycle, findCycles, formatCacheSize, formatTimingTable, generatePreloadScript, generateRunSummary, getAffectedProjects, getChangedFiles, getCurrentBranch, getDependentTasks, getFrameworkEnvVariables, getLastRunSummaryPath, getLeafTasks, getTaskId, getTransitiveDependencies, hashFile, hashStrings, inferFrameworkEnvPatterns, isNativeAvailable, loadNativeBindings, logTimings, makeAcyclic, parseCacheSize, parseCommands, parseNpmLockfile, parsePartition, parsePnpmLockfile, parseTaskId, parseYarnLockfile, projectGraphToDot, readLastRunSummary, readPackageDeps, resetBranchCache, resolveOutputs, resolveTaskCwd, reverseTaskGraph, runConcurrentFallback, runConcurrently, runTeardown, sortObjectKeys, stripQuotes, toChromeTrace, toGraphAscii, toGraphHtml, toGraphJson, toGraphvizDot, uniqueId, walkTaskGraph, withRestart, writeChromeTrace, writeLastRunSummary, writeRunSummary };
3061
+ /**
3062
+ * Returns the absolute path to the *main* git worktree root when
3063
+ * `workspaceRoot` is a linked worktree, or `undefined` for primary
3064
+ * checkouts and non-git directories.
3065
+ *
3066
+ * Result is memoized for the lifetime of the process — worktree topology
3067
+ * does not change at runtime, so the second call is a hash lookup.
3068
+ *
3069
+ * Detection logic:
3070
+ * 1. If `{workspaceRoot}/.git` is a *directory*, this is a primary checkout
3071
+ * (or vanilla repo). Returns `undefined`.
3072
+ * 2. If `{workspaceRoot}/.git` is a *file* (gitlink), resolves to the parent
3073
+ * of `git rev-parse --git-common-dir` — that is the main worktree root.
3074
+ * 3. On any error (missing git binary, shallow CI checkout, etc.), returns
3075
+ * `undefined` so the caller falls back to the workspace-local cache.
3076
+ * @param workspaceRoot Absolute path to the candidate workspace root.
3077
+ * @returns The main worktree root, or `undefined` if not a linked worktree.
3078
+ */
3079
+ declare const getMainWorktreeRoot: (workspaceRoot: string) => string | undefined;
3080
+ /**
3081
+ * Returns `true` when `{workspaceRoot}/.git` is a regular file (the gitlink
3082
+ * pointer used by `git worktree add`), `false` otherwise. Cheap pre-flight
3083
+ * before invoking `git rev-parse`.
3084
+ * @param workspaceRoot Absolute path to the candidate workspace root.
3085
+ */
3086
+ declare const isLinkedWorktree: (workspaceRoot: string) => boolean;
3087
+ /**
3088
+ * Clears the in-process detection cache. Tests must call this between
3089
+ * scenarios because the cache key is the canonicalized workspace path —
3090
+ * recreating a fixture at the same path would otherwise leak stale results.
3091
+ */
3092
+ declare const resetWorktreeCache: () => void;
3093
+ 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 ConcurrentCloseEvent, type ConcurrentCommandConfig, type ConcurrentCommandInput, type ConcurrentRunResult, type ConcurrentRunnerOptions, ConsoleLifeCycle, type ConstraintViolation, type ConstraintsConfig, DEFAULT_CACHE_DIRECTORY_NAME, type DependencyKindRules, type DependencyType, type DetectedFramework, EmptyLifeCycle, type EnvMatcher, type EnvironmentInput, type ExternalDependencyInput, type FileAccess, FileAccessTracker, type FileSetBase, type FileSetInput, type FileSetPattern, type FileSnapshot, FingerprintManager, type GraphFormat, type GraphJson, type GraphVisualizerOptions, HttpRemoteCache, INPUT_URI_SCHEMES, InProcessTaskHasher, IncrementalFileHasher, type IncrementalHasherOptions, type InputDefinition, type InputHandlerOptions, type InputUriScheme, InvalidInputUriError, type LifeCycleInterface, LockfileHasher, type LogMode, LogReporter, type NamedInputs, type NodePlatform, type OutputSpec, type PackageLockfileHash, type ParseCommandsOptions, type PartitionOptions, type ProcessEvent, type ProjectConfiguration, type ProjectGraph, type ProjectGraphDependency, type ProjectGraphProjectNode, ReapiRemoteCache, type ReapiRemoteCacheOptions, type RemoteCacheBackend, type RemoteCacheCompression, type RemoteCacheOptions, type RemoteCacheSigning, type ResolvedDependency, type RestartOptions, type RunSummary, type RuntimeInput, type TagRelationships, type TargetConfiguration, type TargetDependencyConfig, type Task, type TaskExecutionOptions, type TaskExecutor, type TaskFingerprint, type TaskGraph, type TaskHashDetails, type TaskHasher, type TaskHasherOptions, TaskOrchestrator, type TaskOrchestratorOptions, type TaskPriority, type TaskResult, type TaskResults, type TaskRunnerContext, type TaskRunnerOptions, TaskScheduler, type TaskStatus, type TaskSummary, type TaskTarget, type TasksRunner, type TeardownOptions, TerminalBuffer, type TokenContext, type TrackedExecutionResult, TrackedTaskExecutor, type TrackingResult, type TypeBoundaries, V2_AC, V2_CAS, V2_INDEX, V2_ROOT, V2_TMP, type WhenCondition, type WhenContext, type WorkspaceConfiguration, acEntryPath, actionDigestForTaskHash, buildForwardDependencyMap, buildReverseDependencyMap, casBlobPath, collectFiles, computeTaskHash, containsBlob, containsByTaskHash, createFailureResult, createInputHandler, createLogReporter, createRemoteCacheBackend, createTaskGraph, defaultTaskRunner, detectFrameworks, detectScriptShell, digestBuffer, digestFile, enforceProjectConstraints, evaluateWhen, expandAffected, expandArguments, expandShortcut, expandTokens, expandTokensInString, expandWildcard, explainWhen, extractPackageName, fetchBlobToFile, filterAffectedTasks, findCycle, findCycles, formatCacheSize, formatTimingTable, generatePreloadScript, generateRunSummary, getAffectedProjects, getChangedFiles, getCurrentBranch, getDependentTasks, getFrameworkEnvVariables, getLastRunSummaryPath, getLeafTasks, getMainWorktreeRoot, getTaskId, getTransitiveDependencies, hashFile, hashStrings, inferFrameworkEnvPatterns, isLinkedWorktree, isNativeAvailable, loadNativeBindings, logTimings, looksLikeInputUri, makeAcyclic, parseCacheSize, parseCommands, parseInputUri, parseNpmLockfile, parsePartition, parsePnpmLockfile, parseTaskId, parseYarnLockfile, projectGraphToDot, putBlobFromBytes, putBlobFromFile, readLastRunSummary, readPackageDeps, resetBranchCache, resetWorktreeCache, resolveCacheMode, resolveOutputs, resolveTaskCwd, retrieveByTaskHash, reverseTaskGraph, runConcurrentFallback, runConcurrently, runTeardown, sortObjectKeys, storeByTaskHash, stripQuotes, taskHashIndexPath, toChromeTrace, toGraphAscii, toGraphHtml, toGraphJson, toGraphvizDot, touchBlob, uniqueId, verifyBlob, walkTaskGraph, withRestart, writeChromeTrace, writeLastRunSummary, writeRunSummary };