@code-yeongyu/senpi 2026.11.15-2 → 2026.11.15-3
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/CHANGELOG.md +19 -0
- package/dist/bundle/chunks/{chunk-Q4RJ4WLE.js → chunk-225TDKTA.js} +4 -4
- package/dist/bundle/chunks/{chunk-QSU2456D.js → chunk-3KR3CDGM.js} +1 -1
- package/dist/bundle/chunks/{chunk-AOTHPTMU.js → chunk-5GCZFK4X.js} +2 -2
- package/dist/bundle/chunks/{chunk-BMADOJHZ.js → chunk-5QIFH624.js} +1 -1
- package/dist/bundle/chunks/{chunk-THYIV26N.js → chunk-GQEXLLWJ.js} +1 -1
- package/dist/bundle/chunks/{chunk-3RILB2OD.js → chunk-OJOPOUAY.js} +9 -9
- package/dist/bundle/chunks/{chunk-64FJ4QQJ.js → chunk-PPCAJQDJ.js} +1 -1
- package/dist/bundle/chunks/{chunk-74HCKLRH.js → chunk-TEXE4HT6.js} +1 -1
- package/dist/bundle/chunks/{chunk-RRFDLGX6.js → chunk-TNYFCGI7.js} +156 -156
- package/dist/bundle/chunks/{cli-main-GNQY2LTU.js → cli-main-CV3ODX4A.js} +1 -1
- package/dist/bundle/chunks/{hand-entry-D3DNH6XL.js → hand-entry-2NN7ZTY6.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-ATBPY4M5.js → interactive-mode-TGHZWM2T.js} +1 -1
- package/dist/bundle/chunks/{main-OMFVSMUR.js → main-B7WEY4JW.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-ACQ5N4UB.js → multi-session-host-53YLL2OD.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-2VMRV4HL.js → package-manager-cli-2U54AVKW.js} +1 -1
- package/dist/bundle/chunks/{role-BTH5PVME.js → role-4IXZZAL4.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-XUK5SYKH.js → rpc-mode-RRYXENML.js} +1 -1
- package/dist/bundle/chunks/{runtime-session-VO3X4OE3.js → runtime-session-KKHEYZOH.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-Y5EIFFPY.js → session-control-endpoint-HVULSIUR.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +331 -331
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/core/agent-session.d.ts +6 -0
- package/dist/core/agent-session.js +34 -3
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.d.ts +2 -0
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/failover.d.ts +2 -0
- package/dist/core/extensions/builtin/anthropic-subscription/failover.js +22 -4
- package/dist/core/extensions/builtin/anthropic-subscription/index.js +7 -2
- package/dist/core/extensions/builtin/anthropic-subscription/limit-recheck.d.ts +15 -0
- package/dist/core/extensions/builtin/anthropic-subscription/limit-recheck.js +55 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-stream.d.ts +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-stream.js +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js +8 -5
- package/dist/core/extensions/builtin/anthropic-subscription/stream.d.ts +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/stream.js +3 -1
- package/dist/core/extensions/builtin/terminal/extension-state.js +15 -0
- package/dist/core/extensions/builtin/terminal/notify.d.ts +2 -1
- package/dist/core/extensions/builtin/terminal/notify.js +15 -6
- package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +5 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.js +41 -1
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts +4 -0
- package/dist/core/extensions/loader.js +5 -0
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.js +5 -0
- package/dist/core/extensions/types.d.ts +7 -0
- package/dist/core/run-background-jobs.d.ts +33 -0
- package/dist/core/run-background-jobs.js +47 -0
- package/dist/core/tools/bash-core.d.ts +50 -0
- package/dist/core/tools/bash-core.js +169 -0
- package/dist/core/tools/bash-schema.d.ts +48 -0
- package/dist/core/tools/bash-schema.js +34 -0
- package/dist/core/tools/bash.d.ts +4 -70
- package/dist/core/tools/bash.js +32 -409
- package/dist/core/tools/coding-tools-core.d.ts +24 -0
- package/dist/core/tools/coding-tools-core.js +14 -0
- package/dist/core/tools/edit-core.d.ts +56 -0
- package/dist/core/tools/edit-core.js +175 -0
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.js +0 -32
- package/dist/core/tools/edit-preview.d.ts +7 -0
- package/dist/core/tools/edit-preview.js +34 -0
- package/dist/core/tools/edit.d.ts +5 -43
- package/dist/core/tools/edit.js +10 -158
- package/dist/core/tools/encoding.d.ts +10 -0
- package/dist/core/tools/encoding.js +58 -0
- package/dist/core/tools/file-tool-host.d.ts +25 -0
- package/dist/core/tools/file-tool-host.js +109 -0
- package/dist/core/tools/fs-error-code.d.ts +2 -0
- package/dist/core/tools/fs-error-code.js +15 -0
- package/dist/core/tools/machine-output-spill.d.ts +11 -0
- package/dist/core/tools/machine-output-spill.js +65 -0
- package/dist/core/tools/machine.d.ts +4 -24
- package/dist/core/tools/machine.js +4 -44
- package/dist/core/tools/output-accumulator-core.d.ts +84 -0
- package/dist/core/tools/output-accumulator-core.js +200 -0
- package/dist/core/tools/output-accumulator.d.ts +9 -61
- package/dist/core/tools/output-accumulator.js +90 -265
- package/dist/core/tools/path-utils.js +3 -48
- package/dist/core/tools/posix-signals.d.ts +3 -0
- package/dist/core/tools/posix-signals.js +113 -0
- package/dist/core/tools/read-core.d.ts +64 -0
- package/dist/core/tools/read-core.js +180 -0
- package/dist/core/tools/read-text.d.ts +23 -0
- package/dist/core/tools/read-text.js +90 -0
- package/dist/core/tools/read.d.ts +6 -47
- package/dist/core/tools/read.js +13 -218
- package/dist/core/tools/renderers/bash.d.ts +0 -1
- package/dist/core/tools/renderers/bash.js +0 -1
- package/dist/core/tools/renderers/edit.d.ts +1 -1
- package/dist/core/tools/renderers/edit.js +1 -1
- package/dist/core/tools/secret-output-redactor.js +2 -1
- package/dist/core/tools/shell-operations.d.ts +53 -0
- package/dist/core/tools/shell-operations.js +115 -0
- package/dist/core/tools/shell-output.d.ts +20 -0
- package/dist/core/tools/shell-output.js +152 -0
- package/dist/core/tools/tail-window.js +5 -7
- package/dist/core/tools/tool-machine.d.ts +30 -0
- package/dist/core/tools/tool-machine.js +47 -0
- package/dist/core/tools/truncate.js +15 -14
- package/dist/core/tools/write-core.d.ts +47 -0
- package/dist/core/tools/write-core.js +108 -0
- package/dist/core/tools/write-result.d.ts +0 -1
- package/dist/core/tools/write-result.js +0 -13
- package/dist/core/tools/write.d.ts +6 -31
- package/dist/core/tools/write.js +25 -77
- package/dist/core/turn-origin.d.ts +4 -2
- package/dist/core/turn-origin.js +15 -8
- package/dist/modes/interactive/session-control-session-commands.js +1 -1
- package/dist/modes/rpc/connection-handler.js +2 -2
- package/dist/modes/rpc/effect-commands.d.ts +1 -4
- package/dist/modes/rpc/method-classes-schema-session.d.ts +1 -0
- package/dist/modes/rpc/method-classes-schema-session.js +5 -1
- package/dist/modes/rpc/method-classes-schema.d.ts +1 -0
- package/dist/modes/rpc/pause-command-validation.js +2 -0
- package/dist/modes/rpc/rpc-mutation-response-types.d.ts +2 -4
- package/dist/modes/rpc/rpc-session-command-types.d.ts +1 -0
- package/dist/modes/rpc/session-control-actions-data.d.ts +7 -3
- package/dist/modes/rpc/session-control-actions-data.js +12 -6
- package/dist/tools-core.d.ts +14 -0
- package/dist/tools-core.js +9 -0
- package/dist/utils/image-mime.d.ts +4 -0
- package/dist/utils/image-mime.js +99 -0
- package/dist/utils/mime.d.ts +1 -1
- package/dist/utils/mime.js +2 -97
- package/docs/extensions.md +7 -1
- package/docs/rpc.md +14 -0
- package/package.json +11 -7
- package/vendor/senpi-machine-ops/files/local-files.js +34 -5
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// What the file tools need from the host they run for, with defaults that need no Node runtime. The local tools
|
|
2
|
+
// pass their Node implementations (path-utils.ts, the machine-ops mutation queue and realpath canonicalization).
|
|
3
|
+
const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
|
4
|
+
function normalizePosixPath(path) {
|
|
5
|
+
const parts = [];
|
|
6
|
+
for (const part of path.split("/")) {
|
|
7
|
+
if (part === "" || part === ".")
|
|
8
|
+
continue;
|
|
9
|
+
if (part === "..")
|
|
10
|
+
parts.pop();
|
|
11
|
+
else
|
|
12
|
+
parts.push(part);
|
|
13
|
+
}
|
|
14
|
+
return `/${parts.join("/")}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* POSIX resolution as `path.posix.resolve` does it, after the same input clean-up the local resolver applies (Unicode
|
|
18
|
+
* spaces, a leading `@`, `file://` URLs). `~` is not expanded: the host's home directory is unknown here.
|
|
19
|
+
*/
|
|
20
|
+
export const resolvePosixToolPath = (input, cwd) => {
|
|
21
|
+
let path = input.replace(UNICODE_SPACES, " ");
|
|
22
|
+
if (path.startsWith("@"))
|
|
23
|
+
path = path.slice(1);
|
|
24
|
+
if (path.startsWith("file://"))
|
|
25
|
+
path = decodeURIComponent(new URL(path).pathname);
|
|
26
|
+
return normalizePosixPath(path.startsWith("/") ? path : `${cwd}/${path}`);
|
|
27
|
+
};
|
|
28
|
+
export function posixDirname(path) {
|
|
29
|
+
const trimmed = path.length > 1 ? path.replace(/\/+$/, "") : path;
|
|
30
|
+
const slash = trimmed.lastIndexOf("/");
|
|
31
|
+
if (slash === -1)
|
|
32
|
+
return ".";
|
|
33
|
+
return slash === 0 ? "/" : trimmed.slice(0, slash);
|
|
34
|
+
}
|
|
35
|
+
const pathTails = new Map();
|
|
36
|
+
/** Serializes mutations per resolved path string within this isolate. */
|
|
37
|
+
export const inProcessFileMutationQueue = async (absolutePath, fn) => {
|
|
38
|
+
const previous = pathTails.get(absolutePath) ?? Promise.resolve();
|
|
39
|
+
let release = () => { };
|
|
40
|
+
const current = new Promise((resolve) => {
|
|
41
|
+
release = resolve;
|
|
42
|
+
});
|
|
43
|
+
const tail = previous.then(() => current);
|
|
44
|
+
pathTails.set(absolutePath, tail);
|
|
45
|
+
await previous;
|
|
46
|
+
try {
|
|
47
|
+
return await fn();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
release();
|
|
51
|
+
if (pathTails.get(absolutePath) === tail)
|
|
52
|
+
pathTails.delete(absolutePath);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
export const lexicalCanonicalPath = async (absolutePath) => absolutePath;
|
|
56
|
+
const NARROW_NO_BREAK_SPACE = "\u202F";
|
|
57
|
+
export function tryMacOSScreenshotPath(filePath) {
|
|
58
|
+
return filePath.replace(/ (AM|PM)\./gi, `${NARROW_NO_BREAK_SPACE}$1.`);
|
|
59
|
+
}
|
|
60
|
+
export function tryNFDVariant(filePath) {
|
|
61
|
+
// macOS stores filenames in NFD (decomposed) form, try converting user input to NFD
|
|
62
|
+
return filePath.normalize("NFD");
|
|
63
|
+
}
|
|
64
|
+
export function tryCurlyQuoteVariant(filePath) {
|
|
65
|
+
// macOS uses U+2019 (right single quotation mark) in screenshot names like "Capture d'écran"
|
|
66
|
+
// Users typically type U+0027 (straight apostrophe)
|
|
67
|
+
return filePath.replace(/'/g, "\u2019");
|
|
68
|
+
}
|
|
69
|
+
// Windows Explorer "Copy as path" wraps the path in double quotes (senpi#2170).
|
|
70
|
+
export function unquotedPath(filePath) {
|
|
71
|
+
const match = /^@?(["'])(.+)\1$/.exec(filePath.trim());
|
|
72
|
+
return match?.[2];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The read tool's path: the resolved path when it exists, else the first existing spelling variant (quoted,
|
|
76
|
+
* macOS screenshot AM/PM, NFD, curly quote), else the resolved path.
|
|
77
|
+
*/
|
|
78
|
+
export async function resolveReadPathVariants(filePath, cwd, resolve, exists) {
|
|
79
|
+
const resolved = resolve(filePath, cwd);
|
|
80
|
+
if (await exists(resolved)) {
|
|
81
|
+
return resolved;
|
|
82
|
+
}
|
|
83
|
+
const unquoted = unquotedPath(filePath);
|
|
84
|
+
if (unquoted !== undefined) {
|
|
85
|
+
return resolveReadPathVariants(unquoted, cwd, resolve, exists);
|
|
86
|
+
}
|
|
87
|
+
// Try macOS AM/PM variant (narrow no-break space before AM/PM)
|
|
88
|
+
const amPmVariant = tryMacOSScreenshotPath(resolved);
|
|
89
|
+
if (amPmVariant !== resolved && (await exists(amPmVariant))) {
|
|
90
|
+
return amPmVariant;
|
|
91
|
+
}
|
|
92
|
+
// Try NFD variant (macOS stores filenames in NFD form)
|
|
93
|
+
const nfdVariant = tryNFDVariant(resolved);
|
|
94
|
+
if (nfdVariant !== resolved && (await exists(nfdVariant))) {
|
|
95
|
+
return nfdVariant;
|
|
96
|
+
}
|
|
97
|
+
// Try curly quote variant (macOS uses U+2019 in screenshot names)
|
|
98
|
+
const curlyVariant = tryCurlyQuoteVariant(resolved);
|
|
99
|
+
if (curlyVariant !== resolved && (await exists(curlyVariant))) {
|
|
100
|
+
return curlyVariant;
|
|
101
|
+
}
|
|
102
|
+
// Try combined NFD + curly quote (for French macOS screenshots like "Capture d'écran")
|
|
103
|
+
const nfdCurlyVariant = tryCurlyQuoteVariant(nfdVariant);
|
|
104
|
+
if (nfdCurlyVariant !== resolved && (await exists(nfdCurlyVariant))) {
|
|
105
|
+
return nfdCurlyVariant;
|
|
106
|
+
}
|
|
107
|
+
return resolved;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=file-tool-host.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// The fs error code a tool shows for a failed operation. A ToolMachine may pass fs errors through unchanged (the
|
|
2
|
+
// local machine does) or report the operation layer's own codes, as a machine reached over a wire does; the
|
|
3
|
+
// tools answer with the same fs code either way.
|
|
4
|
+
const FS_CODE_OF_OP_CODE = {
|
|
5
|
+
not_found: "ENOENT",
|
|
6
|
+
permission_denied: "EACCES",
|
|
7
|
+
aborted: "ABORT_ERR",
|
|
8
|
+
};
|
|
9
|
+
export function fsErrorCode(error) {
|
|
10
|
+
if (typeof error !== "object" || error === null || !("code" in error) || typeof error.code !== "string") {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
return FS_CODE_OF_OP_CODE[error.code] ?? error.code;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=fs-error-code.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type OutputSpillFactory } from "./output-accumulator-core.ts";
|
|
2
|
+
import type { ToolMachine } from "./tool-machine.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Spills to a file on the tool machine, so the path a truncation notice names is one the model can read there.
|
|
5
|
+
* Chunks are written in order at their byte offsets as they arrive. The operation layer has no delete, so `remove`
|
|
6
|
+
* empties the file instead, and does nothing when no chunk ever reached the machine.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createMachineOutputSpill(machine: ToolMachine, options?: {
|
|
9
|
+
tempDir?: string;
|
|
10
|
+
}): OutputSpillFactory;
|
|
11
|
+
//# sourceMappingURL=machine-output-spill.d.ts.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { encodeUtf8 } from "./encoding.js";
|
|
2
|
+
import { joinHeadAndTail } from "./output-accumulator-core.js";
|
|
3
|
+
function randomHex(bytes) {
|
|
4
|
+
return Array.from(crypto.getRandomValues(new Uint8Array(bytes)), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Spills to a file on the tool machine, so the path a truncation notice names is one the model can read there.
|
|
8
|
+
* Chunks are written in order at their byte offsets as they arrive. The operation layer has no delete, so `remove`
|
|
9
|
+
* empties the file instead, and does nothing when no chunk ever reached the machine.
|
|
10
|
+
*/
|
|
11
|
+
export function createMachineOutputSpill(machine, options) {
|
|
12
|
+
const tempDir = options?.tempDir ?? "/tmp";
|
|
13
|
+
return (prefix) => {
|
|
14
|
+
const path = `${tempDir}/${prefix}-${randomHex(8)}.log`;
|
|
15
|
+
const ctx = machine.context(undefined);
|
|
16
|
+
let size = 0;
|
|
17
|
+
let written = false;
|
|
18
|
+
let writeFailed = false;
|
|
19
|
+
let writeError;
|
|
20
|
+
let pending = Promise.resolve();
|
|
21
|
+
const read = (offset, limitBytes) => machine.ops.files.read({ path, offset, limitBytes }, ctx).then((result) => result.bytes);
|
|
22
|
+
return {
|
|
23
|
+
path,
|
|
24
|
+
write: (chunk) => {
|
|
25
|
+
const bytes = typeof chunk === "string" ? encodeUtf8(chunk) : chunk;
|
|
26
|
+
const offset = size;
|
|
27
|
+
size += bytes.length;
|
|
28
|
+
pending = pending.then(async () => {
|
|
29
|
+
if (writeFailed)
|
|
30
|
+
return;
|
|
31
|
+
try {
|
|
32
|
+
await machine.ops.files.write({ path, bytes, offset, createParentDirs: true }, ctx);
|
|
33
|
+
written = true;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
writeFailed = true;
|
|
37
|
+
writeError = error;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
close: async () => {
|
|
42
|
+
await pending;
|
|
43
|
+
if (writeFailed)
|
|
44
|
+
throw writeError;
|
|
45
|
+
},
|
|
46
|
+
remove: async () => {
|
|
47
|
+
await pending;
|
|
48
|
+
// Nothing reached the machine (the first write failed): there is no file to empty.
|
|
49
|
+
if (!written)
|
|
50
|
+
return;
|
|
51
|
+
await machine.ops.files.write({ path, bytes: new Uint8Array() }, ctx);
|
|
52
|
+
},
|
|
53
|
+
readFull: async (maxBytes) => {
|
|
54
|
+
if (size <= maxBytes) {
|
|
55
|
+
return { content: new TextDecoder().decode(await read(0, size)), truncated: false };
|
|
56
|
+
}
|
|
57
|
+
const headBytes = Math.floor(maxBytes / 2);
|
|
58
|
+
const tailBytes = maxBytes - headBytes;
|
|
59
|
+
const [head, tail] = await Promise.all([read(0, headBytes), read(size - tailBytes, tailBytes)]);
|
|
60
|
+
return { content: joinHeadAndTail(head, tail, size - headBytes - tailBytes), truncated: true };
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=machine-output-spill.js.map
|
|
@@ -1,31 +1,11 @@
|
|
|
1
|
-
import { type MachineOps
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* being silently ignored.
|
|
6
|
-
*/
|
|
7
|
-
export type ToolPathPolicy = Pick<PolicyHooks, "beforeOp"> & {
|
|
8
|
-
readonly filesystem?: never;
|
|
9
|
-
readonly afterOp?: never;
|
|
10
|
-
};
|
|
11
|
-
/** One MachineOps bound to the tool path: principal `local-session`, no scope root (the tools resolve paths). */
|
|
12
|
-
export interface ToolMachine {
|
|
13
|
-
readonly ops: MachineOps;
|
|
14
|
-
/**
|
|
15
|
-
* Runs `PolicyHooks.beforeOp` once for one tool call; a denial rejects with `Error(reason)` before any effect.
|
|
16
|
-
* Without a `beforeOp` it returns undefined synchronously, so a tool that awaits only a returned promise keeps
|
|
17
|
-
* its exact pre-layer timing (bash emits its first partial update before any await).
|
|
18
|
-
*/
|
|
19
|
-
begin(op: OpName, args: unknown, signal: AbortSignal | undefined): Promise<void> | undefined;
|
|
20
|
-
/** Context for a sub-operation inside a call `begin` already admitted. */
|
|
21
|
-
context(signal: AbortSignal | undefined, pathLock?: "caller"): OpContext;
|
|
22
|
-
}
|
|
1
|
+
import { type MachineOps } from "../../../vendor/senpi-machine-ops/index.js";
|
|
2
|
+
import { type ToolMachine, type ToolPathPolicy } from "./tool-machine.ts";
|
|
3
|
+
export type { ToolMachine, ToolPathPolicy } from "./tool-machine.ts";
|
|
4
|
+
/** A ToolMachine over `ops`, or over the process-wide local machine when the caller supplies none. */
|
|
23
5
|
export declare function createToolMachine(options?: {
|
|
24
6
|
ops?: MachineOps;
|
|
25
7
|
policy?: ToolPathPolicy;
|
|
26
8
|
}): ToolMachine;
|
|
27
9
|
/** The ToolMachine a tool factory uses when its options carry none. */
|
|
28
10
|
export declare function defaultToolMachine(): ToolMachine;
|
|
29
|
-
/** Bytes from the operation layer as the Buffer the tool seams have always returned. */
|
|
30
|
-
export declare function asBuffer(bytes: Uint8Array): Buffer;
|
|
31
11
|
//# sourceMappingURL=machine.d.ts.map
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
import { createLocalMachineOps
|
|
1
|
+
import { createLocalMachineOps } from "../../../vendor/senpi-machine-ops/index.js";
|
|
2
2
|
import { createInProcessHolderLink } from "../../../vendor/senpi-machine-ops/process/in-process-holder.js";
|
|
3
3
|
import { killProcessTree } from "../../utils/shell.js";
|
|
4
4
|
import { ensureTool } from "../../utils/tools-manager.js";
|
|
5
|
-
|
|
6
|
-
* Tool-path limits. The tools keep their own truncation (truncate.ts, the output accumulator), so the operation
|
|
7
|
-
* layer reads files whole and streams exec output uncaptured, exactly as the tools did before the layer existed.
|
|
8
|
-
*/
|
|
9
|
-
const TOOL_PATH_LIMITS = {
|
|
10
|
-
...DEFAULT_LIMITS,
|
|
11
|
-
readMaxBytes: Number.POSITIVE_INFINITY,
|
|
12
|
-
execStdoutMaxBytes: 0,
|
|
13
|
-
execStderrMaxBytes: 0,
|
|
14
|
-
execTimeoutDefaultMs: Number.POSITIVE_INFINITY,
|
|
15
|
-
};
|
|
5
|
+
import { bindToolMachine } from "./tool-machine.js";
|
|
16
6
|
let processMachineOps;
|
|
17
7
|
/**
|
|
18
8
|
* The process-wide local MachineOps the built-in tools use when no caller supplies one. It has no `stateDir`, so it
|
|
@@ -28,35 +18,9 @@ function defaultLocalMachineOps() {
|
|
|
28
18
|
});
|
|
29
19
|
return processMachineOps;
|
|
30
20
|
}
|
|
31
|
-
|
|
21
|
+
/** A ToolMachine over `ops`, or over the process-wide local machine when the caller supplies none. */
|
|
32
22
|
export function createToolMachine(options) {
|
|
33
|
-
|
|
34
|
-
const context = (signal, pathLock) => ({
|
|
35
|
-
scope: { id: "local-session" },
|
|
36
|
-
principal: { kind: "local-session", id: "local-session" },
|
|
37
|
-
signal: signal ?? NEVER_ABORTED,
|
|
38
|
-
limits: TOOL_PATH_LIMITS,
|
|
39
|
-
// The tools run the extension filesystem policy themselves (with their own error text) and the gate below
|
|
40
|
-
// already ran once for the call, so sub-operations carry no hooks.
|
|
41
|
-
policy: {},
|
|
42
|
-
...(pathLock !== undefined ? { pathLock } : {}),
|
|
43
|
-
});
|
|
44
|
-
return {
|
|
45
|
-
get ops() {
|
|
46
|
-
return resolveOps();
|
|
47
|
-
},
|
|
48
|
-
begin(op, args, signal) {
|
|
49
|
-
const policy = options?.policy;
|
|
50
|
-
const beforeOp = policy?.beforeOp;
|
|
51
|
-
if (beforeOp === undefined)
|
|
52
|
-
return undefined;
|
|
53
|
-
return beforeOp(op, args, { ...context(signal), policy: policy ?? {} }).then((decision) => {
|
|
54
|
-
if (!decision.allow)
|
|
55
|
-
throw new Error(decision.reason);
|
|
56
|
-
});
|
|
57
|
-
},
|
|
58
|
-
context,
|
|
59
|
-
};
|
|
23
|
+
return bindToolMachine(() => options?.ops ?? defaultLocalMachineOps(), options?.policy);
|
|
60
24
|
}
|
|
61
25
|
let defaultMachine;
|
|
62
26
|
/** The ToolMachine a tool factory uses when its options carry none. */
|
|
@@ -64,8 +28,4 @@ export function defaultToolMachine() {
|
|
|
64
28
|
defaultMachine ??= createToolMachine();
|
|
65
29
|
return defaultMachine;
|
|
66
30
|
}
|
|
67
|
-
/** Bytes from the operation layer as the Buffer the tool seams have always returned. */
|
|
68
|
-
export function asBuffer(bytes) {
|
|
69
|
-
return Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
70
|
-
}
|
|
71
31
|
//# sourceMappingURL=machine.js.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type TruncationResult } from "./truncate.ts";
|
|
2
|
+
export interface OutputSnapshot {
|
|
3
|
+
content: string;
|
|
4
|
+
truncation: TruncationResult;
|
|
5
|
+
fullOutputPath?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface FullOutput {
|
|
8
|
+
content: string;
|
|
9
|
+
/** Whether `content` omits part of the output. */
|
|
10
|
+
truncated: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** Where an accumulator keeps the full output once it outgrows the display limits. */
|
|
13
|
+
export interface OutputSpill {
|
|
14
|
+
/** The path truncation notices show the model. */
|
|
15
|
+
readonly path: string;
|
|
16
|
+
/** Appends a chunk; a failure is latched and reported by `close`. */
|
|
17
|
+
write(chunk: Uint8Array | string): void;
|
|
18
|
+
/** Settles every write; rejects with the first write or close failure. */
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
/** Discards the spill; a rejection leaves it in place for a retry. */
|
|
21
|
+
remove(): Promise<void>;
|
|
22
|
+
/** The whole output after `close`, cut by {@link joinHeadAndTail} past `maxBytes`. */
|
|
23
|
+
readFull(maxBytes: number): Promise<FullOutput>;
|
|
24
|
+
}
|
|
25
|
+
export type OutputSpillFactory = (prefix: string) => OutputSpill;
|
|
26
|
+
export interface OutputAccumulatorOptions {
|
|
27
|
+
maxLines?: number;
|
|
28
|
+
maxBytes?: number;
|
|
29
|
+
tempFilePrefix?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The first and last bytes of an output too long to return whole, around an omission marker. Cuts at character
|
|
33
|
+
* boundaries: streaming decode holds back an incomplete trailing sequence, and the tail skips continuation bytes.
|
|
34
|
+
*/
|
|
35
|
+
export declare function joinHeadAndTail(head: Uint8Array, tail: Uint8Array, omittedBytes: number): string;
|
|
36
|
+
/**
|
|
37
|
+
* Incrementally tracks streaming output with bounded memory.
|
|
38
|
+
*
|
|
39
|
+
* Appends decode chunks with a streaming UTF-8 decoder, keeps only a decoded tail for display snapshots, and opens a
|
|
40
|
+
* spill when the full output needs to be preserved.
|
|
41
|
+
*/
|
|
42
|
+
export declare class OutputAccumulator {
|
|
43
|
+
private readonly maxLines;
|
|
44
|
+
private readonly maxBytes;
|
|
45
|
+
private readonly tempFilePrefix;
|
|
46
|
+
private readonly createSpill;
|
|
47
|
+
private readonly decoder;
|
|
48
|
+
private readonly tail;
|
|
49
|
+
private rawChunks;
|
|
50
|
+
private totalRawBytes;
|
|
51
|
+
private totalDecodedBytes;
|
|
52
|
+
private completedLines;
|
|
53
|
+
private totalLines;
|
|
54
|
+
private currentLineBytes;
|
|
55
|
+
private hasOpenLine;
|
|
56
|
+
private finished;
|
|
57
|
+
/** The spill that owns `fullOutputPath`, until removed. */
|
|
58
|
+
private spill;
|
|
59
|
+
/** The same spill while it still takes writes, until closed. */
|
|
60
|
+
private openSpill;
|
|
61
|
+
constructor(options: OutputAccumulatorOptions & {
|
|
62
|
+
spill: OutputSpillFactory;
|
|
63
|
+
});
|
|
64
|
+
append(data: Uint8Array): void;
|
|
65
|
+
appendText(text: string): void;
|
|
66
|
+
finish(): void;
|
|
67
|
+
snapshot(options?: {
|
|
68
|
+
persistIfTruncated?: boolean;
|
|
69
|
+
}): OutputSnapshot;
|
|
70
|
+
closeTempFile(): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* The complete output, for callers that can take more than the display snapshot. Call after
|
|
73
|
+
* `finish()` and `closeTempFile()`. Output longer than `maxBytes` raw bytes keeps its first and
|
|
74
|
+
* last `maxBytes / 2` bytes around an omission marker.
|
|
75
|
+
*/
|
|
76
|
+
readFullOutput(maxBytes: number): Promise<FullOutput>;
|
|
77
|
+
getLastLineBytes(): number;
|
|
78
|
+
removeTempFile(): Promise<void>;
|
|
79
|
+
private appendDecodedText;
|
|
80
|
+
private getSnapshotText;
|
|
81
|
+
private shouldUseTempFile;
|
|
82
|
+
private ensureTempFile;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=output-accumulator-core.d.ts.map
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { concatBytes, encodeUtf8, utf8ByteLength } from "./encoding.js";
|
|
2
|
+
import { TailWindow } from "./tail-window.js";
|
|
3
|
+
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateTail } from "./truncate.js";
|
|
4
|
+
/**
|
|
5
|
+
* The first and last bytes of an output too long to return whole, around an omission marker. Cuts at character
|
|
6
|
+
* boundaries: streaming decode holds back an incomplete trailing sequence, and the tail skips continuation bytes.
|
|
7
|
+
*/
|
|
8
|
+
export function joinHeadAndTail(head, tail, omittedBytes) {
|
|
9
|
+
const headText = new TextDecoder().decode(head, { stream: true });
|
|
10
|
+
let tailStart = 0;
|
|
11
|
+
while (tailStart < tail.length && (tail[tailStart] & 0xc0) === 0x80)
|
|
12
|
+
tailStart++;
|
|
13
|
+
const tailText = new TextDecoder().decode(tail.subarray(tailStart));
|
|
14
|
+
return `${headText}\n\n[... ${omittedBytes} bytes omitted ...]\n\n${tailText}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Incrementally tracks streaming output with bounded memory.
|
|
18
|
+
*
|
|
19
|
+
* Appends decode chunks with a streaming UTF-8 decoder, keeps only a decoded tail for display snapshots, and opens a
|
|
20
|
+
* spill when the full output needs to be preserved.
|
|
21
|
+
*/
|
|
22
|
+
export class OutputAccumulator {
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.decoder = new TextDecoder();
|
|
25
|
+
this.rawChunks = [];
|
|
26
|
+
this.totalRawBytes = 0;
|
|
27
|
+
this.totalDecodedBytes = 0;
|
|
28
|
+
this.completedLines = 0;
|
|
29
|
+
this.totalLines = 0;
|
|
30
|
+
this.currentLineBytes = 0;
|
|
31
|
+
this.hasOpenLine = false;
|
|
32
|
+
this.finished = false;
|
|
33
|
+
this.maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
|
|
34
|
+
this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
35
|
+
this.tempFilePrefix = options.tempFilePrefix ?? "pi-output";
|
|
36
|
+
this.createSpill = options.spill;
|
|
37
|
+
this.tail = new TailWindow(Math.max(this.maxBytes * 2, 1));
|
|
38
|
+
}
|
|
39
|
+
append(data) {
|
|
40
|
+
if (this.finished) {
|
|
41
|
+
throw new Error("Cannot append to a finished output accumulator");
|
|
42
|
+
}
|
|
43
|
+
const text = this.decoder.decode(data, { stream: true });
|
|
44
|
+
this.totalRawBytes += data.length;
|
|
45
|
+
this.appendDecodedText(text, utf8ByteLength(text));
|
|
46
|
+
if (this.openSpill || this.shouldUseTempFile()) {
|
|
47
|
+
this.ensureTempFile();
|
|
48
|
+
this.openSpill?.write(data);
|
|
49
|
+
}
|
|
50
|
+
else if (data.length > 0) {
|
|
51
|
+
this.rawChunks.push(data);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
appendText(text) {
|
|
55
|
+
if (this.finished) {
|
|
56
|
+
throw new Error("Cannot append to a finished output accumulator");
|
|
57
|
+
}
|
|
58
|
+
if (text.length === 0) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const bytes = utf8ByteLength(text);
|
|
62
|
+
this.totalRawBytes += bytes;
|
|
63
|
+
this.appendDecodedText(text, bytes);
|
|
64
|
+
if (this.openSpill || this.shouldUseTempFile()) {
|
|
65
|
+
this.ensureTempFile();
|
|
66
|
+
this.openSpill?.write(text);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.rawChunks.push(text);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
finish() {
|
|
73
|
+
if (this.finished) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.finished = true;
|
|
77
|
+
const finalText = this.decoder.decode();
|
|
78
|
+
this.appendDecodedText(finalText, utf8ByteLength(finalText));
|
|
79
|
+
if (this.shouldUseTempFile()) {
|
|
80
|
+
this.ensureTempFile();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
snapshot(options = {}) {
|
|
84
|
+
const tailTruncation = truncateTail(this.getSnapshotText(), {
|
|
85
|
+
maxLines: this.maxLines,
|
|
86
|
+
maxBytes: this.maxBytes,
|
|
87
|
+
});
|
|
88
|
+
const truncated = this.totalLines > this.maxLines || this.totalDecodedBytes > this.maxBytes;
|
|
89
|
+
const truncatedBy = truncated
|
|
90
|
+
? (tailTruncation.truncatedBy ?? (this.totalDecodedBytes > this.maxBytes ? "bytes" : "lines"))
|
|
91
|
+
: null;
|
|
92
|
+
const truncation = {
|
|
93
|
+
...tailTruncation,
|
|
94
|
+
truncated,
|
|
95
|
+
truncatedBy,
|
|
96
|
+
totalLines: this.totalLines,
|
|
97
|
+
totalBytes: this.totalDecodedBytes,
|
|
98
|
+
maxLines: this.maxLines,
|
|
99
|
+
maxBytes: this.maxBytes,
|
|
100
|
+
};
|
|
101
|
+
if (options.persistIfTruncated && truncation.truncated) {
|
|
102
|
+
this.ensureTempFile();
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
content: truncation.content,
|
|
106
|
+
truncation,
|
|
107
|
+
fullOutputPath: this.spill?.path,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
async closeTempFile() {
|
|
111
|
+
const spill = this.openSpill;
|
|
112
|
+
this.openSpill = undefined;
|
|
113
|
+
if (!spill) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
await spill.close();
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
try {
|
|
121
|
+
await this.removeTempFile();
|
|
122
|
+
}
|
|
123
|
+
catch (unlinkError) {
|
|
124
|
+
throw new AggregateError([error, unlinkError], "Output spill cleanup failed");
|
|
125
|
+
}
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The complete output, for callers that can take more than the display snapshot. Call after
|
|
131
|
+
* `finish()` and `closeTempFile()`. Output longer than `maxBytes` raw bytes keeps its first and
|
|
132
|
+
* last `maxBytes / 2` bytes around an omission marker.
|
|
133
|
+
*/
|
|
134
|
+
async readFullOutput(maxBytes) {
|
|
135
|
+
if (!this.spill) {
|
|
136
|
+
// Fork `appendText()` buffers strings beside raw chunks; encode them back before decoding the whole.
|
|
137
|
+
const chunks = this.rawChunks.map((chunk) => (typeof chunk === "string" ? encodeUtf8(chunk) : chunk));
|
|
138
|
+
return { content: new TextDecoder().decode(concatBytes(chunks)), truncated: false };
|
|
139
|
+
}
|
|
140
|
+
return this.spill.readFull(maxBytes);
|
|
141
|
+
}
|
|
142
|
+
getLastLineBytes() {
|
|
143
|
+
return this.currentLineBytes;
|
|
144
|
+
}
|
|
145
|
+
async removeTempFile() {
|
|
146
|
+
if (!this.spill) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
await this.spill.remove();
|
|
150
|
+
this.spill = undefined;
|
|
151
|
+
}
|
|
152
|
+
appendDecodedText(text, bytes) {
|
|
153
|
+
if (text.length === 0) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
this.totalDecodedBytes += bytes;
|
|
157
|
+
this.tail.append(text, bytes, !this.hasOpenLine);
|
|
158
|
+
let newlines = 0;
|
|
159
|
+
let lastNewline = -1;
|
|
160
|
+
for (let i = text.indexOf("\n"); i !== -1; i = text.indexOf("\n", i + 1)) {
|
|
161
|
+
newlines++;
|
|
162
|
+
lastNewline = i;
|
|
163
|
+
}
|
|
164
|
+
if (newlines === 0) {
|
|
165
|
+
this.currentLineBytes += bytes;
|
|
166
|
+
this.hasOpenLine = true;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
this.completedLines += newlines;
|
|
170
|
+
const tail = text.slice(lastNewline + 1);
|
|
171
|
+
this.currentLineBytes = utf8ByteLength(tail);
|
|
172
|
+
this.hasOpenLine = tail.length > 0;
|
|
173
|
+
}
|
|
174
|
+
this.totalLines = this.completedLines + (this.hasOpenLine ? 1 : 0);
|
|
175
|
+
}
|
|
176
|
+
getSnapshotText() {
|
|
177
|
+
const text = this.tail.text();
|
|
178
|
+
if (this.tail.startsAtLineBoundary) {
|
|
179
|
+
return text;
|
|
180
|
+
}
|
|
181
|
+
const firstNewline = text.indexOf("\n");
|
|
182
|
+
return firstNewline === -1 ? text : text.slice(firstNewline + 1);
|
|
183
|
+
}
|
|
184
|
+
shouldUseTempFile() {
|
|
185
|
+
return (this.totalRawBytes > this.maxBytes || this.totalDecodedBytes > this.maxBytes || this.totalLines > this.maxLines);
|
|
186
|
+
}
|
|
187
|
+
ensureTempFile() {
|
|
188
|
+
if (this.spill) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const spill = this.createSpill(this.tempFilePrefix);
|
|
192
|
+
this.spill = spill;
|
|
193
|
+
this.openSpill = spill;
|
|
194
|
+
for (const chunk of this.rawChunks) {
|
|
195
|
+
spill.write(chunk);
|
|
196
|
+
}
|
|
197
|
+
this.rawChunks = [];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=output-accumulator-core.js.map
|
|
@@ -1,63 +1,11 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
fullOutputPath?: string;
|
|
11
|
-
}
|
|
12
|
-
export interface FullOutput {
|
|
13
|
-
content: string;
|
|
14
|
-
/** Whether `content` omits part of the output. */
|
|
15
|
-
truncated: boolean;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Incrementally tracks streaming output with bounded memory.
|
|
19
|
-
*
|
|
20
|
-
* Appends decode chunks with a streaming UTF-8 decoder, keeps only a decoded
|
|
21
|
-
* tail for display snapshots, and opens a temp file when the full output needs
|
|
22
|
-
* to be preserved.
|
|
23
|
-
*/
|
|
24
|
-
export declare class OutputAccumulator {
|
|
25
|
-
private readonly maxLines;
|
|
26
|
-
private readonly maxBytes;
|
|
27
|
-
private readonly tempFilePrefix;
|
|
28
|
-
private readonly decoder;
|
|
29
|
-
private readonly tail;
|
|
30
|
-
private rawChunks;
|
|
31
|
-
private totalRawBytes;
|
|
32
|
-
private totalDecodedBytes;
|
|
33
|
-
private completedLines;
|
|
34
|
-
private totalLines;
|
|
35
|
-
private currentLineBytes;
|
|
36
|
-
private hasOpenLine;
|
|
37
|
-
private finished;
|
|
38
|
-
private tempFilePath;
|
|
39
|
-
private tempFileStream;
|
|
40
|
-
private tempFileError;
|
|
41
|
-
constructor(options?: OutputAccumulatorOptions);
|
|
42
|
-
append(data: Buffer): void;
|
|
43
|
-
appendText(text: string): void;
|
|
44
|
-
finish(): void;
|
|
45
|
-
snapshot(options?: {
|
|
46
|
-
persistIfTruncated?: boolean;
|
|
47
|
-
}): OutputSnapshot;
|
|
48
|
-
closeTempFile(): Promise<void>;
|
|
49
|
-
/**
|
|
50
|
-
* The complete output, for callers that can take more than the display snapshot. Call after
|
|
51
|
-
* `finish()` and `closeTempFile()`. Output longer than `maxBytes` raw bytes keeps its first and
|
|
52
|
-
* last `maxBytes / 2` bytes around an omission marker.
|
|
53
|
-
*/
|
|
54
|
-
readFullOutput(maxBytes: number): Promise<FullOutput>;
|
|
55
|
-
getLastLineBytes(): number;
|
|
56
|
-
private appendDecodedText;
|
|
57
|
-
private getSnapshotText;
|
|
58
|
-
private shouldUseTempFile;
|
|
59
|
-
private ensureTempFile;
|
|
60
|
-
removeTempFile(): Promise<void>;
|
|
61
|
-
private takeTempFileStream;
|
|
1
|
+
import { OutputAccumulator as CoreOutputAccumulator, type OutputAccumulatorOptions, type OutputSpillFactory } from "./output-accumulator-core.ts";
|
|
2
|
+
export type { FullOutput, OutputAccumulatorOptions, OutputSnapshot } from "./output-accumulator-core.ts";
|
|
3
|
+
/** The local spill: a temp file under the OS temp directory, streamed as output arrives. */
|
|
4
|
+
export declare const createTempFileSpill: OutputSpillFactory;
|
|
5
|
+
/** The output accumulator of the local tools: it spills to a temp file unless the caller supplies a spill. */
|
|
6
|
+
export declare class OutputAccumulator extends CoreOutputAccumulator {
|
|
7
|
+
constructor(options?: OutputAccumulatorOptions & {
|
|
8
|
+
spill?: OutputSpillFactory;
|
|
9
|
+
});
|
|
62
10
|
}
|
|
63
11
|
//# sourceMappingURL=output-accumulator.d.ts.map
|