@cydm/pie 2.6.12 → 2.7.1
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 +85 -3
- package/dist/builtin/extensions/ask-user/index.js +3 -3
- package/dist/builtin/extensions/plan-mode/index.js +130 -21
- package/dist/builtin/extensions/subagent/index.js +15 -6
- package/dist/builtin/extensions/todo/index.js +3 -3
- package/dist/builtin/skills/computer-use-axi/SKILL.md +172 -0
- package/dist/builtin/{extensions/computer-use/index.js → skills/computer-use-axi/computer-use-axi-broker.js} +2122 -708
- package/dist/builtin/skills/computer-use-axi/computer-use-axi-cli.js +37 -0
- package/dist/builtin/skills/computer-use-axi/computer-use-axi-runtime.js +728 -0
- package/dist/builtin/skills/computer-use-axi/package.json +11 -0
- package/dist/chunks/{chunk-PPEWRUMQ.js → chunk-M44CWVDZ.js} +1249 -752
- package/dist/chunks/{chunk-HHNOENEH.js → chunk-NJFPBU36.js} +14 -1
- package/dist/chunks/{chunk-AAEBH6KX.js → chunk-W74NRSRV.js} +239 -231
- package/dist/chunks/{chunk-7B4IIBVS.js → chunk-XUROMYTT.js} +1747 -14
- package/dist/chunks/{src-OWQI5M6W.js → src-C4KA4G75.js} +2 -2
- package/dist/chunks/{test-stream-Y7WXELAJ.js → test-stream-A5ZELKNG.js} +1 -1
- package/dist/cli.js +863 -305
- package/models.schema.json +57 -0
- package/package.json +3 -3
- package/dist/builtin/extensions/computer-use/package.json +0 -14
- package/dist/builtin/extensions/computer-use/scripts/build-native.mjs +0 -278
- package/dist/builtin/extensions/computer-use/scripts/test-native-retention.mjs +0 -204
- package/dist/chunks/chunk-5353DEEC.js +0 -130
- package/dist/chunks/webapi-MBCKO7ZV.js +0 -2481
- package/dist/code-intel-worker.cjs +0 -211227
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/linux/arm64/linux-bridge +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/linux/x64/linux-bridge +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/arm64/pie-computer-use +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/Info.plist +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/MacOS/pie-computer-use +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/_CodeSignature/CodeResources +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/x64/pie-computer-use +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/windows/windows-bridge.exe +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/scripts/helper-path.mjs +0 -0
- /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/scripts/setup-helper.mjs +0 -0
|
@@ -1,15 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
10
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
11
|
+
}) : x)(function(x) {
|
|
12
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
13
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
14
|
+
});
|
|
15
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
16
|
+
try {
|
|
17
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
throw mod = 0, e;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var __copyProps = (to, from, except, desc) => {
|
|
23
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
24
|
+
for (let key of __getOwnPropNames(from))
|
|
25
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
26
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
27
|
+
}
|
|
28
|
+
return to;
|
|
29
|
+
};
|
|
30
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
31
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
32
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
33
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
34
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
35
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
36
|
+
mod
|
|
37
|
+
));
|
|
13
38
|
|
|
14
39
|
// ../../node_modules/ws/lib/constants.js
|
|
15
40
|
var require_constants = __commonJS({
|
|
@@ -2248,9 +2273,9 @@ var require_websocket = __commonJS({
|
|
|
2248
2273
|
var EventEmitter = __require("events");
|
|
2249
2274
|
var https = __require("https");
|
|
2250
2275
|
var http = __require("http");
|
|
2251
|
-
var
|
|
2276
|
+
var net4 = __require("net");
|
|
2252
2277
|
var tls = __require("tls");
|
|
2253
|
-
var { randomBytes, createHash } = __require("crypto");
|
|
2278
|
+
var { randomBytes, createHash: createHash4 } = __require("crypto");
|
|
2254
2279
|
var { Duplex, Readable } = __require("stream");
|
|
2255
2280
|
var { URL: URL2 } = __require("url");
|
|
2256
2281
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -2931,7 +2956,7 @@ var require_websocket = __commonJS({
|
|
|
2931
2956
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
2932
2957
|
return;
|
|
2933
2958
|
}
|
|
2934
|
-
const digest =
|
|
2959
|
+
const digest = createHash4("sha1").update(key + GUID).digest("base64");
|
|
2935
2960
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
2936
2961
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
2937
2962
|
return;
|
|
@@ -3005,12 +3030,12 @@ var require_websocket = __commonJS({
|
|
|
3005
3030
|
}
|
|
3006
3031
|
function netConnect(options) {
|
|
3007
3032
|
options.path = options.socketPath;
|
|
3008
|
-
return
|
|
3033
|
+
return net4.connect(options);
|
|
3009
3034
|
}
|
|
3010
3035
|
function tlsConnect(options) {
|
|
3011
3036
|
options.path = void 0;
|
|
3012
3037
|
if (!options.servername && options.servername !== "") {
|
|
3013
|
-
options.servername =
|
|
3038
|
+
options.servername = net4.isIP(options.host) ? "" : options.host;
|
|
3014
3039
|
}
|
|
3015
3040
|
return tls.connect(options);
|
|
3016
3041
|
}
|
|
@@ -3300,7 +3325,7 @@ var require_websocket_server = __commonJS({
|
|
|
3300
3325
|
var EventEmitter = __require("events");
|
|
3301
3326
|
var http = __require("http");
|
|
3302
3327
|
var { Duplex } = __require("stream");
|
|
3303
|
-
var { createHash } = __require("crypto");
|
|
3328
|
+
var { createHash: createHash4 } = __require("crypto");
|
|
3304
3329
|
var extension2 = require_extension();
|
|
3305
3330
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
3306
3331
|
var subprotocol2 = require_subprotocol();
|
|
@@ -3607,7 +3632,7 @@ var require_websocket_server = __commonJS({
|
|
|
3607
3632
|
);
|
|
3608
3633
|
}
|
|
3609
3634
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
3610
|
-
const digest =
|
|
3635
|
+
const digest = createHash4("sha1").update(key + GUID).digest("base64");
|
|
3611
3636
|
const headers = [
|
|
3612
3637
|
"HTTP/1.1 101 Switching Protocols",
|
|
3613
3638
|
"Upgrade: websocket",
|
|
@@ -3694,16 +3719,834 @@ var require_websocket_server = __commonJS({
|
|
|
3694
3719
|
}
|
|
3695
3720
|
});
|
|
3696
3721
|
|
|
3697
|
-
//
|
|
3722
|
+
// vendor/computer-use-axi/src/broker-server.ts
|
|
3723
|
+
import { createHash as createHash3, randomUUID as randomUUID8 } from "node:crypto";
|
|
3724
|
+
import { chmodSync as chmodSync2, lstatSync as lstatSync4, readFileSync as readFileSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
3725
|
+
import net3 from "node:net";
|
|
3726
|
+
import path14 from "node:path";
|
|
3727
|
+
|
|
3728
|
+
// vendor/computer-use-axi/src/artifact-store.ts
|
|
3729
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
3730
|
+
import { closeSync, existsSync as existsSync2, lstatSync as lstatSync3, mkdirSync as mkdirSync4, openSync, readdirSync as readdirSync2, rmdirSync, unlinkSync as unlinkSync2, writeSync } from "node:fs";
|
|
3731
|
+
import path3 from "node:path";
|
|
3732
|
+
|
|
3733
|
+
// vendor/computer-use-axi/src/broker-protocol.ts
|
|
3734
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3735
|
+
import { chmodSync, lstatSync as lstatSync2, mkdirSync as mkdirSync3 } from "node:fs";
|
|
3736
|
+
import path2 from "node:path";
|
|
3737
|
+
|
|
3738
|
+
// vendor/computer-use-axi/src/persist.ts
|
|
3739
|
+
import { lstatSync, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "node:fs";
|
|
3740
|
+
import path from "node:path";
|
|
3741
|
+
|
|
3742
|
+
// src/config.ts
|
|
3743
|
+
import { existsSync, mkdirSync, readFileSync, renameSync } from "fs";
|
|
3744
|
+
import { homedir } from "os";
|
|
3745
|
+
import { dirname, join } from "path";
|
|
3746
|
+
import { fileURLToPath } from "url";
|
|
3747
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
3748
|
+
var __dirname = dirname(__filename);
|
|
3749
|
+
var isBunRuntime = !!process.versions.bun;
|
|
3750
|
+
function getPackageDir() {
|
|
3751
|
+
const envDir = process.env.PIE_PACKAGE_DIR;
|
|
3752
|
+
if (envDir) {
|
|
3753
|
+
if (envDir === "~") return homedir();
|
|
3754
|
+
if (envDir.startsWith("~/")) return homedir() + envDir.slice(1);
|
|
3755
|
+
return envDir;
|
|
3756
|
+
}
|
|
3757
|
+
let dir = __dirname;
|
|
3758
|
+
while (dir !== dirname(dir)) {
|
|
3759
|
+
if (existsSync(join(dir, "package.json"))) {
|
|
3760
|
+
return dir;
|
|
3761
|
+
}
|
|
3762
|
+
dir = dirname(dir);
|
|
3763
|
+
}
|
|
3764
|
+
return __dirname;
|
|
3765
|
+
}
|
|
3766
|
+
function getPackageJsonPath() {
|
|
3767
|
+
return join(getPackageDir(), "package.json");
|
|
3768
|
+
}
|
|
3769
|
+
var pkg = JSON.parse(readFileSync(getPackageJsonPath(), "utf-8"));
|
|
3770
|
+
var CONFIG_DIR_NAME = ".pie";
|
|
3771
|
+
var VERSION = pkg.version;
|
|
3772
|
+
var ENV_AGENT_DIR = "PIE_CODING_AGENT_DIR";
|
|
3773
|
+
function getConfigDir() {
|
|
3774
|
+
const envDir = process.env[ENV_AGENT_DIR];
|
|
3775
|
+
if (envDir) {
|
|
3776
|
+
if (envDir === "~") return homedir();
|
|
3777
|
+
if (envDir.startsWith("~/")) return homedir() + envDir.slice(1);
|
|
3778
|
+
return envDir;
|
|
3779
|
+
}
|
|
3780
|
+
return join(homedir(), CONFIG_DIR_NAME);
|
|
3781
|
+
}
|
|
3782
|
+
|
|
3783
|
+
// vendor/computer-use-axi/src/persist.ts
|
|
3784
|
+
function stateRootDir(env = process.env) {
|
|
3785
|
+
const override = env.PIE_COMPUTER_USE_STATE_DIR?.trim();
|
|
3786
|
+
if (override) return override;
|
|
3787
|
+
return path.join(getConfigDir(), "computer-use");
|
|
3788
|
+
}
|
|
3789
|
+
function statesDir(env = process.env) {
|
|
3790
|
+
return path.join(stateRootDir(env), "states");
|
|
3791
|
+
}
|
|
3792
|
+
function outputsDir(env = process.env) {
|
|
3793
|
+
return path.join(stateRootDir(env), "outputs");
|
|
3794
|
+
}
|
|
3795
|
+
function runtimeStatePath(env = process.env) {
|
|
3796
|
+
return path.join(stateRootDir(env), "runtime.json");
|
|
3797
|
+
}
|
|
3798
|
+
function brokerStateDir(env = process.env) {
|
|
3799
|
+
return path.join(stateRootDir(env), "broker");
|
|
3800
|
+
}
|
|
3801
|
+
function brokerManifestPath(env = process.env) {
|
|
3802
|
+
return path.join(brokerStateDir(env), "manifest.json");
|
|
3803
|
+
}
|
|
3804
|
+
function loadBrokerManifest(env = process.env) {
|
|
3805
|
+
const manifest = readJsonFile(brokerManifestPath(env));
|
|
3806
|
+
if (!manifest || typeof manifest.generation !== "string") return void 0;
|
|
3807
|
+
return {
|
|
3808
|
+
generation: manifest.generation,
|
|
3809
|
+
helperGeneration: typeof manifest.helperGeneration === "string" ? manifest.helperGeneration : "",
|
|
3810
|
+
stateIds: Array.isArray(manifest.stateIds) ? manifest.stateIds.filter((id) => typeof id === "string" && /^[A-Za-z0-9-]{1,128}$/.test(id)).slice(0, 256) : [],
|
|
3811
|
+
updatedAt: Number.isFinite(manifest.updatedAt) ? manifest.updatedAt : 0
|
|
3812
|
+
};
|
|
3813
|
+
}
|
|
3814
|
+
function saveBrokerManifest(manifest, env = process.env) {
|
|
3815
|
+
mkdirSync2(brokerStateDir(env), { recursive: true, mode: 448 });
|
|
3816
|
+
writeJsonAtomic(brokerManifestPath(env), { ...manifest, stateIds: manifest.stateIds.slice(0, 256) });
|
|
3817
|
+
}
|
|
3818
|
+
function brokerDrainMarkerPath(env = process.env) {
|
|
3819
|
+
return path.join(brokerStateDir(env), "draining.json");
|
|
3820
|
+
}
|
|
3821
|
+
function loadDrainMarker(env = process.env) {
|
|
3822
|
+
const marker = readJsonFile(brokerDrainMarkerPath(env));
|
|
3823
|
+
if (!marker || typeof marker !== "object" || !Number.isFinite(marker.pid)) return void 0;
|
|
3824
|
+
return {
|
|
3825
|
+
pid: Math.trunc(marker.pid),
|
|
3826
|
+
startedAt: Number.isFinite(marker.startedAt) ? marker.startedAt : 0,
|
|
3827
|
+
deadlineAt: Number.isFinite(marker.deadlineAt) ? marker.deadlineAt : 0
|
|
3828
|
+
};
|
|
3829
|
+
}
|
|
3830
|
+
function saveDrainMarker(marker, env = process.env) {
|
|
3831
|
+
mkdirSync2(brokerStateDir(env), { recursive: true, mode: 448 });
|
|
3832
|
+
writeJsonAtomic(brokerDrainMarkerPath(env), marker);
|
|
3833
|
+
}
|
|
3834
|
+
function clearDrainMarker(env = process.env) {
|
|
3835
|
+
try {
|
|
3836
|
+
const stats = lstatSync(brokerDrainMarkerPath(env));
|
|
3837
|
+
if (stats.isFile() && !stats.isSymbolicLink()) unlinkSync(brokerDrainMarkerPath(env));
|
|
3838
|
+
} catch {
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
function cleanupLegacyBrokerCache(env = process.env) {
|
|
3842
|
+
let runtimeFile = false;
|
|
3843
|
+
let states = 0;
|
|
3844
|
+
let outputs2 = 0;
|
|
3845
|
+
const runtime = runtimeStatePath(env);
|
|
3846
|
+
try {
|
|
3847
|
+
const stats = lstatSync(runtime);
|
|
3848
|
+
if (stats.isFile() && !stats.isSymbolicLink()) {
|
|
3849
|
+
unlinkSync(runtime);
|
|
3850
|
+
runtimeFile = true;
|
|
3851
|
+
}
|
|
3852
|
+
} catch {
|
|
3853
|
+
}
|
|
3854
|
+
const statesDirPath = statesDir(env);
|
|
3855
|
+
try {
|
|
3856
|
+
for (const name of readdirSync(statesDirPath).slice(0, 512)) {
|
|
3857
|
+
if (!/^[A-Za-z0-9-]+\.json$/.test(name)) continue;
|
|
3858
|
+
const target = path.join(statesDirPath, name);
|
|
3859
|
+
try {
|
|
3860
|
+
const stats = lstatSync(target);
|
|
3861
|
+
if (!stats.isFile() || stats.isSymbolicLink()) continue;
|
|
3862
|
+
unlinkSync(target);
|
|
3863
|
+
states += 1;
|
|
3864
|
+
} catch {
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
} catch {
|
|
3868
|
+
}
|
|
3869
|
+
const oldOutputs = outputsDir(env);
|
|
3870
|
+
try {
|
|
3871
|
+
for (const name of readdirSync(oldOutputs).slice(0, 512)) {
|
|
3872
|
+
if (!/^o\d+\.(txt|meta\.json)$/.test(name)) continue;
|
|
3873
|
+
const target = path.join(oldOutputs, name);
|
|
3874
|
+
try {
|
|
3875
|
+
const stats = lstatSync(target);
|
|
3876
|
+
if (!stats.isFile() || stats.isSymbolicLink()) continue;
|
|
3877
|
+
unlinkSync(target);
|
|
3878
|
+
outputs2 += 1;
|
|
3879
|
+
} catch {
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
} catch {
|
|
3883
|
+
}
|
|
3884
|
+
return { runtimeFile, states, outputs: outputs2 };
|
|
3885
|
+
}
|
|
3886
|
+
function writeJsonAtomic(filePath, value) {
|
|
3887
|
+
mkdirSync2(path.dirname(filePath), { recursive: true });
|
|
3888
|
+
const temp = `${filePath}.${process.pid}.tmp`;
|
|
3889
|
+
writeFileSync(temp, JSON.stringify(value), "utf8");
|
|
3890
|
+
renameSync2(temp, filePath);
|
|
3891
|
+
}
|
|
3892
|
+
function readJsonFile(filePath) {
|
|
3893
|
+
try {
|
|
3894
|
+
return JSON.parse(readFileSync2(filePath, "utf8"));
|
|
3895
|
+
} catch {
|
|
3896
|
+
return void 0;
|
|
3897
|
+
}
|
|
3898
|
+
}
|
|
3899
|
+
|
|
3900
|
+
// src/runtime/computer-use-owner.ts
|
|
3901
|
+
var COMPUTER_USE_STANDALONE_OWNER = "standalone";
|
|
3902
|
+
var OWNER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:@-]{0,255}$/;
|
|
3903
|
+
function normalizeComputerUseOwner(value) {
|
|
3904
|
+
if (typeof value !== "string") return COMPUTER_USE_STANDALONE_OWNER;
|
|
3905
|
+
const trimmed = value.trim();
|
|
3906
|
+
if (!trimmed) return COMPUTER_USE_STANDALONE_OWNER;
|
|
3907
|
+
return OWNER_PATTERN.test(trimmed) ? trimmed : COMPUTER_USE_STANDALONE_OWNER;
|
|
3908
|
+
}
|
|
3909
|
+
|
|
3910
|
+
// vendor/computer-use-axi/src/broker-protocol.ts
|
|
3911
|
+
var BROKER_PROTOCOL_VERSION = 1;
|
|
3912
|
+
var BROKER_ENGINE_VERSION = 1;
|
|
3913
|
+
var BROKER_PACKAGE_VERSION = "0.5.1-pie.1";
|
|
3914
|
+
var BROKER_MAX_FRAME_BYTES = 4 * 1024 * 1024;
|
|
3915
|
+
var BROKER_MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
3916
|
+
var BROKER_MAX_QUEUED_REQUESTS = 64;
|
|
3917
|
+
var BROKER_MAX_CONCURRENT_REQUESTS = 16;
|
|
3918
|
+
var BROKER_DEFAULT_REQUEST_BUDGET_MS = 12e4;
|
|
3919
|
+
var BROKER_MAX_REQUEST_BUDGET_MS = 15 * 6e4;
|
|
3920
|
+
var BROKER_IDLE_EXIT_MS = 30 * 6e4;
|
|
3921
|
+
var BROKER_DRAIN_MAX_MS = 3e4;
|
|
3922
|
+
var ENGINE_COMMANDS = [
|
|
3923
|
+
"find_roots",
|
|
3924
|
+
"observe_ui",
|
|
3925
|
+
"search_ui",
|
|
3926
|
+
"expand_ui",
|
|
3927
|
+
"inspect_ui",
|
|
3928
|
+
"read_text",
|
|
3929
|
+
"wait_for",
|
|
3930
|
+
"act_ui",
|
|
3931
|
+
"launch_browser",
|
|
3932
|
+
"navigate_browser",
|
|
3933
|
+
"evaluate_browser"
|
|
3934
|
+
];
|
|
3935
|
+
var CONTROL_COMMANDS = ["status", "stop", "debug_restart_helper"];
|
|
3936
|
+
function isEngineCommand(value) {
|
|
3937
|
+
return ENGINE_COMMANDS.includes(value);
|
|
3938
|
+
}
|
|
3939
|
+
function isControlCommand(value) {
|
|
3940
|
+
return CONTROL_COMMANDS.includes(value);
|
|
3941
|
+
}
|
|
3942
|
+
function computeBuildIdentity() {
|
|
3943
|
+
const seed = `${BROKER_PROTOCOL_VERSION}:${BROKER_ENGINE_VERSION}:${BROKER_PACKAGE_VERSION}`;
|
|
3944
|
+
return {
|
|
3945
|
+
version: BROKER_PACKAGE_VERSION,
|
|
3946
|
+
identity: createHash("sha256").update(seed).digest("hex").slice(0, 16)
|
|
3947
|
+
};
|
|
3948
|
+
}
|
|
3949
|
+
var BROKER_BUILD = computeBuildIdentity();
|
|
3950
|
+
var BrokerProtocolError = class extends Error {
|
|
3951
|
+
constructor(code, message) {
|
|
3952
|
+
super(message);
|
|
3953
|
+
this.code = code;
|
|
3954
|
+
this.name = "BrokerProtocolError";
|
|
3955
|
+
}
|
|
3956
|
+
code;
|
|
3957
|
+
};
|
|
3958
|
+
function isRecord(value) {
|
|
3959
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3960
|
+
}
|
|
3961
|
+
function assertPlainRecord(value, label2) {
|
|
3962
|
+
if (!isRecord(value)) throw new BrokerProtocolError("invalid_frame", `${label2} must be an object.`);
|
|
3963
|
+
for (const key of Object.keys(value)) {
|
|
3964
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
3965
|
+
throw new BrokerProtocolError("invalid_frame", `${label2} contains a forbidden key '${key}'.`);
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
var REQUEST_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
3970
|
+
var STATE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9-]{0,127}$/;
|
|
3971
|
+
var ROOT_REF_PATTERN = /^@r\d{1,6}$/;
|
|
3972
|
+
var ELEMENT_REF_PATTERN = /^@e\d{1,6}$/;
|
|
3973
|
+
var OUTPUT_REF_PATTERN = /^@o\d{1,9}$/;
|
|
3974
|
+
function boundedString(value, label2, maxBytes) {
|
|
3975
|
+
if (typeof value !== "string") throw new BrokerProtocolError("invalid_params", `${label2} must be a string.`);
|
|
3976
|
+
if (Buffer.byteLength(value, "utf8") > maxBytes) throw new BrokerProtocolError("invalid_params", `${label2} is too large.`);
|
|
3977
|
+
return value;
|
|
3978
|
+
}
|
|
3979
|
+
function boundedNumber(value, label2, min, max) {
|
|
3980
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new BrokerProtocolError("invalid_params", `${label2} must be a finite number.`);
|
|
3981
|
+
if (value < min || value > max) throw new BrokerProtocolError("invalid_params", `${label2} is out of range.`);
|
|
3982
|
+
return value;
|
|
3983
|
+
}
|
|
3984
|
+
function oneOf(value, label2, allowed) {
|
|
3985
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
3986
|
+
throw new BrokerProtocolError("invalid_params", `${label2} must be one of ${allowed.join(", ")}.`);
|
|
3987
|
+
}
|
|
3988
|
+
return value;
|
|
3989
|
+
}
|
|
3990
|
+
function assertKeys(value, label2, allowed) {
|
|
3991
|
+
for (const key of Object.keys(value)) {
|
|
3992
|
+
if (!allowed.includes(key)) throw new BrokerProtocolError("invalid_params", `${label2} has unknown field '${key}'.`);
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
function stateId(value, label2 = "stateId") {
|
|
3996
|
+
if (typeof value !== "string" || !STATE_ID_PATTERN.test(value)) {
|
|
3997
|
+
throw new BrokerProtocolError("invalid_params", `${label2} is not a valid state id.`);
|
|
3998
|
+
}
|
|
3999
|
+
return value;
|
|
4000
|
+
}
|
|
4001
|
+
function elementOrOutputRef(value, label2) {
|
|
4002
|
+
if (typeof value !== "string" || !ELEMENT_REF_PATTERN.test(value) && !OUTPUT_REF_PATTERN.test(value)) {
|
|
4003
|
+
throw new BrokerProtocolError("invalid_params", `${label2} must be an @e or @o ref.`);
|
|
4004
|
+
}
|
|
4005
|
+
return value;
|
|
4006
|
+
}
|
|
4007
|
+
function elementRef(value, label2) {
|
|
4008
|
+
if (typeof value !== "string" || !ELEMENT_REF_PATTERN.test(value)) {
|
|
4009
|
+
throw new BrokerProtocolError("invalid_params", `${label2} must be an @e ref.`);
|
|
4010
|
+
}
|
|
4011
|
+
return value;
|
|
4012
|
+
}
|
|
4013
|
+
function httpUrl(value, label2) {
|
|
4014
|
+
const url = boundedString(value, label2, 2048);
|
|
4015
|
+
if (!/^https?:\/\//i.test(url)) throw new BrokerProtocolError("invalid_params", `${label2} must be an absolute HTTP(S) URL.`);
|
|
4016
|
+
return url;
|
|
4017
|
+
}
|
|
4018
|
+
function validateCondition(value, label2) {
|
|
4019
|
+
assertPlainRecord(value, label2);
|
|
4020
|
+
assertKeys(value, label2, ["ref", "scopeRef", "text", "role", "value", "until", "timeoutMs"]);
|
|
4021
|
+
const condition = {};
|
|
4022
|
+
if (value.ref !== void 0) condition.ref = elementRef(value.ref, `${label2}.ref`);
|
|
4023
|
+
if (value.scopeRef !== void 0) condition.scopeRef = elementRef(value.scopeRef, `${label2}.scopeRef`);
|
|
4024
|
+
if (value.text !== void 0) condition.text = boundedString(value.text, `${label2}.text`, 8192);
|
|
4025
|
+
if (value.role !== void 0) condition.role = boundedString(value.role, `${label2}.role`, 256);
|
|
4026
|
+
if (value.value !== void 0) condition.value = boundedString(value.value, `${label2}.value`, 8192);
|
|
4027
|
+
if (value.until !== void 0) condition.until = oneOf(value.until, `${label2}.until`, ["present", "absent"]);
|
|
4028
|
+
if (value.timeoutMs !== void 0) condition.timeoutMs = boundedNumber(value.timeoutMs, `${label2}.timeoutMs`, 1, 6e4);
|
|
4029
|
+
return condition;
|
|
4030
|
+
}
|
|
4031
|
+
function validateAction(value, index) {
|
|
4032
|
+
const label2 = `actions[${index}]`;
|
|
4033
|
+
assertPlainRecord(value, label2);
|
|
4034
|
+
assertKeys(value, label2, ["action", "ref", "x", "y", "text", "keys", "scrollX", "scrollY", "path", "button", "clickCount"]);
|
|
4035
|
+
const action = {
|
|
4036
|
+
action: oneOf(value.action, `${label2}.action`, ["press", "click", "setText", "typeText", "keypress", "scroll", "drag", "moveMouse"])
|
|
4037
|
+
};
|
|
4038
|
+
if (value.ref !== void 0) action.ref = elementRef(value.ref, `${label2}.ref`);
|
|
4039
|
+
if (value.x !== void 0) action.x = boundedNumber(value.x, `${label2}.x`, -1e6, 1e6);
|
|
4040
|
+
if (value.y !== void 0) action.y = boundedNumber(value.y, `${label2}.y`, -1e6, 1e6);
|
|
4041
|
+
if (value.text !== void 0) action.text = boundedString(value.text, `${label2}.text`, 64 * 1024);
|
|
4042
|
+
if (value.keys !== void 0) {
|
|
4043
|
+
if (!Array.isArray(value.keys) || value.keys.length === 0 || value.keys.length > 32) {
|
|
4044
|
+
throw new BrokerProtocolError("invalid_params", `${label2}.keys must be a non-empty array of at most 32 keys.`);
|
|
4045
|
+
}
|
|
4046
|
+
action.keys = value.keys.map((key, keyIndex) => boundedString(key, `${label2}.keys[${keyIndex}]`, 64));
|
|
4047
|
+
}
|
|
4048
|
+
if (value.scrollX !== void 0) action.scrollX = boundedNumber(value.scrollX, `${label2}.scrollX`, -1e6, 1e6);
|
|
4049
|
+
if (value.scrollY !== void 0) action.scrollY = boundedNumber(value.scrollY, `${label2}.scrollY`, -1e6, 1e6);
|
|
4050
|
+
if (value.path !== void 0) {
|
|
4051
|
+
if (!Array.isArray(value.path) || value.path.length > 256) {
|
|
4052
|
+
throw new BrokerProtocolError("invalid_params", `${label2}.path must be an array of at most 256 points.`);
|
|
4053
|
+
}
|
|
4054
|
+
action.path = value.path.map((point, pointIndex) => {
|
|
4055
|
+
if (Array.isArray(point)) {
|
|
4056
|
+
if (point.length !== 2) throw new BrokerProtocolError("invalid_params", `${label2}.path[${pointIndex}] must be [x,y].`);
|
|
4057
|
+
return [boundedNumber(point[0], `${label2}.path[${pointIndex}][0]`, -1e6, 1e6), boundedNumber(point[1], `${label2}.path[${pointIndex}][1]`, -1e6, 1e6)];
|
|
4058
|
+
}
|
|
4059
|
+
assertPlainRecord(point, `${label2}.path[${pointIndex}]`);
|
|
4060
|
+
assertKeys(point, `${label2}.path[${pointIndex}]`, ["x", "y"]);
|
|
4061
|
+
return {
|
|
4062
|
+
x: boundedNumber(point.x, `${label2}.path[${pointIndex}].x`, -1e6, 1e6),
|
|
4063
|
+
y: boundedNumber(point.y, `${label2}.path[${pointIndex}].y`, -1e6, 1e6)
|
|
4064
|
+
};
|
|
4065
|
+
});
|
|
4066
|
+
}
|
|
4067
|
+
if (value.button !== void 0) action.button = oneOf(value.button, `${label2}.button`, ["left", "right", "middle"]);
|
|
4068
|
+
if (value.clickCount !== void 0) action.clickCount = Math.trunc(boundedNumber(value.clickCount, `${label2}.clickCount`, 1, 3));
|
|
4069
|
+
const hasRef = typeof action.ref === "string";
|
|
4070
|
+
const hasX = action.x !== void 0;
|
|
4071
|
+
const hasY = action.y !== void 0;
|
|
4072
|
+
if (hasX !== hasY) throw new BrokerProtocolError("invalid_params", `${label2} coordinates require both x and y.`);
|
|
4073
|
+
const hasPoint = hasX && hasY;
|
|
4074
|
+
if ((action.action === "click" || action.action === "moveMouse") && hasRef === hasPoint) {
|
|
4075
|
+
throw new BrokerProtocolError("invalid_params", `${label2}.${String(action.action)} requires exactly one target: ref or x/y coordinates.`);
|
|
4076
|
+
}
|
|
4077
|
+
if (action.action === "press" && !hasRef) throw new BrokerProtocolError("invalid_params", `${label2}.press requires an actionable ref.`);
|
|
4078
|
+
if (action.action === "keypress" && !Array.isArray(action.keys)) throw new BrokerProtocolError("invalid_params", `${label2}.keypress requires keys.`);
|
|
4079
|
+
if (action.action === "drag" && !Array.isArray(action.path)) throw new BrokerProtocolError("invalid_params", `${label2}.drag requires path.`);
|
|
4080
|
+
if (action.action === "scroll" && (action.scrollX ?? 0) === 0 && (action.scrollY ?? 0) === 0) {
|
|
4081
|
+
throw new BrokerProtocolError("invalid_params", `${label2}.scroll requires a non-zero scrollX or scrollY delta.`);
|
|
4082
|
+
}
|
|
4083
|
+
if ((action.action === "setText" || action.action === "typeText") && typeof action.text !== "string") {
|
|
4084
|
+
throw new BrokerProtocolError("invalid_params", `${label2}.${String(action.action)} requires text.`);
|
|
4085
|
+
}
|
|
4086
|
+
return action;
|
|
4087
|
+
}
|
|
4088
|
+
function validateCommandParams(command, raw) {
|
|
4089
|
+
if (command === "status" || command === "stop") {
|
|
4090
|
+
if (raw === void 0 || raw === null) return {};
|
|
4091
|
+
assertPlainRecord(raw, "params");
|
|
4092
|
+
assertKeys(raw, "params", []);
|
|
4093
|
+
return {};
|
|
4094
|
+
}
|
|
4095
|
+
if (command === "debug_restart_helper") {
|
|
4096
|
+
if (raw === void 0 || raw === null) return {};
|
|
4097
|
+
assertPlainRecord(raw, "params");
|
|
4098
|
+
assertKeys(raw, "params", []);
|
|
4099
|
+
return {};
|
|
4100
|
+
}
|
|
4101
|
+
assertPlainRecord(raw, "params");
|
|
4102
|
+
const params = raw;
|
|
4103
|
+
switch (command) {
|
|
4104
|
+
case "find_roots": {
|
|
4105
|
+
assertKeys(params, "params", ["text", "app", "bundleId", "pid", "kind"]);
|
|
4106
|
+
const out = {};
|
|
4107
|
+
if (params.text !== void 0) out.text = boundedString(params.text, "text", 512);
|
|
4108
|
+
if (params.app !== void 0) out.app = boundedString(params.app, "app", 512);
|
|
4109
|
+
if (params.bundleId !== void 0) out.bundleId = boundedString(params.bundleId, "bundleId", 512);
|
|
4110
|
+
if (params.pid !== void 0) out.pid = Math.trunc(boundedNumber(params.pid, "pid", 1, 2 ** 31 - 1));
|
|
4111
|
+
if (params.kind !== void 0) out.kind = oneOf(params.kind, "kind", ["window", "menu", "sheet", "popover", "dialog", "browser_page"]);
|
|
4112
|
+
return out;
|
|
4113
|
+
}
|
|
4114
|
+
case "observe_ui": {
|
|
4115
|
+
assertKeys(params, "params", ["root", "mode", "readText"]);
|
|
4116
|
+
const out = {};
|
|
4117
|
+
if (params.root !== void 0) {
|
|
4118
|
+
if (typeof params.root !== "string" || !ROOT_REF_PATTERN.test(params.root)) throw new BrokerProtocolError("invalid_params", "root must be an @r ref.");
|
|
4119
|
+
out.root = params.root;
|
|
4120
|
+
}
|
|
4121
|
+
if (params.mode !== void 0) out.mode = oneOf(params.mode, "mode", ["semantic", "visual", "fused"]);
|
|
4122
|
+
if (params.readText !== void 0) out.readText = oneOf(params.readText, "readText", ["auto", "always", "never"]);
|
|
4123
|
+
return out;
|
|
4124
|
+
}
|
|
4125
|
+
case "search_ui": {
|
|
4126
|
+
assertKeys(params, "params", ["stateId", "text", "role", "capability"]);
|
|
4127
|
+
const out = { stateId: stateId(params.stateId) };
|
|
4128
|
+
let matchers = 0;
|
|
4129
|
+
if (params.text !== void 0) {
|
|
4130
|
+
out.text = boundedString(params.text, "text", 512);
|
|
4131
|
+
matchers += 1;
|
|
4132
|
+
}
|
|
4133
|
+
if (params.role !== void 0) {
|
|
4134
|
+
out.role = boundedString(params.role, "role", 128);
|
|
4135
|
+
matchers += 1;
|
|
4136
|
+
}
|
|
4137
|
+
if (params.capability !== void 0) {
|
|
4138
|
+
out.capability = boundedString(params.capability, "capability", 128);
|
|
4139
|
+
matchers += 1;
|
|
4140
|
+
}
|
|
4141
|
+
if (matchers === 0) throw new BrokerProtocolError("invalid_params", "search_ui requires text, role, or capability.");
|
|
4142
|
+
return out;
|
|
4143
|
+
}
|
|
4144
|
+
case "expand_ui": {
|
|
4145
|
+
assertKeys(params, "params", ["stateId", "ref", "depth"]);
|
|
4146
|
+
const out = { stateId: stateId(params.stateId), ref: elementRef(params.ref, "ref") };
|
|
4147
|
+
if (params.depth !== void 0) out.depth = Math.trunc(boundedNumber(params.depth, "depth", 1, 8));
|
|
4148
|
+
return out;
|
|
4149
|
+
}
|
|
4150
|
+
case "inspect_ui": {
|
|
4151
|
+
assertKeys(params, "params", ["stateId", "ref"]);
|
|
4152
|
+
return { stateId: stateId(params.stateId), ref: elementRef(params.ref, "ref") };
|
|
4153
|
+
}
|
|
4154
|
+
case "read_text": {
|
|
4155
|
+
assertKeys(params, "params", ["stateId", "ref", "offset"]);
|
|
4156
|
+
const out = { ref: elementOrOutputRef(params.ref, "ref") };
|
|
4157
|
+
if (typeof out.ref === "string" && out.ref.startsWith("@e")) out.stateId = stateId(params.stateId);
|
|
4158
|
+
else if (params.stateId !== void 0) out.stateId = stateId(params.stateId);
|
|
4159
|
+
if (params.offset !== void 0) out.offset = Math.trunc(boundedNumber(params.offset, "offset", 0, 2 ** 31));
|
|
4160
|
+
return out;
|
|
4161
|
+
}
|
|
4162
|
+
case "wait_for": {
|
|
4163
|
+
assertKeys(params, "params", ["stateId", "text", "role", "value", "ref", "scopeRef", "until", "timeoutMs"]);
|
|
4164
|
+
const out = { stateId: stateId(params.stateId) };
|
|
4165
|
+
if (params.ref !== void 0) out.ref = elementRef(params.ref, "ref");
|
|
4166
|
+
if (params.scopeRef !== void 0) out.scopeRef = elementRef(params.scopeRef, "scopeRef");
|
|
4167
|
+
if (params.text !== void 0) out.text = boundedString(params.text, "text", 8192);
|
|
4168
|
+
if (params.role !== void 0) out.role = boundedString(params.role, "role", 256);
|
|
4169
|
+
if (params.value !== void 0) out.value = boundedString(params.value, "value", 8192);
|
|
4170
|
+
if (params.until !== void 0) out.until = oneOf(params.until, "until", ["present", "absent"]);
|
|
4171
|
+
if (params.timeoutMs !== void 0) out.timeoutMs = boundedNumber(params.timeoutMs, "timeoutMs", 100, 6e4);
|
|
4172
|
+
return out;
|
|
4173
|
+
}
|
|
4174
|
+
case "act_ui": {
|
|
4175
|
+
assertKeys(params, "params", ["stateId", "actions", "expect"]);
|
|
4176
|
+
if (!Array.isArray(params.actions) || params.actions.length === 0 || params.actions.length > 20) {
|
|
4177
|
+
throw new BrokerProtocolError("invalid_params", "actions must be a non-empty array of at most 20 actions.");
|
|
4178
|
+
}
|
|
4179
|
+
const out = {
|
|
4180
|
+
stateId: stateId(params.stateId),
|
|
4181
|
+
actions: params.actions.map((action, index) => validateAction(action, index))
|
|
4182
|
+
};
|
|
4183
|
+
if (params.expect !== void 0) out.expect = validateCondition(params.expect, "expect");
|
|
4184
|
+
return out;
|
|
4185
|
+
}
|
|
4186
|
+
case "launch_browser": {
|
|
4187
|
+
assertKeys(params, "params", ["url"]);
|
|
4188
|
+
const out = {};
|
|
4189
|
+
if (params.url !== void 0) out.url = httpUrl(params.url, "url");
|
|
4190
|
+
return out;
|
|
4191
|
+
}
|
|
4192
|
+
case "navigate_browser": {
|
|
4193
|
+
assertKeys(params, "params", ["stateId", "url"]);
|
|
4194
|
+
return { stateId: stateId(params.stateId), url: httpUrl(params.url, "url") };
|
|
4195
|
+
}
|
|
4196
|
+
case "evaluate_browser": {
|
|
4197
|
+
assertKeys(params, "params", ["stateId", "expression"]);
|
|
4198
|
+
const expression = boundedString(params.expression, "expression", 256 * 1024);
|
|
4199
|
+
if (!expression.trim()) throw new BrokerProtocolError("invalid_params", "expression must be non-empty.");
|
|
4200
|
+
return { stateId: stateId(params.stateId), expression };
|
|
4201
|
+
}
|
|
4202
|
+
default:
|
|
4203
|
+
throw new BrokerProtocolError("unknown_command", `Unknown command '${String(command)}'.`);
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
function validateRequest(value) {
|
|
4207
|
+
assertPlainRecord(value, "request");
|
|
4208
|
+
if (value.type !== "request") throw new BrokerProtocolError("invalid_frame", "Expected a request frame.");
|
|
4209
|
+
const commandCandidate = typeof value.command === "string" ? value.command : "";
|
|
4210
|
+
if (value.protocolVersion !== BROKER_PROTOCOL_VERSION && commandCandidate !== "stop") {
|
|
4211
|
+
throw new BrokerProtocolError("protocol_mismatch", `Protocol mismatch: broker ${BROKER_PROTOCOL_VERSION}, client ${String(value.protocolVersion)}.`);
|
|
4212
|
+
}
|
|
4213
|
+
if (typeof value.requestId !== "string" || !REQUEST_ID_PATTERN.test(value.requestId)) {
|
|
4214
|
+
throw new BrokerProtocolError("invalid_request", "requestId is missing or malformed.");
|
|
4215
|
+
}
|
|
4216
|
+
const ownerId = normalizeComputerUseOwner(value.ownerId);
|
|
4217
|
+
if (typeof value.ownerId === "string" && value.ownerId.trim() && ownerId === COMPUTER_USE_STANDALONE_OWNER && value.ownerId.trim() !== COMPUTER_USE_STANDALONE_OWNER) {
|
|
4218
|
+
throw new BrokerProtocolError("invalid_request", "ownerId is malformed.");
|
|
4219
|
+
}
|
|
4220
|
+
if (!isEngineCommand(commandCandidate) && !isControlCommand(commandCandidate)) {
|
|
4221
|
+
throw new BrokerProtocolError("unknown_command", `Unknown command '${commandCandidate}'.`);
|
|
4222
|
+
}
|
|
4223
|
+
const command = commandCandidate;
|
|
4224
|
+
if (command === "debug_restart_helper" && value.params !== void 0 && value.params !== null) {
|
|
4225
|
+
assertPlainRecord(value.params, "params");
|
|
4226
|
+
}
|
|
4227
|
+
const params = validateCommandParams(command, value.params);
|
|
4228
|
+
const cwd = value.cwd === void 0 ? process.cwd() : boundedString(value.cwd, "cwd", 4096);
|
|
4229
|
+
if (!path2.isAbsolute(cwd)) throw new BrokerProtocolError("invalid_request", "cwd must be absolute.");
|
|
4230
|
+
const budgetMs = value.budgetMs === void 0 ? BROKER_DEFAULT_REQUEST_BUDGET_MS : Math.trunc(boundedNumber(value.budgetMs, "budgetMs", 100, BROKER_MAX_REQUEST_BUDGET_MS));
|
|
4231
|
+
return {
|
|
4232
|
+
requestId: value.requestId,
|
|
4233
|
+
ownerId,
|
|
4234
|
+
executionId: value.executionId === void 0 ? void 0 : boundedString(value.executionId, "executionId", 256),
|
|
4235
|
+
generation: value.generation === void 0 ? void 0 : boundedString(value.generation, "generation", 128),
|
|
4236
|
+
command,
|
|
4237
|
+
params,
|
|
4238
|
+
cwd,
|
|
4239
|
+
budgetMs
|
|
4240
|
+
};
|
|
4241
|
+
}
|
|
4242
|
+
function validateHello(value) {
|
|
4243
|
+
assertPlainRecord(value, "hello");
|
|
4244
|
+
if (value.type !== "hello") throw new BrokerProtocolError("invalid_frame", "Expected a hello frame.");
|
|
4245
|
+
if (value.protocolVersion !== BROKER_PROTOCOL_VERSION) {
|
|
4246
|
+
throw new BrokerProtocolError("protocol_mismatch", `Protocol mismatch: broker ${BROKER_PROTOCOL_VERSION}, client ${String(value.protocolVersion)}.`);
|
|
4247
|
+
}
|
|
4248
|
+
return {
|
|
4249
|
+
type: "hello",
|
|
4250
|
+
protocolVersion: value.protocolVersion,
|
|
4251
|
+
clientVersion: value.clientVersion === void 0 ? void 0 : boundedString(value.clientVersion, "clientVersion", 128)
|
|
4252
|
+
};
|
|
4253
|
+
}
|
|
4254
|
+
function encodeFrame(value, maxBytes = BROKER_MAX_FRAME_BYTES) {
|
|
4255
|
+
let json;
|
|
4256
|
+
try {
|
|
4257
|
+
json = JSON.stringify(value);
|
|
4258
|
+
} catch (error) {
|
|
4259
|
+
throw new BrokerProtocolError("encode_failed", error instanceof Error ? error.message : String(error));
|
|
4260
|
+
}
|
|
4261
|
+
const bytes = Buffer.byteLength(json, "utf8");
|
|
4262
|
+
if (bytes > maxBytes) throw new BrokerProtocolError("frame_too_large", `Frame of ${bytes} bytes exceeds the ${maxBytes}-byte limit.`);
|
|
4263
|
+
return Buffer.from(`${json}
|
|
4264
|
+
`, "utf8");
|
|
4265
|
+
}
|
|
4266
|
+
var FrameDecoder = class {
|
|
4267
|
+
constructor(maxBytes = BROKER_MAX_FRAME_BYTES) {
|
|
4268
|
+
this.maxBytes = maxBytes;
|
|
4269
|
+
}
|
|
4270
|
+
maxBytes;
|
|
4271
|
+
buffer = Buffer.alloc(0);
|
|
4272
|
+
push(chunk) {
|
|
4273
|
+
this.buffer = this.buffer.length === 0 ? chunk : Buffer.concat([this.buffer, chunk]);
|
|
4274
|
+
const frames = [];
|
|
4275
|
+
for (; ; ) {
|
|
4276
|
+
const newline = this.buffer.indexOf(10);
|
|
4277
|
+
if (newline < 0) break;
|
|
4278
|
+
const line = this.buffer.subarray(0, newline).toString("utf8").trim();
|
|
4279
|
+
this.buffer = this.buffer.subarray(newline + 1);
|
|
4280
|
+
if (!line) continue;
|
|
4281
|
+
let parsed;
|
|
4282
|
+
try {
|
|
4283
|
+
parsed = JSON.parse(line);
|
|
4284
|
+
} catch {
|
|
4285
|
+
throw new BrokerProtocolError("invalid_json", "Frame is not valid JSON.");
|
|
4286
|
+
}
|
|
4287
|
+
frames.push(parsed);
|
|
4288
|
+
}
|
|
4289
|
+
if (this.buffer.length > this.maxBytes) {
|
|
4290
|
+
throw new BrokerProtocolError("frame_too_large", `Unterminated frame exceeds the ${this.maxBytes}-byte limit.`);
|
|
4291
|
+
}
|
|
4292
|
+
return frames;
|
|
4293
|
+
}
|
|
4294
|
+
};
|
|
4295
|
+
function resolveBrokerEndpoint(env = process.env) {
|
|
4296
|
+
const socketOverride = env.PIE_COMPUTER_USE_BROKER_SOCKET?.trim();
|
|
4297
|
+
if (process.platform === "win32") {
|
|
4298
|
+
const seed = socketOverride || stateRootDir(env);
|
|
4299
|
+
const key = createHash("sha256").update(seed).digest("hex").slice(0, 16);
|
|
4300
|
+
return { kind: "named-pipe", path: `\\\\.\\pipe\\pie-computer-use-axi-${key}` };
|
|
4301
|
+
}
|
|
4302
|
+
const socketPath = socketOverride || path2.join(stateRootDir(env), "run", "broker.sock");
|
|
4303
|
+
if (Buffer.byteLength(socketPath, "utf8") > 100) {
|
|
4304
|
+
throw new BrokerProtocolError("endpoint_too_long", `Unix socket path is ${Buffer.byteLength(socketPath)} bytes (limit ~104): ${socketPath}. Set PIE_COMPUTER_USE_BROKER_SOCKET to a shorter path.`);
|
|
4305
|
+
}
|
|
4306
|
+
return { kind: "unix-socket", path: socketPath, directory: path2.dirname(socketPath) };
|
|
4307
|
+
}
|
|
4308
|
+
function ensurePrivateDirectory(directory) {
|
|
4309
|
+
mkdirSync3(directory, { recursive: true, mode: 448 });
|
|
4310
|
+
const stats = lstatSync2(directory);
|
|
4311
|
+
if (stats.isSymbolicLink()) throw new BrokerProtocolError("unsafe_path", `Refusing symlinked private directory: ${directory}`);
|
|
4312
|
+
if (!stats.isDirectory()) throw new BrokerProtocolError("unsafe_path", `Not a directory: ${directory}`);
|
|
4313
|
+
if (process.platform !== "win32") chmodSync(directory, 448);
|
|
4314
|
+
}
|
|
4315
|
+
function canonicalize(value) {
|
|
4316
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
4317
|
+
if (value && typeof value === "object") {
|
|
4318
|
+
const record = value;
|
|
4319
|
+
const sorted = {};
|
|
4320
|
+
for (const key of Object.keys(record).sort()) sorted[key] = canonicalize(record[key]);
|
|
4321
|
+
return sorted;
|
|
4322
|
+
}
|
|
4323
|
+
return value;
|
|
4324
|
+
}
|
|
4325
|
+
function requestFingerprint(command, params, cwd) {
|
|
4326
|
+
const seed = JSON.stringify({ command, params: canonicalize(params), cwd });
|
|
4327
|
+
return createHash("sha256").update(seed).digest("hex").slice(0, 32);
|
|
4328
|
+
}
|
|
4329
|
+
|
|
4330
|
+
// vendor/computer-use-axi/src/artifact-store.ts
|
|
4331
|
+
var SAFE_COMPONENT = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
4332
|
+
function safeComponent(value, label2) {
|
|
4333
|
+
if (!SAFE_COMPONENT.test(value)) throw new Error(`Unsafe artifact ${label2}: ${value}`);
|
|
4334
|
+
return value;
|
|
4335
|
+
}
|
|
4336
|
+
function extensionFor(mimeType) {
|
|
4337
|
+
return mimeType === "image/png" ? "png" : "jpg";
|
|
4338
|
+
}
|
|
4339
|
+
var ArtifactStore = class {
|
|
4340
|
+
constructor(options) {
|
|
4341
|
+
this.options = options;
|
|
4342
|
+
this.maxCount = options.maxCount ?? 128;
|
|
4343
|
+
this.maxBytes = options.maxBytes ?? 128 * 1024 * 1024;
|
|
4344
|
+
this.maxAgeMs = options.maxAgeMs ?? 30 * 6e4;
|
|
4345
|
+
this.now = options.now ?? Date.now;
|
|
4346
|
+
}
|
|
4347
|
+
options;
|
|
4348
|
+
records = /* @__PURE__ */ new Map();
|
|
4349
|
+
pins = /* @__PURE__ */ new Map();
|
|
4350
|
+
maxCount;
|
|
4351
|
+
maxBytes;
|
|
4352
|
+
maxAgeMs;
|
|
4353
|
+
now;
|
|
4354
|
+
totalBytes = 0;
|
|
4355
|
+
/** Absolute path for one owner/generation, created 0700 and symlink-checked. */
|
|
4356
|
+
directoryFor(owner, generation = this.options.generation) {
|
|
4357
|
+
const root = path3.join(this.options.root, safeComponent(generation, "generation"));
|
|
4358
|
+
const ownerDir = path3.join(root, safeComponent(normalizeComputerUseOwner(owner), "owner"));
|
|
4359
|
+
ensurePrivateDirectory(this.options.root);
|
|
4360
|
+
ensurePrivateDirectory(root);
|
|
4361
|
+
ensurePrivateDirectory(ownerDir);
|
|
4362
|
+
return ownerDir;
|
|
4363
|
+
}
|
|
4364
|
+
put(input) {
|
|
4365
|
+
const generation = input.generation ?? this.options.generation;
|
|
4366
|
+
const directory = this.directoryFor(input.owner, generation);
|
|
4367
|
+
const artifactId = randomUUID2();
|
|
4368
|
+
const absolutePath = path3.join(directory, `${artifactId}.${extensionFor(input.mimeType)}`);
|
|
4369
|
+
const fd = openSync(absolutePath, "wx", 384);
|
|
4370
|
+
try {
|
|
4371
|
+
let offset = 0;
|
|
4372
|
+
while (offset < input.bytes.byteLength) {
|
|
4373
|
+
offset += writeSync(fd, input.bytes, offset, input.bytes.byteLength - offset);
|
|
4374
|
+
}
|
|
4375
|
+
} finally {
|
|
4376
|
+
closeSync(fd);
|
|
4377
|
+
}
|
|
4378
|
+
const record = {
|
|
4379
|
+
artifactId,
|
|
4380
|
+
stateId: input.stateId,
|
|
4381
|
+
owner: normalizeComputerUseOwner(input.owner),
|
|
4382
|
+
generation,
|
|
4383
|
+
mimeType: input.mimeType,
|
|
4384
|
+
width: Math.max(1, Math.trunc(input.width)),
|
|
4385
|
+
height: Math.max(1, Math.trunc(input.height)),
|
|
4386
|
+
hash: createHash2("sha256").update(input.bytes).digest("hex"),
|
|
4387
|
+
bytes: input.bytes.byteLength,
|
|
4388
|
+
absolutePath,
|
|
4389
|
+
createdAt: this.now()
|
|
4390
|
+
};
|
|
4391
|
+
this.records.set(artifactId, record);
|
|
4392
|
+
this.totalBytes += record.bytes;
|
|
4393
|
+
this.pin(artifactId);
|
|
4394
|
+
this.sweep();
|
|
4395
|
+
return record;
|
|
4396
|
+
}
|
|
4397
|
+
pin(artifactId) {
|
|
4398
|
+
this.pins.set(artifactId, (this.pins.get(artifactId) ?? 0) + 1);
|
|
4399
|
+
}
|
|
4400
|
+
unpin(artifactId) {
|
|
4401
|
+
const current = this.pins.get(artifactId) ?? 0;
|
|
4402
|
+
if (current <= 1) this.pins.delete(artifactId);
|
|
4403
|
+
else this.pins.set(artifactId, current - 1);
|
|
4404
|
+
this.sweep();
|
|
4405
|
+
}
|
|
4406
|
+
get(artifactId) {
|
|
4407
|
+
return this.records.get(artifactId);
|
|
4408
|
+
}
|
|
4409
|
+
/** Delete all artifacts belonging to one state (state eviction). */
|
|
4410
|
+
dropForState(stateId2) {
|
|
4411
|
+
let removed = 0;
|
|
4412
|
+
for (const record of [...this.records.values()]) {
|
|
4413
|
+
if (record.stateId !== stateId2) continue;
|
|
4414
|
+
this.remove(record);
|
|
4415
|
+
removed += 1;
|
|
4416
|
+
}
|
|
4417
|
+
return removed;
|
|
4418
|
+
}
|
|
4419
|
+
stats() {
|
|
4420
|
+
return { count: this.records.size, bytes: this.totalBytes, pinned: this.pins.size, maxCount: this.maxCount, maxBytes: this.maxBytes };
|
|
4421
|
+
}
|
|
4422
|
+
/** Drop expired entries first, then oldest until count/byte bounds hold. */
|
|
4423
|
+
sweep() {
|
|
4424
|
+
const now = this.now();
|
|
4425
|
+
for (const record of [...this.records.values()]) {
|
|
4426
|
+
if (this.isPinned(record.artifactId)) continue;
|
|
4427
|
+
if (now - record.createdAt <= this.maxAgeMs) continue;
|
|
4428
|
+
this.remove(record);
|
|
4429
|
+
this.options.onEvict?.(record.stateId, "age");
|
|
4430
|
+
}
|
|
4431
|
+
const evictOldest = (reason) => {
|
|
4432
|
+
for (const record of this.records.values()) {
|
|
4433
|
+
if (this.isPinned(record.artifactId)) continue;
|
|
4434
|
+
this.remove(record);
|
|
4435
|
+
this.options.onEvict?.(record.stateId, reason);
|
|
4436
|
+
return true;
|
|
4437
|
+
}
|
|
4438
|
+
return false;
|
|
4439
|
+
};
|
|
4440
|
+
while (this.records.size > this.maxCount && evictOldest("count")) {
|
|
4441
|
+
}
|
|
4442
|
+
while (this.totalBytes > this.maxBytes && evictOldest("bytes")) {
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
/** Remove files for a generation that no longer owns the broker. */
|
|
4446
|
+
cleanupGeneration(generation) {
|
|
4447
|
+
if (!SAFE_COMPONENT.test(generation)) return 0;
|
|
4448
|
+
const directory = path3.join(this.options.root, generation);
|
|
4449
|
+
if (!existsSync2(directory)) return 0;
|
|
4450
|
+
const stats = lstatSync3(directory);
|
|
4451
|
+
if (stats.isSymbolicLink() || !stats.isDirectory()) return 0;
|
|
4452
|
+
let removed = 0;
|
|
4453
|
+
for (const name of readdirSync2(directory)) {
|
|
4454
|
+
const ownerDir = path3.join(directory, name);
|
|
4455
|
+
let ownerStats;
|
|
4456
|
+
try {
|
|
4457
|
+
ownerStats = lstatSync3(ownerDir);
|
|
4458
|
+
} catch {
|
|
4459
|
+
continue;
|
|
4460
|
+
}
|
|
4461
|
+
if (ownerStats.isSymbolicLink() || !ownerStats.isDirectory()) continue;
|
|
4462
|
+
for (const file of readdirSync2(ownerDir)) {
|
|
4463
|
+
const target = path3.join(ownerDir, file);
|
|
4464
|
+
let fileStats;
|
|
4465
|
+
try {
|
|
4466
|
+
fileStats = lstatSync3(target);
|
|
4467
|
+
} catch {
|
|
4468
|
+
continue;
|
|
4469
|
+
}
|
|
4470
|
+
if (fileStats.isSymbolicLink() || !fileStats.isFile()) continue;
|
|
4471
|
+
unlinkSync2(target);
|
|
4472
|
+
removed += 1;
|
|
4473
|
+
}
|
|
4474
|
+
try {
|
|
4475
|
+
rmdirSync(ownerDir);
|
|
4476
|
+
} catch {
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
try {
|
|
4480
|
+
rmdirSync(directory);
|
|
4481
|
+
} catch {
|
|
4482
|
+
}
|
|
4483
|
+
return removed;
|
|
4484
|
+
}
|
|
4485
|
+
/**
|
|
4486
|
+
* Remove every generation directory except the current one. A SIGKILLed
|
|
4487
|
+
* broker never gets to clean up after itself, so multiple stale generations
|
|
4488
|
+
* can accumulate; startup sweeps them with a strict bound and refuses to
|
|
4489
|
+
* follow symlinked or unrecognized entries.
|
|
4490
|
+
*/
|
|
4491
|
+
cleanupOtherGenerations(currentGeneration, maxGenerations = 32) {
|
|
4492
|
+
const current = safeComponent(currentGeneration, "generation");
|
|
4493
|
+
let names;
|
|
4494
|
+
try {
|
|
4495
|
+
names = readdirSync2(this.options.root);
|
|
4496
|
+
} catch {
|
|
4497
|
+
return 0;
|
|
4498
|
+
}
|
|
4499
|
+
let removed = 0;
|
|
4500
|
+
let scanned = 0;
|
|
4501
|
+
for (const name of names) {
|
|
4502
|
+
if (scanned >= maxGenerations) break;
|
|
4503
|
+
if (name === current || !SAFE_COMPONENT.test(name)) continue;
|
|
4504
|
+
const directory = path3.join(this.options.root, name);
|
|
4505
|
+
let stats;
|
|
4506
|
+
try {
|
|
4507
|
+
stats = lstatSync3(directory);
|
|
4508
|
+
} catch {
|
|
4509
|
+
continue;
|
|
4510
|
+
}
|
|
4511
|
+
if (stats.isSymbolicLink() || !stats.isDirectory()) continue;
|
|
4512
|
+
scanned += 1;
|
|
4513
|
+
removed += this.cleanupGeneration(name);
|
|
4514
|
+
}
|
|
4515
|
+
return removed;
|
|
4516
|
+
}
|
|
4517
|
+
close() {
|
|
4518
|
+
this.records.clear();
|
|
4519
|
+
this.pins.clear();
|
|
4520
|
+
this.totalBytes = 0;
|
|
4521
|
+
}
|
|
4522
|
+
isPinned(artifactId) {
|
|
4523
|
+
return (this.pins.get(artifactId) ?? 0) > 0;
|
|
4524
|
+
}
|
|
4525
|
+
remove(record) {
|
|
4526
|
+
this.records.delete(record.artifactId);
|
|
4527
|
+
this.totalBytes = Math.max(0, this.totalBytes - record.bytes);
|
|
4528
|
+
try {
|
|
4529
|
+
unlinkSync2(record.absolutePath);
|
|
4530
|
+
} catch {
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
};
|
|
4534
|
+
function artifactsRoot(env = process.env) {
|
|
4535
|
+
const base = env.PIE_COMPUTER_USE_ARTIFACTS_DIR?.trim();
|
|
4536
|
+
if (base) return path3.resolve(base);
|
|
4537
|
+
return path3.join(stateRootDir(env), "artifacts");
|
|
4538
|
+
}
|
|
4539
|
+
|
|
4540
|
+
// vendor/computer-use-axi/src/bridge.ts
|
|
3698
4541
|
import { spawn as spawn4 } from "node:child_process";
|
|
3699
|
-
import { randomUUID as
|
|
3700
|
-
import { constants as fsConstants5, mkdtempSync
|
|
4542
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
4543
|
+
import { constants as fsConstants5, mkdtempSync } from "node:fs";
|
|
3701
4544
|
import { access as access4 } from "node:fs/promises";
|
|
3702
4545
|
import net2 from "node:net";
|
|
3703
|
-
import
|
|
3704
|
-
import
|
|
4546
|
+
import os4 from "node:os";
|
|
4547
|
+
import path13 from "node:path";
|
|
3705
4548
|
|
|
3706
|
-
//
|
|
4549
|
+
// vendor/computer-use-axi/src/platform/coerce.ts
|
|
3707
4550
|
function toBoolean(value) {
|
|
3708
4551
|
return value === true || value === "true" || value === 1;
|
|
3709
4552
|
}
|
|
@@ -3719,7 +4562,7 @@ function toOptionalString(value) {
|
|
|
3719
4562
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3720
4563
|
}
|
|
3721
4564
|
|
|
3722
|
-
//
|
|
4565
|
+
// vendor/computer-use-axi/src/actions.ts
|
|
3723
4566
|
function mouseButton(value) {
|
|
3724
4567
|
return value === "right" || value === "middle" ? value : "left";
|
|
3725
4568
|
}
|
|
@@ -3733,11 +4576,11 @@ function keys(value) {
|
|
|
3733
4576
|
if (!Array.isArray(value) || value.length === 0) throw new Error("keypress.keys must contain at least one key.");
|
|
3734
4577
|
return value.map((key) => String(key));
|
|
3735
4578
|
}
|
|
3736
|
-
function
|
|
4579
|
+
function path4(value, env) {
|
|
3737
4580
|
if (!Array.isArray(value) || value.length < 2) throw new Error("drag.path must contain at least two points.");
|
|
3738
|
-
return value.map((
|
|
3739
|
-
const x = Array.isArray(
|
|
3740
|
-
const y = Array.isArray(
|
|
4581
|
+
return value.map((point, index) => {
|
|
4582
|
+
const x = Array.isArray(point) ? toFiniteNumber(point[0], NaN) : toFiniteNumber(point?.x, NaN);
|
|
4583
|
+
const y = Array.isArray(point) ? toFiniteNumber(point[1], NaN) : toFiniteNumber(point?.y, NaN);
|
|
3741
4584
|
env.validatePoint(x, y, `Drag point ${index + 1}`);
|
|
3742
4585
|
return { x, y };
|
|
3743
4586
|
});
|
|
@@ -3747,17 +4590,17 @@ function nativeTarget(action, operation, env) {
|
|
|
3747
4590
|
const node = env.node(action.ref.trim());
|
|
3748
4591
|
const semanticClick = operation === "click" || operation === "press";
|
|
3749
4592
|
if (semanticClick && node.isTextInput) {
|
|
3750
|
-
const
|
|
3751
|
-
env.validatePoint(
|
|
3752
|
-
return
|
|
4593
|
+
const point2 = env.center(node);
|
|
4594
|
+
env.validatePoint(point2.x, point2.y);
|
|
4595
|
+
return point2;
|
|
3753
4596
|
}
|
|
3754
4597
|
const onlyIncidentalActions = node.actions.every((candidate) => candidate === "AXShowMenu" || candidate === "AXScrollToVisible");
|
|
3755
4598
|
if (node.wireRef && !node.pictureOnly && (!semanticClick || node.canPress || node.canFocus || node.canSetValue || !onlyIncidentalActions)) {
|
|
3756
4599
|
return { ref: node.wireRef };
|
|
3757
4600
|
}
|
|
3758
|
-
const
|
|
3759
|
-
env.validatePoint(
|
|
3760
|
-
return
|
|
4601
|
+
const point = env.center(node);
|
|
4602
|
+
env.validatePoint(point.x, point.y);
|
|
4603
|
+
return point;
|
|
3761
4604
|
}
|
|
3762
4605
|
const x = toFiniteNumber(action.x, NaN);
|
|
3763
4606
|
const y = toFiniteNumber(action.y, NaN);
|
|
@@ -3765,7 +4608,7 @@ function nativeTarget(action, operation, env) {
|
|
|
3765
4608
|
env.validatePoint(x, y);
|
|
3766
4609
|
return { x, y };
|
|
3767
4610
|
}
|
|
3768
|
-
if (operation === "drag" && action.path?.length) return
|
|
4611
|
+
if (operation === "drag" && action.path?.length) return path4(action.path, env)[0];
|
|
3769
4612
|
throw new Error(`${operation} requires either ref or both x and y.`);
|
|
3770
4613
|
}
|
|
3771
4614
|
function focusedTarget(env) {
|
|
@@ -3835,7 +4678,7 @@ function prepareAction(action, state, env) {
|
|
|
3835
4678
|
case "scroll":
|
|
3836
4679
|
return { action: operation, target, params: { scrollX: scrollDelta(action.scrollX), scrollY: scrollDelta(action.scrollY) }, usesCurrentFocus: false, needsForeground: false };
|
|
3837
4680
|
case "drag":
|
|
3838
|
-
return { action: operation, target, params: { path:
|
|
4681
|
+
return { action: operation, target, params: { path: path4(action.path, env) }, usesCurrentFocus: false, needsForeground: false };
|
|
3839
4682
|
case "moveMouse":
|
|
3840
4683
|
return { action: operation, target, params: {}, usesCurrentFocus: false, needsForeground: false };
|
|
3841
4684
|
}
|
|
@@ -3860,8 +4703,8 @@ function outcomeAfterObservedValues(current, actions, valueForRef) {
|
|
|
3860
4703
|
return matches ? "worked" : current;
|
|
3861
4704
|
}
|
|
3862
4705
|
|
|
3863
|
-
//
|
|
3864
|
-
import { randomUUID } from "node:crypto";
|
|
4706
|
+
// vendor/computer-use-axi/src/cdp.ts
|
|
4707
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
3865
4708
|
|
|
3866
4709
|
// ../../node_modules/ws/wrapper.mjs
|
|
3867
4710
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -3874,9 +4717,9 @@ var import_websocket = __toESM(require_websocket(), 1);
|
|
|
3874
4717
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
3875
4718
|
var wrapper_default = import_websocket.default;
|
|
3876
4719
|
|
|
3877
|
-
//
|
|
4720
|
+
// vendor/computer-use-axi/src/outline.ts
|
|
3878
4721
|
var DEFAULT_BUDGET = { maxDepth: 2, maxNodes: 150 };
|
|
3879
|
-
function
|
|
4722
|
+
function isRecord2(value) {
|
|
3880
4723
|
return typeof value === "object" && value !== null;
|
|
3881
4724
|
}
|
|
3882
4725
|
function toString(value) {
|
|
@@ -3894,7 +4737,7 @@ function toBoolean2(value) {
|
|
|
3894
4737
|
return value === true;
|
|
3895
4738
|
}
|
|
3896
4739
|
function parseRect(raw) {
|
|
3897
|
-
const rect =
|
|
4740
|
+
const rect = isRecord2(raw) ? raw : {};
|
|
3898
4741
|
return {
|
|
3899
4742
|
x: toNumber(rect.x),
|
|
3900
4743
|
y: toNumber(rect.y),
|
|
@@ -3905,7 +4748,7 @@ function parseRect(raw) {
|
|
|
3905
4748
|
function parseText(raw) {
|
|
3906
4749
|
if (!Array.isArray(raw)) return [];
|
|
3907
4750
|
return raw.map((item) => {
|
|
3908
|
-
if (!
|
|
4751
|
+
if (!isRecord2(item)) return void 0;
|
|
3909
4752
|
const string = toString(item.string);
|
|
3910
4753
|
if (!string) return void 0;
|
|
3911
4754
|
return {
|
|
@@ -3916,7 +4759,7 @@ function parseText(raw) {
|
|
|
3916
4759
|
}).filter((item) => Boolean(item));
|
|
3917
4760
|
}
|
|
3918
4761
|
function parseNode(raw, parent) {
|
|
3919
|
-
const record =
|
|
4762
|
+
const record = isRecord2(raw) ? raw : {};
|
|
3920
4763
|
const wireRef = toString(record.ref) || void 0;
|
|
3921
4764
|
const node = {
|
|
3922
4765
|
ref: "",
|
|
@@ -3940,7 +4783,7 @@ function parseNode(raw, parent) {
|
|
|
3940
4783
|
offscreen: toBoolean2(record.offscreen),
|
|
3941
4784
|
pictureOnly: toBoolean2(record.pictureOnly),
|
|
3942
4785
|
truncated: toBoolean2(record.truncated),
|
|
3943
|
-
scrollExtent:
|
|
4786
|
+
scrollExtent: isRecord2(record.scrollExtent) ? { seen: Math.max(0, Math.trunc(toNumber(record.scrollExtent.seen))), total: Math.max(0, Math.trunc(toNumber(record.scrollExtent.total))) } : void 0,
|
|
3944
4787
|
text: parseText(record.text),
|
|
3945
4788
|
children: [],
|
|
3946
4789
|
parent
|
|
@@ -3949,12 +4792,12 @@ function parseNode(raw, parent) {
|
|
|
3949
4792
|
return node;
|
|
3950
4793
|
}
|
|
3951
4794
|
function parseLookResponse(raw) {
|
|
3952
|
-
const record =
|
|
3953
|
-
const image =
|
|
3954
|
-
const window =
|
|
3955
|
-
const metadata =
|
|
4795
|
+
const record = isRecord2(raw) ? raw : {};
|
|
4796
|
+
const image = isRecord2(record.image) ? record.image : void 0;
|
|
4797
|
+
const window = isRecord2(record.window) ? record.window : {};
|
|
4798
|
+
const metadata = isRecord2(window.metadata) ? window.metadata : void 0;
|
|
3956
4799
|
const outline = buildOutline(toString(record.lookId), parseNode(record.outline));
|
|
3957
|
-
const readText =
|
|
4800
|
+
const readText = isRecord2(record.readText) ? record.readText : void 0;
|
|
3958
4801
|
const requestedReadText = readText?.requested === "auto" || readText?.requested === "always" || readText?.requested === "never" ? readText.requested : void 0;
|
|
3959
4802
|
const look = {
|
|
3960
4803
|
lookId: toString(record.lookId),
|
|
@@ -3977,7 +4820,7 @@ function parseLookResponse(raw) {
|
|
|
3977
4820
|
height: Math.max(1, Math.trunc(toNumber(image.height, 1)))
|
|
3978
4821
|
} : void 0,
|
|
3979
4822
|
outline: outline.root,
|
|
3980
|
-
timings:
|
|
4823
|
+
timings: isRecord2(record.timings) ? Object.fromEntries(Object.entries(record.timings).map(([key, value]) => [key, toNumber(value)])) : {},
|
|
3981
4824
|
readText: readText ? { requested: requestedReadText, executed: toBoolean2(readText.executed) } : void 0,
|
|
3982
4825
|
parsedOutline: outline
|
|
3983
4826
|
};
|
|
@@ -4336,7 +5179,7 @@ function graftScopedOutline(outline, targetRef, scoped) {
|
|
|
4336
5179
|
return target;
|
|
4337
5180
|
}
|
|
4338
5181
|
|
|
4339
|
-
//
|
|
5182
|
+
// vendor/computer-use-axi/src/cdp.ts
|
|
4340
5183
|
var COMMAND_TIMEOUT_MS = 5e3;
|
|
4341
5184
|
var CDP_CONTEXT_PREFIX = "browser:";
|
|
4342
5185
|
var NAVIGATE_LOAD_TIMEOUT_MS = 1e4;
|
|
@@ -4367,11 +5210,11 @@ var CdpTab = class _CdpTab {
|
|
|
4367
5210
|
const ws = new wrapper_default(wsUrl);
|
|
4368
5211
|
connectingSockets.add(ws);
|
|
4369
5212
|
try {
|
|
4370
|
-
await new Promise((
|
|
5213
|
+
await new Promise((resolve2, reject) => {
|
|
4371
5214
|
const timer = setTimeout(() => reject(new Error(`Timed out connecting to CDP target at ${wsUrl}`)), COMMAND_TIMEOUT_MS);
|
|
4372
5215
|
ws.onopen = () => {
|
|
4373
5216
|
clearTimeout(timer);
|
|
4374
|
-
|
|
5217
|
+
resolve2();
|
|
4375
5218
|
};
|
|
4376
5219
|
ws.onerror = () => {
|
|
4377
5220
|
clearTimeout(timer);
|
|
@@ -4419,14 +5262,14 @@ var CdpTab = class _CdpTab {
|
|
|
4419
5262
|
this.loadFired = void 0;
|
|
4420
5263
|
this.rejectAllPending(new Error("CDP connection closed."));
|
|
4421
5264
|
if (this.ws.readyState === wrapper_default.CLOSED) return;
|
|
4422
|
-
await new Promise((
|
|
5265
|
+
await new Promise((resolve2) => {
|
|
4423
5266
|
const timer = setTimeout(() => {
|
|
4424
5267
|
this.ws.terminate();
|
|
4425
|
-
|
|
5268
|
+
resolve2();
|
|
4426
5269
|
}, 500);
|
|
4427
5270
|
this.ws.once("close", () => {
|
|
4428
5271
|
clearTimeout(timer);
|
|
4429
|
-
|
|
5272
|
+
resolve2();
|
|
4430
5273
|
});
|
|
4431
5274
|
if (this.ws.readyState === wrapper_default.CONNECTING) this.ws.terminate();
|
|
4432
5275
|
else this.ws.close();
|
|
@@ -4446,13 +5289,13 @@ var CdpTab = class _CdpTab {
|
|
|
4446
5289
|
}
|
|
4447
5290
|
async navigate(url) {
|
|
4448
5291
|
let timer;
|
|
4449
|
-
const loaded = new Promise((
|
|
4450
|
-
this.loadFired =
|
|
5292
|
+
const loaded = new Promise((resolve2) => {
|
|
5293
|
+
this.loadFired = resolve2;
|
|
4451
5294
|
});
|
|
4452
5295
|
try {
|
|
4453
5296
|
await this.send("Page.navigate", { url });
|
|
4454
|
-
await Promise.race([loaded, new Promise((
|
|
4455
|
-
timer = setTimeout(
|
|
5297
|
+
await Promise.race([loaded, new Promise((resolve2) => {
|
|
5298
|
+
timer = setTimeout(resolve2, NAVIGATE_LOAD_TIMEOUT_MS);
|
|
4456
5299
|
})]);
|
|
4457
5300
|
} finally {
|
|
4458
5301
|
clearTimeout(timer);
|
|
@@ -4489,11 +5332,11 @@ var CdpTab = class _CdpTab {
|
|
|
4489
5332
|
async mouseAt(x, y, type, button = "left", clickCount2 = 1) {
|
|
4490
5333
|
await this.send("Input.dispatchMouseEvent", { type, x, y, button: type === "mouseMoved" ? "none" : button, clickCount: clickCount2 });
|
|
4491
5334
|
}
|
|
4492
|
-
async dragPath(
|
|
4493
|
-
if (
|
|
4494
|
-
await this.mouseAt(
|
|
4495
|
-
for (const
|
|
4496
|
-
const end =
|
|
5335
|
+
async dragPath(path15) {
|
|
5336
|
+
if (path15.length < 2) throw new Error("CDP drag requires at least two points.");
|
|
5337
|
+
await this.mouseAt(path15[0].x, path15[0].y, "mousePressed");
|
|
5338
|
+
for (const point of path15.slice(1)) await this.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: point.x, y: point.y, button: "left", buttons: 1 });
|
|
5339
|
+
const end = path15[path15.length - 1];
|
|
4497
5340
|
await this.mouseAt(end.x, end.y, "mouseReleased");
|
|
4498
5341
|
}
|
|
4499
5342
|
async withBackendNode(backendNodeId, functionDeclaration, args = []) {
|
|
@@ -4524,7 +5367,7 @@ var CdpTab = class _CdpTab {
|
|
|
4524
5367
|
}
|
|
4525
5368
|
send(method, params = {}) {
|
|
4526
5369
|
const id = this.nextId++;
|
|
4527
|
-
return new Promise((
|
|
5370
|
+
return new Promise((resolve2, reject) => {
|
|
4528
5371
|
const timer = setTimeout(() => {
|
|
4529
5372
|
this.pending.delete(id);
|
|
4530
5373
|
reject(new Error(`CDP command '${method}' timed out after ${COMMAND_TIMEOUT_MS}ms.`));
|
|
@@ -4532,7 +5375,7 @@ var CdpTab = class _CdpTab {
|
|
|
4532
5375
|
this.pending.set(id, {
|
|
4533
5376
|
resolve: (result) => {
|
|
4534
5377
|
clearTimeout(timer);
|
|
4535
|
-
|
|
5378
|
+
resolve2(result);
|
|
4536
5379
|
},
|
|
4537
5380
|
reject: (error) => {
|
|
4538
5381
|
clearTimeout(timer);
|
|
@@ -4712,9 +5555,9 @@ async function cdpMouseForContext(contextId, x, y, type, button = "left", clickC
|
|
|
4712
5555
|
return true;
|
|
4713
5556
|
}) === true;
|
|
4714
5557
|
}
|
|
4715
|
-
async function cdpDragForContext(contextId,
|
|
5558
|
+
async function cdpDragForContext(contextId, path15) {
|
|
4716
5559
|
return await withCdpContextTab(contextId, async (tab) => {
|
|
4717
|
-
await tab.dragPath(
|
|
5560
|
+
await tab.dragPath(path15);
|
|
4718
5561
|
return true;
|
|
4719
5562
|
}) === true;
|
|
4720
5563
|
}
|
|
@@ -4748,7 +5591,7 @@ async function cdpSnapshotForContext(contextId) {
|
|
|
4748
5591
|
tab.accessibilityTree().catch(() => [])
|
|
4749
5592
|
]);
|
|
4750
5593
|
assertConnectionEpoch(epoch);
|
|
4751
|
-
const snapshotId =
|
|
5594
|
+
const snapshotId = randomUUID3();
|
|
4752
5595
|
const { targets, outline } = cdpSnapshotOutline(snapshotId, nodes);
|
|
4753
5596
|
return {
|
|
4754
5597
|
contextId,
|
|
@@ -4818,7 +5661,7 @@ function cdpSnapshotOutline(snapshotId, nodes) {
|
|
|
4818
5661
|
}
|
|
4819
5662
|
const targets = [];
|
|
4820
5663
|
const build = (raw, seen) => {
|
|
4821
|
-
const nodeId = String(raw?.nodeId ??
|
|
5664
|
+
const nodeId = String(raw?.nodeId ?? randomUUID3());
|
|
4822
5665
|
if (seen.has(nodeId)) return void 0;
|
|
4823
5666
|
seen.add(nodeId);
|
|
4824
5667
|
const role = axString(raw?.role);
|
|
@@ -4944,16 +5787,16 @@ function titlesMatch(tabTitle, windowTitle) {
|
|
|
4944
5787
|
return tab === win || win.startsWith(tab) || tab.startsWith(win);
|
|
4945
5788
|
}
|
|
4946
5789
|
|
|
4947
|
-
//
|
|
4948
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
4949
|
-
import
|
|
5790
|
+
// vendor/computer-use-axi/src/config.ts
|
|
5791
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
5792
|
+
import path5 from "node:path";
|
|
4950
5793
|
|
|
4951
|
-
//
|
|
5794
|
+
// vendor/computer-use-axi/host.ts
|
|
4952
5795
|
function getAgentDir() {
|
|
4953
5796
|
return getConfigDir();
|
|
4954
5797
|
}
|
|
4955
5798
|
|
|
4956
|
-
//
|
|
5799
|
+
// vendor/computer-use-axi/src/config.ts
|
|
4957
5800
|
var DEFAULT_CONFIG = {
|
|
4958
5801
|
browser_use: true,
|
|
4959
5802
|
headless: false,
|
|
@@ -4986,9 +5829,9 @@ function normalizePartial(raw) {
|
|
|
4986
5829
|
return out;
|
|
4987
5830
|
}
|
|
4988
5831
|
function readConfigFile(filePath) {
|
|
4989
|
-
if (!
|
|
5832
|
+
if (!existsSync3(filePath)) return { path: filePath, exists: false };
|
|
4990
5833
|
try {
|
|
4991
|
-
const parsed = JSON.parse(
|
|
5834
|
+
const parsed = JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
4992
5835
|
return { path: filePath, exists: true, values: normalizePartial(parsed) };
|
|
4993
5836
|
} catch (error) {
|
|
4994
5837
|
return { path: filePath, exists: true, error: error instanceof Error ? error.message : String(error) };
|
|
@@ -5008,8 +5851,8 @@ function readEnv() {
|
|
|
5008
5851
|
}
|
|
5009
5852
|
function loadComputerUseConfig(cwd) {
|
|
5010
5853
|
const sources = [
|
|
5011
|
-
readConfigFile(
|
|
5012
|
-
readConfigFile(
|
|
5854
|
+
readConfigFile(path5.join(getAgentDir(), "extensions", "computer-use.json")),
|
|
5855
|
+
readConfigFile(path5.join(cwd, ".pie", "computer-use.json"))
|
|
5013
5856
|
];
|
|
5014
5857
|
const env = readEnv();
|
|
5015
5858
|
const config = { ...DEFAULT_CONFIG };
|
|
@@ -5024,9 +5867,6 @@ function loadComputerUseConfig(cwd) {
|
|
|
5024
5867
|
function getComputerUseConfig() {
|
|
5025
5868
|
return activeConfig;
|
|
5026
5869
|
}
|
|
5027
|
-
function getLoadedComputerUseConfig() {
|
|
5028
|
-
return activeLoadedConfig;
|
|
5029
|
-
}
|
|
5030
5870
|
function isHeadlessMode() {
|
|
5031
5871
|
return activeConfig.headless;
|
|
5032
5872
|
}
|
|
@@ -5034,7 +5874,7 @@ function isBrowserUseEnabled() {
|
|
|
5034
5874
|
return activeConfig.browser_use;
|
|
5035
5875
|
}
|
|
5036
5876
|
|
|
5037
|
-
//
|
|
5877
|
+
// vendor/computer-use-axi/src/note.ts
|
|
5038
5878
|
function normalizedLabel(value) {
|
|
5039
5879
|
return value.toLowerCase().replace(/\s+/g, " ").trim();
|
|
5040
5880
|
}
|
|
@@ -5186,10 +6026,9 @@ function renderNote(note) {
|
|
|
5186
6026
|
\u2026 note byte budget reached`;
|
|
5187
6027
|
}
|
|
5188
6028
|
|
|
5189
|
-
//
|
|
5190
|
-
import { closeSync,
|
|
5191
|
-
import
|
|
5192
|
-
import path3 from "node:path";
|
|
6029
|
+
// vendor/computer-use-axi/src/output.ts
|
|
6030
|
+
import { closeSync as closeSync2, mkdirSync as mkdirSync5, openSync as openSync2, readSync, readdirSync as readdirSync3, statSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
6031
|
+
import path6 from "node:path";
|
|
5193
6032
|
var MODEL_TEXT_MAX_BYTES = 48 * 1024;
|
|
5194
6033
|
var MODEL_TEXT_MAX_LINES = 2e3;
|
|
5195
6034
|
var OUTPUT_PAGE_BYTES = 16 * 1024;
|
|
@@ -5198,9 +6037,13 @@ var MODEL_PREVIEW_BYTES = 16 * 1024;
|
|
|
5198
6037
|
var OUTPUT_ENTRY_MAX_BYTES = 16 * 1024 * 1024;
|
|
5199
6038
|
var OUTPUT_STORE_MAX_BYTES = 64 * 1024 * 1024;
|
|
5200
6039
|
var outputs = /* @__PURE__ */ new Map();
|
|
5201
|
-
var outputDirectory;
|
|
5202
|
-
var outputBytes = 0;
|
|
5203
6040
|
var nextOutputId = 1;
|
|
6041
|
+
var outputBytes = 0;
|
|
6042
|
+
function outputDirectoryFor(owner) {
|
|
6043
|
+
const dir = path6.join(outputsDir(), "owners", normalizeComputerUseOwner(owner));
|
|
6044
|
+
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
6045
|
+
return dir;
|
|
6046
|
+
}
|
|
5204
6047
|
function utf8Prefix(bytes, maxBytes) {
|
|
5205
6048
|
if (bytes.byteLength <= maxBytes) return bytes;
|
|
5206
6049
|
let end = Math.max(0, maxBytes);
|
|
@@ -5212,14 +6055,14 @@ function boundedPrefix(value, maxBytes, maxLines) {
|
|
|
5212
6055
|
const lineBounded = lines.length > maxLines ? lines.slice(0, maxLines).join("\n") : value;
|
|
5213
6056
|
return new TextDecoder().decode(utf8Prefix(new TextEncoder().encode(lineBounded), maxBytes));
|
|
5214
6057
|
}
|
|
5215
|
-
function storeOutput(value) {
|
|
6058
|
+
function storeOutput(value, owner) {
|
|
5216
6059
|
const encoded = new TextEncoder().encode(value);
|
|
5217
6060
|
const stored = encoded.byteLength > OUTPUT_ENTRY_MAX_BYTES ? utf8Prefix(encoded, OUTPUT_ENTRY_MAX_BYTES) : encoded;
|
|
5218
|
-
|
|
6061
|
+
const directory = outputDirectoryFor(owner);
|
|
5219
6062
|
const ref = `@o${nextOutputId++}`;
|
|
5220
|
-
const filePath =
|
|
5221
|
-
|
|
5222
|
-
const entry = { ref, filePath, storedBytes: stored.byteLength, totalBytes: encoded.byteLength, complete: stored.byteLength === encoded.byteLength };
|
|
6063
|
+
const filePath = path6.join(directory, `${ref.slice(2)}.txt`);
|
|
6064
|
+
writeFileSync2(filePath, stored, { mode: 384 });
|
|
6065
|
+
const entry = { ref, owner: normalizeComputerUseOwner(owner), filePath, storedBytes: stored.byteLength, totalBytes: encoded.byteLength, complete: stored.byteLength === encoded.byteLength };
|
|
5223
6066
|
outputs.set(entry.ref, entry);
|
|
5224
6067
|
outputBytes += entry.storedBytes;
|
|
5225
6068
|
while (outputBytes > OUTPUT_STORE_MAX_BYTES && outputs.size > 1) {
|
|
@@ -5229,7 +6072,7 @@ function storeOutput(value) {
|
|
|
5229
6072
|
outputs.delete(oldestRef);
|
|
5230
6073
|
outputBytes -= oldest.storedBytes;
|
|
5231
6074
|
try {
|
|
5232
|
-
|
|
6075
|
+
unlinkSync3(oldest.filePath);
|
|
5233
6076
|
} catch {
|
|
5234
6077
|
}
|
|
5235
6078
|
}
|
|
@@ -5242,16 +6085,16 @@ function refinementFor(tool) {
|
|
|
5242
6085
|
if (tool === "observe_ui" || tool === "expand_ui") return "use search_ui or expand a more specific ref";
|
|
5243
6086
|
return "request a smaller or more focused result";
|
|
5244
6087
|
}
|
|
5245
|
-
function applyOutputEnvelope(tool, result) {
|
|
6088
|
+
function applyOutputEnvelope(tool, result, owner = COMPUTER_USE_STANDALONE_OWNER) {
|
|
5246
6089
|
const textParts = result.content.filter((part) => part.type === "text");
|
|
5247
6090
|
const combined = textParts.map((part) => part.text).join("\n");
|
|
5248
6091
|
const bytes = new TextEncoder().encode(combined).byteLength;
|
|
5249
6092
|
const lines = combined === "" ? 0 : combined.split("\n").length;
|
|
5250
6093
|
if (bytes <= MODEL_TEXT_MAX_BYTES && lines <= MODEL_TEXT_MAX_LINES) return result;
|
|
5251
|
-
const entry = storeOutput(combined);
|
|
6094
|
+
const entry = storeOutput(combined, owner);
|
|
5252
6095
|
const preview = boundedPrefix(combined, MODEL_PREVIEW_BYTES, MODEL_TEXT_MAX_LINES - 4);
|
|
5253
6096
|
const returnedBytes = new TextEncoder().encode(preview).byteLength;
|
|
5254
|
-
const availability = entry.complete ? `continue:
|
|
6097
|
+
const availability = entry.complete ? `continue: computer-use-axi read --ref "${entry.ref}" --offset ${returnedBytes}` : `continue: computer-use-axi read --ref "${entry.ref}" --offset ${returnedBytes}; only the first ${entry.storedBytes} bytes were stored, so refine for the remainder`;
|
|
5255
6098
|
const trailer = [
|
|
5256
6099
|
`output truncated: returned ${returnedBytes} of ${entry.totalBytes} utf-8 bytes`,
|
|
5257
6100
|
`refine: ${refinementFor(tool)}`,
|
|
@@ -5262,12 +6105,12 @@ function applyOutputEnvelope(tool, result) {
|
|
|
5262
6105
|
|
|
5263
6106
|
${trailer}` }, ...images] };
|
|
5264
6107
|
}
|
|
5265
|
-
function boundToolError(tool, error) {
|
|
6108
|
+
function boundToolError(tool, error, owner = COMPUTER_USE_STANDALONE_OWNER) {
|
|
5266
6109
|
const message = error instanceof Error ? error.message : String(error);
|
|
5267
6110
|
const bytes = new TextEncoder().encode(message).byteLength;
|
|
5268
6111
|
const lines = message.split("\n").length;
|
|
5269
6112
|
if (bytes <= MODEL_TEXT_MAX_BYTES && lines <= MODEL_TEXT_MAX_LINES) return error instanceof Error ? error : new Error(message);
|
|
5270
|
-
const entry = storeOutput(message);
|
|
6113
|
+
const entry = storeOutput(message, owner);
|
|
5271
6114
|
const preview = boundedPrefix(message, MODEL_PREVIEW_BYTES, MODEL_TEXT_MAX_LINES - 4);
|
|
5272
6115
|
const returnedBytes = new TextEncoder().encode(preview).byteLength;
|
|
5273
6116
|
const storageNote = entry.complete ? "" : `; only the first ${entry.storedBytes} bytes were stored`;
|
|
@@ -5275,19 +6118,19 @@ function boundToolError(tool, error) {
|
|
|
5275
6118
|
|
|
5276
6119
|
error truncated: returned ${returnedBytes} of ${entry.totalBytes} utf-8 bytes
|
|
5277
6120
|
refine: ${refinementFor(tool)}
|
|
5278
|
-
continue:
|
|
6121
|
+
continue: computer-use-axi read --ref "${entry.ref}" --offset ${returnedBytes}${storageNote}`);
|
|
5279
6122
|
}
|
|
5280
|
-
function readStoredOutput(ref, offsetValue) {
|
|
6123
|
+
function readStoredOutput(ref, offsetValue, owner = COMPUTER_USE_STANDALONE_OWNER) {
|
|
5281
6124
|
const entry = outputs.get(ref);
|
|
5282
|
-
if (!entry) return void 0;
|
|
6125
|
+
if (!entry || entry.owner !== normalizeComputerUseOwner(owner)) return void 0;
|
|
5283
6126
|
let offset = Math.min(entry.storedBytes, Math.max(0, Math.trunc(typeof offsetValue === "number" && Number.isFinite(offsetValue) ? offsetValue : 0)));
|
|
5284
6127
|
const requestedBytes = Math.min(entry.storedBytes - offset, OUTPUT_PAGE_BYTES + 4);
|
|
5285
6128
|
const buffer = new Uint8Array(Math.max(0, requestedBytes));
|
|
5286
|
-
const fd =
|
|
6129
|
+
const fd = openSync2(entry.filePath, "r");
|
|
5287
6130
|
try {
|
|
5288
6131
|
if (buffer.byteLength > 0) readSync(fd, buffer, 0, buffer.byteLength, offset);
|
|
5289
6132
|
} finally {
|
|
5290
|
-
|
|
6133
|
+
closeSync2(fd);
|
|
5291
6134
|
}
|
|
5292
6135
|
let start = 0;
|
|
5293
6136
|
while (start < buffer.byteLength && (buffer[start] & 192) === 128) start += 1;
|
|
@@ -5303,56 +6146,52 @@ function readStoredOutput(ref, offsetValue) {
|
|
|
5303
6146
|
complete: entry.complete
|
|
5304
6147
|
};
|
|
5305
6148
|
}
|
|
5306
|
-
function clearStoredOutputs() {
|
|
5307
|
-
|
|
5308
|
-
|
|
6149
|
+
function clearStoredOutputs(owner) {
|
|
6150
|
+
if (owner === void 0) {
|
|
6151
|
+
for (const entry of outputs.values()) {
|
|
6152
|
+
try {
|
|
6153
|
+
unlinkSync3(entry.filePath);
|
|
6154
|
+
} catch {
|
|
6155
|
+
}
|
|
6156
|
+
}
|
|
6157
|
+
outputs.clear();
|
|
6158
|
+
outputBytes = 0;
|
|
6159
|
+
return;
|
|
6160
|
+
}
|
|
6161
|
+
const normalized = normalizeComputerUseOwner(owner);
|
|
6162
|
+
for (const [ref, entry] of [...outputs.entries()]) {
|
|
6163
|
+
if (entry.owner !== normalized) continue;
|
|
6164
|
+
outputs.delete(ref);
|
|
6165
|
+
outputBytes = Math.max(0, outputBytes - entry.storedBytes);
|
|
5309
6166
|
try {
|
|
5310
|
-
|
|
6167
|
+
unlinkSync3(entry.filePath);
|
|
5311
6168
|
} catch {
|
|
5312
6169
|
}
|
|
5313
6170
|
}
|
|
5314
|
-
outputDirectory = void 0;
|
|
5315
|
-
outputBytes = 0;
|
|
5316
|
-
nextOutputId = 1;
|
|
5317
6171
|
}
|
|
5318
6172
|
|
|
5319
|
-
//
|
|
5320
|
-
var AGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
5321
|
-
"find_roots",
|
|
5322
|
-
"read_text",
|
|
5323
|
-
"wait_for",
|
|
5324
|
-
"observe_ui",
|
|
5325
|
-
"search_ui",
|
|
5326
|
-
"expand_ui",
|
|
5327
|
-
"inspect_ui",
|
|
5328
|
-
"act_ui",
|
|
5329
|
-
"navigate_browser",
|
|
5330
|
-
"evaluate_browser",
|
|
5331
|
-
"launch_browser"
|
|
5332
|
-
]);
|
|
5333
|
-
|
|
5334
|
-
// builtin/extensions/computer-use/src/platform/macos/helper.ts
|
|
6173
|
+
// vendor/computer-use-axi/src/platform/macos/helper.ts
|
|
5335
6174
|
import { spawn } from "node:child_process";
|
|
5336
6175
|
import { constants as fsConstants2 } from "node:fs";
|
|
5337
6176
|
import { access, mkdir, realpath } from "node:fs/promises";
|
|
5338
6177
|
import net from "node:net";
|
|
5339
|
-
import os3 from "node:os";
|
|
5340
|
-
import path6 from "node:path";
|
|
5341
|
-
|
|
5342
|
-
// builtin/extensions/computer-use/src/platform/macos/helper-path.mjs
|
|
5343
|
-
import { accessSync, constants as fsConstants, existsSync as existsSync2 } from "node:fs";
|
|
5344
6178
|
import os2 from "node:os";
|
|
5345
|
-
import
|
|
6179
|
+
import path9 from "node:path";
|
|
6180
|
+
|
|
6181
|
+
// vendor/computer-use-axi/src/platform/macos/helper-path.mjs
|
|
6182
|
+
import { accessSync, constants as fsConstants, existsSync as existsSync4 } from "node:fs";
|
|
6183
|
+
import os from "node:os";
|
|
6184
|
+
import path7 from "node:path";
|
|
5346
6185
|
var HELPER_APP_NAME = "pie-computer-use.app";
|
|
5347
|
-
var SYSTEM_HELPER_APP_PATH =
|
|
6186
|
+
var SYSTEM_HELPER_APP_PATH = path7.join("/Applications", HELPER_APP_NAME);
|
|
5348
6187
|
function resolveMacosHelperAppPath(options = {}) {
|
|
5349
6188
|
const env = options.env ?? process.env;
|
|
5350
6189
|
const explicitPath = env.PIE_COMPUTER_USE_HELPER_APP_PATH?.trim();
|
|
5351
|
-
if (explicitPath) return
|
|
5352
|
-
const homeDir = options.homeDir ??
|
|
6190
|
+
if (explicitPath) return path7.resolve(explicitPath);
|
|
6191
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
5353
6192
|
const configDir = options.configDir ?? resolvePieConfigDir(env, homeDir);
|
|
5354
6193
|
const systemHelperAppPath = options.systemHelperAppPath ?? SYSTEM_HELPER_APP_PATH;
|
|
5355
|
-
const fileExists = options.fileExists ??
|
|
6194
|
+
const fileExists = options.fileExists ?? existsSync4;
|
|
5356
6195
|
const directoryIsWritable = options.directoryIsWritable ?? ((directoryPath) => {
|
|
5357
6196
|
try {
|
|
5358
6197
|
accessSync(directoryPath, fsConstants.W_OK);
|
|
@@ -5361,10 +6200,10 @@ function resolveMacosHelperAppPath(options = {}) {
|
|
|
5361
6200
|
return false;
|
|
5362
6201
|
}
|
|
5363
6202
|
});
|
|
5364
|
-
if (fileExists(systemHelperAppPath) && directoryIsWritable(
|
|
6203
|
+
if (fileExists(systemHelperAppPath) && directoryIsWritable(path7.dirname(systemHelperAppPath))) {
|
|
5365
6204
|
return systemHelperAppPath;
|
|
5366
6205
|
}
|
|
5367
|
-
return
|
|
6206
|
+
return path7.join(configDir, "computer-use", HELPER_APP_NAME);
|
|
5368
6207
|
}
|
|
5369
6208
|
function resolvePieConfigDir(env, homeDir) {
|
|
5370
6209
|
const envDir = env.PIE_CODING_AGENT_DIR;
|
|
@@ -5373,37 +6212,37 @@ function resolvePieConfigDir(env, homeDir) {
|
|
|
5373
6212
|
if (envDir.startsWith("~/")) return homeDir + envDir.slice(1);
|
|
5374
6213
|
return envDir;
|
|
5375
6214
|
}
|
|
5376
|
-
return
|
|
6215
|
+
return path7.join(homeDir, ".pie");
|
|
5377
6216
|
}
|
|
5378
6217
|
|
|
5379
|
-
//
|
|
5380
|
-
import { existsSync as
|
|
5381
|
-
import
|
|
5382
|
-
import { fileURLToPath } from "node:url";
|
|
6218
|
+
// vendor/computer-use-axi/src/platform/package-root.ts
|
|
6219
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
6220
|
+
import path8 from "node:path";
|
|
6221
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
5383
6222
|
function resolveExtensionRoot(moduleUrl) {
|
|
5384
6223
|
const override = process.env.PIE_COMPUTER_USE_PACKAGE_ROOT?.trim();
|
|
5385
|
-
if (override) return
|
|
5386
|
-
let dir =
|
|
6224
|
+
if (override) return path8.resolve(override);
|
|
6225
|
+
let dir = path8.dirname(fileURLToPath2(moduleUrl));
|
|
5387
6226
|
for (let depth = 0; depth < 12; depth += 1) {
|
|
5388
|
-
if (
|
|
5389
|
-
const parent =
|
|
6227
|
+
if (existsSync5(path8.join(dir, "scripts", "setup-helper.mjs"))) return dir;
|
|
6228
|
+
const parent = path8.dirname(dir);
|
|
5390
6229
|
if (parent === dir) break;
|
|
5391
6230
|
dir = parent;
|
|
5392
6231
|
}
|
|
5393
|
-
throw new Error(`computer-use: could not locate extension root (scripts/setup-helper.mjs) above ${
|
|
6232
|
+
throw new Error(`computer-use: could not locate extension root (scripts/setup-helper.mjs) above ${path8.dirname(fileURLToPath2(moduleUrl))}. Set PIE_COMPUTER_USE_PACKAGE_ROOT.`);
|
|
5394
6233
|
}
|
|
5395
6234
|
|
|
5396
|
-
//
|
|
6235
|
+
// vendor/computer-use-axi/src/platform/macos/helper.ts
|
|
5397
6236
|
var COMMAND_TIMEOUT_MS2 = 15e3;
|
|
5398
6237
|
var HELPER_PROTOCOL_VERSION = 6;
|
|
5399
6238
|
var HELPER_SETUP_TIMEOUT_MS = 6e4;
|
|
5400
6239
|
var HELPER_APP_PATH = resolveMacosHelperAppPath();
|
|
5401
|
-
var HELPER_APP_EXECUTABLE_PATH =
|
|
5402
|
-
var DEFAULT_HELPER_SOCKET_PATH =
|
|
6240
|
+
var HELPER_APP_EXECUTABLE_PATH = path9.join(HELPER_APP_PATH, "Contents", "MacOS", "pie-computer-use");
|
|
6241
|
+
var DEFAULT_HELPER_SOCKET_PATH = path9.join(os2.homedir(), "Library", "Caches", "pie-computer-use", "bridge.sock");
|
|
5403
6242
|
var HELPER_SOCKET_PATH = process.env.PIE_COMPUTER_USE_SOCKET_PATH ?? DEFAULT_HELPER_SOCKET_PATH;
|
|
5404
6243
|
var usingExternalHelperSocket = HELPER_SOCKET_PATH !== DEFAULT_HELPER_SOCKET_PATH;
|
|
5405
6244
|
var PACKAGE_ROOT = resolveExtensionRoot(import.meta.url);
|
|
5406
|
-
var SETUP_HELPER_SCRIPT =
|
|
6245
|
+
var SETUP_HELPER_SCRIPT = path9.join(PACKAGE_ROOT, "scripts", "setup-helper.mjs");
|
|
5407
6246
|
var HelperTransportError = class extends Error {
|
|
5408
6247
|
constructor(message) {
|
|
5409
6248
|
super(message);
|
|
@@ -5423,8 +6262,8 @@ function throwIfAborted(signal) {
|
|
|
5423
6262
|
}
|
|
5424
6263
|
async function sleep(ms, signal) {
|
|
5425
6264
|
throwIfAborted(signal);
|
|
5426
|
-
await new Promise((
|
|
5427
|
-
const timer = setTimeout(
|
|
6265
|
+
await new Promise((resolve2, reject) => {
|
|
6266
|
+
const timer = setTimeout(resolve2, ms);
|
|
5428
6267
|
const onAbort = () => {
|
|
5429
6268
|
clearTimeout(timer);
|
|
5430
6269
|
reject(new Error("Operation aborted."));
|
|
@@ -5443,14 +6282,14 @@ async function isExecutable(filePath) {
|
|
|
5443
6282
|
async function isResolvedHelperExecutable(filePath) {
|
|
5444
6283
|
if (!filePath) return true;
|
|
5445
6284
|
const [actualPath, expectedPath] = await Promise.all([
|
|
5446
|
-
realpath(filePath).catch(() =>
|
|
5447
|
-
realpath(HELPER_APP_EXECUTABLE_PATH).catch(() =>
|
|
6285
|
+
realpath(filePath).catch(() => path9.resolve(filePath)),
|
|
6286
|
+
realpath(HELPER_APP_EXECUTABLE_PATH).catch(() => path9.resolve(HELPER_APP_EXECUTABLE_PATH))
|
|
5448
6287
|
]);
|
|
5449
6288
|
return actualPath === expectedPath;
|
|
5450
6289
|
}
|
|
5451
6290
|
async function runProcess(command, args, timeoutMs, signal, env) {
|
|
5452
6291
|
throwIfAborted(signal);
|
|
5453
|
-
await new Promise((
|
|
6292
|
+
await new Promise((resolve2, reject) => {
|
|
5454
6293
|
const child = spawn(command, args, {
|
|
5455
6294
|
stdio: ["ignore", "pipe", "pipe"],
|
|
5456
6295
|
env
|
|
@@ -5484,7 +6323,7 @@ async function runProcess(command, args, timeoutMs, signal, env) {
|
|
|
5484
6323
|
child.on("close", (code) => {
|
|
5485
6324
|
cleanup();
|
|
5486
6325
|
if (code === 0) {
|
|
5487
|
-
|
|
6326
|
+
resolve2();
|
|
5488
6327
|
return;
|
|
5489
6328
|
}
|
|
5490
6329
|
const output = [stderr.trim(), stdout.trim()].filter(Boolean).join("\n");
|
|
@@ -5517,11 +6356,11 @@ var MacosHelperClient = class {
|
|
|
5517
6356
|
}
|
|
5518
6357
|
async launchDaemon(signal) {
|
|
5519
6358
|
if (usingExternalHelperSocket) throw new HelperTransportError(`External helper socket is unavailable at ${HELPER_SOCKET_PATH}.`);
|
|
5520
|
-
await mkdir(
|
|
6359
|
+
await mkdir(path9.dirname(HELPER_SOCKET_PATH), { recursive: true });
|
|
5521
6360
|
await runProcess("open", ["-n", "-g", HELPER_APP_PATH, "--args", "serve", "--socket", HELPER_SOCKET_PATH], COMMAND_TIMEOUT_MS2, signal);
|
|
5522
6361
|
}
|
|
5523
6362
|
async daemonCommand(cmd, args, timeoutMs, signal) {
|
|
5524
|
-
return await new Promise((
|
|
6363
|
+
return await new Promise((resolve2, reject) => {
|
|
5525
6364
|
const id = `req_${++this.requestSequence}`;
|
|
5526
6365
|
const socket = net.createConnection(HELPER_SOCKET_PATH);
|
|
5527
6366
|
let buffer = "";
|
|
@@ -5550,7 +6389,7 @@ var MacosHelperClient = class {
|
|
|
5550
6389
|
socket.end();
|
|
5551
6390
|
try {
|
|
5552
6391
|
const parsed = JSON.parse(buffer.slice(0, newline));
|
|
5553
|
-
if (parsed.ok === true)
|
|
6392
|
+
if (parsed.ok === true) resolve2(parsed.result);
|
|
5554
6393
|
else reject(new HelperCommandError(parsed?.error?.message ?? `Daemon command '${cmd}' failed.`, parsed?.error?.code));
|
|
5555
6394
|
} catch (error) {
|
|
5556
6395
|
reject(error);
|
|
@@ -5640,7 +6479,7 @@ var MacosHelperClient = class {
|
|
|
5640
6479
|
};
|
|
5641
6480
|
var macosHelper = new MacosHelperClient();
|
|
5642
6481
|
|
|
5643
|
-
//
|
|
6482
|
+
// vendor/computer-use-axi/src/platform/macos/backend.ts
|
|
5644
6483
|
function parseApps(result) {
|
|
5645
6484
|
const array = Array.isArray(result) ? result : result?.apps;
|
|
5646
6485
|
if (!Array.isArray(array)) return [];
|
|
@@ -5751,7 +6590,7 @@ var macosBackend = {
|
|
|
5751
6590
|
}
|
|
5752
6591
|
};
|
|
5753
6592
|
|
|
5754
|
-
//
|
|
6593
|
+
// vendor/computer-use-axi/src/platform/macos/browser.ts
|
|
5755
6594
|
var BROWSER_WINDOW_OPEN_TIMEOUT_MS = 1e4;
|
|
5756
6595
|
var BROWSER_BUNDLE_IDS = /* @__PURE__ */ new Set([
|
|
5757
6596
|
"com.apple.Safari",
|
|
@@ -5849,7 +6688,7 @@ async function openBrowserLocationWithAppleScript(target, url, signal) {
|
|
|
5849
6688
|
}
|
|
5850
6689
|
}
|
|
5851
6690
|
|
|
5852
|
-
//
|
|
6691
|
+
// vendor/computer-use-axi/src/permissions.ts
|
|
5853
6692
|
function throwIfAborted2(signal) {
|
|
5854
6693
|
if (signal?.aborted) throw new Error("Operation aborted.");
|
|
5855
6694
|
}
|
|
@@ -5892,7 +6731,7 @@ async function ensurePermissions(ctx, bridge, helperPath, signal) {
|
|
|
5892
6731
|
return status;
|
|
5893
6732
|
}
|
|
5894
6733
|
|
|
5895
|
-
//
|
|
6734
|
+
// vendor/computer-use-axi/src/platform/architecture.ts
|
|
5896
6735
|
var PLATFORM_ARCHITECTURE_VERSION = 1;
|
|
5897
6736
|
var REQUIRED_PLATFORM_INVARIANTS = [
|
|
5898
6737
|
"state-scoped-observations",
|
|
@@ -5914,7 +6753,7 @@ function assertPlatformArchitecture(platform, diagnostics) {
|
|
|
5914
6753
|
}
|
|
5915
6754
|
}
|
|
5916
6755
|
|
|
5917
|
-
//
|
|
6756
|
+
// vendor/computer-use-axi/src/platform/macos/permissions.ts
|
|
5918
6757
|
var GRANT_INSTRUCTIONS = "Grant Accessibility and Screen Recording to pie-computer-use.app in System Settings \u2192 Privacy & Security. Screen Recording lets the agent see the window; Accessibility lets it interact with the window.";
|
|
5919
6758
|
var SIGNING_MIGRATION_WARNING = "If these permissions were enabled before this install/update, macOS invalidated the old grants because pie-computer-use.app was re-signed. Re-enable both toggles for the newly signed helper. If a toggle is already on, switch it off and on again.";
|
|
5920
6759
|
var macosPermissionKinds = [
|
|
@@ -6033,18 +6872,18 @@ Helper path: ${helperPath}`,
|
|
|
6033
6872
|
return { permissionStatus, lastPermissionCheckAt, helperDiagnostics };
|
|
6034
6873
|
}
|
|
6035
6874
|
|
|
6036
|
-
//
|
|
6875
|
+
// vendor/computer-use-axi/src/platform/linux/helper.ts
|
|
6037
6876
|
import { spawn as spawn2 } from "node:child_process";
|
|
6038
|
-
import { randomUUID as
|
|
6877
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
6039
6878
|
import { constants as fsConstants3 } from "node:fs";
|
|
6040
6879
|
import { access as access2 } from "node:fs/promises";
|
|
6041
|
-
import
|
|
6880
|
+
import path10 from "node:path";
|
|
6042
6881
|
var PACKAGE_ROOT2 = resolveExtensionRoot(import.meta.url);
|
|
6043
|
-
var SETUP_HELPER_SCRIPT2 =
|
|
6882
|
+
var SETUP_HELPER_SCRIPT2 = path10.join(PACKAGE_ROOT2, "scripts", "setup-helper.mjs");
|
|
6044
6883
|
var HELPER_SETUP_TIMEOUT_MS2 = 6e4;
|
|
6045
6884
|
var COMMAND_TIMEOUT_MS3 = 15e3;
|
|
6046
6885
|
var LINUX_HELPER_PROTOCOL_VERSION = 4;
|
|
6047
|
-
var LINUX_HELPER_PATH = process.env.PIE_COMPUTER_USE_LINUX_HELPER_PATH ||
|
|
6886
|
+
var LINUX_HELPER_PATH = process.env.PIE_COMPUTER_USE_LINUX_HELPER_PATH || path10.join(getAgentDir(), "helpers", "computer-use", "linux-bridge");
|
|
6048
6887
|
async function isExecutable2(filePath) {
|
|
6049
6888
|
try {
|
|
6050
6889
|
await access2(filePath, fsConstants3.X_OK);
|
|
@@ -6055,7 +6894,7 @@ async function isExecutable2(filePath) {
|
|
|
6055
6894
|
}
|
|
6056
6895
|
async function runProcess2(command, args, timeoutMs, signal, env) {
|
|
6057
6896
|
if (signal?.aborted) throw new Error("Operation aborted.");
|
|
6058
|
-
await new Promise((
|
|
6897
|
+
await new Promise((resolve2, reject) => {
|
|
6059
6898
|
const child = spawn2(command, args, { stdio: ["ignore", "pipe", "pipe"], env });
|
|
6060
6899
|
let stdout = "";
|
|
6061
6900
|
let stderr = "";
|
|
@@ -6066,7 +6905,7 @@ async function runProcess2(command, args, timeoutMs, signal, env) {
|
|
|
6066
6905
|
clearTimeout(timer);
|
|
6067
6906
|
signal?.removeEventListener("abort", onAbort);
|
|
6068
6907
|
if (error) reject(error);
|
|
6069
|
-
else
|
|
6908
|
+
else resolve2();
|
|
6070
6909
|
};
|
|
6071
6910
|
const timer = setTimeout(() => {
|
|
6072
6911
|
child.kill("SIGTERM");
|
|
@@ -6095,13 +6934,13 @@ ${output}`.trim()));
|
|
|
6095
6934
|
async function waitForShared(promise, signal) {
|
|
6096
6935
|
if (!signal) return await promise;
|
|
6097
6936
|
if (signal.aborted) throw new Error("Operation aborted.");
|
|
6098
|
-
return await new Promise((
|
|
6937
|
+
return await new Promise((resolve2, reject) => {
|
|
6099
6938
|
const onAbort = () => reject(new Error("Operation aborted."));
|
|
6100
6939
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
6101
6940
|
promise.then(
|
|
6102
6941
|
(value) => {
|
|
6103
6942
|
signal.removeEventListener("abort", onAbort);
|
|
6104
|
-
|
|
6943
|
+
resolve2(value);
|
|
6105
6944
|
},
|
|
6106
6945
|
(error) => {
|
|
6107
6946
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -6203,8 +7042,8 @@ var LinuxHelperClient = class {
|
|
|
6203
7042
|
});
|
|
6204
7043
|
this.child = child;
|
|
6205
7044
|
this.buffer = "";
|
|
6206
|
-
return await new Promise((
|
|
6207
|
-
child.once("spawn", () =>
|
|
7045
|
+
return await new Promise((resolve2, reject) => {
|
|
7046
|
+
child.once("spawn", () => resolve2(child));
|
|
6208
7047
|
child.once("error", reject);
|
|
6209
7048
|
});
|
|
6210
7049
|
}
|
|
@@ -6239,9 +7078,9 @@ var LinuxHelperClient = class {
|
|
|
6239
7078
|
}
|
|
6240
7079
|
async command(cmd, args = {}, options) {
|
|
6241
7080
|
const child = await this.process(options?.signal);
|
|
6242
|
-
const id =
|
|
7081
|
+
const id = randomUUID4();
|
|
6243
7082
|
const timeoutMs = options?.timeoutMs ?? COMMAND_TIMEOUT_MS3;
|
|
6244
|
-
return await new Promise((
|
|
7083
|
+
return await new Promise((resolve2, reject) => {
|
|
6245
7084
|
const onAbort = () => {
|
|
6246
7085
|
this.pending.delete(id);
|
|
6247
7086
|
clearTimeout(timer);
|
|
@@ -6255,7 +7094,7 @@ var LinuxHelperClient = class {
|
|
|
6255
7094
|
this.pending.set(id, {
|
|
6256
7095
|
resolve: (value) => {
|
|
6257
7096
|
options?.signal?.removeEventListener("abort", onAbort);
|
|
6258
|
-
|
|
7097
|
+
resolve2(value);
|
|
6259
7098
|
},
|
|
6260
7099
|
reject: (error) => {
|
|
6261
7100
|
options?.signal?.removeEventListener("abort", onAbort);
|
|
@@ -6277,7 +7116,7 @@ var LinuxHelperClient = class {
|
|
|
6277
7116
|
};
|
|
6278
7117
|
var linuxHelper = new LinuxHelperClient();
|
|
6279
7118
|
|
|
6280
|
-
//
|
|
7119
|
+
// vendor/computer-use-axi/src/platform/linux/backend.ts
|
|
6281
7120
|
function normalizedProcessName(appName) {
|
|
6282
7121
|
return appName.toLowerCase().split("/").pop().replace(/\.desktop$/i, "");
|
|
6283
7122
|
}
|
|
@@ -6433,18 +7272,18 @@ var linuxBackend = {
|
|
|
6433
7272
|
}
|
|
6434
7273
|
};
|
|
6435
7274
|
|
|
6436
|
-
//
|
|
7275
|
+
// vendor/computer-use-axi/src/platform/windows/helper.ts
|
|
6437
7276
|
import { spawn as spawn3 } from "node:child_process";
|
|
6438
|
-
import { randomUUID as
|
|
7277
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
6439
7278
|
import { constants as fsConstants4 } from "node:fs";
|
|
6440
7279
|
import { access as access3 } from "node:fs/promises";
|
|
6441
|
-
import
|
|
7280
|
+
import path11 from "node:path";
|
|
6442
7281
|
var PACKAGE_ROOT3 = resolveExtensionRoot(import.meta.url);
|
|
6443
|
-
var SETUP_HELPER_SCRIPT3 =
|
|
7282
|
+
var SETUP_HELPER_SCRIPT3 = path11.join(PACKAGE_ROOT3, "scripts", "setup-helper.mjs");
|
|
6444
7283
|
var HELPER_SETUP_TIMEOUT_MS3 = 6e4;
|
|
6445
7284
|
var COMMAND_TIMEOUT_MS4 = 15e3;
|
|
6446
7285
|
var WINDOWS_HELPER_PROTOCOL_VERSION = 4;
|
|
6447
|
-
var WINDOWS_HELPER_PATH = process.env.PIE_COMPUTER_USE_WINDOWS_HELPER_PATH ||
|
|
7286
|
+
var WINDOWS_HELPER_PATH = process.env.PIE_COMPUTER_USE_WINDOWS_HELPER_PATH || path11.join(getAgentDir(), "helpers", "computer-use", "windows-bridge.exe");
|
|
6448
7287
|
async function isExecutable3(filePath) {
|
|
6449
7288
|
try {
|
|
6450
7289
|
await access3(filePath, fsConstants4.X_OK);
|
|
@@ -6455,7 +7294,7 @@ async function isExecutable3(filePath) {
|
|
|
6455
7294
|
}
|
|
6456
7295
|
async function runProcess3(command, args, timeoutMs, signal, env) {
|
|
6457
7296
|
if (signal?.aborted) throw new Error("Operation aborted.");
|
|
6458
|
-
await new Promise((
|
|
7297
|
+
await new Promise((resolve2, reject) => {
|
|
6459
7298
|
const child = spawn3(command, args, { stdio: ["ignore", "pipe", "pipe"], env });
|
|
6460
7299
|
let stdout = "";
|
|
6461
7300
|
let stderr = "";
|
|
@@ -6485,7 +7324,7 @@ async function runProcess3(command, args, timeoutMs, signal, env) {
|
|
|
6485
7324
|
});
|
|
6486
7325
|
child.on("close", (code) => {
|
|
6487
7326
|
cleanup();
|
|
6488
|
-
if (code === 0) return
|
|
7327
|
+
if (code === 0) return resolve2();
|
|
6489
7328
|
const output = [stderr.trim(), stdout.trim()].filter(Boolean).join("\n");
|
|
6490
7329
|
reject(new Error(`Command failed (${code}): ${command} ${args.join(" ")}
|
|
6491
7330
|
${output}`.trim()));
|
|
@@ -6570,14 +7409,14 @@ var WindowsHelperClient = class {
|
|
|
6570
7409
|
}
|
|
6571
7410
|
async command(cmd, args = {}, options) {
|
|
6572
7411
|
const child = await this.process(options?.signal);
|
|
6573
|
-
const id =
|
|
7412
|
+
const id = randomUUID5();
|
|
6574
7413
|
const timeoutMs = options?.timeoutMs ?? COMMAND_TIMEOUT_MS4;
|
|
6575
|
-
return await new Promise((
|
|
7414
|
+
return await new Promise((resolve2, reject) => {
|
|
6576
7415
|
const timer = setTimeout(() => {
|
|
6577
7416
|
this.pending.delete(id);
|
|
6578
7417
|
reject(new Error(`Helper command '${cmd}' timed out after ${timeoutMs}ms.`));
|
|
6579
7418
|
}, timeoutMs);
|
|
6580
|
-
this.pending.set(id, { resolve, reject, timer });
|
|
7419
|
+
this.pending.set(id, { resolve: resolve2, reject, timer });
|
|
6581
7420
|
child.stdin.write(`${JSON.stringify({ protocolVersion: WINDOWS_HELPER_PROTOCOL_VERSION, id, cmd, args })}
|
|
6582
7421
|
`, (error) => {
|
|
6583
7422
|
if (!error) return;
|
|
@@ -6590,7 +7429,7 @@ var WindowsHelperClient = class {
|
|
|
6590
7429
|
};
|
|
6591
7430
|
var windowsHelper = new WindowsHelperClient();
|
|
6592
7431
|
|
|
6593
|
-
//
|
|
7432
|
+
// vendor/computer-use-axi/src/platform/windows/backend.ts
|
|
6594
7433
|
function normalizedProcessName2(appName) {
|
|
6595
7434
|
return appName.toLowerCase().replace(/\.exe$/i, "");
|
|
6596
7435
|
}
|
|
@@ -6726,7 +7565,7 @@ var windowsBackend = {
|
|
|
6726
7565
|
}
|
|
6727
7566
|
};
|
|
6728
7567
|
|
|
6729
|
-
//
|
|
7568
|
+
// vendor/computer-use-axi/src/platform/index.ts
|
|
6730
7569
|
var macosPlatformBackend = {
|
|
6731
7570
|
name: "macos",
|
|
6732
7571
|
ensureReady: ensureMacosReady,
|
|
@@ -6797,9 +7636,14 @@ function platformBackendForRuntime(platform = process.platform) {
|
|
|
6797
7636
|
return new UnsupportedPlatformBackend(platform);
|
|
6798
7637
|
}
|
|
6799
7638
|
var currentPlatformBackend = platformBackendForRuntime();
|
|
7639
|
+
var backendOverrideInstalled = false;
|
|
7640
|
+
function setPlatformBackend(backend) {
|
|
7641
|
+
currentPlatformBackend = backend;
|
|
7642
|
+
backendOverrideInstalled = true;
|
|
7643
|
+
}
|
|
6800
7644
|
|
|
6801
|
-
//
|
|
6802
|
-
import { randomUUID as
|
|
7645
|
+
// vendor/computer-use-axi/src/runtime.ts
|
|
7646
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
6803
7647
|
var StaleResourceStateError = class extends Error {
|
|
6804
7648
|
constructor(resourceKey, expectedEpoch, actualEpoch) {
|
|
6805
7649
|
super(`State is stale for ${resourceKey}: expected epoch ${expectedEpoch}, current epoch ${actualEpoch}.`);
|
|
@@ -6812,14 +7656,22 @@ var StaleResourceStateError = class extends Error {
|
|
|
6812
7656
|
expectedEpoch;
|
|
6813
7657
|
actualEpoch;
|
|
6814
7658
|
};
|
|
7659
|
+
var OperationAbortedError = class extends Error {
|
|
7660
|
+
constructor() {
|
|
7661
|
+
super("Operation cancelled before dispatch.");
|
|
7662
|
+
this.name = "OperationAbortedError";
|
|
7663
|
+
}
|
|
7664
|
+
};
|
|
6815
7665
|
var StateStore = class {
|
|
6816
|
-
constructor(limit = 128) {
|
|
7666
|
+
constructor(limit = 128, onEvict) {
|
|
6817
7667
|
this.limit = limit;
|
|
7668
|
+
this.onEvict = onEvict;
|
|
6818
7669
|
}
|
|
6819
7670
|
limit;
|
|
7671
|
+
onEvict;
|
|
6820
7672
|
records = /* @__PURE__ */ new Map();
|
|
6821
|
-
create(resourceKey, epoch, value) {
|
|
6822
|
-
const record = { stateId:
|
|
7673
|
+
create(resourceKey, epoch, value, meta = {}) {
|
|
7674
|
+
const record = { stateId: randomUUID6(), resourceKey, epoch, value, ...meta };
|
|
6823
7675
|
this.set(record);
|
|
6824
7676
|
return record;
|
|
6825
7677
|
}
|
|
@@ -6829,12 +7681,25 @@ var StateStore = class {
|
|
|
6829
7681
|
while (this.records.size > this.limit) {
|
|
6830
7682
|
const oldest = this.records.keys().next().value;
|
|
6831
7683
|
if (!oldest) break;
|
|
7684
|
+
const evicted = this.records.get(oldest);
|
|
6832
7685
|
this.records.delete(oldest);
|
|
7686
|
+
if (evicted) this.onEvict?.(evicted);
|
|
6833
7687
|
}
|
|
6834
7688
|
}
|
|
6835
7689
|
get(stateId2) {
|
|
6836
7690
|
return this.records.get(stateId2);
|
|
6837
7691
|
}
|
|
7692
|
+
ids() {
|
|
7693
|
+
return [...this.records.keys()];
|
|
7694
|
+
}
|
|
7695
|
+
/** Owner probes exist only to distinguish "expired for this owner" from
|
|
7696
|
+
* "unknown id" in diagnostics; they never return the value. */
|
|
7697
|
+
ownerOf(stateId2) {
|
|
7698
|
+
return this.records.get(stateId2)?.owner;
|
|
7699
|
+
}
|
|
7700
|
+
drop(stateId2) {
|
|
7701
|
+
return this.records.delete(stateId2);
|
|
7702
|
+
}
|
|
6838
7703
|
clear() {
|
|
6839
7704
|
this.records.clear();
|
|
6840
7705
|
}
|
|
@@ -6843,8 +7708,15 @@ var StateStore = class {
|
|
|
6843
7708
|
}
|
|
6844
7709
|
};
|
|
6845
7710
|
var ResourceScheduler = class {
|
|
7711
|
+
constructor(hooks = {}) {
|
|
7712
|
+
this.hooks = hooks;
|
|
7713
|
+
}
|
|
7714
|
+
hooks;
|
|
6846
7715
|
resources = /* @__PURE__ */ new Map();
|
|
6847
7716
|
closed = false;
|
|
7717
|
+
epochs() {
|
|
7718
|
+
return [...this.resources.entries()].map(([key, record]) => [key, record.epoch]);
|
|
7719
|
+
}
|
|
6848
7720
|
epoch(resourceKey) {
|
|
6849
7721
|
return this.resource(resourceKey).epoch;
|
|
6850
7722
|
}
|
|
@@ -6852,22 +7724,23 @@ var ResourceScheduler = class {
|
|
|
6852
7724
|
const record = this.resource(resourceKey);
|
|
6853
7725
|
record.epoch = Math.max(record.epoch, Math.max(0, Math.trunc(epoch)));
|
|
6854
7726
|
}
|
|
6855
|
-
async read(resourceKey, work) {
|
|
6856
|
-
return await this.enqueue(resourceKey, async (record) => ({ value: await work(record.epoch), epoch: record.epoch }));
|
|
7727
|
+
async read(resourceKey, work, signal) {
|
|
7728
|
+
return await this.enqueue(resourceKey, async (record) => ({ value: await work(record.epoch), epoch: record.epoch }), signal);
|
|
6857
7729
|
}
|
|
6858
|
-
async readAt(resourceKey, expectedEpoch, work) {
|
|
7730
|
+
async readAt(resourceKey, expectedEpoch, work, signal) {
|
|
6859
7731
|
return await this.enqueue(resourceKey, async (record) => {
|
|
6860
7732
|
if (record.epoch !== expectedEpoch) throw new StaleResourceStateError(resourceKey, expectedEpoch, record.epoch);
|
|
6861
7733
|
return { value: await work(record.epoch), epoch: record.epoch };
|
|
6862
|
-
});
|
|
7734
|
+
}, signal);
|
|
6863
7735
|
}
|
|
6864
|
-
async write(resourceKey, baseEpoch, work) {
|
|
7736
|
+
async write(resourceKey, baseEpoch, work, signal) {
|
|
6865
7737
|
return await this.enqueue(resourceKey, async (record) => {
|
|
6866
7738
|
if (record.epoch !== baseEpoch) throw new StaleResourceStateError(resourceKey, baseEpoch, record.epoch);
|
|
6867
7739
|
const nextEpoch = record.epoch + 1;
|
|
6868
7740
|
record.epoch = nextEpoch;
|
|
7741
|
+
this.hooks.onEpochChange?.(resourceKey, nextEpoch);
|
|
6869
7742
|
return { value: await work(nextEpoch), epoch: nextEpoch };
|
|
6870
|
-
});
|
|
7743
|
+
}, signal);
|
|
6871
7744
|
}
|
|
6872
7745
|
async drain() {
|
|
6873
7746
|
await Promise.all([...this.resources.values()].map((record) => record.tail.catch(() => void 0)));
|
|
@@ -6885,17 +7758,22 @@ var ResourceScheduler = class {
|
|
|
6885
7758
|
}
|
|
6886
7759
|
return record;
|
|
6887
7760
|
}
|
|
6888
|
-
async enqueue(resourceKey, work) {
|
|
7761
|
+
async enqueue(resourceKey, work, signal) {
|
|
6889
7762
|
if (this.closed) throw new Error("Computer-use session is shutting down.");
|
|
6890
7763
|
const record = this.resource(resourceKey);
|
|
6891
7764
|
const previous = record.tail;
|
|
6892
7765
|
let release;
|
|
6893
|
-
const next = new Promise((
|
|
6894
|
-
release =
|
|
7766
|
+
const next = new Promise((resolve2) => {
|
|
7767
|
+
release = resolve2;
|
|
6895
7768
|
});
|
|
6896
7769
|
record.tail = previous.catch(() => void 0).then(() => next);
|
|
6897
|
-
|
|
7770
|
+
if (signal?.aborted) {
|
|
7771
|
+
release();
|
|
7772
|
+
throw new OperationAbortedError();
|
|
7773
|
+
}
|
|
6898
7774
|
try {
|
|
7775
|
+
await previous.catch(() => void 0);
|
|
7776
|
+
if (signal?.aborted) throw new OperationAbortedError();
|
|
6899
7777
|
if (this.closed) throw new Error("Computer-use session is shutting down.");
|
|
6900
7778
|
return await work(record);
|
|
6901
7779
|
} finally {
|
|
@@ -6903,28 +7781,108 @@ var ResourceScheduler = class {
|
|
|
6903
7781
|
}
|
|
6904
7782
|
}
|
|
6905
7783
|
};
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
7784
|
+
var OwnerScopedMap = class {
|
|
7785
|
+
constructor(scope) {
|
|
7786
|
+
this.scope = scope;
|
|
7787
|
+
}
|
|
7788
|
+
scope;
|
|
7789
|
+
maps = /* @__PURE__ */ new Map();
|
|
7790
|
+
owner() {
|
|
7791
|
+
const owner = this.scope.currentOwner();
|
|
7792
|
+
return owner || COMPUTER_USE_STANDALONE_OWNER;
|
|
7793
|
+
}
|
|
7794
|
+
map() {
|
|
7795
|
+
const owner = this.owner();
|
|
7796
|
+
let map = this.maps.get(owner);
|
|
7797
|
+
if (!map) {
|
|
7798
|
+
map = /* @__PURE__ */ new Map();
|
|
7799
|
+
this.maps.set(owner, map);
|
|
7800
|
+
}
|
|
7801
|
+
return map;
|
|
7802
|
+
}
|
|
7803
|
+
get(key) {
|
|
7804
|
+
return this.map().get(key);
|
|
7805
|
+
}
|
|
7806
|
+
has(key) {
|
|
7807
|
+
return this.map().has(key);
|
|
7808
|
+
}
|
|
7809
|
+
set(key, value) {
|
|
7810
|
+
this.map().set(key, value);
|
|
7811
|
+
return this;
|
|
7812
|
+
}
|
|
7813
|
+
delete(key) {
|
|
7814
|
+
return this.map().delete(key);
|
|
7815
|
+
}
|
|
7816
|
+
values() {
|
|
7817
|
+
return this.map().values();
|
|
7818
|
+
}
|
|
7819
|
+
entries() {
|
|
7820
|
+
return this.map().entries();
|
|
7821
|
+
}
|
|
7822
|
+
clear() {
|
|
7823
|
+
this.map().clear();
|
|
7824
|
+
}
|
|
7825
|
+
clearAll() {
|
|
7826
|
+
this.maps.clear();
|
|
7827
|
+
}
|
|
7828
|
+
owners() {
|
|
7829
|
+
return [...this.maps.keys()];
|
|
7830
|
+
}
|
|
7831
|
+
};
|
|
7832
|
+
var OwnerScopedCounter = class {
|
|
7833
|
+
constructor(scope, initial = 1) {
|
|
7834
|
+
this.scope = scope;
|
|
7835
|
+
this.initial = initial;
|
|
7836
|
+
}
|
|
7837
|
+
scope;
|
|
7838
|
+
initial;
|
|
7839
|
+
counters = /* @__PURE__ */ new Map();
|
|
7840
|
+
owner() {
|
|
7841
|
+
const owner = this.scope.currentOwner();
|
|
7842
|
+
return owner || COMPUTER_USE_STANDALONE_OWNER;
|
|
7843
|
+
}
|
|
7844
|
+
next() {
|
|
7845
|
+
const owner = this.owner();
|
|
7846
|
+
const value = this.counters.get(owner) ?? this.initial;
|
|
7847
|
+
this.counters.set(owner, value + 1);
|
|
7848
|
+
return value;
|
|
7849
|
+
}
|
|
7850
|
+
current() {
|
|
7851
|
+
return this.counters.get(this.owner()) ?? this.initial;
|
|
7852
|
+
}
|
|
7853
|
+
raiseAtLeast(value) {
|
|
7854
|
+
const owner = this.owner();
|
|
7855
|
+
this.counters.set(owner, Math.max(this.counters.get(owner) ?? this.initial, Math.trunc(value)));
|
|
7856
|
+
}
|
|
7857
|
+
clear() {
|
|
7858
|
+
this.counters.delete(this.owner());
|
|
7859
|
+
}
|
|
7860
|
+
clearAll() {
|
|
7861
|
+
this.counters.clear();
|
|
7862
|
+
}
|
|
7863
|
+
};
|
|
7864
|
+
|
|
7865
|
+
// vendor/computer-use-axi/src/browser-owner.ts
|
|
7866
|
+
import { execFile } from "node:child_process";
|
|
7867
|
+
import { rm } from "node:fs/promises";
|
|
7868
|
+
import os3 from "node:os";
|
|
7869
|
+
import path12 from "node:path";
|
|
7870
|
+
async function requestBrowserClose(port) {
|
|
7871
|
+
const response = await fetch(`http://127.0.0.1:${port}/json/version`, { signal: AbortSignal.timeout(500) });
|
|
7872
|
+
const data = await response.json();
|
|
7873
|
+
if (!data.webSocketDebuggerUrl) return;
|
|
7874
|
+
const url = new URL(data.webSocketDebuggerUrl);
|
|
7875
|
+
if (url.hostname !== "127.0.0.1" && url.hostname !== "localhost") throw new Error("Owned browser exposed a non-local debugger.");
|
|
7876
|
+
if (url.port !== port) throw new Error("Owned browser debugger port changed.");
|
|
7877
|
+
await new Promise((resolve2) => {
|
|
7878
|
+
const ws = new wrapper_default(url);
|
|
7879
|
+
let done = false;
|
|
7880
|
+
const finish = () => {
|
|
7881
|
+
if (done) return;
|
|
6924
7882
|
done = true;
|
|
6925
7883
|
clearTimeout(timer);
|
|
6926
7884
|
ws.terminate();
|
|
6927
|
-
|
|
7885
|
+
resolve2();
|
|
6928
7886
|
};
|
|
6929
7887
|
const timer = setTimeout(finish, 700);
|
|
6930
7888
|
ws.on("open", () => ws.send(JSON.stringify({ id: 1, method: "Browser.close" })));
|
|
@@ -6935,15 +7893,15 @@ async function requestBrowserClose(port) {
|
|
|
6935
7893
|
}
|
|
6936
7894
|
async function waitForExit(child, timeoutMs) {
|
|
6937
7895
|
if (child.exitCode !== null || child.signalCode !== null) return true;
|
|
6938
|
-
return new Promise((
|
|
7896
|
+
return new Promise((resolve2) => {
|
|
6939
7897
|
const finish = () => {
|
|
6940
7898
|
clearTimeout(timer);
|
|
6941
7899
|
child.off("exit", finish);
|
|
6942
|
-
|
|
7900
|
+
resolve2(true);
|
|
6943
7901
|
};
|
|
6944
7902
|
const timer = setTimeout(() => {
|
|
6945
7903
|
child.off("exit", finish);
|
|
6946
|
-
|
|
7904
|
+
resolve2(false);
|
|
6947
7905
|
}, timeoutMs);
|
|
6948
7906
|
child.once("exit", finish);
|
|
6949
7907
|
});
|
|
@@ -6954,11 +7912,11 @@ async function releaseOwnedBrowser(child, port, profile) {
|
|
|
6954
7912
|
if (!await waitForExit(child, 700)) {
|
|
6955
7913
|
if (process.platform === "win32" && child.pid) {
|
|
6956
7914
|
const pid = child.pid;
|
|
6957
|
-
await new Promise((
|
|
7915
|
+
await new Promise((resolve2) => {
|
|
6958
7916
|
execFile("taskkill.exe", ["/PID", String(pid), "/T", "/F"], {
|
|
6959
7917
|
windowsHide: true,
|
|
6960
7918
|
timeout: 2e3
|
|
6961
|
-
}, () =>
|
|
7919
|
+
}, () => resolve2());
|
|
6962
7920
|
});
|
|
6963
7921
|
if (!await waitForExit(child, 1500)) {
|
|
6964
7922
|
try {
|
|
@@ -6979,8 +7937,8 @@ async function releaseOwnedBrowser(child, port, profile) {
|
|
|
6979
7937
|
}
|
|
6980
7938
|
}
|
|
6981
7939
|
if (profile) {
|
|
6982
|
-
const resolved =
|
|
6983
|
-
if (
|
|
7940
|
+
const resolved = path12.resolve(profile);
|
|
7941
|
+
if (path12.dirname(resolved) !== path12.resolve(os3.tmpdir()) || !/^pie-(chrome|helium)-cdp-/.test(path12.basename(resolved))) {
|
|
6984
7942
|
throw new Error("Refusing to remove a profile outside the owned temporary browser directory.");
|
|
6985
7943
|
}
|
|
6986
7944
|
await rm(resolved, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
@@ -6992,7 +7950,7 @@ async function releaseOwnedBrowserState(owner) {
|
|
|
6992
7950
|
owner.managedBrowserProfileDir = void 0;
|
|
6993
7951
|
}
|
|
6994
7952
|
|
|
6995
|
-
//
|
|
7953
|
+
// vendor/computer-use-axi/src/root-selection.ts
|
|
6996
7954
|
function scoreWindow(window) {
|
|
6997
7955
|
let score = 0;
|
|
6998
7956
|
if (window.isModal) score += 180;
|
|
@@ -7011,11 +7969,14 @@ function shouldPreferForegroundModalWindow(current, candidate) {
|
|
|
7011
7969
|
return candidateIsInFront && scoreWindow(candidate) >= scoreWindow(current);
|
|
7012
7970
|
}
|
|
7013
7971
|
|
|
7014
|
-
//
|
|
7972
|
+
// vendor/computer-use-axi/src/state.ts
|
|
7015
7973
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7016
7974
|
var SavedStates = class {
|
|
7017
|
-
store
|
|
7975
|
+
store;
|
|
7018
7976
|
operations = new AsyncLocalStorage();
|
|
7977
|
+
constructor(store = new StateStore(128)) {
|
|
7978
|
+
this.store = store;
|
|
7979
|
+
}
|
|
7019
7980
|
current() {
|
|
7020
7981
|
const state = this.operations.getStore();
|
|
7021
7982
|
if (!state) throw new Error("Computer-use operation state is unavailable.");
|
|
@@ -7047,6 +8008,9 @@ var SavedStates = class {
|
|
|
7047
8008
|
currentOutline: outline2,
|
|
7048
8009
|
resourceKey: record.resourceKey,
|
|
7049
8010
|
epoch: record.epoch,
|
|
8011
|
+
owner: record.owner,
|
|
8012
|
+
generation: record.generation,
|
|
8013
|
+
helperGeneration: record.helperGeneration,
|
|
7050
8014
|
browserSnapshot: record.value.snapshot,
|
|
7051
8015
|
contextId: record.value.snapshot.contextId
|
|
7052
8016
|
};
|
|
@@ -7061,7 +8025,10 @@ var SavedStates = class {
|
|
|
7061
8025
|
currentOutline: outline,
|
|
7062
8026
|
currentNote: record.value.note ? structuredClone(record.value.note) : void 0,
|
|
7063
8027
|
resourceKey: record.resourceKey,
|
|
7064
|
-
epoch: record.epoch
|
|
8028
|
+
epoch: record.epoch,
|
|
8029
|
+
owner: record.owner,
|
|
8030
|
+
generation: record.generation,
|
|
8031
|
+
helperGeneration: record.helperGeneration
|
|
7065
8032
|
};
|
|
7066
8033
|
}
|
|
7067
8034
|
saveDesktop(state, resourceKey, epoch) {
|
|
@@ -7070,6 +8037,9 @@ var SavedStates = class {
|
|
|
7070
8037
|
stateId: state.currentCapture.stateId,
|
|
7071
8038
|
resourceKey,
|
|
7072
8039
|
epoch,
|
|
8040
|
+
owner: state.owner,
|
|
8041
|
+
generation: state.generation,
|
|
8042
|
+
helperGeneration: state.helperGeneration,
|
|
7073
8043
|
value: {
|
|
7074
8044
|
kind: "desktop",
|
|
7075
8045
|
target: { ...state.currentTarget },
|
|
@@ -7090,7 +8060,7 @@ var SavedStates = class {
|
|
|
7090
8060
|
}
|
|
7091
8061
|
};
|
|
7092
8062
|
|
|
7093
|
-
//
|
|
8063
|
+
// vendor/computer-use-axi/src/view.ts
|
|
7094
8064
|
function numericRef(ref) {
|
|
7095
8065
|
const match = /^@e(\d+)$/.exec(ref);
|
|
7096
8066
|
return match ? Number(match[1]) : 0;
|
|
@@ -7226,7 +8196,7 @@ function renderChanges(changes) {
|
|
|
7226
8196
|
}).join("\n");
|
|
7227
8197
|
}
|
|
7228
8198
|
|
|
7229
|
-
//
|
|
8199
|
+
// vendor/computer-use-axi/src/bridge.ts
|
|
7230
8200
|
var MISSING_TARGET_ERROR = "No current controlled window. Call observe_ui first to choose a target window.";
|
|
7231
8201
|
var CURRENT_TARGET_GONE_ERROR = "The current controlled window is no longer available. Call observe_ui to choose a new target window.";
|
|
7232
8202
|
var COMMAND_TIMEOUT_MS5 = 15e3;
|
|
@@ -7237,17 +8207,51 @@ var MANAGED_BROWSER_READY_TIMEOUT_MS = 15e3;
|
|
|
7237
8207
|
var AUTO_IMAGE_MAX_DIMENSION = 900;
|
|
7238
8208
|
var EXPLICIT_IMAGE_MAX_DIMENSION = 1600;
|
|
7239
8209
|
var BROWSER_TRANSACTION_ACTIONS = /* @__PURE__ */ new Set(["press", "click", "setText", "typeText", "keypress", "scroll", "drag", "moveMouse"]);
|
|
8210
|
+
var ownerScope = {
|
|
8211
|
+
currentOwner: () => savedStates.operations.getStore()?.owner ?? COMPUTER_USE_STANDALONE_OWNER
|
|
8212
|
+
};
|
|
7240
8213
|
var runtimeState = {
|
|
7241
8214
|
lastPermissionCheckAt: 0,
|
|
7242
|
-
windowRefs:
|
|
7243
|
-
windowRefByIdentity:
|
|
7244
|
-
browserRootByContext:
|
|
7245
|
-
browserContextByRoot:
|
|
7246
|
-
nextRootRefIndex: 1
|
|
8215
|
+
windowRefs: new OwnerScopedMap(ownerScope),
|
|
8216
|
+
windowRefByIdentity: new OwnerScopedMap(ownerScope),
|
|
8217
|
+
browserRootByContext: new OwnerScopedMap(ownerScope),
|
|
8218
|
+
browserContextByRoot: new OwnerScopedMap(ownerScope),
|
|
8219
|
+
nextRootRefIndex: new OwnerScopedCounter(ownerScope, 1)
|
|
7247
8220
|
};
|
|
7248
|
-
var
|
|
8221
|
+
var engineGeneration = "";
|
|
8222
|
+
var helperGeneration = "";
|
|
8223
|
+
var engineArtifacts;
|
|
8224
|
+
function createStateStore() {
|
|
8225
|
+
return new StateStore(128, (record) => {
|
|
8226
|
+
engineArtifacts?.dropForState(record.stateId);
|
|
8227
|
+
});
|
|
8228
|
+
}
|
|
8229
|
+
var savedStates = new SavedStates(createStateStore());
|
|
7249
8230
|
var resourceScheduler = new ResourceScheduler();
|
|
7250
8231
|
var shutdownInFlight;
|
|
8232
|
+
function configureEngineRuntime(options) {
|
|
8233
|
+
engineGeneration = options.generation;
|
|
8234
|
+
if (options.helperGeneration !== void 0) helperGeneration = options.helperGeneration;
|
|
8235
|
+
engineArtifacts = options.artifacts;
|
|
8236
|
+
expiredStateIds = new Set(options.expiredStateIds ?? []);
|
|
8237
|
+
previousBrokerGeneration = options.previousGeneration ?? "";
|
|
8238
|
+
}
|
|
8239
|
+
function bumpHelperGeneration() {
|
|
8240
|
+
helperGeneration = randomUUID7();
|
|
8241
|
+
return helperGeneration;
|
|
8242
|
+
}
|
|
8243
|
+
function expireStatesAfterHelperRestart(reason = "the native helper restarted") {
|
|
8244
|
+
const ids = savedStates.store.ids?.() ?? [];
|
|
8245
|
+
for (const id of ids) expiredStateReasons.set(id, reason);
|
|
8246
|
+
savedStates.clear();
|
|
8247
|
+
return ids;
|
|
8248
|
+
}
|
|
8249
|
+
function expireStateForArtifactEviction(stateId2) {
|
|
8250
|
+
expiredStateReasons.set(stateId2, "its image artifact was evicted");
|
|
8251
|
+
savedStates.store.drop?.(stateId2);
|
|
8252
|
+
}
|
|
8253
|
+
function persistRuntimeBridgeState() {
|
|
8254
|
+
}
|
|
7251
8255
|
function operationState() {
|
|
7252
8256
|
return savedStates.current();
|
|
7253
8257
|
}
|
|
@@ -7289,11 +8293,11 @@ async function releaseComputerUseSession() {
|
|
|
7289
8293
|
}
|
|
7290
8294
|
savedStates.clear();
|
|
7291
8295
|
clearStoredOutputs();
|
|
7292
|
-
runtimeState.windowRefs.
|
|
7293
|
-
runtimeState.windowRefByIdentity.
|
|
7294
|
-
runtimeState.browserRootByContext.
|
|
7295
|
-
runtimeState.browserContextByRoot.
|
|
7296
|
-
runtimeState.nextRootRefIndex
|
|
8296
|
+
runtimeState.windowRefs.clearAll();
|
|
8297
|
+
runtimeState.windowRefByIdentity.clearAll();
|
|
8298
|
+
runtimeState.browserRootByContext.clearAll();
|
|
8299
|
+
runtimeState.browserContextByRoot.clearAll();
|
|
8300
|
+
runtimeState.nextRootRefIndex.clearAll();
|
|
7297
8301
|
runtimeState.permissionStatus = void 0;
|
|
7298
8302
|
runtimeState.helperDiagnostics = void 0;
|
|
7299
8303
|
runtimeState.lastPermissionCheckAt = 0;
|
|
@@ -7341,10 +8345,10 @@ function throwIfAborted3(signal) {
|
|
|
7341
8345
|
async function sleep2(ms, signal) {
|
|
7342
8346
|
if (ms <= 0) return;
|
|
7343
8347
|
throwIfAborted3(signal);
|
|
7344
|
-
await new Promise((
|
|
8348
|
+
await new Promise((resolve2, reject) => {
|
|
7345
8349
|
const timer = setTimeout(() => {
|
|
7346
8350
|
cleanup();
|
|
7347
|
-
|
|
8351
|
+
resolve2();
|
|
7348
8352
|
}, ms);
|
|
7349
8353
|
const onAbort = () => {
|
|
7350
8354
|
cleanup();
|
|
@@ -7364,8 +8368,9 @@ async function withWindowWriteLock(target, work) {
|
|
|
7364
8368
|
const result = await resourceScheduler.write(key, baseEpoch, async (nextEpoch) => {
|
|
7365
8369
|
state.resourceKey = key;
|
|
7366
8370
|
state.epoch = nextEpoch;
|
|
8371
|
+
state.onDispatch?.();
|
|
7367
8372
|
return await work();
|
|
7368
|
-
});
|
|
8373
|
+
}, state.signal);
|
|
7369
8374
|
return result.value;
|
|
7370
8375
|
}
|
|
7371
8376
|
function trimOrUndefined(value) {
|
|
@@ -7376,9 +8381,6 @@ function trimOrUndefined(value) {
|
|
|
7376
8381
|
function normalizeText2(value) {
|
|
7377
8382
|
return (value ?? "").trim().toLowerCase();
|
|
7378
8383
|
}
|
|
7379
|
-
function toBoolean3(value) {
|
|
7380
|
-
return value === true;
|
|
7381
|
-
}
|
|
7382
8384
|
function outlineNodeCenter(node) {
|
|
7383
8385
|
if (!node.rect) {
|
|
7384
8386
|
throw new Error(`Outline ref '${node.ref}' has no full-look coordinates after scoped expansion. Re-observe for coordinates.`);
|
|
@@ -7546,18 +8548,20 @@ function storeWindowRef(record) {
|
|
|
7546
8548
|
return updated;
|
|
7547
8549
|
}
|
|
7548
8550
|
}
|
|
7549
|
-
const ref = `@r${runtimeState.nextRootRefIndex
|
|
8551
|
+
const ref = `@r${runtimeState.nextRootRefIndex.next()}`;
|
|
7550
8552
|
const stored = { ...record, ref };
|
|
7551
8553
|
runtimeState.windowRefByIdentity.set(identity, ref);
|
|
7552
8554
|
runtimeState.windowRefs.set(ref, stored);
|
|
8555
|
+
persistRuntimeBridgeState();
|
|
7553
8556
|
return stored;
|
|
7554
8557
|
}
|
|
7555
8558
|
function storeBrowserRootRef(contextId) {
|
|
7556
8559
|
const existing = runtimeState.browserRootByContext.get(contextId);
|
|
7557
8560
|
if (existing) return existing;
|
|
7558
|
-
const ref = `@r${runtimeState.nextRootRefIndex
|
|
8561
|
+
const ref = `@r${runtimeState.nextRootRefIndex.next()}`;
|
|
7559
8562
|
runtimeState.browserRootByContext.set(contextId, ref);
|
|
7560
8563
|
runtimeState.browserContextByRoot.set(ref, contextId);
|
|
8564
|
+
persistRuntimeBridgeState();
|
|
7561
8565
|
return ref;
|
|
7562
8566
|
}
|
|
7563
8567
|
function storeWindowRefForTarget(target) {
|
|
@@ -7792,7 +8796,7 @@ async function ensureTargetWindowId(target, signal) {
|
|
|
7792
8796
|
}
|
|
7793
8797
|
function captureForLook(look) {
|
|
7794
8798
|
return {
|
|
7795
|
-
stateId:
|
|
8799
|
+
stateId: randomUUID7(),
|
|
7796
8800
|
width: look.image?.width ?? 0,
|
|
7797
8801
|
height: look.image?.height ?? 0,
|
|
7798
8802
|
scaleFactor: look.window.scaleFactor,
|
|
@@ -7853,6 +8857,33 @@ async function buildToolResult(tool, summary, result, execution, _signal, imageM
|
|
|
7853
8857
|
const folded = foldToBudget(result.outline);
|
|
7854
8858
|
const renderedNote = renderNote(state.currentNote);
|
|
7855
8859
|
const failure = tool === "act_ui" ? actionFailureMessage(execution) : void 0;
|
|
8860
|
+
const image = result.look.image;
|
|
8861
|
+
let artifact;
|
|
8862
|
+
if (image?.jpegBase64 && engineArtifacts) {
|
|
8863
|
+
const bytes = Buffer.from(image.jpegBase64, "base64");
|
|
8864
|
+
const record = engineArtifacts.put({
|
|
8865
|
+
owner: state.owner ?? COMPUTER_USE_STANDALONE_OWNER,
|
|
8866
|
+
generation: state.generation ?? engineGeneration,
|
|
8867
|
+
stateId: result.capture.stateId,
|
|
8868
|
+
mimeType: image.mimeType ?? "image/jpeg",
|
|
8869
|
+
width: image.width,
|
|
8870
|
+
height: image.height,
|
|
8871
|
+
bytes
|
|
8872
|
+
});
|
|
8873
|
+
state.artifactIds ??= [];
|
|
8874
|
+
state.artifactIds.push(record.artifactId);
|
|
8875
|
+
artifact = {
|
|
8876
|
+
artifactId: record.artifactId,
|
|
8877
|
+
path: record.absolutePath,
|
|
8878
|
+
mimeType: record.mimeType,
|
|
8879
|
+
width: record.width,
|
|
8880
|
+
height: record.height,
|
|
8881
|
+
sha256: record.hash,
|
|
8882
|
+
bytes: record.bytes,
|
|
8883
|
+
generation: record.generation,
|
|
8884
|
+
stateId: record.stateId
|
|
8885
|
+
};
|
|
8886
|
+
}
|
|
7856
8887
|
const details = {
|
|
7857
8888
|
tool,
|
|
7858
8889
|
target: {
|
|
@@ -7870,7 +8901,8 @@ async function buildToolResult(tool, summary, result, execution, _signal, imageM
|
|
|
7870
8901
|
height: result.capture.height,
|
|
7871
8902
|
scaleFactor: result.capture.scaleFactor,
|
|
7872
8903
|
timestamp: result.capture.timestamp,
|
|
7873
|
-
coordinateSpace: "window-relative-screenshot-pixels"
|
|
8904
|
+
coordinateSpace: "window-relative-screenshot-pixels",
|
|
8905
|
+
...artifact ? { artifact } : {}
|
|
7874
8906
|
},
|
|
7875
8907
|
lookId: result.look.lookId,
|
|
7876
8908
|
view: useDiff ? "diff" : "full",
|
|
@@ -7914,12 +8946,15 @@ ${folded.text}`;
|
|
|
7914
8946
|
const fallbackText = fallbackReason ? `
|
|
7915
8947
|
|
|
7916
8948
|
${fallbackReason.message}` : "";
|
|
8949
|
+
const artifactText = artifact ? `
|
|
8950
|
+
|
|
8951
|
+
Image evidence: ${artifact.path} (${artifact.width}x${artifact.height} ${artifact.mimeType}, sha256 ${artifact.sha256}, stateId ${artifact.stateId}, generation ${artifact.generation}). Call read_file on that absolute path before using coordinates; the image belongs only to stateId ${artifact.stateId}.` : "";
|
|
7917
8952
|
const deltaText = rootDeltaLines(execution).join("\n");
|
|
7918
8953
|
const outcomeSummary = failure ? `UI action failed (${failure}). Some actions may already have been delivered; do not replay blindly.` : summary;
|
|
7919
8954
|
const content = [{ type: "text", text: `${outcomeSummary}${deltaText ? `
|
|
7920
|
-
${deltaText}` : ""}${consoleText}${noteText}${outlineText}${fallbackText}` }];
|
|
7921
|
-
if (fallbackReason &&
|
|
7922
|
-
content.push({ type: "image", data:
|
|
8955
|
+
${deltaText}` : ""}${consoleText}${noteText}${outlineText}${fallbackText}${artifactText}` }];
|
|
8956
|
+
if (!artifact && fallbackReason && image?.jpegBase64) {
|
|
8957
|
+
content.push({ type: "image", data: image.jpegBase64, mimeType: image.mimeType ?? "image/jpeg" });
|
|
7923
8958
|
}
|
|
7924
8959
|
return { content, details, ...failure ? { isError: true } : {} };
|
|
7925
8960
|
}
|
|
@@ -7947,7 +8982,7 @@ function modelRefForRootDelta(delta) {
|
|
|
7947
8982
|
for (const record2 of runtimeState.windowRefs.values()) {
|
|
7948
8983
|
if (record2.nativeWindowRef === delta.ref || record2.ref === delta.ref) return record2.ref;
|
|
7949
8984
|
}
|
|
7950
|
-
const ref = `@r${runtimeState.nextRootRefIndex
|
|
8985
|
+
const ref = `@r${runtimeState.nextRootRefIndex.next()}`;
|
|
7951
8986
|
const current = operationState().currentTarget;
|
|
7952
8987
|
const record = {
|
|
7953
8988
|
ref,
|
|
@@ -7965,6 +9000,7 @@ function modelRefForRootDelta(delta) {
|
|
|
7965
9000
|
};
|
|
7966
9001
|
runtimeState.windowRefs.set(ref, record);
|
|
7967
9002
|
runtimeState.windowRefByIdentity.set(windowRecordIdentity(record), ref);
|
|
9003
|
+
persistRuntimeBridgeState();
|
|
7968
9004
|
return ref;
|
|
7969
9005
|
}
|
|
7970
9006
|
function executionTraceFromAct(result, policy = currentDeliveryPolicy()) {
|
|
@@ -8174,12 +9210,22 @@ async function withBrowserWrite(contextId, work) {
|
|
|
8174
9210
|
const result = await resourceScheduler.write(resourceKey, baseEpoch, async (nextEpoch) => {
|
|
8175
9211
|
state.resourceKey = resourceKey;
|
|
8176
9212
|
state.epoch = nextEpoch;
|
|
9213
|
+
state.onDispatch?.();
|
|
8177
9214
|
return await work();
|
|
8178
|
-
});
|
|
9215
|
+
}, state.signal);
|
|
8179
9216
|
return result.value;
|
|
8180
9217
|
}
|
|
8181
9218
|
function browserObservationResult(browser, resourceKey, epoch, tool, base) {
|
|
8182
|
-
|
|
9219
|
+
const operation = operationState();
|
|
9220
|
+
savedStates.set({
|
|
9221
|
+
stateId: browser.snapshotId,
|
|
9222
|
+
resourceKey,
|
|
9223
|
+
epoch,
|
|
9224
|
+
owner: operation.owner,
|
|
9225
|
+
generation: operation.generation,
|
|
9226
|
+
helperGeneration: operation.helperGeneration,
|
|
9227
|
+
value: { kind: "browser", snapshot: browser, outline: browser.outline }
|
|
9228
|
+
});
|
|
8183
9229
|
const currentOutline = restoreOutline(browser.outline);
|
|
8184
9230
|
const transition = base ? changesBetween(restoreOutline(base.outline), currentOutline) : void 0;
|
|
8185
9231
|
const useDiff = Boolean(transition && !transition.useFullView);
|
|
@@ -8215,8 +9261,8 @@ function sliceText(value, offsetValue, _limitValue) {
|
|
|
8215
9261
|
async function performReadText(params, signal) {
|
|
8216
9262
|
const ref = trimOrUndefined(params.ref);
|
|
8217
9263
|
if (ref?.startsWith("@o")) {
|
|
8218
|
-
const page = readStoredOutput(ref, params.offset);
|
|
8219
|
-
if (!page) throw new Error(`Output ref '${ref}' is unavailable or
|
|
9264
|
+
const page = readStoredOutput(ref, params.offset, operationState().owner ?? COMPUTER_USE_STANDALONE_OWNER);
|
|
9265
|
+
if (!page) throw new Error(`Output ref '${ref}' is unavailable, was evicted, or belongs to another session. Rerun the focused query.`);
|
|
8220
9266
|
const details2 = { tool: "read_text", ref, ...page };
|
|
8221
9267
|
const suffix = page.hasMore ? `
|
|
8222
9268
|
|
|
@@ -8269,7 +9315,7 @@ function conditionScopeRef(params) {
|
|
|
8269
9315
|
if (ref && scopeRef) throw new Error("A UI condition accepts ref or scopeRef, not both.");
|
|
8270
9316
|
return ref ?? scopeRef;
|
|
8271
9317
|
}
|
|
8272
|
-
function
|
|
9318
|
+
function validateCondition2(params) {
|
|
8273
9319
|
const text = trimOrUndefined(params.text);
|
|
8274
9320
|
const role = trimOrUndefined(params.role);
|
|
8275
9321
|
const value = trimOrUndefined(params.value);
|
|
@@ -8284,7 +9330,7 @@ function expectCondition(expect) {
|
|
|
8284
9330
|
const hasMatcher = Boolean(trimOrUndefined(expect.text) ?? trimOrUndefined(expect.role) ?? trimOrUndefined(expect.value));
|
|
8285
9331
|
const hasScope = Boolean(conditionScopeRef(expect));
|
|
8286
9332
|
if (!hasMatcher && !hasScope) return void 0;
|
|
8287
|
-
return
|
|
9333
|
+
return validateCondition2(expect);
|
|
8288
9334
|
}
|
|
8289
9335
|
function nodeWithinScope(node, scopeRef, scopeExact) {
|
|
8290
9336
|
if (!scopeRef) return true;
|
|
@@ -8313,7 +9359,7 @@ function conditionScopeNode(outline, condition) {
|
|
|
8313
9359
|
}
|
|
8314
9360
|
async function performWaitFor(params, signal) {
|
|
8315
9361
|
const contextId = operationState().contextId;
|
|
8316
|
-
const condition =
|
|
9362
|
+
const condition = validateCondition2(params);
|
|
8317
9363
|
const { text, role, value, scopeRef, scopeExact, gone, timeoutMs } = condition;
|
|
8318
9364
|
if (isBrowserContextId(contextId)) {
|
|
8319
9365
|
const state2 = operationState();
|
|
@@ -8403,7 +9449,7 @@ function sameRootIdentity(a, b) {
|
|
|
8403
9449
|
}
|
|
8404
9450
|
async function performObserve(params, signal) {
|
|
8405
9451
|
const requestedRoot = typeof params.root === "string" ? params.root : void 0;
|
|
8406
|
-
if (requestedRoot && !/^@r\d+$/.test(requestedRoot)) throw new Error("
|
|
9452
|
+
if (requestedRoot && !/^@r\d+$/.test(requestedRoot)) throw new Error("observe --root must be an exact @r ref issued by roots.");
|
|
8407
9453
|
const browserContextId = requestedRoot ? runtimeState.browserContextByRoot.get(requestedRoot) : void 0;
|
|
8408
9454
|
if (isBrowserContextId(browserContextId)) {
|
|
8409
9455
|
const targetId = browserContextId.slice(BROWSER_CONTEXT_PREFIX.length);
|
|
@@ -8809,8 +9855,8 @@ async function performBrowserTransaction(params, actions, signal) {
|
|
|
8809
9855
|
return await refreshBrowserSnapshot(contextId, "act_ui", baseView);
|
|
8810
9856
|
});
|
|
8811
9857
|
}
|
|
8812
|
-
function normalizeActionPath(
|
|
8813
|
-
return (
|
|
9858
|
+
function normalizeActionPath(path15) {
|
|
9859
|
+
return (path15 ?? []).map((point) => Array.isArray(point) ? { x: toFiniteNumber(point[0], 0), y: toFiniteNumber(point[1], 0) } : { x: toFiniteNumber(point.x, 0), y: toFiniteNumber(point.y, 0) });
|
|
8814
9860
|
}
|
|
8815
9861
|
function validateActionTarget(action) {
|
|
8816
9862
|
const hasRef = Boolean(trimOrUndefined(action.ref));
|
|
@@ -8827,7 +9873,7 @@ function validateActionTarget(action) {
|
|
|
8827
9873
|
}
|
|
8828
9874
|
async function performAct(params, signal) {
|
|
8829
9875
|
const actions = Array.isArray(params.actions) ? params.actions : [];
|
|
8830
|
-
if (actions.length === 0) throw new Error("
|
|
9876
|
+
if (actions.length === 0) throw new Error("act --actions must contain at least one action.");
|
|
8831
9877
|
if (actions.length > 20) throw new Error("act_ui supports at most 20 actions per transaction.");
|
|
8832
9878
|
for (const action of actions) validateActionTarget(action);
|
|
8833
9879
|
if (operationState().contextId) return await performBrowserTransaction(params, actions, signal);
|
|
@@ -8836,25 +9882,25 @@ async function performAct(params, signal) {
|
|
|
8836
9882
|
function managedBrowserExecutableCandidates(browser) {
|
|
8837
9883
|
const overrideName = browser === "helium" ? "PIE_COMPUTER_USE_HELIUM_EXECUTABLE" : "PIE_COMPUTER_USE_CHROME_EXECUTABLE";
|
|
8838
9884
|
const override = trimOrUndefined(process.env[overrideName]);
|
|
8839
|
-
if (override) return [
|
|
9885
|
+
if (override) return [path13.resolve(override)];
|
|
8840
9886
|
const platformCandidates = process.platform === "darwin" ? browser === "helium" ? [
|
|
8841
9887
|
"/Applications/Helium.app/Contents/MacOS/Helium",
|
|
8842
|
-
|
|
9888
|
+
path13.join(os4.homedir(), "Applications", "Helium.app", "Contents", "MacOS", "Helium")
|
|
8843
9889
|
] : [
|
|
8844
9890
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
8845
|
-
|
|
9891
|
+
path13.join(os4.homedir(), "Applications", "Google Chrome.app", "Contents", "MacOS", "Google Chrome")
|
|
8846
9892
|
] : process.platform === "win32" ? browser === "helium" ? [
|
|
8847
|
-
process.env.LOCALAPPDATA &&
|
|
8848
|
-
process.env.PROGRAMFILES &&
|
|
9893
|
+
process.env.LOCALAPPDATA && path13.join(process.env.LOCALAPPDATA, "Helium", "Application", "helium.exe"),
|
|
9894
|
+
process.env.PROGRAMFILES && path13.join(process.env.PROGRAMFILES, "Helium", "Application", "helium.exe")
|
|
8849
9895
|
] : [
|
|
8850
|
-
process.env.LOCALAPPDATA &&
|
|
8851
|
-
process.env.PROGRAMFILES &&
|
|
8852
|
-
process.env["PROGRAMFILES(X86)"] &&
|
|
9896
|
+
process.env.LOCALAPPDATA && path13.join(process.env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe"),
|
|
9897
|
+
process.env.PROGRAMFILES && path13.join(process.env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe"),
|
|
9898
|
+
process.env["PROGRAMFILES(X86)"] && path13.join(process.env["PROGRAMFILES(X86)"], "Google", "Chrome", "Application", "chrome.exe")
|
|
8853
9899
|
] : browser === "helium" ? [
|
|
8854
9900
|
"/usr/bin/helium",
|
|
8855
9901
|
"/usr/bin/helium-browser",
|
|
8856
9902
|
"/opt/helium/chrome",
|
|
8857
|
-
|
|
9903
|
+
path13.join(os4.homedir(), ".local", "helium", "chrome")
|
|
8858
9904
|
] : [
|
|
8859
9905
|
"/usr/bin/google-chrome",
|
|
8860
9906
|
"/usr/bin/google-chrome-stable",
|
|
@@ -8863,7 +9909,7 @@ function managedBrowserExecutableCandidates(browser) {
|
|
|
8863
9909
|
"/snap/bin/chromium"
|
|
8864
9910
|
];
|
|
8865
9911
|
const pathNames = browser === "helium" ? process.platform === "win32" ? ["helium.exe"] : ["helium", "helium-browser"] : process.platform === "win32" ? ["chrome.exe"] : ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"];
|
|
8866
|
-
const pathCandidates = (process.env.PATH ?? "").split(
|
|
9912
|
+
const pathCandidates = (process.env.PATH ?? "").split(path13.delimiter).filter(Boolean).flatMap((directory) => pathNames.map((name) => path13.join(directory, name)));
|
|
8867
9913
|
return [.../* @__PURE__ */ new Set([...platformCandidates.filter((candidate) => Boolean(candidate)), ...pathCandidates])];
|
|
8868
9914
|
}
|
|
8869
9915
|
async function managedBrowserExecutable(browser) {
|
|
@@ -8882,13 +9928,13 @@ async function managedBrowserExecutable(browser) {
|
|
|
8882
9928
|
throw new Error(`${browser} executable was not found. Set ${overrideName} to its absolute path.`);
|
|
8883
9929
|
}
|
|
8884
9930
|
function freeTcpPort() {
|
|
8885
|
-
return new Promise((
|
|
9931
|
+
return new Promise((resolve2, reject) => {
|
|
8886
9932
|
const server = net2.createServer();
|
|
8887
9933
|
server.on("error", reject);
|
|
8888
9934
|
server.listen(0, "127.0.0.1", () => {
|
|
8889
9935
|
const address = server.address();
|
|
8890
9936
|
const port = typeof address === "object" && address ? address.port : 0;
|
|
8891
|
-
server.close(() => port > 0 ?
|
|
9937
|
+
server.close(() => port > 0 ? resolve2(port) : reject(new Error("Could not allocate a local CDP port.")));
|
|
8892
9938
|
});
|
|
8893
9939
|
});
|
|
8894
9940
|
}
|
|
@@ -8914,7 +9960,7 @@ async function performLaunchBrowser(params, signal) {
|
|
|
8914
9960
|
const url = requestedUrl ?? "about:blank";
|
|
8915
9961
|
await disconnectCdp();
|
|
8916
9962
|
await releaseOwnedBrowserState(runtimeState);
|
|
8917
|
-
const profileDir =
|
|
9963
|
+
const profileDir = mkdtempSync(path13.join(os4.tmpdir(), `pie-${browser}-cdp-`));
|
|
8918
9964
|
const args = [
|
|
8919
9965
|
`--remote-debugging-port=${port}`,
|
|
8920
9966
|
`--user-data-dir=${profileDir}`,
|
|
@@ -8925,11 +9971,20 @@ async function performLaunchBrowser(params, signal) {
|
|
|
8925
9971
|
if (runtimeState.previousCdpPort === void 0 && runtimeState.managedBrowserCdpPort === void 0) {
|
|
8926
9972
|
runtimeState.previousCdpPort = process.env.PIE_COMPUTER_USE_CDP_PORT;
|
|
8927
9973
|
}
|
|
8928
|
-
const managedBrowser = spawn4(executable, args, { stdio: "ignore", detached:
|
|
9974
|
+
const managedBrowser = spawn4(executable, args, { stdio: "ignore", detached: true });
|
|
8929
9975
|
managedBrowser.unref();
|
|
9976
|
+
const stalePid = runtimeState.managedBrowserPid;
|
|
9977
|
+
if (stalePid !== void 0 && stalePid !== managedBrowser.pid) {
|
|
9978
|
+
try {
|
|
9979
|
+
process.kill(stalePid, "SIGTERM");
|
|
9980
|
+
} catch {
|
|
9981
|
+
}
|
|
9982
|
+
}
|
|
8930
9983
|
runtimeState.managedBrowser = managedBrowser;
|
|
8931
9984
|
runtimeState.managedBrowserCdpPort = String(port);
|
|
9985
|
+
runtimeState.managedBrowserPid = managedBrowser.pid;
|
|
8932
9986
|
runtimeState.managedBrowserProfileDir = profileDir;
|
|
9987
|
+
persistRuntimeBridgeState();
|
|
8933
9988
|
process.env.PIE_COMPUTER_USE_CDP_PORT = String(port);
|
|
8934
9989
|
try {
|
|
8935
9990
|
await waitForCdpPort(port, signal);
|
|
@@ -8986,447 +10041,806 @@ async function performEvaluateBrowser(params) {
|
|
|
8986
10041
|
return { content: [...successor.content, { type: "text", text: `Evaluation value: ${JSON.stringify(result.value)}` }], details };
|
|
8987
10042
|
});
|
|
8988
10043
|
}
|
|
8989
|
-
|
|
10044
|
+
var expiredStateIds = /* @__PURE__ */ new Set();
|
|
10045
|
+
var previousBrokerGeneration = "";
|
|
10046
|
+
var expiredStateReasons = /* @__PURE__ */ new Map();
|
|
10047
|
+
function assertStateAccess(stateId2, record, operation) {
|
|
10048
|
+
const owner = operation.owner ?? COMPUTER_USE_STANDALONE_OWNER;
|
|
10049
|
+
if (record.owner !== void 0 && record.owner !== owner) {
|
|
10050
|
+
throw new Error(`State '${stateId2}' is not available to this session. Observe the root again in this session.`);
|
|
10051
|
+
}
|
|
10052
|
+
if (record.generation !== void 0 && operation.generation !== void 0 && record.generation !== operation.generation) {
|
|
10053
|
+
throw new Error(`State '${stateId2}' expired (broker generation changed from ${record.generation} to ${operation.generation}). Observe the root again.`);
|
|
10054
|
+
}
|
|
10055
|
+
if (record.helperGeneration !== void 0 && operation.helperGeneration !== void 0 && record.helperGeneration !== operation.helperGeneration) {
|
|
10056
|
+
throw new Error(`State '${stateId2}' expired after the native helper restarted. Observe the root again.`);
|
|
10057
|
+
}
|
|
10058
|
+
}
|
|
10059
|
+
async function executeTool(tool, ctx, params, signal, run, runtime) {
|
|
8990
10060
|
if (shutdownInFlight) throw new Error("Computer-use resources are shutting down.");
|
|
8991
10061
|
const outputRef = tool === "read_text" && trimOrUndefined(params?.ref)?.startsWith("@o") === true;
|
|
8992
10062
|
const requestedStateId = outputRef ? void 0 : trimOrUndefined(params?.stateId);
|
|
8993
10063
|
const stateRecord = requestedStateId ? savedStates.get(requestedStateId) : void 0;
|
|
8994
10064
|
if (requestedStateId && !stateRecord) {
|
|
10065
|
+
const reason = expiredStateReasons.get(requestedStateId);
|
|
10066
|
+
if (reason) {
|
|
10067
|
+
throw new Error(`State '${requestedStateId}' expired because ${reason}. Observe the root again; old state is never replayed.`);
|
|
10068
|
+
}
|
|
10069
|
+
if (expiredStateIds.has(requestedStateId)) {
|
|
10070
|
+
throw new Error(`State '${requestedStateId}' expired (previous broker generation ${previousBrokerGeneration || "unknown"}). Observe the root again; old CLI cache is not restored.`);
|
|
10071
|
+
}
|
|
8995
10072
|
throw new Error(`State '${requestedStateId}' is unavailable or was evicted. Observe the root again.`);
|
|
8996
10073
|
}
|
|
8997
10074
|
const operation = savedStates.hydrate(stateRecord);
|
|
10075
|
+
operation.owner = runtime?.owner ?? operation.owner ?? COMPUTER_USE_STANDALONE_OWNER;
|
|
10076
|
+
operation.generation = runtime?.generation ?? operation.generation ?? engineGeneration;
|
|
10077
|
+
operation.helperGeneration = runtime?.helperGeneration ?? operation.helperGeneration ?? helperGeneration;
|
|
10078
|
+
operation.requestId = runtime?.requestId;
|
|
10079
|
+
operation.signal = signal ?? runtime?.signal;
|
|
10080
|
+
operation.onDispatch = runtime?.onDispatch;
|
|
10081
|
+
if (requestedStateId && stateRecord) assertStateAccess(requestedStateId, stateRecord, operation);
|
|
8998
10082
|
return await savedStates.operations.run(operation, async () => {
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
10083
|
+
try {
|
|
10084
|
+
const browserOnly = operation.contextId || ["launch_browser", "navigate_browser", "evaluate_browser"].includes(tool) || tool === "find_roots" && params.kind === "browser_page" || tool === "observe_ui" && runtimeState.browserContextByRoot.has(params.root ?? "");
|
|
10085
|
+
await resourceScheduler.read("session-lifecycle", async () => {
|
|
10086
|
+
if (browserOnly || outputRef) {
|
|
10087
|
+
loadComputerUseConfig(ctx.cwd);
|
|
10088
|
+
if (browserOnly && !outputRef && tool !== "find_roots" && !isBrowserUseEnabled()) {
|
|
10089
|
+
throw new Error("Browser use is disabled by computer-use config. Enable browser_use to control browser pages.");
|
|
10090
|
+
}
|
|
10091
|
+
} else await ensureReady3(ctx, signal);
|
|
10092
|
+
}, operation.signal);
|
|
10093
|
+
throwIfAborted3(signal);
|
|
10094
|
+
const result = await run();
|
|
10095
|
+
persistOperation(operation);
|
|
10096
|
+
return result;
|
|
10097
|
+
} finally {
|
|
10098
|
+
for (const artifactId of operation.artifactIds ?? []) engineArtifacts?.unpin(artifactId);
|
|
10099
|
+
}
|
|
9012
10100
|
});
|
|
9013
10101
|
}
|
|
9014
|
-
|
|
9015
|
-
|
|
10102
|
+
var BROKER_PERFORMERS = {
|
|
10103
|
+
find_roots: performListWindows,
|
|
10104
|
+
observe_ui: performObserve,
|
|
10105
|
+
search_ui: performSearchUi,
|
|
10106
|
+
expand_ui: performExpandUi,
|
|
10107
|
+
inspect_ui: performInspectUi,
|
|
10108
|
+
read_text: performReadText,
|
|
10109
|
+
wait_for: performWaitFor,
|
|
10110
|
+
act_ui: performAct,
|
|
10111
|
+
launch_browser: performLaunchBrowser,
|
|
10112
|
+
navigate_browser: performNavigateBrowser,
|
|
10113
|
+
evaluate_browser: performEvaluateBrowser
|
|
10114
|
+
};
|
|
10115
|
+
async function executeBrokerCommand(command, params, runtime) {
|
|
10116
|
+
const perform = BROKER_PERFORMERS[command];
|
|
10117
|
+
if (!perform) throw new Error(`Unsupported broker command '${String(command)}'.`);
|
|
10118
|
+
const ctx = { cwd: runtime.cwd, getMessages: () => [] };
|
|
10119
|
+
return await executeTool(command, ctx, params, runtime.signal, async () => {
|
|
10120
|
+
const owner = operationState().owner ?? COMPUTER_USE_STANDALONE_OWNER;
|
|
9016
10121
|
try {
|
|
9017
|
-
return applyOutputEnvelope(
|
|
10122
|
+
return applyOutputEnvelope(command, await perform(params, runtime.signal), owner);
|
|
9018
10123
|
} catch (error) {
|
|
9019
|
-
throw boundToolError(
|
|
10124
|
+
throw boundToolError(command, error, owner);
|
|
9020
10125
|
}
|
|
9021
|
-
};
|
|
10126
|
+
}, runtime);
|
|
9022
10127
|
}
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9047
|
-
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
|
|
9064
|
-
|
|
9065
|
-
|
|
9066
|
-
|
|
9067
|
-
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
|
|
9073
|
-
|
|
9074
|
-
|
|
9075
|
-
|
|
9076
|
-
|
|
9077
|
-
const
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
if (typeof details.capture.stateId !== "string") continue;
|
|
9081
|
-
const target = {
|
|
9082
|
-
appName: app,
|
|
9083
|
-
bundleId: details.target.bundleId,
|
|
9084
|
-
pid: Math.trunc(details.target.pid),
|
|
9085
|
-
windowTitle: details.target.windowTitle ?? "(untitled)",
|
|
9086
|
-
windowId: Math.trunc(details.target.windowId),
|
|
9087
|
-
windowRef: typeof details.target.windowRef === "string" ? details.target.windowRef : void 0,
|
|
9088
|
-
nativeWindowRef: typeof details.target.nativeWindowRef === "string" ? details.target.nativeWindowRef : void 0
|
|
10128
|
+
|
|
10129
|
+
// vendor/computer-use-axi/src/broker-server.ts
|
|
10130
|
+
function sleep3(ms) {
|
|
10131
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
10132
|
+
}
|
|
10133
|
+
function isAlive(pid) {
|
|
10134
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
10135
|
+
try {
|
|
10136
|
+
process.kill(pid, 0);
|
|
10137
|
+
return true;
|
|
10138
|
+
} catch {
|
|
10139
|
+
return false;
|
|
10140
|
+
}
|
|
10141
|
+
}
|
|
10142
|
+
function acquireBootstrapLock(directory) {
|
|
10143
|
+
ensurePrivateDirectory(directory);
|
|
10144
|
+
const lockPath = path14.join(directory, "broker.lock");
|
|
10145
|
+
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
10146
|
+
try {
|
|
10147
|
+
writeFileSync3(lockPath, String(process.pid), { flag: "wx", mode: 384 });
|
|
10148
|
+
return () => {
|
|
10149
|
+
try {
|
|
10150
|
+
unlinkSync4(lockPath);
|
|
10151
|
+
} catch {
|
|
10152
|
+
}
|
|
10153
|
+
};
|
|
10154
|
+
} catch (error) {
|
|
10155
|
+
if (error.code !== "EEXIST") throw error;
|
|
10156
|
+
let ownerPid = 0;
|
|
10157
|
+
try {
|
|
10158
|
+
ownerPid = Number.parseInt(readFileSync4(lockPath, "utf8").trim(), 10);
|
|
10159
|
+
} catch {
|
|
10160
|
+
}
|
|
10161
|
+
if (Number.isInteger(ownerPid) && ownerPid > 0 && isAlive(ownerPid)) {
|
|
10162
|
+
const until = Date.now() + 100;
|
|
10163
|
+
while (Date.now() < until) {
|
|
10164
|
+
}
|
|
10165
|
+
continue;
|
|
10166
|
+
}
|
|
10167
|
+
try {
|
|
10168
|
+
unlinkSync4(lockPath);
|
|
10169
|
+
} catch {
|
|
10170
|
+
}
|
|
10171
|
+
}
|
|
10172
|
+
}
|
|
10173
|
+
throw new Error(`Could not acquire the computer-use broker bootstrap lock at ${lockPath}.`);
|
|
10174
|
+
}
|
|
10175
|
+
async function listen(server, endpoint) {
|
|
10176
|
+
if (endpoint.kind === "unix-socket") ensurePrivateDirectory(endpoint.directory);
|
|
10177
|
+
await new Promise((resolve2, reject) => {
|
|
10178
|
+
const onError = (error) => {
|
|
10179
|
+
server.off("listening", onListening);
|
|
10180
|
+
reject(error);
|
|
10181
|
+
};
|
|
10182
|
+
const onListening = () => {
|
|
10183
|
+
server.off("error", onError);
|
|
10184
|
+
resolve2();
|
|
9089
10185
|
};
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
10186
|
+
server.once("error", onError);
|
|
10187
|
+
server.once("listening", onListening);
|
|
10188
|
+
server.listen(endpoint.path);
|
|
10189
|
+
});
|
|
10190
|
+
if (endpoint.kind === "unix-socket") chmodSync2(endpoint.path, 384);
|
|
10191
|
+
}
|
|
10192
|
+
async function probeBroker(endpoint, timeoutMs = 800) {
|
|
10193
|
+
return await new Promise((resolve2) => {
|
|
10194
|
+
const socket = net3.createConnection(endpoint.path);
|
|
10195
|
+
const decoder = new FrameDecoder();
|
|
10196
|
+
let settled = false;
|
|
10197
|
+
const finish = (result) => {
|
|
10198
|
+
if (settled) return;
|
|
10199
|
+
settled = true;
|
|
10200
|
+
socket.destroy();
|
|
10201
|
+
resolve2(result);
|
|
10202
|
+
};
|
|
10203
|
+
socket.setTimeout(timeoutMs, () => finish({ error: "timeout" }));
|
|
10204
|
+
socket.on("connect", () => {
|
|
10205
|
+
socket.write(encodeFrame({ type: "hello", protocolVersion: BROKER_PROTOCOL_VERSION, clientVersion: "probe" }));
|
|
10206
|
+
});
|
|
10207
|
+
socket.on("data", (chunk) => {
|
|
10208
|
+
try {
|
|
10209
|
+
const frames = decoder.push(chunk);
|
|
10210
|
+
for (const frame of frames) {
|
|
10211
|
+
if (frame && typeof frame === "object" && frame.type === "hello") finish({ live: frame });
|
|
10212
|
+
}
|
|
10213
|
+
} catch (error) {
|
|
10214
|
+
finish({ error: error instanceof Error ? error.message : String(error) });
|
|
10215
|
+
}
|
|
10216
|
+
});
|
|
10217
|
+
socket.on("error", (error) => {
|
|
10218
|
+
if (error.code === "ECONNREFUSED" || error.code === "ENOENT") finish({ dead: true });
|
|
10219
|
+
else finish({ error: error.message });
|
|
10220
|
+
});
|
|
10221
|
+
socket.on("close", () => finish({ error: "closed" }));
|
|
10222
|
+
});
|
|
10223
|
+
}
|
|
10224
|
+
function canProveStaleEndpoint(endpoint) {
|
|
10225
|
+
if (endpoint.kind !== "unix-socket") return false;
|
|
10226
|
+
try {
|
|
10227
|
+
const stats = lstatSync4(endpoint.path);
|
|
10228
|
+
if (!stats.isSocket()) return false;
|
|
10229
|
+
return typeof process.getuid === "function" ? stats.uid === process.getuid() : true;
|
|
10230
|
+
} catch {
|
|
10231
|
+
return false;
|
|
10232
|
+
}
|
|
10233
|
+
}
|
|
10234
|
+
function removeStaleSocket(endpoint) {
|
|
10235
|
+
if (!canProveStaleEndpoint(endpoint)) throw new Error(`Refusing to remove an endpoint that cannot be proven stale: ${endpoint.path}`);
|
|
10236
|
+
unlinkSync4(endpoint.path);
|
|
10237
|
+
}
|
|
10238
|
+
function collectStateIds(value, into) {
|
|
10239
|
+
if (!value || typeof value !== "object") return;
|
|
10240
|
+
const record = value;
|
|
10241
|
+
const details = record.details;
|
|
10242
|
+
const candidate = details?.stateId ?? details?.capture?.stateId;
|
|
10243
|
+
if (typeof candidate === "string" && /^[A-Za-z0-9-]{1,128}$/.test(candidate)) into.add(candidate);
|
|
10244
|
+
}
|
|
10245
|
+
async function startBroker(options = {}) {
|
|
10246
|
+
const endpoint = options.endpoint ?? resolveBrokerEndpoint();
|
|
10247
|
+
const idleExitMs = options.idleExitMs ?? BROKER_IDLE_EXIT_MS;
|
|
10248
|
+
const generation = randomUUID8();
|
|
10249
|
+
const helperGeneration2 = randomUUID8();
|
|
10250
|
+
let liveHelperGeneration = helperGeneration2;
|
|
10251
|
+
const startedAt = Date.now();
|
|
10252
|
+
const legacyCache = cleanupLegacyBrokerCache();
|
|
10253
|
+
const prior = loadBrokerManifest();
|
|
10254
|
+
const artifacts = new ArtifactStore({
|
|
10255
|
+
root: artifactsRoot(),
|
|
10256
|
+
generation,
|
|
10257
|
+
onEvict: (stateId2) => {
|
|
10258
|
+
artifacts.dropForState(stateId2);
|
|
10259
|
+
expireStateForArtifactEviction(stateId2);
|
|
10260
|
+
}
|
|
10261
|
+
});
|
|
10262
|
+
try {
|
|
10263
|
+
artifacts.cleanupOtherGenerations(generation);
|
|
10264
|
+
} catch {
|
|
10265
|
+
}
|
|
10266
|
+
configureEngineRuntime({
|
|
10267
|
+
generation,
|
|
10268
|
+
helperGeneration: helperGeneration2,
|
|
10269
|
+
artifacts,
|
|
10270
|
+
expiredStateIds: prior?.stateIds ?? [],
|
|
10271
|
+
previousGeneration: prior?.generation
|
|
10272
|
+
});
|
|
10273
|
+
saveBrokerManifest({ generation, helperGeneration: helperGeneration2, stateIds: [], updatedAt: Date.now() });
|
|
10274
|
+
const entries = /* @__PURE__ */ new Map();
|
|
10275
|
+
const owners = /* @__PURE__ */ new Set();
|
|
10276
|
+
const knownStateIds = /* @__PURE__ */ new Set();
|
|
10277
|
+
let active = 0;
|
|
10278
|
+
let queued = 0;
|
|
10279
|
+
let lastActivity = Date.now();
|
|
10280
|
+
let drainRequested = false;
|
|
10281
|
+
let stopping = false;
|
|
10282
|
+
let idleTimer;
|
|
10283
|
+
let statusHandle;
|
|
10284
|
+
const persistManifest = () => {
|
|
10285
|
+
try {
|
|
10286
|
+
saveBrokerManifest({ generation, helperGeneration: currentHelperGenerationSafe(), stateIds: [...knownStateIds], updatedAt: Date.now() });
|
|
10287
|
+
} catch {
|
|
10288
|
+
}
|
|
10289
|
+
};
|
|
10290
|
+
const currentHelperGenerationSafe = () => liveHelperGeneration;
|
|
10291
|
+
const send = (socket, frame) => {
|
|
10292
|
+
if (!socket || socket.destroyed) return;
|
|
10293
|
+
try {
|
|
10294
|
+
socket.write(encodeFrame(frame, BROKER_MAX_RESPONSE_BYTES));
|
|
10295
|
+
} catch (error) {
|
|
10296
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10297
|
+
try {
|
|
10298
|
+
socket.write(encodeFrame({ type: "response", requestId: "broker", ok: false, state: "error", generation, helperGeneration: currentHelperGenerationSafe(), error: { code: "frame_too_large", message } }));
|
|
10299
|
+
} catch {
|
|
10300
|
+
}
|
|
10301
|
+
}
|
|
10302
|
+
};
|
|
10303
|
+
const respond = (entry, state, result, error) => {
|
|
10304
|
+
send(entry.socket, {
|
|
10305
|
+
type: "response",
|
|
10306
|
+
requestId: entry.requestId,
|
|
10307
|
+
ok: state === "done" || state === "in-flight",
|
|
10308
|
+
state,
|
|
10309
|
+
generation,
|
|
10310
|
+
helperGeneration: currentHelperGenerationSafe(),
|
|
10311
|
+
...result === void 0 ? {} : { result },
|
|
10312
|
+
...error ? { error } : {}
|
|
10313
|
+
});
|
|
10314
|
+
};
|
|
10315
|
+
const waiters = [];
|
|
10316
|
+
const acquireSlot = (entry) => {
|
|
10317
|
+
if (entry.controller.signal.aborted) return Promise.resolve(false);
|
|
10318
|
+
if (active < BROKER_MAX_CONCURRENT_REQUESTS) {
|
|
10319
|
+
active += 1;
|
|
10320
|
+
return Promise.resolve(true);
|
|
10321
|
+
}
|
|
10322
|
+
if (waiters.length >= BROKER_MAX_QUEUED_REQUESTS) return Promise.resolve(false);
|
|
10323
|
+
return new Promise((resolve2) => {
|
|
10324
|
+
const waiter = {
|
|
10325
|
+
start: () => {
|
|
10326
|
+
entry.controller.signal.removeEventListener("abort", waiter.cancel);
|
|
10327
|
+
queued = Math.max(0, queued - 1);
|
|
10328
|
+
resolve2(true);
|
|
10329
|
+
},
|
|
10330
|
+
cancel: () => {
|
|
10331
|
+
const index = waiters.indexOf(waiter);
|
|
10332
|
+
if (index >= 0) waiters.splice(index, 1);
|
|
10333
|
+
queued = Math.max(0, queued - 1);
|
|
10334
|
+
resolve2(false);
|
|
10335
|
+
}
|
|
10336
|
+
};
|
|
10337
|
+
queued += 1;
|
|
10338
|
+
entry.controller.signal.addEventListener("abort", waiter.cancel, { once: true });
|
|
10339
|
+
waiters.push(waiter);
|
|
10340
|
+
});
|
|
10341
|
+
};
|
|
10342
|
+
const releaseSlot = () => {
|
|
10343
|
+
active = Math.max(0, active - 1);
|
|
10344
|
+
const next = waiters.shift();
|
|
10345
|
+
if (next) {
|
|
10346
|
+
active += 1;
|
|
10347
|
+
next.start();
|
|
10348
|
+
}
|
|
10349
|
+
};
|
|
10350
|
+
const finish = (entry, state, result, error) => {
|
|
10351
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
10352
|
+
entry.timer = void 0;
|
|
10353
|
+
if (entry.finished) {
|
|
10354
|
+
if (state === "done") {
|
|
10355
|
+
entry.state = "done";
|
|
10356
|
+
entry.result = result;
|
|
10357
|
+
entry.error = void 0;
|
|
10358
|
+
if (result !== void 0) collectStateIds(result, knownStateIds);
|
|
10359
|
+
if (knownStateIds.size > 0) persistManifest();
|
|
10360
|
+
}
|
|
10361
|
+
if (entry.slotHeld) {
|
|
10362
|
+
entry.slotHeld = false;
|
|
10363
|
+
releaseSlot();
|
|
10364
|
+
}
|
|
10365
|
+
return;
|
|
10366
|
+
}
|
|
10367
|
+
entry.finished = true;
|
|
10368
|
+
entry.state = state;
|
|
10369
|
+
entry.result = result;
|
|
10370
|
+
entry.error = error;
|
|
10371
|
+
if (result !== void 0) collectStateIds(result, knownStateIds);
|
|
10372
|
+
if (entry.slotHeld) {
|
|
10373
|
+
entry.slotHeld = false;
|
|
10374
|
+
releaseSlot();
|
|
10375
|
+
}
|
|
10376
|
+
respond(entry, state === "unknown" ? "outcome-unknown" : state === "error" ? "error" : "done", result, error);
|
|
10377
|
+
if (knownStateIds.size > 0) persistManifest();
|
|
10378
|
+
};
|
|
10379
|
+
const runEntry = async (entry, request) => {
|
|
10380
|
+
entry.state = "running";
|
|
10381
|
+
entry.dispatched = request.command === "launch_browser";
|
|
10382
|
+
lastActivity = Date.now();
|
|
10383
|
+
try {
|
|
10384
|
+
const result = await executeBrokerCommand(request.command, request.params, {
|
|
10385
|
+
owner: request.ownerId,
|
|
10386
|
+
generation,
|
|
10387
|
+
helperGeneration: currentHelperGenerationSafe(),
|
|
10388
|
+
requestId: request.requestId,
|
|
10389
|
+
cwd: request.cwd,
|
|
10390
|
+
signal: entry.controller.signal,
|
|
10391
|
+
onDispatch: () => {
|
|
10392
|
+
entry.dispatched = true;
|
|
10393
|
+
}
|
|
10394
|
+
});
|
|
10395
|
+
finish(entry, "done", result);
|
|
10396
|
+
} catch (error) {
|
|
10397
|
+
const currentState = entry.state;
|
|
10398
|
+
if (currentState === "unknown" || currentState === "error") return;
|
|
10399
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10400
|
+
finish(entry, "error", void 0, { code: "engine_error", message, dispatched: entry.dispatched });
|
|
10401
|
+
}
|
|
10402
|
+
};
|
|
10403
|
+
const handleEngineRequest = async (request, socket) => {
|
|
10404
|
+
if (stopping || drainRequested) {
|
|
10405
|
+
send(socket, { type: "response", requestId: request.requestId, ok: false, state: "error", generation, helperGeneration: currentHelperGenerationSafe(), error: { code: "broker_draining", message: "The broker is shutting down and draining in-flight work; this request was not accepted. Retry after it exits.", dispatched: false } });
|
|
10406
|
+
return;
|
|
10407
|
+
}
|
|
10408
|
+
const key = request.requestId;
|
|
10409
|
+
const fingerprint = requestFingerprint(request.command, request.params, request.cwd);
|
|
10410
|
+
const existing = entries.get(key);
|
|
10411
|
+
if (existing) {
|
|
10412
|
+
if (existing.ownerId !== request.ownerId || existing.command !== request.command || existing.fingerprint !== fingerprint) {
|
|
10413
|
+
send(socket, { type: "response", requestId: request.requestId, ok: false, state: "error", generation, helperGeneration: currentHelperGenerationSafe(), error: { code: "request_id_conflict", message: "This requestId was already used with a different owner, command, params, or cwd. Use a fresh requestId." } });
|
|
10414
|
+
return;
|
|
10415
|
+
}
|
|
10416
|
+
if (existing.state === "done") respond({ ...existing, socket }, "done", existing.result);
|
|
10417
|
+
else if (existing.state === "running" || existing.state === "queued") respond({ ...existing, socket }, "in-flight");
|
|
10418
|
+
else if (existing.state === "unknown") respond({ ...existing, socket }, "outcome-unknown", void 0, existing.error);
|
|
10419
|
+
else respond({ ...existing, socket }, "error", void 0, existing.error);
|
|
10420
|
+
return;
|
|
10421
|
+
}
|
|
10422
|
+
if (request.generation && request.generation !== generation) {
|
|
10423
|
+
send(socket, { type: "response", requestId: request.requestId, ok: false, state: "error", generation, helperGeneration: currentHelperGenerationSafe(), error: { code: "generation_mismatch", message: `This state belongs to broker generation ${request.generation}; the live broker is ${generation}. Observe again.` } });
|
|
10424
|
+
return;
|
|
10425
|
+
}
|
|
10426
|
+
if (entries.size >= 256) {
|
|
10427
|
+
for (const [id, candidate] of entries) {
|
|
10428
|
+
if (candidate.state === "done" || candidate.state === "error" || candidate.state === "unknown") {
|
|
10429
|
+
entries.delete(id);
|
|
10430
|
+
break;
|
|
10431
|
+
}
|
|
10432
|
+
}
|
|
10433
|
+
}
|
|
10434
|
+
const entry = {
|
|
10435
|
+
requestId: request.requestId,
|
|
10436
|
+
ownerId: request.ownerId,
|
|
10437
|
+
command: request.command,
|
|
10438
|
+
state: "queued",
|
|
10439
|
+
dispatched: false,
|
|
10440
|
+
finished: false,
|
|
10441
|
+
slotHeld: false,
|
|
10442
|
+
controller: new AbortController(),
|
|
10443
|
+
fingerprint,
|
|
10444
|
+
cwd: request.cwd,
|
|
10445
|
+
socket,
|
|
10446
|
+
startedAt: Date.now()
|
|
9098
10447
|
};
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
lookId: details.outline.lookId,
|
|
9113
|
-
capturedAt: details.capture.timestamp / 1e3,
|
|
9114
|
-
window: {
|
|
9115
|
-
windowId: Math.trunc(details.target.windowId),
|
|
9116
|
-
framePoints: { x: 0, y: 0, w: details.capture.width, h: details.capture.height },
|
|
9117
|
-
scaleFactor: details.capture.scaleFactor,
|
|
9118
|
-
isModal: false,
|
|
9119
|
-
role: "",
|
|
9120
|
-
subrole: ""
|
|
9121
|
-
},
|
|
9122
|
-
image: { jpegBase64: "", width: details.capture.width, height: details.capture.height },
|
|
9123
|
-
timings: {}
|
|
9124
|
-
},
|
|
9125
|
-
note: details.note
|
|
10448
|
+
entries.set(key, entry);
|
|
10449
|
+
owners.add(request.ownerId);
|
|
10450
|
+
entry.timer = setTimeout(() => {
|
|
10451
|
+
if (entry.state === "queued") {
|
|
10452
|
+
entry.controller.abort();
|
|
10453
|
+
finish(entry, "error", void 0, { code: "cancelled", message: "Request was cancelled while queued; it was not dispatched.", dispatched: false });
|
|
10454
|
+
return;
|
|
10455
|
+
}
|
|
10456
|
+
if (entry.state === "running") {
|
|
10457
|
+
if (!entry.dispatched) {
|
|
10458
|
+
entry.controller.abort();
|
|
10459
|
+
finish(entry, "error", void 0, { code: "cancelled", message: "Request was cancelled while waiting for the resource lane; it was not dispatched.", dispatched: false });
|
|
10460
|
+
return;
|
|
9126
10461
|
}
|
|
10462
|
+
finish(entry, "unknown", void 0, { code: "outcome_unknown", message: "The broker did not observe completion within the request budget. The native action may have been dispatched; do not replay it. Observe the resource again to establish a successor state.", dispatched: true });
|
|
10463
|
+
}
|
|
10464
|
+
}, request.budgetMs);
|
|
10465
|
+
const acquired = await acquireSlot(entry);
|
|
10466
|
+
if (!acquired) {
|
|
10467
|
+
if (entry.state === "queued") {
|
|
10468
|
+
finish(entry, "error", void 0, { code: "broker_busy", message: "The broker queue is full; no action was dispatched. Retry later." });
|
|
10469
|
+
}
|
|
10470
|
+
return;
|
|
10471
|
+
}
|
|
10472
|
+
if (entry.controller.signal.aborted) {
|
|
10473
|
+
finish(entry, "error", void 0, { code: "cancelled", message: "Request was cancelled before dispatch.", dispatched: false });
|
|
10474
|
+
return;
|
|
10475
|
+
}
|
|
10476
|
+
entry.slotHeld = true;
|
|
10477
|
+
await runEntry(entry, request);
|
|
10478
|
+
};
|
|
10479
|
+
const handleControl = async (request, socket) => {
|
|
10480
|
+
if ((stopping || drainRequested) && request.command !== "stop") {
|
|
10481
|
+
send(socket, { type: "response", requestId: request.requestId, ok: false, state: "error", generation, helperGeneration: currentHelperGenerationSafe(), error: { code: "broker_draining", message: "The broker is shutting down and draining in-flight work; control requests other than 'stop' are unavailable. Retry after it exits." } });
|
|
10482
|
+
return;
|
|
10483
|
+
}
|
|
10484
|
+
if (request.command === "status") {
|
|
10485
|
+
send(socket, { type: "response", requestId: request.requestId, ok: true, state: "done", generation, helperGeneration: currentHelperGenerationSafe(), result: statusHandle?.status() });
|
|
10486
|
+
return;
|
|
10487
|
+
}
|
|
10488
|
+
if (request.command === "debug_restart_helper") {
|
|
10489
|
+
if (!options.testCommands) {
|
|
10490
|
+
send(socket, { type: "response", requestId: request.requestId, ok: false, state: "error", generation, helperGeneration: currentHelperGenerationSafe(), error: { code: "unknown_command", message: "Test commands are disabled." } });
|
|
10491
|
+
return;
|
|
10492
|
+
}
|
|
10493
|
+
const helper = bumpHelperGeneration();
|
|
10494
|
+
liveHelperGeneration = helper;
|
|
10495
|
+
expireStatesAfterHelperRestart();
|
|
10496
|
+
const fake = options.restartTestHelper?.();
|
|
10497
|
+
send(socket, { type: "response", requestId: request.requestId, ok: true, state: "done", generation, helperGeneration: helper, result: { helperGeneration: helper, fakeHelperGeneration: fake } });
|
|
10498
|
+
return;
|
|
10499
|
+
}
|
|
10500
|
+
drainRequested = true;
|
|
10501
|
+
lastActivity = Date.now();
|
|
10502
|
+
send(socket, { type: "response", requestId: request.requestId, ok: true, state: "done", generation, helperGeneration: currentHelperGenerationSafe(), result: { stopping: true } });
|
|
10503
|
+
setTimeout(() => {
|
|
10504
|
+
void shutdown("stop");
|
|
10505
|
+
}, 10);
|
|
10506
|
+
};
|
|
10507
|
+
const connections = /* @__PURE__ */ new Set();
|
|
10508
|
+
const server = net3.createServer((socket) => {
|
|
10509
|
+
lastActivity = Date.now();
|
|
10510
|
+
connections.add(socket);
|
|
10511
|
+
const decoder = new FrameDecoder();
|
|
10512
|
+
let helloOk = false;
|
|
10513
|
+
let helloRejected = false;
|
|
10514
|
+
const drainingConnection = stopping || drainRequested;
|
|
10515
|
+
if (drainingConnection) {
|
|
10516
|
+
send(socket, {
|
|
10517
|
+
type: "hello",
|
|
10518
|
+
ok: false,
|
|
10519
|
+
error: { code: "broker_draining", message: "The broker is shutting down and draining in-flight native work. No new request was accepted; retry after it exits." },
|
|
10520
|
+
protocolVersion: BROKER_PROTOCOL_VERSION,
|
|
10521
|
+
build: BROKER_BUILD,
|
|
10522
|
+
generation,
|
|
10523
|
+
pid: process.pid,
|
|
10524
|
+
startedAt
|
|
9127
10525
|
});
|
|
9128
|
-
restoredResources.add(resourceKey);
|
|
9129
10526
|
}
|
|
10527
|
+
socket.on("error", () => {
|
|
10528
|
+
});
|
|
10529
|
+
socket.on("data", (chunk) => {
|
|
10530
|
+
let frames;
|
|
10531
|
+
try {
|
|
10532
|
+
frames = decoder.push(chunk);
|
|
10533
|
+
} catch (error) {
|
|
10534
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10535
|
+
send(socket, { type: "hello", ok: false, error: { code: "invalid_frame", message } });
|
|
10536
|
+
socket.destroy();
|
|
10537
|
+
return;
|
|
10538
|
+
}
|
|
10539
|
+
if (drainingConnection) {
|
|
10540
|
+
for (const frame of frames) {
|
|
10541
|
+
let request;
|
|
10542
|
+
try {
|
|
10543
|
+
request = validateRequest(frame);
|
|
10544
|
+
} catch {
|
|
10545
|
+
continue;
|
|
10546
|
+
}
|
|
10547
|
+
if (request.command === "stop") {
|
|
10548
|
+
void handleControl(request, socket);
|
|
10549
|
+
continue;
|
|
10550
|
+
}
|
|
10551
|
+
send(socket, { type: "response", requestId: request.requestId, ok: false, state: "error", generation, helperGeneration: currentHelperGenerationSafe(), error: { code: "broker_draining", message: "The broker is shutting down and draining in-flight native work; this request was not accepted. Retry after it exits.", dispatched: false } });
|
|
10552
|
+
}
|
|
10553
|
+
return;
|
|
10554
|
+
}
|
|
10555
|
+
for (const frame of frames) {
|
|
10556
|
+
if (!helloOk) {
|
|
10557
|
+
if (helloRejected) {
|
|
10558
|
+
try {
|
|
10559
|
+
const request2 = validateRequest(frame);
|
|
10560
|
+
if (request2.command === "stop") {
|
|
10561
|
+
void handleControl(request2, socket);
|
|
10562
|
+
continue;
|
|
10563
|
+
}
|
|
10564
|
+
} catch {
|
|
10565
|
+
}
|
|
10566
|
+
send(socket, {
|
|
10567
|
+
type: "hello",
|
|
10568
|
+
ok: false,
|
|
10569
|
+
error: { code: "protocol_mismatch", message: "Only 'stop' is accepted before a compatible handshake." },
|
|
10570
|
+
protocolVersion: BROKER_PROTOCOL_VERSION,
|
|
10571
|
+
build: BROKER_BUILD,
|
|
10572
|
+
generation,
|
|
10573
|
+
pid: process.pid,
|
|
10574
|
+
startedAt
|
|
10575
|
+
});
|
|
10576
|
+
continue;
|
|
10577
|
+
}
|
|
10578
|
+
try {
|
|
10579
|
+
validateHello(frame);
|
|
10580
|
+
helloOk = true;
|
|
10581
|
+
send(socket, {
|
|
10582
|
+
type: "hello",
|
|
10583
|
+
ok: true,
|
|
10584
|
+
protocolVersion: BROKER_PROTOCOL_VERSION,
|
|
10585
|
+
build: BROKER_BUILD,
|
|
10586
|
+
generation,
|
|
10587
|
+
helperGeneration: currentHelperGenerationSafe(),
|
|
10588
|
+
pid: process.pid,
|
|
10589
|
+
startedAt,
|
|
10590
|
+
platform: process.platform,
|
|
10591
|
+
transport: endpoint.kind
|
|
10592
|
+
});
|
|
10593
|
+
} catch (error) {
|
|
10594
|
+
helloRejected = true;
|
|
10595
|
+
send(socket, {
|
|
10596
|
+
type: "hello",
|
|
10597
|
+
ok: false,
|
|
10598
|
+
error: { code: error.code ?? "protocol_mismatch", message: error instanceof Error ? error.message : String(error) },
|
|
10599
|
+
protocolVersion: BROKER_PROTOCOL_VERSION,
|
|
10600
|
+
build: BROKER_BUILD,
|
|
10601
|
+
generation,
|
|
10602
|
+
pid: process.pid,
|
|
10603
|
+
startedAt
|
|
10604
|
+
});
|
|
10605
|
+
}
|
|
10606
|
+
continue;
|
|
10607
|
+
}
|
|
10608
|
+
let request;
|
|
10609
|
+
try {
|
|
10610
|
+
request = validateRequest(frame);
|
|
10611
|
+
} catch (error) {
|
|
10612
|
+
send(socket, {
|
|
10613
|
+
type: "response",
|
|
10614
|
+
requestId: typeof frame?.requestId === "string" ? frame.requestId : "invalid",
|
|
10615
|
+
ok: false,
|
|
10616
|
+
state: "error",
|
|
10617
|
+
generation,
|
|
10618
|
+
helperGeneration: currentHelperGenerationSafe(),
|
|
10619
|
+
error: { code: error.code ?? "invalid_request", message: error instanceof Error ? error.message : String(error) }
|
|
10620
|
+
});
|
|
10621
|
+
continue;
|
|
10622
|
+
}
|
|
10623
|
+
void (request.command === "status" || request.command === "stop" || request.command === "debug_restart_helper" ? handleControl(request, socket) : handleEngineRequest(request, socket));
|
|
10624
|
+
}
|
|
10625
|
+
});
|
|
10626
|
+
socket.on("close", () => {
|
|
10627
|
+
connections.delete(socket);
|
|
10628
|
+
for (const entry of entries.values()) {
|
|
10629
|
+
if (entry.socket !== socket) continue;
|
|
10630
|
+
entry.socket = void 0;
|
|
10631
|
+
if (entry.state === "queued") {
|
|
10632
|
+
entry.controller.abort();
|
|
10633
|
+
}
|
|
10634
|
+
}
|
|
10635
|
+
});
|
|
10636
|
+
});
|
|
10637
|
+
const bootstrapLock = endpoint.kind === "unix-socket" ? acquireBootstrapLock(endpoint.directory) : () => {
|
|
10638
|
+
};
|
|
10639
|
+
try {
|
|
10640
|
+
let drainWaitUntil = 0;
|
|
10641
|
+
const waitForDrain = async () => {
|
|
10642
|
+
if (drainWaitUntil === 0) drainWaitUntil = Date.now() + BROKER_DRAIN_MAX_MS + 1e4;
|
|
10643
|
+
if (Date.now() >= drainWaitUntil) return false;
|
|
10644
|
+
await sleep3(100);
|
|
10645
|
+
return true;
|
|
10646
|
+
};
|
|
10647
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
10648
|
+
try {
|
|
10649
|
+
await listen(server, endpoint);
|
|
10650
|
+
break;
|
|
10651
|
+
} catch (error) {
|
|
10652
|
+
if (error.code !== "EADDRINUSE") throw error;
|
|
10653
|
+
const probe = await probeBroker(endpoint);
|
|
10654
|
+
if (probe.live) {
|
|
10655
|
+
const reply = probe.live;
|
|
10656
|
+
if (reply.ok === false && reply.error.code === "broker_draining") {
|
|
10657
|
+
if (await waitForDrain()) continue;
|
|
10658
|
+
throw new Error(`The computer-use broker at ${endpoint.path} is still draining in-flight work. Refusing to start a second execution authority; retry shortly.`);
|
|
10659
|
+
}
|
|
10660
|
+
const incompatible = reply.ok === false || reply.build?.identity !== BROKER_BUILD.identity || reply.protocolVersion !== BROKER_PROTOCOL_VERSION;
|
|
10661
|
+
if (incompatible) {
|
|
10662
|
+
const detail = reply.ok === false ? reply.error.message : `build ${reply.build?.identity ?? "unknown"}`;
|
|
10663
|
+
throw new Error(`A different computer-use broker is already listening at ${endpoint.path} (${detail}). Run 'computer-use-axi stop' to restart it safely; refusing to start a second execution authority.`);
|
|
10664
|
+
}
|
|
10665
|
+
throw Object.assign(new Error(`Computer-use broker already running (pid ${reply.pid}, generation ${reply.generation}) at ${endpoint.path}.`), { code: "EXISTING_BROKER", generation: reply.generation });
|
|
10666
|
+
}
|
|
10667
|
+
if (probe.dead && canProveStaleEndpoint(endpoint)) {
|
|
10668
|
+
const marker = loadDrainMarker();
|
|
10669
|
+
if (marker && isAlive(marker.pid) && Date.now() < marker.deadlineAt + 1e4) {
|
|
10670
|
+
if (await waitForDrain()) continue;
|
|
10671
|
+
}
|
|
10672
|
+
removeStaleSocket(endpoint);
|
|
10673
|
+
continue;
|
|
10674
|
+
}
|
|
10675
|
+
if (attempt >= 4) throw error;
|
|
10676
|
+
throw new Error(`Computer-use endpoint ${endpoint.path} is in use and its owner cannot be proven stale (${probe.error ?? "no response"}). Refusing to steal it.`);
|
|
10677
|
+
}
|
|
10678
|
+
}
|
|
10679
|
+
} finally {
|
|
10680
|
+
bootstrapLock();
|
|
9130
10681
|
}
|
|
10682
|
+
const shutdown = async (reason) => {
|
|
10683
|
+
if (stopping) return;
|
|
10684
|
+
stopping = true;
|
|
10685
|
+
drainRequested = true;
|
|
10686
|
+
if (idleTimer) clearInterval(idleTimer);
|
|
10687
|
+
try {
|
|
10688
|
+
saveDrainMarker({ pid: process.pid, startedAt: Date.now(), deadlineAt: Date.now() + BROKER_DRAIN_MAX_MS });
|
|
10689
|
+
} catch {
|
|
10690
|
+
}
|
|
10691
|
+
for (const entry of entries.values()) {
|
|
10692
|
+
if (entry.state === "queued") {
|
|
10693
|
+
entry.controller.abort();
|
|
10694
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
10695
|
+
entry.socket = void 0;
|
|
10696
|
+
}
|
|
10697
|
+
}
|
|
10698
|
+
const deadline = Date.now() + BROKER_DRAIN_MAX_MS;
|
|
10699
|
+
while ([...entries.values()].some((entry) => entry.state === "running" || entry.state === "unknown") && Date.now() < deadline) {
|
|
10700
|
+
await sleep3(50);
|
|
10701
|
+
}
|
|
10702
|
+
persistManifest();
|
|
10703
|
+
try {
|
|
10704
|
+
await Promise.race([shutdownComputerUseSession(), sleep3(1e4)]);
|
|
10705
|
+
} catch {
|
|
10706
|
+
}
|
|
10707
|
+
artifacts.close();
|
|
10708
|
+
try {
|
|
10709
|
+
artifacts.cleanupGeneration(generation);
|
|
10710
|
+
} catch {
|
|
10711
|
+
}
|
|
10712
|
+
await new Promise((resolve2) => {
|
|
10713
|
+
let settled = false;
|
|
10714
|
+
const done = () => {
|
|
10715
|
+
if (!settled) {
|
|
10716
|
+
settled = true;
|
|
10717
|
+
resolve2();
|
|
10718
|
+
}
|
|
10719
|
+
};
|
|
10720
|
+
server.close(done);
|
|
10721
|
+
const timer = setTimeout(() => {
|
|
10722
|
+
for (const socket of connections) socket.destroy();
|
|
10723
|
+
done();
|
|
10724
|
+
}, 1e3);
|
|
10725
|
+
timer.unref?.();
|
|
10726
|
+
});
|
|
10727
|
+
try {
|
|
10728
|
+
clearDrainMarker();
|
|
10729
|
+
} catch {
|
|
10730
|
+
}
|
|
10731
|
+
if (endpoint.kind === "unix-socket") {
|
|
10732
|
+
try {
|
|
10733
|
+
const stats = lstatSync4(endpoint.path);
|
|
10734
|
+
if (stats.isSocket()) unlinkSync4(endpoint.path);
|
|
10735
|
+
} catch {
|
|
10736
|
+
}
|
|
10737
|
+
}
|
|
10738
|
+
options.onShutdown?.();
|
|
10739
|
+
void reason;
|
|
10740
|
+
};
|
|
10741
|
+
statusHandle = {
|
|
10742
|
+
generation,
|
|
10743
|
+
helperGeneration: helperGeneration2,
|
|
10744
|
+
endpoint,
|
|
10745
|
+
startedAt,
|
|
10746
|
+
status() {
|
|
10747
|
+
const uncertain = [...entries.values()].filter((entry) => entry.state === "unknown").length;
|
|
10748
|
+
return {
|
|
10749
|
+
pid: process.pid,
|
|
10750
|
+
generation,
|
|
10751
|
+
helperGeneration: currentHelperGenerationSafe(),
|
|
10752
|
+
build: BROKER_BUILD,
|
|
10753
|
+
protocolVersion: BROKER_PROTOCOL_VERSION,
|
|
10754
|
+
startedAt,
|
|
10755
|
+
uptimeMs: Date.now() - startedAt,
|
|
10756
|
+
endpoint: { kind: endpoint.kind, path: endpoint.path },
|
|
10757
|
+
transportAcl: endpoint.kind === "unix-socket" ? "unix socket 0600 inside a 0700 directory" : "windows named pipe with the Node process-default DACL (not explicitly SID-scoped; unverified on this host)",
|
|
10758
|
+
active,
|
|
10759
|
+
queued,
|
|
10760
|
+
uncertain,
|
|
10761
|
+
requests: entries.size,
|
|
10762
|
+
ownerCount: owners.size,
|
|
10763
|
+
ownerFingerprints: [...owners].map((owner) => createHash3("sha256").update(owner).digest("hex").slice(0, 12)),
|
|
10764
|
+
artifacts: artifacts.stats(),
|
|
10765
|
+
legacyCache,
|
|
10766
|
+
idleExitMs
|
|
10767
|
+
};
|
|
10768
|
+
},
|
|
10769
|
+
async close() {
|
|
10770
|
+
await shutdown("close");
|
|
10771
|
+
}
|
|
10772
|
+
};
|
|
10773
|
+
idleTimer = setInterval(() => {
|
|
10774
|
+
if (stopping) return;
|
|
10775
|
+
const uncertain = [...entries.values()].some((entry) => entry.state === "running" || entry.state === "unknown" || entry.state === "queued");
|
|
10776
|
+
if (uncertain) return;
|
|
10777
|
+
if (Date.now() - lastActivity < idleExitMs) return;
|
|
10778
|
+
void shutdown("idle");
|
|
10779
|
+
}, Math.min(6e4, Math.max(1e3, Math.floor(idleExitMs / 4))));
|
|
10780
|
+
idleTimer.unref?.();
|
|
10781
|
+
return statusHandle;
|
|
9131
10782
|
}
|
|
9132
10783
|
|
|
9133
|
-
//
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
Type.Object({ action: Type.Literal("setText"), ref: Type.String({ description: "Editable outline ref" }), text: Type.String() }),
|
|
9144
|
-
Type.Object({ action: Type.Literal("typeText"), ref: Type.Optional(Type.String({ description: "Omit after a click to type into the focus established by that click" })), text: Type.String() }),
|
|
9145
|
-
Type.Object({ action: Type.Literal("keypress"), ref: Type.Optional(Type.String({ description: "Omit to send keys to the focused control" })), keys: Type.Array(Type.String(), { minItems: 1 }) }),
|
|
9146
|
-
Type.Object({ action: Type.Literal("scroll"), ref: Type.Optional(Type.String()), scrollX: Type.Optional(Type.Number()), scrollY: Type.Optional(Type.Number()) }),
|
|
9147
|
-
Type.Object({ action: Type.Literal("drag"), path: Type.Array(Type.Object(point), { minItems: 2 }) }),
|
|
9148
|
-
Type.Object({ action: Type.Literal("moveMouse"), ...point })
|
|
9149
|
-
]);
|
|
9150
|
-
var conditionProperties = {
|
|
9151
|
-
ref: Type.Optional(Type.String({ description: "Specific @e ref to test", maxLength: 128 })),
|
|
9152
|
-
scopeRef: Type.Optional(Type.String({ description: "Restrict matching to this @e subtree", maxLength: 128 })),
|
|
9153
|
-
text: Type.Optional(Type.String({ description: "Text that must match", maxLength: 512 })),
|
|
9154
|
-
role: Type.Optional(Type.String({ description: "Exact normalized role", maxLength: 128 })),
|
|
9155
|
-
value: Type.Optional(Type.String({ description: "Exact normalized value; normally pair with ref" })),
|
|
9156
|
-
until: Type.Optional(Type.Union([Type.Literal("present"), Type.Literal("absent")], { description: "Desired condition, default present" })),
|
|
9157
|
-
timeoutMs: Type.Optional(Type.Number({ description: "Maximum wait, default 10000ms", minimum: 100, maximum: 6e4 }))
|
|
9158
|
-
};
|
|
9159
|
-
var OBSERVE_RISK = {
|
|
9160
|
-
riskClass: "read_only",
|
|
9161
|
-
concurrencySafe: false,
|
|
9162
|
-
permissionScope: "host",
|
|
9163
|
-
readsHostResource: true,
|
|
9164
|
-
allowedForSubagentByDefault: false
|
|
9165
|
-
};
|
|
9166
|
-
var BROWSER_RISK = {
|
|
9167
|
-
riskClass: "external_side_effect",
|
|
9168
|
-
concurrencySafe: false,
|
|
9169
|
-
requiresPermission: true,
|
|
9170
|
-
permissionScope: "host",
|
|
9171
|
-
externalSideEffect: true,
|
|
9172
|
-
allowedForSubagentByDefault: false
|
|
9173
|
-
};
|
|
9174
|
-
var ACT_RISK = {
|
|
9175
|
-
riskClass: "external_side_effect",
|
|
9176
|
-
concurrencySafe: false,
|
|
9177
|
-
requiresPermission: true,
|
|
9178
|
-
permissionScope: "host",
|
|
9179
|
-
writesHostResource: true,
|
|
9180
|
-
externalSideEffect: true,
|
|
9181
|
-
highRisk: true,
|
|
9182
|
-
allowedForSubagentByDefault: false
|
|
9183
|
-
};
|
|
9184
|
-
function describe(description, ...extra) {
|
|
9185
|
-
const lines = [description];
|
|
9186
|
-
for (const part of extra) {
|
|
9187
|
-
if (!part) continue;
|
|
9188
|
-
for (const line of Array.isArray(part) ? part : [part]) lines.push(line);
|
|
9189
|
-
}
|
|
9190
|
-
return lines.join(" ");
|
|
9191
|
-
}
|
|
9192
|
-
var findTool = {
|
|
9193
|
-
name: "find_roots",
|
|
9194
|
-
label: "Find Roots",
|
|
9195
|
-
risk: OBSERVE_RISK,
|
|
9196
|
-
description: describe(
|
|
9197
|
-
"Find a bounded, ranked set of controllable UI roots with refs, geometry, and focus state.",
|
|
9198
|
-
"Find a target root before observe_ui when needed. Desktop control is a last resort - prefer API/CLI/web tools when they suffice."
|
|
9199
|
-
),
|
|
9200
|
-
parameters: Type.Object({
|
|
9201
|
-
text: Type.Optional(Type.String({ description: "Ranked app or title text", maxLength: 256 })),
|
|
9202
|
-
app: Type.Optional(Type.String({ description: "Exact normalized app name", maxLength: 256 })),
|
|
9203
|
-
bundleId: Type.Optional(Type.String({ description: "Exact bundle id" })),
|
|
9204
|
-
pid: Type.Optional(Type.Number({ description: "Exact process id" })),
|
|
9205
|
-
kind: Type.Optional(Type.Union([Type.Literal("window"), Type.Literal("menu"), Type.Literal("sheet"), Type.Literal("popover"), Type.Literal("dialog"), Type.Literal("browser_page")], { description: "Exact root kind" }))
|
|
9206
|
-
}),
|
|
9207
|
-
executor: executeFind
|
|
9208
|
-
};
|
|
9209
|
-
var observeTool = {
|
|
9210
|
-
name: "observe_ui",
|
|
9211
|
-
label: "Observe UI",
|
|
9212
|
-
risk: OBSERVE_RISK,
|
|
9213
|
-
description: describe(
|
|
9214
|
-
"Capture the current/frontmost root or one exact @r root and return a bounded UI outline.",
|
|
9215
|
-
"Primary UI observation tool. Follow with search_ui, expand_ui, inspect_ui, or act_ui.",
|
|
9216
|
-
[
|
|
9217
|
-
"Use mode=semantic to skip OCR and images, visual to force them, and fused for automatic selection.",
|
|
9218
|
-
"Use @e outline refs from observe_ui/search_ui for act_ui; pictureOnly refs are coordinate-only and blocked by UI-tree-only policy."
|
|
9219
|
-
]
|
|
9220
|
-
),
|
|
9221
|
-
parameters: Type.Object({
|
|
9222
|
-
root: Type.Optional(Type.String({ description: "Exact @r ref issued by find_roots" })),
|
|
9223
|
-
mode: Type.Optional(Type.Union([Type.Literal("semantic"), Type.Literal("visual"), Type.Literal("fused")], { description: "Observation mode, default fused" }))
|
|
9224
|
-
}),
|
|
9225
|
-
executor: executeObserve
|
|
9226
|
-
};
|
|
9227
|
-
var searchUiTool = {
|
|
9228
|
-
name: "search_ui",
|
|
9229
|
-
label: "Search UI",
|
|
9230
|
-
risk: OBSERVE_RISK,
|
|
9231
|
-
description: describe(
|
|
9232
|
-
"Return a bounded, deterministically ranked search of the cached outline. At least one predicate is required.",
|
|
9233
|
-
"Find targets not shown in the compact observe_ui output; refine broad searches instead of paging matches."
|
|
9234
|
-
),
|
|
9235
|
-
parameters: Type.Object({
|
|
9236
|
-
text: Type.Optional(Type.String({ description: "Human-readable text or label", maxLength: 256 })),
|
|
9237
|
-
role: Type.Optional(Type.String({ description: "Exact normalized role, e.g. button", maxLength: 128 })),
|
|
9238
|
-
capability: Type.Optional(Type.String({ description: "Exact capability, e.g. press", maxLength: 128 })),
|
|
9239
|
-
stateId
|
|
9240
|
-
}),
|
|
9241
|
-
executor: executeSearchUi
|
|
9242
|
-
};
|
|
9243
|
-
var expandUiTool = {
|
|
9244
|
-
name: "expand_ui",
|
|
9245
|
-
label: "Expand UI",
|
|
9246
|
-
risk: OBSERVE_RISK,
|
|
9247
|
-
description: describe(
|
|
9248
|
-
"Unfold bounded local outline context for one @e ref.",
|
|
9249
|
-
"Expand a specific ref instead of dumping unrelated UI."
|
|
9250
|
-
),
|
|
9251
|
-
parameters: Type.Object({ ref: Type.String(), depth: Type.Optional(Type.Number({ minimum: 1, maximum: 8, description: "Subtree depth, default 3" })), stateId }),
|
|
9252
|
-
executor: executeExpandUi
|
|
9253
|
-
};
|
|
9254
|
-
var inspectUiTool = {
|
|
9255
|
-
name: "inspect_ui",
|
|
9256
|
-
label: "Inspect UI",
|
|
9257
|
-
risk: OBSERVE_RISK,
|
|
9258
|
-
description: describe(
|
|
9259
|
-
"Inspect one exact outline ref with fields, geometry, capabilities, and annotations.",
|
|
9260
|
-
"Use when a target's evidence or provenance matters."
|
|
9261
|
-
),
|
|
9262
|
-
parameters: Type.Object({ ref: Type.String(), stateId }),
|
|
9263
|
-
executor: executeInspectUi
|
|
9264
|
-
};
|
|
9265
|
-
var actTool = {
|
|
9266
|
-
name: "act_ui",
|
|
9267
|
-
label: "Act",
|
|
9268
|
-
risk: ACT_RISK,
|
|
9269
|
-
description: describe(
|
|
9270
|
-
"Perform one or more precisely targeted checked actions and return the successor state.",
|
|
9271
|
-
"Pass dependent click/type steps together and use expect for observable completion.",
|
|
9272
|
-
["After clicking an editable region, omit ref from typeText/keypress so input follows the established focus."]
|
|
9273
|
-
),
|
|
9274
|
-
parameters: Type.Object({ stateId, expect: Type.Optional(Type.Object(conditionProperties)), actions: Type.Array(uiAction, { minItems: 1, maxItems: 20 }) }),
|
|
9275
|
-
executor: executeAct
|
|
9276
|
-
};
|
|
9277
|
-
var readTextTool = {
|
|
9278
|
-
name: "read_text",
|
|
9279
|
-
label: "Read Text",
|
|
9280
|
-
risk: OBSERVE_RISK,
|
|
9281
|
-
description: describe(
|
|
9282
|
-
"Read a fixed-size page from an @e UI ref or immutable @o truncated-output ref.",
|
|
9283
|
-
"Use @e with its stateId; @o continuation refs don't need stateId."
|
|
9284
|
-
),
|
|
9285
|
-
parameters: Type.Object({ ref: Type.String(), offset: Type.Optional(Type.Number({ minimum: 0 })), stateId: Type.Optional(stateId) }),
|
|
9286
|
-
executor: executeReadText
|
|
9287
|
-
};
|
|
9288
|
-
var waitForTool = {
|
|
9289
|
-
name: "wait_for",
|
|
9290
|
-
label: "Wait For",
|
|
9291
|
-
risk: OBSERVE_RISK,
|
|
9292
|
-
description: describe(
|
|
9293
|
-
"Wait for one scoped UI condition and return the successor state.",
|
|
9294
|
-
"Use after asynchronous UI changes instead of polling observe_ui."
|
|
9295
|
-
),
|
|
9296
|
-
parameters: Type.Object({ ...conditionProperties, stateId }),
|
|
9297
|
-
executor: executeWaitFor
|
|
9298
|
-
};
|
|
9299
|
-
var launchBrowserTool = {
|
|
9300
|
-
name: "launch_browser",
|
|
9301
|
-
label: "Launch Browser Context",
|
|
9302
|
-
risk: BROWSER_RISK,
|
|
9303
|
-
description: describe(
|
|
9304
|
-
"Launch the configured Pie-managed CDP browser and return an observed browser-page state.",
|
|
9305
|
-
"Use for browser work that needs a managed CDP context.",
|
|
9306
|
-
["Prefer curl through bash when the page is directly fetchable."]
|
|
9307
|
-
),
|
|
9308
|
-
parameters: Type.Object({ url: Type.Optional(Type.String({ maxLength: 8192 })) }),
|
|
9309
|
-
executor: executeLaunchBrowser
|
|
9310
|
-
};
|
|
9311
|
-
var navigateBrowserTool = {
|
|
9312
|
-
name: "navigate_browser",
|
|
9313
|
-
label: "Navigate Browser",
|
|
9314
|
-
risk: BROWSER_RISK,
|
|
9315
|
-
description: describe(
|
|
9316
|
-
"Navigate an observed CDP browser-page state to an HTTP(S) URL.",
|
|
9317
|
-
"Native browser windows use act_ui; this tool is CDP-only."
|
|
9318
|
-
),
|
|
9319
|
-
parameters: Type.Object({ url: Type.String({ maxLength: 8192 }), stateId }),
|
|
9320
|
-
executor: executeNavigateBrowser
|
|
9321
|
-
};
|
|
9322
|
-
var evaluateBrowserTool = {
|
|
9323
|
-
name: "evaluate_browser",
|
|
9324
|
-
label: "Evaluate Browser",
|
|
9325
|
-
risk: BROWSER_RISK,
|
|
9326
|
-
description: describe(
|
|
9327
|
-
"Evaluate targeted JavaScript in a CDP browser-page state; returned output is strictly bounded.",
|
|
9328
|
-
"Prefer observe/search/read; return selected fields, aggregates, or bounded slices."
|
|
9329
|
-
),
|
|
9330
|
-
parameters: Type.Object({ stateId, expression: Type.String({ maxLength: 65536 }) }),
|
|
9331
|
-
executor: executeEvaluateBrowser
|
|
9332
|
-
};
|
|
9333
|
-
var OBSERVE_TOOLS = [findTool, observeTool, searchUiTool, expandUiTool, inspectUiTool, readTextTool, waitForTool, launchBrowserTool, navigateBrowserTool, evaluateBrowserTool];
|
|
9334
|
-
var ACT_TOOLS = [actTool];
|
|
9335
|
-
function summarizeActArgs(args) {
|
|
9336
|
-
const actions = args?.actions;
|
|
9337
|
-
if (!Array.isArray(actions) || actions.length === 0) return "(no actions)";
|
|
9338
|
-
const names = actions.map((action) => action?.action ?? "unknown");
|
|
9339
|
-
const unique = [...new Set(names)];
|
|
9340
|
-
return `${actions.length} action(s): ${unique.join(", ")}`;
|
|
9341
|
-
}
|
|
9342
|
-
async function confirmAct(ctx, args) {
|
|
9343
|
-
if (ctx.yoloMode) return true;
|
|
9344
|
-
if (!ctx.interactions.isAvailable()) return false;
|
|
9345
|
-
const response = await ctx.interactions.request({
|
|
9346
|
-
type: "confirm",
|
|
9347
|
-
id: `computer-use:${Date.now()}`,
|
|
9348
|
-
prompt: "Allow computer-use to act on your desktop (click/type/scroll/press)?",
|
|
9349
|
-
detail: [
|
|
9350
|
-
"Class: computer_use_act",
|
|
9351
|
-
`Tool: act_ui \u2014 ${summarizeActArgs(args)}`,
|
|
9352
|
-
"",
|
|
9353
|
-
"The agent will synthesize real input events on your machine."
|
|
9354
|
-
].join("\n"),
|
|
9355
|
-
timeoutMs: 12e4
|
|
9356
|
-
}).catch(() => void 0);
|
|
9357
|
-
return !!response && response.type === "confirm" && response.confirmed === true;
|
|
9358
|
-
}
|
|
9359
|
-
function blockedActResult() {
|
|
9360
|
-
return {
|
|
9361
|
-
content: [{
|
|
9362
|
-
type: "text",
|
|
9363
|
-
text: "act_ui was blocked: the user did not confirm desktop control (or no interaction handler is available). Do not retry act_ui; explain what you intended to do and ask the user how to proceed."
|
|
9364
|
-
}],
|
|
9365
|
-
isError: true
|
|
9366
|
-
};
|
|
10784
|
+
// vendor/computer-use-axi/src/test-backend-guard.ts
|
|
10785
|
+
function resolveTestOnlyBackend(env = process.env) {
|
|
10786
|
+
if (env.PIE_COMPUTER_USE_FAKE_BACKEND !== "1") return { enabled: false };
|
|
10787
|
+
if (env.PIE_COMPUTER_USE_ENABLE_TEST_COMMANDS !== "1") {
|
|
10788
|
+
return {
|
|
10789
|
+
enabled: false,
|
|
10790
|
+
warning: "PIE_COMPUTER_USE_FAKE_BACKEND is test-only and was ignored: it requires PIE_COMPUTER_USE_ENABLE_TEST_COMMANDS=1 as well."
|
|
10791
|
+
};
|
|
10792
|
+
}
|
|
10793
|
+
return { enabled: true };
|
|
9367
10794
|
}
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
|
|
10795
|
+
|
|
10796
|
+
// vendor/computer-use-axi/src/broker-main.ts
|
|
10797
|
+
async function loadTestOnlyBackend() {
|
|
10798
|
+
const decision = resolveTestOnlyBackend();
|
|
10799
|
+
if (decision.warning) process.stderr.write(`computer-use-axi broker: ${decision.warning}
|
|
10800
|
+
`);
|
|
10801
|
+
if (!decision.enabled) return void 0;
|
|
10802
|
+
const fakeBackendModule = "./platform/fake-backend.ts";
|
|
10803
|
+
try {
|
|
10804
|
+
const { createFakePlatformBackend } = await import(fakeBackendModule);
|
|
10805
|
+
const fake = createFakePlatformBackend();
|
|
10806
|
+
setPlatformBackend(fake);
|
|
10807
|
+
return fake;
|
|
10808
|
+
} catch (error) {
|
|
10809
|
+
process.stderr.write(
|
|
10810
|
+
`computer-use-axi broker: the test-only fake backend is not available in this build (${error instanceof Error ? error.message : String(error)}); continuing with the real platform backend.
|
|
10811
|
+
`
|
|
10812
|
+
);
|
|
10813
|
+
return void 0;
|
|
10814
|
+
}
|
|
10815
|
+
}
|
|
10816
|
+
async function main() {
|
|
10817
|
+
const fake = await loadTestOnlyBackend();
|
|
10818
|
+
const handle = await startBroker({
|
|
10819
|
+
testCommands: process.env.PIE_COMPUTER_USE_ENABLE_TEST_COMMANDS === "1",
|
|
10820
|
+
restartTestHelper: fake ? () => fake.fakeControl.restart() : void 0,
|
|
10821
|
+
onShutdown: () => {
|
|
10822
|
+
setTimeout(() => process.exit(0), 50).unref();
|
|
9382
10823
|
}
|
|
9383
10824
|
});
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
const loaded = getLoadedComputerUseConfig();
|
|
9387
|
-
return [
|
|
9388
|
-
"computer-use configuration",
|
|
9389
|
-
`browser_use: ${loaded.config.browser_use ? "enabled" : "disabled"}`,
|
|
9390
|
-
`managed_browser: ${loaded.config.managed_browser}`,
|
|
9391
|
-
`headless: ${loaded.config.headless ? "enabled" : "disabled"}`,
|
|
9392
|
-
`cursor_overlay: ${loaded.config.cursor_overlay ? "enabled" : "disabled"}`,
|
|
9393
|
-
"",
|
|
9394
|
-
"Sources:",
|
|
9395
|
-
...loaded.sources.map((source) => `- ${source.path}: ${source.error ? `error: ${source.error}` : source.exists ? "loaded" : "not found"}`),
|
|
9396
|
-
`- env overrides: ${Object.keys(loaded.env).join(", ") || "none"}`
|
|
9397
|
-
].join("\n");
|
|
9398
|
-
}
|
|
9399
|
-
async function initializeSessionState(ctx) {
|
|
9400
|
-
loadComputerUseConfig(ctx.cwd);
|
|
9401
|
-
reconstructStateFromBranch(ctx);
|
|
9402
|
-
}
|
|
9403
|
-
function computerUseExtension(ctx) {
|
|
9404
|
-
ctx.registerCleanup?.(() => shutdownComputerUseSession());
|
|
9405
|
-
let actApproved = false;
|
|
9406
|
-
const confirmActOnce = async (args) => {
|
|
9407
|
-
if (actApproved) return true;
|
|
9408
|
-
const approved = await confirmAct(ctx, args);
|
|
9409
|
-
if (approved) actApproved = true;
|
|
9410
|
-
return approved;
|
|
10825
|
+
const stop = () => {
|
|
10826
|
+
void handle.close().catch(() => void 0);
|
|
9411
10827
|
};
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
10828
|
+
process.on("SIGTERM", stop);
|
|
10829
|
+
process.on("SIGINT", stop);
|
|
10830
|
+
process.on("SIGHUP", stop);
|
|
10831
|
+
process.on("uncaughtException", (error) => {
|
|
10832
|
+
process.stderr.write(`computer-use-axi broker fatal: ${error instanceof Error ? error.stack ?? error.message : String(error)}
|
|
10833
|
+
`);
|
|
10834
|
+
process.exit(1);
|
|
9415
10835
|
});
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
handler: (commandCtx) => {
|
|
9420
|
-
loadComputerUseConfig(commandCtx.cwd);
|
|
9421
|
-
commandCtx.ui.notify(formatConfigStatus(), "info");
|
|
9422
|
-
}
|
|
9423
|
-
});
|
|
9424
|
-
ctx.on("session:changed", async () => {
|
|
9425
|
-
actApproved = false;
|
|
9426
|
-
await shutdownComputerUseSession().catch(() => void 0);
|
|
9427
|
-
await initializeSessionState(ctx);
|
|
10836
|
+
process.on("unhandledRejection", (reason) => {
|
|
10837
|
+
process.stderr.write(`computer-use-axi broker unhandled rejection: ${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}
|
|
10838
|
+
`);
|
|
9428
10839
|
});
|
|
9429
10840
|
}
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
}
|
|
10841
|
+
main().catch((error) => {
|
|
10842
|
+
const code = error.code;
|
|
10843
|
+
process.stderr.write(`computer-use-axi broker: ${error instanceof Error ? error.message : String(error)}
|
|
10844
|
+
`);
|
|
10845
|
+
process.exit(code === "EXISTING_BROKER" ? 0 : 1);
|
|
10846
|
+
});
|