@j0hanz/filesystem-mcp 2.1.2 → 2.1.4
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/README.md +1 -1
- package/dist/cli-help.js +1 -1
- package/dist/cli-help.js.map +1 -1
- package/dist/core/concurrency.d.ts +2 -2
- package/dist/core/cursor.d.ts +47 -10
- package/dist/core/cursor.d.ts.map +1 -1
- package/dist/core/cursor.js +36 -1
- package/dist/core/cursor.js.map +1 -1
- package/dist/core/fmt.d.ts +18 -0
- package/dist/core/fmt.d.ts.map +1 -1
- package/dist/core/fmt.js +35 -0
- package/dist/core/fmt.js.map +1 -1
- package/dist/tools/batch.d.ts +20 -0
- package/dist/tools/batch.d.ts.map +1 -1
- package/dist/tools/batch.js +17 -0
- package/dist/tools/batch.js.map +1 -1
- package/dist/tools/create.js +3 -3
- package/dist/tools/create.js.map +1 -1
- package/dist/tools/define.d.ts +6 -0
- package/dist/tools/define.d.ts.map +1 -1
- package/dist/tools/define.js +1 -29
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/delete-file.d.ts.map +1 -1
- package/dist/tools/delete-file.js +4 -1
- package/dist/tools/delete-file.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +2 -1
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/list.d.ts.map +1 -1
- package/dist/tools/list.js +54 -66
- package/dist/tools/list.js.map +1 -1
- package/dist/tools/move.d.ts.map +1 -1
- package/dist/tools/move.js +5 -2
- package/dist/tools/move.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +10 -7
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/replace-in-files.d.ts.map +1 -1
- package/dist/tools/replace-in-files.js +4 -2
- package/dist/tools/replace-in-files.js.map +1 -1
- package/dist/tools/search-content.d.ts.map +1 -1
- package/dist/tools/search-content.js +74 -92
- package/dist/tools/search-content.js.map +1 -1
- package/dist/tools/search-files.d.ts.map +1 -1
- package/dist/tools/search-files.js +56 -62
- package/dist/tools/search-files.js.map +1 -1
- package/dist/tools/stat.d.ts.map +1 -1
- package/dist/tools/stat.js +2 -1
- package/dist/tools/stat.js.map +1 -1
- package/dist/{http-policy.d.ts → transport/http-policy.d.ts} +4 -4
- package/dist/transport/http-policy.d.ts.map +1 -0
- package/dist/{http-policy.js → transport/http-policy.js} +13 -9
- package/dist/transport/http-policy.js.map +1 -0
- package/dist/transport/http.js +1 -1
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/shared.d.ts +14 -0
- package/dist/transport/shared.d.ts.map +1 -1
- package/dist/transport/shared.js +13 -5
- package/dist/transport/shared.js.map +1 -1
- package/dist/transport/stdio.d.ts.map +1 -1
- package/dist/transport/stdio.js +4 -12
- package/dist/transport/stdio.js.map +1 -1
- package/package.json +1 -1
- package/dist/http-policy.d.ts.map +0 -1
- package/dist/http-policy.js.map +0 -1
package/README.md
CHANGED
|
@@ -402,7 +402,7 @@ Flags take precedence when both are set.
|
|
|
402
402
|
| `FS_MAX_REQUEST_BYTES` | Max HTTP request body bytes (default 4194304, 1024–268435456). |
|
|
403
403
|
| `FS_KEEPALIVE_TIMEOUT_MS` | HTTP keep-alive timeout in ms; set above any fronting proxy's idle timeout (default 5000, 1000–600000). |
|
|
404
404
|
| `FS_MAX_WATCHERS` | Max concurrent file watchers (default 256, 1–4096). |
|
|
405
|
-
| `FS_MAX_INLINE_MATCHES` |
|
|
405
|
+
| `FS_MAX_INLINE_MATCHES` | Deprecated and ignored; `maxResults` sets the `search_text` page size. Logs a warning when set; removed in the next major. |
|
|
406
406
|
| `FS_MAX_READ_MANY_BYTES` | Max total bytes across a batched `read` (default 524288, 10240–104857600). |
|
|
407
407
|
| `FS_SEARCH_TIMEOUT_MS` | Search timeout in ms (default 5000, 100–60000). |
|
|
408
408
|
| `NO_COLOR` | Any value disables ANSI color output. |
|
package/dist/cli-help.js
CHANGED
|
@@ -110,7 +110,7 @@ const ENV_HELP = [
|
|
|
110
110
|
},
|
|
111
111
|
{
|
|
112
112
|
flags: 'FS_MAX_INLINE_MATCHES',
|
|
113
|
-
desc: '
|
|
113
|
+
desc: 'Deprecated and ignored; maxResults sets the search_text page size. Removed in the next major',
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
116
|
flags: 'FS_MAX_READ_MANY_BYTES',
|
package/dist/cli-help.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-help.js","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"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","sourcesContent":["import packageJson from '../package.json' with { type: 'json' };\nimport { cliFmt, padEndVisible } from './core/fmt.js';\nimport { MUTATING_TOOL_NAMES } from './tools/index.js';\n\n// Help text and its rendering. Split out of cli.ts so that module holds only\n// argument parsing and directory validation: this file is static copy plus a\n// formatter, and shares no state with the parser.\n\nconst { version: SERVER_VERSION } = packageJson;\n\ninterface HelpRow {\n flags: string;\n desc: string;\n}\n\nconst OPTIONS_HELP: HelpRow[] = [\n { flags: '-h, --help', desc: 'Show this help message' },\n { flags: '-v, --version', desc: 'Show the server version' },\n { flags: '--allow-cwd', desc: 'Add the current working directory as an allowed root' },\n {\n flags: '--port <number>',\n desc: 'Start HTTP transport on this port (env: FS_PORT)',\n },\n {\n flags: '--read-only',\n desc: `Disable write tools: ${[...MUTATING_TOOL_NAMES].sort().join(', ')}`,\n },\n { flags: '--safe', desc: 'Alias for --read-only' },\n {\n flags: '--print-config',\n desc: 'Print the active configuration and exit (use with --json for machine output)',\n },\n { flags: '--json', desc: 'Output --print-config as JSON' },\n {\n flags: '--log-level <level>',\n desc: 'Log level, RFC 5424: debug|info|notice|warn|error|critical|alert|emergency (env: FS_LOG_LEVEL)',\n },\n { flags: '--http-host <host>', desc: 'HTTP server bind address (env: FS_HTTP_HOST)' },\n {\n flags: '--api-key <key>',\n desc: 'Require this API key on HTTP requests; prefer FS_API_KEY (argv is world-readable)',\n },\n {\n flags: '--allow-sensitive',\n desc: 'Allow access to sensitive system paths (env: FS_ALLOW_SENSITIVE)',\n },\n {\n flags: '--root-boundary <path>',\n desc: 'Require all allowed roots to fall under this path (env: FS_ROOT_BOUNDARY)',\n },\n {\n flags: '--max-file-size <bytes>',\n desc: 'Maximum file size for reads in bytes (env: FS_MAX_FILE_SIZE)',\n },\n { flags: '--walk-cwd', desc: 'Walk up from CWD to find a project root; implies --allow-cwd' },\n { flags: '--deny <pattern>', desc: 'Block paths matching this pattern; repeatable' },\n {\n flags: '--allow-missing-roots',\n desc: 'Start even if configured allowed directories do not exist',\n },\n];\n\nconst ENV_HELP: HelpRow[] = [\n {\n flags: 'FS_LOG_LEVEL',\n desc: 'Log level, RFC 5424: debug|info|notice|warn|error|critical|alert|emergency',\n },\n { flags: 'FS_PORT', desc: 'Start HTTP transport on this port (unset = stdio)' },\n { flags: 'FS_HTTP_HOST', desc: 'HTTP bind address' },\n { flags: 'FS_API_KEY', desc: 'HTTP API key' },\n {\n flags: 'FS_TRUST_PROXY',\n desc: 'Express trust-proxy setting: hop count or expression (unset = do not trust X-Forwarded-*)',\n },\n {\n flags: 'FS_ALLOW_SENSITIVE',\n desc: 'Allow sensitive system paths (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_ROOT_BOUNDARY', desc: 'Path prefix all allowed roots must fall under' },\n { flags: 'FS_MAX_FILE_SIZE', desc: 'Maximum file size for reads in bytes' },\n {\n flags: 'FS_ALLOWED_DIRS',\n desc: 'Allowed dirs: colon-separated (Unix), semicolon-separated (Windows)',\n },\n {\n flags: 'FS_ALLOW_CWD_WALK',\n desc: 'Walk up from CWD to find a project root (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_DENYLIST', desc: 'Paths/patterns to block, comma-separated' },\n {\n flags: 'FS_ALLOW_MISSING_ROOTS',\n desc: 'Start even if configured allowed directories do not exist (\"true\" or \"1\" enables this)',\n },\n {\n flags: 'FS_ALLOWED_HOSTS',\n desc: 'Comma-separated Host header values to accept (HTTP transport)',\n },\n { flags: 'FS_ALLOWED_ORIGINS', desc: 'Comma-separated origin hostnames for CORS' },\n {\n flags: 'FS_ALLOW_UNRESTRICTED_HOSTS',\n desc: 'Bind a wildcard host with no Host validation, accepts the risk (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_PUBLIC_URL', desc: 'Resource identifier URL for RFC 9728 discovery' },\n {\n flags: 'FS_RATE_LIMIT_RPM',\n desc: 'Per-client-IP requests/min (default 120 authenticated, 6000 keyless loopback; 1–100000)',\n },\n {\n flags: 'FS_MAX_REQUEST_BYTES',\n desc: 'Max HTTP request body bytes (default 4194304, 1024–268435456)',\n },\n {\n flags: 'FS_KEEPALIVE_TIMEOUT_MS',\n desc: 'HTTP keep-alive timeout; set above any fronting proxy idle timeout (default 5000, 1000–600000)',\n },\n {\n flags: 'FS_MAX_WATCHERS',\n desc: 'Max concurrent file watchers (default 256, 1–4096)',\n },\n {\n flags: 'FS_MAX_INLINE_MATCHES',\n desc: 'Max inline content matches per search (default 50, 1–10000)',\n },\n {\n flags: 'FS_MAX_READ_MANY_BYTES',\n desc: 'Max total bytes across one batch read (default 524288, 10240–104857600)',\n },\n { flags: 'FS_SEARCH_TIMEOUT_MS', desc: 'Search timeout in ms (default 5000, 100–60000)' },\n { flags: 'NO_COLOR', desc: 'Any value disables ANSI color output' },\n {\n flags: 'FS_REQUEST_STATE_KEY',\n desc: 'HMAC key for input_required state; optional outside fleet mode, shared and >=32 bytes in fleet mode',\n },\n];\n\nconst EXAMPLES_HELP = [\n '$ filesystem-mcp /path/to/allowed/dir',\n '$ filesystem-mcp --allow-cwd',\n '$ filesystem-mcp /project/src /project/tests --allow-cwd',\n '$ filesystem-mcp --port 3000 /path/to/allowed/dir',\n '$ filesystem-mcp --read-only /data/readonly',\n '$ filesystem-mcp --print-config --json /project',\n];\n\nexport function printHelpAndExit(): never {\n const { bold, dim, section, flag, placeholder, cyan } = cliFmt;\n const COL = 27;\n\n const optRow = (flags: string, desc: string): string => {\n const colored = flags\n .replace(/<[^>]+>/g, (m) => placeholder(m))\n .replace(/-{1,2}[\\w-]+/g, (m) => flag(m));\n return ` ${padEndVisible(colored, COL)}${desc}`;\n };\n\n const envRow = (name: string, desc: string): string => {\n return ` ${padEndVisible(cyan(name), COL - 1)} ${desc}`;\n };\n\n const lines = [\n '',\n `${bold('Filesystem MCP')} ${dim(`v${SERVER_VERSION}`)}`,\n '',\n dim('Pass one or more directories to set the allowed access roots.'),\n '',\n section('Options:'),\n ...OPTIONS_HELP.map(({ flags, desc }) => optRow(flags, desc)),\n '',\n `${section('Environment variables:')} ${dim('(flags take precedence when both are set)')}`,\n ...ENV_HELP.map(({ flags, desc }) => envRow(flags, desc)),\n '',\n section('Examples:'),\n ...EXAMPLES_HELP.map((ex) => ` ${dim(ex)}`),\n '',\n ];\n\n process.stdout.write(lines.join('\\n'));\n process.exit(0);\n}\n\nexport function printVersionAndExit(): never {\n process.stdout.write(`${cliFmt.cyan(SERVER_VERSION)}\\n`);\n process.exit(0);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cli-help.js","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"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","sourcesContent":["import packageJson from '../package.json' with { type: 'json' };\nimport { cliFmt, padEndVisible } from './core/fmt.js';\nimport { MUTATING_TOOL_NAMES } from './tools/index.js';\n\n// Help text and its rendering. Split out of cli.ts so that module holds only\n// argument parsing and directory validation: this file is static copy plus a\n// formatter, and shares no state with the parser.\n\nconst { version: SERVER_VERSION } = packageJson;\n\ninterface HelpRow {\n flags: string;\n desc: string;\n}\n\nconst OPTIONS_HELP: HelpRow[] = [\n { flags: '-h, --help', desc: 'Show this help message' },\n { flags: '-v, --version', desc: 'Show the server version' },\n { flags: '--allow-cwd', desc: 'Add the current working directory as an allowed root' },\n {\n flags: '--port <number>',\n desc: 'Start HTTP transport on this port (env: FS_PORT)',\n },\n {\n flags: '--read-only',\n desc: `Disable write tools: ${[...MUTATING_TOOL_NAMES].sort().join(', ')}`,\n },\n { flags: '--safe', desc: 'Alias for --read-only' },\n {\n flags: '--print-config',\n desc: 'Print the active configuration and exit (use with --json for machine output)',\n },\n { flags: '--json', desc: 'Output --print-config as JSON' },\n {\n flags: '--log-level <level>',\n desc: 'Log level, RFC 5424: debug|info|notice|warn|error|critical|alert|emergency (env: FS_LOG_LEVEL)',\n },\n { flags: '--http-host <host>', desc: 'HTTP server bind address (env: FS_HTTP_HOST)' },\n {\n flags: '--api-key <key>',\n desc: 'Require this API key on HTTP requests; prefer FS_API_KEY (argv is world-readable)',\n },\n {\n flags: '--allow-sensitive',\n desc: 'Allow access to sensitive system paths (env: FS_ALLOW_SENSITIVE)',\n },\n {\n flags: '--root-boundary <path>',\n desc: 'Require all allowed roots to fall under this path (env: FS_ROOT_BOUNDARY)',\n },\n {\n flags: '--max-file-size <bytes>',\n desc: 'Maximum file size for reads in bytes (env: FS_MAX_FILE_SIZE)',\n },\n { flags: '--walk-cwd', desc: 'Walk up from CWD to find a project root; implies --allow-cwd' },\n { flags: '--deny <pattern>', desc: 'Block paths matching this pattern; repeatable' },\n {\n flags: '--allow-missing-roots',\n desc: 'Start even if configured allowed directories do not exist',\n },\n];\n\nconst ENV_HELP: HelpRow[] = [\n {\n flags: 'FS_LOG_LEVEL',\n desc: 'Log level, RFC 5424: debug|info|notice|warn|error|critical|alert|emergency',\n },\n { flags: 'FS_PORT', desc: 'Start HTTP transport on this port (unset = stdio)' },\n { flags: 'FS_HTTP_HOST', desc: 'HTTP bind address' },\n { flags: 'FS_API_KEY', desc: 'HTTP API key' },\n {\n flags: 'FS_TRUST_PROXY',\n desc: 'Express trust-proxy setting: hop count or expression (unset = do not trust X-Forwarded-*)',\n },\n {\n flags: 'FS_ALLOW_SENSITIVE',\n desc: 'Allow sensitive system paths (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_ROOT_BOUNDARY', desc: 'Path prefix all allowed roots must fall under' },\n { flags: 'FS_MAX_FILE_SIZE', desc: 'Maximum file size for reads in bytes' },\n {\n flags: 'FS_ALLOWED_DIRS',\n desc: 'Allowed dirs: colon-separated (Unix), semicolon-separated (Windows)',\n },\n {\n flags: 'FS_ALLOW_CWD_WALK',\n desc: 'Walk up from CWD to find a project root (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_DENYLIST', desc: 'Paths/patterns to block, comma-separated' },\n {\n flags: 'FS_ALLOW_MISSING_ROOTS',\n desc: 'Start even if configured allowed directories do not exist (\"true\" or \"1\" enables this)',\n },\n {\n flags: 'FS_ALLOWED_HOSTS',\n desc: 'Comma-separated Host header values to accept (HTTP transport)',\n },\n { flags: 'FS_ALLOWED_ORIGINS', desc: 'Comma-separated origin hostnames for CORS' },\n {\n flags: 'FS_ALLOW_UNRESTRICTED_HOSTS',\n desc: 'Bind a wildcard host with no Host validation, accepts the risk (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_PUBLIC_URL', desc: 'Resource identifier URL for RFC 9728 discovery' },\n {\n flags: 'FS_RATE_LIMIT_RPM',\n desc: 'Per-client-IP requests/min (default 120 authenticated, 6000 keyless loopback; 1–100000)',\n },\n {\n flags: 'FS_MAX_REQUEST_BYTES',\n desc: 'Max HTTP request body bytes (default 4194304, 1024–268435456)',\n },\n {\n flags: 'FS_KEEPALIVE_TIMEOUT_MS',\n desc: 'HTTP keep-alive timeout; set above any fronting proxy idle timeout (default 5000, 1000–600000)',\n },\n {\n flags: 'FS_MAX_WATCHERS',\n desc: 'Max concurrent file watchers (default 256, 1–4096)',\n },\n {\n flags: 'FS_MAX_INLINE_MATCHES',\n desc: 'Deprecated and ignored; maxResults sets the search_text page size. Removed in the next major',\n },\n {\n flags: 'FS_MAX_READ_MANY_BYTES',\n desc: 'Max total bytes across one batch read (default 524288, 10240–104857600)',\n },\n { flags: 'FS_SEARCH_TIMEOUT_MS', desc: 'Search timeout in ms (default 5000, 100–60000)' },\n { flags: 'NO_COLOR', desc: 'Any value disables ANSI color output' },\n {\n flags: 'FS_REQUEST_STATE_KEY',\n desc: 'HMAC key for input_required state; optional outside fleet mode, shared and >=32 bytes in fleet mode',\n },\n];\n\nconst EXAMPLES_HELP = [\n '$ filesystem-mcp /path/to/allowed/dir',\n '$ filesystem-mcp --allow-cwd',\n '$ filesystem-mcp /project/src /project/tests --allow-cwd',\n '$ filesystem-mcp --port 3000 /path/to/allowed/dir',\n '$ filesystem-mcp --read-only /data/readonly',\n '$ filesystem-mcp --print-config --json /project',\n];\n\nexport function printHelpAndExit(): never {\n const { bold, dim, section, flag, placeholder, cyan } = cliFmt;\n const COL = 27;\n\n const optRow = (flags: string, desc: string): string => {\n const colored = flags\n .replace(/<[^>]+>/g, (m) => placeholder(m))\n .replace(/-{1,2}[\\w-]+/g, (m) => flag(m));\n return ` ${padEndVisible(colored, COL)}${desc}`;\n };\n\n const envRow = (name: string, desc: string): string => {\n return ` ${padEndVisible(cyan(name), COL - 1)} ${desc}`;\n };\n\n const lines = [\n '',\n `${bold('Filesystem MCP')} ${dim(`v${SERVER_VERSION}`)}`,\n '',\n dim('Pass one or more directories to set the allowed access roots.'),\n '',\n section('Options:'),\n ...OPTIONS_HELP.map(({ flags, desc }) => optRow(flags, desc)),\n '',\n `${section('Environment variables:')} ${dim('(flags take precedence when both are set)')}`,\n ...ENV_HELP.map(({ flags, desc }) => envRow(flags, desc)),\n '',\n section('Examples:'),\n ...EXAMPLES_HELP.map((ex) => ` ${dim(ex)}`),\n '',\n ];\n\n process.stdout.write(lines.join('\\n'));\n process.exit(0);\n}\n\nexport function printVersionAndExit(): never {\n process.stdout.write(`${cliFmt.cyan(SERVER_VERSION)}\\n`);\n process.exit(0);\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as z from 'zod/v4';
|
|
2
2
|
export type StoppedReason = 'maxResults' | 'maxFiles' | 'timeout';
|
|
3
3
|
export declare const StoppedReasonSchema: z.ZodOptional<z.ZodEnum<{
|
|
4
|
-
timeout: "timeout";
|
|
5
4
|
maxResults: "maxResults";
|
|
5
|
+
timeout: "timeout";
|
|
6
6
|
maxFiles: "maxFiles";
|
|
7
7
|
}>>;
|
|
8
8
|
/**
|
|
@@ -12,8 +12,8 @@ export declare const StoppedReasonSchema: z.ZodOptional<z.ZodEnum<{
|
|
|
12
12
|
* both tools' own descriptions.
|
|
13
13
|
*/
|
|
14
14
|
export declare const SearchStoppedReasonSchema: z.ZodOptional<z.ZodEnum<{
|
|
15
|
-
timeout: "timeout";
|
|
16
15
|
maxResults: "maxResults";
|
|
16
|
+
timeout: "timeout";
|
|
17
17
|
}>>;
|
|
18
18
|
/**
|
|
19
19
|
* Accumulates why an enumeration stopped early. maxResults wins over maxFiles
|
package/dist/core/cursor.d.ts
CHANGED
|
@@ -5,19 +5,56 @@ export declare function createFirstPage<T, M>(params: {
|
|
|
5
5
|
items: readonly T[];
|
|
6
6
|
metadata: M;
|
|
7
7
|
pageSize: number;
|
|
8
|
-
}):
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
}): Page<T, M>;
|
|
9
|
+
/**
|
|
10
|
+
* The full result set a paged tool produced, before slicing. `truncated` is the
|
|
11
|
+
* engine's own stop state — the hard result cap or a timeout cut the set — and
|
|
12
|
+
* says nothing about page size.
|
|
13
|
+
*/
|
|
14
|
+
export interface ProducedPage<T, M> {
|
|
15
|
+
readonly items: readonly T[];
|
|
16
|
+
readonly metadata: M;
|
|
17
|
+
readonly truncated: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface Page<T, M> {
|
|
20
|
+
readonly page: readonly T[];
|
|
21
|
+
readonly metadata: M;
|
|
22
|
+
readonly nextCursor: string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Index of this page's first item in the full set. A tool that reports
|
|
25
|
+
* progress in its text needs it: without it every page can only say how many
|
|
26
|
+
* rows it holds, which is the same number on page 1 and page 9.
|
|
27
|
+
*/
|
|
28
|
+
readonly offset: number;
|
|
29
|
+
}
|
|
30
|
+
export interface PaginatedPage<T, M, R> extends Page<T, M> {
|
|
31
|
+
/** The externalized full set. First page only, and only when incomplete. */
|
|
32
|
+
readonly resource?: R;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The one replay-or-produce branch every paged tool runs. A cursor replays a
|
|
36
|
+
* stored snapshot; without one, `produce` runs the query and its result is
|
|
37
|
+
* snapshotted for later pages.
|
|
38
|
+
*
|
|
39
|
+
* The response is incomplete when the set spans more than one page or the
|
|
40
|
+
* engine truncated it. Then — and only on the first page — `externalize`
|
|
41
|
+
* stores the full set and its handle rides back as `resource`. Replayed pages
|
|
42
|
+
* never carry it: the stored entry expires on the resource store's own TTL and
|
|
43
|
+
* LRU, not this snapshot's, so replaying its URI would hand back a dead
|
|
44
|
+
* pointer. Nor do they mint a new one; the set was already stored whole.
|
|
45
|
+
*/
|
|
46
|
+
export declare function paginate<T, M, R>(params: {
|
|
47
|
+
store: PageSnapshotStore;
|
|
48
|
+
queryKey: string;
|
|
49
|
+
cursor: string | undefined;
|
|
50
|
+
pageSize: number;
|
|
51
|
+
produce: () => Promise<ProducedPage<T, M>>;
|
|
52
|
+
externalize?: ((items: readonly T[], metadata: M) => R) | undefined;
|
|
53
|
+
}): Promise<PaginatedPage<T, M, R>>;
|
|
13
54
|
export declare function readNextPage<T, M>(params: {
|
|
14
55
|
store: PageSnapshotStore;
|
|
15
56
|
queryKey: string;
|
|
16
57
|
cursor: string;
|
|
17
58
|
pageSize: number;
|
|
18
|
-
}):
|
|
19
|
-
page: readonly T[];
|
|
20
|
-
metadata: M;
|
|
21
|
-
nextCursor: string | undefined;
|
|
22
|
-
};
|
|
59
|
+
}): Page<T, M>;
|
|
23
60
|
//# sourceMappingURL=cursor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/core/cursor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/core/cursor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAgCvE,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE;IAC5C,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAkBb;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,EAAE,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAE,SAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IACxD,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE;IAC9C,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC;CACrE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAoBlC;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE;IACzC,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAIb"}
|
package/dist/core/cursor.js
CHANGED
|
@@ -11,6 +11,7 @@ function pageResult(snapshotId, offset, pageSize, snapshot) {
|
|
|
11
11
|
return {
|
|
12
12
|
page,
|
|
13
13
|
metadata: snapshot.metadata,
|
|
14
|
+
offset,
|
|
14
15
|
nextCursor: nextOffset < snapshot.items.length
|
|
15
16
|
? encodePageCursor({ snapshotId, offset: nextOffset })
|
|
16
17
|
: undefined,
|
|
@@ -22,6 +23,7 @@ export function createFirstPage(params) {
|
|
|
22
23
|
page: params.items,
|
|
23
24
|
metadata: params.metadata,
|
|
24
25
|
nextCursor: undefined,
|
|
26
|
+
offset: 0,
|
|
25
27
|
};
|
|
26
28
|
}
|
|
27
29
|
const snapshotId = params.store.create({
|
|
@@ -34,7 +36,40 @@ export function createFirstPage(params) {
|
|
|
34
36
|
metadata: params.metadata,
|
|
35
37
|
});
|
|
36
38
|
}
|
|
37
|
-
|
|
39
|
+
/**
|
|
40
|
+
* The one replay-or-produce branch every paged tool runs. A cursor replays a
|
|
41
|
+
* stored snapshot; without one, `produce` runs the query and its result is
|
|
42
|
+
* snapshotted for later pages.
|
|
43
|
+
*
|
|
44
|
+
* The response is incomplete when the set spans more than one page or the
|
|
45
|
+
* engine truncated it. Then — and only on the first page — `externalize`
|
|
46
|
+
* stores the full set and its handle rides back as `resource`. Replayed pages
|
|
47
|
+
* never carry it: the stored entry expires on the resource store's own TTL and
|
|
48
|
+
* LRU, not this snapshot's, so replaying its URI would hand back a dead
|
|
49
|
+
* pointer. Nor do they mint a new one; the set was already stored whole.
|
|
50
|
+
*/
|
|
51
|
+
export async function paginate(params) {
|
|
52
|
+
if (params.cursor !== undefined) {
|
|
53
|
+
return readNextPage({
|
|
54
|
+
store: params.store,
|
|
55
|
+
queryKey: params.queryKey,
|
|
56
|
+
cursor: params.cursor,
|
|
57
|
+
pageSize: params.pageSize,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const produced = await params.produce();
|
|
61
|
+
const incomplete = produced.items.length > params.pageSize || produced.truncated;
|
|
62
|
+
const first = createFirstPage({
|
|
63
|
+
store: params.store,
|
|
64
|
+
queryKey: params.queryKey,
|
|
65
|
+
items: produced.items,
|
|
66
|
+
metadata: produced.metadata,
|
|
67
|
+
pageSize: params.pageSize,
|
|
68
|
+
});
|
|
69
|
+
if (!incomplete || params.externalize === undefined)
|
|
70
|
+
return first;
|
|
71
|
+
return { ...first, resource: params.externalize(produced.items, produced.metadata) };
|
|
72
|
+
}
|
|
38
73
|
export function readNextPage(params) {
|
|
39
74
|
const decoded = decodePageCursor(params.cursor);
|
|
40
75
|
const snapshot = params.store.read(decoded.snapshotId, params.queryKey);
|
package/dist/core/cursor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/core/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,yBAAyB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAOhD,SAAS,gBAAgB,CAAC,MAAkB;IAC1C,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,UAAU,CACjB,UAAkB,EAClB,MAAc,EACd,QAAgB,EAChB,QAA4B;IAE5B,IAAI,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM;QAAE,MAAM,aAAa,EAAE,CAAC;IAC3D,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACxC,OAAO;QACL,IAAI;QACJ,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,UAAU,EACR,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM;YAChC,CAAC,CAAC,gBAAgB,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YACtD,CAAC,CAAC,SAAS;KAChB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAO,MAMrC;IACC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC3C,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,KAAK;YAClB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,UAAU,EAAE,SAAS;
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/core/cursor.ts"],"names":[],"mappings":"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","sourcesContent":["import { ErrorCode, formatUnknownErrorMessage, FsError } from './errors.js';\nimport type { PageSnapshot, PageSnapshotStore } from './page-store.js';\nimport { invalidCursor } from './page-store.js';\n\ninterface PageCursor {\n readonly snapshotId: string;\n readonly offset: number;\n}\n\nfunction encodePageCursor(cursor: PageCursor): string {\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction pageResult<T, M>(\n snapshotId: string,\n offset: number,\n pageSize: number,\n snapshot: PageSnapshot<T, M>,\n): Page<T, M> {\n if (offset >= snapshot.items.length) throw invalidCursor();\n const page = snapshot.items.slice(offset, offset + pageSize);\n const nextOffset = offset + page.length;\n return {\n page,\n metadata: snapshot.metadata,\n offset,\n nextCursor:\n nextOffset < snapshot.items.length\n ? encodePageCursor({ snapshotId, offset: nextOffset })\n : undefined,\n };\n}\n\nexport function createFirstPage<T, M>(params: {\n store: PageSnapshotStore;\n queryKey: string;\n items: readonly T[];\n metadata: M;\n pageSize: number;\n}): Page<T, M> {\n if (params.items.length <= params.pageSize) {\n return {\n page: params.items,\n metadata: params.metadata,\n nextCursor: undefined,\n offset: 0,\n };\n }\n const snapshotId = params.store.create({\n queryKey: params.queryKey,\n items: params.items,\n metadata: params.metadata,\n });\n return pageResult(snapshotId, 0, params.pageSize, {\n items: params.items,\n metadata: params.metadata,\n });\n}\n\n/**\n * The full result set a paged tool produced, before slicing. `truncated` is the\n * engine's own stop state — the hard result cap or a timeout cut the set — and\n * says nothing about page size.\n */\nexport interface ProducedPage<T, M> {\n readonly items: readonly T[];\n readonly metadata: M;\n readonly truncated: boolean;\n}\n\nexport interface Page<T, M> {\n readonly page: readonly T[];\n readonly metadata: M;\n readonly nextCursor: string | undefined;\n /**\n * Index of this page's first item in the full set. A tool that reports\n * progress in its text needs it: without it every page can only say how many\n * rows it holds, which is the same number on page 1 and page 9.\n */\n readonly offset: number;\n}\n\nexport interface PaginatedPage<T, M, R> extends Page<T, M> {\n /** The externalized full set. First page only, and only when incomplete. */\n readonly resource?: R;\n}\n\n/**\n * The one replay-or-produce branch every paged tool runs. A cursor replays a\n * stored snapshot; without one, `produce` runs the query and its result is\n * snapshotted for later pages.\n *\n * The response is incomplete when the set spans more than one page or the\n * engine truncated it. Then — and only on the first page — `externalize`\n * stores the full set and its handle rides back as `resource`. Replayed pages\n * never carry it: the stored entry expires on the resource store's own TTL and\n * LRU, not this snapshot's, so replaying its URI would hand back a dead\n * pointer. Nor do they mint a new one; the set was already stored whole.\n */\nexport async function paginate<T, M, R>(params: {\n store: PageSnapshotStore;\n queryKey: string;\n cursor: string | undefined;\n pageSize: number;\n produce: () => Promise<ProducedPage<T, M>>;\n externalize?: ((items: readonly T[], metadata: M) => R) | undefined;\n}): Promise<PaginatedPage<T, M, R>> {\n if (params.cursor !== undefined) {\n return readNextPage<T, M>({\n store: params.store,\n queryKey: params.queryKey,\n cursor: params.cursor,\n pageSize: params.pageSize,\n });\n }\n const produced = await params.produce();\n const incomplete = produced.items.length > params.pageSize || produced.truncated;\n const first = createFirstPage<T, M>({\n store: params.store,\n queryKey: params.queryKey,\n items: produced.items,\n metadata: produced.metadata,\n pageSize: params.pageSize,\n });\n if (!incomplete || params.externalize === undefined) return first;\n return { ...first, resource: params.externalize(produced.items, produced.metadata) };\n}\n\nexport function readNextPage<T, M>(params: {\n store: PageSnapshotStore;\n queryKey: string;\n cursor: string;\n pageSize: number;\n}): Page<T, M> {\n const decoded = decodePageCursor(params.cursor);\n const snapshot = params.store.read<T, M>(decoded.snapshotId, params.queryKey);\n return pageResult(decoded.snapshotId, decoded.offset, params.pageSize, snapshot);\n}\n\nfunction decodePageCursor(cursor: string): PageCursor {\n try {\n const text = Buffer.from(cursor, 'base64url').toString('utf-8');\n const parsed = JSON.parse(text) as { snapshotId?: unknown; offset?: unknown };\n if (\n typeof parsed.snapshotId === 'string' &&\n parsed.snapshotId.length > 0 &&\n typeof parsed.offset === 'number' &&\n Number.isInteger(parsed.offset) &&\n parsed.offset >= 0\n ) {\n return { snapshotId: parsed.snapshotId, offset: parsed.offset };\n }\n throw new Error('Invalid page cursor');\n } catch (error) {\n if (error instanceof FsError) throw error;\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n invalidCursor().message,\n undefined,\n { originalError: formatUnknownErrorMessage(error) },\n error instanceof Error ? error : undefined,\n );\n }\n}\n"]}
|
package/dist/core/fmt.d.ts
CHANGED
|
@@ -23,6 +23,24 @@ export declare function pathLabel(path: string): string;
|
|
|
23
23
|
*/
|
|
24
24
|
export declare function joinRoster(items: readonly string[], separator?: string): string;
|
|
25
25
|
export declare function truncateProgressPattern(pattern: string, maxLength?: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* The `//` lines a paged search appends to its text block. Paging and the
|
|
28
|
+
* engine's stop state live in the structured half, which `defineTool` ships
|
|
29
|
+
* under `_meta` for a tool that authors its own text — and no client renders
|
|
30
|
+
* that. These lines are the only place a caller learns that more remains or
|
|
31
|
+
* that the scan was cut. They are independent: a scan can stop early with too
|
|
32
|
+
* few results to page, so a truncation with no cursor must still say so.
|
|
33
|
+
* Prefixed `//` so neither can be mistaken for a result row.
|
|
34
|
+
*/
|
|
35
|
+
export declare function pageTrailer(p: {
|
|
36
|
+
offset: number;
|
|
37
|
+
shown: number;
|
|
38
|
+
total: number;
|
|
39
|
+
noun: string;
|
|
40
|
+
tool: string;
|
|
41
|
+
nextCursor?: string | undefined;
|
|
42
|
+
stoppedReason?: string | undefined;
|
|
43
|
+
}): string;
|
|
26
44
|
export declare function padEndVisible(s: string, width: number): string;
|
|
27
45
|
export declare const cliFmt: {
|
|
28
46
|
bold: (t: string) => string;
|
package/dist/core/fmt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fmt.d.ts","sourceRoot":"","sources":["../../src/core/fmt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fmt.d.ts","sourceRoot":"","sources":["../../src/core/fmt.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAkC7C,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,GAAG,MAAM,CAGnE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAiB,GAAG,MAAM,CAE5F;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAKD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,SAAQ,GAAG,MAAM,CAI9E;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,SAAK,GAAG,MAAM,CAE/E;AAYD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,GAAG,MAAM,CAoBT;AAUD,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG9D;AASD,eAAO,MAAM,MAAM;cACP,MAAM;aACP,MAAM;cACL,MAAM;gBACJ,MAAM;cACR,MAAM;qBACC,MAAM;iBACV,MAAM;cACT,OAAO;CAClB,CAAC"}
|
package/dist/core/fmt.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { basename } from 'node:path';
|
|
2
2
|
import { stripVTControlCharacters, styleText } from 'node:util';
|
|
3
|
+
import { MAX_SEARCH_RESULTS } from './util.js';
|
|
3
4
|
function buildBody(ctx, phase) {
|
|
4
5
|
const items = [];
|
|
5
6
|
if (ctx.subject) {
|
|
@@ -61,6 +62,40 @@ export function joinRoster(items, separator = ' · ') {
|
|
|
61
62
|
export function truncateProgressPattern(pattern, maxLength = 40) {
|
|
62
63
|
return pattern.length <= maxLength ? pattern : `${pattern.slice(0, maxLength)}…`;
|
|
63
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* `stoppedReason` names the engine's own cap, and `maxResults` is also the name
|
|
67
|
+
* of the caller's page-size argument — spelling the cap out keeps a caller who
|
|
68
|
+
* passed `maxResults: 5` from reading their own argument back.
|
|
69
|
+
*/
|
|
70
|
+
const STOP_REASON_TEXT = {
|
|
71
|
+
maxResults: `hit the server's ${String(MAX_SEARCH_RESULTS)}-result scan cap, not your maxResults`,
|
|
72
|
+
timeout: 'hit the time limit, or the request was cancelled',
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* The `//` lines a paged search appends to its text block. Paging and the
|
|
76
|
+
* engine's stop state live in the structured half, which `defineTool` ships
|
|
77
|
+
* under `_meta` for a tool that authors its own text — and no client renders
|
|
78
|
+
* that. These lines are the only place a caller learns that more remains or
|
|
79
|
+
* that the scan was cut. They are independent: a scan can stop early with too
|
|
80
|
+
* few results to page, so a truncation with no cursor must still say so.
|
|
81
|
+
* Prefixed `//` so neither can be mistaken for a result row.
|
|
82
|
+
*/
|
|
83
|
+
export function pageTrailer(p) {
|
|
84
|
+
const lines = [];
|
|
85
|
+
// Position is owed on every page of a split set, including the last one —
|
|
86
|
+
// which has no cursor and would otherwise read as the whole answer.
|
|
87
|
+
if (p.offset > 0 || p.total > p.shown) {
|
|
88
|
+
const next = p.nextCursor === undefined
|
|
89
|
+
? ''
|
|
90
|
+
: ` Next page: ${p.tool} ${JSON.stringify({ cursor: p.nextCursor })}`;
|
|
91
|
+
lines.push(`// showing ${String(p.offset + 1)}-${String(p.offset + p.shown)} of ${String(p.total)} ${p.noun}.${next}`);
|
|
92
|
+
}
|
|
93
|
+
if (p.stoppedReason !== undefined) {
|
|
94
|
+
const cause = STOP_REASON_TEXT[p.stoppedReason] ?? `stopped (${p.stoppedReason})`;
|
|
95
|
+
lines.push(`// scan stopped early: ${cause}. That total is a floor, not the count. Narrow path or pattern for the rest.`);
|
|
96
|
+
}
|
|
97
|
+
return lines.length > 0 ? `\n\n${lines.join('\n')}` : '';
|
|
98
|
+
}
|
|
64
99
|
// ---------------------------------------------------------------------------
|
|
65
100
|
// CLI color helpers
|
|
66
101
|
// ---------------------------------------------------------------------------
|
package/dist/core/fmt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fmt.js","sourceRoot":"","sources":["../../src/core/fmt.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename } from 'node:path';\nimport { stripVTControlCharacters, styleText } from 'node:util';\n\nexport interface ProgressCtx {\n label: string;\n subject?: string;\n scope?: string;\n current?: number;\n total?: number;\n detail?: string;\n error?: string;\n}\n\nexport type Phase = 'tick' | 'done' | 'fail';\n\nfunction buildBody(ctx: ProgressCtx, phase: Phase): string {\n const items: string[] = [];\n if (ctx.subject) {\n items.push(ctx.subject);\n }\n\n switch (phase) {\n case 'tick':\n if (ctx.current !== undefined && ctx.total !== undefined) {\n items.push(`${ctx.current}/${ctx.total}`);\n } else if (ctx.current !== undefined) {\n items.push(String(ctx.current));\n }\n break;\n case 'done':\n if (ctx.scope) {\n items.push(ctx.scope);\n }\n if (ctx.detail) {\n items.push(ctx.detail);\n }\n break;\n case 'fail':\n if (ctx.error) {\n items.push(ctx.error);\n }\n break;\n }\n\n return items.join(' · ');\n}\n\nexport function plainMessage(phase: Phase, ctx: ProgressCtx): string {\n const body = buildBody(ctx, phase);\n return body ? `${ctx.label}: ${body}` : `${ctx.label}:`;\n}\n\nexport function formatCount(count: number, singular: string, plural = `${singular}s`): string {\n return `${String(count)} ${count === 1 ? singular : plural}`;\n}\n\n/**\n * Short label for a path in summary text. `basename` returns '' for a root\n * (`C:\\`, `/`) and for a trailing-slash path, which rendered as a bare\n * separator in the per-path summaries, so fall back to the full path.\n */\nexport function pathLabel(path: string): string {\n return basename(path) || path;\n}\n\n/** Names past this point are noise in a summary line; structuredContent holds them all. */\nconst MAX_ROSTER_ITEMS = 20;\n\n/**\n * Join a per-path summary roster, capped. A 1000-path delete would otherwise\n * name every entry in the text block that `structuredContent` already carries\n * in full.\n */\nexport function joinRoster(items: readonly string[], separator = ' · '): string {\n if (items.length <= MAX_ROSTER_ITEMS) return items.join(separator);\n const shown = items.slice(0, MAX_ROSTER_ITEMS).join(separator);\n return `${shown}${separator}+${String(items.length - MAX_ROSTER_ITEMS)} more`;\n}\n\nexport function truncateProgressPattern(pattern: string, maxLength = 40): string {\n return pattern.length <= maxLength ? pattern : `${pattern.slice(0, maxLength)}…`;\n}\n\n// ---------------------------------------------------------------------------\n// CLI color helpers\n// ---------------------------------------------------------------------------\n\nfunction isColorEnabled(stream: { isTTY?: boolean } = process.stdout): boolean {\n return stream.isTTY === true && !process.env['NO_COLOR'];\n}\n\nexport function padEndVisible(s: string, width: number): string {\n const visible = stripVTControlCharacters(s).length;\n return visible >= width ? s : s + ' '.repeat(width - visible);\n}\n\ntype Style = Parameters<typeof styleText>[0];\n\n/** Same palette as above, but only when the target stream wants color. */\nfunction tint(format: Style, text: string, stream?: { isTTY?: boolean }): string {\n return isColorEnabled(stream) ? styleText(format, text) : text;\n}\n\nexport const cliFmt = {\n bold: (t: string) => tint('bold', t),\n dim: (t: string) => tint('dim', t),\n cyan: (t: string) => tint('cyan', t),\n yellow: (t: string) => tint('yellow', t),\n flag: (t: string) => tint('green', t),\n placeholder: (t: string) => tint('yellow', t),\n section: (t: string) => tint(['cyan', 'bold'], t),\n bool: (v: boolean) => (v ? tint('green', 'true') : tint('red', 'false')),\n};\n"]}
|
|
1
|
+
{"version":3,"file":"fmt.js","sourceRoot":"","sources":["../../src/core/fmt.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename } from 'node:path';\nimport { stripVTControlCharacters, styleText } from 'node:util';\n\nimport { MAX_SEARCH_RESULTS } from './util.js';\n\nexport interface ProgressCtx {\n label: string;\n subject?: string;\n scope?: string;\n current?: number;\n total?: number;\n detail?: string;\n error?: string;\n}\n\nexport type Phase = 'tick' | 'done' | 'fail';\n\nfunction buildBody(ctx: ProgressCtx, phase: Phase): string {\n const items: string[] = [];\n if (ctx.subject) {\n items.push(ctx.subject);\n }\n\n switch (phase) {\n case 'tick':\n if (ctx.current !== undefined && ctx.total !== undefined) {\n items.push(`${ctx.current}/${ctx.total}`);\n } else if (ctx.current !== undefined) {\n items.push(String(ctx.current));\n }\n break;\n case 'done':\n if (ctx.scope) {\n items.push(ctx.scope);\n }\n if (ctx.detail) {\n items.push(ctx.detail);\n }\n break;\n case 'fail':\n if (ctx.error) {\n items.push(ctx.error);\n }\n break;\n }\n\n return items.join(' · ');\n}\n\nexport function plainMessage(phase: Phase, ctx: ProgressCtx): string {\n const body = buildBody(ctx, phase);\n return body ? `${ctx.label}: ${body}` : `${ctx.label}:`;\n}\n\nexport function formatCount(count: number, singular: string, plural = `${singular}s`): string {\n return `${String(count)} ${count === 1 ? singular : plural}`;\n}\n\n/**\n * Short label for a path in summary text. `basename` returns '' for a root\n * (`C:\\`, `/`) and for a trailing-slash path, which rendered as a bare\n * separator in the per-path summaries, so fall back to the full path.\n */\nexport function pathLabel(path: string): string {\n return basename(path) || path;\n}\n\n/** Names past this point are noise in a summary line; structuredContent holds them all. */\nconst MAX_ROSTER_ITEMS = 20;\n\n/**\n * Join a per-path summary roster, capped. A 1000-path delete would otherwise\n * name every entry in the text block that `structuredContent` already carries\n * in full.\n */\nexport function joinRoster(items: readonly string[], separator = ' · '): string {\n if (items.length <= MAX_ROSTER_ITEMS) return items.join(separator);\n const shown = items.slice(0, MAX_ROSTER_ITEMS).join(separator);\n return `${shown}${separator}+${String(items.length - MAX_ROSTER_ITEMS)} more`;\n}\n\nexport function truncateProgressPattern(pattern: string, maxLength = 40): string {\n return pattern.length <= maxLength ? pattern : `${pattern.slice(0, maxLength)}…`;\n}\n\n/**\n * `stoppedReason` names the engine's own cap, and `maxResults` is also the name\n * of the caller's page-size argument — spelling the cap out keeps a caller who\n * passed `maxResults: 5` from reading their own argument back.\n */\nconst STOP_REASON_TEXT: Record<string, string> = {\n maxResults: `hit the server's ${String(MAX_SEARCH_RESULTS)}-result scan cap, not your maxResults`,\n timeout: 'hit the time limit, or the request was cancelled',\n};\n\n/**\n * The `//` lines a paged search appends to its text block. Paging and the\n * engine's stop state live in the structured half, which `defineTool` ships\n * under `_meta` for a tool that authors its own text — and no client renders\n * that. These lines are the only place a caller learns that more remains or\n * that the scan was cut. They are independent: a scan can stop early with too\n * few results to page, so a truncation with no cursor must still say so.\n * Prefixed `//` so neither can be mistaken for a result row.\n */\nexport function pageTrailer(p: {\n offset: number;\n shown: number;\n total: number;\n noun: string;\n tool: string;\n nextCursor?: string | undefined;\n stoppedReason?: string | undefined;\n}): string {\n const lines: string[] = [];\n // Position is owed on every page of a split set, including the last one —\n // which has no cursor and would otherwise read as the whole answer.\n if (p.offset > 0 || p.total > p.shown) {\n const next =\n p.nextCursor === undefined\n ? ''\n : ` Next page: ${p.tool} ${JSON.stringify({ cursor: p.nextCursor })}`;\n lines.push(\n `// showing ${String(p.offset + 1)}-${String(p.offset + p.shown)} of ${String(p.total)} ${p.noun}.${next}`,\n );\n }\n if (p.stoppedReason !== undefined) {\n const cause = STOP_REASON_TEXT[p.stoppedReason] ?? `stopped (${p.stoppedReason})`;\n lines.push(\n `// scan stopped early: ${cause}. That total is a floor, not the count. Narrow path or pattern for the rest.`,\n );\n }\n return lines.length > 0 ? `\\n\\n${lines.join('\\n')}` : '';\n}\n\n// ---------------------------------------------------------------------------\n// CLI color helpers\n// ---------------------------------------------------------------------------\n\nfunction isColorEnabled(stream: { isTTY?: boolean } = process.stdout): boolean {\n return stream.isTTY === true && !process.env['NO_COLOR'];\n}\n\nexport function padEndVisible(s: string, width: number): string {\n const visible = stripVTControlCharacters(s).length;\n return visible >= width ? s : s + ' '.repeat(width - visible);\n}\n\ntype Style = Parameters<typeof styleText>[0];\n\n/** Same palette as above, but only when the target stream wants color. */\nfunction tint(format: Style, text: string, stream?: { isTTY?: boolean }): string {\n return isColorEnabled(stream) ? styleText(format, text) : text;\n}\n\nexport const cliFmt = {\n bold: (t: string) => tint('bold', t),\n dim: (t: string) => tint('dim', t),\n cyan: (t: string) => tint('cyan', t),\n yellow: (t: string) => tint('yellow', t),\n flag: (t: string) => tint('green', t),\n placeholder: (t: string) => tint('yellow', t),\n section: (t: string) => tint(['cyan', 'bold'], t),\n bool: (v: boolean) => (v ? tint('green', 'true') : tint('red', 'false')),\n};\n"]}
|
package/dist/tools/batch.d.ts
CHANGED
|
@@ -69,6 +69,26 @@ export interface PairBatchOutcome<TResult> {
|
|
|
69
69
|
skipped: string[];
|
|
70
70
|
failures: PairFailureItem[];
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* A batch where every requested item failed produced no work at all, so it is a
|
|
74
|
+
* failed call — `isError` must say so. Partial failure is deliberately NOT an
|
|
75
|
+
* error: the per-item entries carry which failed, and the succeeded ones really
|
|
76
|
+
* were done. A skipped item is work the caller asked to skip, not work that
|
|
77
|
+
* failed — a self-move that was silently dropped means the call did something.
|
|
78
|
+
* Zero items is not a failure either.
|
|
79
|
+
*
|
|
80
|
+
* Takes either count shape a batch tool holds at its return site: a `summary`
|
|
81
|
+
* (`runOverPaths`, or the hand-built ones in `read`, `delete`, `replace_text`)
|
|
82
|
+
* or a pair outcome (`runOverPairs`).
|
|
83
|
+
*/
|
|
84
|
+
export declare function isTotalFailure(shape: {
|
|
85
|
+
readonly total: number;
|
|
86
|
+
readonly failed: number;
|
|
87
|
+
} | {
|
|
88
|
+
readonly results: readonly unknown[];
|
|
89
|
+
readonly skipped: readonly unknown[];
|
|
90
|
+
readonly failures: readonly unknown[];
|
|
91
|
+
}): boolean;
|
|
72
92
|
export declare function pairFailure(pair: {
|
|
73
93
|
source: string;
|
|
74
94
|
destination: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../../src/tools/batch.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../../src/tools/batch.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/batch.js
CHANGED
|
@@ -55,6 +55,23 @@ export async function runOverPaths(args, ctx, perPath, options) {
|
|
|
55
55
|
summary: { total, succeeded, failed: total - succeeded },
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* A batch where every requested item failed produced no work at all, so it is a
|
|
60
|
+
* failed call — `isError` must say so. Partial failure is deliberately NOT an
|
|
61
|
+
* error: the per-item entries carry which failed, and the succeeded ones really
|
|
62
|
+
* were done. A skipped item is work the caller asked to skip, not work that
|
|
63
|
+
* failed — a self-move that was silently dropped means the call did something.
|
|
64
|
+
* Zero items is not a failure either.
|
|
65
|
+
*
|
|
66
|
+
* Takes either count shape a batch tool holds at its return site: a `summary`
|
|
67
|
+
* (`runOverPaths`, or the hand-built ones in `read`, `delete`, `replace_text`)
|
|
68
|
+
* or a pair outcome (`runOverPairs`).
|
|
69
|
+
*/
|
|
70
|
+
export function isTotalFailure(shape) {
|
|
71
|
+
if ('total' in shape)
|
|
72
|
+
return shape.total > 0 && shape.failed === shape.total;
|
|
73
|
+
return shape.failures.length > 0 && shape.results.length + shape.skipped.length === 0;
|
|
74
|
+
}
|
|
58
75
|
export function pairFailure(pair, error) {
|
|
59
76
|
return {
|
|
60
77
|
source: pair.source,
|
package/dist/tools/batch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch.js","sourceRoot":"","sources":["../../src/tools/batch.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InputRequiredResult } from '@modelcontextprotocol/server';\n\nimport { processInParallel } from '../core/concurrency.js';\nimport { ErrorCode, FsError, Problem, rethrowIfAborted } from '../core/errors.js';\nimport { choiceInput, pendingRoundTrip } from '../core/input-required.js';\nimport { IS_CASE_INSENSITIVE_FS } from '../core/path-utils.js';\nimport type { PairFailureItem } from '../core/schema.js';\nimport { PARALLEL_CONCURRENCY } from '../core/util.js';\nimport type { ToolCtx } from './define.js';\n\ninterface PerPathError {\n code: ErrorCode;\n message: string;\n path?: string;\n suggestion?: string;\n}\n\nexport type PerPathResult<T> = { path: string; value: T } | { path: string; error: PerPathError };\n\nexport interface BatchResult<T> {\n results: PerPathResult<T>[];\n summary: { total: number; succeeded: number; failed: number };\n}\n\ntype BatchInput<TOverride> =\n { path: string } | { paths: string[] } | { files: ({ path: string } & TOverride)[] };\n\ninterface RunOverPathsOptions {\n defaultErrorCode?: ErrorCode;\n concurrency?: number;\n}\n\nfunction normalizeBatchItems<TOverride>(\n args: BatchInput<TOverride>,\n): { path: string; override?: TOverride }[] {\n if ('path' in args) return [{ path: args.path }];\n if ('paths' in args) return args.paths.map((path) => ({ path }));\n if ('files' in args)\n return args.files.map(({ path, ...rest }) => ({\n path,\n override: rest as TOverride,\n }));\n return [];\n}\n\nexport async function runOverPaths<TOverride, TPerPath>(\n args: BatchInput<TOverride>,\n ctx: ToolCtx,\n perPath: (item: { path: string; override?: TOverride }, ctx: ToolCtx) => Promise<TPerPath>,\n options?: RunOverPathsOptions,\n): Promise<BatchResult<TPerPath>> {\n const items = normalizeBatchItems(args);\n if (items.length === 0) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n \"runOverPaths: at least one of 'path', 'paths', or 'files' must be provided\",\n );\n }\n\n const defaultErrorCode = options?.defaultErrorCode ?? ErrorCode.UNKNOWN;\n const concurrency = options?.concurrency ?? PARALLEL_CONCURRENCY;\n\n const total = items.length;\n let completed = 0;\n const results: PerPathResult<TPerPath>[] = new Array<PerPathResult<TPerPath>>(total);\n\n const tick = (): void => {\n completed += 1;\n ctx.onProgress?.({ current: completed, total });\n };\n\n await processInParallel<\n { item: { path: string; override?: TOverride }; index: number },\n undefined\n >(\n items.map((item, index) => ({ item, index })),\n async ({ item, index }) => {\n try {\n const value = await perPath(item, ctx);\n results[index] = { path: item.path, value };\n } catch (error: unknown) {\n results[index] = {\n path: item.path,\n error: Problem.toPerFileError(error, defaultErrorCode, item.path),\n };\n } finally {\n tick();\n }\n return undefined;\n },\n concurrency,\n ctx.signal,\n );\n\n let succeeded = 0;\n for (const result of results) {\n if (!('error' in result)) succeeded += 1;\n }\n\n return {\n results,\n summary: { total, succeeded, failed: total - succeeded },\n };\n}\n\n/** The minimum a pair plan must expose for the shared driver to route it. */\nexport interface PairPlan {\n readonly pair: { source: string; destination: string };\n readonly validDest: string;\n readonly pending: boolean;\n}\n\nexport type PairPlanResult<TPlan extends PairPlan> =\n | { status: 'fail'; failure: PairFailureItem }\n | { status: 'noop' }\n | { status: 'plan'; plan: TPlan };\n\n/** A discriminated union, so the fold below narrows without a generic cast. */\nexport type PairExecResult<TResult> = { readonly skipped: string } | { readonly value: TResult };\n\nexport interface PairBatchOutcome<TResult> {\n results: TResult[];\n skipped: string[];\n failures: PairFailureItem[];\n}\n\nexport function pairFailure(\n pair: { source: string; destination: string },\n error: unknown,\n): PairFailureItem {\n return {\n source: pair.source,\n destination: pair.destination,\n error: Problem.toPerFileError(error, ErrorCode.UNKNOWN, pair.source),\n };\n}\n\ninterface RunOverPairsOptions<TItem, TPlan extends PairPlan, TResult> {\n readonly op: 'copy' | 'move';\n readonly plan: (item: TItem) => Promise<PairPlanResult<TPlan>>;\n readonly execute: (\n plan: TPlan,\n pendingSorted: readonly string[],\n ) => Promise<PairExecResult<TResult>>;\n}\n\n/**\n * The source→destination sibling of `runOverPaths`: plan every pair, fail closed\n * on a duplicated destination, round-trip the overwrite confirmations as one\n * set, then execute what survived in parallel. `copy` and `move` differ only in\n * their `plan` and `execute` callbacks.\n */\nexport async function runOverPairs<TItem, TPlan extends PairPlan, TResult>(\n items: readonly TItem[],\n ctx: ToolCtx,\n opts: RunOverPairsOptions<TItem, TPlan, TResult>,\n): Promise<PairBatchOutcome<TResult> | InputRequiredResult> {\n const verb = opts.op === 'copy' ? 'Copy' : 'Move';\n\n const { results: planned, errors: planErrors } = await processInParallel(\n items,\n opts.plan,\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n // plan callbacks are fail-closed (they return { status: 'fail' }, never\n // throw), so planErrors should always be empty. Surface the first rather\n // than silently dropping the item if a future plan callback violates that.\n const firstPlanError = planErrors[0];\n if (firstPlanError) throw firstPlanError.error;\n\n const failures: PairFailureItem[] = [];\n const candidates: TPlan[] = [];\n for (const { value } of planned) {\n if (value.status === 'fail') failures.push(value.failure);\n else if (value.status === 'plan') candidates.push(value.plan);\n // 'noop' (self-copy / self-move) is silently skipped.\n }\n\n // Two sources targeting the same destination in one batch would otherwise\n // collapse to a single shared overwrite confirmation and let the second\n // one silently clobber the first's freshly-written content. Fail closed:\n // only the first plan per destination proceeds; later ones targeting the\n // same destination are reported as a per-item failure.\n const seenDest = new Set<string>();\n const ready: TPlan[] = [];\n for (const plan of candidates) {\n const destKey = IS_CASE_INSENSITIVE_FS ? plan.validDest.toLowerCase() : plan.validDest;\n if (seenDest.has(destKey)) {\n failures.push(\n pairFailure(\n plan.pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `${verb} cancelled: another entry in this batch already targets destination \"${plan.pair.destination}\"`,\n plan.pair.destination,\n ),\n ),\n );\n continue;\n }\n seenDest.add(destKey);\n ready.push(plan);\n }\n\n const pendingSorted = ready\n .filter((p) => p.pending)\n .map((p) => p.validDest)\n .sort();\n if (pendingSorted.length > 0) {\n // Round 1 returns input_required; a retry whose verified state does not\n // bind this overwrite set throws (R9) via `pendingRoundTrip`.\n const round = await pendingRoundTrip({\n op: opts.op,\n pending: pendingSorted,\n requestState: ctx.requestState,\n clientCapabilities: ctx.clientCapabilities,\n buildInputs: (dests) =>\n dests.map((dest, i) =>\n choiceInput(\n `confirm_${i}`,\n opts.op === 'move'\n ? `\"${dest}\" already exists. Overwrite it?`\n : `Destination \"${dest}\" already exists. Overwrite it?`,\n [\n { value: 'overwrite', title: 'Overwrite' },\n { value: 'skip', title: 'Skip' },\n ],\n ),\n ),\n });\n if (round !== undefined) return round;\n }\n\n const total = ready.length;\n let completed = 0;\n const tick = (): void => {\n completed += 1;\n ctx.onProgress?.({ current: completed, total });\n };\n const { results: execResults, errors: execErrors } = await processInParallel(\n ready,\n async (plan) => {\n try {\n return await opts.execute(plan, pendingSorted);\n } finally {\n tick();\n }\n },\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n\n for (const { error, index } of execErrors) {\n rethrowIfAborted(error);\n const plan = ready[index];\n if (plan) failures.push(pairFailure(plan.pair, error));\n }\n\n const results: TResult[] = [];\n const skipped: string[] = [];\n for (const { value } of execResults) {\n if ('skipped' in value) skipped.push(value.skipped);\n else results.push(value.value);\n }\n\n return { results, skipped, failures };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"batch.js","sourceRoot":"","sources":["../../src/tools/batch.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InputRequiredResult } from '@modelcontextprotocol/server';\n\nimport { processInParallel } from '../core/concurrency.js';\nimport { ErrorCode, FsError, Problem, rethrowIfAborted } from '../core/errors.js';\nimport { choiceInput, pendingRoundTrip } from '../core/input-required.js';\nimport { IS_CASE_INSENSITIVE_FS } from '../core/path-utils.js';\nimport type { PairFailureItem } from '../core/schema.js';\nimport { PARALLEL_CONCURRENCY } from '../core/util.js';\nimport type { ToolCtx } from './define.js';\n\ninterface PerPathError {\n code: ErrorCode;\n message: string;\n path?: string;\n suggestion?: string;\n}\n\nexport type PerPathResult<T> = { path: string; value: T } | { path: string; error: PerPathError };\n\nexport interface BatchResult<T> {\n results: PerPathResult<T>[];\n summary: { total: number; succeeded: number; failed: number };\n}\n\ntype BatchInput<TOverride> =\n { path: string } | { paths: string[] } | { files: ({ path: string } & TOverride)[] };\n\ninterface RunOverPathsOptions {\n defaultErrorCode?: ErrorCode;\n concurrency?: number;\n}\n\nfunction normalizeBatchItems<TOverride>(\n args: BatchInput<TOverride>,\n): { path: string; override?: TOverride }[] {\n if ('path' in args) return [{ path: args.path }];\n if ('paths' in args) return args.paths.map((path) => ({ path }));\n if ('files' in args)\n return args.files.map(({ path, ...rest }) => ({\n path,\n override: rest as TOverride,\n }));\n return [];\n}\n\nexport async function runOverPaths<TOverride, TPerPath>(\n args: BatchInput<TOverride>,\n ctx: ToolCtx,\n perPath: (item: { path: string; override?: TOverride }, ctx: ToolCtx) => Promise<TPerPath>,\n options?: RunOverPathsOptions,\n): Promise<BatchResult<TPerPath>> {\n const items = normalizeBatchItems(args);\n if (items.length === 0) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n \"runOverPaths: at least one of 'path', 'paths', or 'files' must be provided\",\n );\n }\n\n const defaultErrorCode = options?.defaultErrorCode ?? ErrorCode.UNKNOWN;\n const concurrency = options?.concurrency ?? PARALLEL_CONCURRENCY;\n\n const total = items.length;\n let completed = 0;\n const results: PerPathResult<TPerPath>[] = new Array<PerPathResult<TPerPath>>(total);\n\n const tick = (): void => {\n completed += 1;\n ctx.onProgress?.({ current: completed, total });\n };\n\n await processInParallel<\n { item: { path: string; override?: TOverride }; index: number },\n undefined\n >(\n items.map((item, index) => ({ item, index })),\n async ({ item, index }) => {\n try {\n const value = await perPath(item, ctx);\n results[index] = { path: item.path, value };\n } catch (error: unknown) {\n results[index] = {\n path: item.path,\n error: Problem.toPerFileError(error, defaultErrorCode, item.path),\n };\n } finally {\n tick();\n }\n return undefined;\n },\n concurrency,\n ctx.signal,\n );\n\n let succeeded = 0;\n for (const result of results) {\n if (!('error' in result)) succeeded += 1;\n }\n\n return {\n results,\n summary: { total, succeeded, failed: total - succeeded },\n };\n}\n\n/** The minimum a pair plan must expose for the shared driver to route it. */\nexport interface PairPlan {\n readonly pair: { source: string; destination: string };\n readonly validDest: string;\n readonly pending: boolean;\n}\n\nexport type PairPlanResult<TPlan extends PairPlan> =\n | { status: 'fail'; failure: PairFailureItem }\n | { status: 'noop' }\n | { status: 'plan'; plan: TPlan };\n\n/** A discriminated union, so the fold below narrows without a generic cast. */\nexport type PairExecResult<TResult> = { readonly skipped: string } | { readonly value: TResult };\n\nexport interface PairBatchOutcome<TResult> {\n results: TResult[];\n skipped: string[];\n failures: PairFailureItem[];\n}\n\n/**\n * A batch where every requested item failed produced no work at all, so it is a\n * failed call — `isError` must say so. Partial failure is deliberately NOT an\n * error: the per-item entries carry which failed, and the succeeded ones really\n * were done. A skipped item is work the caller asked to skip, not work that\n * failed — a self-move that was silently dropped means the call did something.\n * Zero items is not a failure either.\n *\n * Takes either count shape a batch tool holds at its return site: a `summary`\n * (`runOverPaths`, or the hand-built ones in `read`, `delete`, `replace_text`)\n * or a pair outcome (`runOverPairs`).\n */\nexport function isTotalFailure(\n shape:\n | { readonly total: number; readonly failed: number }\n | {\n readonly results: readonly unknown[];\n readonly skipped: readonly unknown[];\n readonly failures: readonly unknown[];\n },\n): boolean {\n if ('total' in shape) return shape.total > 0 && shape.failed === shape.total;\n return shape.failures.length > 0 && shape.results.length + shape.skipped.length === 0;\n}\n\nexport function pairFailure(\n pair: { source: string; destination: string },\n error: unknown,\n): PairFailureItem {\n return {\n source: pair.source,\n destination: pair.destination,\n error: Problem.toPerFileError(error, ErrorCode.UNKNOWN, pair.source),\n };\n}\n\ninterface RunOverPairsOptions<TItem, TPlan extends PairPlan, TResult> {\n readonly op: 'copy' | 'move';\n readonly plan: (item: TItem) => Promise<PairPlanResult<TPlan>>;\n readonly execute: (\n plan: TPlan,\n pendingSorted: readonly string[],\n ) => Promise<PairExecResult<TResult>>;\n}\n\n/**\n * The source→destination sibling of `runOverPaths`: plan every pair, fail closed\n * on a duplicated destination, round-trip the overwrite confirmations as one\n * set, then execute what survived in parallel. `copy` and `move` differ only in\n * their `plan` and `execute` callbacks.\n */\nexport async function runOverPairs<TItem, TPlan extends PairPlan, TResult>(\n items: readonly TItem[],\n ctx: ToolCtx,\n opts: RunOverPairsOptions<TItem, TPlan, TResult>,\n): Promise<PairBatchOutcome<TResult> | InputRequiredResult> {\n const verb = opts.op === 'copy' ? 'Copy' : 'Move';\n\n const { results: planned, errors: planErrors } = await processInParallel(\n items,\n opts.plan,\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n // plan callbacks are fail-closed (they return { status: 'fail' }, never\n // throw), so planErrors should always be empty. Surface the first rather\n // than silently dropping the item if a future plan callback violates that.\n const firstPlanError = planErrors[0];\n if (firstPlanError) throw firstPlanError.error;\n\n const failures: PairFailureItem[] = [];\n const candidates: TPlan[] = [];\n for (const { value } of planned) {\n if (value.status === 'fail') failures.push(value.failure);\n else if (value.status === 'plan') candidates.push(value.plan);\n // 'noop' (self-copy / self-move) is silently skipped.\n }\n\n // Two sources targeting the same destination in one batch would otherwise\n // collapse to a single shared overwrite confirmation and let the second\n // one silently clobber the first's freshly-written content. Fail closed:\n // only the first plan per destination proceeds; later ones targeting the\n // same destination are reported as a per-item failure.\n const seenDest = new Set<string>();\n const ready: TPlan[] = [];\n for (const plan of candidates) {\n const destKey = IS_CASE_INSENSITIVE_FS ? plan.validDest.toLowerCase() : plan.validDest;\n if (seenDest.has(destKey)) {\n failures.push(\n pairFailure(\n plan.pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `${verb} cancelled: another entry in this batch already targets destination \"${plan.pair.destination}\"`,\n plan.pair.destination,\n ),\n ),\n );\n continue;\n }\n seenDest.add(destKey);\n ready.push(plan);\n }\n\n const pendingSorted = ready\n .filter((p) => p.pending)\n .map((p) => p.validDest)\n .sort();\n if (pendingSorted.length > 0) {\n // Round 1 returns input_required; a retry whose verified state does not\n // bind this overwrite set throws (R9) via `pendingRoundTrip`.\n const round = await pendingRoundTrip({\n op: opts.op,\n pending: pendingSorted,\n requestState: ctx.requestState,\n clientCapabilities: ctx.clientCapabilities,\n buildInputs: (dests) =>\n dests.map((dest, i) =>\n choiceInput(\n `confirm_${i}`,\n opts.op === 'move'\n ? `\"${dest}\" already exists. Overwrite it?`\n : `Destination \"${dest}\" already exists. Overwrite it?`,\n [\n { value: 'overwrite', title: 'Overwrite' },\n { value: 'skip', title: 'Skip' },\n ],\n ),\n ),\n });\n if (round !== undefined) return round;\n }\n\n const total = ready.length;\n let completed = 0;\n const tick = (): void => {\n completed += 1;\n ctx.onProgress?.({ current: completed, total });\n };\n const { results: execResults, errors: execErrors } = await processInParallel(\n ready,\n async (plan) => {\n try {\n return await opts.execute(plan, pendingSorted);\n } finally {\n tick();\n }\n },\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n\n for (const { error, index } of execErrors) {\n rethrowIfAborted(error);\n const plan = ready[index];\n if (plan) failures.push(pairFailure(plan.pair, error));\n }\n\n const results: TResult[] = [];\n const skipped: string[] = [];\n for (const { value } of execResults) {\n if ('skipped' in value) skipped.push(value.skipped);\n else results.push(value.value);\n }\n\n return { results, skipped, failures };\n}\n"]}
|
package/dist/tools/create.js
CHANGED
|
@@ -6,7 +6,7 @@ import { detectMimeFromContent } from '../core/mime.js';
|
|
|
6
6
|
import { countLines } from '../core/read.js';
|
|
7
7
|
import { FileKind, IsoDateTime, NonNegInt, PathFailureSchema, RequiredPath, } from '../core/schema.js';
|
|
8
8
|
import { getMaxTextFileSize } from '../core/util.js';
|
|
9
|
-
import { runOverPaths } from './batch.js';
|
|
9
|
+
import { isTotalFailure, runOverPaths } from './batch.js';
|
|
10
10
|
import { defineTool } from './define.js';
|
|
11
11
|
const CreateFileItemSchema = z.strictObject({
|
|
12
12
|
path: RequiredPath.describe('Absolute path where the file will be created'),
|
|
@@ -109,9 +109,9 @@ export const CREATE = defineTool({
|
|
|
109
109
|
// asked `create` for. Supplying no text makes this a data tool, so
|
|
110
110
|
// `define.ts` renders the JSON and keeps it in `structuredContent`.
|
|
111
111
|
if (links.length > 0) {
|
|
112
|
-
return { structured, resources: links };
|
|
112
|
+
return { structured, resources: links, isError: isTotalFailure(batch.summary) };
|
|
113
113
|
}
|
|
114
|
-
return { structured };
|
|
114
|
+
return { structured, isError: isTotalFailure(batch.summary) };
|
|
115
115
|
},
|
|
116
116
|
progress: (args) => ({
|
|
117
117
|
label: 'Create',
|