@j0hanz/filesystem-mcp 2.4.0 → 2.5.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.
- package/README.md +23 -23
- package/dist/cli-help.js +1 -1
- package/dist/cli-help.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3 -10
- package/dist/cli.js.map +1 -1
- package/dist/core/concurrency.d.ts +1 -1
- package/dist/core/concurrency.d.ts.map +1 -1
- package/dist/core/concurrency.js +1 -1
- package/dist/core/concurrency.js.map +1 -1
- package/dist/core/cursor.d.ts.map +1 -1
- package/dist/core/cursor.js +2 -5
- package/dist/core/cursor.js.map +1 -1
- package/dist/core/errors.d.ts +1 -4
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +14 -20
- package/dist/core/errors.js.map +1 -1
- package/dist/core/file-uri.d.ts.map +1 -1
- package/dist/core/file-uri.js +3 -8
- package/dist/core/file-uri.js.map +1 -1
- package/dist/core/fmt.d.ts +13 -7
- package/dist/core/fmt.d.ts.map +1 -1
- package/dist/core/fmt.js +22 -10
- package/dist/core/fmt.js.map +1 -1
- package/dist/core/fs.d.ts +5 -18
- package/dist/core/fs.d.ts.map +1 -1
- package/dist/core/fs.js +1 -6
- package/dist/core/fs.js.map +1 -1
- package/dist/core/glob.d.ts +1 -2
- package/dist/core/glob.d.ts.map +1 -1
- package/dist/core/glob.js +1 -7
- package/dist/core/glob.js.map +1 -1
- package/dist/core/input-required.d.ts +0 -8
- package/dist/core/input-required.d.ts.map +1 -1
- package/dist/core/input-required.js +5 -15
- package/dist/core/input-required.js.map +1 -1
- package/dist/core/path-completer.d.ts.map +1 -1
- package/dist/core/path-completer.js +0 -2
- package/dist/core/path-completer.js.map +1 -1
- package/dist/core/path.d.ts.map +1 -1
- package/dist/core/path.js +1 -8
- package/dist/core/path.js.map +1 -1
- package/dist/core/read.d.ts.map +1 -1
- package/dist/core/read.js +13 -35
- package/dist/core/read.js.map +1 -1
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +1 -1
- package/dist/core/schema.js.map +1 -1
- package/dist/core/search.d.ts +3 -4
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +4 -0
- package/dist/core/search.js.map +1 -1
- package/dist/core/store.d.ts.map +1 -1
- package/dist/core/store.js +0 -3
- package/dist/core/store.js.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +1 -4
- package/dist/prompts.js.map +1 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +23 -18
- package/dist/resources.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +4 -3
- package/dist/server.js.map +1 -1
- package/dist/tools/batch.d.ts.map +1 -1
- package/dist/tools/batch.js +1 -2
- package/dist/tools/batch.js.map +1 -1
- package/dist/tools/create.d.ts.map +1 -1
- package/dist/tools/create.js +2 -6
- package/dist/tools/create.js.map +1 -1
- package/dist/tools/define.d.ts +6 -4
- package/dist/tools/define.d.ts.map +1 -1
- package/dist/tools/define.js +1 -1
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/delete.d.ts.map +1 -1
- package/dist/tools/delete.js +1 -7
- package/dist/tools/delete.js.map +1 -1
- package/dist/tools/diff.d.ts.map +1 -1
- package/dist/tools/diff.js +17 -21
- package/dist/tools/diff.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +22 -16
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/find-files.d.ts.map +1 -1
- package/dist/tools/find-files.js +3 -5
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/list-roots.d.ts.map +1 -1
- package/dist/tools/list-roots.js +4 -6
- package/dist/tools/list-roots.js.map +1 -1
- package/dist/tools/list.d.ts.map +1 -1
- package/dist/tools/list.js +18 -8
- package/dist/tools/list.js.map +1 -1
- package/dist/tools/move.d.ts.map +1 -1
- package/dist/tools/move.js +2 -14
- package/dist/tools/move.js.map +1 -1
- package/dist/tools/patch.d.ts.map +1 -1
- package/dist/tools/patch.js +83 -76
- package/dist/tools/patch.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +2 -6
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/replace-text.d.ts.map +1 -1
- package/dist/tools/replace-text.js +51 -17
- package/dist/tools/replace-text.js.map +1 -1
- package/dist/tools/search-text.d.ts.map +1 -1
- package/dist/tools/search-text.js +24 -23
- package/dist/tools/search-text.js.map +1 -1
- package/dist/tools/stat.d.ts.map +1 -1
- package/dist/tools/stat.js +3 -5
- package/dist/tools/stat.js.map +1 -1
- package/dist/transport/http-policy.d.ts +15 -10
- package/dist/transport/http-policy.d.ts.map +1 -1
- package/dist/transport/http-policy.js +21 -28
- package/dist/transport/http-policy.js.map +1 -1
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +4 -4
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/shared.d.ts +13 -12
- package/dist/transport/shared.d.ts.map +1 -1
- package/dist/transport/shared.js +21 -25
- package/dist/transport/shared.js.map +1 -1
- package/dist/transport/stdio.d.ts.map +1 -1
- package/dist/transport/stdio.js +5 -8
- package/dist/transport/stdio.js.map +1 -1
- package/package.json +1 -2
|
@@ -29,14 +29,6 @@ export interface PendingInput {
|
|
|
29
29
|
*/
|
|
30
30
|
readonly multi?: boolean;
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
* What a minted `requestState` is bound to: the JSON-RPC method of the
|
|
34
|
-
* request that minted it and the authenticated caller (`ctx.http` is absent
|
|
35
|
-
* on stdio, so the caller part is empty there and constant across rounds).
|
|
36
|
-
* The SDK stores an HMAC tag of this value in the token and refuses an echo
|
|
37
|
-
* whose context yields a different value.
|
|
38
|
-
*/
|
|
39
|
-
export declare function requestStateBinding(ctx: ServerContext): string;
|
|
40
32
|
/**
|
|
41
33
|
* The codec: `mint` seals a `PendingState` into the opaque wire string a
|
|
42
34
|
* handler returns from `inputRequired({ requestState })`; `verify` drops into
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input-required.d.ts","sourceRoot":"","sources":["../../src/core/input-required.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,kBAAkB,EAElB,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACd,MAAM,8BAA8B,CAAC;AAetC,mEAAmE;AACnE,KAAK,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;
|
|
1
|
+
{"version":3,"file":"input-required.d.ts","sourceRoot":"","sources":["../../src/core/input-required.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,kBAAkB,EAElB,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACd,MAAM,8BAA8B,CAAC;AAetC,mEAAmE;AACnE,KAAK,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAiCD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,iBAAiB,CAAC,YAAY,CAI1D,CAAC;AA6BL;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,GAAG,EAAE,aAAa,GACjB,OAAO,CAAC,mBAAmB,CAAC,CAa9B;AAED;;;;;;;;;;;;;GAaG;AACH,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,YAAY,EAAE,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IACpE;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC7D,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,SAAS,YAAY,EAAE,CAAC;IAC9E,2FAA2F;IAC3F,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;CACnC;AA2CD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAyB1C;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC9C,GAAG,EAAE,MAAM,GACV,OAAO,CAET;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC9C,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAEpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC9C,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,GAAG,SAAS,CAEtB;AAED,uGAAuG;AACvG,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC9C,GAAG,EAAE,MAAM,GACV,MAAM,CAMR"}
|
|
@@ -21,9 +21,6 @@ function configuredRequestStateKey() {
|
|
|
21
21
|
}
|
|
22
22
|
return undefined;
|
|
23
23
|
}
|
|
24
|
-
// Built on first use, not at module load, so an unset env var costs nothing at
|
|
25
|
-
// import and the random per-boot fallback is minted only once something needs it.
|
|
26
|
-
let codec;
|
|
27
24
|
/**
|
|
28
25
|
* What a minted `requestState` is bound to: the JSON-RPC method of the
|
|
29
26
|
* request that minted it and the authenticated caller (`ctx.http` is absent
|
|
@@ -31,26 +28,19 @@ let codec;
|
|
|
31
28
|
* The SDK stores an HMAC tag of this value in the token and refuses an echo
|
|
32
29
|
* whose context yields a different value.
|
|
33
30
|
*/
|
|
34
|
-
|
|
31
|
+
function requestStateBinding(ctx) {
|
|
35
32
|
return `${ctx.mcpReq.method}\0${ctx.http?.authInfo?.clientId ?? ''}`;
|
|
36
33
|
}
|
|
37
|
-
function getRequestStateCodec() {
|
|
38
|
-
codec ??= createRequestStateCodec({
|
|
39
|
-
key: configuredRequestStateKey() ?? randomBytes(32),
|
|
40
|
-
bind: requestStateBinding,
|
|
41
|
-
});
|
|
42
|
-
return codec;
|
|
43
|
-
}
|
|
44
34
|
/**
|
|
45
35
|
* The codec: `mint` seals a `PendingState` into the opaque wire string a
|
|
46
36
|
* handler returns from `inputRequired({ requestState })`; `verify` drops into
|
|
47
37
|
* `ServerOptions.requestState.verify` and throws on tamper, expiry, or bind
|
|
48
38
|
* mismatch (the seam answers with the frozen `-32602`).
|
|
49
39
|
*/
|
|
50
|
-
export const requestStateCodec = {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
};
|
|
40
|
+
export const requestStateCodec = createRequestStateCodec({
|
|
41
|
+
key: configuredRequestStateKey() ?? randomBytes(32),
|
|
42
|
+
bind: requestStateBinding,
|
|
43
|
+
});
|
|
54
44
|
// Response shapes handed to the SDK's schema-aware `acceptedContent` overload:
|
|
55
45
|
// it returns `undefined` for a missing key, a decline/cancel, a non-elicit
|
|
56
46
|
// response, AND a payload that fails validation — one call covers every refusal
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input-required.js","sourceRoot":"","sources":["../../src/core/input-required.ts"],"names":[],"mappings":"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","sourcesContent":["// Shared infrastructure for the SEP-2577 `input_required` multi-round-trip\n// destructive-confirmation flows (recursive delete, move overwrite, out-of-root\n// access grant). A handler returns `inputRequired(...)` instead of the deprecated\n// push-style server-to-client elicitation request; the client retries the same\n// `tools/call` carrying `inputResponses`, and the handler re-enters from the top\n// reading the verified `requestState` and the accepted responses.\n//\n// `requestState` round-trips through the client (attacker-controlled on\n// re-entry), so it is sealed with the SDK's HMAC-SHA256 codec. The payload binds\n// each confirmation to its operation kind and sorted target-path set (spec R9,\n// R10); the handler additionally rejects any retry whose decoded paths do not\n// match the retried request's parameters (the codec only proves the state was\n// not tampered — it cannot see the current args).\nimport type {\n ClientCapabilities,\n InputRequest,\n InputRequiredResult,\n RequestStateCodec,\n ServerContext,\n} from '@modelcontextprotocol/server';\nimport {\n acceptedContent,\n createRequestStateCodec,\n inputRequired,\n inputResponse,\n} from '@modelcontextprotocol/server';\n\nimport { randomBytes } from 'node:crypto';\n\nimport * as z from 'zod/v4';\n\nimport { ErrorCode, FsError } from './errors.ts';\nimport { Logger } from './observability.ts';\n\n/** The destructive operation a pending confirmation authorizes. */\ntype PendingOp = 'delete' | 'move' | 'copy' | 'create' | 'grant';\n\n/**\n * Integrity-protected state minted into an `input_required` result and echoed\n * back by the client on retry. `paths` is the sorted canonical set of target\n * paths the confirmation covers; the handler compares it to the retried\n * request's parameters and rejects a mismatch (R9).\n */\nexport interface PendingState {\n readonly op: PendingOp;\n readonly paths: readonly string[];\n}\n\n/** One embedded form-mode confirmation, keyed within the call. */\nexport interface PendingInput {\n /** Server-assigned key, unique within the `tools/call`. */\n readonly key: string;\n /** Human-readable prompt for this item. */\n readonly message: string;\n /**\n * When set, the form offers a single-select enum (`choice` field) over\n * these values instead of a boolean `confirm`.\n */\n readonly choices?: readonly string[];\n /**\n * When set with `choices`, the form offers a multi-select enum: `choice` is\n * a string array (`MultiSelectEnumSchema` shape) and the caller reads the\n * accepted set with `readAcceptedMultiChoice`. Single-select otherwise.\n */\n readonly multi?: boolean;\n}\n\n/**\n * HMAC key for the requestState codec. Read once from\n * `FS_REQUEST_STATE_KEY` (UTF-8, must be >=32 bytes); a random\n * 32-byte key is generated at boot when the env var is unset or too short. A\n * per-process key is correct for stdio and the single-node HTTP leg (decision\n * record 11). A server restart invalidates in-flight tokens; the client\n * re-requests, which is fail-closed and safe.\n */\nfunction configuredRequestStateKey(): Uint8Array | undefined {\n const env = process.env['FS_REQUEST_STATE_KEY'];\n if (env) {\n const bytes = Buffer.from(env, 'utf8');\n if (bytes.length >= 32) return bytes;\n Logger.warn(\n `FS_REQUEST_STATE_KEY is ${String(bytes.length)} bytes; 32 are required. Falling back to a random per-boot key — in-flight input_required rounds will not survive a restart.`,\n );\n }\n return undefined;\n}\n\n// Built on first use, not at module load, so an unset env var costs nothing at\n// import and the random per-boot fallback is minted only once something needs it.\nlet codec: RequestStateCodec<PendingState> | undefined;\n\n/**\n * What a minted `requestState` is bound to: the JSON-RPC method of the\n * request that minted it and the authenticated caller (`ctx.http` is absent\n * on stdio, so the caller part is empty there and constant across rounds).\n * The SDK stores an HMAC tag of this value in the token and refuses an echo\n * whose context yields a different value.\n */\nexport function requestStateBinding(ctx: ServerContext): string {\n return `${ctx.mcpReq.method}\\0${ctx.http?.authInfo?.clientId ?? ''}`;\n}\n\nfunction getRequestStateCodec(): RequestStateCodec<PendingState> {\n codec ??= createRequestStateCodec<PendingState>({\n key: configuredRequestStateKey() ?? randomBytes(32),\n bind: requestStateBinding,\n });\n return codec;\n}\n\n/**\n * The codec: `mint` seals a `PendingState` into the opaque wire string a\n * handler returns from `inputRequired({ requestState })`; `verify` drops into\n * `ServerOptions.requestState.verify` and throws on tamper, expiry, or bind\n * mismatch (the seam answers with the frozen `-32602`).\n */\nexport const requestStateCodec: RequestStateCodec<PendingState> = {\n mint: (payload, ctx) => getRequestStateCodec().mint(payload, ctx),\n verify: (state, ctx) => getRequestStateCodec().verify(state, ctx),\n};\n\n// Response shapes handed to the SDK's schema-aware `acceptedContent` overload:\n// it returns `undefined` for a missing key, a decline/cancel, a non-elicit\n// response, AND a payload that fails validation — one call covers every refusal\n// case the readers below used to hand-check. `buildInputRequired` reuses\n// `ConfirmContent` as the no-choices form schema (below), so these sit above it.\nconst ConfirmContent = z.object({ confirm: z.boolean().meta({ title: 'Confirm' }) });\nconst ChoiceContent = z.object({ choice: z.string() });\nconst MultiChoiceContent = z.object({ choice: z.array(z.string()) });\n\n/**\n * The form the client renders for one pending input, as the Zod schema the\n * SDK converts to the elicitation wire shape. A boolean `confirm` when no\n * choices are offered; otherwise a `choice` enum over the offered values,\n * wrapped in an array for multi-select so the client may accept a subset.\n * The offered lists are never empty by construction (single-select always\n * offers two literals; multi-select only runs with two or more grant dirs),\n * so no empty-array guard is added. Each field carries a `title` so a host\n * that renders the form labels it instead of showing the property name.\n */\nfunction requestedSchemaFor(input: PendingInput): z.ZodObject {\n if (!input.choices) return ConfirmContent;\n const value = z.enum(input.choices);\n return z.object({\n choice: input.multi ? z.array(value).meta({ title: 'Allow' }) : value.meta({ title: 'Action' }),\n });\n}\n\n/**\n * Build an `input_required` result carrying one boolean confirmation per pending\n * item, plus the integrity-protected `requestState` sealing the operation kind\n * and sorted target paths. `mint` is async (HMAC + base64url).\n */\nexport async function buildInputRequired(\n pending: PendingState,\n inputs: readonly PendingInput[],\n ctx: ServerContext,\n): Promise<InputRequiredResult> {\n const inputRequests: Record<string, InputRequest> = {};\n for (const input of inputs) {\n inputRequests[input.key] = inputRequired.elicit({\n message: input.message,\n requestedSchema: requestedSchemaFor(input),\n });\n }\n const requestState = await requestStateCodec.mint(\n { op: pending.op, paths: [...pending.paths].sort() },\n ctx,\n );\n return inputRequired({ inputRequests, requestState });\n}\n\n/**\n * The shared read-state → `buildInputRequired` → mismatch-throw flow used by\n * every destructive-confirmation handler (delete, move, grant). No verified\n * state, OR a verified state belonging to a different `op` (e.g. a chained\n * call's grant round already resolved and this is now that same call's own\n * delete/move confirmation round), mints a fresh `input_required` for this\n * op. A retry whose verified state matches this op but not the pending path\n * set throws `FsError(INVALID_INPUT)` (R9) — uniformly surfaced as an\n * `isError` tool result by the handler's catch. Returns `undefined` on a\n * matching same-op retry so the caller proceeds.\n *\n * One home for the R9 binding check means a future fix cannot miss two of three\n * sites. The caller supplies `buildInputs` so only the prompt text varies.\n */\ninterface PendingRoundTripOpts {\n readonly op: PendingOp;\n readonly pending: readonly string[];\n readonly requestState: (() => PendingState | undefined) | undefined;\n /**\n * What the client declared it can do, or `undefined` when this connection\n * cannot say. Only a positively-absent `elicitation` short-circuits; see\n * {@link assertCanElicit}.\n */\n readonly clientCapabilities?: ClientCapabilities | undefined;\n readonly buildInputs: (pending: readonly string[]) => readonly PendingInput[];\n /** The live handler context; the codec binds the minted state to its method and caller. */\n readonly serverCtx: ServerContext;\n}\n\n/**\n * What to tell the model when the round-trip cannot happen. Each names the way\n * forward that does not need a confirmation, or says plainly that there is\n * none — a dead end the model can report is worth more than a retry loop.\n */\nconst NO_ELICITATION_HINT: Readonly<Record<PendingOp, string>> = {\n delete:\n 'Deleting a non-empty directory needs a confirmation this client cannot show. ' +\n 'Delete the entries inside it individually, or connect a client that declares the elicitation capability.',\n move:\n 'Overwriting an existing destination needs a confirmation this client cannot show. ' +\n 'Delete the destination first, or move to a path that does not exist yet.',\n copy:\n 'Overwriting an existing destination needs a confirmation this client cannot show. ' +\n 'Pass overwrite=true to replace it without confirming, or copy to a path that does not exist yet.',\n create:\n 'Overwriting an existing file needs a confirmation this client cannot show. ' +\n 'Pass overwrite: true on that entry to replace it without confirming, append: true to add to it, or read it and use edit.',\n grant:\n 'Granting access to a directory outside the allowed roots needs a confirmation this client cannot show. ' +\n 'Call list_roots and use a path under one of the roots it returns.',\n};\n\n/**\n * Fail early, and legibly, when the client cannot answer an embedded request.\n * The SDK checks each `inputRequests` entry against the declared client\n * capabilities and rejects the whole call with `-32021` before anything reaches\n * the wire — a protocol error the model never sees. Throwing `FsError` here\n * instead lands in the tool executor's catch and reaches the model as an\n * `isError` result carrying the workaround.\n *\n * `undefined` capabilities mean the connection cannot report them, NOT that the\n * client has none: proceed and let the SDK decide, exactly as before this check\n * existed.\n */\nfunction assertCanElicit(op: PendingOp, capabilities: ClientCapabilities | undefined): void {\n if (capabilities === undefined) return;\n if (capabilities.elicitation !== undefined) return;\n throw new FsError(ErrorCode.INVALID_INPUT, `${op}: ${NO_ELICITATION_HINT[op]}`);\n}\n\nexport async function pendingRoundTrip(\n opts: PendingRoundTripOpts,\n): Promise<InputRequiredResult | undefined> {\n const state = opts.requestState?.();\n // No verified state yet, OR the verified state belongs to a different\n // flow (e.g. an access-grant round's state is still the retried request's\n // requestState after the grant was applied, and this call is now the\n // destructive-confirmation round for the SAME tool call) — either way,\n // this op has not yet had its own round-trip, so mint a fresh\n // input_required rather than treating a foreign-but-valid state as a\n // tamper/mismatch error.\n if (state?.op !== opts.op) {\n assertCanElicit(opts.op, opts.clientCapabilities);\n return buildInputRequired(\n { op: opts.op, paths: opts.pending },\n opts.buildInputs(opts.pending),\n opts.serverCtx,\n );\n }\n // Retry for THIS op (R9): the verified state must bind the same pending set.\n if (!pathsEqual(state.paths, opts.pending)) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n `${opts.op}: confirmation does not match the requested paths`,\n );\n }\n return undefined;\n}\n\n/**\n * Round-trip key for `pendingSorted[index]` — the single home of the\n * `confirm_${i}` convention. The build side mints it with the map index; the\n * read side re-derives it from the same sorted pending set the requestState\n * binds (R9), so a format change lands here once instead of at six call\n * sites.\n */\nexport function confirmKey(index: number): string {\n return `confirm_${index}`;\n}\n\n/**\n * Read one pending item's boolean confirmation from a retried request's\n * `inputResponses`. Returns `true` only when the client explicitly accepted AND\n * the `confirm` field is `true`; every other outcome (decline, cancel, missing\n * key, accept-without-confirm) returns `false`, which the caller reports as\n * `CANCELLED` (R3 proceed, R4/R5 cancelled).\n */\nexport function readAcceptedConfirm(\n responses: Record<string, unknown> | undefined,\n key: string,\n): boolean {\n return acceptedContent(responses, key, ConfirmContent)?.confirm === true;\n}\n\n/**\n * Read one pending item's enum selection from a retried request's\n * `inputResponses`. Returns the chosen `value` only when the client explicitly\n * accepted AND the `choice` field is a string; every other outcome (decline,\n * cancel, missing key, accept-without-choice) returns `undefined`, which the\n * caller reports as `CANCELLED` — same contract as the boolean reader.\n *\n * The shape is SDK-validated, but the returned value is NOT checked for\n * membership in the `choices` offered in the request schema. Callers must\n * compare against the expected values (e.g. `choice === 'overwrite'`) before\n * acting — never echo the string back into a path or trust it as an enum\n * member.\n */\nexport function readAcceptedChoice(\n responses: Record<string, unknown> | undefined,\n key: string,\n): string | undefined {\n return acceptedContent(responses, key, ChoiceContent)?.choice;\n}\n\n/**\n * Read one pending item's multi-select enum selection from a retried request's\n * `inputResponses`. Returns the accepted `value`s only when the client\n * explicitly accepted AND `choice` is a string array of strings; every other\n * outcome (decline, cancel, missing key, accept-without-choice, non-array\n * `choice`, non-string elements) returns `undefined`, which the caller reports\n * as `CANCELLED` — same contract as the single-select reader.\n *\n * As with `readAcceptedChoice`, the returned values are NOT checked for\n * membership in the offered `choices`; callers must compare against the\n * expected set before acting.\n */\nexport function readAcceptedMultiChoice(\n responses: Record<string, unknown> | undefined,\n key: string,\n): string[] | undefined {\n return acceptedContent(responses, key, MultiChoiceContent)?.choice;\n}\n\n/** Refusal wording for the `CANCELLED` error; call after a `readAccepted*` reader returned nothing. */\nexport function describeRefusal(\n responses: Record<string, unknown> | undefined,\n key: string,\n): string {\n const view = inputResponse(responses, key);\n if (view.kind !== 'elicit') return 'not answered';\n if (view.action === 'decline') return 'declined by the user';\n if (view.action === 'cancel') return 'dismissed by the user';\n return 'answered without a valid choice';\n}\n\n/**\n * Order-independent equality of two sorted, de-duplicated path lists. Used to\n * compare a recomputed pending set against the `requestState`-bound set (R9):\n * both are sorted by construction, so a straight element-wise compare settles\n * it without allocating.\n */\nfunction pathsEqual(a: readonly string[], b: readonly string[]): boolean {\n // The length check is load-bearing, not an early-out: `every` alone returns\n // true whenever `a` is a prefix of a longer `b`, so a confirmation minted for\n // ['/x'] would authorize a retry for ['/x', '/y'] — a direct R9 bypass.\n return a.length === b.length && a.every((value, index) => value === b[index]);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"input-required.js","sourceRoot":"","sources":["../../src/core/input-required.ts"],"names":[],"mappings":"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","sourcesContent":["// Shared infrastructure for the SEP-2577 `input_required` multi-round-trip\n// destructive-confirmation flows (recursive delete, move overwrite, out-of-root\n// access grant). A handler returns `inputRequired(...)` instead of the deprecated\n// push-style server-to-client elicitation request; the client retries the same\n// `tools/call` carrying `inputResponses`, and the handler re-enters from the top\n// reading the verified `requestState` and the accepted responses.\n//\n// `requestState` round-trips through the client (attacker-controlled on\n// re-entry), so it is sealed with the SDK's HMAC-SHA256 codec. The payload binds\n// each confirmation to its operation kind and sorted target-path set (spec R9,\n// R10); the handler additionally rejects any retry whose decoded paths do not\n// match the retried request's parameters (the codec only proves the state was\n// not tampered — it cannot see the current args).\nimport type {\n ClientCapabilities,\n InputRequest,\n InputRequiredResult,\n RequestStateCodec,\n ServerContext,\n} from '@modelcontextprotocol/server';\nimport {\n acceptedContent,\n createRequestStateCodec,\n inputRequired,\n inputResponse,\n} from '@modelcontextprotocol/server';\n\nimport { randomBytes } from 'node:crypto';\n\nimport * as z from 'zod/v4';\n\nimport { ErrorCode, FsError } from './errors.ts';\nimport { Logger } from './observability.ts';\n\n/** The destructive operation a pending confirmation authorizes. */\ntype PendingOp = 'delete' | 'move' | 'copy' | 'create' | 'grant';\n\n/**\n * Integrity-protected state minted into an `input_required` result and echoed\n * back by the client on retry. `paths` is the sorted canonical set of target\n * paths the confirmation covers; the handler compares it to the retried\n * request's parameters and rejects a mismatch (R9).\n */\nexport interface PendingState {\n readonly op: PendingOp;\n readonly paths: readonly string[];\n}\n\n/** One embedded form-mode confirmation, keyed within the call. */\nexport interface PendingInput {\n /** Server-assigned key, unique within the `tools/call`. */\n readonly key: string;\n /** Human-readable prompt for this item. */\n readonly message: string;\n /**\n * When set, the form offers a single-select enum (`choice` field) over\n * these values instead of a boolean `confirm`.\n */\n readonly choices?: readonly string[];\n /**\n * When set with `choices`, the form offers a multi-select enum: `choice` is\n * a string array (`MultiSelectEnumSchema` shape) and the caller reads the\n * accepted set with `readAcceptedMultiChoice`. Single-select otherwise.\n */\n readonly multi?: boolean;\n}\n\n/**\n * HMAC key for the requestState codec. Read once from\n * `FS_REQUEST_STATE_KEY` (UTF-8, must be >=32 bytes); a random\n * 32-byte key is generated at boot when the env var is unset or too short. A\n * per-process key is correct for stdio and the single-node HTTP leg (decision\n * record 11). A server restart invalidates in-flight tokens; the client\n * re-requests, which is fail-closed and safe.\n */\nfunction configuredRequestStateKey(): Uint8Array | undefined {\n const env = process.env['FS_REQUEST_STATE_KEY'];\n if (env) {\n const bytes = Buffer.from(env, 'utf8');\n if (bytes.length >= 32) return bytes;\n Logger.warn(\n `FS_REQUEST_STATE_KEY is ${String(bytes.length)} bytes; 32 are required. Falling back to a random per-boot key — in-flight input_required rounds will not survive a restart.`,\n );\n }\n return undefined;\n}\n\n/**\n * What a minted `requestState` is bound to: the JSON-RPC method of the\n * request that minted it and the authenticated caller (`ctx.http` is absent\n * on stdio, so the caller part is empty there and constant across rounds).\n * The SDK stores an HMAC tag of this value in the token and refuses an echo\n * whose context yields a different value.\n */\nfunction requestStateBinding(ctx: ServerContext): string {\n return `${ctx.mcpReq.method}\\0${ctx.http?.authInfo?.clientId ?? ''}`;\n}\n\n/**\n * The codec: `mint` seals a `PendingState` into the opaque wire string a\n * handler returns from `inputRequired({ requestState })`; `verify` drops into\n * `ServerOptions.requestState.verify` and throws on tamper, expiry, or bind\n * mismatch (the seam answers with the frozen `-32602`).\n */\nexport const requestStateCodec: RequestStateCodec<PendingState> =\n createRequestStateCodec<PendingState>({\n key: configuredRequestStateKey() ?? randomBytes(32),\n bind: requestStateBinding,\n });\n\n// Response shapes handed to the SDK's schema-aware `acceptedContent` overload:\n// it returns `undefined` for a missing key, a decline/cancel, a non-elicit\n// response, AND a payload that fails validation — one call covers every refusal\n// case the readers below used to hand-check. `buildInputRequired` reuses\n// `ConfirmContent` as the no-choices form schema (below), so these sit above it.\nconst ConfirmContent = z.object({ confirm: z.boolean().meta({ title: 'Confirm' }) });\nconst ChoiceContent = z.object({ choice: z.string() });\nconst MultiChoiceContent = z.object({ choice: z.array(z.string()) });\n\n/**\n * The form the client renders for one pending input, as the Zod schema the\n * SDK converts to the elicitation wire shape. A boolean `confirm` when no\n * choices are offered; otherwise a `choice` enum over the offered values,\n * wrapped in an array for multi-select so the client may accept a subset.\n * The offered lists are never empty by construction (single-select always\n * offers two literals; multi-select only runs with two or more grant dirs),\n * so no empty-array guard is added. Each field carries a `title` so a host\n * that renders the form labels it instead of showing the property name.\n */\nfunction requestedSchemaFor(input: PendingInput): z.ZodObject {\n if (!input.choices) return ConfirmContent;\n const value = z.enum(input.choices);\n return z.object({\n choice: input.multi ? z.array(value).meta({ title: 'Allow' }) : value.meta({ title: 'Action' }),\n });\n}\n\n/**\n * Build an `input_required` result carrying one boolean confirmation per pending\n * item, plus the integrity-protected `requestState` sealing the operation kind\n * and sorted target paths. `mint` is async (HMAC + base64url).\n */\nexport async function buildInputRequired(\n pending: PendingState,\n inputs: readonly PendingInput[],\n ctx: ServerContext,\n): Promise<InputRequiredResult> {\n const inputRequests: Record<string, InputRequest> = {};\n for (const input of inputs) {\n inputRequests[input.key] = inputRequired.elicit({\n message: input.message,\n requestedSchema: requestedSchemaFor(input),\n });\n }\n const requestState = await requestStateCodec.mint(\n { op: pending.op, paths: [...pending.paths].sort() },\n ctx,\n );\n return inputRequired({ inputRequests, requestState });\n}\n\n/**\n * The shared read-state → `buildInputRequired` → mismatch-throw flow used by\n * every destructive-confirmation handler (delete, move, grant). No verified\n * state, OR a verified state belonging to a different `op` (e.g. a chained\n * call's grant round already resolved and this is now that same call's own\n * delete/move confirmation round), mints a fresh `input_required` for this\n * op. A retry whose verified state matches this op but not the pending path\n * set throws `FsError(INVALID_INPUT)` (R9) — uniformly surfaced as an\n * `isError` tool result by the handler's catch. Returns `undefined` on a\n * matching same-op retry so the caller proceeds.\n *\n * One home for the R9 binding check means a future fix cannot miss two of three\n * sites. The caller supplies `buildInputs` so only the prompt text varies.\n */\ninterface PendingRoundTripOpts {\n readonly op: PendingOp;\n readonly pending: readonly string[];\n readonly requestState: (() => PendingState | undefined) | undefined;\n /**\n * What the client declared it can do, or `undefined` when this connection\n * cannot say. Only a positively-absent `elicitation` short-circuits; see\n * {@link assertCanElicit}.\n */\n readonly clientCapabilities?: ClientCapabilities | undefined;\n readonly buildInputs: (pending: readonly string[]) => readonly PendingInput[];\n /** The live handler context; the codec binds the minted state to its method and caller. */\n readonly serverCtx: ServerContext;\n}\n\n/**\n * What to tell the model when the round-trip cannot happen. Each names the way\n * forward that does not need a confirmation, or says plainly that there is\n * none — a dead end the model can report is worth more than a retry loop.\n */\nconst NO_ELICITATION_HINT: Readonly<Record<PendingOp, string>> = {\n delete:\n 'Deleting a non-empty directory needs a confirmation this client cannot show. ' +\n 'Delete the entries inside it individually, or connect a client that declares the elicitation capability.',\n move:\n 'Overwriting an existing destination needs a confirmation this client cannot show. ' +\n 'Delete the destination first, or move to a path that does not exist yet.',\n copy:\n 'Overwriting an existing destination needs a confirmation this client cannot show. ' +\n 'Pass overwrite=true to replace it without confirming, or copy to a path that does not exist yet.',\n create:\n 'Overwriting an existing file needs a confirmation this client cannot show. ' +\n 'Pass overwrite: true on that entry to replace it without confirming, append: true to add to it, or read it and use edit.',\n grant:\n 'Granting access to a directory outside the allowed roots needs a confirmation this client cannot show. ' +\n 'Call list_roots and use a path under one of the roots it returns.',\n};\n\n/**\n * Fail early, and legibly, when the client cannot answer an embedded request.\n * The SDK checks each `inputRequests` entry against the declared client\n * capabilities and rejects the whole call with `-32021` before anything reaches\n * the wire — a protocol error the model never sees. Throwing `FsError` here\n * instead lands in the tool executor's catch and reaches the model as an\n * `isError` result carrying the workaround.\n *\n * `undefined` capabilities mean the connection cannot report them, NOT that the\n * client has none: proceed and let the SDK decide, exactly as before this check\n * existed.\n */\nfunction assertCanElicit(op: PendingOp, capabilities: ClientCapabilities | undefined): void {\n if (capabilities === undefined) return;\n if (capabilities.elicitation !== undefined) return;\n throw new FsError(ErrorCode.INVALID_INPUT, `${op}: ${NO_ELICITATION_HINT[op]}`);\n}\n\nexport async function pendingRoundTrip(\n opts: PendingRoundTripOpts,\n): Promise<InputRequiredResult | undefined> {\n const state = opts.requestState?.();\n // No verified state yet, OR the verified state belongs to a different\n // flow (e.g. an access-grant round's state is still the retried request's\n // requestState after the grant was applied, and this call is now the\n // destructive-confirmation round for the SAME tool call) — either way,\n // this op has not yet had its own round-trip, so mint a fresh\n // input_required rather than treating a foreign-but-valid state as a\n // tamper/mismatch error.\n if (state?.op !== opts.op) {\n assertCanElicit(opts.op, opts.clientCapabilities);\n return buildInputRequired(\n { op: opts.op, paths: opts.pending },\n opts.buildInputs(opts.pending),\n opts.serverCtx,\n );\n }\n // Retry for THIS op (R9): the verified state must bind the same pending set.\n if (!pathsEqual(state.paths, opts.pending)) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n `${opts.op}: confirmation does not match the requested paths`,\n );\n }\n return undefined;\n}\n\n/**\n * Round-trip key for `pendingSorted[index]` — the single home of the\n * `confirm_${i}` convention. The build side mints it with the map index; the\n * read side re-derives it from the same sorted pending set the requestState\n * binds (R9), so a format change lands here once instead of at six call\n * sites.\n */\nexport function confirmKey(index: number): string {\n return `confirm_${index}`;\n}\n\n/**\n * Read one pending item's boolean confirmation from a retried request's\n * `inputResponses`. Returns `true` only when the client explicitly accepted AND\n * the `confirm` field is `true`; every other outcome (decline, cancel, missing\n * key, accept-without-confirm) returns `false`, which the caller reports as\n * `CANCELLED` (R3 proceed, R4/R5 cancelled).\n */\nexport function readAcceptedConfirm(\n responses: Record<string, unknown> | undefined,\n key: string,\n): boolean {\n return acceptedContent(responses, key, ConfirmContent)?.confirm === true;\n}\n\n/**\n * Read one pending item's enum selection from a retried request's\n * `inputResponses`. Returns the chosen `value` only when the client explicitly\n * accepted AND the `choice` field is a string; every other outcome (decline,\n * cancel, missing key, accept-without-choice) returns `undefined`, which the\n * caller reports as `CANCELLED` — same contract as the boolean reader.\n *\n * The shape is SDK-validated, but the returned value is NOT checked for\n * membership in the `choices` offered in the request schema. Callers must\n * compare against the expected values (e.g. `choice === 'overwrite'`) before\n * acting — never echo the string back into a path or trust it as an enum\n * member.\n */\nexport function readAcceptedChoice(\n responses: Record<string, unknown> | undefined,\n key: string,\n): string | undefined {\n return acceptedContent(responses, key, ChoiceContent)?.choice;\n}\n\n/**\n * Read one pending item's multi-select enum selection from a retried request's\n * `inputResponses`. Returns the accepted `value`s only when the client\n * explicitly accepted AND `choice` is a string array of strings; every other\n * outcome (decline, cancel, missing key, accept-without-choice, non-array\n * `choice`, non-string elements) returns `undefined`, which the caller reports\n * as `CANCELLED` — same contract as the single-select reader.\n *\n * As with `readAcceptedChoice`, the returned values are NOT checked for\n * membership in the offered `choices`; callers must compare against the\n * expected set before acting.\n */\nexport function readAcceptedMultiChoice(\n responses: Record<string, unknown> | undefined,\n key: string,\n): string[] | undefined {\n return acceptedContent(responses, key, MultiChoiceContent)?.choice;\n}\n\n/** Refusal wording for the `CANCELLED` error; call after a `readAccepted*` reader returned nothing. */\nexport function describeRefusal(\n responses: Record<string, unknown> | undefined,\n key: string,\n): string {\n const view = inputResponse(responses, key);\n if (view.kind !== 'elicit') return 'not answered';\n if (view.action === 'decline') return 'declined by the user';\n if (view.action === 'cancel') return 'dismissed by the user';\n return 'answered without a valid choice';\n}\n\n/**\n * Order-independent equality of two sorted, de-duplicated path lists. Used to\n * compare a recomputed pending set against the `requestState`-bound set (R9):\n * both are sorted by construction, so a straight element-wise compare settles\n * it without allocating.\n */\nfunction pathsEqual(a: readonly string[], b: readonly string[]): boolean {\n // The length check is load-bearing, not an early-out: `every` alone returns\n // true whenever `a` is a prefix of a longer `b`, so a confirmation minted for\n // ['/x'] would authorize a retry for ['/x', '/y'] — a direct R9 bypass.\n return a.length === b.length && a.every((value, index) => value === b[index]);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-completer.d.ts","sourceRoot":"","sources":["../../src/core/path-completer.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"path-completer.d.ts","sourceRoot":"","sources":["../../src/core/path-completer.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AA0K3C;;;GAGG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA0BzF"}
|
|
@@ -72,8 +72,6 @@ function collectAllowedRoots(allowed, predicate) {
|
|
|
72
72
|
}
|
|
73
73
|
function findRootPrefixMatches(currentValue, allowed) {
|
|
74
74
|
const rootPrefix = parseNamedRootInput(currentValue)?.rootName.toLowerCase() ?? '';
|
|
75
|
-
if (!rootPrefix)
|
|
76
|
-
return collectAllowedRoots(allowed, () => true);
|
|
77
75
|
return collectAllowedRoots(allowed, (root) => basename(root).toLowerCase().startsWith(rootPrefix));
|
|
78
76
|
}
|
|
79
77
|
function findMatchingRoots(searchDir, prefix, allowed) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-completer.js","sourceRoot":"","sources":["../../src/core/path-completer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErF,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EACL,uBAAuB,EACvB,UAAU,EACV,OAAO,EACP,aAAa,EACb,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC,+EAA+E;AAE/E,SAAS,oBAAoB,CAAC,KAAa;IACzC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,eAAe,CACtB,IAAY,EACZ,QAAgB,EAChB,iBAA0B;IAE1B,MAAM,eAAe,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/D,IAAI,iBAAiB;QAAE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACzE,OAAO;QACL,SAAS,EAAE,OAAO,CAAC,eAAe,CAAC;QACnC,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC;KAClC,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAa;IACxC,MAAM,eAAe,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,CAAC,eAAe;QAAE,OAAO,SAAS,CAAC;IACvC,MAAM,UAAU,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,UAAU,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC3E,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IACtD,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,EAAE,CAAC;AACxE,CAAC;AAED,SAAS,qBAAqB,CAAC,QAAgB,EAAE,OAA0B;IACzE,MAAM,kBAAkB,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IAClD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,KAAK,kBAAkB,CAAC,CAAC;AAC/F,CAAC;AAED,SAAS,uBAAuB,CAC9B,YAAoB,EACpB,OAA0B;IAE1B,MAAM,MAAM,GAAG,mBAAmB,CAAC,YAAY,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,MAAM,IAAI,GAAG,qBAAqB,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC7D,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;IAC7D,OAAO,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;AACpE,CAAC;AAED,KAAK,UAAU,4BAA4B,CACzC,IAAY,EACZ,OAA0B;IAE1B,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1D,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,OAAO,KAAK,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;QACzC,OAAO,IAAI,KAAK,IAAI,IAAI,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;YAC1E,MAAM,CAAC,KAAK,CAAC,sDAAsD,EAAE;gBACnE,IAAI;gBACJ,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;aACnB,CAAC,CAAC;QACL,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAa;IAC3C,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC;AACxD,CAAC;AAED,SAAS,mBAAmB,CAC1B,OAA0B,EAC1B,SAAoC;IAEpC,OAAO,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,qBAAqB,CAAC,YAAoB,EAAE,OAA0B;IAC7E,MAAM,UAAU,GAAG,mBAAmB,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;IACnF,IAAI,CAAC,UAAU;QAAE,OAAO,mBAAmB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACjE,OAAO,mBAAmB,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAC3C,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CACpD,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CACxB,SAAiB,EACjB,MAAc,EACd,OAA0B;IAE1B,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACzC,MAAM,mBAAmB,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IACrD,OAAO,mBAAmB,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;QAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,mBAAmB,CAAC;YAAE,OAAO,KAAK,CAAC;QAC5D,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,sBAAsB,CAC7B,UAA6B,EAC7B,WAA8B;IAE9B,MAAM,KAAK,GAAG,CAAC,CAAS,EAAW,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CACvD,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CACzF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,sBAAsB,CACnC,SAAiB,EACjB,MAAc,EACd,OAA0B,EAC1B,WAAuC;IAEvC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,CAAC,MAAM,4BAA4B,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9E,IAAI,CAAC;QACH,uEAAuE;QACvE,uEAAuE;QACvE,sEAAsE;QACtE,sEAAsE;QACtE,+BAA+B;QAC/B,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;YACzC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;gBAC9B,IAAI,MAAM,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC;oBAAE,SAAS;gBACjF,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC7C,IAAI,WAAW,EAAE,CAAC,QAAQ,CAAC;oBAAE,SAAS;gBACtC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,QAAQ,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YACrE,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC3B,kDAAkD;YACpD,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;YAC1E,MAAM,CAAC,IAAI,CAAC,sDAAsD,EAAE;gBAClE,SAAS;gBACT,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;aACnB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,gBAAgB,CACvB,YAAoB,EACpB,OAA0B;IAE1B,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;IAC7D,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,OAAO,eAAe,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,IAAI,GAAG,EAAE,YAAY,EAAE,iBAAiB,CAAC,CAAC;IAC3F,CAAC;IACD,MAAM,gBAAgB,GAAG,uBAAuB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACxE,IAAI,gBAAgB;QAAE,OAAO,gBAAgB,CAAC;IAC9C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,IAAI;YAAE,OAAO,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,iBAAiB,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,SAAoB,EAAE,KAAa;IACpE,MAAM,OAAO,GAAG,SAAS,CAAC,qBAAqB,EAAE,CAAC;IAElD,IAAI,CAAC;QACH,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACjD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,qBAAqB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC;QAC9E,CAAC;QAED,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QACtC,MAAM,UAAU,GAAG,MAAM,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAChF,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CACzB,CAAC;QACF,MAAM,WAAW,GAAG,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAClE,OAAO,sBAAsB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC;IACxF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACxB,MAAM,CAAC,IAAI,CAAC,uDAAuD,EAAE;YACnE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC/E,CAAC,CAAC;QACH,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC","sourcesContent":["import { opendir, stat } from 'node:fs/promises';\nimport { basename, dirname, isAbsolute, join, parse, resolve, sep } from 'node:path';\n\nimport { isNodeError, isNotFoundErrno, rethrowIfAborted } from './errors.ts';\nimport { Logger } from './observability.ts';\nimport {\n isPathWithinDirectories,\n isSamePath,\n isSlash,\n normalizePath,\n toPosixPath,\n} from './path-utils.ts';\nimport type { PathGuard } from './path.ts';\nimport { resolveRealPath } from './path.ts';\n\nconst MAX_COMPLETION_ITEMS = 100;\n\n// ─── pure path-completion helpers ───────────────────────────────────────────\n\nfunction hasTrailingSeparator(value: string): boolean {\n return value.length > 0 && isSlash(value.charCodeAt(value.length - 1));\n}\n\nfunction resolveFromBase(\n base: string,\n rawValue: string,\n trailingSeparator: boolean,\n): { searchDir: string; prefix: string } {\n const normalizedValue = normalizePath(resolve(base, rawValue));\n if (trailingSeparator) return { searchDir: normalizedValue, prefix: '' };\n return {\n searchDir: dirname(normalizedValue),\n prefix: basename(normalizedValue),\n };\n}\n\nfunction parseNamedRootInput(value: string): { rootName: string; remainder: string } | undefined {\n const normalizedInput = toPosixPath(value);\n if (!normalizedInput) return undefined;\n const slashIndex = normalizedInput.indexOf('/');\n if (slashIndex === -1) return { rootName: normalizedInput, remainder: '' };\n const rootName = normalizedInput.slice(0, slashIndex);\n if (!rootName) return undefined;\n return { rootName, remainder: normalizedInput.slice(slashIndex + 1) };\n}\n\nfunction findAllowedRootByName(rootName: string, allowed: readonly string[]): string | undefined {\n const normalizedRootName = rootName.toLowerCase();\n return allowed.find((candidate) => basename(candidate).toLowerCase() === normalizedRootName);\n}\n\nfunction resolveNamedRootContext(\n currentValue: string,\n allowed: readonly string[],\n): { searchDir: string; prefix: string } | undefined {\n const parsed = parseNamedRootInput(currentValue);\n if (!parsed) return undefined;\n const root = findAllowedRootByName(parsed.rootName, allowed);\n if (!root) return undefined;\n const trailingSeparator = hasTrailingSeparator(currentValue);\n return resolveFromBase(root, parsed.remainder, trailingSeparator);\n}\n\nasync function isAllowedCompletionDirectory(\n path: string,\n allowed: readonly string[],\n): Promise<boolean> {\n if (!isPathWithinDirectories(path, allowed)) return false;\n try {\n const stats = await stat(path);\n if (!stats.isDirectory()) return false;\n const real = await resolveRealPath(path);\n return real !== null && isPathWithinDirectories(real, allowed);\n } catch (err) {\n if (!isNotFoundErrno(err) && (!isNodeError(err) || err.code !== 'EACCES')) {\n Logger.debug('isAllowedCompletionDirectory: unexpected probe error', {\n path,\n error: String(err),\n });\n }\n return false;\n }\n}\n\nfunction withDirectorySeparator(value: string): string {\n return value.endsWith(sep) ? value : `${value}${sep}`;\n}\n\nfunction collectAllowedRoots(\n allowed: readonly string[],\n predicate: (root: string) => boolean,\n): string[] {\n return allowed.filter(predicate).map(withDirectorySeparator);\n}\n\nfunction findRootPrefixMatches(currentValue: string, allowed: readonly string[]): string[] {\n const rootPrefix = parseNamedRootInput(currentValue)?.rootName.toLowerCase() ?? '';\n if (!rootPrefix) return collectAllowedRoots(allowed, () => true);\n return collectAllowedRoots(allowed, (root) =>\n basename(root).toLowerCase().startsWith(rootPrefix),\n );\n}\n\nfunction findMatchingRoots(\n searchDir: string,\n prefix: string,\n allowed: readonly string[],\n): string[] {\n const lowerPrefix = prefix.toLowerCase();\n const normalizedSearchDir = normalizePath(searchDir);\n return collectAllowedRoots(allowed, (root) => {\n const rootDir = dirname(root);\n if (!isSamePath(rootDir, normalizedSearchDir)) return false;\n return basename(root).toLowerCase().startsWith(lowerPrefix);\n });\n}\n\nfunction mergeCompletionMatches(\n dirMatches: readonly string[],\n rootMatches: readonly string[],\n): string[] {\n const isDir = (v: string): boolean => v.endsWith(sep);\n return [...new Set([...dirMatches, ...rootMatches])].sort(\n (left, right) => Number(isDir(right)) - Number(isDir(left)) || left.localeCompare(right),\n );\n}\n\nasync function findMatchesInDirectory(\n searchDir: string,\n prefix: string,\n allowed: readonly string[],\n isSensitive?: (path: string) => boolean,\n): Promise<string[]> {\n const matches: string[] = [];\n if (!(await isAllowedCompletionDirectory(searchDir, allowed))) return matches;\n try {\n // Stream via opendir and collect every match; the MAX_COMPLETION_ITEMS\n // cap is applied AFTER the alphabetical sort in mergeCompletionMatches\n // (slice at the call site). Capping here would keep the opendir-first\n // 100, not the alphabetically-first 100 — the sort would only reorder\n // an already-arbitrary subset.\n const dir = await opendir(searchDir);\n try {\n const lowerPrefix = prefix.toLowerCase();\n for await (const entry of dir) {\n if (prefix !== '' && !entry.name.toLowerCase().startsWith(lowerPrefix)) continue;\n const fullPath = join(searchDir, entry.name);\n if (isSensitive?.(fullPath)) continue;\n matches.push(entry.isDirectory() ? `${fullPath}${sep}` : fullPath);\n }\n } finally {\n await dir.close().catch(() => {\n /* dir already closed or opendir never resolved */\n });\n }\n } catch (err) {\n if (!isNodeError(err) || (err.code !== 'ENOENT' && err.code !== 'EACCES')) {\n Logger.warn('PathCompleter.findMatchesInDirectory: readdir failed', {\n searchDir,\n error: String(err),\n });\n }\n }\n return matches;\n}\n\nfunction getSearchContext(\n currentValue: string,\n allowed: readonly string[],\n): { searchDir: string; prefix: string } | undefined {\n const trailingSeparator = hasTrailingSeparator(currentValue);\n if (isAbsolute(currentValue)) {\n return resolveFromBase(parse(currentValue).root || sep, currentValue, trailingSeparator);\n }\n const namedRootContext = resolveNamedRootContext(currentValue, allowed);\n if (namedRootContext) return namedRootContext;\n if (allowed.length === 1) {\n const base = allowed[0];\n if (base) return resolveFromBase(base, currentValue, trailingSeparator);\n }\n return undefined;\n}\n\n/**\n * Path suggestions for `completion/complete` on the file template. One\n * `opendir` per call; clients debounce, so nothing is cached here.\n */\nexport async function suggestPaths(pathGuard: PathGuard, value: string): Promise<string[]> {\n const allowed = pathGuard.getAllowedDirectories();\n\n try {\n if (!value) {\n return allowed.slice(0, MAX_COMPLETION_ITEMS);\n }\n\n const context = getSearchContext(value, allowed);\n if (!context) {\n return findRootPrefixMatches(value, allowed).slice(0, MAX_COMPLETION_ITEMS);\n }\n\n const { searchDir, prefix } = context;\n const dirMatches = await findMatchesInDirectory(searchDir, prefix, allowed, (p) =>\n pathGuard.isSensitive(p),\n );\n const rootMatches = findMatchingRoots(searchDir, prefix, allowed);\n return mergeCompletionMatches(dirMatches, rootMatches).slice(0, MAX_COMPLETION_ITEMS);\n } catch (error) {\n rethrowIfAborted(error);\n Logger.warn('suggestPaths: completion failed, returning empty list', {\n error: error instanceof Error ? (error.stack ?? error.message) : String(error),\n });\n return [];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"path-completer.js","sourceRoot":"","sources":["../../src/core/path-completer.ts"],"names":[],"mappings":"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","sourcesContent":["import { opendir, stat } from 'node:fs/promises';\nimport { basename, dirname, isAbsolute, join, parse, resolve, sep } from 'node:path';\n\nimport { isNodeError, isNotFoundErrno, rethrowIfAborted } from './errors.ts';\nimport { Logger } from './observability.ts';\nimport {\n isPathWithinDirectories,\n isSamePath,\n isSlash,\n normalizePath,\n toPosixPath,\n} from './path-utils.ts';\nimport type { PathGuard } from './path.ts';\nimport { resolveRealPath } from './path.ts';\n\nconst MAX_COMPLETION_ITEMS = 100;\n\n// ─── pure path-completion helpers ───────────────────────────────────────────\n\nfunction hasTrailingSeparator(value: string): boolean {\n return value.length > 0 && isSlash(value.charCodeAt(value.length - 1));\n}\n\nfunction resolveFromBase(\n base: string,\n rawValue: string,\n trailingSeparator: boolean,\n): { searchDir: string; prefix: string } {\n const normalizedValue = normalizePath(resolve(base, rawValue));\n if (trailingSeparator) return { searchDir: normalizedValue, prefix: '' };\n return {\n searchDir: dirname(normalizedValue),\n prefix: basename(normalizedValue),\n };\n}\n\nfunction parseNamedRootInput(value: string): { rootName: string; remainder: string } | undefined {\n const normalizedInput = toPosixPath(value);\n if (!normalizedInput) return undefined;\n const slashIndex = normalizedInput.indexOf('/');\n if (slashIndex === -1) return { rootName: normalizedInput, remainder: '' };\n const rootName = normalizedInput.slice(0, slashIndex);\n if (!rootName) return undefined;\n return { rootName, remainder: normalizedInput.slice(slashIndex + 1) };\n}\n\nfunction findAllowedRootByName(rootName: string, allowed: readonly string[]): string | undefined {\n const normalizedRootName = rootName.toLowerCase();\n return allowed.find((candidate) => basename(candidate).toLowerCase() === normalizedRootName);\n}\n\nfunction resolveNamedRootContext(\n currentValue: string,\n allowed: readonly string[],\n): { searchDir: string; prefix: string } | undefined {\n const parsed = parseNamedRootInput(currentValue);\n if (!parsed) return undefined;\n const root = findAllowedRootByName(parsed.rootName, allowed);\n if (!root) return undefined;\n const trailingSeparator = hasTrailingSeparator(currentValue);\n return resolveFromBase(root, parsed.remainder, trailingSeparator);\n}\n\nasync function isAllowedCompletionDirectory(\n path: string,\n allowed: readonly string[],\n): Promise<boolean> {\n if (!isPathWithinDirectories(path, allowed)) return false;\n try {\n const stats = await stat(path);\n if (!stats.isDirectory()) return false;\n const real = await resolveRealPath(path);\n return real !== null && isPathWithinDirectories(real, allowed);\n } catch (err) {\n if (!isNotFoundErrno(err) && (!isNodeError(err) || err.code !== 'EACCES')) {\n Logger.debug('isAllowedCompletionDirectory: unexpected probe error', {\n path,\n error: String(err),\n });\n }\n return false;\n }\n}\n\nfunction withDirectorySeparator(value: string): string {\n return value.endsWith(sep) ? value : `${value}${sep}`;\n}\n\nfunction collectAllowedRoots(\n allowed: readonly string[],\n predicate: (root: string) => boolean,\n): string[] {\n return allowed.filter(predicate).map(withDirectorySeparator);\n}\n\nfunction findRootPrefixMatches(currentValue: string, allowed: readonly string[]): string[] {\n const rootPrefix = parseNamedRootInput(currentValue)?.rootName.toLowerCase() ?? '';\n return collectAllowedRoots(allowed, (root) =>\n basename(root).toLowerCase().startsWith(rootPrefix),\n );\n}\n\nfunction findMatchingRoots(\n searchDir: string,\n prefix: string,\n allowed: readonly string[],\n): string[] {\n const lowerPrefix = prefix.toLowerCase();\n const normalizedSearchDir = normalizePath(searchDir);\n return collectAllowedRoots(allowed, (root) => {\n const rootDir = dirname(root);\n if (!isSamePath(rootDir, normalizedSearchDir)) return false;\n return basename(root).toLowerCase().startsWith(lowerPrefix);\n });\n}\n\nfunction mergeCompletionMatches(\n dirMatches: readonly string[],\n rootMatches: readonly string[],\n): string[] {\n const isDir = (v: string): boolean => v.endsWith(sep);\n return [...new Set([...dirMatches, ...rootMatches])].sort(\n (left, right) => Number(isDir(right)) - Number(isDir(left)) || left.localeCompare(right),\n );\n}\n\nasync function findMatchesInDirectory(\n searchDir: string,\n prefix: string,\n allowed: readonly string[],\n isSensitive?: (path: string) => boolean,\n): Promise<string[]> {\n const matches: string[] = [];\n if (!(await isAllowedCompletionDirectory(searchDir, allowed))) return matches;\n try {\n // Stream via opendir and collect every match; the MAX_COMPLETION_ITEMS\n // cap is applied AFTER the alphabetical sort in mergeCompletionMatches\n // (slice at the call site). Capping here would keep the opendir-first\n // 100, not the alphabetically-first 100 — the sort would only reorder\n // an already-arbitrary subset.\n const dir = await opendir(searchDir);\n try {\n const lowerPrefix = prefix.toLowerCase();\n for await (const entry of dir) {\n if (prefix !== '' && !entry.name.toLowerCase().startsWith(lowerPrefix)) continue;\n const fullPath = join(searchDir, entry.name);\n if (isSensitive?.(fullPath)) continue;\n matches.push(entry.isDirectory() ? `${fullPath}${sep}` : fullPath);\n }\n } finally {\n await dir.close().catch(() => {\n /* dir already closed or opendir never resolved */\n });\n }\n } catch (err) {\n if (!isNodeError(err) || (err.code !== 'ENOENT' && err.code !== 'EACCES')) {\n Logger.warn('PathCompleter.findMatchesInDirectory: readdir failed', {\n searchDir,\n error: String(err),\n });\n }\n }\n return matches;\n}\n\nfunction getSearchContext(\n currentValue: string,\n allowed: readonly string[],\n): { searchDir: string; prefix: string } | undefined {\n const trailingSeparator = hasTrailingSeparator(currentValue);\n if (isAbsolute(currentValue)) {\n return resolveFromBase(parse(currentValue).root || sep, currentValue, trailingSeparator);\n }\n const namedRootContext = resolveNamedRootContext(currentValue, allowed);\n if (namedRootContext) return namedRootContext;\n if (allowed.length === 1) {\n const base = allowed[0];\n if (base) return resolveFromBase(base, currentValue, trailingSeparator);\n }\n return undefined;\n}\n\n/**\n * Path suggestions for `completion/complete` on the file template. One\n * `opendir` per call; clients debounce, so nothing is cached here.\n */\nexport async function suggestPaths(pathGuard: PathGuard, value: string): Promise<string[]> {\n const allowed = pathGuard.getAllowedDirectories();\n\n try {\n if (!value) {\n return allowed.slice(0, MAX_COMPLETION_ITEMS);\n }\n\n const context = getSearchContext(value, allowed);\n if (!context) {\n return findRootPrefixMatches(value, allowed).slice(0, MAX_COMPLETION_ITEMS);\n }\n\n const { searchDir, prefix } = context;\n const dirMatches = await findMatchesInDirectory(searchDir, prefix, allowed, (p) =>\n pathGuard.isSensitive(p),\n );\n const rootMatches = findMatchingRoots(searchDir, prefix, allowed);\n return mergeCompletionMatches(dirMatches, rootMatches).slice(0, MAX_COMPLETION_ITEMS);\n } catch (error) {\n rethrowIfAborted(error);\n Logger.warn('suggestPaths: completion failed, returning empty list', {\n error: error instanceof Error ? (error.stack ?? error.message) : String(error),\n });\n return [];\n }\n}\n"]}
|
package/dist/core/path.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/core/path.ts"],"names":[],"mappings":"AAsCA,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAwCD,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEhE;AAMD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/core/path.ts"],"names":[],"mappings":"AAsCA,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAwCD,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEhE;AAMD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAE7E;AAED,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAexB;AAED,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,EAAE,CAAC,CAWnB;AAMD;;;;;;;;;;GAUG;AACH,qBAAa,SAAS;;IACpB,OAAO,CAAC,uBAAuB,CAAuB;IACtD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,cAAc,CAAgB;IAOtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;gBAExC,OAAO,CAAC,EAAE,aAAa;IAInC,UAAU,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;IAI7C,aAAa,IAAI,OAAO;IAIxB;;;;OAIG;YACW,YAAY;IAS1B,qBAAqB,IAAI,MAAM,EAAE;IAOjC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAItC;;;;;;;;;;;;;OAaG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBtD,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlE;;;;;;;;OAQG;YACW,mBAAmB;IAYjC,8EAA8E;YAChE,qBAAqB;IAgBnC;;;;;;;;;;OAUG;IACG,cAAc,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAiCjE;;;;;;;;;;OAUG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAwCvC,gBAAgB;IAa9B,OAAO,CAAC,4BAA4B;IAepC,OAAO,CAAC,cAAc;YAqDR,mBAAmB;IA2C3B,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAmClF,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAsBvE,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM;IAwBxD,aAAa,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO;IAW9C,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,iBAAiB;YAOX,8BAA8B;IAwDtC,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoB5D,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoE7D,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC;CAoDnD"}
|
package/dist/core/path.js
CHANGED
|
@@ -42,14 +42,7 @@ export function toPosixRelative(from, to) {
|
|
|
42
42
|
// Resolver pipeline
|
|
43
43
|
// ---------------------------------------------------------------------------
|
|
44
44
|
export function normalizeAllowedDirectories(dirs) {
|
|
45
|
-
|
|
46
|
-
for (const dir of dirs) {
|
|
47
|
-
const entry = normalizeAllowedDirectory(dir);
|
|
48
|
-
if (entry.length > 0) {
|
|
49
|
-
normalized.push(entry);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return [...new Set(normalized)];
|
|
45
|
+
return [...new Set(dirs.map(normalizeAllowedDirectory).filter((entry) => entry.length > 0))];
|
|
53
46
|
}
|
|
54
47
|
export async function resolveRealPath(normalized, signal) {
|
|
55
48
|
try {
|
package/dist/core/path.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/core/path.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Stats } from 'node:fs';\nimport { lstat, readlink, realpath, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';\n\nimport { withAbort } from './concurrency.ts';\nimport { cli } from './config.ts';\nimport {\n ERRNO_MAP,\n ErrorCode,\n FsError,\n isFsError,\n isNodeError,\n isNotFoundErrno,\n rethrowIfAborted,\n SKIPPABLE_ERRNOS,\n SKIPPABLE_FS_CODES,\n} from './errors.ts';\nimport { Logger } from './observability.ts';\nimport { findProjectRoot, isUnsafeCwdPath, resolveConfiguredDirs } from './path-discovery.ts';\nimport {\n getReservedDeviceNameForPath,\n isPathWithinDirectories,\n isSamePath,\n isWindowsDriveRelativePath,\n normalizeAllowedDirectory,\n normalizePath,\n parseTrueEnvFlag,\n toPosixPath,\n} from './path-utils.ts';\nimport { SensitiveMatcher } from './sensitive.ts';\nimport { ROOTS_TIMEOUT_MS } from './util.ts';\n\n// Allowed-directory assembly and the PathGuard that enforces it. The\n// sensitive-file denylist lives in sensitive.ts, and the character-level\n// primitives (IS_WINDOWS, isAlpha, isSlash, toPosixPath) live in\n// path-utils.ts.\n\nexport interface ValidatedPathDetails {\n requestedPath: string;\n resolvedPath: string;\n isSymlink: boolean;\n}\n\nexport interface ServerOptions {\n allowCwd?: boolean;\n cliAllowedDirs?: string[];\n readOnly?: boolean;\n}\n\n/** True when `normalizedRoot` really resolves inside `bounds` (FS_ROOT_BOUNDARY). */\nasync function isRootWithin(\n normalizedRoot: string,\n bounds: readonly string[],\n signal?: AbortSignal,\n): Promise<boolean> {\n try {\n signal?.throwIfAborted();\n const realPath = await withAbort(realpath(normalizedRoot), signal);\n return isPathWithinDirectories(normalizePath(realPath), bounds);\n } catch (error) {\n rethrowIfAborted(error);\n if (isNotFoundErrno(error)) {\n return false;\n }\n Logger.warn('grantBoundary: realpath failed unexpectedly', {\n root: normalizedRoot,\n error: String(error),\n });\n return false;\n }\n}\n\nasync function filterRootsWithin(\n roots: readonly string[],\n bounds: readonly string[],\n signal?: AbortSignal,\n): Promise<string[]> {\n const normalizedBounds = normalizeAllowedDirectories(bounds);\n const normalizedRoots = roots.map(normalizePath);\n\n const results = await Promise.all(\n normalizedRoots.map((root) => isRootWithin(root, normalizedBounds, signal)),\n );\n\n return normalizedRoots.filter((_, i) => results[i]);\n}\n\n/** `path.relative` with forward slashes, so displayed paths match across platforms. */\nexport function toPosixRelative(from: string, to: string): string {\n return toPosixPath(relative(from, to));\n}\n\n// ---------------------------------------------------------------------------\n// Resolver pipeline\n// ---------------------------------------------------------------------------\n\nexport function normalizeAllowedDirectories(dirs: readonly string[]): string[] {\n const normalized: string[] = [];\n for (const dir of dirs) {\n const entry = normalizeAllowedDirectory(dir);\n if (entry.length > 0) {\n normalized.push(entry);\n }\n }\n return [...new Set(normalized)];\n}\n\nexport async function resolveRealPath(\n normalized: string,\n signal?: AbortSignal,\n): Promise<string | null> {\n try {\n signal?.throwIfAborted();\n const realPath = await withAbort(realpath(normalized), signal);\n return normalizeAllowedDirectory(realPath);\n } catch (error) {\n rethrowIfAborted(error);\n // Only suppress ENOENT — the path genuinely does not exist.\n // EACCES, EIO, and other unexpected errors are rethrown so callers\n // cannot silently operate with a narrowed allowed-directory set.\n if (isNotFoundErrno(error)) {\n return null;\n }\n throw error;\n }\n}\n\nexport async function resolveAllowedDirectoriesState(\n dirs: readonly string[],\n signal?: AbortSignal,\n): Promise<string[]> {\n const primary = normalizeAllowedDirectories(dirs);\n const reals = await Promise.all(primary.map((dir) => resolveRealPath(dir, signal)));\n return [\n ...new Set(\n primary.flatMap((dir, i) => {\n const real = reals[i];\n return real && !isSamePath(real, dir) ? [dir, real] : [dir];\n }),\n ),\n ];\n}\n\n// ---------------------------------------------------------------------------\n// Windows helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Accepted risk: validation methods resolve/verify a path (symlinks, boundaries,\n * sensitivity) and then return a plain string; the actual fs operation happens\n * afterward as a separate syscall (classic TOCTOU). A symlink swapped in that\n * exact window could redirect the follow-up operation. This is mitigated by\n * re-validating the resolved real path (not just the requested path) and by\n * walking ancestors to catch escapes before the target exists, but it is not\n * eliminated — doing so would require fd-based operations (open with\n * O_NOFOLLOW / operate on the resolved fd) throughout core/fs.ts. Acceptable\n * tradeoff for a local, single-user filesystem server today.\n */\nexport class PathGuard {\n private allowedDirectoriesState: string[] | undefined;\n private readonly sensitive = new SensitiveMatcher();\n /**\n * Directories added by an accepted access grant (R8) — from the tool\n * executor's grant round-trip, or from a client's declared workspace roots on\n * the legacy stdio leg. Held separately from the configured baseline because\n * `recomputeAllowedDirectories` rebuilds that baseline from CLI args and env\n * on every call: anything merged into it would be recomputed away.\n *\n * These are NOT filtered against the baseline. A granted directory is\n * out-of-baseline by definition, so a baseline filter dropped every one while\n * `applyGrant` still reported success — the whole round-trip prompted the\n * user and then changed nothing. FS_ROOT_BOUNDARY and the unsafe-path denylist\n * remain the limits, both checked in `applyGrant` — the only writer of this\n * field — so the recompute can rebuild from it without re-filtering.\n */\n private grantedDirectories: string[] = [];\n private rootBoundaries: string[] = [];\n\n // ponytail: one mutex per PathGuard. If per-session grant throughput ever\n // matters, split into per-grant-dir locks; a single lock is correct for the\n // stdio + InMemoryEventStore single-process model.\n #mutex = Promise.resolve();\n\n private readonly options: ServerOptions | undefined;\n\n constructor(options?: ServerOptions) {\n this.options = options;\n }\n\n initialize(expanded: readonly string[]): void {\n this.allowedDirectoriesState = normalizeAllowedDirectories(expanded);\n }\n\n isInitialized(): boolean {\n return this.allowedDirectoriesState !== undefined;\n }\n\n /**\n * Run `fn` as the next holder of the guard's single mutation lock. Every\n * change to the allowed set goes through this so concurrent grants cannot\n * interleave their read-`await`-write and lose a grant (GRANT-1).\n */\n private async runExclusive<T>(fn: () => Promise<T>): Promise<T> {\n const result = this.#mutex.then(fn, fn);\n this.#mutex = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n }\n\n getAllowedDirectories(): string[] {\n if (!this.allowedDirectoriesState) {\n return [];\n }\n return [...this.allowedDirectoriesState];\n }\n\n isSensitive(filePath: string): boolean {\n return this.sensitive.isSensitive(filePath);\n }\n\n /**\n * True when `entryPath` is both within the guard's allowed directories and\n * not sensitive, checking the requested AND resolved paths. EVERY entry is\n * realpath-resolved via validateExistingPathDetailed: a symlinked ancestor\n * directory pointing outside the sandbox would otherwise pass the lexical\n * containment check (fs.glob follows symlinks and yields external entries as\n * non-symlink dirents). validateExistingPathDetailed re-checks containment on\n * the real path against this.allowedDirectoriesState (the guard's full\n * allowed set — a superset of the single-root `bounds` callers pass) and\n * re-checks sensitivity on the resolved target, throwing ACCESS_DENIED for\n * escapes/sensitive, which the catch below turns into a filter. Skippable\n * errno/fs errors return false (the entry is filtered, not fatal). The\n * accepted TOCTOU window is documented at the class docstring above.\n */\n async isEntryAccessible(entryPath: string): Promise<boolean> {\n const isSensitive = (requestedPath: string, resolvedPath: string): boolean =>\n this.isSensitive(requestedPath) || this.isSensitive(resolvedPath);\n try {\n const validated = await this.validateExistingPathDetailed(entryPath);\n return !isSensitive(validated.requestedPath, validated.resolvedPath);\n } catch (error) {\n if (isFsError(error)) {\n if (SKIPPABLE_FS_CODES.has(error.code)) return false;\n throw error;\n }\n if (isNodeError(error) && error.code !== undefined && SKIPPABLE_ERRNOS.has(error.code))\n return false;\n throw error;\n }\n }\n\n async validateExistingPath(requestedPath: string): Promise<string> {\n const details = await this.validateExistingPathDetailed(requestedPath);\n return details.resolvedPath;\n }\n\n /**\n * True when a grant must never admit `targetDir` — an unsafe path (home,\n * /etc, C:\\Windows, ...). Checked on the lexical path AND on the one it\n * resolves to: `resolveAllowedDirectoriesState` pushes each root's realpath\n * into the allowed set, so a lexical-only check let a symlink or junction\n * aliasing $HOME/C:\\Windows in under an innocuous name — and the confirmation\n * prompt showed the alias, not the target. A target that cannot be resolved\n * is judged lexically; it does not exist to escape into.\n */\n private async isUnsafeGrantTarget(targetDir: string): Promise<boolean> {\n const normalized = normalizePath(targetDir);\n if (isUnsafeCwdPath(normalized)) return true;\n let resolved: string;\n try {\n resolved = normalizePath(await realpath(normalized));\n } catch {\n return false;\n }\n return !isSamePath(resolved, normalized) && isUnsafeCwdPath(resolved);\n }\n\n /** Walk up from a blocked path to the closest existing ancestor directory. */\n private async resolveGrantTargetDir(blockedPath: string): Promise<string> {\n let targetDir = blockedPath;\n for (;;) {\n const parent = dirname(targetDir);\n try {\n // An existing directory is the grant target; an existing file grants\n // its parent directory.\n return (await stat(targetDir)).isDirectory() ? targetDir : parent;\n } catch {\n // Missing — keep walking up.\n }\n if (parent === targetDir) return targetDir;\n targetDir = parent;\n }\n }\n\n /**\n * Pre-check (no mutation, no callback): given the paths a tool is about to\n * operate on, return the sorted, de-duplicated grant-target directories for\n * those that are outside the allowed roots AND grantable (within FS_ROOT_BOUNDARY\n * when it is configured). A path whose nearest existing ancestor escapes the\n * boundary is NOT grantable and is omitted — the operation will fail with\n * ACCESS_DENIED for it rather than prompt. The caller returns an\n * `input_required` result carrying exactly this set (R7), and on retry applies\n * each accepted grant via {@link applyGrant} (R8). `requestState` binds this\n * set so a grant accepted for X cannot authorize Y (R9).\n */\n async precheckAccess(paths: readonly string[]): Promise<string[]> {\n if (!this.allowedDirectoriesState || paths.length === 0) {\n return [];\n }\n const allowedDirs = this.allowedDirectoriesState;\n const grantDirs: string[] = [];\n for (const requested of paths) {\n if (!requested) {\n continue;\n }\n const normalized = normalizePath(requested);\n if (isPathWithinDirectories(normalized, allowedDirs)) {\n continue;\n }\n const targetDir = normalizePath(await this.resolveGrantTargetDir(normalized));\n // Never offer a grant into a bare filesystem root or an unsafe path (home,\n // /etc, C:\\Windows, ...). Without FS_ROOT_BOUNDARY, isWithinBoundary returns\n // true for everything, so this is the one guard that still rejects roots a\n // misleading grant could reach. Mirrors the check gating --allow-cwd.\n if (await this.isUnsafeGrantTarget(targetDir)) {\n continue;\n }\n if (grantDirs.includes(targetDir)) {\n continue;\n }\n if (!(await this.isWithinBoundary(targetDir))) {\n continue;\n }\n grantDirs.push(targetDir);\n }\n return grantDirs.sort();\n }\n\n /**\n * Apply an accepted access grant: enforce FS_ROOT_BOUNDARY again (a TOCTOU\n * re-check against the boundary resolved at config time), then extend the\n * allowed roots for the remainder of the session (R8, A4). Returns false when\n * the boundary or the unsafe-path denylist blocks the grant, AND when the\n * recompute did not actually admit the directory — the return value now\n * reports what the guard's allowed set really holds, not merely that the\n * pre-checks passed. The caller leaves a refused path to fail with\n * ACCESS_DENIED during the operation. Idempotent: re-granting an already\n * allowed directory is a no-op via the dedup in `initialize`.\n */\n async applyGrant(targetDir: string): Promise<boolean> {\n // Defense-in-depth: even a tampered/accepted grant cannot extend roots into\n // an unsafe path. The precheckAccess guard already refuses to offer these;\n // this catches a grant that arrived by another route — the legacy stdio\n // `roots/list` seeding, which has no confirmation round-trip at all.\n if (await this.isUnsafeGrantTarget(targetDir)) {\n return false;\n }\n if (!(await this.isWithinBoundary(targetDir))) {\n return false;\n }\n // Read + write under the mutation lock so a concurrent grant cannot\n // interleave and lose this grant (GRANT-1). runExclusive is not reentrant:\n // nothing in this body may take the lock again.\n return this.runExclusive(async () => {\n const previous = this.grantedDirectories;\n this.grantedDirectories = [...previous, normalizePath(targetDir)];\n try {\n await this.recomputeAllowedDirectories();\n } catch (error) {\n // A failed recompute leaves the guard with its previous, consistent view.\n this.grantedDirectories = previous;\n throw error;\n }\n // Verify rather than assume: a boundary that no longer covers this\n // directory would drop it in the recompute above, and reporting success\n // for a grant that did not land is exactly the failure this replaces.\n if (isPathWithinDirectories(normalizePath(targetDir), this.getAllowedDirectories())) {\n return true;\n }\n this.grantedDirectories = previous;\n await this.recomputeAllowedDirectories();\n return false;\n });\n }\n\n // The guard already realpath-resolved FS_ROOT_BOUNDARY into rootBoundaries\n // during recomputeAllowedDirectories. Reuse that single source of truth so\n // grant paths check the same boundary the rest of the guard enforces,\n // instead of re-reading the env and re-resolving each entry.\n private async isWithinBoundary(targetDir: string): Promise<boolean> {\n if (this.rootBoundaries.length === 0) {\n return true;\n }\n let resolved: string;\n try {\n resolved = normalizePath(await realpath(targetDir));\n } catch {\n resolved = normalizePath(targetDir);\n }\n return isPathWithinDirectories(resolved, this.rootBoundaries);\n }\n\n private validateAccessAndSensitivity(requestedPath: string): {\n normalizedRequested: string;\n allowedDirs: string[];\n accessDeniedHint: string;\n } {\n const result = this.validateAccess(requestedPath);\n this.assertNotSensitiveFile(requestedPath, requestedPath);\n this.assertNotSensitiveFile(result.normalizedRequested, requestedPath);\n return result;\n }\n\n // Synchronous since the access-grant round-trip moved to the executor's\n // pre-check: validateAccess only does lexical containment math and throws,\n // no async I/O remains. Callers still `await` it for uniform control-flow;\n // awaiting a non-thenable returns it unchanged.\n private validateAccess(requestedPath: string): {\n normalizedRequested: string;\n allowedDirs: string[];\n accessDeniedHint: string;\n } {\n if (!this.allowedDirectoriesState) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'PathGuard not initialized. Call initialize() first.',\n requestedPath,\n );\n }\n if (isWindowsDriveRelativePath(requestedPath)) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'Drive-relative paths are not allowed. Use C:\\\\path or C:/path instead of C:path.',\n requestedPath,\n );\n }\n const reservedDevice = getReservedDeviceNameForPath(requestedPath);\n if (reservedDevice) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n `Reserved Windows device name not allowed: ${reservedDevice}.`,\n requestedPath,\n );\n }\n\n const normalizedRequested = normalizePath(requestedPath);\n const allowedDirs = this.allowedDirectoriesState;\n\n const accessDeniedHint =\n allowedDirs.length > 0\n ? `Allowed: ${allowedDirs.join(', ')}`\n : 'No allowed directories configured.';\n\n if (!isPathWithinDirectories(normalizedRequested, allowedDirs)) {\n // Out of root. The access-grant `input_required` round-trip is driven by\n // the executor's pre-check (precheckAccess) BEFORE the operation runs, so\n // by the time validation reaches here any grantable out-of-root path has\n // already been accepted and added to the allowed set. A path still out of\n // root here was either declined, ungrantable (outside FS_ROOT_BOUNDARY), or\n // never pre-checked — fail closed.\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n\n return {\n normalizedRequested,\n allowedDirs: this.allowedDirectoriesState,\n accessDeniedHint,\n };\n }\n\n private async handleRealpathError(\n error: unknown,\n normalizedRequested: string,\n allowedDirs: string[],\n accessDeniedHint: string,\n requestedPath: string,\n ): Promise<never> {\n if (isNotFoundErrno(error)) {\n // Resolve the nearest existing ancestor to detect out-of-sandbox symlinks.\n // e.g. if `link -> C:\\external` and path is `link\\nonexistent.txt`, the\n // ancestor resolves outside allowed dirs → ACCESS_DENIED, not NOT_FOUND.\n try {\n const { realAncestor, resolvedTarget } = await this.resolveNearestExistingAncestor(\n requestedPath,\n normalizedRequested,\n );\n if (\n !isPathWithinDirectories(realAncestor, allowedDirs) ||\n !isPathWithinDirectories(resolvedTarget, allowedDirs)\n ) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n } catch (ancestorErr) {\n // Rethrow any FsError — collapsing e.g. UNKNOWN to NOT_FOUND would mask\n // incomplete sandbox checks and make bugs invisible to callers.\n if (isFsError(ancestorErr)) {\n throw ancestorErr;\n }\n }\n\n throw new FsError(ErrorCode.NOT_FOUND, 'Path not found', requestedPath, error);\n }\n\n const mapped =\n isNodeError(error) && error.code !== undefined ? ERRNO_MAP[error.code] : undefined;\n throw new FsError(\n mapped ?? ErrorCode.UNKNOWN,\n 'Cannot access path',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n\n async validateExistingPathDetailed(requestedPath: string): Promise<ValidatedPathDetails> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccessAndSensitivity(requestedPath);\n\n let realPath: string;\n try {\n realPath = await realpath(normalizedRequested);\n } catch (error) {\n realPath = await this.handleRealpathError(\n error,\n normalizedRequested,\n allowedDirs,\n accessDeniedHint,\n requestedPath,\n );\n }\n\n const normalizedReal = normalizePath(realPath);\n\n if (!isPathWithinDirectories(normalizedReal, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n\n // Re-check the resolved real path: a symlink inside an allowed root may\n // point at a sensitive file (e.g. link -> .env). The early check above only\n // sees the requested/normalized path, not the symlink target.\n this.assertNotSensitiveFile(normalizedReal, requestedPath);\n\n return {\n requestedPath: normalizedRequested,\n resolvedPath: normalizedReal,\n isSymlink: !isSamePath(normalizedRequested, normalizedReal),\n };\n }\n\n async validateExistingDirectory(requestedPath: string): Promise<string> {\n const details = await this.validateExistingPathDetailed(requestedPath);\n\n let stats: Stats;\n try {\n stats = await stat(details.resolvedPath);\n } catch (error) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot access directory',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n\n if (!stats.isDirectory()) {\n throw new FsError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requestedPath);\n }\n\n return details.resolvedPath;\n }\n\n resolvePathOrRoot(pathValue: string | undefined): string {\n if (pathValue && pathValue.trim().length > 0) {\n return pathValue;\n }\n const roots = this.getAllowedDirectories();\n if (roots.length === 0) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'No roots configured. Use the roots tool or --allow-cwd.',\n );\n }\n if (roots.length > 1) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'Multiple roots configured. Provide an explicit path.',\n );\n }\n const root = roots[0];\n if (!root) {\n throw new FsError(ErrorCode.ACCESS_DENIED, 'Workspace root is unexpectedly undefined');\n }\n return root;\n }\n\n isAllowedRoot(normalizedPath: string): boolean {\n // isSamePath case-folds on case-insensitive filesystems (win + darwin),\n // matching the containment checks used everywhere else in the guard. The\n // previous IS_WINDOWS-only fold left darwin doing exact-case compares here\n // while isPathInsideDirectory folded — a root matched case-insensitively\n // everywhere else was missed.\n return this.getAllowedDirectories().some((dir) => isSamePath(dir, normalizedPath));\n }\n\n // Checks ONLY the sensitive-file denylist. Root containment and symlink\n // resolution must be verified separately (e.g. via validateExistingPath).\n private assertNotSensitiveFile(checkPath: string, requestedPath: string): void {\n if (this.isSensitive(checkPath)) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'Sensitive file blocked. Set ALLOW_SENSITIVE=1 to override.',\n requestedPath,\n );\n }\n }\n\n private throwAccessDenied(requestedPath: string, hint?: string): never {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n hint ? `Outside allowed directories. ${hint}` : 'Outside allowed directories.',\n requestedPath,\n );\n }\n private async resolveNearestExistingAncestor(\n requestedPath: string,\n currentPath: string,\n ): Promise<{ realAncestor: string; resolvedTarget: string }> {\n const missingSegments: string[] = [];\n let current = currentPath;\n for (;;) {\n try {\n const realAncestor = normalizePath(await realpath(current));\n const resolvedTarget =\n missingSegments.length === 0\n ? realAncestor\n : normalizePath(join(realAncestor, ...missingSegments.reverse()));\n return { realAncestor, resolvedTarget };\n } catch (error) {\n try {\n const stats = await lstat(current);\n if (stats.isSymbolicLink()) {\n const target = await readlink(current);\n const resolvedTarget = isAbsolute(target) ? target : resolve(dirname(current), target);\n const normalizedTarget = normalizePath(resolvedTarget);\n const allowedDirs = this.getAllowedDirectories();\n if (!isPathWithinDirectories(normalizedTarget, allowedDirs)) {\n this.throwAccessDenied(requestedPath);\n }\n }\n } catch (lstatErr) {\n if (isFsError(lstatErr) && lstatErr.code === ErrorCode.ACCESS_DENIED) {\n throw lstatErr;\n }\n // ENOENT is expected during ancestor walk — the entry simply doesn't exist.\n // Any other error (EACCES, EIO, ELOOP) is unexpected; fail safe.\n if (!isNodeError(lstatErr) || lstatErr.code !== 'ENOENT') {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot probe symlink ancestor',\n requestedPath,\n lstatErr instanceof Error ? lstatErr : undefined,\n );\n }\n }\n const parent = dirname(current);\n if (parent === current) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot resolve path',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n missingSegments.push(basename(current));\n current = parent;\n }\n }\n }\n\n async validatePathForWrite(requestedPath: string): Promise<string> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccessAndSensitivity(requestedPath);\n\n const { realAncestor, resolvedTarget } = await this.resolveNearestExistingAncestor(\n requestedPath,\n normalizedRequested,\n );\n if (\n !isPathWithinDirectories(realAncestor, allowedDirs) ||\n !isPathWithinDirectories(resolvedTarget, allowedDirs)\n ) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n // Re-check the resolved target: a symlink inside an allowed root may point\n // at a sensitive file. Writing through such a link must be blocked too.\n this.assertNotSensitiveFile(resolvedTarget, requestedPath);\n return resolvedTarget;\n }\n\n async validatePathForDelete(requestedPath: string): Promise<string> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccessAndSensitivity(requestedPath);\n\n // A workspace root is refused here, before the parent-containment check\n // below reaches for a parent that is out-of-root by construction and\n // reports \"Outside allowed directories\" for the one directory the caller\n // can see IS allowed. delete-file.ts keeps its own root check for the\n // nested-root case, where the parent is itself an allowed root and this\n // path is never taken.\n if (this.isAllowedRoot(normalizedRequested)) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'Deleting a workspace root directory is not allowed',\n requestedPath,\n );\n }\n\n const parent = dirname(normalizedRequested);\n let realParent: string;\n try {\n realParent = await realpath(parent);\n } catch (error) {\n throw new FsError(\n ErrorCode.NOT_FOUND,\n 'Parent directory not found',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n const normalizedRealParent = normalizePath(realParent);\n\n if (!isPathWithinDirectories(normalizedRealParent, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n\n // Resolve the final component when it exists. For deletion, we ONLY\n // block if the real target is outside the sandbox IF the target is\n // NOT a symlink. Deleting a symlink is safe even if it points outside.\n try {\n const stats = await lstat(normalizedRequested);\n if (stats.isSymbolicLink()) {\n // Symlink: check link sensitivity but don't resolve target.\n // The parent check above ensures the link itself is in an allowed root.\n this.assertNotSensitiveFile(normalizedRequested, requestedPath);\n return normalizedRequested;\n }\n\n // Not a symlink: resolve to catch path escapes (e.g. /allowed/dir/../../etc)\n // and block sensitive files.\n const realTarget = normalizePath(await realpath(normalizedRequested));\n if (!isPathWithinDirectories(realTarget, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n this.assertNotSensitiveFile(realTarget, requestedPath);\n return realTarget;\n } catch (error) {\n // A denial raised inside this block (out-of-root real target, or a\n // sensitive file reached through a symlinked parent) must propagate.\n // Only a probe failure — ENOENT and friends — falls through to the\n // parent check, which is sufficient for a path that does not exist.\n if (isFsError(error)) {\n throw error;\n }\n }\n return normalizedRequested;\n }\n\n async recomputeAllowedDirectories(): Promise<void> {\n const cliAllowedDirs = normalizeAllowedDirectories(this.options?.cliAllowedDirs ?? []);\n\n // Parse allowed directories from environment variable\n const allowMissing =\n cli.allowMissingRoots ??\n parseTrueEnvFlag(process.env['FS_ALLOW_MISSING_ROOTS'], 'FS_ALLOW_MISSING_ROOTS');\n const envAllowedDirs = await resolveConfiguredDirs('FS_ALLOWED_DIRS', { allowMissing });\n\n // Parse FS_ROOT_BOUNDARY (the --root-boundary flag beats the env var)\n const boundaries = await resolveConfiguredDirs('FS_ROOT_BOUNDARY', {\n resolveReal: true,\n ...(cli.rootBoundary !== undefined ? { rawValue: cli.rootBoundary } : {}),\n });\n\n const allowCwd = Boolean(this.options?.allowCwd);\n const allowCwdDirs: string[] = [];\n if (allowCwd) {\n let cwd = normalizePath(process.cwd());\n const walkCwd =\n cli.allowCwdWalk ?? parseTrueEnvFlag(process.env['FS_ALLOW_CWD_WALK'], 'FS_ALLOW_CWD_WALK');\n if (walkCwd) {\n cwd = await findProjectRoot(cwd, [...boundaries, homedir()]);\n }\n if (isUnsafeCwdPath(cwd)) {\n Logger.emit(\n 'warning',\n `Skipped adding unsafe current working directory to allowed list: ${cwd}`,\n );\n } else {\n allowCwdDirs.push(cwd);\n }\n }\n\n const baseline = [...cliAllowedDirs, ...envAllowedDirs, ...allowCwdDirs];\n\n const signal = AbortSignal.timeout(ROOTS_TIMEOUT_MS);\n // FS_ROOT_BOUNDARY is the only filter grants answer to (see\n // `grantedDirectories`); without one they pass through as accepted.\n const grantsToInclude =\n boundaries.length > 0\n ? await filterRootsWithin(this.grantedDirectories, boundaries, signal)\n : this.grantedDirectories;\n\n const combined = [...baseline, ...grantsToInclude];\n const nextState = await resolveAllowedDirectoriesState(combined, signal);\n // Commit boundaries and the allowed set together, after every await has\n // resolved, so a rejecting recompute leaves the guard's previous,\n // consistent view intact.\n this.rootBoundaries = boundaries;\n this.initialize(nextState);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/core/path.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Stats } from 'node:fs';\nimport { lstat, readlink, realpath, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';\n\nimport { withAbort } from './concurrency.ts';\nimport { cli } from './config.ts';\nimport {\n ERRNO_MAP,\n ErrorCode,\n FsError,\n isFsError,\n isNodeError,\n isNotFoundErrno,\n rethrowIfAborted,\n SKIPPABLE_ERRNOS,\n SKIPPABLE_FS_CODES,\n} from './errors.ts';\nimport { Logger } from './observability.ts';\nimport { findProjectRoot, isUnsafeCwdPath, resolveConfiguredDirs } from './path-discovery.ts';\nimport {\n getReservedDeviceNameForPath,\n isPathWithinDirectories,\n isSamePath,\n isWindowsDriveRelativePath,\n normalizeAllowedDirectory,\n normalizePath,\n parseTrueEnvFlag,\n toPosixPath,\n} from './path-utils.ts';\nimport { SensitiveMatcher } from './sensitive.ts';\nimport { ROOTS_TIMEOUT_MS } from './util.ts';\n\n// Allowed-directory assembly and the PathGuard that enforces it. The\n// sensitive-file denylist lives in sensitive.ts, and the character-level\n// primitives (IS_WINDOWS, isAlpha, isSlash, toPosixPath) live in\n// path-utils.ts.\n\nexport interface ValidatedPathDetails {\n requestedPath: string;\n resolvedPath: string;\n isSymlink: boolean;\n}\n\nexport interface ServerOptions {\n allowCwd?: boolean;\n cliAllowedDirs?: string[];\n readOnly?: boolean;\n}\n\n/** True when `normalizedRoot` really resolves inside `bounds` (FS_ROOT_BOUNDARY). */\nasync function isRootWithin(\n normalizedRoot: string,\n bounds: readonly string[],\n signal?: AbortSignal,\n): Promise<boolean> {\n try {\n signal?.throwIfAborted();\n const realPath = await withAbort(realpath(normalizedRoot), signal);\n return isPathWithinDirectories(normalizePath(realPath), bounds);\n } catch (error) {\n rethrowIfAborted(error);\n if (isNotFoundErrno(error)) {\n return false;\n }\n Logger.warn('grantBoundary: realpath failed unexpectedly', {\n root: normalizedRoot,\n error: String(error),\n });\n return false;\n }\n}\n\nasync function filterRootsWithin(\n roots: readonly string[],\n bounds: readonly string[],\n signal?: AbortSignal,\n): Promise<string[]> {\n const normalizedBounds = normalizeAllowedDirectories(bounds);\n const normalizedRoots = roots.map(normalizePath);\n\n const results = await Promise.all(\n normalizedRoots.map((root) => isRootWithin(root, normalizedBounds, signal)),\n );\n\n return normalizedRoots.filter((_, i) => results[i]);\n}\n\n/** `path.relative` with forward slashes, so displayed paths match across platforms. */\nexport function toPosixRelative(from: string, to: string): string {\n return toPosixPath(relative(from, to));\n}\n\n// ---------------------------------------------------------------------------\n// Resolver pipeline\n// ---------------------------------------------------------------------------\n\nexport function normalizeAllowedDirectories(dirs: readonly string[]): string[] {\n return [...new Set(dirs.map(normalizeAllowedDirectory).filter((entry) => entry.length > 0))];\n}\n\nexport async function resolveRealPath(\n normalized: string,\n signal?: AbortSignal,\n): Promise<string | null> {\n try {\n signal?.throwIfAborted();\n const realPath = await withAbort(realpath(normalized), signal);\n return normalizeAllowedDirectory(realPath);\n } catch (error) {\n rethrowIfAborted(error);\n // Only suppress ENOENT — the path genuinely does not exist.\n // EACCES, EIO, and other unexpected errors are rethrown so callers\n // cannot silently operate with a narrowed allowed-directory set.\n if (isNotFoundErrno(error)) {\n return null;\n }\n throw error;\n }\n}\n\nexport async function resolveAllowedDirectoriesState(\n dirs: readonly string[],\n signal?: AbortSignal,\n): Promise<string[]> {\n const primary = normalizeAllowedDirectories(dirs);\n const reals = await Promise.all(primary.map((dir) => resolveRealPath(dir, signal)));\n return [\n ...new Set(\n primary.flatMap((dir, i) => {\n const real = reals[i];\n return real && !isSamePath(real, dir) ? [dir, real] : [dir];\n }),\n ),\n ];\n}\n\n// ---------------------------------------------------------------------------\n// Windows helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Accepted risk: validation methods resolve/verify a path (symlinks, boundaries,\n * sensitivity) and then return a plain string; the actual fs operation happens\n * afterward as a separate syscall (classic TOCTOU). A symlink swapped in that\n * exact window could redirect the follow-up operation. This is mitigated by\n * re-validating the resolved real path (not just the requested path) and by\n * walking ancestors to catch escapes before the target exists, but it is not\n * eliminated — doing so would require fd-based operations (open with\n * O_NOFOLLOW / operate on the resolved fd) throughout core/fs.ts. Acceptable\n * tradeoff for a local, single-user filesystem server today.\n */\nexport class PathGuard {\n private allowedDirectoriesState: string[] | undefined;\n private readonly sensitive = new SensitiveMatcher();\n /**\n * Directories added by an accepted access grant (R8) — from the tool\n * executor's grant round-trip, or from a client's declared workspace roots on\n * the legacy stdio leg. Held separately from the configured baseline because\n * `recomputeAllowedDirectories` rebuilds that baseline from CLI args and env\n * on every call: anything merged into it would be recomputed away.\n *\n * These are NOT filtered against the baseline. A granted directory is\n * out-of-baseline by definition, so a baseline filter dropped every one while\n * `applyGrant` still reported success — the whole round-trip prompted the\n * user and then changed nothing. FS_ROOT_BOUNDARY and the unsafe-path denylist\n * remain the limits, both checked in `applyGrant` — the only writer of this\n * field — so the recompute can rebuild from it without re-filtering.\n */\n private grantedDirectories: string[] = [];\n private rootBoundaries: string[] = [];\n\n // ponytail: one mutex per PathGuard. If per-session grant throughput ever\n // matters, split into per-grant-dir locks; a single lock is correct for the\n // stdio + InMemoryEventStore single-process model.\n #mutex = Promise.resolve();\n\n private readonly options: ServerOptions | undefined;\n\n constructor(options?: ServerOptions) {\n this.options = options;\n }\n\n initialize(expanded: readonly string[]): void {\n this.allowedDirectoriesState = normalizeAllowedDirectories(expanded);\n }\n\n isInitialized(): boolean {\n return this.allowedDirectoriesState !== undefined;\n }\n\n /**\n * Run `fn` as the next holder of the guard's single mutation lock. Every\n * change to the allowed set goes through this so concurrent grants cannot\n * interleave their read-`await`-write and lose a grant (GRANT-1).\n */\n private async runExclusive<T>(fn: () => Promise<T>): Promise<T> {\n const result = this.#mutex.then(fn, fn);\n this.#mutex = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n }\n\n getAllowedDirectories(): string[] {\n if (!this.allowedDirectoriesState) {\n return [];\n }\n return [...this.allowedDirectoriesState];\n }\n\n isSensitive(filePath: string): boolean {\n return this.sensitive.isSensitive(filePath);\n }\n\n /**\n * True when `entryPath` is both within the guard's allowed directories and\n * not sensitive, checking the requested AND resolved paths. EVERY entry is\n * realpath-resolved via validateExistingPathDetailed: a symlinked ancestor\n * directory pointing outside the sandbox would otherwise pass the lexical\n * containment check (fs.glob follows symlinks and yields external entries as\n * non-symlink dirents). validateExistingPathDetailed re-checks containment on\n * the real path against this.allowedDirectoriesState (the guard's full\n * allowed set — a superset of the single-root `bounds` callers pass) and\n * re-checks sensitivity on the resolved target, throwing ACCESS_DENIED for\n * escapes/sensitive, which the catch below turns into a filter. Skippable\n * errno/fs errors return false (the entry is filtered, not fatal). The\n * accepted TOCTOU window is documented at the class docstring above.\n */\n async isEntryAccessible(entryPath: string): Promise<boolean> {\n const isSensitive = (requestedPath: string, resolvedPath: string): boolean =>\n this.isSensitive(requestedPath) || this.isSensitive(resolvedPath);\n try {\n const validated = await this.validateExistingPathDetailed(entryPath);\n return !isSensitive(validated.requestedPath, validated.resolvedPath);\n } catch (error) {\n if (isFsError(error)) {\n if (SKIPPABLE_FS_CODES.has(error.code)) return false;\n throw error;\n }\n if (isNodeError(error) && error.code !== undefined && SKIPPABLE_ERRNOS.has(error.code))\n return false;\n throw error;\n }\n }\n\n async validateExistingPath(requestedPath: string): Promise<string> {\n const details = await this.validateExistingPathDetailed(requestedPath);\n return details.resolvedPath;\n }\n\n /**\n * True when a grant must never admit `targetDir` — an unsafe path (home,\n * /etc, C:\\Windows, ...). Checked on the lexical path AND on the one it\n * resolves to: `resolveAllowedDirectoriesState` pushes each root's realpath\n * into the allowed set, so a lexical-only check let a symlink or junction\n * aliasing $HOME/C:\\Windows in under an innocuous name — and the confirmation\n * prompt showed the alias, not the target. A target that cannot be resolved\n * is judged lexically; it does not exist to escape into.\n */\n private async isUnsafeGrantTarget(targetDir: string): Promise<boolean> {\n const normalized = normalizePath(targetDir);\n if (isUnsafeCwdPath(normalized)) return true;\n let resolved: string;\n try {\n resolved = normalizePath(await realpath(normalized));\n } catch {\n return false;\n }\n return !isSamePath(resolved, normalized) && isUnsafeCwdPath(resolved);\n }\n\n /** Walk up from a blocked path to the closest existing ancestor directory. */\n private async resolveGrantTargetDir(blockedPath: string): Promise<string> {\n let targetDir = blockedPath;\n for (;;) {\n const parent = dirname(targetDir);\n try {\n // An existing directory is the grant target; an existing file grants\n // its parent directory.\n return (await stat(targetDir)).isDirectory() ? targetDir : parent;\n } catch {\n // Missing — keep walking up.\n }\n if (parent === targetDir) return targetDir;\n targetDir = parent;\n }\n }\n\n /**\n * Pre-check (no mutation, no callback): given the paths a tool is about to\n * operate on, return the sorted, de-duplicated grant-target directories for\n * those that are outside the allowed roots AND grantable (within FS_ROOT_BOUNDARY\n * when it is configured). A path whose nearest existing ancestor escapes the\n * boundary is NOT grantable and is omitted — the operation will fail with\n * ACCESS_DENIED for it rather than prompt. The caller returns an\n * `input_required` result carrying exactly this set (R7), and on retry applies\n * each accepted grant via {@link applyGrant} (R8). `requestState` binds this\n * set so a grant accepted for X cannot authorize Y (R9).\n */\n async precheckAccess(paths: readonly string[]): Promise<string[]> {\n if (!this.allowedDirectoriesState || paths.length === 0) {\n return [];\n }\n const allowedDirs = this.allowedDirectoriesState;\n const grantDirs: string[] = [];\n for (const requested of paths) {\n if (!requested) {\n continue;\n }\n const normalized = normalizePath(requested);\n if (isPathWithinDirectories(normalized, allowedDirs)) {\n continue;\n }\n const targetDir = normalizePath(await this.resolveGrantTargetDir(normalized));\n // Never offer a grant into a bare filesystem root or an unsafe path (home,\n // /etc, C:\\Windows, ...). Without FS_ROOT_BOUNDARY, isWithinBoundary returns\n // true for everything, so this is the one guard that still rejects roots a\n // misleading grant could reach. Mirrors the check gating --allow-cwd.\n if (await this.isUnsafeGrantTarget(targetDir)) {\n continue;\n }\n if (grantDirs.includes(targetDir)) {\n continue;\n }\n if (!(await this.isWithinBoundary(targetDir))) {\n continue;\n }\n grantDirs.push(targetDir);\n }\n return grantDirs.sort();\n }\n\n /**\n * Apply an accepted access grant: enforce FS_ROOT_BOUNDARY again (a TOCTOU\n * re-check against the boundary resolved at config time), then extend the\n * allowed roots for the remainder of the session (R8, A4). Returns false when\n * the boundary or the unsafe-path denylist blocks the grant, AND when the\n * recompute did not actually admit the directory — the return value now\n * reports what the guard's allowed set really holds, not merely that the\n * pre-checks passed. The caller leaves a refused path to fail with\n * ACCESS_DENIED during the operation. Idempotent: re-granting an already\n * allowed directory is a no-op via the dedup in `initialize`.\n */\n async applyGrant(targetDir: string): Promise<boolean> {\n // Defense-in-depth: even a tampered/accepted grant cannot extend roots into\n // an unsafe path. The precheckAccess guard already refuses to offer these;\n // this catches a grant that arrived by another route — the legacy stdio\n // `roots/list` seeding, which has no confirmation round-trip at all.\n if (await this.isUnsafeGrantTarget(targetDir)) {\n return false;\n }\n if (!(await this.isWithinBoundary(targetDir))) {\n return false;\n }\n // Read + write under the mutation lock so a concurrent grant cannot\n // interleave and lose this grant (GRANT-1). runExclusive is not reentrant:\n // nothing in this body may take the lock again.\n return this.runExclusive(async () => {\n const previous = this.grantedDirectories;\n this.grantedDirectories = [...previous, normalizePath(targetDir)];\n try {\n await this.recomputeAllowedDirectories();\n } catch (error) {\n // A failed recompute leaves the guard with its previous, consistent view.\n this.grantedDirectories = previous;\n throw error;\n }\n // Verify rather than assume: a boundary that no longer covers this\n // directory would drop it in the recompute above, and reporting success\n // for a grant that did not land is exactly the failure this replaces.\n if (isPathWithinDirectories(normalizePath(targetDir), this.getAllowedDirectories())) {\n return true;\n }\n this.grantedDirectories = previous;\n await this.recomputeAllowedDirectories();\n return false;\n });\n }\n\n // The guard already realpath-resolved FS_ROOT_BOUNDARY into rootBoundaries\n // during recomputeAllowedDirectories. Reuse that single source of truth so\n // grant paths check the same boundary the rest of the guard enforces,\n // instead of re-reading the env and re-resolving each entry.\n private async isWithinBoundary(targetDir: string): Promise<boolean> {\n if (this.rootBoundaries.length === 0) {\n return true;\n }\n let resolved: string;\n try {\n resolved = normalizePath(await realpath(targetDir));\n } catch {\n resolved = normalizePath(targetDir);\n }\n return isPathWithinDirectories(resolved, this.rootBoundaries);\n }\n\n private validateAccessAndSensitivity(requestedPath: string): {\n normalizedRequested: string;\n allowedDirs: string[];\n accessDeniedHint: string;\n } {\n const result = this.validateAccess(requestedPath);\n this.assertNotSensitiveFile(requestedPath, requestedPath);\n this.assertNotSensitiveFile(result.normalizedRequested, requestedPath);\n return result;\n }\n\n // Synchronous since the access-grant round-trip moved to the executor's\n // pre-check: validateAccess only does lexical containment math and throws,\n // no async I/O remains. Callers still `await` it for uniform control-flow;\n // awaiting a non-thenable returns it unchanged.\n private validateAccess(requestedPath: string): {\n normalizedRequested: string;\n allowedDirs: string[];\n accessDeniedHint: string;\n } {\n if (!this.allowedDirectoriesState) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'PathGuard not initialized. Call initialize() first.',\n requestedPath,\n );\n }\n if (isWindowsDriveRelativePath(requestedPath)) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'Drive-relative paths are not allowed. Use C:\\\\path or C:/path instead of C:path.',\n requestedPath,\n );\n }\n const reservedDevice = getReservedDeviceNameForPath(requestedPath);\n if (reservedDevice) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n `Reserved Windows device name not allowed: ${reservedDevice}.`,\n requestedPath,\n );\n }\n\n const normalizedRequested = normalizePath(requestedPath);\n const allowedDirs = this.allowedDirectoriesState;\n\n const accessDeniedHint =\n allowedDirs.length > 0\n ? `Allowed: ${allowedDirs.join(', ')}`\n : 'No allowed directories configured.';\n\n if (!isPathWithinDirectories(normalizedRequested, allowedDirs)) {\n // Out of root. The access-grant `input_required` round-trip is driven by\n // the executor's pre-check (precheckAccess) BEFORE the operation runs, so\n // by the time validation reaches here any grantable out-of-root path has\n // already been accepted and added to the allowed set. A path still out of\n // root here was either declined, ungrantable (outside FS_ROOT_BOUNDARY), or\n // never pre-checked — fail closed.\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n\n return {\n normalizedRequested,\n allowedDirs: this.allowedDirectoriesState,\n accessDeniedHint,\n };\n }\n\n private async handleRealpathError(\n error: unknown,\n normalizedRequested: string,\n allowedDirs: string[],\n accessDeniedHint: string,\n requestedPath: string,\n ): Promise<never> {\n if (isNotFoundErrno(error)) {\n // Resolve the nearest existing ancestor to detect out-of-sandbox symlinks.\n // e.g. if `link -> C:\\external` and path is `link\\nonexistent.txt`, the\n // ancestor resolves outside allowed dirs → ACCESS_DENIED, not NOT_FOUND.\n try {\n const { realAncestor, resolvedTarget } = await this.resolveNearestExistingAncestor(\n requestedPath,\n normalizedRequested,\n );\n if (\n !isPathWithinDirectories(realAncestor, allowedDirs) ||\n !isPathWithinDirectories(resolvedTarget, allowedDirs)\n ) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n } catch (ancestorErr) {\n // Rethrow any FsError — collapsing e.g. UNKNOWN to NOT_FOUND would mask\n // incomplete sandbox checks and make bugs invisible to callers.\n if (isFsError(ancestorErr)) {\n throw ancestorErr;\n }\n }\n\n throw new FsError(ErrorCode.NOT_FOUND, 'Path not found', requestedPath, error);\n }\n\n const mapped =\n isNodeError(error) && error.code !== undefined ? ERRNO_MAP[error.code] : undefined;\n throw new FsError(\n mapped ?? ErrorCode.UNKNOWN,\n 'Cannot access path',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n\n async validateExistingPathDetailed(requestedPath: string): Promise<ValidatedPathDetails> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccessAndSensitivity(requestedPath);\n\n let realPath: string;\n try {\n realPath = await realpath(normalizedRequested);\n } catch (error) {\n realPath = await this.handleRealpathError(\n error,\n normalizedRequested,\n allowedDirs,\n accessDeniedHint,\n requestedPath,\n );\n }\n\n const normalizedReal = normalizePath(realPath);\n\n if (!isPathWithinDirectories(normalizedReal, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n\n // Re-check the resolved real path: a symlink inside an allowed root may\n // point at a sensitive file (e.g. link -> .env). The early check above only\n // sees the requested/normalized path, not the symlink target.\n this.assertNotSensitiveFile(normalizedReal, requestedPath);\n\n return {\n requestedPath: normalizedRequested,\n resolvedPath: normalizedReal,\n isSymlink: !isSamePath(normalizedRequested, normalizedReal),\n };\n }\n\n async validateExistingDirectory(requestedPath: string): Promise<string> {\n const details = await this.validateExistingPathDetailed(requestedPath);\n\n let stats: Stats;\n try {\n stats = await stat(details.resolvedPath);\n } catch (error) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot access directory',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n\n if (!stats.isDirectory()) {\n throw new FsError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requestedPath);\n }\n\n return details.resolvedPath;\n }\n\n resolvePathOrRoot(pathValue: string | undefined): string {\n if (pathValue && pathValue.trim().length > 0) {\n return pathValue;\n }\n const roots = this.getAllowedDirectories();\n if (roots.length === 0) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'No roots configured. Use the roots tool or --allow-cwd.',\n );\n }\n if (roots.length > 1) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'Multiple roots configured. Provide an explicit path.',\n );\n }\n const root = roots[0];\n if (!root) {\n throw new FsError(ErrorCode.ACCESS_DENIED, 'Workspace root is unexpectedly undefined');\n }\n return root;\n }\n\n isAllowedRoot(normalizedPath: string): boolean {\n // isSamePath case-folds on case-insensitive filesystems (win + darwin),\n // matching the containment checks used everywhere else in the guard. The\n // previous IS_WINDOWS-only fold left darwin doing exact-case compares here\n // while isPathInsideDirectory folded — a root matched case-insensitively\n // everywhere else was missed.\n return this.getAllowedDirectories().some((dir) => isSamePath(dir, normalizedPath));\n }\n\n // Checks ONLY the sensitive-file denylist. Root containment and symlink\n // resolution must be verified separately (e.g. via validateExistingPath).\n private assertNotSensitiveFile(checkPath: string, requestedPath: string): void {\n if (this.isSensitive(checkPath)) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'Sensitive file blocked. Set ALLOW_SENSITIVE=1 to override.',\n requestedPath,\n );\n }\n }\n\n private throwAccessDenied(requestedPath: string, hint?: string): never {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n hint ? `Outside allowed directories. ${hint}` : 'Outside allowed directories.',\n requestedPath,\n );\n }\n private async resolveNearestExistingAncestor(\n requestedPath: string,\n currentPath: string,\n ): Promise<{ realAncestor: string; resolvedTarget: string }> {\n const missingSegments: string[] = [];\n let current = currentPath;\n for (;;) {\n try {\n const realAncestor = normalizePath(await realpath(current));\n const resolvedTarget =\n missingSegments.length === 0\n ? realAncestor\n : normalizePath(join(realAncestor, ...missingSegments.reverse()));\n return { realAncestor, resolvedTarget };\n } catch (error) {\n try {\n const stats = await lstat(current);\n if (stats.isSymbolicLink()) {\n const target = await readlink(current);\n const resolvedTarget = isAbsolute(target) ? target : resolve(dirname(current), target);\n const normalizedTarget = normalizePath(resolvedTarget);\n const allowedDirs = this.getAllowedDirectories();\n if (!isPathWithinDirectories(normalizedTarget, allowedDirs)) {\n this.throwAccessDenied(requestedPath);\n }\n }\n } catch (lstatErr) {\n if (isFsError(lstatErr) && lstatErr.code === ErrorCode.ACCESS_DENIED) {\n throw lstatErr;\n }\n // ENOENT is expected during ancestor walk — the entry simply doesn't exist.\n // Any other error (EACCES, EIO, ELOOP) is unexpected; fail safe.\n if (!isNodeError(lstatErr) || lstatErr.code !== 'ENOENT') {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot probe symlink ancestor',\n requestedPath,\n lstatErr instanceof Error ? lstatErr : undefined,\n );\n }\n }\n const parent = dirname(current);\n if (parent === current) {\n throw new FsError(\n ErrorCode.UNKNOWN,\n 'Cannot resolve path',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n missingSegments.push(basename(current));\n current = parent;\n }\n }\n }\n\n async validatePathForWrite(requestedPath: string): Promise<string> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccessAndSensitivity(requestedPath);\n\n const { realAncestor, resolvedTarget } = await this.resolveNearestExistingAncestor(\n requestedPath,\n normalizedRequested,\n );\n if (\n !isPathWithinDirectories(realAncestor, allowedDirs) ||\n !isPathWithinDirectories(resolvedTarget, allowedDirs)\n ) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n // Re-check the resolved target: a symlink inside an allowed root may point\n // at a sensitive file. Writing through such a link must be blocked too.\n this.assertNotSensitiveFile(resolvedTarget, requestedPath);\n return resolvedTarget;\n }\n\n async validatePathForDelete(requestedPath: string): Promise<string> {\n const { normalizedRequested, allowedDirs, accessDeniedHint } =\n this.validateAccessAndSensitivity(requestedPath);\n\n // A workspace root is refused here, before the parent-containment check\n // below reaches for a parent that is out-of-root by construction and\n // reports \"Outside allowed directories\" for the one directory the caller\n // can see IS allowed. delete-file.ts keeps its own root check for the\n // nested-root case, where the parent is itself an allowed root and this\n // path is never taken.\n if (this.isAllowedRoot(normalizedRequested)) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n 'Deleting a workspace root directory is not allowed',\n requestedPath,\n );\n }\n\n const parent = dirname(normalizedRequested);\n let realParent: string;\n try {\n realParent = await realpath(parent);\n } catch (error) {\n throw new FsError(\n ErrorCode.NOT_FOUND,\n 'Parent directory not found',\n requestedPath,\n error instanceof Error ? error : undefined,\n );\n }\n const normalizedRealParent = normalizePath(realParent);\n\n if (!isPathWithinDirectories(normalizedRealParent, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n\n // Resolve the final component when it exists. For deletion, we ONLY\n // block if the real target is outside the sandbox IF the target is\n // NOT a symlink. Deleting a symlink is safe even if it points outside.\n try {\n const stats = await lstat(normalizedRequested);\n if (stats.isSymbolicLink()) {\n // Symlink: check link sensitivity but don't resolve target.\n // The parent check above ensures the link itself is in an allowed root.\n this.assertNotSensitiveFile(normalizedRequested, requestedPath);\n return normalizedRequested;\n }\n\n // Not a symlink: resolve to catch path escapes (e.g. /allowed/dir/../../etc)\n // and block sensitive files.\n const realTarget = normalizePath(await realpath(normalizedRequested));\n if (!isPathWithinDirectories(realTarget, allowedDirs)) {\n this.throwAccessDenied(requestedPath, accessDeniedHint);\n }\n this.assertNotSensitiveFile(realTarget, requestedPath);\n return realTarget;\n } catch (error) {\n // A denial raised inside this block (out-of-root real target, or a\n // sensitive file reached through a symlinked parent) must propagate.\n // Only a probe failure — ENOENT and friends — falls through to the\n // parent check, which is sufficient for a path that does not exist.\n if (isFsError(error)) {\n throw error;\n }\n }\n return normalizedRequested;\n }\n\n async recomputeAllowedDirectories(): Promise<void> {\n const cliAllowedDirs = normalizeAllowedDirectories(this.options?.cliAllowedDirs ?? []);\n\n // Parse allowed directories from environment variable\n const allowMissing =\n cli.allowMissingRoots ??\n parseTrueEnvFlag(process.env['FS_ALLOW_MISSING_ROOTS'], 'FS_ALLOW_MISSING_ROOTS');\n const envAllowedDirs = await resolveConfiguredDirs('FS_ALLOWED_DIRS', { allowMissing });\n\n // Parse FS_ROOT_BOUNDARY (the --root-boundary flag beats the env var)\n const boundaries = await resolveConfiguredDirs('FS_ROOT_BOUNDARY', {\n resolveReal: true,\n ...(cli.rootBoundary !== undefined ? { rawValue: cli.rootBoundary } : {}),\n });\n\n const allowCwd = Boolean(this.options?.allowCwd);\n const allowCwdDirs: string[] = [];\n if (allowCwd) {\n let cwd = normalizePath(process.cwd());\n const walkCwd =\n cli.allowCwdWalk ?? parseTrueEnvFlag(process.env['FS_ALLOW_CWD_WALK'], 'FS_ALLOW_CWD_WALK');\n if (walkCwd) {\n cwd = await findProjectRoot(cwd, [...boundaries, homedir()]);\n }\n if (isUnsafeCwdPath(cwd)) {\n Logger.emit(\n 'warning',\n `Skipped adding unsafe current working directory to allowed list: ${cwd}`,\n );\n } else {\n allowCwdDirs.push(cwd);\n }\n }\n\n const baseline = [...cliAllowedDirs, ...envAllowedDirs, ...allowCwdDirs];\n\n const signal = AbortSignal.timeout(ROOTS_TIMEOUT_MS);\n // FS_ROOT_BOUNDARY is the only filter grants answer to (see\n // `grantedDirectories`); without one they pass through as accepted.\n const grantsToInclude =\n boundaries.length > 0\n ? await filterRootsWithin(this.grantedDirectories, boundaries, signal)\n : this.grantedDirectories;\n\n const combined = [...baseline, ...grantsToInclude];\n const nextState = await resolveAllowedDirectoriesState(combined, signal);\n // Commit boundaries and the allowed set together, after every await has\n // resolved, so a rejecting recompute leaves the guard's previous,\n // consistent view intact.\n this.rootBoundaries = boundaries;\n this.initialize(nextState);\n }\n}\n"]}
|
package/dist/core/read.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/core/read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAwC,OAAO,EAAa,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/core/read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAwC,OAAO,EAAa,MAAM,aAAa,CAAC;AAkDvF,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC;AAazE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,GACpB,OAAO,CAMT;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CAkCjB;AAED,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,KAAG,MAI5C,CAAC;AA4VF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAIpE;AAED,iEAAiE;AACjE,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,cAAc,CAAC,CAczB"}
|