@asterxsk/kiln 0.4.0 → 0.4.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/README.md +155 -155
- package/agent/AGENTS.md +67 -67
- package/agent/README.md +5 -5
- package/agent/extensions/ask-user/index.ts +418 -418
- package/agent/extensions/ask-user/install.ps1 +27 -27
- package/agent/extensions/ask-user/install.sh +24 -24
- package/agent/extensions/ask-user/package-lock.json +769 -769
- package/agent/extensions/ask-user/package.json +19 -19
- package/agent/extensions/ask-user/prompt.ts +45 -45
- package/agent/extensions/ask-user/tsconfig.json +7 -7
- package/agent/extensions/background-terminals/docs/implementation-guide.md +942 -942
- package/agent/extensions/background-terminals/index.ts +627 -627
- package/agent/extensions/background-terminals/install.ps1 +27 -27
- package/agent/extensions/background-terminals/install.sh +24 -24
- package/agent/extensions/background-terminals/manager.test.ts +735 -735
- package/agent/extensions/background-terminals/output.test.ts +109 -109
- package/agent/extensions/background-terminals/package-lock.json +769 -769
- package/agent/extensions/background-terminals/package.json +17 -17
- package/agent/extensions/background-terminals/prompt.test.ts +125 -125
- package/agent/extensions/background-terminals/ps.test.ts +82 -82
- package/agent/extensions/background-terminals/result-delivery.test.ts +44 -44
- package/agent/extensions/background-terminals/src/domain.ts +87 -87
- package/agent/extensions/background-terminals/src/manager.ts +907 -907
- package/agent/extensions/background-terminals/src/output.ts +84 -84
- package/agent/extensions/background-terminals/src/prompt.ts +142 -142
- package/agent/extensions/background-terminals/src/result-delivery.ts +27 -27
- package/agent/extensions/background-terminals/src/runtime.ts +36 -36
- package/agent/extensions/background-terminals/src/ui/output-view.ts +79 -79
- package/agent/extensions/background-terminals/src/ui/ps.ts +621 -621
- package/agent/extensions/background-terminals/tsconfig.json +7 -7
- package/agent/extensions/destructive/README.md +31 -31
- package/agent/extensions/destructive/index.ts +88 -88
- package/agent/extensions/destructive/install.ps1 +27 -27
- package/agent/extensions/destructive/install.sh +24 -24
- package/agent/extensions/file-search/index.spec.ts +443 -443
- package/agent/extensions/file-search/index.ts +459 -459
- package/agent/extensions/file-search/install.ps1 +27 -27
- package/agent/extensions/file-search/install.sh +24 -24
- package/agent/extensions/file-search/package-lock.json +2253 -2253
- package/agent/extensions/file-search/package.json +23 -23
- package/agent/extensions/file-search/src/args.ts +122 -122
- package/agent/extensions/file-search/src/binaries.ts +422 -422
- package/agent/extensions/file-search/src/output.ts +126 -126
- package/agent/extensions/file-search/src/process.ts +146 -146
- package/agent/extensions/file-search/src/prompt.ts +52 -52
- package/agent/extensions/file-search/tsconfig.json +7 -7
- package/agent/extensions/goal/README.md +50 -50
- package/agent/extensions/goal/index.ts +155 -155
- package/agent/extensions/goal/install.ps1 +27 -27
- package/agent/extensions/goal/install.sh +24 -24
- package/agent/extensions/kiln-update/README.md +38 -25
- package/agent/extensions/kiln-update/index.ts +177 -99
- package/agent/extensions/modelconf/PLAN.md +915 -915
- package/agent/extensions/modelconf/README.md +66 -66
- package/agent/extensions/modelconf/index.ts +296 -296
- package/agent/extensions/modelconf/install.ps1 +27 -27
- package/agent/extensions/modelconf/install.sh +24 -24
- package/agent/extensions/modelconf/package-lock.json +1809 -1809
- package/agent/extensions/modelconf/package.json +13 -13
- package/agent/extensions/modelconf/src/fuzzy.ts +26 -26
- package/agent/extensions/modelconf/src/glob.ts +13 -13
- package/agent/extensions/modelconf/src/persistence.test.ts +46 -46
- package/agent/extensions/modelconf/src/persistence.ts +164 -164
- package/agent/extensions/modelconf/src/ui/ModelConfView.test.ts +75 -75
- package/agent/extensions/modelconf/src/ui/ModelConfView.ts +1101 -1101
- package/agent/extensions/modelconf/tsconfig.json +15 -15
- package/agent/extensions/pi-web-access/CHANGELOG.md +690 -690
- package/agent/extensions/pi-web-access/LICENSE +21 -21
- package/agent/extensions/pi-web-access/README.md +470 -470
- package/agent/extensions/pi-web-access/SECURITY.md +5 -5
- package/agent/extensions/pi-web-access/activity.ts +101 -101
- package/agent/extensions/pi-web-access/auth-fetch.ts +148 -148
- package/agent/extensions/pi-web-access/brightdata-unlocker.ts +272 -272
- package/agent/extensions/pi-web-access/chrome-cookies.ts +669 -669
- package/agent/extensions/pi-web-access/content-find.ts +139 -139
- package/agent/extensions/pi-web-access/credential-source.ts +191 -191
- package/agent/extensions/pi-web-access/data-uri-sanitize.ts +406 -406
- package/agent/extensions/pi-web-access/datalab-pdf-extract.ts +568 -568
- package/agent/extensions/pi-web-access/declared-web-links.ts +173 -173
- package/agent/extensions/pi-web-access/evidence/CONTRACT-EVIDENCE.md +496 -496
- package/agent/extensions/pi-web-access/evidence/contract-probe.mjs +140 -140
- package/agent/extensions/pi-web-access/exa.ts +526 -526
- package/agent/extensions/pi-web-access/extract.ts +1196 -1196
- package/agent/extensions/pi-web-access/feature-config.ts +29 -29
- package/agent/extensions/pi-web-access/fetch-params.ts +111 -111
- package/agent/extensions/pi-web-access/gemini-adc.ts +298 -298
- package/agent/extensions/pi-web-access/gemini-api.ts +353 -353
- package/agent/extensions/pi-web-access/gemini-pdf-extract.ts +108 -108
- package/agent/extensions/pi-web-access/gemini-search.ts +21 -21
- package/agent/extensions/pi-web-access/gemini-url-context.ts +128 -128
- package/agent/extensions/pi-web-access/gemini-web-config.ts +101 -101
- package/agent/extensions/pi-web-access/gemini-web.ts +487 -487
- package/agent/extensions/pi-web-access/github-api.ts +197 -197
- package/agent/extensions/pi-web-access/github-extract.ts +746 -746
- package/agent/extensions/pi-web-access/github-issue-pr.ts +700 -700
- package/agent/extensions/pi-web-access/index.ts +1737 -1737
- package/agent/extensions/pi-web-access/package-lock.json +5808 -5808
- package/agent/extensions/pi-web-access/package.json +64 -64
- package/agent/extensions/pi-web-access/page-query.ts +96 -96
- package/agent/extensions/pi-web-access/pdf-extract.ts +409 -409
- package/agent/extensions/pi-web-access/promise-try.d.ts +7 -7
- package/agent/extensions/pi-web-access/query-rewrite.ts +51 -51
- package/agent/extensions/pi-web-access/render-search-error.ts +170 -170
- package/agent/extensions/pi-web-access/rsc-extract.ts +338 -338
- package/agent/extensions/pi-web-access/search-types.ts +20 -20
- package/agent/extensions/pi-web-access/source-check.ts +282 -282
- package/agent/extensions/pi-web-access/ssrf-protection.ts +526 -526
- package/agent/extensions/pi-web-access/storage.ts +521 -521
- package/agent/extensions/pi-web-access/summary-model-scope.ts +125 -125
- package/agent/extensions/pi-web-access/test/auth-fetch.test.mjs +208 -208
- package/agent/extensions/pi-web-access/test/brightdata-unlocker.test.mjs +840 -840
- package/agent/extensions/pi-web-access/test/chrome-cookie-extraction.test.mjs +441 -441
- package/agent/extensions/pi-web-access/test/config-path.test.mjs +283 -283
- package/agent/extensions/pi-web-access/test/content-find.test.mjs +25 -25
- package/agent/extensions/pi-web-access/test/credential-source.test.mjs +118 -118
- package/agent/extensions/pi-web-access/test/data-uri-sanitize.test.mjs +210 -210
- package/agent/extensions/pi-web-access/test/datalab-pdf-extract.test.mjs +552 -552
- package/agent/extensions/pi-web-access/test/declared-web-links.test.mjs +212 -212
- package/agent/extensions/pi-web-access/test/fetch-answer-storage.test.mjs +40 -40
- package/agent/extensions/pi-web-access/test/fetch-cache-storage.test.mjs +334 -334
- package/agent/extensions/pi-web-access/test/fetch-content-domain-policy.test.mjs +95 -95
- package/agent/extensions/pi-web-access/test/fetch-modes.test.mjs +53 -53
- package/agent/extensions/pi-web-access/test/fetch-not-found-guidance.test.mjs +92 -92
- package/agent/extensions/pi-web-access/test/fetch-params.test.mjs +86 -86
- package/agent/extensions/pi-web-access/test/fetch-render-call.test.mjs +34 -34
- package/agent/extensions/pi-web-access/test/fetch-routing.test.mjs +173 -173
- package/agent/extensions/pi-web-access/test/gemini-adc-auth.test.mjs +257 -257
- package/agent/extensions/pi-web-access/test/gemini-api-transport.test.mjs +170 -170
- package/agent/extensions/pi-web-access/test/gemini-pdf-extract.test.mjs +133 -133
- package/agent/extensions/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +178 -178
- package/agent/extensions/pi-web-access/test/gemini-web-header-overflow.test.mjs +148 -148
- package/agent/extensions/pi-web-access/test/get-search-content.test.mjs +223 -223
- package/agent/extensions/pi-web-access/test/github-extract.test.mjs +378 -378
- package/agent/extensions/pi-web-access/test/github-issue-pr.test.mjs +565 -565
- package/agent/extensions/pi-web-access/test/inline-content-config.test.mjs +99 -99
- package/agent/extensions/pi-web-access/test/lazy-extract-load.test.mjs +118 -118
- package/agent/extensions/pi-web-access/test/local-video-oversize.test.mjs +52 -52
- package/agent/extensions/pi-web-access/test/package-typebox-dependency.test.mjs +50 -50
- package/agent/extensions/pi-web-access/test/page-query.test.mjs +51 -51
- package/agent/extensions/pi-web-access/test/pdf-config.test.mjs +140 -140
- package/agent/extensions/pi-web-access/test/pdf-extract.test.mjs +500 -500
- package/agent/extensions/pi-web-access/test/proxy-transport.test.mjs +286 -286
- package/agent/extensions/pi-web-access/test/query-rewrite.test.mjs +52 -52
- package/agent/extensions/pi-web-access/test/rsc-fallback.test.mjs +102 -102
- package/agent/extensions/pi-web-access/test/search-error-render.test.mjs +152 -152
- package/agent/extensions/pi-web-access/test/search-providers.test.mjs +274 -274
- package/agent/extensions/pi-web-access/test/source-check.test.mjs +179 -179
- package/agent/extensions/pi-web-access/test/ssrf-allow-ranges-config.test.mjs +205 -205
- package/agent/extensions/pi-web-access/test/ssrf-protection.test.mjs +456 -456
- package/agent/extensions/pi-web-access/test/summary-model-scope.test.mjs +106 -106
- package/agent/extensions/pi-web-access/test/tool-registration-config.test.mjs +182 -182
- package/agent/extensions/pi-web-access/test/web-search-answer-render.test.mjs +66 -66
- package/agent/extensions/pi-web-access/test/youtube-extract-errors.test.mjs +64 -64
- package/agent/extensions/pi-web-access/tsconfig.json +11 -11
- package/agent/extensions/pi-web-access/utils.ts +451 -451
- package/agent/extensions/pi-web-access/video-extract.ts +392 -392
- package/agent/extensions/pi-web-access/youtube-extract.ts +328 -328
- package/agent/extensions/shared/activity-status.ts +31 -31
- package/agent/extensions/shared/child-session.test.ts +270 -270
- package/agent/extensions/shared/child-session.ts +148 -148
- package/agent/extensions/shared/context-utilization.test.ts +48 -48
- package/agent/extensions/shared/context-utilization.ts +47 -47
- package/agent/extensions/shared/dashboard-state.ts +99 -99
- package/agent/extensions/shared/install.ps1 +27 -27
- package/agent/extensions/shared/install.sh +24 -24
- package/agent/extensions/shared/tool-call-timeout.test.ts +117 -117
- package/agent/extensions/shared/tool-call-timeout.ts +104 -104
- package/agent/extensions/skillsconf/README.md +74 -74
- package/agent/extensions/skillsconf/index.ts +92 -92
- package/agent/extensions/skillsconf/install.ps1 +27 -27
- package/agent/extensions/skillsconf/install.sh +24 -24
- package/agent/extensions/skillsconf/package-lock.json +1809 -1809
- package/agent/extensions/skillsconf/package.json +18 -18
- package/agent/extensions/skillsconf/src/delete-skill.test.ts +64 -64
- package/agent/extensions/skillsconf/src/delete-skill.ts +45 -45
- package/agent/extensions/skillsconf/src/filter.test.ts +80 -80
- package/agent/extensions/skillsconf/src/filter.ts +74 -74
- package/agent/extensions/skillsconf/src/fuzzy.ts +26 -26
- package/agent/extensions/skillsconf/src/persistence.test.ts +72 -72
- package/agent/extensions/skillsconf/src/persistence.ts +169 -169
- package/agent/extensions/skillsconf/src/ui/SkillConfView.test.ts +337 -337
- package/agent/extensions/skillsconf/src/ui/SkillConfView.ts +758 -758
- package/agent/extensions/skillsconf/src/ui/text-input.ts +91 -91
- package/agent/extensions/skillsconf/src/ui/tui-helpers.ts +144 -144
- package/agent/extensions/skillsconf/tsconfig.json +15 -15
- package/agent/extensions/statusline/index.ts +273 -282
- package/agent/extensions/statusline/install.ps1 +27 -27
- package/agent/extensions/statusline/install.sh +24 -24
- package/agent/extensions/subagents/by-the-way.test.ts +29 -29
- package/agent/extensions/subagents/claude.test.ts +119 -119
- package/agent/extensions/subagents/codex.test.ts +102 -102
- package/agent/extensions/subagents/context-usage.test.ts +107 -107
- package/agent/extensions/subagents/docs/design-plan.md +568 -568
- package/agent/extensions/subagents/docs/effect-v4-extension-guide.md +354 -354
- package/agent/extensions/subagents/docs/effect-v4-notes.md +571 -571
- package/agent/extensions/subagents/index.ts +779 -779
- package/agent/extensions/subagents/install.ps1 +27 -27
- package/agent/extensions/subagents/install.sh +24 -24
- package/agent/extensions/subagents/manager.test.ts +276 -276
- package/agent/extensions/subagents/package-lock.json +2244 -2244
- package/agent/extensions/subagents/package.json +19 -19
- package/agent/extensions/subagents/result-delivery.test.ts +27 -27
- package/agent/extensions/subagents/src/backend.ts +73 -73
- package/agent/extensions/subagents/src/backends/claude.ts +701 -701
- package/agent/extensions/subagents/src/backends/codex.ts +1060 -1060
- package/agent/extensions/subagents/src/backends/pi.ts +575 -575
- package/agent/extensions/subagents/src/backends/stub.ts +300 -300
- package/agent/extensions/subagents/src/by-the-way.ts +21 -21
- package/agent/extensions/subagents/src/domain.ts +253 -253
- package/agent/extensions/subagents/src/format.ts +74 -74
- package/agent/extensions/subagents/src/manager.ts +736 -736
- package/agent/extensions/subagents/src/prompt.ts +92 -92
- package/agent/extensions/subagents/src/result-delivery.ts +20 -20
- package/agent/extensions/subagents/src/runtime.ts +53 -53
- package/agent/extensions/subagents/src/ui/takeover.ts +583 -583
- package/agent/extensions/subagents/src/ui/transcript.ts +201 -201
- package/agent/extensions/subagents/takeover.test.ts +29 -29
- package/agent/extensions/subagents/tsconfig.json +7 -7
- package/agent/extensions/todo/AGENTS.md +38 -38
- package/agent/extensions/todo/LICENSE +21 -21
- package/agent/extensions/todo/config.ts +55 -55
- package/agent/extensions/todo/index.ts +151 -151
- package/agent/extensions/todo/install.ps1 +27 -27
- package/agent/extensions/todo/install.sh +24 -24
- package/agent/extensions/todo/locales/de.json +17 -17
- package/agent/extensions/todo/locales/en.json +15 -15
- package/agent/extensions/todo/locales/es.json +17 -17
- package/agent/extensions/todo/locales/fr.json +17 -17
- package/agent/extensions/todo/locales/pt-BR.json +17 -17
- package/agent/extensions/todo/locales/pt.json +17 -17
- package/agent/extensions/todo/locales/ru.json +17 -17
- package/agent/extensions/todo/locales/uk.json +17 -17
- package/agent/extensions/todo/locales/zh.json +17 -17
- package/agent/extensions/todo/package-lock.json +3358 -3358
- package/agent/extensions/todo/package.json +67 -67
- package/agent/extensions/todo/state/i18n-bridge.ts +64 -64
- package/agent/extensions/todo/state/invariants.ts +20 -20
- package/agent/extensions/todo/state/replay.ts +38 -38
- package/agent/extensions/todo/state/selectors.ts +107 -107
- package/agent/extensions/todo/state/state-reducer.ts +326 -326
- package/agent/extensions/todo/state/state.ts +18 -18
- package/agent/extensions/todo/state/store.ts +82 -82
- package/agent/extensions/todo/state/task-graph.ts +57 -57
- package/agent/extensions/todo/todo-overlay.ts +200 -200
- package/agent/extensions/todo/todo.ts +155 -155
- package/agent/extensions/todo/tool/response-envelope.ts +109 -109
- package/agent/extensions/todo/tool/types.ts +206 -206
- package/agent/extensions/todo/view/format.ts +177 -177
- package/agent/extensions/trim-context/README.md +54 -54
- package/agent/extensions/trim-context/index.ts +487 -487
- package/agent/extensions/trim-context/install.ps1 +27 -27
- package/agent/extensions/trim-context/install.sh +24 -24
- package/agent/keybindings.json +7 -7
- package/agent/version.txt +1 -1
- package/bin/kiln.js +20 -13
- package/package.json +1 -1
- package/agent/extensions/taste/index.ts +0 -443
- package/agent/extensions/taste/install.ps1 +0 -27
- package/agent/extensions/taste/install.sh +0 -24
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
import { test } from "node:test";
|
|
7
|
-
|
|
8
|
-
const indexUrl = new URL("../index.ts", import.meta.url).href;
|
|
9
|
-
const storageUrl = new URL("../storage.ts", import.meta.url).href;
|
|
10
|
-
|
|
11
|
-
async function runScenario(maxInlineContentChars) {
|
|
12
|
-
const agentDir = await mkdtemp(join(tmpdir(), "pi-web-access-inline-content-"));
|
|
13
|
-
if (maxInlineContentChars !== undefined) {
|
|
14
|
-
await writeFile(join(agentDir, "web-search.json"), JSON.stringify({ maxInlineContentChars }) + "\n", "utf8");
|
|
15
|
-
}
|
|
16
|
-
const child = spawnSync(process.execPath, ["--input-type=module"], {
|
|
17
|
-
input: `
|
|
18
|
-
import initializeExtension from ${JSON.stringify(indexUrl)};
|
|
19
|
-
import { clearResults } from ${JSON.stringify(storageUrl)};
|
|
20
|
-
clearResults();
|
|
21
|
-
globalThis.fetch = async () => new Response("A".repeat(40000) + "TAIL", {
|
|
22
|
-
status: 200,
|
|
23
|
-
headers: { "content-type": "text/plain" },
|
|
24
|
-
});
|
|
25
|
-
const tools = [];
|
|
26
|
-
initializeExtension({ registerTool(tool) { tools.push(tool); }, registerCommand() {}, registerShortcut() {}, on() {}, appendEntry() {} });
|
|
27
|
-
const fetchTool = tools.find(tool => tool.name === "fetch_content");
|
|
28
|
-
const contentTool = tools.find(tool => tool.name === "get_search_content");
|
|
29
|
-
const fetched = await fetchTool.execute("call", { url: "https://93.184.216.34/page" });
|
|
30
|
-
const retrieved = await contentTool.execute("call", { responseId: fetched.details.responseId, urlIndex: 0 });
|
|
31
|
-
const tail = await contentTool.execute("call", { responseId: fetched.details.responseId, urlIndex: 0, offset: 40000, limit: 4 });
|
|
32
|
-
const rejected = await contentTool.execute("call", { responseId: fetched.details.responseId, urlIndex: 0, limit: ${maxInlineContentChars === undefined ? 30_001 : maxInlineContentChars + 1} });
|
|
33
|
-
console.log(JSON.stringify({
|
|
34
|
-
schemaMax: contentTool.parameters.properties.limit.maximum,
|
|
35
|
-
fetchTruncated: fetched.details.truncated,
|
|
36
|
-
fetchEndOffset: fetched.content.find(item => item.type === "text").text.indexOf("\\n\\n---"),
|
|
37
|
-
retrievedChars: retrieved.details.returnedChars,
|
|
38
|
-
retrievedNextOffset: retrieved.details.nextOffset,
|
|
39
|
-
tail: tail.content[0].text.includes("TAIL"),
|
|
40
|
-
rejectedMax: rejected.details.maxLimit,
|
|
41
|
-
}));
|
|
42
|
-
`,
|
|
43
|
-
encoding: "utf8",
|
|
44
|
-
env: { ...process.env, PI_CODING_AGENT_DIR: agentDir, XDG_CONFIG_HOME: undefined },
|
|
45
|
-
});
|
|
46
|
-
assert.equal(child.status, 0, child.stderr);
|
|
47
|
-
return JSON.parse(child.stdout);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
test("inline content defaults to 30,000 characters", async () => {
|
|
51
|
-
const result = await runScenario();
|
|
52
|
-
assert.deepEqual(result, {
|
|
53
|
-
schemaMax: 30_000,
|
|
54
|
-
fetchTruncated: true,
|
|
55
|
-
fetchEndOffset: 30_000,
|
|
56
|
-
retrievedChars: 30_000,
|
|
57
|
-
retrievedNextOffset: 30_000,
|
|
58
|
-
tail: true,
|
|
59
|
-
rejectedMax: 30_000,
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("maxInlineContentChars applies to direct and stored content slices", async () => {
|
|
64
|
-
const result = await runScenario(40_000);
|
|
65
|
-
assert.deepEqual(result, {
|
|
66
|
-
schemaMax: 40_000,
|
|
67
|
-
fetchTruncated: true,
|
|
68
|
-
fetchEndOffset: 40_000,
|
|
69
|
-
retrievedChars: 40_000,
|
|
70
|
-
retrievedNextOffset: 40_000,
|
|
71
|
-
tail: true,
|
|
72
|
-
rejectedMax: 40_000,
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("stored content schema and execution keep one registered limit", async () => {
|
|
77
|
-
const agentDir = await mkdtemp(join(tmpdir(), "pi-web-access-inline-content-"));
|
|
78
|
-
await writeFile(join(agentDir, "web-search.json"), JSON.stringify({ maxInlineContentChars: 40_000 }) + "\n", "utf8");
|
|
79
|
-
const child = spawnSync(process.execPath, ["--input-type=module"], {
|
|
80
|
-
input: `
|
|
81
|
-
import initializeExtension from ${JSON.stringify(indexUrl)};
|
|
82
|
-
import { storeResult, clearResults } from ${JSON.stringify(storageUrl)};
|
|
83
|
-
import { writeFile } from "node:fs/promises";
|
|
84
|
-
import { join } from "node:path";
|
|
85
|
-
clearResults();
|
|
86
|
-
const tools = [];
|
|
87
|
-
initializeExtension({ registerTool(tool) { tools.push(tool); }, registerCommand() {}, registerShortcut() {}, on() {}, appendEntry() {} });
|
|
88
|
-
await writeFile(join(process.env.PI_CODING_AGENT_DIR, "web-search.json"), JSON.stringify({ maxInlineContentChars: 20_000 }) + "\\n", "utf8");
|
|
89
|
-
storeResult("stored", { id: "stored", type: "fetch", timestamp: Date.now(), urls: [{ url: "https://example.test", title: "Stored", content: "A".repeat(50_000), error: null }] });
|
|
90
|
-
const contentTool = tools.find(tool => tool.name === "get_search_content");
|
|
91
|
-
const rejected = await contentTool.execute("call", { responseId: "stored", urlIndex: 0, limit: 40_001 });
|
|
92
|
-
console.log(JSON.stringify({ schemaMax: contentTool.parameters.properties.limit.maximum, rejectedMax: rejected.details.maxLimit }));
|
|
93
|
-
`,
|
|
94
|
-
encoding: "utf8",
|
|
95
|
-
env: { ...process.env, PI_CODING_AGENT_DIR: agentDir, XDG_CONFIG_HOME: undefined },
|
|
96
|
-
});
|
|
97
|
-
assert.equal(child.status, 0, child.stderr);
|
|
98
|
-
assert.deepEqual(JSON.parse(child.stdout), { schemaMax: 40_000, rejectedMax: 40_000 });
|
|
99
|
-
});
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { test } from "node:test";
|
|
7
|
+
|
|
8
|
+
const indexUrl = new URL("../index.ts", import.meta.url).href;
|
|
9
|
+
const storageUrl = new URL("../storage.ts", import.meta.url).href;
|
|
10
|
+
|
|
11
|
+
async function runScenario(maxInlineContentChars) {
|
|
12
|
+
const agentDir = await mkdtemp(join(tmpdir(), "pi-web-access-inline-content-"));
|
|
13
|
+
if (maxInlineContentChars !== undefined) {
|
|
14
|
+
await writeFile(join(agentDir, "web-search.json"), JSON.stringify({ maxInlineContentChars }) + "\n", "utf8");
|
|
15
|
+
}
|
|
16
|
+
const child = spawnSync(process.execPath, ["--input-type=module"], {
|
|
17
|
+
input: `
|
|
18
|
+
import initializeExtension from ${JSON.stringify(indexUrl)};
|
|
19
|
+
import { clearResults } from ${JSON.stringify(storageUrl)};
|
|
20
|
+
clearResults();
|
|
21
|
+
globalThis.fetch = async () => new Response("A".repeat(40000) + "TAIL", {
|
|
22
|
+
status: 200,
|
|
23
|
+
headers: { "content-type": "text/plain" },
|
|
24
|
+
});
|
|
25
|
+
const tools = [];
|
|
26
|
+
initializeExtension({ registerTool(tool) { tools.push(tool); }, registerCommand() {}, registerShortcut() {}, on() {}, appendEntry() {} });
|
|
27
|
+
const fetchTool = tools.find(tool => tool.name === "fetch_content");
|
|
28
|
+
const contentTool = tools.find(tool => tool.name === "get_search_content");
|
|
29
|
+
const fetched = await fetchTool.execute("call", { url: "https://93.184.216.34/page" });
|
|
30
|
+
const retrieved = await contentTool.execute("call", { responseId: fetched.details.responseId, urlIndex: 0 });
|
|
31
|
+
const tail = await contentTool.execute("call", { responseId: fetched.details.responseId, urlIndex: 0, offset: 40000, limit: 4 });
|
|
32
|
+
const rejected = await contentTool.execute("call", { responseId: fetched.details.responseId, urlIndex: 0, limit: ${maxInlineContentChars === undefined ? 30_001 : maxInlineContentChars + 1} });
|
|
33
|
+
console.log(JSON.stringify({
|
|
34
|
+
schemaMax: contentTool.parameters.properties.limit.maximum,
|
|
35
|
+
fetchTruncated: fetched.details.truncated,
|
|
36
|
+
fetchEndOffset: fetched.content.find(item => item.type === "text").text.indexOf("\\n\\n---"),
|
|
37
|
+
retrievedChars: retrieved.details.returnedChars,
|
|
38
|
+
retrievedNextOffset: retrieved.details.nextOffset,
|
|
39
|
+
tail: tail.content[0].text.includes("TAIL"),
|
|
40
|
+
rejectedMax: rejected.details.maxLimit,
|
|
41
|
+
}));
|
|
42
|
+
`,
|
|
43
|
+
encoding: "utf8",
|
|
44
|
+
env: { ...process.env, PI_CODING_AGENT_DIR: agentDir, XDG_CONFIG_HOME: undefined },
|
|
45
|
+
});
|
|
46
|
+
assert.equal(child.status, 0, child.stderr);
|
|
47
|
+
return JSON.parse(child.stdout);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
test("inline content defaults to 30,000 characters", async () => {
|
|
51
|
+
const result = await runScenario();
|
|
52
|
+
assert.deepEqual(result, {
|
|
53
|
+
schemaMax: 30_000,
|
|
54
|
+
fetchTruncated: true,
|
|
55
|
+
fetchEndOffset: 30_000,
|
|
56
|
+
retrievedChars: 30_000,
|
|
57
|
+
retrievedNextOffset: 30_000,
|
|
58
|
+
tail: true,
|
|
59
|
+
rejectedMax: 30_000,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("maxInlineContentChars applies to direct and stored content slices", async () => {
|
|
64
|
+
const result = await runScenario(40_000);
|
|
65
|
+
assert.deepEqual(result, {
|
|
66
|
+
schemaMax: 40_000,
|
|
67
|
+
fetchTruncated: true,
|
|
68
|
+
fetchEndOffset: 40_000,
|
|
69
|
+
retrievedChars: 40_000,
|
|
70
|
+
retrievedNextOffset: 40_000,
|
|
71
|
+
tail: true,
|
|
72
|
+
rejectedMax: 40_000,
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("stored content schema and execution keep one registered limit", async () => {
|
|
77
|
+
const agentDir = await mkdtemp(join(tmpdir(), "pi-web-access-inline-content-"));
|
|
78
|
+
await writeFile(join(agentDir, "web-search.json"), JSON.stringify({ maxInlineContentChars: 40_000 }) + "\n", "utf8");
|
|
79
|
+
const child = spawnSync(process.execPath, ["--input-type=module"], {
|
|
80
|
+
input: `
|
|
81
|
+
import initializeExtension from ${JSON.stringify(indexUrl)};
|
|
82
|
+
import { storeResult, clearResults } from ${JSON.stringify(storageUrl)};
|
|
83
|
+
import { writeFile } from "node:fs/promises";
|
|
84
|
+
import { join } from "node:path";
|
|
85
|
+
clearResults();
|
|
86
|
+
const tools = [];
|
|
87
|
+
initializeExtension({ registerTool(tool) { tools.push(tool); }, registerCommand() {}, registerShortcut() {}, on() {}, appendEntry() {} });
|
|
88
|
+
await writeFile(join(process.env.PI_CODING_AGENT_DIR, "web-search.json"), JSON.stringify({ maxInlineContentChars: 20_000 }) + "\\n", "utf8");
|
|
89
|
+
storeResult("stored", { id: "stored", type: "fetch", timestamp: Date.now(), urls: [{ url: "https://example.test", title: "Stored", content: "A".repeat(50_000), error: null }] });
|
|
90
|
+
const contentTool = tools.find(tool => tool.name === "get_search_content");
|
|
91
|
+
const rejected = await contentTool.execute("call", { responseId: "stored", urlIndex: 0, limit: 40_001 });
|
|
92
|
+
console.log(JSON.stringify({ schemaMax: contentTool.parameters.properties.limit.maximum, rejectedMax: rejected.details.maxLimit }));
|
|
93
|
+
`,
|
|
94
|
+
encoding: "utf8",
|
|
95
|
+
env: { ...process.env, PI_CODING_AGENT_DIR: agentDir, XDG_CONFIG_HOME: undefined },
|
|
96
|
+
});
|
|
97
|
+
assert.equal(child.status, 0, child.stderr);
|
|
98
|
+
assert.deepEqual(JSON.parse(child.stdout), { schemaMax: 40_000, rejectedMax: 40_000 });
|
|
99
|
+
});
|
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { test } from "node:test";
|
|
4
|
-
|
|
5
|
-
const indexUrl = new URL("../index.ts", import.meta.url).href;
|
|
6
|
-
|
|
7
|
-
test("registers eagerly and loads content extraction on first use", () => {
|
|
8
|
-
const child = spawnSync(process.execPath, ["--input-type=module"], {
|
|
9
|
-
input: buildChildScript(indexUrl),
|
|
10
|
-
encoding: "utf8",
|
|
11
|
-
maxBuffer: 2 * 1024 * 1024,
|
|
12
|
-
timeout: 30_000,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
assert.equal(
|
|
16
|
-
child.status,
|
|
17
|
-
0,
|
|
18
|
-
"Lazy extraction regression child failed:\n" + errorSummary(child.stderr),
|
|
19
|
-
);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
function buildChildScript(moduleUrl) {
|
|
23
|
-
return `
|
|
24
|
-
import assert from "node:assert/strict";
|
|
25
|
-
import { existsSync } from "node:fs";
|
|
26
|
-
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
27
|
-
import { createServer } from "node:http";
|
|
28
|
-
import { register } from "node:module";
|
|
29
|
-
import { tmpdir } from "node:os";
|
|
30
|
-
import { join } from "node:path";
|
|
31
|
-
|
|
32
|
-
const tempDir = await mkdtemp(join(tmpdir(), "pi-web-access-lazy-"));
|
|
33
|
-
const markerPath = join(tempDir, "extract-loaded");
|
|
34
|
-
process.env.PI_CODING_AGENT_DIR = tempDir;
|
|
35
|
-
await writeFile(
|
|
36
|
-
join(tempDir, "web-search.json"),
|
|
37
|
-
JSON.stringify({ ssrf: { allowRanges: ["127.0.0.0/8"] } }),
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const hookSource = \`
|
|
41
|
-
import { appendFileSync } from "node:fs";
|
|
42
|
-
let markerPath;
|
|
43
|
-
export function initialize(data) { markerPath = data.markerPath; }
|
|
44
|
-
export async function load(url, context, nextLoad) {
|
|
45
|
-
if (new URL(url).pathname.endsWith("/extract.ts")) {
|
|
46
|
-
appendFileSync(markerPath, url + "\\\\n");
|
|
47
|
-
}
|
|
48
|
-
return nextLoad(url, context);
|
|
49
|
-
}
|
|
50
|
-
\`;
|
|
51
|
-
register("data:text/javascript," + encodeURIComponent(hookSource), {
|
|
52
|
-
parentURL: import.meta.url,
|
|
53
|
-
data: { markerPath },
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const { default: initializeExtension } = await import(${JSON.stringify(moduleUrl)});
|
|
57
|
-
assert.equal(existsSync(markerPath), false, "extract.ts loaded during extension import");
|
|
58
|
-
|
|
59
|
-
const tools = [];
|
|
60
|
-
const commands = [];
|
|
61
|
-
const shortcuts = [];
|
|
62
|
-
const events = [];
|
|
63
|
-
const pi = new Proxy({}, {
|
|
64
|
-
get(_target, property) {
|
|
65
|
-
if (property === "registerTool") return (tool) => tools.push(tool);
|
|
66
|
-
if (property === "registerCommand") return (name) => commands.push(name);
|
|
67
|
-
if (property === "registerShortcut") return (name) => shortcuts.push(name);
|
|
68
|
-
if (property === "on") return (name) => events.push(name);
|
|
69
|
-
return () => {};
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
initializeExtension(pi);
|
|
74
|
-
assert.deepEqual(
|
|
75
|
-
tools.map((tool) => tool.name),
|
|
76
|
-
["web_search", "source_check", "fetch_content", "get_search_content"],
|
|
77
|
-
);
|
|
78
|
-
assert.ok(commands.includes("google-account"), "google-account command was not registered");
|
|
79
|
-
assert.ok(shortcuts.length > 0, "shortcuts were not registered");
|
|
80
|
-
assert.ok(events.includes("session_start"), "session handlers were not registered");
|
|
81
|
-
assert.equal(existsSync(markerPath), false, "extract.ts loaded during registration");
|
|
82
|
-
|
|
83
|
-
const articleText = "Lazy loading keeps extension startup responsive while preserving first-use extraction. ";
|
|
84
|
-
const html = \`<!doctype html><html><head><title>Lazy extraction</title></head><body>
|
|
85
|
-
<article><h1>Lazy extraction works</h1><p>\${articleText.repeat(20)}</p></article>
|
|
86
|
-
</body></html>\`;
|
|
87
|
-
const server = createServer((_request, response) => {
|
|
88
|
-
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
89
|
-
response.end(html);
|
|
90
|
-
});
|
|
91
|
-
await new Promise((resolve, reject) => {
|
|
92
|
-
server.once("error", reject);
|
|
93
|
-
server.listen(0, "127.0.0.1", resolve);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const address = server.address();
|
|
98
|
-
assert.ok(address && typeof address === "object");
|
|
99
|
-
const fetchTool = tools.find((tool) => tool.name === "fetch_content");
|
|
100
|
-
const result = await fetchTool.execute(
|
|
101
|
-
"lazy-load-regression",
|
|
102
|
-
{ url: \`http://127.0.0.1:\${address.port}/article\` },
|
|
103
|
-
undefined,
|
|
104
|
-
() => {},
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
assert.equal(existsSync(markerPath), true, "extract.ts did not load on first use");
|
|
108
|
-
assert.equal(result.details.successful, 1);
|
|
109
|
-
assert.match(result.content.at(-1).text, /preserving first-use extraction/);
|
|
110
|
-
} finally {
|
|
111
|
-
await new Promise((resolve) => server.close(resolve));
|
|
112
|
-
}
|
|
113
|
-
`;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function errorSummary(value, size = 2000) {
|
|
117
|
-
return value.length > size ? value.slice(-size) : value;
|
|
118
|
-
}
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { test } from "node:test";
|
|
4
|
+
|
|
5
|
+
const indexUrl = new URL("../index.ts", import.meta.url).href;
|
|
6
|
+
|
|
7
|
+
test("registers eagerly and loads content extraction on first use", () => {
|
|
8
|
+
const child = spawnSync(process.execPath, ["--input-type=module"], {
|
|
9
|
+
input: buildChildScript(indexUrl),
|
|
10
|
+
encoding: "utf8",
|
|
11
|
+
maxBuffer: 2 * 1024 * 1024,
|
|
12
|
+
timeout: 30_000,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
assert.equal(
|
|
16
|
+
child.status,
|
|
17
|
+
0,
|
|
18
|
+
"Lazy extraction regression child failed:\n" + errorSummary(child.stderr),
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
function buildChildScript(moduleUrl) {
|
|
23
|
+
return `
|
|
24
|
+
import assert from "node:assert/strict";
|
|
25
|
+
import { existsSync } from "node:fs";
|
|
26
|
+
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
27
|
+
import { createServer } from "node:http";
|
|
28
|
+
import { register } from "node:module";
|
|
29
|
+
import { tmpdir } from "node:os";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
|
|
32
|
+
const tempDir = await mkdtemp(join(tmpdir(), "pi-web-access-lazy-"));
|
|
33
|
+
const markerPath = join(tempDir, "extract-loaded");
|
|
34
|
+
process.env.PI_CODING_AGENT_DIR = tempDir;
|
|
35
|
+
await writeFile(
|
|
36
|
+
join(tempDir, "web-search.json"),
|
|
37
|
+
JSON.stringify({ ssrf: { allowRanges: ["127.0.0.0/8"] } }),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const hookSource = \`
|
|
41
|
+
import { appendFileSync } from "node:fs";
|
|
42
|
+
let markerPath;
|
|
43
|
+
export function initialize(data) { markerPath = data.markerPath; }
|
|
44
|
+
export async function load(url, context, nextLoad) {
|
|
45
|
+
if (new URL(url).pathname.endsWith("/extract.ts")) {
|
|
46
|
+
appendFileSync(markerPath, url + "\\\\n");
|
|
47
|
+
}
|
|
48
|
+
return nextLoad(url, context);
|
|
49
|
+
}
|
|
50
|
+
\`;
|
|
51
|
+
register("data:text/javascript," + encodeURIComponent(hookSource), {
|
|
52
|
+
parentURL: import.meta.url,
|
|
53
|
+
data: { markerPath },
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const { default: initializeExtension } = await import(${JSON.stringify(moduleUrl)});
|
|
57
|
+
assert.equal(existsSync(markerPath), false, "extract.ts loaded during extension import");
|
|
58
|
+
|
|
59
|
+
const tools = [];
|
|
60
|
+
const commands = [];
|
|
61
|
+
const shortcuts = [];
|
|
62
|
+
const events = [];
|
|
63
|
+
const pi = new Proxy({}, {
|
|
64
|
+
get(_target, property) {
|
|
65
|
+
if (property === "registerTool") return (tool) => tools.push(tool);
|
|
66
|
+
if (property === "registerCommand") return (name) => commands.push(name);
|
|
67
|
+
if (property === "registerShortcut") return (name) => shortcuts.push(name);
|
|
68
|
+
if (property === "on") return (name) => events.push(name);
|
|
69
|
+
return () => {};
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
initializeExtension(pi);
|
|
74
|
+
assert.deepEqual(
|
|
75
|
+
tools.map((tool) => tool.name),
|
|
76
|
+
["web_search", "source_check", "fetch_content", "get_search_content"],
|
|
77
|
+
);
|
|
78
|
+
assert.ok(commands.includes("google-account"), "google-account command was not registered");
|
|
79
|
+
assert.ok(shortcuts.length > 0, "shortcuts were not registered");
|
|
80
|
+
assert.ok(events.includes("session_start"), "session handlers were not registered");
|
|
81
|
+
assert.equal(existsSync(markerPath), false, "extract.ts loaded during registration");
|
|
82
|
+
|
|
83
|
+
const articleText = "Lazy loading keeps extension startup responsive while preserving first-use extraction. ";
|
|
84
|
+
const html = \`<!doctype html><html><head><title>Lazy extraction</title></head><body>
|
|
85
|
+
<article><h1>Lazy extraction works</h1><p>\${articleText.repeat(20)}</p></article>
|
|
86
|
+
</body></html>\`;
|
|
87
|
+
const server = createServer((_request, response) => {
|
|
88
|
+
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
89
|
+
response.end(html);
|
|
90
|
+
});
|
|
91
|
+
await new Promise((resolve, reject) => {
|
|
92
|
+
server.once("error", reject);
|
|
93
|
+
server.listen(0, "127.0.0.1", resolve);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const address = server.address();
|
|
98
|
+
assert.ok(address && typeof address === "object");
|
|
99
|
+
const fetchTool = tools.find((tool) => tool.name === "fetch_content");
|
|
100
|
+
const result = await fetchTool.execute(
|
|
101
|
+
"lazy-load-regression",
|
|
102
|
+
{ url: \`http://127.0.0.1:\${address.port}/article\` },
|
|
103
|
+
undefined,
|
|
104
|
+
() => {},
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
assert.equal(existsSync(markerPath), true, "extract.ts did not load on first use");
|
|
108
|
+
assert.equal(result.details.successful, 1);
|
|
109
|
+
assert.match(result.content.at(-1).text, /preserving first-use extraction/);
|
|
110
|
+
} finally {
|
|
111
|
+
await new Promise((resolve) => server.close(resolve));
|
|
112
|
+
}
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function errorSummary(value, size = 2000) {
|
|
117
|
+
return value.length > size ? value.slice(-size) : value;
|
|
118
|
+
}
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { mkdtemp, rm, truncate, writeFile } from "node:fs/promises";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
import { test } from "node:test";
|
|
7
|
-
|
|
8
|
-
const extractUrl = new URL("../extract.ts", import.meta.url).href;
|
|
9
|
-
|
|
10
|
-
async function runOversizeProbe(expression) {
|
|
11
|
-
const root = await mkdtemp(join(tmpdir(), "pi-web-access-oversize-video-"));
|
|
12
|
-
const videoPath = join(root, "oversized.mp4");
|
|
13
|
-
await writeFile(videoPath, "");
|
|
14
|
-
await truncate(videoPath, 50 * 1024 * 1024 + 1);
|
|
15
|
-
try {
|
|
16
|
-
const child = spawnSync(process.execPath, ["--input-type=module"], {
|
|
17
|
-
input: `
|
|
18
|
-
process.env.PI_CODING_AGENT_DIR = ${JSON.stringify(root)};
|
|
19
|
-
const { extractContent } = await import(${JSON.stringify(extractUrl)});
|
|
20
|
-
const result = await (${expression})(extractContent, ${JSON.stringify(videoPath)});
|
|
21
|
-
console.log(JSON.stringify({ title: result.title, content: result.content, error: result.error }));
|
|
22
|
-
`,
|
|
23
|
-
encoding: "utf8",
|
|
24
|
-
timeout: 30_000,
|
|
25
|
-
});
|
|
26
|
-
assert.equal(child.status, 0, child.stderr);
|
|
27
|
-
return JSON.parse(child.stdout);
|
|
28
|
-
} finally {
|
|
29
|
-
await rm(root, { recursive: true, force: true });
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
test("oversized local videos are still recognized for frame extraction", async () => {
|
|
34
|
-
const result = await runOversizeProbe(`(extractContent, videoPath) => extractContent(videoPath, undefined, { frames: 1 })`);
|
|
35
|
-
|
|
36
|
-
assert.notEqual(result.error, "Frame extraction only works with YouTube and local video files");
|
|
37
|
-
assert.match(result.error, /ffprobe|Cannot determine video duration|failed|not installed/i);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("oversized local videos are still recognized for timestamp extraction", async () => {
|
|
41
|
-
const result = await runOversizeProbe(`(extractContent, videoPath) => extractContent(videoPath, undefined, { timestamp: "1" })`);
|
|
42
|
-
|
|
43
|
-
assert.notEqual(result.error, "Timestamp extraction only works with YouTube and local video files");
|
|
44
|
-
assert.match(result.error, /ffmpeg|failed|not installed/i);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("oversized local video analysis reports the upload size limit", async () => {
|
|
48
|
-
const result = await runOversizeProbe(`(extractContent, videoPath) => extractContent(videoPath, undefined, { prompt: "summarize" })`);
|
|
49
|
-
|
|
50
|
-
assert.match(result.error, /above configured video\.maxSizeMB/);
|
|
51
|
-
assert.match(result.error, /timestamp\/frames/);
|
|
52
|
-
});
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { mkdtemp, rm, truncate, writeFile } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { test } from "node:test";
|
|
7
|
+
|
|
8
|
+
const extractUrl = new URL("../extract.ts", import.meta.url).href;
|
|
9
|
+
|
|
10
|
+
async function runOversizeProbe(expression) {
|
|
11
|
+
const root = await mkdtemp(join(tmpdir(), "pi-web-access-oversize-video-"));
|
|
12
|
+
const videoPath = join(root, "oversized.mp4");
|
|
13
|
+
await writeFile(videoPath, "");
|
|
14
|
+
await truncate(videoPath, 50 * 1024 * 1024 + 1);
|
|
15
|
+
try {
|
|
16
|
+
const child = spawnSync(process.execPath, ["--input-type=module"], {
|
|
17
|
+
input: `
|
|
18
|
+
process.env.PI_CODING_AGENT_DIR = ${JSON.stringify(root)};
|
|
19
|
+
const { extractContent } = await import(${JSON.stringify(extractUrl)});
|
|
20
|
+
const result = await (${expression})(extractContent, ${JSON.stringify(videoPath)});
|
|
21
|
+
console.log(JSON.stringify({ title: result.title, content: result.content, error: result.error }));
|
|
22
|
+
`,
|
|
23
|
+
encoding: "utf8",
|
|
24
|
+
timeout: 30_000,
|
|
25
|
+
});
|
|
26
|
+
assert.equal(child.status, 0, child.stderr);
|
|
27
|
+
return JSON.parse(child.stdout);
|
|
28
|
+
} finally {
|
|
29
|
+
await rm(root, { recursive: true, force: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
test("oversized local videos are still recognized for frame extraction", async () => {
|
|
34
|
+
const result = await runOversizeProbe(`(extractContent, videoPath) => extractContent(videoPath, undefined, { frames: 1 })`);
|
|
35
|
+
|
|
36
|
+
assert.notEqual(result.error, "Frame extraction only works with YouTube and local video files");
|
|
37
|
+
assert.match(result.error, /ffprobe|Cannot determine video duration|failed|not installed/i);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("oversized local videos are still recognized for timestamp extraction", async () => {
|
|
41
|
+
const result = await runOversizeProbe(`(extractContent, videoPath) => extractContent(videoPath, undefined, { timestamp: "1" })`);
|
|
42
|
+
|
|
43
|
+
assert.notEqual(result.error, "Timestamp extraction only works with YouTube and local video files");
|
|
44
|
+
assert.match(result.error, /ffmpeg|failed|not installed/i);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("oversized local video analysis reports the upload size limit", async () => {
|
|
48
|
+
const result = await runOversizeProbe(`(extractContent, videoPath) => extractContent(videoPath, undefined, { prompt: "summarize" })`);
|
|
49
|
+
|
|
50
|
+
assert.match(result.error, /above configured video\.maxSizeMB/);
|
|
51
|
+
assert.match(result.error, /timestamp\/frames/);
|
|
52
|
+
});
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { execFileSync } from "node:child_process";
|
|
3
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
-
import { createRequire } from "node:module";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
import { dirname, join } from "node:path";
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { test } from "node:test";
|
|
9
|
-
|
|
10
|
-
// npm ships as a shell script / .cmd wrapper, neither of which is spawnable on
|
|
11
|
-
// Windows (extensionless `npm` does not resolve, `.cmd` is refused without a
|
|
12
|
-
// shell). There, invoke its CLI entry through the current node binary instead.
|
|
13
|
-
const NPM_BIN = process.platform === "win32" ? process.execPath : "npm";
|
|
14
|
-
const NPM_PREFIX = process.platform === "win32"
|
|
15
|
-
? [join(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js")]
|
|
16
|
-
: [];
|
|
17
|
-
|
|
18
|
-
const repoRoot = fileURLToPath(new URL("..", import.meta.url));
|
|
19
|
-
|
|
20
|
-
test("packed installs include typebox without peer dependencies", async () => {
|
|
21
|
-
const tempDir = await mkdtemp(join(tmpdir(), "pi-web-access-pack-install-"));
|
|
22
|
-
try {
|
|
23
|
-
const packOutput = execFileSync(NPM_BIN, [...NPM_PREFIX, "pack", "--json", "--pack-destination", tempDir], {
|
|
24
|
-
cwd: repoRoot,
|
|
25
|
-
encoding: "utf8",
|
|
26
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
27
|
-
});
|
|
28
|
-
const [{ filename, files }] = JSON.parse(packOutput);
|
|
29
|
-
const packedFiles = files.map((file) => file.path);
|
|
30
|
-
assert.ok(packedFiles.includes("index.ts"));
|
|
31
|
-
assert.ok(packedFiles.includes("CHANGELOG.md"));
|
|
32
|
-
assert.ok(packedFiles.includes("SECURITY.md"));
|
|
33
|
-
assert.ok(!packedFiles.some((path) => path.startsWith("skills/")));
|
|
34
|
-
assert.ok(!packedFiles.some((path) => path.startsWith("test/")));
|
|
35
|
-
const tarball = join(tempDir, filename);
|
|
36
|
-
|
|
37
|
-
execFileSync(NPM_BIN, [...NPM_PREFIX, "install", "--omit=peer", "--ignore-scripts", "--no-audit", "--no-fund", tarball], {
|
|
38
|
-
cwd: tempDir,
|
|
39
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const packageRequire = createRequire(join(tempDir, "node_modules", "pi-web-access", "package.json"));
|
|
43
|
-
const installedManifest = packageRequire("pi-web-access/package.json");
|
|
44
|
-
assert.equal(installedManifest.peerDependencies?.typebox, undefined);
|
|
45
|
-
assert.match(installedManifest.dependencies?.typebox, /^\^1\./);
|
|
46
|
-
assert.match(packageRequire.resolve("typebox").replaceAll("\\", "/"), /node_modules\/typebox\//);
|
|
47
|
-
} finally {
|
|
48
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
49
|
-
}
|
|
50
|
-
});
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { test } from "node:test";
|
|
9
|
+
|
|
10
|
+
// npm ships as a shell script / .cmd wrapper, neither of which is spawnable on
|
|
11
|
+
// Windows (extensionless `npm` does not resolve, `.cmd` is refused without a
|
|
12
|
+
// shell). There, invoke its CLI entry through the current node binary instead.
|
|
13
|
+
const NPM_BIN = process.platform === "win32" ? process.execPath : "npm";
|
|
14
|
+
const NPM_PREFIX = process.platform === "win32"
|
|
15
|
+
? [join(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js")]
|
|
16
|
+
: [];
|
|
17
|
+
|
|
18
|
+
const repoRoot = fileURLToPath(new URL("..", import.meta.url));
|
|
19
|
+
|
|
20
|
+
test("packed installs include typebox without peer dependencies", async () => {
|
|
21
|
+
const tempDir = await mkdtemp(join(tmpdir(), "pi-web-access-pack-install-"));
|
|
22
|
+
try {
|
|
23
|
+
const packOutput = execFileSync(NPM_BIN, [...NPM_PREFIX, "pack", "--json", "--pack-destination", tempDir], {
|
|
24
|
+
cwd: repoRoot,
|
|
25
|
+
encoding: "utf8",
|
|
26
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
27
|
+
});
|
|
28
|
+
const [{ filename, files }] = JSON.parse(packOutput);
|
|
29
|
+
const packedFiles = files.map((file) => file.path);
|
|
30
|
+
assert.ok(packedFiles.includes("index.ts"));
|
|
31
|
+
assert.ok(packedFiles.includes("CHANGELOG.md"));
|
|
32
|
+
assert.ok(packedFiles.includes("SECURITY.md"));
|
|
33
|
+
assert.ok(!packedFiles.some((path) => path.startsWith("skills/")));
|
|
34
|
+
assert.ok(!packedFiles.some((path) => path.startsWith("test/")));
|
|
35
|
+
const tarball = join(tempDir, filename);
|
|
36
|
+
|
|
37
|
+
execFileSync(NPM_BIN, [...NPM_PREFIX, "install", "--omit=peer", "--ignore-scripts", "--no-audit", "--no-fund", tarball], {
|
|
38
|
+
cwd: tempDir,
|
|
39
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const packageRequire = createRequire(join(tempDir, "node_modules", "pi-web-access", "package.json"));
|
|
43
|
+
const installedManifest = packageRequire("pi-web-access/package.json");
|
|
44
|
+
assert.equal(installedManifest.peerDependencies?.typebox, undefined);
|
|
45
|
+
assert.match(installedManifest.dependencies?.typebox, /^\^1\./);
|
|
46
|
+
assert.match(packageRequire.resolve("typebox").replaceAll("\\", "/"), /node_modules\/typebox\//);
|
|
47
|
+
} finally {
|
|
48
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
49
|
+
}
|
|
50
|
+
});
|