@cydm/pie 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin/extensions/ask-user/index.js +3 -3
- package/dist/builtin/extensions/computer-use/index.js +26 -4
- package/dist/builtin/extensions/computer-use/package.json +3 -0
- package/dist/builtin/extensions/computer-use/prebuilt/macos/arm64/pie-computer-use +0 -0
- package/dist/builtin/extensions/computer-use/prebuilt/macos/universal/pie-computer-use.app/Contents/MacOS/pie-computer-use +0 -0
- package/dist/builtin/extensions/computer-use/prebuilt/macos/x64/pie-computer-use +0 -0
- package/dist/builtin/extensions/computer-use/scripts/build-native.mjs +2 -1
- package/dist/builtin/extensions/computer-use/scripts/setup-helper.mjs +2 -2
- package/dist/builtin/extensions/computer-use/scripts/test-native-retention.mjs +204 -0
- package/dist/builtin/extensions/mcp-adapter/index.js +48 -13
- package/dist/builtin/extensions/plan-mode/index.js +3 -3
- package/dist/builtin/extensions/subagent/index.js +5 -4
- package/dist/builtin/extensions/todo/index.js +3 -3
- package/dist/builtin/skills/chrome-devtools-axi/chrome-devtools-axi-runtime.js +1 -1
- package/dist/builtin/skills/chrome-devtools-axi/{cli-F2VCSVL4.js → cli-P4VG6ZZG.js} +48 -8
- package/dist/builtin/skills/skill-creator/scripts/pie_runner.mjs +33 -12
- package/dist/builtin/skills/tui-use/tui-use-daemon.cjs +5 -7
- package/dist/chunks/{chunk-UPPICLVS.js → chunk-JXY3YLDS.js} +8247 -7345
- package/dist/chunks/{chunk-P3QPJXS7.js → chunk-PU7IIEKH.js} +108 -36
- package/dist/chunks/{chunk-IM43UYVV.js → chunk-YRXXRLMQ.js} +75 -14
- package/dist/chunks/{chunk-QJH6YN2W.js → chunk-ZKHMNEXL.js} +1 -1
- package/dist/chunks/{src-QSP2OPHU.js → src-SXUQ7OY6.js} +2 -2
- package/dist/chunks/{test-stream-CVGWFLSX.js → test-stream-OD3B57AS.js} +1 -1
- package/dist/cli.js +1350 -876
- package/package.json +2 -2
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createAskUserCapability
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
5
|
-
import "../../../chunks/chunk-
|
|
6
|
-
import "../../../chunks/chunk-
|
|
4
|
+
} from "../../../chunks/chunk-JXY3YLDS.js";
|
|
5
|
+
import "../../../chunks/chunk-ZKHMNEXL.js";
|
|
6
|
+
import "../../../chunks/chunk-YRXXRLMQ.js";
|
|
7
7
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
8
8
|
import "../../../chunks/chunk-LZQXQOPJ.js";
|
|
9
9
|
|
|
@@ -4420,6 +4420,9 @@ var CdpTab = class _CdpTab {
|
|
|
4420
4420
|
/** Evaluates a JS expression in the page and returns its primitive value. */
|
|
4421
4421
|
async evaluate(expression) {
|
|
4422
4422
|
const result = await this.send("Runtime.evaluate", { expression, returnByValue: true, timeout: COMMAND_TIMEOUT_MS, awaitPromise: true });
|
|
4423
|
+
if (result?.exceptionDetails) {
|
|
4424
|
+
throw new Error(`Evaluation failed: ${exceptionDetailsMessage(result.exceptionDetails)}`);
|
|
4425
|
+
}
|
|
4423
4426
|
return result?.result?.value;
|
|
4424
4427
|
}
|
|
4425
4428
|
async accessibilityTree() {
|
|
@@ -4444,6 +4447,9 @@ var CdpTab = class _CdpTab {
|
|
|
4444
4447
|
async clickBackendNode(backendNodeId) {
|
|
4445
4448
|
await this.withBackendNode(backendNodeId, "function(){ this.scrollIntoView({block:'center', inline:'center'}); this.click(); }");
|
|
4446
4449
|
}
|
|
4450
|
+
async focusBackendNode(backendNodeId) {
|
|
4451
|
+
await this.withBackendNode(backendNodeId, "function(){ this.scrollIntoView({block:'center', inline:'center'}); this.focus(); }");
|
|
4452
|
+
}
|
|
4447
4453
|
async typeIntoBackendNode(backendNodeId, text, replace) {
|
|
4448
4454
|
await this.withBackendNode(backendNodeId, "function(text, replace){ this.scrollIntoView({block:'center', inline:'center'}); this.focus(); if (replace) { if ('value' in this) this.value = ''; else this.textContent = ''; } if ('value' in this) this.value += text; else this.textContent = (this.textContent || '') + text; this.dispatchEvent(new InputEvent('input', {bubbles:true, inputType:'insertText', data:text})); this.dispatchEvent(new Event('change', {bubbles:true})); }", [text, replace]);
|
|
4449
4455
|
}
|
|
@@ -4479,11 +4485,14 @@ var CdpTab = class _CdpTab {
|
|
|
4479
4485
|
const resolved = await this.send("DOM.resolveNode", { backendNodeId });
|
|
4480
4486
|
const objectId = resolved?.object?.objectId;
|
|
4481
4487
|
if (typeof objectId !== "string") throw new Error(`CDP could not resolve backend node ${backendNodeId}.`);
|
|
4482
|
-
await this.send("Runtime.callFunctionOn", {
|
|
4488
|
+
const callResult = await this.send("Runtime.callFunctionOn", {
|
|
4483
4489
|
objectId,
|
|
4484
4490
|
functionDeclaration,
|
|
4485
4491
|
arguments: args.map((value) => ({ value }))
|
|
4486
4492
|
});
|
|
4493
|
+
if (callResult?.exceptionDetails) {
|
|
4494
|
+
throw new Error(`CDP call on backend node ${backendNodeId} failed: ${exceptionDetailsMessage(callResult.exceptionDetails)}`);
|
|
4495
|
+
}
|
|
4487
4496
|
}
|
|
4488
4497
|
/** Screen bounds of the browser window containing this tab. */
|
|
4489
4498
|
async windowBounds() {
|
|
@@ -4670,6 +4679,12 @@ async function cdpTypeFocusedForContext(contextId, text) {
|
|
|
4670
4679
|
return true;
|
|
4671
4680
|
}) === true;
|
|
4672
4681
|
}
|
|
4682
|
+
async function cdpFocusForContext(contextId, backendNodeId) {
|
|
4683
|
+
return await withCdpContextTab(contextId, async (tab) => {
|
|
4684
|
+
await tab.focusBackendNode(backendNodeId);
|
|
4685
|
+
return true;
|
|
4686
|
+
}) === true;
|
|
4687
|
+
}
|
|
4673
4688
|
async function cdpKeypressForContext(contextId, keys2) {
|
|
4674
4689
|
return await withCdpContextTab(contextId, async (tab) => {
|
|
4675
4690
|
await tab.keypress(keys2);
|
|
@@ -4773,6 +4788,13 @@ function axString(raw) {
|
|
|
4773
4788
|
const value = raw?.value ?? raw;
|
|
4774
4789
|
return typeof value === "string" ? value.trim() : "";
|
|
4775
4790
|
}
|
|
4791
|
+
function exceptionDetailsMessage(details) {
|
|
4792
|
+
const exception = details?.exception;
|
|
4793
|
+
if (typeof exception?.description === "string" && exception.description) return exception.description;
|
|
4794
|
+
if (typeof exception?.value === "string" && exception.value) return exception.value;
|
|
4795
|
+
if (typeof details?.text === "string" && details.text) return details.text;
|
|
4796
|
+
return "the script threw an exception.";
|
|
4797
|
+
}
|
|
4776
4798
|
function cdpSnapshotOutline(snapshotId, nodes) {
|
|
4777
4799
|
const records = /* @__PURE__ */ new Map();
|
|
4778
4800
|
for (const raw of nodes) {
|
|
@@ -4789,7 +4811,7 @@ function cdpSnapshotOutline(snapshotId, nodes) {
|
|
|
4789
4811
|
const actions = browserActionsForAxRole(role);
|
|
4790
4812
|
const backendNodeId = Number.isFinite(raw?.backendDOMNodeId) ? Math.trunc(raw.backendDOMNodeId) : void 0;
|
|
4791
4813
|
const wireRef = `cdp:${nodeId}`;
|
|
4792
|
-
if (actions.length > 0 &&
|
|
4814
|
+
if (actions.length > 0 && (!actions.includes("click") || backendNodeId)) {
|
|
4793
4815
|
targets.push({ ref: wireRef, source: "browser_ax", role, name, value: axString(raw?.value) || void 0, actions, backendNodeId });
|
|
4794
4816
|
}
|
|
4795
4817
|
const childIds = Array.isArray(raw?.childIds) ? raw.childIds.map(String) : [];
|
|
@@ -8728,7 +8750,7 @@ async function performBrowserTransaction(params, actions, signal) {
|
|
|
8728
8750
|
if (!BROWSER_TRANSACTION_ACTIONS.has(action.action)) throw new Error(`Browser transactions do not support '${action.action}'.`);
|
|
8729
8751
|
if (action.action === "click" && action.ref && action.button && action.button !== "left") throw new Error("Browser ref clicks support only the left button; use coordinate clicks for right or middle buttons.");
|
|
8730
8752
|
const target = browserSnapshotTarget(params.stateId, trimOrUndefined(action.ref));
|
|
8731
|
-
if ((action.action === "press" || action.action === "setText" || action.action === "click" && action.ref || action.action === "typeText" && action.ref) && !Number.isFinite(target?.backendNodeId)) {
|
|
8753
|
+
if ((action.action === "press" || action.action === "setText" || action.action === "click" && action.ref || action.action === "typeText" && action.ref || action.action === "keypress" && action.ref) && !Number.isFinite(target?.backendNodeId)) {
|
|
8732
8754
|
throw new Error(`Browser ${action.action} requires an actionable @e ref owned by ${params.stateId}.`);
|
|
8733
8755
|
}
|
|
8734
8756
|
if (action.ref && (!target || target.contextId !== contextId)) throw new Error(`Browser ${action.action} ref must be owned by ${params.stateId}.`);
|
|
@@ -8744,7 +8766,7 @@ async function performBrowserTransaction(params, actions, signal) {
|
|
|
8744
8766
|
worked = await cdpMouseForContext(contextId, action.x, action.y, "mousePressed", action.button ?? "left", action.clickCount ?? 1) && await cdpMouseForContext(contextId, action.x, action.y, "mouseReleased", action.button ?? "left", action.clickCount ?? 1);
|
|
8745
8767
|
} else if (action.action === "setText") worked = await cdpTypeForContext(contextId, target.backendNodeId, action.text ?? "", true);
|
|
8746
8768
|
else if (action.action === "typeText") worked = target?.backendNodeId ? await cdpTypeForContext(contextId, target.backendNodeId, action.text ?? "", false) : await cdpTypeFocusedForContext(contextId, action.text ?? "");
|
|
8747
|
-
else if (action.action === "keypress") worked = await cdpKeypressForContext(contextId, action.keys ?? []);
|
|
8769
|
+
else if (action.action === "keypress") worked = target?.backendNodeId ? await cdpFocusForContext(contextId, target.backendNodeId) && await cdpKeypressForContext(contextId, action.keys ?? []) : await cdpKeypressForContext(contextId, action.keys ?? []);
|
|
8748
8770
|
else if (action.action === "scroll") worked = await cdpScrollForContext(contextId, toFiniteNumber(action.scrollX, 0), toFiniteNumber(action.scrollY, 0), target?.backendNodeId);
|
|
8749
8771
|
else if (action.action === "drag") worked = await cdpDragForContext(contextId, normalizeActionPath(action.path));
|
|
8750
8772
|
else if (action.action === "moveMouse") worked = await cdpMouseForContext(contextId, action.x, action.y, "mouseMoved");
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
"description": "Desktop computer-use tools (observe/act) vendored from @injaneity/pi-computer-use",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test:native-retention": "node scripts/test-native-retention.mjs"
|
|
9
|
+
},
|
|
7
10
|
"pie": {
|
|
8
11
|
"name": "computer-use",
|
|
9
12
|
"main": "index.js"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -11,6 +11,7 @@ const macosSourcePaths = [
|
|
|
11
11
|
"agent_cursor.swift",
|
|
12
12
|
"agent_cursor_motion.swift",
|
|
13
13
|
"bridge.swift",
|
|
14
|
+
"ref_store.swift",
|
|
14
15
|
].map((file) => path.join(rootDir, "native", "macos", file));
|
|
15
16
|
const windowsCrateDir = path.join(rootDir, "native", "windows", "bridge-rs");
|
|
16
17
|
const linuxCrateDir = path.join(rootDir, "native", "linux", "bridge-rs");
|
|
@@ -20,7 +21,7 @@ const archTriples = {
|
|
|
20
21
|
x64: "x86_64-apple-macosx",
|
|
21
22
|
};
|
|
22
23
|
const deploymentTarget = "14.0";
|
|
23
|
-
const frameworks = ["ApplicationServices", "AppKit", "ScreenCaptureKit", "Foundation", "SwiftUI"];
|
|
24
|
+
const frameworks = ["ApplicationServices", "AppKit", "ScreenCaptureKit", "Foundation", "SwiftUI", "CryptoKit"];
|
|
24
25
|
const defaultCodeSignIdentifier = "com.pie.computer-use";
|
|
25
26
|
|
|
26
27
|
async function exists(filePath) {
|
|
@@ -20,7 +20,7 @@ const windowsCrateDir = path.join(rootDir, "native", "windows", "bridge-rs");
|
|
|
20
20
|
const windowsHelperDestPath = process.env.PIE_COMPUTER_USE_WINDOWS_HELPER_PATH || path.join(resolvePieConfigDir(), "helpers", "computer-use", "windows-bridge.exe");
|
|
21
21
|
const linuxCrateDir = path.join(rootDir, "native", "linux", "bridge-rs");
|
|
22
22
|
const linuxHelperDestPath = process.env.PIE_COMPUTER_USE_LINUX_HELPER_PATH || path.join(resolvePieConfigDir(), "helpers", "computer-use", "linux-bridge");
|
|
23
|
-
const helperSourcePaths = ["agent_cursor.swift", "agent_cursor_motion.swift", "bridge.swift"]
|
|
23
|
+
const helperSourcePaths = ["agent_cursor.swift", "agent_cursor_motion.swift", "bridge.swift", "ref_store.swift"]
|
|
24
24
|
.map((file) => path.join(rootDir, "native", "macos", file));
|
|
25
25
|
const packageJsonPath = path.join(rootDir, "package.json");
|
|
26
26
|
const localCodeSignCommonName = "pie-computer-use Local Signing (com.pie.computer-use)";
|
|
@@ -57,7 +57,7 @@ const archTriples = {
|
|
|
57
57
|
x64: "x86_64-apple-macosx",
|
|
58
58
|
};
|
|
59
59
|
const deploymentTarget = "14.0";
|
|
60
|
-
const frameworks = ["ApplicationServices", "AppKit", "ScreenCaptureKit", "Foundation", "SwiftUI"];
|
|
60
|
+
const frameworks = ["ApplicationServices", "AppKit", "ScreenCaptureKit", "Foundation", "SwiftUI", "CryptoKit"];
|
|
61
61
|
const defaultCodeSignIdentifier = "com.pie.computer-use";
|
|
62
62
|
|
|
63
63
|
function normalizeArch(arch) {
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Compiles and runs the macOS native AXRefStore retention tests
|
|
4
|
+
// (native/macos/retention_tests.swift) from source, then compiles the full
|
|
5
|
+
// helper and drives its stdin JSON-lines transport with permission-free
|
|
6
|
+
// requests to assert the bridge-level stale-ref/stale-look integration.
|
|
7
|
+
//
|
|
8
|
+
// Nothing here needs Accessibility or Screen Recording permission or real UI
|
|
9
|
+
// interaction: the store tests use synthetic AXUIElement values, and the
|
|
10
|
+
// helper protocol probes only exercise failure paths (unknown look/element
|
|
11
|
+
// ids, missing windows). This script is macOS-only by design; on other
|
|
12
|
+
// platforms it skips with an explicit message (Linux/Windows helpers have
|
|
13
|
+
// their own native suites).
|
|
14
|
+
|
|
15
|
+
import { spawn } from "node:child_process";
|
|
16
|
+
import fs from "node:fs/promises";
|
|
17
|
+
import os from "node:os";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
|
|
21
|
+
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
22
|
+
const nativeDir = path.join(rootDir, "native", "macos");
|
|
23
|
+
const storeTestSources = ["ref_store.swift", "retention_tests.swift"].map((file) => path.join(nativeDir, file));
|
|
24
|
+
const helperSources = ["agent_cursor.swift", "agent_cursor_motion.swift", "bridge.swift", "ref_store.swift"].map((file) => path.join(nativeDir, file));
|
|
25
|
+
const deploymentTarget = "14.0";
|
|
26
|
+
const helperFrameworks = ["ApplicationServices", "AppKit", "ScreenCaptureKit", "Foundation", "SwiftUI", "CryptoKit"];
|
|
27
|
+
|
|
28
|
+
function run(command, args, options = {}) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const child = spawn(command, args, { stdio: "inherit", ...options });
|
|
31
|
+
child.on("error", reject);
|
|
32
|
+
child.on("close", (code) => {
|
|
33
|
+
if (code === 0) {
|
|
34
|
+
resolve();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
reject(new Error(`Command failed (${code}): ${command} ${args.join(" ")}`));
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function swiftcArgs(sources, output, moduleCache, frameworks) {
|
|
43
|
+
const args = [
|
|
44
|
+
"swiftc",
|
|
45
|
+
"-target",
|
|
46
|
+
`${process.arch === "arm64" ? "arm64" : "x86_64"}-apple-macosx${deploymentTarget}`,
|
|
47
|
+
"-module-cache-path",
|
|
48
|
+
moduleCache,
|
|
49
|
+
"-O",
|
|
50
|
+
];
|
|
51
|
+
for (const framework of frameworks) args.push("-framework", framework);
|
|
52
|
+
args.push(...sources, "-o", output);
|
|
53
|
+
return args;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Drives the helper over its stdin transport and resolves one response per request id. */
|
|
57
|
+
function helperResponses(executable, requests, timeoutMs = 15_000) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
const child = spawn(executable, [], { stdio: ["pipe", "pipe", "ignore"] });
|
|
60
|
+
const pending = new Map(requests.map((request) => [request.id, request]));
|
|
61
|
+
const settled = new Map();
|
|
62
|
+
const fail = (error) => {
|
|
63
|
+
child.kill();
|
|
64
|
+
reject(error);
|
|
65
|
+
};
|
|
66
|
+
const timer = setTimeout(() => fail(new Error(`Helper did not answer all requests within ${timeoutMs}ms`)), timeoutMs);
|
|
67
|
+
let buffer = "";
|
|
68
|
+
child.stdout.setEncoding("utf8");
|
|
69
|
+
child.stdout.on("data", (chunk) => {
|
|
70
|
+
buffer += chunk;
|
|
71
|
+
let newline;
|
|
72
|
+
while ((newline = buffer.indexOf("\n")) >= 0) {
|
|
73
|
+
const line = buffer.slice(0, newline);
|
|
74
|
+
buffer = buffer.slice(newline + 1);
|
|
75
|
+
if (!line.trim()) continue;
|
|
76
|
+
let response;
|
|
77
|
+
try {
|
|
78
|
+
response = JSON.parse(line);
|
|
79
|
+
} catch {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (typeof response.id === "string" && pending.delete(response.id)) {
|
|
83
|
+
settled.set(response.id, response);
|
|
84
|
+
if (pending.size === 0) {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
child.stdin.end();
|
|
87
|
+
child.on("close", () => resolve(settled));
|
|
88
|
+
child.kill();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
child.on("error", fail);
|
|
94
|
+
for (const request of requests) child.stdin.write(`${JSON.stringify(request)}\n`);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Verifies the helper's true-exit path: shutdown answers, then the process exits zero on its own. */
|
|
99
|
+
function helperShutdownExits(executable, timeoutMs = 8_000) {
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
const child = spawn(executable, [], { stdio: ["pipe", "pipe", "ignore"] });
|
|
102
|
+
let buffer = "";
|
|
103
|
+
let sawResponse = false;
|
|
104
|
+
const fail = (error) => {
|
|
105
|
+
child.kill();
|
|
106
|
+
reject(error);
|
|
107
|
+
};
|
|
108
|
+
const timer = setTimeout(() => fail(new Error(`Helper did not shut down within ${timeoutMs}ms`)), timeoutMs);
|
|
109
|
+
child.stdout.setEncoding("utf8");
|
|
110
|
+
child.stdout.on("data", (chunk) => {
|
|
111
|
+
buffer += chunk;
|
|
112
|
+
let newline;
|
|
113
|
+
while ((newline = buffer.indexOf("\n")) >= 0) {
|
|
114
|
+
const line = buffer.slice(0, newline);
|
|
115
|
+
buffer = buffer.slice(newline + 1);
|
|
116
|
+
if (!line.trim()) continue;
|
|
117
|
+
let response;
|
|
118
|
+
try {
|
|
119
|
+
response = JSON.parse(line);
|
|
120
|
+
} catch {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (response.id === "shutdown" && response.ok === true && response.result?.shuttingDown === true) sawResponse = true;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
child.on("error", fail);
|
|
127
|
+
child.on("close", (code, signal) => {
|
|
128
|
+
clearTimeout(timer);
|
|
129
|
+
if (!sawResponse) {
|
|
130
|
+
reject(new Error("Helper did not acknowledge shutdown before exiting"));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (code === 0) {
|
|
134
|
+
resolve();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
reject(new Error(`Helper exited with ${code ?? "null"}${signal ? ` (signal ${signal})` : ""} instead of 0`));
|
|
138
|
+
});
|
|
139
|
+
child.stdin.write(`${JSON.stringify({ id: "shutdown", cmd: "shutdown" })}\n`);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function assert(condition, message) {
|
|
144
|
+
if (!condition) throw new Error(`[pie-computer-use] bridge integration assertion failed: ${message}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function runBridgeIntegration(helperPath) {
|
|
148
|
+
const responses = await helperResponses(helperPath, [
|
|
149
|
+
{ id: "diag", cmd: "diagnostics" },
|
|
150
|
+
{ id: "act-stale", cmd: "act", lookId: "look_999", pid: 123, action: "press", target: { ref: "e1" } },
|
|
151
|
+
{ id: "batch-stale", cmd: "actBatch", actions: [{ lookId: "look_999", pid: 123, action: "press", target: { ref: "e1" } }] },
|
|
152
|
+
{ id: "hit-stale", cmd: "hitTest", lookId: "look_999", x: 10, y: 10 },
|
|
153
|
+
{ id: "read-stale", cmd: "axReadText", elementRef: "e999999" },
|
|
154
|
+
{ id: "look-stale-base", cmd: "look", baseLookId: "look_999", windowId: 12345 },
|
|
155
|
+
]);
|
|
156
|
+
const diagnostics = responses.get("diag");
|
|
157
|
+
assert(diagnostics?.ok === true, "diagnostics must succeed");
|
|
158
|
+
assert(diagnostics.result?.protocolVersion === 6, "helper must keep protocol version 6");
|
|
159
|
+
assert(Array.isArray(diagnostics.result?.invariants) && diagnostics.result.invariants.includes("bounded-observation-history"), "helper must report the bounded-observation-history invariant");
|
|
160
|
+
for (const [id, expectedCode] of [
|
|
161
|
+
["act-stale", "stale_look"],
|
|
162
|
+
["batch-stale", "stale_look"],
|
|
163
|
+
["hit-stale", "stale_look"],
|
|
164
|
+
["read-stale", "element_ref_invalid"],
|
|
165
|
+
["look-stale-base", "stale_look"],
|
|
166
|
+
]) {
|
|
167
|
+
const response = responses.get(id);
|
|
168
|
+
assert(response?.ok === false, `${id} must fail`);
|
|
169
|
+
assert(response.error?.code === expectedCode, `${id} must fail with ${expectedCode}, got ${response.error?.code}`);
|
|
170
|
+
}
|
|
171
|
+
console.log("[pie-computer-use] bridge integration probes passed (stale look/ref failures, protocol v6).");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function main() {
|
|
175
|
+
if (process.platform !== "darwin") {
|
|
176
|
+
console.log(`[pie-computer-use] skipping native retention tests: they are macOS-only and cannot run on ${process.platform}.`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
let tempDir;
|
|
180
|
+
try {
|
|
181
|
+
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "pie-computer-use-retention-"));
|
|
182
|
+
const moduleCache = path.join(tempDir, "module-cache");
|
|
183
|
+
const storeTestOutput = path.join(tempDir, "retention_tests");
|
|
184
|
+
console.log("[pie-computer-use] compiling native retention tests...");
|
|
185
|
+
await run("xcrun", swiftcArgs(storeTestSources, storeTestOutput, moduleCache, ["ApplicationServices", "CryptoKit"]));
|
|
186
|
+
console.log("[pie-computer-use] running native retention tests...");
|
|
187
|
+
await run(storeTestOutput, []);
|
|
188
|
+
|
|
189
|
+
const helperOutput = path.join(tempDir, "pie-computer-use");
|
|
190
|
+
console.log("[pie-computer-use] compiling native helper for bridge integration probes...");
|
|
191
|
+
await run("xcrun", swiftcArgs(helperSources, helperOutput, moduleCache, helperFrameworks));
|
|
192
|
+
await runBridgeIntegration(helperOutput);
|
|
193
|
+
await helperShutdownExits(helperOutput);
|
|
194
|
+
console.log("[pie-computer-use] helper shutdown exits cleanly with code 0.");
|
|
195
|
+
console.log("[pie-computer-use] native retention tests passed.");
|
|
196
|
+
} finally {
|
|
197
|
+
if (tempDir) await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
main().catch((error) => {
|
|
202
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
203
|
+
process.exit(1);
|
|
204
|
+
});
|
|
@@ -24535,6 +24535,24 @@ var McpServerManager = class {
|
|
|
24535
24535
|
getDefinition(server) {
|
|
24536
24536
|
return this.definitions.get(sanitizeName(server));
|
|
24537
24537
|
}
|
|
24538
|
+
/**
|
|
24539
|
+
* Resolve a `<server>_<tool>` prefixed name against CONFIGURED server names.
|
|
24540
|
+
* Splitting at the first underscore breaks servers whose own name contains
|
|
24541
|
+
* underscores (`my_server_echo` would resolve to server "my"), so match the
|
|
24542
|
+
* longest configured server name that prefixes the tool name instead. When
|
|
24543
|
+
* several configured names prefix (e.g. servers `my` and `my_server` both
|
|
24544
|
+
* exist), the longest match wins: that is how toPrefixedName() would have
|
|
24545
|
+
* constructed the deepest claim, and it is deterministic.
|
|
24546
|
+
*/
|
|
24547
|
+
resolvePrefixedName(prefixedName) {
|
|
24548
|
+
let best = null;
|
|
24549
|
+
for (const key of this.definitions.keys()) {
|
|
24550
|
+
if (!prefixedName.startsWith(`${key}_`)) continue;
|
|
24551
|
+
if (best !== null && key.length <= best.server.length) continue;
|
|
24552
|
+
best = { server: key, tool: prefixedName.slice(key.length + 1) };
|
|
24553
|
+
}
|
|
24554
|
+
return best;
|
|
24555
|
+
}
|
|
24538
24556
|
listDefinitions() {
|
|
24539
24557
|
return [...this.definitions.values()];
|
|
24540
24558
|
}
|
|
@@ -24788,13 +24806,10 @@ var McpServerManager = class {
|
|
|
24788
24806
|
let server = descriptor?.server;
|
|
24789
24807
|
let toolName = descriptor?.tool;
|
|
24790
24808
|
if (!server) {
|
|
24791
|
-
const
|
|
24792
|
-
if (
|
|
24793
|
-
|
|
24794
|
-
|
|
24795
|
-
server = candidate;
|
|
24796
|
-
toolName = prefixedName.slice(separator + 1);
|
|
24797
|
-
}
|
|
24809
|
+
const resolved = this.resolvePrefixedName(prefixedName);
|
|
24810
|
+
if (resolved) {
|
|
24811
|
+
server = resolved.server;
|
|
24812
|
+
toolName = resolved.tool;
|
|
24798
24813
|
}
|
|
24799
24814
|
}
|
|
24800
24815
|
if (!server || !toolName) {
|
|
@@ -24828,9 +24843,15 @@ ${tail}` : "")
|
|
|
24828
24843
|
error: `MCP connection to "${server}" closed before the call could start.`
|
|
24829
24844
|
};
|
|
24830
24845
|
}
|
|
24846
|
+
if (signal?.aborted) {
|
|
24847
|
+
return {
|
|
24848
|
+
delivery: "not_sent",
|
|
24849
|
+
error: `MCP call "${prefixedName}" was cancelled before the request was sent; nothing was executed.`
|
|
24850
|
+
};
|
|
24851
|
+
}
|
|
24831
24852
|
try {
|
|
24832
24853
|
const result = await withTimeout(
|
|
24833
|
-
client.callTool({ name: toolName, arguments: args }),
|
|
24854
|
+
client.callTool({ name: toolName, arguments: args }, void 0, signal !== void 0 ? { signal } : void 0),
|
|
24834
24855
|
CALL_TIMEOUT_MS,
|
|
24835
24856
|
`call "${prefixedName}"`,
|
|
24836
24857
|
signal
|
|
@@ -24843,6 +24864,12 @@ ${tail}` : "")
|
|
|
24843
24864
|
};
|
|
24844
24865
|
} catch (error2) {
|
|
24845
24866
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
24867
|
+
if (error2 instanceof ProtocolError && (error2.code === ProtocolErrorCode.InvalidRequest || error2.code === ProtocolErrorCode.InvalidParams) && /output schema|structured content/i.test(message)) {
|
|
24868
|
+
return {
|
|
24869
|
+
delivery: "invalid_result",
|
|
24870
|
+
error: `MCP server "${server}" returned a result that fails its own outputSchema: ${message}. The call DID reach the server and the tool likely executed \u2014 do NOT retry automatically; verify state with a read-only tool first.`
|
|
24871
|
+
};
|
|
24872
|
+
}
|
|
24846
24873
|
if (runtime.client === void 0 || /closed|aborted|timed out|socket|ECONNRESET/i.test(message)) {
|
|
24847
24874
|
return {
|
|
24848
24875
|
delivery: "may_have_run",
|
|
@@ -24990,7 +25017,7 @@ function structuredContentIsMirroredInText(textBlocks, structured) {
|
|
|
24990
25017
|
}
|
|
24991
25018
|
function mapMcpOutcome(outcome, maxBytes) {
|
|
24992
25019
|
if (outcome.delivery !== "ok") {
|
|
24993
|
-
const prefix = outcome.delivery === "may_have_run" ? "[outcome unknown]" : "[not executed]";
|
|
25020
|
+
const prefix = outcome.delivery === "may_have_run" || outcome.delivery === "invalid_result" ? "[outcome unknown]" : "[not executed]";
|
|
24994
25021
|
return {
|
|
24995
25022
|
content: [{ type: "text", text: `${prefix} ${outcome.error ?? "MCP call failed"}` }],
|
|
24996
25023
|
details: { mode: "mcp", delivery: outcome.delivery, ...outcome.protocolCode !== void 0 ? { protocolCode: outcome.protocolCode } : {} },
|
|
@@ -25077,8 +25104,8 @@ ${truncate(JSON.stringify(schema ?? {}, null, 2), maxBytes)}` : `unknown MCP too
|
|
|
25077
25104
|
isError: descriptor === void 0
|
|
25078
25105
|
};
|
|
25079
25106
|
}
|
|
25080
|
-
const
|
|
25081
|
-
const server = descriptor?.server ??
|
|
25107
|
+
const resolved = getManager().resolvePrefixedName(params.tool);
|
|
25108
|
+
const server = descriptor?.server ?? resolved?.server ?? "";
|
|
25082
25109
|
if (server) {
|
|
25083
25110
|
const trust = await ensureTrusted(server);
|
|
25084
25111
|
if (!trust.trusted) {
|
|
@@ -25267,10 +25294,18 @@ function mcpAdapter(ctx) {
|
|
|
25267
25294
|
const filePath = path4.join(ctx.cwd, ".pie", "mcp-adapter.json");
|
|
25268
25295
|
const parsed = readProjectOverride();
|
|
25269
25296
|
const servers = parsed.mcpServers && typeof parsed.mcpServers === "object" && !Array.isArray(parsed.mcpServers) ? { ...parsed.mcpServers } : {};
|
|
25297
|
+
const existing = servers[serverName];
|
|
25298
|
+
const existingObj = existing && typeof existing === "object" && !Array.isArray(existing) ? { ...existing } : {};
|
|
25270
25299
|
if (disabled === null) {
|
|
25271
|
-
delete
|
|
25300
|
+
delete existingObj.disabled;
|
|
25301
|
+
if (Object.keys(existingObj).length === 0) {
|
|
25302
|
+
delete servers[serverName];
|
|
25303
|
+
} else {
|
|
25304
|
+
servers[serverName] = existingObj;
|
|
25305
|
+
}
|
|
25272
25306
|
} else {
|
|
25273
|
-
|
|
25307
|
+
existingObj.disabled = disabled;
|
|
25308
|
+
servers[serverName] = existingObj;
|
|
25274
25309
|
}
|
|
25275
25310
|
parsed.mcpServers = servers;
|
|
25276
25311
|
fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
isPlanModeSafeCommand,
|
|
8
8
|
markCompletedPlanSteps,
|
|
9
9
|
restoreExecutionState
|
|
10
|
-
} from "../../../chunks/chunk-
|
|
11
|
-
import "../../../chunks/chunk-
|
|
12
|
-
import "../../../chunks/chunk-
|
|
10
|
+
} from "../../../chunks/chunk-JXY3YLDS.js";
|
|
11
|
+
import "../../../chunks/chunk-ZKHMNEXL.js";
|
|
12
|
+
import "../../../chunks/chunk-YRXXRLMQ.js";
|
|
13
13
|
import {
|
|
14
14
|
Type
|
|
15
15
|
} from "../../../chunks/chunk-XXGYUYXI.js";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createCliHostCapabilities
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
4
|
+
} from "../../../chunks/chunk-PU7IIEKH.js";
|
|
5
5
|
import {
|
|
6
6
|
createSharedFileSystemTools,
|
|
7
7
|
createSubagentCapability
|
|
8
|
-
} from "../../../chunks/chunk-
|
|
9
|
-
import "../../../chunks/chunk-
|
|
10
|
-
import "../../../chunks/chunk-
|
|
8
|
+
} from "../../../chunks/chunk-JXY3YLDS.js";
|
|
9
|
+
import "../../../chunks/chunk-ZKHMNEXL.js";
|
|
10
|
+
import "../../../chunks/chunk-YRXXRLMQ.js";
|
|
11
11
|
import "../../../chunks/chunk-5353DEEC.js";
|
|
12
12
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
13
13
|
import "../../../chunks/chunk-LZQXQOPJ.js";
|
|
@@ -38,6 +38,7 @@ function subagentExtension(ctx) {
|
|
|
38
38
|
notify: ctx.hasUI ? (message, type) => ctx.ui.notify(message, type === "success" ? "info" : type) : void 0,
|
|
39
39
|
debugLog: (module, message, data) => ctx.log(`[subagent:${module}] ${message}${data !== void 0 ? ` ${JSON.stringify(data)}` : ""}`),
|
|
40
40
|
resolveModelClass: (modelClass) => ctx.resolveModelClass?.(modelClass),
|
|
41
|
+
resolveModelRef: (modelRef) => ctx.resolveModelRef?.(modelRef),
|
|
41
42
|
createRuntimeTools: () => {
|
|
42
43
|
const parentRuntimeTools = ctx.getRuntimeTools();
|
|
43
44
|
if (Array.isArray(parentRuntimeTools) && parentRuntimeTools.length > 0) {
|
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
executeManageTodoList,
|
|
9
9
|
executionStateToTodos,
|
|
10
10
|
restoreExecutionState
|
|
11
|
-
} from "../../../chunks/chunk-
|
|
12
|
-
import "../../../chunks/chunk-
|
|
13
|
-
import "../../../chunks/chunk-
|
|
11
|
+
} from "../../../chunks/chunk-JXY3YLDS.js";
|
|
12
|
+
import "../../../chunks/chunk-ZKHMNEXL.js";
|
|
13
|
+
import "../../../chunks/chunk-YRXXRLMQ.js";
|
|
14
14
|
import "../../../chunks/chunk-XXGYUYXI.js";
|
|
15
15
|
import "../../../chunks/chunk-LZQXQOPJ.js";
|
|
16
16
|
|
|
@@ -21,6 +21,6 @@ function tryFastPath(argv, options) {
|
|
|
21
21
|
|
|
22
22
|
// vendor/chrome-devtools-axi/bin/chrome-devtools-axi.ts
|
|
23
23
|
if (!tryFastPath(process.argv.slice(2), { version: VERSION })) {
|
|
24
|
-
const { main } = await import("./cli-
|
|
24
|
+
const { main } = await import("./cli-P4VG6ZZG.js");
|
|
25
25
|
await main(process.argv.slice(2));
|
|
26
26
|
}
|
|
@@ -1612,7 +1612,7 @@ function installSessionStartHooks(options = {}) {
|
|
|
1612
1612
|
|
|
1613
1613
|
// vendor/chrome-devtools-axi/src/client.ts
|
|
1614
1614
|
import { execFileSync, spawn as spawn2 } from "node:child_process";
|
|
1615
|
-
import { readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
|
|
1615
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3, unlinkSync } from "node:fs";
|
|
1616
1616
|
import { request } from "node:http";
|
|
1617
1617
|
import { dirname as dirname3 } from "node:path";
|
|
1618
1618
|
|
|
@@ -1945,6 +1945,39 @@ function isBridgeProcess(pid) {
|
|
|
1945
1945
|
return false;
|
|
1946
1946
|
}
|
|
1947
1947
|
}
|
|
1948
|
+
async function probeBridgeIdentity(port, sessionName) {
|
|
1949
|
+
try {
|
|
1950
|
+
const resp = await httpGet(port, "/health", HEALTH_TIMEOUT_MS);
|
|
1951
|
+
const data = JSON.parse(resp);
|
|
1952
|
+
if (data.status !== "ok") return "unknown";
|
|
1953
|
+
if (typeof data.session === "string" && data.session !== sessionName) {
|
|
1954
|
+
return "other";
|
|
1955
|
+
}
|
|
1956
|
+
return "own";
|
|
1957
|
+
} catch {
|
|
1958
|
+
return "unknown";
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
async function judgeRecordedBridge(pidInfo, sessionName) {
|
|
1962
|
+
const identity = await probeBridgeIdentity(pidInfo.port, sessionName);
|
|
1963
|
+
if (identity === "own") {
|
|
1964
|
+
return {
|
|
1965
|
+
terminate: true,
|
|
1966
|
+
killProcessGroup: isBridgeProcess(pidInfo.pid)
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1969
|
+
if (identity === "other") {
|
|
1970
|
+
return { terminate: false, killProcessGroup: false };
|
|
1971
|
+
}
|
|
1972
|
+
const bridge = isBridgeProcess(pidInfo.pid);
|
|
1973
|
+
return { terminate: bridge, killProcessGroup: bridge };
|
|
1974
|
+
}
|
|
1975
|
+
function removePidFile(pidFile) {
|
|
1976
|
+
try {
|
|
1977
|
+
unlinkSync(pidFile);
|
|
1978
|
+
} catch {
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1948
1981
|
async function terminateBridgeProcess(pid, opts = {}) {
|
|
1949
1982
|
if (!isProcessAlive(pid)) return;
|
|
1950
1983
|
const killProcessGroup = opts.killProcessGroup === true;
|
|
@@ -2065,9 +2098,14 @@ async function ensureBridge(spawnBridge = spawnBridgeProcess, notice) {
|
|
|
2065
2098
|
})) {
|
|
2066
2099
|
return pidInfo.port;
|
|
2067
2100
|
}
|
|
2068
|
-
await
|
|
2069
|
-
|
|
2070
|
-
|
|
2101
|
+
const verdict = await judgeRecordedBridge(pidInfo, sessionName);
|
|
2102
|
+
if (verdict.terminate) {
|
|
2103
|
+
await terminateBridgeProcess(pidInfo.pid, {
|
|
2104
|
+
killProcessGroup: verdict.killProcessGroup
|
|
2105
|
+
});
|
|
2106
|
+
} else {
|
|
2107
|
+
removePidFile(pidFile);
|
|
2108
|
+
}
|
|
2071
2109
|
}
|
|
2072
2110
|
clearSelectedPageId();
|
|
2073
2111
|
const child = spawnBridge(port, sessionName);
|
|
@@ -2338,8 +2376,10 @@ async function stopBridge() {
|
|
|
2338
2376
|
const pidInfo = readPidFile();
|
|
2339
2377
|
if (!pidInfo) return false;
|
|
2340
2378
|
if (!isProcessAlive(pidInfo.pid)) return false;
|
|
2379
|
+
const verdict = await judgeRecordedBridge(pidInfo, resolveSessionName());
|
|
2380
|
+
if (!verdict.terminate) return false;
|
|
2341
2381
|
await terminateBridgeProcess(pidInfo.pid, {
|
|
2342
|
-
killProcessGroup:
|
|
2382
|
+
killProcessGroup: verdict.killProcessGroup
|
|
2343
2383
|
});
|
|
2344
2384
|
clearSelectedPageId();
|
|
2345
2385
|
return true;
|
|
@@ -2350,7 +2390,7 @@ import {
|
|
|
2350
2390
|
existsSync as existsSync4,
|
|
2351
2391
|
mkdirSync as mkdirSync2,
|
|
2352
2392
|
readFileSync as readFileSync4,
|
|
2353
|
-
unlinkSync,
|
|
2393
|
+
unlinkSync as unlinkSync2,
|
|
2354
2394
|
writeFileSync as writeFileSync2
|
|
2355
2395
|
} from "node:fs";
|
|
2356
2396
|
import { dirname as dirname4, join as join3 } from "node:path";
|
|
@@ -2379,7 +2419,7 @@ function bumpGeneration() {
|
|
|
2379
2419
|
}
|
|
2380
2420
|
|
|
2381
2421
|
// vendor/chrome-devtools-axi/src/run.ts
|
|
2382
|
-
import { mkdtempSync, writeFileSync as writeFileSync3, unlinkSync as
|
|
2422
|
+
import { mkdtempSync, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, rmdirSync } from "node:fs";
|
|
2383
2423
|
import { join as join4 } from "node:path";
|
|
2384
2424
|
import { tmpdir } from "node:os";
|
|
2385
2425
|
import { pathToFileURL } from "node:url";
|
|
@@ -2786,7 +2826,7 @@ async function runScript(content, callTool2) {
|
|
|
2786
2826
|
globalThis.page = prevPage;
|
|
2787
2827
|
}
|
|
2788
2828
|
try {
|
|
2789
|
-
|
|
2829
|
+
unlinkSync3(tmpFile);
|
|
2790
2830
|
rmdirSync(tmpDir);
|
|
2791
2831
|
} catch {
|
|
2792
2832
|
}
|