toolcraft 0.0.766 → 0.0.768
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/composition.json +2 -2
- package/dist/composition.json +2 -2
- package/node_modules/@poe-code/safe-fs/README.md +1 -1
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/filesystem.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/filesystem.js +53 -14
- package/node_modules/@poe-code/safe-fs/dist/fs/scoped.js +1 -1
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -2
package/composition.json
CHANGED
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
"name": "toolcraft",
|
|
136
|
-
"version": "0.0.
|
|
136
|
+
"version": "0.0.768",
|
|
137
137
|
"license": "MIT"
|
|
138
138
|
},
|
|
139
139
|
{
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
"name": "toolcraft-schema",
|
|
146
|
-
"version": "0.0.
|
|
146
|
+
"version": "0.0.768",
|
|
147
147
|
"license": "MIT"
|
|
148
148
|
},
|
|
149
149
|
{
|
package/dist/composition.json
CHANGED
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
"name": "toolcraft",
|
|
136
|
-
"version": "0.0.
|
|
136
|
+
"version": "0.0.768",
|
|
137
137
|
"license": "MIT"
|
|
138
138
|
},
|
|
139
139
|
{
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
"name": "toolcraft-schema",
|
|
146
|
-
"version": "0.0.
|
|
146
|
+
"version": "0.0.768",
|
|
147
147
|
"license": "MIT"
|
|
148
148
|
},
|
|
149
149
|
{
|
|
@@ -86,7 +86,7 @@ cause. Preserve cancellation/control exceptions separately. Cross-family
|
|
|
86
86
|
| Change the namespace | `mkdir`, `rm`, `rename`, `copyFile` |
|
|
87
87
|
| Remove an empty directory | Optional `rmdir`; never a recursive-delete fallback |
|
|
88
88
|
| Links and metadata | Optional `readlink`, `symlink`, `link`, `chmod`, `utimes`, `truncate` |
|
|
89
|
-
| Enumerate directories lazily | Optional `iterateDirectory`; memory and
|
|
89
|
+
| Enumerate directories lazily | Optional `iterateDirectory`; memory, native and paged S3 backends, with mount/overlay/readonly/quota/device/scoped delegation; backend order, no sorted-array fallback |
|
|
90
90
|
| Stream bytes | Optional `readStream`, `writeStream`, using async iterables of byte chunks |
|
|
91
91
|
| Retain an open file | Optional `open` with positioned I/O and synchronization; memory and real backends support `noFollow: true` to atomically refuse a final symlink |
|
|
92
92
|
| Publish immutable objects atomically | Optional `publishFileConditional` with opaque identity/version stats and authoritative compare-and-publish |
|
|
@@ -109,6 +109,7 @@ export declare class S3FileSystem implements FileSystem {
|
|
|
109
109
|
appendFile(path: string, data: Uint8Array, options?: AppendFileOptions): Promise<void>;
|
|
110
110
|
mkdir(input: string, options?: MkdirOptions): Promise<void>;
|
|
111
111
|
readdir(input: string, options?: ReadDirectoryOptions): Promise<DirectoryEntry[]>;
|
|
112
|
+
iterateDirectory(input: string, options?: FsOptions): AsyncIterable<DirectoryEntry>;
|
|
112
113
|
private tree;
|
|
113
114
|
rmdir(input: string, options?: FsOptions): Promise<void>;
|
|
114
115
|
rm(input: string, options?: RemoveOptions): Promise<void>;
|
|
@@ -341,25 +341,33 @@ export class S3FileSystem {
|
|
|
341
341
|
if (key.endsWith("/") && object.Size !== 0)
|
|
342
342
|
fail("ENOTSUP", "listObjectsV2", path, "nonempty slash-suffixed objects cannot be treated as directories");
|
|
343
343
|
}
|
|
344
|
-
async *pages(prefix, path, options, delimiter, maxKeys = this.pageSize) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
344
|
+
async *pages(prefix, path, options, delimiter, maxKeys = this.pageSize, rememberTokens = true) {
|
|
345
|
+
// Buffered callers preserve exact repeated-token rejection. Lazy traversal
|
|
346
|
+
// detects continuation cycles without retaining the whole token history.
|
|
347
|
+
const tokens = rememberTokens ? new Set() : undefined;
|
|
348
|
+
let token, checkpoint;
|
|
349
|
+
let entries = 0, pageCount = 0, span = 0, power = 1;
|
|
348
350
|
do {
|
|
349
351
|
const page = await this.page(prefix, path, options, delimiter, token, maxKeys);
|
|
350
352
|
entries += (page.Contents?.length ?? 0) + (page.CommonPrefixes?.length ?? 0);
|
|
351
|
-
if (entries > this.maxListEntries ||
|
|
353
|
+
if (entries > this.maxListEntries || ++pageCount > this.maxListEntries)
|
|
352
354
|
fail("EFBIG", "listObjectsV2", path, "listing exceeds maxListEntries");
|
|
355
|
+
const truncated = page.IsTruncated, next = page.NextContinuationToken;
|
|
353
356
|
yield page;
|
|
354
|
-
if (!
|
|
357
|
+
if (!truncated)
|
|
355
358
|
return;
|
|
356
|
-
token
|
|
357
|
-
if (typeof token !== "string" || !token || tokens.has(token))
|
|
359
|
+
if (typeof next !== "string" || !next || next === token || next === checkpoint || tokens?.has(next))
|
|
358
360
|
fail("EIO", "listObjectsV2", path, "missing or repeated continuation token");
|
|
359
|
-
|
|
361
|
+
if (++span === power) {
|
|
362
|
+
checkpoint = next;
|
|
363
|
+
span = 0;
|
|
364
|
+
power = Math.min(Number.MAX_SAFE_INTEGER, power * 2);
|
|
365
|
+
}
|
|
366
|
+
tokens?.add(next);
|
|
367
|
+
token = next;
|
|
360
368
|
} while (true);
|
|
361
369
|
}
|
|
362
|
-
async inspect(path, options) {
|
|
370
|
+
async inspect(path, options, rememberTokens = true) {
|
|
363
371
|
if (path === "/") {
|
|
364
372
|
const listing = await this.page(this.prefix, path, options, undefined, undefined, 1);
|
|
365
373
|
const marker = this.prefix && listing.Contents?.some(object => object.Key === this.prefix)
|
|
@@ -375,7 +383,7 @@ export class S3FileSystem {
|
|
|
375
383
|
const directoryKey = this.directoryKey(path);
|
|
376
384
|
let directory = false;
|
|
377
385
|
let hasMarker = false;
|
|
378
|
-
for await (const children of this.pages(directoryKey, path, options, undefined, 1)) {
|
|
386
|
+
for await (const children of this.pages(directoryKey, path, options, undefined, 1, rememberTokens)) {
|
|
379
387
|
if ((children.Contents?.length ?? 0) > 0) {
|
|
380
388
|
directory = true;
|
|
381
389
|
hasMarker = children.Contents.some(object => object.Key === directoryKey);
|
|
@@ -393,7 +401,7 @@ export class S3FileSystem {
|
|
|
393
401
|
return { stat: this.makeStat("directory", marker), ...(marker ? { metadata: marker } : {}) };
|
|
394
402
|
return undefined;
|
|
395
403
|
}
|
|
396
|
-
async lookup(path, options) {
|
|
404
|
+
async lookup(path, options, rememberTokens = true) {
|
|
397
405
|
const ancestors = [];
|
|
398
406
|
for (let parent = posix.dirname(path); path !== "/"; parent = posix.dirname(parent)) {
|
|
399
407
|
ancestors.unshift(parent);
|
|
@@ -403,13 +411,13 @@ export class S3FileSystem {
|
|
|
403
411
|
for (const parent of ancestors) {
|
|
404
412
|
if (parent === "/" && this.prefix === "")
|
|
405
413
|
continue;
|
|
406
|
-
const ancestor = await this.inspect(parent, options);
|
|
414
|
+
const ancestor = await this.inspect(parent, options, rememberTokens);
|
|
407
415
|
if (!ancestor)
|
|
408
416
|
fail("ENOENT", "stat", parent);
|
|
409
417
|
if (ancestor.stat.type !== "directory")
|
|
410
418
|
fail("ENOTDIR", "stat", parent);
|
|
411
419
|
}
|
|
412
|
-
return this.inspect(path, options);
|
|
420
|
+
return this.inspect(path, options, rememberTokens);
|
|
413
421
|
}
|
|
414
422
|
requireDirectorySuffix(input, info) {
|
|
415
423
|
if (input.endsWith("/") && info?.stat.type === "file")
|
|
@@ -596,6 +604,37 @@ export class S3FileSystem {
|
|
|
596
604
|
}
|
|
597
605
|
return [...entries.values()].sort((left, right) => compareKeys(left.name, right.name));
|
|
598
606
|
}
|
|
607
|
+
async *iterateDirectory(input, options = {}) {
|
|
608
|
+
const path = this.path(input);
|
|
609
|
+
const directory = await this.lookup(path, options, false);
|
|
610
|
+
if (!directory)
|
|
611
|
+
fail("ENOENT", "iterateDirectory", path);
|
|
612
|
+
if (directory.stat.type !== "directory")
|
|
613
|
+
fail("ENOTDIR", "iterateDirectory", path);
|
|
614
|
+
const prefix = this.directoryKey(path);
|
|
615
|
+
for await (const page of this.pages(prefix, path, options, "/", this.pageSize, false)) {
|
|
616
|
+
// Snapshot one bounded service page before yielding to caller code.
|
|
617
|
+
if ((page.Contents?.length ?? 0) + (page.CommonPrefixes?.length ?? 0) > this.pageSize)
|
|
618
|
+
fail("EIO", "iterateDirectory", path, "transport exceeded requested page size");
|
|
619
|
+
const entries = [];
|
|
620
|
+
for (const item of page.Contents ?? [])
|
|
621
|
+
if (item.Key !== prefix)
|
|
622
|
+
entries.push({ name: item.Key.slice(prefix.length), type: "file" });
|
|
623
|
+
for (const item of page.CommonPrefixes ?? [])
|
|
624
|
+
entries.push({ name: item.Prefix.slice(prefix.length, -1), type: "directory" });
|
|
625
|
+
for (const entry of entries) {
|
|
626
|
+
this.checkAbort(options, "iterateDirectory", path);
|
|
627
|
+
if (!entry.name || entry.name.includes("/"))
|
|
628
|
+
fail("EIO", "iterateDirectory", path, "invalid direct child in delimited listing");
|
|
629
|
+
// Revalidation preserves file/prefix collision detection across pages
|
|
630
|
+
// and follows the backend's live namespace without retaining all names.
|
|
631
|
+
const child = await this.lookup(path === "/" ? `/${entry.name}` : `${path}/${entry.name}`, options, false);
|
|
632
|
+
this.checkAbort(options, "iterateDirectory", path);
|
|
633
|
+
if (child)
|
|
634
|
+
yield { name: entry.name, type: child.stat.type };
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
599
638
|
async tree(path, options) {
|
|
600
639
|
const objects = new Map();
|
|
601
640
|
for await (const page of this.pages(this.directoryKey(path), path, options)) {
|
|
@@ -444,7 +444,7 @@ export function scopeFileSystem(filesystem, charge, signal, cleanupCharge = char
|
|
|
444
444
|
return executeDispatch(args);
|
|
445
445
|
if (operations.has(property)) {
|
|
446
446
|
const optionIndex = ["compareEntry", "createStagedFile", "utimes"].includes(String(property)) ? 3
|
|
447
|
-
: ["writeFile", "appendFile", "writeStream", "rename", "copyFile", "link", "symlink", "access", "chmod", "truncate", "resizeFile", "publishFileConditional", "writeFileConditional", "publishStagedFile"].includes(String(property)) ? 2 : 1;
|
|
447
|
+
: ["writeFile", "appendFile", "writeStream", "rename", "copyFile", "link", "symlink", "access", "chmod", "truncate", "resizeFile", "publishFileConditional", "writeFileConditional", "publishStagedFile", "publishStagedFileSet"].includes(String(property)) ? 2 : 1;
|
|
448
448
|
args[optionIndex] = scopeTransportOptions((args[optionIndex] ?? {}), admit, credit);
|
|
449
449
|
}
|
|
450
450
|
return runScopedTransportBudget(admit, () => executeDispatch(args), credit);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.768",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
"@poe-code/terminal-text"
|
|
176
176
|
],
|
|
177
177
|
"optionalDependencies": {
|
|
178
|
-
"toolcraft-schema": "0.0.
|
|
178
|
+
"toolcraft-schema": "0.0.768",
|
|
179
179
|
"toolcraft-design": "*",
|
|
180
180
|
"@poe-code/frontmatter": "*",
|
|
181
181
|
"@poe-code/agent-mcp-config": "*",
|