@caupulican/pi-adaptative 0.85.8 → 0.86.2
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 +42 -0
- package/dist/core/agent-paths.d.ts +6 -3
- package/dist/core/agent-paths.d.ts.map +1 -1
- package/dist/core/agent-paths.js +10 -3
- package/dist/core/agent-paths.js.map +1 -1
- package/dist/core/agent-session.d.ts +5 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +7 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
- package/dist/core/autonomy/bounded-completion.js +19 -8
- package/dist/core/autonomy/bounded-completion.js.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.d.ts +11 -5
- package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.js +34 -8
- package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
- package/dist/core/autonomy/subagent-prompt.d.ts +3 -3
- package/dist/core/autonomy/subagent-prompt.js +9 -9
- package/dist/core/autonomy/subagent-prompt.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +10 -8
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +23 -13
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/bash-execution-controller.d.ts +4 -0
- package/dist/core/bash-execution-controller.d.ts.map +1 -1
- package/dist/core/bash-execution-controller.js +11 -4
- package/dist/core/bash-execution-controller.js.map +1 -1
- package/dist/core/bash-executor.d.ts +2 -0
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +2 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/capability-contract.d.ts +1 -1
- package/dist/core/capability-contract.d.ts.map +1 -1
- package/dist/core/capability-contract.js +0 -1
- package/dist/core/capability-contract.js.map +1 -1
- package/dist/core/default-tool-surface.d.ts +1 -0
- package/dist/core/default-tool-surface.d.ts.map +1 -1
- package/dist/core/default-tool-surface.js +3 -2
- package/dist/core/default-tool-surface.js.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts +27 -8
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.js +191 -33
- package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-agent-control.d.ts +45 -6
- package/dist/core/delegation/worker-agent-control.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control.js +110 -13
- package/dist/core/delegation/worker-agent-control.js.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.js +177 -158
- package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
- package/dist/core/delegation/worker-authority-resolver.d.ts +23 -0
- package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -0
- package/dist/core/delegation/worker-authority-resolver.js +128 -0
- package/dist/core/delegation/worker-authority-resolver.js.map +1 -0
- package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -1
- package/dist/core/delegation/worker-conversation-store.js +1 -1
- package/dist/core/delegation/worker-conversation-store.js.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.d.ts +7 -1
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.js +260 -87
- package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
- package/dist/core/delegation/worker-delegation-request.d.ts +25 -4
- package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-request.js +90 -1
- package/dist/core/delegation/worker-delegation-request.js.map +1 -1
- package/dist/core/delegation/worker-execution-policy.d.ts +2 -0
- package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
- package/dist/core/delegation/worker-execution-policy.js +22 -12
- package/dist/core/delegation/worker-execution-policy.js.map +1 -1
- package/dist/core/delegation/worker-lifecycle.d.ts +1 -0
- package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
- package/dist/core/delegation/worker-lifecycle.js +4 -2
- package/dist/core/delegation/worker-lifecycle.js.map +1 -1
- package/dist/core/delegation/worker-profile-resolver.d.ts +0 -2
- package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -1
- package/dist/core/delegation/worker-profile-resolver.js +8 -35
- package/dist/core/delegation/worker-profile-resolver.js.map +1 -1
- package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-recovery-coordinator.js +1 -0
- package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-runner.d.ts +6 -1
- package/dist/core/delegation/worker-runner.d.ts.map +1 -1
- package/dist/core/delegation/worker-runner.js +20 -6
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/delegation/worker-tree-budget-coordinator.d.ts +47 -0
- package/dist/core/delegation/worker-tree-budget-coordinator.d.ts.map +1 -0
- package/dist/core/delegation/worker-tree-budget-coordinator.js +210 -0
- package/dist/core/delegation/worker-tree-budget-coordinator.js.map +1 -0
- package/dist/core/memory/providers/file-store.d.ts +2 -0
- package/dist/core/memory/providers/file-store.d.ts.map +1 -1
- package/dist/core/memory/providers/file-store.js +22 -20
- package/dist/core/memory/providers/file-store.js.map +1 -1
- package/dist/core/models/runtime-process.d.ts.map +1 -1
- package/dist/core/models/runtime-process.js +5 -18
- package/dist/core/models/runtime-process.js.map +1 -1
- package/dist/core/orchestration/bounded-string-array.d.ts +11 -0
- package/dist/core/orchestration/bounded-string-array.d.ts.map +1 -0
- package/dist/core/orchestration/bounded-string-array.js +17 -0
- package/dist/core/orchestration/bounded-string-array.js.map +1 -0
- package/dist/core/orchestration/capability-gateway.d.ts +16 -0
- package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
- package/dist/core/orchestration/capability-gateway.js +38 -0
- package/dist/core/orchestration/capability-gateway.js.map +1 -1
- package/dist/core/orchestration/contracts.d.ts +11 -3
- package/dist/core/orchestration/contracts.d.ts.map +1 -1
- package/dist/core/orchestration/contracts.js +0 -1
- package/dist/core/orchestration/contracts.js.map +1 -1
- package/dist/core/orchestration/delegation-ledger.d.ts +1 -0
- package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
- package/dist/core/orchestration/delegation-ledger.js +3 -0
- package/dist/core/orchestration/delegation-ledger.js.map +1 -1
- package/dist/core/orchestration/event-store.d.ts +12 -10
- package/dist/core/orchestration/event-store.d.ts.map +1 -1
- package/dist/core/orchestration/event-store.js +94 -104
- package/dist/core/orchestration/event-store.js.map +1 -1
- package/dist/core/orchestration/lane-tool-manifests.d.ts +2 -2
- package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -1
- package/dist/core/orchestration/lane-tool-manifests.js +4 -1
- package/dist/core/orchestration/lane-tool-manifests.js.map +1 -1
- package/dist/core/orchestration/policy-compiler.d.ts.map +1 -1
- package/dist/core/orchestration/policy-compiler.js +8 -9
- package/dist/core/orchestration/policy-compiler.js.map +1 -1
- package/dist/core/orchestration/profile-registry.d.ts.map +1 -1
- package/dist/core/orchestration/profile-registry.js +4 -10
- package/dist/core/orchestration/profile-registry.js.map +1 -1
- package/dist/core/orchestration/task-runtime.d.ts +1 -0
- package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
- package/dist/core/orchestration/task-runtime.js +58 -3
- package/dist/core/orchestration/task-runtime.js.map +1 -1
- package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -1
- package/dist/core/orchestration/worker-execution-contract.js +10 -17
- package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
- package/dist/core/risk-classifier.js +3 -3
- package/dist/core/risk-classifier.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +4 -1
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +34 -15
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/secrets/bitwarden-credential-storage.d.ts +51 -0
- package/dist/core/secrets/bitwarden-credential-storage.d.ts.map +1 -0
- package/dist/core/secrets/bitwarden-credential-storage.js +345 -0
- package/dist/core/secrets/bitwarden-credential-storage.js.map +1 -0
- package/dist/core/secrets/credential-exposure-guard.d.ts +8 -4
- package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -1
- package/dist/core/secrets/credential-exposure-guard.js +30 -31
- package/dist/core/secrets/credential-exposure-guard.js.map +1 -1
- package/dist/core/secrets/credential-manager.d.ts +109 -0
- package/dist/core/secrets/credential-manager.d.ts.map +1 -0
- package/dist/core/secrets/credential-manager.js +384 -0
- package/dist/core/secrets/credential-manager.js.map +1 -0
- package/dist/core/secrets/credential-project.d.ts +4 -0
- package/dist/core/secrets/credential-project.d.ts.map +1 -0
- package/dist/core/secrets/credential-project.js +145 -0
- package/dist/core/secrets/credential-project.js.map +1 -0
- package/dist/core/secrets/secret-dotenv.d.ts +1 -1
- package/dist/core/secrets/secret-dotenv.js +1 -1
- package/dist/core/secrets/secret-dotenv.js.map +1 -1
- package/dist/core/settings-manager.d.ts +4 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +9 -5
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tool-capability-policy.js +2 -2
- package/dist/core/tool-capability-policy.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +29 -1
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +179 -28
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/git-filter.d.ts +1 -0
- package/dist/core/tools/git-filter.d.ts.map +1 -1
- package/dist/core/tools/git-filter.js +1 -1
- package/dist/core/tools/git-filter.js.map +1 -1
- package/dist/core/tools/profile-writer.js +3 -3
- package/dist/core/tools/profile-writer.js.map +1 -1
- package/dist/core/tools/python.d.ts +2 -0
- package/dist/core/tools/python.d.ts.map +1 -1
- package/dist/core/tools/python.js +17 -12
- package/dist/core/tools/python.js.map +1 -1
- package/dist/core/tools/run-process.d.ts +2 -0
- package/dist/core/tools/run-process.d.ts.map +1 -1
- package/dist/core/tools/run-process.js +7 -3
- package/dist/core/tools/run-process.js.map +1 -1
- package/dist/core/tools/secret-store.d.ts +8 -18
- package/dist/core/tools/secret-store.d.ts.map +1 -1
- package/dist/core/tools/secret-store.js +83 -348
- package/dist/core/tools/secret-store.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +38 -1
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +35 -14
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +26 -3
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/secret-menu-controller.d.ts +21 -0
- package/dist/modes/interactive/secret-menu-controller.d.ts.map +1 -0
- package/dist/modes/interactive/secret-menu-controller.js +276 -0
- package/dist/modes/interactive/secret-menu-controller.js.map +1 -0
- package/dist/utils/tools-manager.d.ts +2 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +43 -48
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/zip-extractor.d.ts +8 -0
- package/dist/utils/zip-extractor.d.ts.map +1 -0
- package/dist/utils/zip-extractor.js +275 -0
- package/dist/utils/zip-extractor.js.map +1 -0
- package/docs/harness-architecture.md +34 -22
- package/docs/index.md +1 -1
- package/docs/resources.md +1 -1
- package/docs/settings.md +39 -103
- package/docs/usage.md +7 -5
- package/docs/worker-profiles.md +9 -7
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
- package/dist/core/secrets/secret-vault-format.d.ts +0 -112
- package/dist/core/secrets/secret-vault-format.d.ts.map +0 -1
- package/dist/core/secrets/secret-vault-format.js +0 -396
- package/dist/core/secrets/secret-vault-format.js.map +0 -1
- package/dist/core/secrets/secret-vault.d.ts +0 -72
- package/dist/core/secrets/secret-vault.d.ts.map +0 -1
- package/dist/core/secrets/secret-vault.js +0 -663
- package/dist/core/secrets/secret-vault.js.map +0 -1
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { lstat, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { inflateRaw } from "node:zlib";
|
|
4
|
+
const LOCAL_FILE_SIGNATURE = 0x04034b50;
|
|
5
|
+
const CENTRAL_FILE_SIGNATURE = 0x02014b50;
|
|
6
|
+
const END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;
|
|
7
|
+
const END_OF_CENTRAL_DIRECTORY_BYTES = 22;
|
|
8
|
+
const MAX_ZIP_COMMENT_BYTES = 65_535;
|
|
9
|
+
const MAX_ARCHIVE_BYTES = 512 * 1024 * 1024;
|
|
10
|
+
const MAX_ENTRY_BYTES = 512 * 1024 * 1024;
|
|
11
|
+
const MAX_TOTAL_BYTES = 1024 * 1024 * 1024;
|
|
12
|
+
const MAX_ENTRIES = 4_096;
|
|
13
|
+
const MAX_PATH_BYTES = 4_096;
|
|
14
|
+
const ZIP64_16_BIT_SENTINEL = 0xffff;
|
|
15
|
+
const ZIP64_32_BIT_SENTINEL = 0xffffffff;
|
|
16
|
+
export class ZipExtractionError extends Error {
|
|
17
|
+
constructor(code, message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "ZipExtractionError";
|
|
20
|
+
this.code = code;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const CRC32_TABLE = new Uint32Array(256);
|
|
24
|
+
for (let index = 0; index < CRC32_TABLE.length; index++) {
|
|
25
|
+
let value = index;
|
|
26
|
+
for (let bit = 0; bit < 8; bit++)
|
|
27
|
+
value = (value >>> 1) ^ (value & 1 ? 0xedb88320 : 0);
|
|
28
|
+
CRC32_TABLE[index] = value >>> 0;
|
|
29
|
+
}
|
|
30
|
+
function crc32(value) {
|
|
31
|
+
let checksum = 0xffffffff;
|
|
32
|
+
for (const byte of value)
|
|
33
|
+
checksum = (checksum >>> 8) ^ (CRC32_TABLE[(checksum ^ byte) & 0xff] ?? 0);
|
|
34
|
+
return (checksum ^ 0xffffffff) >>> 0;
|
|
35
|
+
}
|
|
36
|
+
function invalidArchive(message) {
|
|
37
|
+
throw new ZipExtractionError("invalid_archive", message);
|
|
38
|
+
}
|
|
39
|
+
function assertBufferRange(buffer, offset, bytes) {
|
|
40
|
+
if (!Number.isSafeInteger(offset) ||
|
|
41
|
+
!Number.isSafeInteger(bytes) ||
|
|
42
|
+
offset < 0 ||
|
|
43
|
+
bytes < 0 ||
|
|
44
|
+
offset + bytes > buffer.length) {
|
|
45
|
+
invalidArchive("ZIP structure extends beyond the archive boundary.");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function findEndOfCentralDirectory(archive) {
|
|
49
|
+
const minimumOffset = Math.max(0, archive.length - END_OF_CENTRAL_DIRECTORY_BYTES - MAX_ZIP_COMMENT_BYTES);
|
|
50
|
+
for (let offset = archive.length - END_OF_CENTRAL_DIRECTORY_BYTES; offset >= minimumOffset; offset--) {
|
|
51
|
+
if (archive.readUInt32LE(offset) !== END_OF_CENTRAL_DIRECTORY_SIGNATURE)
|
|
52
|
+
continue;
|
|
53
|
+
const commentBytes = archive.readUInt16LE(offset + 20);
|
|
54
|
+
if (offset + END_OF_CENTRAL_DIRECTORY_BYTES + commentBytes === archive.length)
|
|
55
|
+
return offset;
|
|
56
|
+
}
|
|
57
|
+
return invalidArchive("ZIP end-of-central-directory record is missing.");
|
|
58
|
+
}
|
|
59
|
+
function normalizeEntryPath(rawName) {
|
|
60
|
+
if (!rawName || rawName.includes("\0") || rawName.includes("\ufffd")) {
|
|
61
|
+
throw new ZipExtractionError("unsafe_entry", "ZIP contains an invalid entry path.");
|
|
62
|
+
}
|
|
63
|
+
const portableName = rawName.replaceAll("\\", "/");
|
|
64
|
+
if (portableName.startsWith("/") || isAbsolute(portableName) || /^[A-Za-z]:/u.test(portableName)) {
|
|
65
|
+
throw new ZipExtractionError("unsafe_entry", "ZIP contains an absolute entry path.");
|
|
66
|
+
}
|
|
67
|
+
const directory = portableName.endsWith("/");
|
|
68
|
+
const segments = portableName.split("/");
|
|
69
|
+
if (directory)
|
|
70
|
+
segments.pop();
|
|
71
|
+
if (segments.length === 0 ||
|
|
72
|
+
segments.some((segment) => segment.length === 0 || segment === "." || segment === ".." || segment.includes(":"))) {
|
|
73
|
+
throw new ZipExtractionError("unsafe_entry", "ZIP contains a traversal or ambiguous entry path.");
|
|
74
|
+
}
|
|
75
|
+
return { segments, directory };
|
|
76
|
+
}
|
|
77
|
+
function parseEntries(archive) {
|
|
78
|
+
const endOffset = findEndOfCentralDirectory(archive);
|
|
79
|
+
const diskNumber = archive.readUInt16LE(endOffset + 4);
|
|
80
|
+
const centralDiskNumber = archive.readUInt16LE(endOffset + 6);
|
|
81
|
+
const entriesOnDisk = archive.readUInt16LE(endOffset + 8);
|
|
82
|
+
const entryCount = archive.readUInt16LE(endOffset + 10);
|
|
83
|
+
const centralBytes = archive.readUInt32LE(endOffset + 12);
|
|
84
|
+
const centralOffset = archive.readUInt32LE(endOffset + 16);
|
|
85
|
+
if (diskNumber !== 0 ||
|
|
86
|
+
centralDiskNumber !== 0 ||
|
|
87
|
+
entriesOnDisk !== entryCount ||
|
|
88
|
+
entryCount === ZIP64_16_BIT_SENTINEL ||
|
|
89
|
+
centralBytes === ZIP64_32_BIT_SENTINEL ||
|
|
90
|
+
centralOffset === ZIP64_32_BIT_SENTINEL) {
|
|
91
|
+
throw new ZipExtractionError("unsupported_archive", "Multi-disk and ZIP64 archives are unsupported.");
|
|
92
|
+
}
|
|
93
|
+
if (entryCount > MAX_ENTRIES) {
|
|
94
|
+
throw new ZipExtractionError("limit_exceeded", `ZIP contains more than ${MAX_ENTRIES} entries.`);
|
|
95
|
+
}
|
|
96
|
+
assertBufferRange(archive, centralOffset, centralBytes);
|
|
97
|
+
if (centralOffset + centralBytes > endOffset)
|
|
98
|
+
invalidArchive("ZIP central directory overlaps its footer.");
|
|
99
|
+
const entries = [];
|
|
100
|
+
const seenPaths = new Set();
|
|
101
|
+
let totalBytes = 0;
|
|
102
|
+
let cursor = centralOffset;
|
|
103
|
+
for (let index = 0; index < entryCount; index++) {
|
|
104
|
+
assertBufferRange(archive, cursor, 46);
|
|
105
|
+
if (archive.readUInt32LE(cursor) !== CENTRAL_FILE_SIGNATURE) {
|
|
106
|
+
invalidArchive("ZIP central directory entry is malformed.");
|
|
107
|
+
}
|
|
108
|
+
const versionMadeBy = archive.readUInt16LE(cursor + 4);
|
|
109
|
+
const flags = archive.readUInt16LE(cursor + 8);
|
|
110
|
+
const method = archive.readUInt16LE(cursor + 10);
|
|
111
|
+
const checksum = archive.readUInt32LE(cursor + 16);
|
|
112
|
+
const compressedBytes = archive.readUInt32LE(cursor + 20);
|
|
113
|
+
const uncompressedBytes = archive.readUInt32LE(cursor + 24);
|
|
114
|
+
const nameBytes = archive.readUInt16LE(cursor + 28);
|
|
115
|
+
const extraBytes = archive.readUInt16LE(cursor + 30);
|
|
116
|
+
const commentBytes = archive.readUInt16LE(cursor + 32);
|
|
117
|
+
const startingDisk = archive.readUInt16LE(cursor + 34);
|
|
118
|
+
const externalAttributes = archive.readUInt32LE(cursor + 38);
|
|
119
|
+
const localHeaderOffset = archive.readUInt32LE(cursor + 42);
|
|
120
|
+
const variableBytes = nameBytes + extraBytes + commentBytes;
|
|
121
|
+
assertBufferRange(archive, cursor + 46, variableBytes);
|
|
122
|
+
if (startingDisk !== 0 ||
|
|
123
|
+
compressedBytes === ZIP64_32_BIT_SENTINEL ||
|
|
124
|
+
uncompressedBytes === ZIP64_32_BIT_SENTINEL ||
|
|
125
|
+
localHeaderOffset === ZIP64_32_BIT_SENTINEL) {
|
|
126
|
+
throw new ZipExtractionError("unsupported_archive", "ZIP64 entries are unsupported.");
|
|
127
|
+
}
|
|
128
|
+
if ((flags & 0x0001) !== 0) {
|
|
129
|
+
throw new ZipExtractionError("unsupported_archive", "Encrypted ZIP entries are unsupported.");
|
|
130
|
+
}
|
|
131
|
+
if (method !== 0 && method !== 8) {
|
|
132
|
+
throw new ZipExtractionError("unsupported_archive", "ZIP compression method is unsupported.");
|
|
133
|
+
}
|
|
134
|
+
if (nameBytes === 0 || nameBytes > MAX_PATH_BYTES) {
|
|
135
|
+
throw new ZipExtractionError("limit_exceeded", "ZIP entry path exceeds its bound.");
|
|
136
|
+
}
|
|
137
|
+
const rawName = archive.subarray(cursor + 46, cursor + 46 + nameBytes).toString("utf8");
|
|
138
|
+
const normalized = normalizeEntryPath(rawName);
|
|
139
|
+
const hostSystem = versionMadeBy >>> 8;
|
|
140
|
+
const unixMode = hostSystem === 3 ? (externalAttributes >>> 16) & 0xffff : 0;
|
|
141
|
+
const unixType = unixMode & 0xf000;
|
|
142
|
+
const directory = normalized.directory || unixType === 0x4000 || (externalAttributes & 0x10) !== 0;
|
|
143
|
+
if (unixType !== 0 && unixType !== 0x4000 && unixType !== 0x8000) {
|
|
144
|
+
throw new ZipExtractionError("unsafe_entry", "ZIP contains a non-file entry.");
|
|
145
|
+
}
|
|
146
|
+
if (uncompressedBytes > MAX_ENTRY_BYTES) {
|
|
147
|
+
throw new ZipExtractionError("limit_exceeded", "ZIP entry exceeds its uncompressed-size bound.");
|
|
148
|
+
}
|
|
149
|
+
totalBytes += uncompressedBytes;
|
|
150
|
+
if (!Number.isSafeInteger(totalBytes) || totalBytes > MAX_TOTAL_BYTES) {
|
|
151
|
+
throw new ZipExtractionError("limit_exceeded", "ZIP exceeds its total uncompressed-size bound.");
|
|
152
|
+
}
|
|
153
|
+
const identity = normalized.segments.join("/");
|
|
154
|
+
const collisionKey = process.platform === "win32" ? identity.toLowerCase() : identity;
|
|
155
|
+
if (seenPaths.has(collisionKey)) {
|
|
156
|
+
throw new ZipExtractionError("unsafe_entry", "ZIP contains duplicate entry paths.");
|
|
157
|
+
}
|
|
158
|
+
seenPaths.add(collisionKey);
|
|
159
|
+
entries.push({
|
|
160
|
+
segments: normalized.segments,
|
|
161
|
+
directory,
|
|
162
|
+
flags,
|
|
163
|
+
method,
|
|
164
|
+
checksum,
|
|
165
|
+
compressedBytes,
|
|
166
|
+
uncompressedBytes,
|
|
167
|
+
localHeaderOffset,
|
|
168
|
+
});
|
|
169
|
+
cursor += 46 + variableBytes;
|
|
170
|
+
}
|
|
171
|
+
if (cursor > centralOffset + centralBytes)
|
|
172
|
+
invalidArchive("ZIP central directory exceeds its declared size.");
|
|
173
|
+
return entries;
|
|
174
|
+
}
|
|
175
|
+
async function inflateEntry(compressed, expectedBytes) {
|
|
176
|
+
return new Promise((resolvePromise, reject) => {
|
|
177
|
+
inflateRaw(compressed, { maxOutputLength: Math.max(1, expectedBytes) }, (error, output) => {
|
|
178
|
+
if (error) {
|
|
179
|
+
reject(new ZipExtractionError("invalid_archive", "ZIP deflate stream is invalid."));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
resolvePromise(output);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async function ensureDirectory(root, segments) {
|
|
187
|
+
let current = root;
|
|
188
|
+
for (const segment of segments) {
|
|
189
|
+
current = join(current, segment);
|
|
190
|
+
try {
|
|
191
|
+
const metadata = await lstat(current);
|
|
192
|
+
if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
|
|
193
|
+
throw new ZipExtractionError("unsafe_entry", "ZIP extraction path crosses a non-directory.");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
if (error instanceof ZipExtractionError)
|
|
198
|
+
throw error;
|
|
199
|
+
if (error.code !== "ENOENT")
|
|
200
|
+
throw error;
|
|
201
|
+
await mkdir(current, { mode: 0o700 });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function resolveEntryTarget(destination, segments) {
|
|
206
|
+
const target = resolve(destination, ...segments);
|
|
207
|
+
const fromDestination = relative(destination, target);
|
|
208
|
+
if (fromDestination === ".." || fromDestination.startsWith(`..${sep}`) || isAbsolute(fromDestination)) {
|
|
209
|
+
throw new ZipExtractionError("unsafe_entry", "ZIP entry escapes the extraction destination.");
|
|
210
|
+
}
|
|
211
|
+
return target;
|
|
212
|
+
}
|
|
213
|
+
async function extractEntry(archive, destination, entry) {
|
|
214
|
+
if (entry.directory) {
|
|
215
|
+
await ensureDirectory(destination, entry.segments);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
await ensureDirectory(destination, entry.segments.slice(0, -1));
|
|
219
|
+
const target = resolveEntryTarget(destination, entry.segments);
|
|
220
|
+
try {
|
|
221
|
+
await lstat(target);
|
|
222
|
+
throw new ZipExtractionError("unsafe_entry", "ZIP extraction refuses to overwrite an existing path.");
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
if (error instanceof ZipExtractionError)
|
|
226
|
+
throw error;
|
|
227
|
+
if (error.code !== "ENOENT")
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
assertBufferRange(archive, entry.localHeaderOffset, 30);
|
|
231
|
+
if (archive.readUInt32LE(entry.localHeaderOffset) !== LOCAL_FILE_SIGNATURE) {
|
|
232
|
+
invalidArchive("ZIP local file header is malformed.");
|
|
233
|
+
}
|
|
234
|
+
const localFlags = archive.readUInt16LE(entry.localHeaderOffset + 6);
|
|
235
|
+
const localMethod = archive.readUInt16LE(entry.localHeaderOffset + 8);
|
|
236
|
+
const localNameBytes = archive.readUInt16LE(entry.localHeaderOffset + 26);
|
|
237
|
+
const localExtraBytes = archive.readUInt16LE(entry.localHeaderOffset + 28);
|
|
238
|
+
if (localFlags !== entry.flags || localMethod !== entry.method) {
|
|
239
|
+
invalidArchive("ZIP local and central headers disagree.");
|
|
240
|
+
}
|
|
241
|
+
const dataOffset = entry.localHeaderOffset + 30 + localNameBytes + localExtraBytes;
|
|
242
|
+
assertBufferRange(archive, dataOffset, entry.compressedBytes);
|
|
243
|
+
const compressed = archive.subarray(dataOffset, dataOffset + entry.compressedBytes);
|
|
244
|
+
const output = entry.method === 0 ? Buffer.from(compressed) : await inflateEntry(compressed, entry.uncompressedBytes);
|
|
245
|
+
if (output.length !== entry.uncompressedBytes || crc32(output) !== entry.checksum) {
|
|
246
|
+
invalidArchive("ZIP entry size or checksum is invalid.");
|
|
247
|
+
}
|
|
248
|
+
await writeFile(target, output, { flag: "wx", mode: 0o600 });
|
|
249
|
+
}
|
|
250
|
+
/** Extract a bounded ordinary ZIP with no dependency on platform archive utilities. */
|
|
251
|
+
export async function extractZipFile(archivePath, destination) {
|
|
252
|
+
try {
|
|
253
|
+
const metadata = await lstat(archivePath);
|
|
254
|
+
if (metadata.isSymbolicLink() || !metadata.isFile())
|
|
255
|
+
invalidArchive("ZIP source is not a regular file.");
|
|
256
|
+
if (metadata.size > MAX_ARCHIVE_BYTES) {
|
|
257
|
+
throw new ZipExtractionError("limit_exceeded", "ZIP archive exceeds its 512 MiB input bound.");
|
|
258
|
+
}
|
|
259
|
+
const archive = await readFile(archivePath);
|
|
260
|
+
const entries = parseEntries(archive);
|
|
261
|
+
await mkdir(destination, { recursive: true, mode: 0o700 });
|
|
262
|
+
const destinationMetadata = await lstat(destination);
|
|
263
|
+
if (destinationMetadata.isSymbolicLink() || !destinationMetadata.isDirectory()) {
|
|
264
|
+
throw new ZipExtractionError("unsafe_entry", "ZIP destination is not a regular directory.");
|
|
265
|
+
}
|
|
266
|
+
for (const entry of entries)
|
|
267
|
+
await extractEntry(archive, destination, entry);
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
if (error instanceof ZipExtractionError)
|
|
271
|
+
throw error;
|
|
272
|
+
throw new ZipExtractionError("write_failed", `ZIP extraction failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=zip-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zip-extractor.js","sourceRoot":"","sources":["../../src/utils/zip-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { inflateRaw } from \"node:zlib\";\n\nconst LOCAL_FILE_SIGNATURE = 0x04034b50;\nconst CENTRAL_FILE_SIGNATURE = 0x02014b50;\nconst END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;\nconst END_OF_CENTRAL_DIRECTORY_BYTES = 22;\nconst MAX_ZIP_COMMENT_BYTES = 65_535;\nconst MAX_ARCHIVE_BYTES = 512 * 1024 * 1024;\nconst MAX_ENTRY_BYTES = 512 * 1024 * 1024;\nconst MAX_TOTAL_BYTES = 1024 * 1024 * 1024;\nconst MAX_ENTRIES = 4_096;\nconst MAX_PATH_BYTES = 4_096;\nconst ZIP64_16_BIT_SENTINEL = 0xffff;\nconst ZIP64_32_BIT_SENTINEL = 0xffffffff;\n\nexport type ZipExtractionErrorCode =\n\t| \"invalid_archive\"\n\t| \"limit_exceeded\"\n\t| \"unsafe_entry\"\n\t| \"unsupported_archive\"\n\t| \"write_failed\";\n\nexport class ZipExtractionError extends Error {\n\treadonly code: ZipExtractionErrorCode;\n\n\tconstructor(code: ZipExtractionErrorCode, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ZipExtractionError\";\n\t\tthis.code = code;\n\t}\n}\n\ninterface ZipEntry {\n\tsegments: string[];\n\tdirectory: boolean;\n\tflags: number;\n\tmethod: number;\n\tchecksum: number;\n\tcompressedBytes: number;\n\tuncompressedBytes: number;\n\tlocalHeaderOffset: number;\n}\n\nconst CRC32_TABLE = new Uint32Array(256);\nfor (let index = 0; index < CRC32_TABLE.length; index++) {\n\tlet value = index;\n\tfor (let bit = 0; bit < 8; bit++) value = (value >>> 1) ^ (value & 1 ? 0xedb88320 : 0);\n\tCRC32_TABLE[index] = value >>> 0;\n}\n\nfunction crc32(value: Buffer): number {\n\tlet checksum = 0xffffffff;\n\tfor (const byte of value) checksum = (checksum >>> 8) ^ (CRC32_TABLE[(checksum ^ byte) & 0xff] ?? 0);\n\treturn (checksum ^ 0xffffffff) >>> 0;\n}\n\nfunction invalidArchive(message: string): never {\n\tthrow new ZipExtractionError(\"invalid_archive\", message);\n}\n\nfunction assertBufferRange(buffer: Buffer, offset: number, bytes: number): void {\n\tif (\n\t\t!Number.isSafeInteger(offset) ||\n\t\t!Number.isSafeInteger(bytes) ||\n\t\toffset < 0 ||\n\t\tbytes < 0 ||\n\t\toffset + bytes > buffer.length\n\t) {\n\t\tinvalidArchive(\"ZIP structure extends beyond the archive boundary.\");\n\t}\n}\n\nfunction findEndOfCentralDirectory(archive: Buffer): number {\n\tconst minimumOffset = Math.max(0, archive.length - END_OF_CENTRAL_DIRECTORY_BYTES - MAX_ZIP_COMMENT_BYTES);\n\tfor (let offset = archive.length - END_OF_CENTRAL_DIRECTORY_BYTES; offset >= minimumOffset; offset--) {\n\t\tif (archive.readUInt32LE(offset) !== END_OF_CENTRAL_DIRECTORY_SIGNATURE) continue;\n\t\tconst commentBytes = archive.readUInt16LE(offset + 20);\n\t\tif (offset + END_OF_CENTRAL_DIRECTORY_BYTES + commentBytes === archive.length) return offset;\n\t}\n\treturn invalidArchive(\"ZIP end-of-central-directory record is missing.\");\n}\n\nfunction normalizeEntryPath(rawName: string): { segments: string[]; directory: boolean } {\n\tif (!rawName || rawName.includes(\"\\0\") || rawName.includes(\"\\ufffd\")) {\n\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP contains an invalid entry path.\");\n\t}\n\tconst portableName = rawName.replaceAll(\"\\\\\", \"/\");\n\tif (portableName.startsWith(\"/\") || isAbsolute(portableName) || /^[A-Za-z]:/u.test(portableName)) {\n\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP contains an absolute entry path.\");\n\t}\n\tconst directory = portableName.endsWith(\"/\");\n\tconst segments = portableName.split(\"/\");\n\tif (directory) segments.pop();\n\tif (\n\t\tsegments.length === 0 ||\n\t\tsegments.some((segment) => segment.length === 0 || segment === \".\" || segment === \"..\" || segment.includes(\":\"))\n\t) {\n\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP contains a traversal or ambiguous entry path.\");\n\t}\n\treturn { segments, directory };\n}\n\nfunction parseEntries(archive: Buffer): ZipEntry[] {\n\tconst endOffset = findEndOfCentralDirectory(archive);\n\tconst diskNumber = archive.readUInt16LE(endOffset + 4);\n\tconst centralDiskNumber = archive.readUInt16LE(endOffset + 6);\n\tconst entriesOnDisk = archive.readUInt16LE(endOffset + 8);\n\tconst entryCount = archive.readUInt16LE(endOffset + 10);\n\tconst centralBytes = archive.readUInt32LE(endOffset + 12);\n\tconst centralOffset = archive.readUInt32LE(endOffset + 16);\n\tif (\n\t\tdiskNumber !== 0 ||\n\t\tcentralDiskNumber !== 0 ||\n\t\tentriesOnDisk !== entryCount ||\n\t\tentryCount === ZIP64_16_BIT_SENTINEL ||\n\t\tcentralBytes === ZIP64_32_BIT_SENTINEL ||\n\t\tcentralOffset === ZIP64_32_BIT_SENTINEL\n\t) {\n\t\tthrow new ZipExtractionError(\"unsupported_archive\", \"Multi-disk and ZIP64 archives are unsupported.\");\n\t}\n\tif (entryCount > MAX_ENTRIES) {\n\t\tthrow new ZipExtractionError(\"limit_exceeded\", `ZIP contains more than ${MAX_ENTRIES} entries.`);\n\t}\n\tassertBufferRange(archive, centralOffset, centralBytes);\n\tif (centralOffset + centralBytes > endOffset) invalidArchive(\"ZIP central directory overlaps its footer.\");\n\n\tconst entries: ZipEntry[] = [];\n\tconst seenPaths = new Set<string>();\n\tlet totalBytes = 0;\n\tlet cursor = centralOffset;\n\tfor (let index = 0; index < entryCount; index++) {\n\t\tassertBufferRange(archive, cursor, 46);\n\t\tif (archive.readUInt32LE(cursor) !== CENTRAL_FILE_SIGNATURE) {\n\t\t\tinvalidArchive(\"ZIP central directory entry is malformed.\");\n\t\t}\n\t\tconst versionMadeBy = archive.readUInt16LE(cursor + 4);\n\t\tconst flags = archive.readUInt16LE(cursor + 8);\n\t\tconst method = archive.readUInt16LE(cursor + 10);\n\t\tconst checksum = archive.readUInt32LE(cursor + 16);\n\t\tconst compressedBytes = archive.readUInt32LE(cursor + 20);\n\t\tconst uncompressedBytes = archive.readUInt32LE(cursor + 24);\n\t\tconst nameBytes = archive.readUInt16LE(cursor + 28);\n\t\tconst extraBytes = archive.readUInt16LE(cursor + 30);\n\t\tconst commentBytes = archive.readUInt16LE(cursor + 32);\n\t\tconst startingDisk = archive.readUInt16LE(cursor + 34);\n\t\tconst externalAttributes = archive.readUInt32LE(cursor + 38);\n\t\tconst localHeaderOffset = archive.readUInt32LE(cursor + 42);\n\t\tconst variableBytes = nameBytes + extraBytes + commentBytes;\n\t\tassertBufferRange(archive, cursor + 46, variableBytes);\n\t\tif (\n\t\t\tstartingDisk !== 0 ||\n\t\t\tcompressedBytes === ZIP64_32_BIT_SENTINEL ||\n\t\t\tuncompressedBytes === ZIP64_32_BIT_SENTINEL ||\n\t\t\tlocalHeaderOffset === ZIP64_32_BIT_SENTINEL\n\t\t) {\n\t\t\tthrow new ZipExtractionError(\"unsupported_archive\", \"ZIP64 entries are unsupported.\");\n\t\t}\n\t\tif ((flags & 0x0001) !== 0) {\n\t\t\tthrow new ZipExtractionError(\"unsupported_archive\", \"Encrypted ZIP entries are unsupported.\");\n\t\t}\n\t\tif (method !== 0 && method !== 8) {\n\t\t\tthrow new ZipExtractionError(\"unsupported_archive\", \"ZIP compression method is unsupported.\");\n\t\t}\n\t\tif (nameBytes === 0 || nameBytes > MAX_PATH_BYTES) {\n\t\t\tthrow new ZipExtractionError(\"limit_exceeded\", \"ZIP entry path exceeds its bound.\");\n\t\t}\n\t\tconst rawName = archive.subarray(cursor + 46, cursor + 46 + nameBytes).toString(\"utf8\");\n\t\tconst normalized = normalizeEntryPath(rawName);\n\t\tconst hostSystem = versionMadeBy >>> 8;\n\t\tconst unixMode = hostSystem === 3 ? (externalAttributes >>> 16) & 0xffff : 0;\n\t\tconst unixType = unixMode & 0xf000;\n\t\tconst directory = normalized.directory || unixType === 0x4000 || (externalAttributes & 0x10) !== 0;\n\t\tif (unixType !== 0 && unixType !== 0x4000 && unixType !== 0x8000) {\n\t\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP contains a non-file entry.\");\n\t\t}\n\t\tif (uncompressedBytes > MAX_ENTRY_BYTES) {\n\t\t\tthrow new ZipExtractionError(\"limit_exceeded\", \"ZIP entry exceeds its uncompressed-size bound.\");\n\t\t}\n\t\ttotalBytes += uncompressedBytes;\n\t\tif (!Number.isSafeInteger(totalBytes) || totalBytes > MAX_TOTAL_BYTES) {\n\t\t\tthrow new ZipExtractionError(\"limit_exceeded\", \"ZIP exceeds its total uncompressed-size bound.\");\n\t\t}\n\t\tconst identity = normalized.segments.join(\"/\");\n\t\tconst collisionKey = process.platform === \"win32\" ? identity.toLowerCase() : identity;\n\t\tif (seenPaths.has(collisionKey)) {\n\t\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP contains duplicate entry paths.\");\n\t\t}\n\t\tseenPaths.add(collisionKey);\n\t\tentries.push({\n\t\t\tsegments: normalized.segments,\n\t\t\tdirectory,\n\t\t\tflags,\n\t\t\tmethod,\n\t\t\tchecksum,\n\t\t\tcompressedBytes,\n\t\t\tuncompressedBytes,\n\t\t\tlocalHeaderOffset,\n\t\t});\n\t\tcursor += 46 + variableBytes;\n\t}\n\tif (cursor > centralOffset + centralBytes) invalidArchive(\"ZIP central directory exceeds its declared size.\");\n\treturn entries;\n}\n\nasync function inflateEntry(compressed: Buffer, expectedBytes: number): Promise<Buffer> {\n\treturn new Promise((resolvePromise, reject) => {\n\t\tinflateRaw(compressed, { maxOutputLength: Math.max(1, expectedBytes) }, (error, output) => {\n\t\t\tif (error) {\n\t\t\t\treject(new ZipExtractionError(\"invalid_archive\", \"ZIP deflate stream is invalid.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresolvePromise(output);\n\t\t});\n\t});\n}\n\nasync function ensureDirectory(root: string, segments: readonly string[]): Promise<void> {\n\tlet current = root;\n\tfor (const segment of segments) {\n\t\tcurrent = join(current, segment);\n\t\ttry {\n\t\t\tconst metadata = await lstat(current);\n\t\t\tif (metadata.isSymbolicLink() || !metadata.isDirectory()) {\n\t\t\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP extraction path crosses a non-directory.\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof ZipExtractionError) throw error;\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t\tawait mkdir(current, { mode: 0o700 });\n\t\t}\n\t}\n}\n\nfunction resolveEntryTarget(destination: string, segments: readonly string[]): string {\n\tconst target = resolve(destination, ...segments);\n\tconst fromDestination = relative(destination, target);\n\tif (fromDestination === \"..\" || fromDestination.startsWith(`..${sep}`) || isAbsolute(fromDestination)) {\n\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP entry escapes the extraction destination.\");\n\t}\n\treturn target;\n}\n\nasync function extractEntry(archive: Buffer, destination: string, entry: ZipEntry): Promise<void> {\n\tif (entry.directory) {\n\t\tawait ensureDirectory(destination, entry.segments);\n\t\treturn;\n\t}\n\tawait ensureDirectory(destination, entry.segments.slice(0, -1));\n\tconst target = resolveEntryTarget(destination, entry.segments);\n\ttry {\n\t\tawait lstat(target);\n\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP extraction refuses to overwrite an existing path.\");\n\t} catch (error) {\n\t\tif (error instanceof ZipExtractionError) throw error;\n\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t}\n\n\tassertBufferRange(archive, entry.localHeaderOffset, 30);\n\tif (archive.readUInt32LE(entry.localHeaderOffset) !== LOCAL_FILE_SIGNATURE) {\n\t\tinvalidArchive(\"ZIP local file header is malformed.\");\n\t}\n\tconst localFlags = archive.readUInt16LE(entry.localHeaderOffset + 6);\n\tconst localMethod = archive.readUInt16LE(entry.localHeaderOffset + 8);\n\tconst localNameBytes = archive.readUInt16LE(entry.localHeaderOffset + 26);\n\tconst localExtraBytes = archive.readUInt16LE(entry.localHeaderOffset + 28);\n\tif (localFlags !== entry.flags || localMethod !== entry.method) {\n\t\tinvalidArchive(\"ZIP local and central headers disagree.\");\n\t}\n\tconst dataOffset = entry.localHeaderOffset + 30 + localNameBytes + localExtraBytes;\n\tassertBufferRange(archive, dataOffset, entry.compressedBytes);\n\tconst compressed = archive.subarray(dataOffset, dataOffset + entry.compressedBytes);\n\tconst output =\n\t\tentry.method === 0 ? Buffer.from(compressed) : await inflateEntry(compressed, entry.uncompressedBytes);\n\tif (output.length !== entry.uncompressedBytes || crc32(output) !== entry.checksum) {\n\t\tinvalidArchive(\"ZIP entry size or checksum is invalid.\");\n\t}\n\tawait writeFile(target, output, { flag: \"wx\", mode: 0o600 });\n}\n\n/** Extract a bounded ordinary ZIP with no dependency on platform archive utilities. */\nexport async function extractZipFile(archivePath: string, destination: string): Promise<void> {\n\ttry {\n\t\tconst metadata = await lstat(archivePath);\n\t\tif (metadata.isSymbolicLink() || !metadata.isFile()) invalidArchive(\"ZIP source is not a regular file.\");\n\t\tif (metadata.size > MAX_ARCHIVE_BYTES) {\n\t\t\tthrow new ZipExtractionError(\"limit_exceeded\", \"ZIP archive exceeds its 512 MiB input bound.\");\n\t\t}\n\t\tconst archive = await readFile(archivePath);\n\t\tconst entries = parseEntries(archive);\n\t\tawait mkdir(destination, { recursive: true, mode: 0o700 });\n\t\tconst destinationMetadata = await lstat(destination);\n\t\tif (destinationMetadata.isSymbolicLink() || !destinationMetadata.isDirectory()) {\n\t\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP destination is not a regular directory.\");\n\t\t}\n\t\tfor (const entry of entries) await extractEntry(archive, destination, entry);\n\t} catch (error) {\n\t\tif (error instanceof ZipExtractionError) throw error;\n\t\tthrow new ZipExtractionError(\n\t\t\t\"write_failed\",\n\t\t\t`ZIP extraction failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t}\n}\n"]}
|
|
@@ -49,7 +49,9 @@ second line of defense, not the main implementation.
|
|
|
49
49
|
context paths before content loading. Live extension loading repeats the profile check before
|
|
50
50
|
import.
|
|
51
51
|
- Isolated research and worker lanes create fresh tools only for their expanded lane grant. They do
|
|
52
|
-
not copy the foreground registry, extensions, skills
|
|
52
|
+
not copy the foreground registry, extensions, or skills. Native agents may materialize fresh
|
|
53
|
+
memory, write/edit, platform-shell, and recursive-delegation adapters when their immutable grant
|
|
54
|
+
and live service switches admit them.
|
|
53
55
|
- Worker orchestration state is lazily materialized only when the active surface grants `delegate`
|
|
54
56
|
(or a managed-lane terminal report requires the shared notifier); worker-role sessions allocate no
|
|
55
57
|
worker scheduler, lifecycle store, or notification coordinator.
|
|
@@ -65,20 +67,23 @@ footprint must omit that extension at the resource layer, not merely block one o
|
|
|
65
67
|
|
|
66
68
|
## Child work contracts
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
The model-facing `delegate` request can choose role label, authenticated model, exact supported
|
|
71
|
+
reasoning level, classified tools, semantic capabilities, read/write paths, and budget. An optional
|
|
72
|
+
owner-authored orchestration profile supplies reusable defaults; it is not a dispatch allowlist.
|
|
73
|
+
Profile-free roots start from the foreground model and maximum host-permitted classified core
|
|
74
|
+
surface. Descendants inherit immutable parent execution authority by default. The host validates and
|
|
75
|
+
materializes every choice, intersects it with parent authority and live global switches, and persists
|
|
76
|
+
the exact resulting contract before execution. Built-in roles are routing/audit labels unless an
|
|
77
|
+
embedding supplies an explicit role ceiling.
|
|
74
78
|
|
|
75
79
|
The durable control plane is an append-only session event store projected into objectives, DAG
|
|
76
80
|
tasks, attempts, leases, checkpoints, logical agent bindings, typed results, and a notification
|
|
77
81
|
outbox. Every result carries its attempt lease and fencing token. A stale worker therefore cannot
|
|
78
|
-
win a race after retry or resume. In-process
|
|
79
|
-
|
|
80
|
-
Pi worker
|
|
81
|
-
the same session, worktree, orchestration
|
|
82
|
+
win a race after retry or resume. In-process agents retain durable logical identity, lineage, exact
|
|
83
|
+
transcript, mailbox, immutable execution contract, and checkpoint usage across interruption and
|
|
84
|
+
resume. A process-backed Pi worker similarly retains its logical agent ID and resume context and is
|
|
85
|
+
woken with `/resume` against the same session, worktree, orchestration preset, and checkpoint
|
|
86
|
+
pointers when present.
|
|
82
87
|
|
|
83
88
|
Policy is a mandatory pre-lease gate. An attempt cannot lease without a compiled execution grant.
|
|
84
89
|
When compilation crosses owner authority or budget, the gate persists a typed approval request and
|
|
@@ -91,17 +96,24 @@ Acceptance is a runtime invariant, not a worker claim. Task criterion IDs must r
|
|
|
91
96
|
objective, a completed criterion-bound result must carry trusted evidence for every linked criterion,
|
|
92
97
|
and an objective cannot close until all required criteria have trusted evidence in its task results.
|
|
93
98
|
|
|
94
|
-
In-process
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
In-process agents have fresh context, a synthetic cache-affinity key, a selected model and exact
|
|
100
|
+
reasoning level, and a construction-filtered tool surface. They inherit `delegate`, may inspect exact
|
|
101
|
+
paginated transcripts for any logical peer in the session tree, exchange threaded messages, and may
|
|
102
|
+
materialize a persistent per-agent platform shell. There is no framework depth or fan-out cap; one
|
|
103
|
+
global scheduler owns concurrency and durable queuing, one root coordinator owns cumulative
|
|
104
|
+
token/cost/tool/active-time/attempt budgets, and exact ancestor task cycles are rejected. `run_process`
|
|
105
|
+
keeps its separate exact executable allowlist, direct argv, scoped environment, bounded output, and
|
|
106
|
+
process-tree termination contract. The platform shell is real host process authority, not container
|
|
107
|
+
isolation or a path-scoped substitute for `write`/`edit`.
|
|
108
|
+
|
|
109
|
+
Worker output remains untrusted evidence. The parent receives a bounded terminal event and retrieves
|
|
110
|
+
lane results explicitly; raw peer transcripts require an explicit `transcript` action and are paged
|
|
111
|
+
only for context size. A preset that requires independent verification names a separate owner-pinned
|
|
112
|
+
verifier profile. The runtime automatically dispatches that verifier as a durable task, withholds the
|
|
113
|
+
implementation's terminal handoff, and reconciles the typed verdict before the implementation can
|
|
114
|
+
become accepted. Restart recovery closes both persistence gaps: an implementation awaiting a
|
|
115
|
+
not-yet-created verifier is re-dispatched, while a persisted verifier result awaiting reconciliation
|
|
116
|
+
is applied exactly once.
|
|
105
117
|
|
|
106
118
|
Out-of-process managed workers use the same lifecycle shape:
|
|
107
119
|
|
package/docs/index.md
CHANGED
|
@@ -55,7 +55,7 @@ For the full first-run flow, see [Quickstart](quickstart.md).
|
|
|
55
55
|
|
|
56
56
|
- [Extensions](extensions.md) - TypeScript modules for tools, commands, events, and custom UI.
|
|
57
57
|
- [tmux agent manager](tmux-agent-manager.md) - bundled event-driven external-provider teams with parent wake handoffs.
|
|
58
|
-
- [Task worker
|
|
58
|
+
- [Task worker presets](worker-profiles.md) - optional immutable session presets for recursive agents with model-authored authority.
|
|
59
59
|
- [Skills](skills.md) - Agent Skills for reusable on-demand capabilities.
|
|
60
60
|
- [Resource profiles & library](resources.md) - curate extensions, skills, and agents per project or situation; share a catalog; install and back up.
|
|
61
61
|
- [Self-adaptation](self-adaptation.md) - draft live skills/extensions and run evidence-backed Pi harness improvement loops.
|
package/docs/resources.md
CHANGED
|
@@ -49,7 +49,7 @@ A profile may also bind a **model**, **thinking level**, **soul**, and **modelRo
|
|
|
49
49
|
|
|
50
50
|
Pi activates the complete situation as one runtime generation: model/router, thinking, soul, extensions, skills, prompts, agents, themes, and tools are reloaded together. A failed reload restores the prior valid generation instead of persisting a partial selection.
|
|
51
51
|
|
|
52
|
-
Built-in research
|
|
52
|
+
Built-in research lanes still use a fixed read-only profile with `read`, `grep`, `find`, and `ls`. Native worker agents use a different contract: orchestration profiles are optional presets, a profile-free root receives the maximum host-permitted classified core surface, and descendants inherit immutable authority while retaining recursive `delegate`, peer-transcript, messaging, memory, write/edit, and platform-shell capabilities when their parent and live service switches permit them. Lane tool globs expand to exact classified names. Opaque extension tools without orchestration metadata are not guessed into a child surface, and an explicit tool request that cannot be materialized is rejected visibly.
|
|
53
53
|
|
|
54
54
|
Profiles are stored in settings under `resourceProfiles`, and the active one(s) in `activeResourceProfile`:
|
|
55
55
|
|