stitchkit 0.114.1 → 0.115.0

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 (108) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/agent-runtime/coding-tool-files.d.ts.map +1 -1
  3. package/dist/agent-runtime/coding-tool-listing.d.ts.map +1 -1
  4. package/dist/agent-runtime/coding-tool-paths.d.ts.map +1 -1
  5. package/dist/agent-runtime/coding-tool-refusals.d.ts +6 -0
  6. package/dist/agent-runtime/coding-tool-refusals.d.ts.map +1 -1
  7. package/dist/agent-runtime/coding-tool-search-patch.d.ts.map +1 -1
  8. package/dist/agent-runtime/contained-scan.d.ts +1 -1
  9. package/dist/agent-runtime/contained-scan.d.ts.map +1 -1
  10. package/dist/agent-runtime/sandbox-broker.d.ts.map +1 -1
  11. package/dist/agent-runtime/sandbox-bubblewrap-storage.d.ts.map +1 -1
  12. package/dist/application/directory-inbox-files.d.ts.map +1 -1
  13. package/dist/application/file-state-store.d.ts.map +1 -1
  14. package/dist/entrypoints/agent-runtime/coding-tools.js +6 -4
  15. package/dist/entrypoints/agent-runtime/harness.js +2 -1
  16. package/dist/entrypoints/agent-runtime/sandbox.js +13 -8
  17. package/dist/entrypoints/agent-runtime/sqlite/bun.js +8 -6
  18. package/dist/entrypoints/agent-runtime/sqlite/node.js +8 -6
  19. package/dist/entrypoints/agent-runtime/testing.js +8 -6
  20. package/dist/entrypoints/agent-runtime.js +8 -6
  21. package/dist/entrypoints/application/diagnostic-journal.js +2 -2
  22. package/dist/entrypoints/application/directory-inbox.js +10 -7
  23. package/dist/entrypoints/cli/publish.js +11 -8
  24. package/dist/entrypoints/cli.js +6 -3
  25. package/dist/entrypoints/files.js +10 -7
  26. package/dist/entrypoints/process/owner-loss.js +1 -1
  27. package/dist/entrypoints/process.js +3 -3
  28. package/dist/entrypoints/react/keyboard/testing.js +1 -1
  29. package/dist/entrypoints/react/keyboard.js +11 -11
  30. package/dist/entrypoints/server.js +4 -4
  31. package/dist/entrypoints/telegram.js +8 -5
  32. package/dist/entrypoints/tools.js +6 -3
  33. package/dist/files/chunk-spool.d.ts.map +1 -1
  34. package/dist/{index-2f9r0jgr.js → index-364k2zwa.js} +4 -4
  35. package/dist/{index-km2qw407.js → index-3qqdqwxe.js} +91 -36
  36. package/dist/{index-s47ac8zt.js → index-7yx8j6ay.js} +133 -156
  37. package/dist/{index-kdnfe0sr.js → index-90p34rts.js} +2 -2
  38. package/dist/{index-a3462dqf.js → index-944x38nv.js} +1 -1
  39. package/dist/{index-07dcf2yn.js → index-ac6vvm30.js} +5 -2
  40. package/dist/{index-qj1sy4ep.js → index-crmswbrt.js} +1 -1
  41. package/dist/{index-7r4knwf8.js → index-dr3t5n1v.js} +14 -4
  42. package/dist/{index-dc9ere5p.js → index-dx5cm9pc.js} +1 -1
  43. package/dist/{index-84bf5spd.js → index-f6m4c545.js} +5 -2
  44. package/dist/{index-hy802jnr.js → index-fdk4r4r6.js} +6 -3
  45. package/dist/index-j8kf56wx.js +100 -0
  46. package/dist/index-jj9228tt.js +6 -0
  47. package/dist/{index-t65erk4z.js → index-mgb7r8b8.js} +122 -62
  48. package/dist/{index-bh2ht9y5.js → index-n8z3rvzw.js} +4 -1
  49. package/dist/index-nregbbsc.js +107 -0
  50. package/dist/{index-j4zh647a.js → index-tt30h7mr.js} +4 -1
  51. package/dist/{index-zrtrjkbf.js → index-x0ddbj85.js} +148 -172
  52. package/dist/{index-p02zgk8x.js → index-y4rxnvgp.js} +6 -0
  53. package/dist/{index-5v5a9k2n.js → index-z3d6smer.js} +1 -1
  54. package/dist/{index-6b3pcq5z.js → index-zqkz4af9.js} +1 -1
  55. package/dist/internal/atomic-file.d.ts.map +1 -1
  56. package/dist/internal/atomic-staging.d.ts.map +1 -1
  57. package/dist/internal/contained-directory.d.ts +20 -0
  58. package/dist/internal/contained-directory.d.ts.map +1 -0
  59. package/dist/{agent-runtime → internal}/contained-files.d.ts +14 -13
  60. package/dist/internal/contained-files.d.ts.map +1 -0
  61. package/dist/internal/contained-handle.d.ts +30 -0
  62. package/dist/internal/contained-handle.d.ts.map +1 -0
  63. package/dist/internal/contained-write.d.ts +12 -0
  64. package/dist/internal/contained-write.d.ts.map +1 -0
  65. package/dist/internal/darwin-binding.d.ts +1 -1
  66. package/dist/internal/darwin-binding.d.ts.map +1 -1
  67. package/dist/internal/errno.d.ts +3 -0
  68. package/dist/internal/errno.d.ts.map +1 -0
  69. package/dist/internal/pinned-http.d.ts +27 -0
  70. package/dist/internal/pinned-http.d.ts.map +1 -0
  71. package/dist/internal/process-instance.d.ts.map +1 -1
  72. package/dist/internal/secure-fetch.d.ts +26 -6
  73. package/dist/internal/secure-fetch.d.ts.map +1 -1
  74. package/dist/internal/write-download.d.ts +5 -14
  75. package/dist/internal/write-download.d.ts.map +1 -1
  76. package/dist/process/group-diagnostics.d.ts +1 -0
  77. package/dist/process/group-diagnostics.d.ts.map +1 -1
  78. package/dist/process/group-settlement.d.ts +2 -0
  79. package/dist/process/group-settlement.d.ts.map +1 -1
  80. package/dist/process/group.d.ts.map +1 -1
  81. package/dist/process/owner-loss-control.d.ts.map +1 -1
  82. package/dist/react/keyboard/hooks.d.ts +2 -2
  83. package/dist/react/keyboard/hooks.d.ts.map +1 -1
  84. package/dist/react/keyboard/layers.d.ts +2 -0
  85. package/dist/react/keyboard/layers.d.ts.map +1 -1
  86. package/dist/telegram/broadcast-state.d.ts.map +1 -1
  87. package/dist/tools/cli/download.d.ts.map +1 -1
  88. package/dist/tools/cli/publication-storage.d.ts.map +1 -1
  89. package/dist/tools/cli/publication.d.ts.map +1 -1
  90. package/dist/tools/cli/view.d.ts.map +1 -1
  91. package/llms/agent-runtime.txt +16 -6
  92. package/llms/cli.txt +34 -2
  93. package/llms/process.txt +2 -2
  94. package/llms/react-keyboard.txt +18 -2
  95. package/llms/stitchkit.txt +3 -3
  96. package/llms/{upgrading-0.104.0-0.114.0.txt → upgrading-0.104.0-0.115.0.txt} +62 -3
  97. package/llms/upgrading.txt +1 -1
  98. package/llms-full.txt +132 -15
  99. package/llms.txt +5 -5
  100. package/native/darwin-arm64.node +0 -0
  101. package/native/darwin-x64.node +0 -0
  102. package/native-assets.json +4 -4
  103. package/package.json +1 -1
  104. package/dist/agent-runtime/contained-darwin.d.ts +0 -21
  105. package/dist/agent-runtime/contained-darwin.d.ts.map +0 -1
  106. package/dist/agent-runtime/contained-files.d.ts.map +0 -1
  107. package/dist/agent-runtime/contained-write.d.ts +0 -9
  108. package/dist/agent-runtime/contained-write.d.ts.map +0 -1
@@ -0,0 +1,100 @@
1
+ import {
2
+ loadDarwinBinding
3
+ } from "./index-2kbysk9s.js";
4
+ import {
5
+ descriptorPath,
6
+ NumericFileHandle
7
+ } from "./index-x0ddbj85.js";
8
+ import {
9
+ stagingName
10
+ } from "./index-tt30h7mr.js";
11
+
12
+ // src/internal/contained-write.ts
13
+ import { constants, fchmod, open as openDescriptor, write as writeDescriptor } from "node:fs";
14
+ import { rename, unlink } from "node:fs/promises";
15
+ import path from "node:path";
16
+ function entryPath(directory, name) {
17
+ return path.join(descriptorPath(directory), name);
18
+ }
19
+ async function openExclusive(directory, name, mode) {
20
+ if (process.platform === "darwin") {
21
+ return loadDarwinBinding().createFileAt(directory.fd, name, mode);
22
+ }
23
+ return new Promise((resolve, reject) => {
24
+ openDescriptor(entryPath(directory, name), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, mode, (error, descriptor) => error ? reject(error) : resolve(descriptor));
25
+ });
26
+ }
27
+ async function renameEntry(directory, source, target) {
28
+ if (process.platform === "darwin") {
29
+ loadDarwinBinding().renameAt(directory.fd, source, target);
30
+ return;
31
+ }
32
+ await rename(entryPath(directory, source), entryPath(directory, target));
33
+ }
34
+ async function discardEntry(directory, name) {
35
+ try {
36
+ if (process.platform === "darwin")
37
+ loadDarwinBinding().unlinkAt(directory.fd, name);
38
+ else
39
+ await unlink(entryPath(directory, name));
40
+ } catch {}
41
+ }
42
+ async function writeDescriptorFully(descriptor, content) {
43
+ const bytes = typeof content === "string" ? Buffer.from(content) : content;
44
+ let offset = 0;
45
+ while (offset < bytes.byteLength) {
46
+ const written = await new Promise((resolve, reject) => {
47
+ writeDescriptor(descriptor, bytes, offset, bytes.byteLength - offset, null, (error, bytesWritten) => error ? reject(error) : resolve(bytesWritten));
48
+ });
49
+ if (written === 0)
50
+ throw new Error("Contained file write made no progress");
51
+ offset += written;
52
+ }
53
+ }
54
+ async function applyMode(file, mode) {
55
+ if (((await file.stat()).mode & 511) === mode)
56
+ return;
57
+ await new Promise((resolve, reject) => {
58
+ fchmod(file.fd, mode, (error) => error ? reject(error) : resolve());
59
+ });
60
+ }
61
+ async function createFile(directory, name, content, mode) {
62
+ const descriptor = await openExclusive(directory, name, mode ?? 438);
63
+ const file = new NumericFileHandle(descriptor);
64
+ let failed;
65
+ try {
66
+ await writeDescriptorFully(descriptor, content);
67
+ if (mode !== undefined)
68
+ await applyMode(file, mode);
69
+ } catch (error) {
70
+ failed = error;
71
+ } finally {
72
+ try {
73
+ await file.close();
74
+ } catch (error) {
75
+ failed ??= error;
76
+ }
77
+ }
78
+ if (failed) {
79
+ await discardEntry(directory, name);
80
+ throw failed;
81
+ }
82
+ }
83
+ async function writeContainedFile(input) {
84
+ const { handle, basename } = input.parent;
85
+ const mode = input.mode === undefined ? undefined : input.mode & 511;
86
+ if (!input.replace) {
87
+ await createFile(handle, basename, input.content, mode);
88
+ return;
89
+ }
90
+ const temporary = stagingName();
91
+ await createFile(handle, temporary, input.content, mode);
92
+ try {
93
+ await renameEntry(handle, temporary, basename);
94
+ } catch (error) {
95
+ await discardEntry(handle, temporary);
96
+ throw error;
97
+ }
98
+ }
99
+
100
+ export { writeContainedFile };
@@ -0,0 +1,6 @@
1
+ // src/internal/errno.ts
2
+ function errnoOf(error) {
3
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : undefined;
4
+ }
5
+
6
+ export { errnoOf };
@@ -7,9 +7,88 @@ import {
7
7
 
8
8
  // src/internal/secure-fetch.ts
9
9
  import { lookup } from "node:dns/promises";
10
+ import { isIP } from "node:net";
11
+
12
+ // src/internal/pinned-http.ts
10
13
  import { request as httpRequest } from "node:http";
11
14
  import { request as httpsRequest } from "node:https";
12
- import { isIP } from "node:net";
15
+ async function requestPinned(url, resolved, signal, headers = {}) {
16
+ const transport = url.protocol === "https:" ? httpsRequest : httpRequest;
17
+ const incoming = await new Promise((resolve, reject) => {
18
+ let answered = false;
19
+ const request = transport({
20
+ protocol: url.protocol,
21
+ hostname: resolved.address,
22
+ family: resolved.family,
23
+ port: url.port || undefined,
24
+ path: `${url.pathname}${url.search}`,
25
+ method: "GET",
26
+ signal,
27
+ ...url.protocol === "https:" ? { servername: url.hostname } : {},
28
+ headers: { host: url.host, accept: "*/*", ...headers }
29
+ }, (response) => {
30
+ answered = true;
31
+ resolve(response);
32
+ });
33
+ request.once("error", (error) => {
34
+ if (answered)
35
+ return;
36
+ reject(signal?.aborted && signal.reason !== undefined ? signal.reason : error);
37
+ });
38
+ request.end();
39
+ });
40
+ return pinnedResponse(incoming);
41
+ }
42
+ function pinnedResponse(incoming) {
43
+ const status = incoming.statusCode ?? 0;
44
+ const headers = new Headers;
45
+ for (const [name, value] of Object.entries(incoming.headers)) {
46
+ if (Array.isArray(value)) {
47
+ for (const item of value)
48
+ headers.append(name, item);
49
+ } else if (value !== undefined) {
50
+ headers.set(name, value);
51
+ }
52
+ }
53
+ if (status === 204 || status === 205 || status === 304) {
54
+ incoming.resume();
55
+ return new Response(null, { status, statusText: incoming.statusMessage, headers });
56
+ }
57
+ let done = false;
58
+ const body = new ReadableStream({
59
+ start(controller) {
60
+ incoming.on("data", (chunk) => {
61
+ if (done)
62
+ return;
63
+ controller.enqueue(chunk);
64
+ if ((controller.desiredSize ?? 1) <= 0)
65
+ incoming.pause();
66
+ });
67
+ incoming.once("end", () => {
68
+ if (done)
69
+ return;
70
+ done = true;
71
+ controller.close();
72
+ });
73
+ incoming.once("error", (error) => {
74
+ if (done)
75
+ return;
76
+ done = true;
77
+ controller.error(error);
78
+ });
79
+ },
80
+ pull() {
81
+ incoming.resume();
82
+ },
83
+ cancel() {
84
+ done = true;
85
+ incoming.destroy();
86
+ }
87
+ });
88
+ return new Response(body, { status, statusText: incoming.statusMessage, headers });
89
+ }
90
+
91
+ // src/internal/secure-fetch.ts
13
92
  var NUMERIC_HOST = /^(0x[0-9a-f]+|[0-9.]+)$/i;
14
93
  var MAX_REDIRECTS = 5;
15
94
  function assertHttpUrl(url) {
@@ -102,9 +181,10 @@ function isPrivateIp(ip) {
102
181
  }
103
182
  return false;
104
183
  }
105
- async function assertPublicUrl(url, signal) {
106
- await resolvePublicAddress(url, signal);
107
- }
184
+ var defaultLookup = async (host) => {
185
+ const records = await lookup(host, { all: true });
186
+ return Array.isArray(records) ? records : [records];
187
+ };
108
188
  async function fetchGuarded(start, allowPrivate, options = {}) {
109
189
  const timeoutMs = options.timeoutMs ?? 15000;
110
190
  const bodyTimeoutMs = options.bodyTimeoutMs ?? 120000;
@@ -120,15 +200,16 @@ async function fetchGuarded(start, allowPrivate, options = {}) {
120
200
  controller.abort(new Error(`fetch did not produce response headers within ${timeoutMs}ms`));
121
201
  }, timeoutMs);
122
202
  headerTimer.unref?.();
203
+ const transport = options.transport ?? requestPinned;
123
204
  let settled = false;
124
205
  try {
125
206
  let url = start;
126
207
  for (let hop = 0;hop <= MAX_REDIRECTS; hop++) {
127
- if (allowPrivate)
128
- assertHttpUrl(url);
129
- else
130
- await assertPublicUrl(url, signal);
131
- const res = await fetch(url, { redirect: "manual", signal });
208
+ if (url.username || url.password) {
209
+ throw new Error("refusing to fetch a URL that carries credentials");
210
+ }
211
+ const resolved = await resolveAddress(url, allowPrivate, signal, options.dns);
212
+ const res = await transport(url, resolved, signal);
132
213
  const location = res.status >= 300 && res.status < 400 ? res.headers.get("location") : null;
133
214
  if (location === null) {
134
215
  settled = true;
@@ -148,11 +229,27 @@ async function fetchGuarded(start, allowPrivate, options = {}) {
148
229
  clearTimeout(headerTimer);
149
230
  }
150
231
  }
232
+ async function resolveAddress(url, allowPrivate, signal, dns) {
233
+ if (!allowPrivate)
234
+ return resolvePublicAddress(url, signal, dns);
235
+ assertHttpUrl(url);
236
+ const host = url.hostname.replace(/^\[|\]$/g, "");
237
+ if (!host)
238
+ throw new Error("refusing to fetch a URL with no host");
239
+ const ipFamily = isIP(host);
240
+ if (ipFamily)
241
+ return { address: host, family: ipFamily === 6 ? 6 : 4 };
242
+ const records = await (dns ?? defaultLookup)(host, { all: true });
243
+ const record = records[0];
244
+ if (!record)
245
+ throw new Error("host resolved to no addresses");
246
+ return { address: record.address, family: record.family === 6 ? 6 : 4 };
247
+ }
151
248
 
152
249
  class PrivateAddressRefusal extends Error {
153
250
  name = "PrivateAddressRefusal";
154
251
  }
155
- async function resolvePublicAddress(url, signal) {
252
+ async function resolvePublicAddress(url, signal, dns) {
156
253
  assertHttpUrl(url);
157
254
  const host = url.hostname.replace(/^\[|\]$/g, "");
158
255
  if (!host)
@@ -169,7 +266,7 @@ async function resolvePublicAddress(url, signal) {
169
266
  if (host === "localhost" || host.endsWith(".local") || host.endsWith(".internal")) {
170
267
  throw new PrivateAddressRefusal("refusing to fetch an internal host");
171
268
  }
172
- const pending = lookup(host, { all: true });
269
+ const pending = (dns ?? defaultLookup)(host, { all: true });
173
270
  const records = signal ? await raceAbort(pending, signal) : await pending;
174
271
  for (const record of records) {
175
272
  if (isPrivateIp(record.address)) {
@@ -202,61 +299,24 @@ async function fetchPinnedDocument(start, options) {
202
299
  throw new Error("document URL cannot contain credentials");
203
300
  }
204
301
  const resolved = await resolvePublicAddress(url, signal);
205
- const transport = url.protocol === "https:" ? httpsRequest : httpRequest;
206
- const response = await new Promise((resolve, reject) => {
207
- const request = transport({
208
- protocol: url.protocol,
209
- hostname: resolved.address,
210
- family: resolved.family,
211
- port: url.port || undefined,
212
- path: `${url.pathname}${url.search}`,
213
- method: "GET",
214
- signal,
215
- servername: url.hostname,
216
- headers: {
217
- host: url.host,
218
- accept: "application/json",
219
- ...options.headers
220
- }
221
- }, (incoming) => {
222
- const headers = new Headers;
223
- for (const [name, value] of Object.entries(incoming.headers)) {
224
- if (Array.isArray(value)) {
225
- for (const item of value)
226
- headers.append(name, item);
227
- } else if (value !== undefined) {
228
- headers.set(name, value);
229
- }
230
- }
231
- const chunks = [];
232
- let total = 0;
233
- incoming.on("data", (chunk) => {
234
- total += chunk.byteLength;
235
- if (total > options.maxBytes) {
236
- incoming.destroy(new Error("response body exceeds configured byte limit"));
237
- return;
238
- }
239
- chunks.push(chunk);
240
- });
241
- incoming.once("error", reject);
242
- incoming.once("end", () => {
243
- const body = new Uint8Array(total);
244
- let offset = 0;
245
- for (const chunk of chunks) {
246
- body.set(chunk, offset);
247
- offset += chunk.byteLength;
248
- }
249
- resolve({ status: incoming.statusCode ?? 0, headers, body, url });
250
- });
251
- });
252
- request.once("error", reject);
253
- request.end();
302
+ const response = await requestPinned(url, resolved, signal, {
303
+ accept: "application/json",
304
+ ...options.headers
254
305
  });
306
+ const body = await readCapped(response, options.maxBytes);
307
+ if (body === null)
308
+ throw new Error("response body exceeds configured byte limit");
309
+ const document = {
310
+ status: response.status,
311
+ headers: response.headers,
312
+ body: new Uint8Array(body),
313
+ url
314
+ };
255
315
  if (response.status < 300 || response.status >= 400)
256
- return response;
316
+ return document;
257
317
  const location = response.headers.get("location");
258
318
  if (!location)
259
- return response;
319
+ return document;
260
320
  url = new URL(location, url);
261
321
  }
262
322
  throw new Error("too many redirects");
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  loadDarwinBinding
3
3
  } from "./index-2kbysk9s.js";
4
+ import {
5
+ errnoOf
6
+ } from "./index-jj9228tt.js";
4
7
  import {
5
8
  isRecord
6
9
  } from "./index-9jfdm5tg.js";
@@ -126,7 +129,7 @@ async function observeProcessInstanceAt(pid, procRoot) {
126
129
  readlink(`${procRoot}/self/ns/pid`),
127
130
  readFile2(`${procRoot}/self/status`, "utf8"),
128
131
  readlink(`${procRoot}/self/ns/time`).catch((error) => {
129
- if (error instanceof Error && "code" in error && error.code === "ENOENT")
132
+ if (errnoOf(error) === "ENOENT")
130
133
  return "";
131
134
  throw error;
132
135
  }),
@@ -0,0 +1,107 @@
1
+ import {
2
+ openDirectoryAt,
3
+ openPinnedDirectory,
4
+ openFileAt,
5
+ listAt,
6
+ readContainedUtf8Handle
7
+ } from "./index-x0ddbj85.js";
8
+
9
+ // src/agent-runtime/contained-scan.ts
10
+ import path from "node:path";
11
+ async function walkContainedFiles(input) {
12
+ const scan = await scanContainedFiles({ ...input, symlinks: "refuse" });
13
+ if (scan.truncated)
14
+ throw new Error("Contained file traversal exceeded its bounds");
15
+ return scan.files;
16
+ }
17
+ async function scanContainedFiles(input) {
18
+ const files = [];
19
+ let truncated = false;
20
+ let skippedDirectories = 0;
21
+ let skippedSymlinks = 0;
22
+ const denied = [];
23
+ let deniedTruncated = false;
24
+ const root = await openPinnedDirectory(input.root);
25
+ const visit = async (directory, relativeDirectory, depth) => {
26
+ if (truncated)
27
+ return;
28
+ if (depth > input.maxDepth) {
29
+ truncated = true;
30
+ return;
31
+ }
32
+ const entries = [...await listAt(directory)];
33
+ entries.sort((left, right) => left.name.localeCompare(right.name));
34
+ for (const entry of entries) {
35
+ const relative = relativeDirectory ? path.join(relativeDirectory, entry.name) : entry.name;
36
+ const kind = entry.isDirectory() ? "directory" : entry.isSymbolicLink() ? "symlink" : entry.isFile() ? "file" : "other";
37
+ if (kind === "file" && input.includeFile && !input.includeFile(relative))
38
+ continue;
39
+ if (input.authorizePath && !await input.authorizePath(relative, kind)) {
40
+ if (denied.length < input.maxFiles)
41
+ denied.push({ relative, kind });
42
+ else
43
+ deniedTruncated = true;
44
+ continue;
45
+ }
46
+ if (entry.isSymbolicLink()) {
47
+ if (input.symlinks === "refuse") {
48
+ throw new Error(`Contained file traversal refuses symlink: ${relative}`);
49
+ }
50
+ skippedSymlinks += 1;
51
+ continue;
52
+ }
53
+ if (entry.isDirectory()) {
54
+ if (input.excludeDirectory?.(relative)) {
55
+ skippedDirectories += 1;
56
+ continue;
57
+ }
58
+ let child;
59
+ try {
60
+ child = await openDirectoryAt(directory, entry.name);
61
+ } catch (error) {
62
+ if (input.symlinks === "skip") {
63
+ skippedSymlinks += 1;
64
+ continue;
65
+ }
66
+ throw error;
67
+ }
68
+ try {
69
+ await visit(child, relative, depth + 1);
70
+ } finally {
71
+ await child.close();
72
+ }
73
+ continue;
74
+ }
75
+ if (!entry.isFile())
76
+ continue;
77
+ if (files.length >= input.maxFiles) {
78
+ truncated = true;
79
+ return;
80
+ }
81
+ let content;
82
+ if (input.readMaxBytes !== undefined) {
83
+ try {
84
+ const handle = await openFileAt(directory, entry.name);
85
+ try {
86
+ content = await readContainedUtf8Handle(handle, input.readMaxBytes);
87
+ } finally {
88
+ await handle.close();
89
+ }
90
+ } catch (error) {
91
+ if (input.skipUnreadable)
92
+ continue;
93
+ throw error;
94
+ }
95
+ }
96
+ files.push({ absolute: relative, relative, ...content && { content } });
97
+ }
98
+ };
99
+ try {
100
+ await visit(root, "", 0);
101
+ } finally {
102
+ await root.close();
103
+ }
104
+ return { files, truncated, skippedDirectories, skippedSymlinks, denied, deniedTruncated };
105
+ }
106
+
107
+ export { walkContainedFiles, scanContainedFiles };
@@ -1,3 +1,6 @@
1
+ import {
2
+ errnoOf
3
+ } from "./index-jj9228tt.js";
1
4
  import {
2
5
  assertPositiveSafeInteger
3
6
  } from "./index-0s4xebbg.js";
@@ -21,7 +24,7 @@ function isAtomicStagingName2(name) {
21
24
  return NAME.test(name);
22
25
  }
23
26
  function isMissing(error) {
24
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
27
+ return errnoOf(error) === "ENOENT";
25
28
  }
26
29
  async function sweepAtomicStaging2(options) {
27
30
  const { directory, olderThanMs, signal } = options;