@vm0/cli 9.157.0 → 9.158.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{chunk-D6CWF2IH.js → chunk-AUKY6FBC.js} +919 -89
- package/{chunk-D6CWF2IH.js.map → chunk-AUKY6FBC.js.map} +1 -1
- package/index.js +9 -9
- package/package.json +1 -2
- package/zero.js +275 -1043
- package/zero.js.map +1 -1
package/zero.js
CHANGED
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
completeTelegramFileUpload,
|
|
17
17
|
configureGlobalProxyFromEnv,
|
|
18
18
|
connectorTypeSchema,
|
|
19
|
+
createComputerUseReadCommand,
|
|
20
|
+
createComputerUseWriteCommand,
|
|
19
21
|
createLocalBrowserReadCommand,
|
|
20
22
|
createLocalBrowserWriteCommand,
|
|
21
23
|
createSkill,
|
|
@@ -23,6 +25,7 @@ import {
|
|
|
23
25
|
createZeroRun,
|
|
24
26
|
decodeCliTokenPayload,
|
|
25
27
|
decodeZeroTokenPayload,
|
|
28
|
+
deleteComputerUseHost,
|
|
26
29
|
deleteLocalBrowserHost,
|
|
27
30
|
deleteSkill,
|
|
28
31
|
deleteZeroAgent,
|
|
@@ -50,7 +53,7 @@ import {
|
|
|
50
53
|
getActiveOrg,
|
|
51
54
|
getApiUrl,
|
|
52
55
|
getAuthMethodsForType,
|
|
53
|
-
|
|
56
|
+
getComputerUseCommand,
|
|
54
57
|
getConnectorDerivedNames,
|
|
55
58
|
getConnectorEnvironmentMapping,
|
|
56
59
|
getConnectorFirewall,
|
|
@@ -83,6 +86,8 @@ import {
|
|
|
83
86
|
isInteractive,
|
|
84
87
|
isUUID,
|
|
85
88
|
leaveZeroOrg,
|
|
89
|
+
listComputerUseAuditEvents,
|
|
90
|
+
listComputerUseHosts,
|
|
86
91
|
listLocalBrowserAuditEvents,
|
|
87
92
|
listLocalBrowserHosts,
|
|
88
93
|
listSkills,
|
|
@@ -106,7 +111,6 @@ import {
|
|
|
106
111
|
promptPassword,
|
|
107
112
|
promptSelect,
|
|
108
113
|
promptText,
|
|
109
|
-
registerComputerUseHost,
|
|
110
114
|
removeZeroOrgMember,
|
|
111
115
|
renderRunCreated,
|
|
112
116
|
requestDeveloperSupportConsent,
|
|
@@ -130,7 +134,6 @@ import {
|
|
|
130
134
|
source_default,
|
|
131
135
|
submitDeveloperSupport,
|
|
132
136
|
switchZeroOrg,
|
|
133
|
-
unregisterComputerUseHost,
|
|
134
137
|
updateSkill,
|
|
135
138
|
updateZeroAgent,
|
|
136
139
|
updateZeroAgentInstructions,
|
|
@@ -142,7 +145,7 @@ import {
|
|
|
142
145
|
zeroAgentCustomSkillNameSchema,
|
|
143
146
|
zeroLocalAgentCommand,
|
|
144
147
|
zeroTokenAllowsFeatureSwitch
|
|
145
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-AUKY6FBC.js";
|
|
146
149
|
import {
|
|
147
150
|
__commonJS,
|
|
148
151
|
__require,
|
|
@@ -33941,1044 +33944,273 @@ Notes:
|
|
|
33941
33944
|
|
|
33942
33945
|
// src/commands/zero/computer-use/index.ts
|
|
33943
33946
|
init_esm_shims();
|
|
33944
|
-
|
|
33945
|
-
|
|
33946
|
-
|
|
33947
|
-
|
|
33948
|
-
// src/lib/computer-use/desktop-server.ts
|
|
33949
|
-
init_esm_shims();
|
|
33950
|
-
import {
|
|
33951
|
-
createServer
|
|
33952
|
-
} from "http";
|
|
33953
|
-
import { createServer as createNetServer } from "net";
|
|
33954
|
-
|
|
33955
|
-
// src/lib/computer-use/screencapture.ts
|
|
33956
|
-
init_esm_shims();
|
|
33957
|
-
import { execFile } from "child_process";
|
|
33958
|
-
import { readFile, unlink } from "fs/promises";
|
|
33959
|
-
import { randomUUID } from "crypto";
|
|
33960
|
-
import { join as join5 } from "path";
|
|
33961
|
-
import { tmpdir as tmpdir4 } from "os";
|
|
33962
|
-
import { promisify } from "util";
|
|
33963
|
-
var execFileAsync = promisify(execFile);
|
|
33964
|
-
async function captureScreenshot() {
|
|
33965
|
-
const tmpPath = join5(tmpdir4(), `vm0-screenshot-${randomUUID()}.jpg`);
|
|
33966
|
-
try {
|
|
33967
|
-
await execFileAsync("screencapture", ["-x", "-t", "jpg", tmpPath]);
|
|
33968
|
-
const info = await getScreenInfo();
|
|
33969
|
-
if (info.scaleFactor > 1) {
|
|
33970
|
-
await execFileAsync("sips", [
|
|
33971
|
-
"-z",
|
|
33972
|
-
String(info.height),
|
|
33973
|
-
String(info.width),
|
|
33974
|
-
"-s",
|
|
33975
|
-
"formatOptions",
|
|
33976
|
-
"80",
|
|
33977
|
-
tmpPath
|
|
33978
|
-
]);
|
|
33979
|
-
}
|
|
33980
|
-
const buffer = await readFile(tmpPath);
|
|
33981
|
-
return {
|
|
33982
|
-
image: buffer.toString("base64"),
|
|
33983
|
-
width: info.width,
|
|
33984
|
-
height: info.height,
|
|
33985
|
-
scaleFactor: info.scaleFactor,
|
|
33986
|
-
format: "jpg"
|
|
33987
|
-
};
|
|
33988
|
-
} finally {
|
|
33989
|
-
await unlink(tmpPath).catch(() => {
|
|
33990
|
-
});
|
|
33991
|
-
}
|
|
33992
|
-
}
|
|
33993
|
-
async function captureRegionScreenshot(region) {
|
|
33994
|
-
const tmpPath = join5(tmpdir4(), `vm0-zoom-${randomUUID()}.jpg`);
|
|
33995
|
-
try {
|
|
33996
|
-
const regionArg = `${region.x},${region.y},${region.width},${region.height}`;
|
|
33997
|
-
await execFileAsync("screencapture", [
|
|
33998
|
-
"-x",
|
|
33999
|
-
"-t",
|
|
34000
|
-
"jpg",
|
|
34001
|
-
"-R",
|
|
34002
|
-
regionArg,
|
|
34003
|
-
tmpPath
|
|
34004
|
-
]);
|
|
34005
|
-
const info = await getScreenInfo();
|
|
34006
|
-
if (info.scaleFactor > 1) {
|
|
34007
|
-
await execFileAsync("sips", [
|
|
34008
|
-
"-z",
|
|
34009
|
-
String(region.height),
|
|
34010
|
-
String(region.width),
|
|
34011
|
-
"-s",
|
|
34012
|
-
"formatOptions",
|
|
34013
|
-
"80",
|
|
34014
|
-
tmpPath
|
|
34015
|
-
]);
|
|
34016
|
-
}
|
|
34017
|
-
const buffer = await readFile(tmpPath);
|
|
34018
|
-
return {
|
|
34019
|
-
image: buffer.toString("base64"),
|
|
34020
|
-
width: region.width,
|
|
34021
|
-
height: region.height,
|
|
34022
|
-
scaleFactor: info.scaleFactor,
|
|
34023
|
-
format: "jpg"
|
|
34024
|
-
};
|
|
34025
|
-
} finally {
|
|
34026
|
-
await unlink(tmpPath).catch(() => {
|
|
34027
|
-
});
|
|
34028
|
-
}
|
|
33947
|
+
function sleep2(ms) {
|
|
33948
|
+
return new Promise((resolve2) => {
|
|
33949
|
+
setTimeout(resolve2, ms);
|
|
33950
|
+
});
|
|
34029
33951
|
}
|
|
34030
|
-
|
|
34031
|
-
|
|
34032
|
-
|
|
34033
|
-
|
|
34034
|
-
|
|
34035
|
-
const data = JSON.parse(stdout);
|
|
34036
|
-
const displays = data.SPDisplaysDataType ?? [];
|
|
34037
|
-
for (const gpu of displays) {
|
|
34038
|
-
const screens = gpu.spdisplays_ndrvs ?? [];
|
|
34039
|
-
for (const screen of screens) {
|
|
34040
|
-
const pixelStr = screen._spdisplays_pixels;
|
|
34041
|
-
if (pixelStr) {
|
|
34042
|
-
const pixelMatch = pixelStr.match(/(\d+)\s*x\s*(\d+)/);
|
|
34043
|
-
if (pixelMatch?.[1] && pixelMatch[2]) {
|
|
34044
|
-
const physicalWidth = parseInt(pixelMatch[1], 10);
|
|
34045
|
-
const physicalHeight = parseInt(pixelMatch[2], 10);
|
|
34046
|
-
const resStr = screen._spdisplays_resolution ?? screen.spdisplays_resolution ?? "";
|
|
34047
|
-
const resMatch = resStr.match(/(\d+)\s*x\s*(\d+)/);
|
|
34048
|
-
let scaleFactor;
|
|
34049
|
-
let logicalWidth;
|
|
34050
|
-
let logicalHeight;
|
|
34051
|
-
if (resMatch?.[1] && resMatch[2]) {
|
|
34052
|
-
logicalWidth = parseInt(resMatch[1], 10);
|
|
34053
|
-
logicalHeight = parseInt(resMatch[2], 10);
|
|
34054
|
-
scaleFactor = Math.round(physicalWidth / logicalWidth);
|
|
34055
|
-
} else {
|
|
34056
|
-
const isRetina = /retina/i.test(resStr);
|
|
34057
|
-
scaleFactor = isRetina ? 2 : 1;
|
|
34058
|
-
logicalWidth = Math.floor(physicalWidth / scaleFactor);
|
|
34059
|
-
logicalHeight = Math.floor(physicalHeight / scaleFactor);
|
|
34060
|
-
}
|
|
34061
|
-
return {
|
|
34062
|
-
width: logicalWidth,
|
|
34063
|
-
height: logicalHeight,
|
|
34064
|
-
scaleFactor
|
|
34065
|
-
};
|
|
34066
|
-
}
|
|
34067
|
-
}
|
|
34068
|
-
}
|
|
33952
|
+
function parseTimeoutSeconds(value) {
|
|
33953
|
+
if (!value) return 30;
|
|
33954
|
+
const seconds = Number.parseInt(value, 10);
|
|
33955
|
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
33956
|
+
throw new Error("Timeout must be a positive number of seconds");
|
|
34069
33957
|
}
|
|
34070
|
-
return
|
|
34071
|
-
}
|
|
34072
|
-
|
|
34073
|
-
// src/lib/computer-use/cliclick.ts
|
|
34074
|
-
init_esm_shims();
|
|
34075
|
-
import { execFile as execFile2 } from "child_process";
|
|
34076
|
-
import { promisify as promisify2 } from "util";
|
|
34077
|
-
import { setTimeout as sleep2 } from "timers/promises";
|
|
34078
|
-
var execFileAsync2 = promisify2(execFile2);
|
|
34079
|
-
async function leftClickDrag(startX, startY, endX, endY) {
|
|
34080
|
-
await execFileAsync2("cliclick", [
|
|
34081
|
-
`dd:${startX},${startY}`,
|
|
34082
|
-
`du:${endX},${endY}`
|
|
34083
|
-
]);
|
|
34084
|
-
}
|
|
34085
|
-
async function leftMouseDown(x, y) {
|
|
34086
|
-
await execFileAsync2("cliclick", [`dd:${x},${y}`]);
|
|
34087
|
-
}
|
|
34088
|
-
async function leftMouseUp(x, y) {
|
|
34089
|
-
await execFileAsync2("cliclick", [`du:${x},${y}`]);
|
|
33958
|
+
return seconds;
|
|
34090
33959
|
}
|
|
34091
|
-
|
|
34092
|
-
|
|
34093
|
-
|
|
34094
|
-
|
|
34095
|
-
|
|
34096
|
-
triple_click: "tc",
|
|
34097
|
-
move: "m"
|
|
34098
|
-
};
|
|
34099
|
-
var VALID_ACTIONS = new Set(Object.keys(ACTION_COMMANDS));
|
|
34100
|
-
async function isCliclickInstalled() {
|
|
34101
|
-
try {
|
|
34102
|
-
await execFileAsync2("which", ["cliclick"]);
|
|
34103
|
-
return true;
|
|
34104
|
-
} catch {
|
|
34105
|
-
return false;
|
|
33960
|
+
function parseOptionalNonNegativeInteger(value, label) {
|
|
33961
|
+
if (value === void 0) return void 0;
|
|
33962
|
+
const parsed = Number.parseInt(value, 10);
|
|
33963
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
33964
|
+
throw new Error(`${label} must be a non-negative integer`);
|
|
34106
33965
|
}
|
|
33966
|
+
return parsed;
|
|
34107
33967
|
}
|
|
34108
|
-
|
|
34109
|
-
|
|
34110
|
-
|
|
34111
|
-
} catch {
|
|
34112
|
-
throw new Error("cliclick not found. Install with: brew install cliclick");
|
|
34113
|
-
}
|
|
34114
|
-
}
|
|
34115
|
-
async function executeMouseAction(action, x, y) {
|
|
34116
|
-
await checkCliclickInstalled();
|
|
34117
|
-
const prefix = ACTION_COMMANDS[action];
|
|
34118
|
-
await execFileAsync2("cliclick", [`${prefix}:${x},${y}`]);
|
|
34119
|
-
}
|
|
34120
|
-
async function getCursorPosition() {
|
|
34121
|
-
await checkCliclickInstalled();
|
|
34122
|
-
const { stdout } = await execFileAsync2("cliclick", ["p"]);
|
|
34123
|
-
const parts = stdout.trim().split(",");
|
|
34124
|
-
const xStr = parts[0];
|
|
34125
|
-
const yStr = parts[1];
|
|
34126
|
-
if (parts.length !== 2 || xStr === void 0 || yStr === void 0) {
|
|
34127
|
-
throw new Error(`Unexpected cliclick output: ${stdout.trim()}`);
|
|
34128
|
-
}
|
|
34129
|
-
const x = parseInt(xStr, 10);
|
|
34130
|
-
const y = parseInt(yStr, 10);
|
|
34131
|
-
if (Number.isNaN(x) || Number.isNaN(y)) {
|
|
34132
|
-
throw new Error(`Failed to parse cursor position: ${stdout.trim()}`);
|
|
34133
|
-
}
|
|
34134
|
-
return { x, y };
|
|
34135
|
-
}
|
|
34136
|
-
var VALID_SPECIAL_KEYS = /* @__PURE__ */ new Set([
|
|
34137
|
-
"cmd",
|
|
34138
|
-
"ctrl",
|
|
34139
|
-
"alt",
|
|
34140
|
-
"shift",
|
|
34141
|
-
"fn",
|
|
34142
|
-
"arrow-up",
|
|
34143
|
-
"arrow-down",
|
|
34144
|
-
"arrow-left",
|
|
34145
|
-
"arrow-right",
|
|
34146
|
-
"tab",
|
|
34147
|
-
"esc",
|
|
34148
|
-
"space",
|
|
34149
|
-
"delete",
|
|
34150
|
-
"return",
|
|
34151
|
-
"enter",
|
|
34152
|
-
"home",
|
|
34153
|
-
"end",
|
|
34154
|
-
"page-up",
|
|
34155
|
-
"page-down",
|
|
34156
|
-
...Array.from({ length: 19 }, (_, i) => {
|
|
34157
|
-
return `f${i + 1}`;
|
|
34158
|
-
})
|
|
34159
|
-
]);
|
|
34160
|
-
function isValidKeyName(key) {
|
|
34161
|
-
return VALID_SPECIAL_KEYS.has(key) || key.length === 1;
|
|
34162
|
-
}
|
|
34163
|
-
function parseKeyCombo(keys) {
|
|
34164
|
-
const parts = keys.split("+");
|
|
34165
|
-
if (parts.length === 0 || parts.some((p) => {
|
|
34166
|
-
return p === "";
|
|
34167
|
-
})) {
|
|
34168
|
-
throw new Error(`Invalid key combo: "${keys}"`);
|
|
33968
|
+
function parsePositiveInteger2(value, label) {
|
|
33969
|
+
if (value === void 0) {
|
|
33970
|
+
throw new Error(`${label} is required`);
|
|
34169
33971
|
}
|
|
34170
|
-
const
|
|
34171
|
-
|
|
34172
|
-
|
|
34173
|
-
if (!isValidKeyName(key)) {
|
|
34174
|
-
throw new Error(
|
|
34175
|
-
`Unknown key: "${key}". Valid keys: single characters, or special keys like cmd, ctrl, alt, shift, tab, esc, return, arrow-up, f1-f19, etc.`
|
|
34176
|
-
);
|
|
34177
|
-
}
|
|
33972
|
+
const parsed = Number.parseInt(value, 10);
|
|
33973
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
33974
|
+
throw new Error(`${label} must be a positive integer`);
|
|
34178
33975
|
}
|
|
34179
|
-
return
|
|
34180
|
-
}
|
|
34181
|
-
function keyAction(key) {
|
|
34182
|
-
return VALID_SPECIAL_KEYS.has(key) ? `kp:${key}` : `t:${key}`;
|
|
33976
|
+
return parsed;
|
|
34183
33977
|
}
|
|
34184
|
-
|
|
34185
|
-
|
|
34186
|
-
|
|
34187
|
-
|
|
34188
|
-
|
|
34189
|
-
}
|
|
34190
|
-
const args = [];
|
|
34191
|
-
for (const mod of modifiers) {
|
|
34192
|
-
args.push(`kd:${mod}`);
|
|
34193
|
-
}
|
|
34194
|
-
args.push(keyAction(mainKey));
|
|
34195
|
-
for (let i = modifiers.length - 1; i >= 0; i--) {
|
|
34196
|
-
args.push(`ku:${modifiers[i]}`);
|
|
33978
|
+
function parsePositiveNumber(value, label) {
|
|
33979
|
+
if (value === void 0) return void 0;
|
|
33980
|
+
const parsed = Number.parseFloat(value);
|
|
33981
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
33982
|
+
throw new Error(`${label} must be a positive number`);
|
|
34197
33983
|
}
|
|
34198
|
-
|
|
34199
|
-
}
|
|
34200
|
-
async function holdKey(keys, durationMs) {
|
|
34201
|
-
const { modifiers, mainKey } = parseKeyCombo(keys);
|
|
34202
|
-
const allKeys = [...modifiers, mainKey];
|
|
34203
|
-
const downArgs = allKeys.map((k) => {
|
|
34204
|
-
return `kd:${k}`;
|
|
34205
|
-
});
|
|
34206
|
-
const upArgs = [...allKeys].reverse().map((k) => {
|
|
34207
|
-
return `ku:${k}`;
|
|
34208
|
-
});
|
|
34209
|
-
await execFileAsync2("cliclick", downArgs);
|
|
34210
|
-
await sleep2(durationMs);
|
|
34211
|
-
await execFileAsync2("cliclick", upArgs);
|
|
34212
|
-
}
|
|
34213
|
-
async function typeText(text) {
|
|
34214
|
-
await execFileAsync2("cliclick", [`t:${text}`]);
|
|
33984
|
+
return parsed;
|
|
34215
33985
|
}
|
|
34216
|
-
|
|
34217
|
-
|
|
34218
|
-
|
|
34219
|
-
import { execFile as execFile3 } from "child_process";
|
|
34220
|
-
import { promisify as promisify3 } from "util";
|
|
34221
|
-
var execFileAsync3 = promisify3(execFile3);
|
|
34222
|
-
var DEFAULT_SCROLL_AMOUNT = 3;
|
|
34223
|
-
async function scroll(x, y, direction, amount = DEFAULT_SCROLL_AMOUNT) {
|
|
34224
|
-
await execFileAsync3("cliclick", [`m:${x},${y}`]);
|
|
34225
|
-
let dy = 0;
|
|
34226
|
-
let dx = 0;
|
|
34227
|
-
switch (direction) {
|
|
34228
|
-
case "up":
|
|
34229
|
-
dy = amount;
|
|
34230
|
-
break;
|
|
34231
|
-
case "down":
|
|
34232
|
-
dy = -amount;
|
|
34233
|
-
break;
|
|
34234
|
-
case "left":
|
|
34235
|
-
dx = amount;
|
|
34236
|
-
break;
|
|
34237
|
-
case "right":
|
|
34238
|
-
dx = -amount;
|
|
34239
|
-
break;
|
|
33986
|
+
function parseMouseButton(value) {
|
|
33987
|
+
if (value === "left" || value === "right" || value === "middle") {
|
|
33988
|
+
return value;
|
|
34240
33989
|
}
|
|
34241
|
-
|
|
34242
|
-
"ObjC.import('CoreGraphics');",
|
|
34243
|
-
`var e = $.CGEventCreateScrollWheelEvent(null, 0, 2, ${dy}, ${dx});`,
|
|
34244
|
-
"$.CGEventPost($.kCGHIDEventTap, e);"
|
|
34245
|
-
].join(" ");
|
|
34246
|
-
await execFileAsync3("osascript", ["-l", "JavaScript", "-e", script]);
|
|
34247
|
-
}
|
|
34248
|
-
|
|
34249
|
-
// src/lib/computer-use/clipboard.ts
|
|
34250
|
-
init_esm_shims();
|
|
34251
|
-
import { execFile as execFile4, spawn } from "child_process";
|
|
34252
|
-
import { promisify as promisify4 } from "util";
|
|
34253
|
-
var execFileAsync4 = promisify4(execFile4);
|
|
34254
|
-
async function readClipboard() {
|
|
34255
|
-
const { stdout } = await execFileAsync4("pbpaste");
|
|
34256
|
-
return stdout;
|
|
34257
|
-
}
|
|
34258
|
-
async function writeClipboard(text) {
|
|
34259
|
-
return new Promise((resolve2, reject) => {
|
|
34260
|
-
const proc = spawn("pbcopy", { stdio: ["pipe", "ignore", "ignore"] });
|
|
34261
|
-
proc.on("error", reject);
|
|
34262
|
-
proc.on("close", (code) => {
|
|
34263
|
-
if (code === 0) {
|
|
34264
|
-
resolve2();
|
|
34265
|
-
} else {
|
|
34266
|
-
reject(new Error(`pbcopy exited with code ${code}`));
|
|
34267
|
-
}
|
|
34268
|
-
});
|
|
34269
|
-
proc.stdin.end(text);
|
|
34270
|
-
});
|
|
33990
|
+
throw new Error("button must be left, right, or middle");
|
|
34271
33991
|
}
|
|
34272
|
-
|
|
34273
|
-
|
|
34274
|
-
|
|
34275
|
-
import { execFile as execFile5 } from "child_process";
|
|
34276
|
-
import { promisify as promisify5 } from "util";
|
|
34277
|
-
var execFileAsync5 = promisify5(execFile5);
|
|
34278
|
-
async function openApplication(nameOrBundleId) {
|
|
34279
|
-
const isBundleId = nameOrBundleId.includes(".");
|
|
34280
|
-
const flag = isBundleId ? "-b" : "-a";
|
|
34281
|
-
await execFileAsync5("open", [flag, nameOrBundleId]);
|
|
34282
|
-
}
|
|
34283
|
-
|
|
34284
|
-
// src/lib/computer-use/desktop-server.ts
|
|
34285
|
-
function readBody(req) {
|
|
34286
|
-
return new Promise((resolve2, reject) => {
|
|
34287
|
-
const chunks = [];
|
|
34288
|
-
req.on("data", (chunk) => {
|
|
34289
|
-
chunks.push(chunk);
|
|
34290
|
-
});
|
|
34291
|
-
req.on("end", () => {
|
|
34292
|
-
resolve2(Buffer.concat(chunks).toString());
|
|
34293
|
-
});
|
|
34294
|
-
req.on("error", reject);
|
|
34295
|
-
});
|
|
34296
|
-
}
|
|
34297
|
-
async function handleZoom(searchParams, res) {
|
|
34298
|
-
const x = Number(searchParams.get("x"));
|
|
34299
|
-
const y = Number(searchParams.get("y"));
|
|
34300
|
-
const width = Number(searchParams.get("width"));
|
|
34301
|
-
const height = Number(searchParams.get("height"));
|
|
34302
|
-
if ([x, y, width, height].some((v) => {
|
|
34303
|
-
return !Number.isFinite(v);
|
|
34304
|
-
})) {
|
|
34305
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
34306
|
-
res.end(
|
|
34307
|
-
"Missing or invalid query parameters: x, y, width, height are required numbers"
|
|
34308
|
-
);
|
|
34309
|
-
return;
|
|
34310
|
-
}
|
|
34311
|
-
if (width <= 0 || height <= 0) {
|
|
34312
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
34313
|
-
res.end("width and height must be positive");
|
|
34314
|
-
return;
|
|
34315
|
-
}
|
|
34316
|
-
if (x < 0 || y < 0) {
|
|
34317
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
34318
|
-
res.end("x and y must be non-negative");
|
|
34319
|
-
return;
|
|
33992
|
+
function parseLimit4(value) {
|
|
33993
|
+
if (value === void 0) {
|
|
33994
|
+
return 50;
|
|
34320
33995
|
}
|
|
34321
|
-
const
|
|
34322
|
-
if (
|
|
34323
|
-
|
|
34324
|
-
res.end(`Region exceeds screen bounds (${info.width}x${info.height})`);
|
|
34325
|
-
return;
|
|
33996
|
+
const parsed = parsePositiveInteger2(value, "limit");
|
|
33997
|
+
if (parsed > 200) {
|
|
33998
|
+
throw new Error("limit must be 200 or less");
|
|
34326
33999
|
}
|
|
34327
|
-
|
|
34328
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
34329
|
-
res.end(JSON.stringify(result));
|
|
34330
|
-
}
|
|
34331
|
-
function parseJsonBody(req) {
|
|
34332
|
-
return new Promise((resolve2, reject) => {
|
|
34333
|
-
const chunks = [];
|
|
34334
|
-
let size = 0;
|
|
34335
|
-
req.on("data", (chunk) => {
|
|
34336
|
-
size += chunk.length;
|
|
34337
|
-
if (size > 1024) {
|
|
34338
|
-
reject(new Error("Request body too large"));
|
|
34339
|
-
req.destroy();
|
|
34340
|
-
return;
|
|
34341
|
-
}
|
|
34342
|
-
chunks.push(chunk);
|
|
34343
|
-
});
|
|
34344
|
-
req.on("end", () => {
|
|
34345
|
-
try {
|
|
34346
|
-
resolve2(JSON.parse(Buffer.concat(chunks).toString()));
|
|
34347
|
-
} catch {
|
|
34348
|
-
reject(new Error("Invalid JSON body"));
|
|
34349
|
-
}
|
|
34350
|
-
});
|
|
34351
|
-
req.on("error", reject);
|
|
34352
|
-
});
|
|
34000
|
+
return parsed;
|
|
34353
34001
|
}
|
|
34354
|
-
|
|
34355
|
-
|
|
34356
|
-
|
|
34357
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
34358
|
-
res.end("Missing required fields: action, x, y");
|
|
34359
|
-
return;
|
|
34360
|
-
}
|
|
34361
|
-
const { action } = body;
|
|
34362
|
-
if (typeof action !== "string") {
|
|
34363
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
34364
|
-
res.end("Invalid action");
|
|
34365
|
-
return;
|
|
34002
|
+
function resultText(command) {
|
|
34003
|
+
if (!command.result) {
|
|
34004
|
+
return "";
|
|
34366
34005
|
}
|
|
34367
|
-
|
|
34368
|
-
|
|
34369
|
-
|
|
34370
|
-
|
|
34371
|
-
|
|
34006
|
+
return JSON.stringify(command.result, null, 2);
|
|
34007
|
+
}
|
|
34008
|
+
async function waitForCommand(commandId, timeoutSeconds) {
|
|
34009
|
+
const deadline = Date.now() + timeoutSeconds * 1e3;
|
|
34010
|
+
while (Date.now() <= deadline) {
|
|
34011
|
+
const command = await getComputerUseCommand(commandId);
|
|
34012
|
+
if (command.status === "pending_approval" || command.status === "queued" || command.status === "running") {
|
|
34013
|
+
if (process.stdout.isTTY) {
|
|
34014
|
+
process.stdout.write(".");
|
|
34015
|
+
}
|
|
34016
|
+
await sleep2(1e3);
|
|
34017
|
+
continue;
|
|
34372
34018
|
}
|
|
34373
|
-
|
|
34374
|
-
|
|
34375
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
34376
|
-
res.end("Coordinates x and y must be finite numbers");
|
|
34377
|
-
return;
|
|
34019
|
+
if (process.stdout.isTTY) {
|
|
34020
|
+
process.stdout.write("\n");
|
|
34378
34021
|
}
|
|
34379
|
-
|
|
34380
|
-
|
|
34381
|
-
|
|
34382
|
-
res.end(
|
|
34383
|
-
`Coordinates out of bounds. Screen size: ${info.width}x${info.height} (points)`
|
|
34022
|
+
if (command.status === "failed") {
|
|
34023
|
+
throw new Error(
|
|
34024
|
+
command.error ? `${command.error.code}: ${command.error.message}` : "Computer-use command failed"
|
|
34384
34025
|
);
|
|
34385
|
-
return;
|
|
34386
34026
|
}
|
|
34387
|
-
|
|
34388
|
-
|
|
34389
|
-
|
|
34390
|
-
return;
|
|
34391
|
-
}
|
|
34392
|
-
const typedBody = body;
|
|
34393
|
-
switch (typedBody.action) {
|
|
34394
|
-
case "left_click_drag":
|
|
34395
|
-
await leftClickDrag(
|
|
34396
|
-
typedBody.startX,
|
|
34397
|
-
typedBody.startY,
|
|
34398
|
-
typedBody.endX,
|
|
34399
|
-
typedBody.endY
|
|
34400
|
-
);
|
|
34401
|
-
break;
|
|
34402
|
-
case "left_mouse_down":
|
|
34403
|
-
await leftMouseDown(typedBody.x, typedBody.y);
|
|
34404
|
-
break;
|
|
34405
|
-
case "left_mouse_up":
|
|
34406
|
-
await leftMouseUp(typedBody.x, typedBody.y);
|
|
34407
|
-
break;
|
|
34408
|
-
case "scroll":
|
|
34409
|
-
await scroll(
|
|
34410
|
-
typedBody.x,
|
|
34411
|
-
typedBody.y,
|
|
34412
|
-
typedBody.direction,
|
|
34413
|
-
typedBody.amount
|
|
34414
|
-
);
|
|
34415
|
-
break;
|
|
34416
|
-
default:
|
|
34417
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
34418
|
-
res.end(
|
|
34419
|
-
`Unknown mouse action: ${body.action}`
|
|
34420
|
-
);
|
|
34421
|
-
return;
|
|
34422
|
-
}
|
|
34423
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
34424
|
-
res.end(JSON.stringify({ ok: true }));
|
|
34425
|
-
}
|
|
34426
|
-
async function handleKeyboard(req, res) {
|
|
34427
|
-
const raw = await readBody(req);
|
|
34428
|
-
const body = JSON.parse(raw);
|
|
34429
|
-
switch (body.action) {
|
|
34430
|
-
case "key":
|
|
34431
|
-
await pressKey(body.keys);
|
|
34432
|
-
break;
|
|
34433
|
-
case "hold_key":
|
|
34434
|
-
if (typeof body.durationMs !== "number" || !Number.isFinite(body.durationMs) || body.durationMs <= 0) {
|
|
34435
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
34436
|
-
res.end("durationMs must be a positive number");
|
|
34437
|
-
return;
|
|
34438
|
-
}
|
|
34439
|
-
await holdKey(body.keys, body.durationMs);
|
|
34440
|
-
break;
|
|
34441
|
-
case "type":
|
|
34442
|
-
if (typeof body.text !== "string" || body.text.length === 0) {
|
|
34443
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
34444
|
-
res.end("text must be a non-empty string");
|
|
34445
|
-
return;
|
|
34446
|
-
}
|
|
34447
|
-
await typeText(body.text);
|
|
34448
|
-
break;
|
|
34449
|
-
default:
|
|
34450
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
34451
|
-
res.end(
|
|
34452
|
-
`Unknown keyboard action: ${body.action}`
|
|
34453
|
-
);
|
|
34454
|
-
return;
|
|
34455
|
-
}
|
|
34456
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
34457
|
-
res.end(JSON.stringify({ ok: true }));
|
|
34458
|
-
}
|
|
34459
|
-
async function handleClipboard(req, res) {
|
|
34460
|
-
if (req.method === "GET") {
|
|
34461
|
-
const text = await readClipboard();
|
|
34462
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
34463
|
-
res.end(JSON.stringify({ text }));
|
|
34464
|
-
} else if (req.method === "POST") {
|
|
34465
|
-
const raw = await readBody(req);
|
|
34466
|
-
const body = JSON.parse(raw);
|
|
34467
|
-
await writeClipboard(body.text);
|
|
34468
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
34469
|
-
res.end(JSON.stringify({ ok: true }));
|
|
34470
|
-
} else {
|
|
34471
|
-
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
34472
|
-
res.end("Not found");
|
|
34473
|
-
}
|
|
34474
|
-
}
|
|
34475
|
-
async function handleOpenApplication(req, res) {
|
|
34476
|
-
const raw = await readBody(req);
|
|
34477
|
-
const body = JSON.parse(raw);
|
|
34478
|
-
if (typeof body.nameOrBundleId !== "string" || body.nameOrBundleId.length === 0) {
|
|
34479
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
34480
|
-
res.end("nameOrBundleId must be a non-empty string");
|
|
34481
|
-
return;
|
|
34482
|
-
}
|
|
34483
|
-
await openApplication(body.nameOrBundleId);
|
|
34484
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
34485
|
-
res.end(JSON.stringify({ ok: true }));
|
|
34486
|
-
}
|
|
34487
|
-
async function getRandomPort() {
|
|
34488
|
-
return new Promise((resolve2, reject) => {
|
|
34489
|
-
const server = createNetServer();
|
|
34490
|
-
server.listen(0, "127.0.0.1", () => {
|
|
34491
|
-
const { port } = server.address();
|
|
34492
|
-
server.close(() => {
|
|
34493
|
-
resolve2(port);
|
|
34494
|
-
});
|
|
34495
|
-
});
|
|
34496
|
-
server.on("error", reject);
|
|
34497
|
-
});
|
|
34498
|
-
}
|
|
34499
|
-
async function handleCursorPosition(res) {
|
|
34500
|
-
const position = await getCursorPosition();
|
|
34501
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
34502
|
-
res.end(JSON.stringify(position));
|
|
34503
|
-
}
|
|
34504
|
-
function routeKey(method, pathname) {
|
|
34505
|
-
return `${method} ${pathname}`;
|
|
34506
|
-
}
|
|
34507
|
-
async function handleRequest(token, req, res) {
|
|
34508
|
-
if (req.headers["x-vm0-token"] !== token) {
|
|
34509
|
-
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
34510
|
-
res.end("Forbidden");
|
|
34511
|
-
return;
|
|
34512
|
-
}
|
|
34513
|
-
const url = new URL(req.url ?? "/", "http://localhost");
|
|
34514
|
-
const { pathname, searchParams } = url;
|
|
34515
|
-
const key = routeKey(req.method ?? "GET", pathname);
|
|
34516
|
-
try {
|
|
34517
|
-
switch (key) {
|
|
34518
|
-
case "GET /screenshot": {
|
|
34519
|
-
const result = await captureScreenshot();
|
|
34520
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
34521
|
-
res.end(JSON.stringify(result));
|
|
34522
|
-
break;
|
|
34523
|
-
}
|
|
34524
|
-
case "GET /info": {
|
|
34525
|
-
const info = await getScreenInfo();
|
|
34526
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
34527
|
-
res.end(JSON.stringify(info));
|
|
34528
|
-
break;
|
|
34529
|
-
}
|
|
34530
|
-
case "GET /zoom":
|
|
34531
|
-
await handleZoom(searchParams, res);
|
|
34532
|
-
break;
|
|
34533
|
-
case "POST /mouse":
|
|
34534
|
-
await handleMouseRequest(req, res);
|
|
34535
|
-
break;
|
|
34536
|
-
case "GET /clipboard":
|
|
34537
|
-
case "POST /clipboard":
|
|
34538
|
-
await handleClipboard(req, res);
|
|
34539
|
-
break;
|
|
34540
|
-
case "POST /keyboard":
|
|
34541
|
-
await handleKeyboard(req, res);
|
|
34542
|
-
break;
|
|
34543
|
-
case "POST /open-application":
|
|
34544
|
-
await handleOpenApplication(req, res);
|
|
34545
|
-
break;
|
|
34546
|
-
case "GET /cursor-position":
|
|
34547
|
-
await handleCursorPosition(res);
|
|
34548
|
-
break;
|
|
34549
|
-
default:
|
|
34550
|
-
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
34551
|
-
res.end("Not found");
|
|
34027
|
+
const text = resultText(command);
|
|
34028
|
+
if (text) {
|
|
34029
|
+
console.log(text);
|
|
34552
34030
|
}
|
|
34553
|
-
|
|
34554
|
-
const message = error instanceof Error ? error.message : "Internal server error";
|
|
34555
|
-
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
34556
|
-
res.end(message);
|
|
34031
|
+
return;
|
|
34557
34032
|
}
|
|
34033
|
+
throw new Error(`Computer-use command timed out: ${commandId}`);
|
|
34558
34034
|
}
|
|
34559
|
-
function
|
|
34560
|
-
|
|
34561
|
-
|
|
34562
|
-
|
|
34563
|
-
|
|
34564
|
-
|
|
34565
|
-
|
|
34566
|
-
|
|
34567
|
-
});
|
|
34568
|
-
});
|
|
34569
|
-
server.on("error", reject);
|
|
34570
|
-
server.listen(port, "127.0.0.1", () => {
|
|
34571
|
-
resolve2(server);
|
|
34572
|
-
});
|
|
34035
|
+
async function runReadCommand(kind, options, payload = {}) {
|
|
34036
|
+
const timeoutSeconds = parseTimeoutSeconds(options.timeout);
|
|
34037
|
+
const created = await createComputerUseReadCommand({
|
|
34038
|
+
kind,
|
|
34039
|
+
timeoutMs: timeoutSeconds * 1e3,
|
|
34040
|
+
...options.host ? { hostName: options.host } : {},
|
|
34041
|
+
...options.hostId ? { hostId: options.hostId } : {},
|
|
34042
|
+
...payload
|
|
34573
34043
|
});
|
|
34044
|
+
await waitForCommand(created.commandId, timeoutSeconds);
|
|
34574
34045
|
}
|
|
34575
|
-
|
|
34576
|
-
|
|
34577
|
-
|
|
34578
|
-
|
|
34579
|
-
|
|
34580
|
-
|
|
34581
|
-
|
|
34582
|
-
|
|
34583
|
-
throw new Error(
|
|
34584
|
-
"Failed to load ngrok tunnel module. This may be caused by a system library (GLIBC) incompatibility. See: https://github.com/vm0-ai/vm0/issues/6825",
|
|
34585
|
-
{ cause }
|
|
34586
|
-
);
|
|
34587
|
-
}
|
|
34588
|
-
}
|
|
34589
|
-
async function startDesktopTunnel(ngrokToken, endpointPrefix, port) {
|
|
34590
|
-
const ngrok = await loadNgrok();
|
|
34591
|
-
await ngrok.forward({
|
|
34592
|
-
addr: `localhost:${port}`,
|
|
34593
|
-
authtoken: ngrokToken,
|
|
34594
|
-
domain: `desktop.${endpointPrefix}.internal`
|
|
34046
|
+
async function runWriteCommand(kind, options, payload) {
|
|
34047
|
+
const timeoutSeconds = parseTimeoutSeconds(options.timeout);
|
|
34048
|
+
const created = await createComputerUseWriteCommand({
|
|
34049
|
+
kind,
|
|
34050
|
+
timeoutMs: timeoutSeconds * 1e3,
|
|
34051
|
+
...options.host ? { hostName: options.host } : {},
|
|
34052
|
+
...options.hostId ? { hostId: options.hostId } : {},
|
|
34053
|
+
...payload
|
|
34595
34054
|
});
|
|
34055
|
+
await waitForCommand(created.commandId, timeoutSeconds);
|
|
34596
34056
|
}
|
|
34597
|
-
|
|
34598
|
-
|
|
34599
|
-
await ngrok.kill();
|
|
34600
|
-
}
|
|
34601
|
-
|
|
34602
|
-
// src/commands/zero/computer-use/host.ts
|
|
34603
|
-
var hostStartCommand = new Command().name("start").description("Start the computer-use host daemon (macOS only)").action(
|
|
34604
|
-
withErrorHandler(async () => {
|
|
34605
|
-
if (process.platform !== "darwin") {
|
|
34606
|
-
throw new Error(
|
|
34607
|
-
"Computer-use host requires macOS\n\nThe host daemon uses macOS-specific commands (screencapture, system_profiler)."
|
|
34608
|
-
);
|
|
34609
|
-
}
|
|
34610
|
-
if (!await isCliclickInstalled()) {
|
|
34611
|
-
console.log(
|
|
34612
|
-
source_default.yellow(
|
|
34613
|
-
"\u26A0 cliclick not found. Mouse and keyboard operations will not be available.\n Install with: brew install cliclick"
|
|
34614
|
-
)
|
|
34615
|
-
);
|
|
34616
|
-
console.log();
|
|
34617
|
-
}
|
|
34618
|
-
console.log(source_default.cyan("Registering computer-use host..."));
|
|
34619
|
-
const credentials = await registerComputerUseHost();
|
|
34620
|
-
const port = await getRandomPort();
|
|
34621
|
-
const server = await startDesktopServer(credentials.token, port);
|
|
34622
|
-
try {
|
|
34623
|
-
await startDesktopTunnel(
|
|
34624
|
-
credentials.ngrokToken,
|
|
34625
|
-
credentials.endpointPrefix,
|
|
34626
|
-
port
|
|
34627
|
-
);
|
|
34628
|
-
console.log();
|
|
34629
|
-
console.log(source_default.green("\u2713 Computer-use host active"));
|
|
34630
|
-
console.log(` Desktop: desktop.${credentials.domain}`);
|
|
34631
|
-
console.log();
|
|
34632
|
-
console.log(source_default.dim("Press ^C twice to disconnect"));
|
|
34633
|
-
console.log();
|
|
34634
|
-
let sigintCount = 0;
|
|
34635
|
-
await new Promise((resolve2) => {
|
|
34636
|
-
const keepAlive = setInterval(() => {
|
|
34637
|
-
}, 6e4);
|
|
34638
|
-
const done = () => {
|
|
34639
|
-
clearInterval(keepAlive);
|
|
34640
|
-
process.removeListener("SIGINT", onSigint);
|
|
34641
|
-
resolve2();
|
|
34642
|
-
};
|
|
34643
|
-
const onSigint = () => {
|
|
34644
|
-
sigintCount++;
|
|
34645
|
-
if (sigintCount === 1) {
|
|
34646
|
-
console.log(
|
|
34647
|
-
source_default.dim("\nPress ^C again to disconnect and exit...")
|
|
34648
|
-
);
|
|
34649
|
-
} else {
|
|
34650
|
-
done();
|
|
34651
|
-
}
|
|
34652
|
-
};
|
|
34653
|
-
process.on("SIGINT", onSigint);
|
|
34654
|
-
process.once("SIGTERM", done);
|
|
34655
|
-
});
|
|
34656
|
-
} finally {
|
|
34657
|
-
console.log();
|
|
34658
|
-
console.log(source_default.cyan("Stopping computer-use host..."));
|
|
34659
|
-
server.close();
|
|
34660
|
-
await stopDesktopTunnel();
|
|
34661
|
-
await unregisterComputerUseHost().catch(() => {
|
|
34662
|
-
});
|
|
34663
|
-
console.log(source_default.green("\u2713 Host stopped"));
|
|
34664
|
-
}
|
|
34665
|
-
})
|
|
34666
|
-
);
|
|
34667
|
-
var hostStopCommand = new Command().name("stop").description("Stop and unregister the computer-use host").action(
|
|
34668
|
-
withErrorHandler(async () => {
|
|
34669
|
-
console.log(source_default.cyan("Unregistering computer-use host..."));
|
|
34670
|
-
try {
|
|
34671
|
-
await unregisterComputerUseHost();
|
|
34672
|
-
console.log(source_default.green("\u2713 Host unregistered"));
|
|
34673
|
-
} catch (error) {
|
|
34674
|
-
if (error instanceof ApiRequestError && error.status === 404) {
|
|
34675
|
-
console.log(source_default.yellow("No active host registration found"));
|
|
34676
|
-
return;
|
|
34677
|
-
}
|
|
34678
|
-
throw error;
|
|
34679
|
-
}
|
|
34680
|
-
})
|
|
34681
|
-
);
|
|
34682
|
-
|
|
34683
|
-
// src/commands/zero/computer-use/client.ts
|
|
34684
|
-
init_esm_shims();
|
|
34685
|
-
import { writeFile, mkdir } from "fs/promises";
|
|
34686
|
-
import { basename as basename7, join as join6 } from "path";
|
|
34687
|
-
|
|
34688
|
-
// src/lib/computer-use/client.ts
|
|
34689
|
-
init_esm_shims();
|
|
34690
|
-
var cachedHost = null;
|
|
34691
|
-
var CACHE_TTL_MS = 3e4;
|
|
34692
|
-
async function discoverHost() {
|
|
34693
|
-
if (cachedHost && Date.now() - cachedHost.cachedAt < CACHE_TTL_MS) {
|
|
34694
|
-
return { domain: cachedHost.domain, token: cachedHost.token };
|
|
34695
|
-
}
|
|
34696
|
-
const host = await getComputerUseHost();
|
|
34697
|
-
if (!host) {
|
|
34698
|
-
throw new Error(
|
|
34699
|
-
"No active computer-use host found\n\nStart a host with: zero computer-use host start"
|
|
34700
|
-
);
|
|
34701
|
-
}
|
|
34702
|
-
cachedHost = { ...host, cachedAt: Date.now() };
|
|
34703
|
-
return host;
|
|
34704
|
-
}
|
|
34705
|
-
async function callHost(path, options) {
|
|
34706
|
-
const { domain, token } = await discoverHost();
|
|
34707
|
-
const url = `https://desktop.${domain}${path}`;
|
|
34708
|
-
const headers = { "x-vm0-token": token };
|
|
34709
|
-
const init = { headers };
|
|
34710
|
-
if (options?.method) {
|
|
34711
|
-
init.method = options.method;
|
|
34712
|
-
}
|
|
34713
|
-
if (options?.body !== void 0) {
|
|
34714
|
-
headers["content-type"] = "application/json";
|
|
34715
|
-
init.body = JSON.stringify(options.body);
|
|
34716
|
-
}
|
|
34717
|
-
const response = await fetch(url, init);
|
|
34718
|
-
if (!response.ok) {
|
|
34719
|
-
const body = await response.text().catch(() => {
|
|
34720
|
-
return "";
|
|
34721
|
-
});
|
|
34722
|
-
throw new Error(
|
|
34723
|
-
`Host returned ${response.status}: ${body || response.statusText}`
|
|
34724
|
-
);
|
|
34725
|
-
}
|
|
34726
|
-
return response;
|
|
34057
|
+
function addTargetOptions(command) {
|
|
34058
|
+
return command.option("--host <name>", "Run on a named computer-use host").option("--host-id <id>", "Run on a specific computer-use host id").option("--timeout <seconds>", "Maximum time to wait", "30");
|
|
34727
34059
|
}
|
|
34728
|
-
|
|
34729
|
-
|
|
34730
|
-
function safeFormat(format) {
|
|
34731
|
-
return basename7(format);
|
|
34060
|
+
function appOption(command) {
|
|
34061
|
+
return command.requiredOption("--app <name>", "Target app name or bundle id");
|
|
34732
34062
|
}
|
|
34733
|
-
function
|
|
34734
|
-
return
|
|
34735
|
-
|
|
34736
|
-
|
|
34737
|
-
|
|
34738
|
-
|
|
34739
|
-
|
|
34740
|
-
|
|
34741
|
-
|
|
34742
|
-
|
|
34743
|
-
|
|
34744
|
-
|
|
34745
|
-
|
|
34746
|
-
|
|
34063
|
+
function formatAuditEvent(event) {
|
|
34064
|
+
return [
|
|
34065
|
+
`${event.createdAt} ${event.event} ${event.kind}`,
|
|
34066
|
+
`command=${event.commandId}`,
|
|
34067
|
+
event.hostId ? `host=${event.hostId}` : null,
|
|
34068
|
+
event.app ? `app=${event.app}` : null,
|
|
34069
|
+
event.approvalOutcome ? `approval=${event.approvalOutcome}` : null
|
|
34070
|
+
].filter((part) => {
|
|
34071
|
+
return part !== null;
|
|
34072
|
+
}).join(" ");
|
|
34073
|
+
}
|
|
34074
|
+
var listAppsCommand = addTargetOptions(
|
|
34075
|
+
new Command().name("list-apps").description("List apps available to the Desktop Computer Use host").action(
|
|
34076
|
+
withErrorHandler(async (options) => {
|
|
34077
|
+
await runReadCommand("apps.list", options);
|
|
34747
34078
|
})
|
|
34748
|
-
);
|
|
34749
|
-
}
|
|
34750
|
-
var clientScreenshotCommand = new Command().name("screenshot").description("Capture a screenshot from the remote host").action(
|
|
34751
|
-
withErrorHandler(async () => {
|
|
34752
|
-
const response = await callHost("/screenshot");
|
|
34753
|
-
const data = await response.json();
|
|
34754
|
-
const dir = "/tmp/computer-use";
|
|
34755
|
-
await mkdir(dir, { recursive: true });
|
|
34756
|
-
const timestamp = Date.now();
|
|
34757
|
-
const filePath = join6(
|
|
34758
|
-
dir,
|
|
34759
|
-
`screenshot-${timestamp}.${safeFormat(data.format)}`
|
|
34760
|
-
);
|
|
34761
|
-
const buffer = Buffer.from(data.image, "base64");
|
|
34762
|
-
await writeFile(filePath, buffer);
|
|
34763
|
-
process.stdout.write(`${filePath}
|
|
34764
|
-
`);
|
|
34765
|
-
process.stderr.write(
|
|
34766
|
-
JSON.stringify({
|
|
34767
|
-
width: data.width,
|
|
34768
|
-
height: data.height,
|
|
34769
|
-
scaleFactor: data.scaleFactor
|
|
34770
|
-
}) + "\n"
|
|
34771
|
-
);
|
|
34772
|
-
})
|
|
34773
|
-
);
|
|
34774
|
-
var clientZoomCommand = new Command().name("zoom").description("Capture a region screenshot from the remote host").requiredOption("--x <number>", "X coordinate of the region").requiredOption("--y <number>", "Y coordinate of the region").requiredOption("--width <number>", "Width of the region").requiredOption("--height <number>", "Height of the region").action(
|
|
34775
|
-
withErrorHandler(
|
|
34776
|
-
async (opts) => {
|
|
34777
|
-
const params = new URLSearchParams({
|
|
34778
|
-
x: opts.x,
|
|
34779
|
-
y: opts.y,
|
|
34780
|
-
width: opts.width,
|
|
34781
|
-
height: opts.height
|
|
34782
|
-
});
|
|
34783
|
-
const response = await callHost(`/zoom?${params.toString()}`);
|
|
34784
|
-
const data = await response.json();
|
|
34785
|
-
const dir = "/tmp/computer-use";
|
|
34786
|
-
await mkdir(dir, { recursive: true });
|
|
34787
|
-
const timestamp = Date.now();
|
|
34788
|
-
const filePath = join6(
|
|
34789
|
-
dir,
|
|
34790
|
-
`zoom-${timestamp}.${safeFormat(data.format)}`
|
|
34791
|
-
);
|
|
34792
|
-
const buffer = Buffer.from(data.image, "base64");
|
|
34793
|
-
await writeFile(filePath, buffer);
|
|
34794
|
-
process.stdout.write(`${filePath}
|
|
34795
|
-
`);
|
|
34796
|
-
process.stderr.write(
|
|
34797
|
-
JSON.stringify({
|
|
34798
|
-
width: data.width,
|
|
34799
|
-
height: data.height,
|
|
34800
|
-
scaleFactor: data.scaleFactor
|
|
34801
|
-
}) + "\n"
|
|
34802
|
-
);
|
|
34803
|
-
}
|
|
34804
34079
|
)
|
|
34805
34080
|
);
|
|
34806
|
-
var
|
|
34807
|
-
|
|
34808
|
-
|
|
34809
|
-
|
|
34810
|
-
|
|
34811
|
-
|
|
34812
|
-
)
|
|
34813
|
-
|
|
34814
|
-
"left-click",
|
|
34815
|
-
"left_click",
|
|
34816
|
-
"Perform a left click at coordinates"
|
|
34817
|
-
);
|
|
34818
|
-
var clientRightClickCommand = mouseClickCommand(
|
|
34819
|
-
"right-click",
|
|
34820
|
-
"right_click",
|
|
34821
|
-
"Perform a right click at coordinates"
|
|
34081
|
+
var getAppStateCommand = appOption(
|
|
34082
|
+
addTargetOptions(
|
|
34083
|
+
new Command().name("get-app-state").description("Get screenshot and accessibility state for an app").action(
|
|
34084
|
+
withErrorHandler(async (options) => {
|
|
34085
|
+
await runReadCommand("app.state", options, { app: options.app });
|
|
34086
|
+
})
|
|
34087
|
+
)
|
|
34088
|
+
)
|
|
34822
34089
|
);
|
|
34823
|
-
var
|
|
34824
|
-
|
|
34825
|
-
|
|
34826
|
-
|
|
34090
|
+
var clickCommand = appOption(
|
|
34091
|
+
addTargetOptions(
|
|
34092
|
+
new Command().name("click").description("Click an accessibility element or screenshot coordinate").option("--snapshot-id <id>", "Snapshot id returned by get-app-state").option("--element <id>", "Element id from get-app-state").option("--x <points>", "Screenshot x coordinate fallback").option("--y <points>", "Screenshot y coordinate fallback").option("--button <button>", "Mouse button", "left").option("--click-count <count>", "Number of clicks", "1").action(
|
|
34093
|
+
withErrorHandler(async (options) => {
|
|
34094
|
+
const x = parseOptionalNonNegativeInteger(options.x, "x");
|
|
34095
|
+
const y = parseOptionalNonNegativeInteger(options.y, "y");
|
|
34096
|
+
await runWriteCommand("element.click", options, {
|
|
34097
|
+
app: options.app,
|
|
34098
|
+
...options.snapshotId ? { snapshotId: options.snapshotId } : {},
|
|
34099
|
+
...options.element ? { elementId: options.element } : {},
|
|
34100
|
+
...x !== void 0 ? { x } : {},
|
|
34101
|
+
...y !== void 0 ? { y } : {},
|
|
34102
|
+
button: parseMouseButton(options.button),
|
|
34103
|
+
clickCount: parsePositiveInteger2(options.clickCount, "click-count")
|
|
34104
|
+
});
|
|
34105
|
+
})
|
|
34106
|
+
)
|
|
34107
|
+
)
|
|
34827
34108
|
);
|
|
34828
|
-
var
|
|
34829
|
-
|
|
34830
|
-
|
|
34831
|
-
|
|
34109
|
+
var scrollCommand = appOption(
|
|
34110
|
+
addTargetOptions(
|
|
34111
|
+
new Command().name("scroll").description("Scroll an accessibility element").option("--snapshot-id <id>", "Snapshot id returned by get-app-state").requiredOption("--element <id>", "Element id from get-app-state").requiredOption(
|
|
34112
|
+
"--direction <direction>",
|
|
34113
|
+
"Scroll direction: up, down, left, or right"
|
|
34114
|
+
).option("--pages <count>", "Number of pages to scroll", "1").action(
|
|
34115
|
+
withErrorHandler(async (options) => {
|
|
34116
|
+
await runWriteCommand("element.scroll", options, {
|
|
34117
|
+
app: options.app,
|
|
34118
|
+
...options.snapshotId ? { snapshotId: options.snapshotId } : {},
|
|
34119
|
+
elementId: options.element,
|
|
34120
|
+
direction: options.direction,
|
|
34121
|
+
pages: parsePositiveNumber(options.pages, "pages")
|
|
34122
|
+
});
|
|
34123
|
+
})
|
|
34124
|
+
)
|
|
34125
|
+
)
|
|
34832
34126
|
);
|
|
34833
|
-
var
|
|
34834
|
-
|
|
34835
|
-
|
|
34836
|
-
|
|
34127
|
+
var setValueCommand = appOption(
|
|
34128
|
+
addTargetOptions(
|
|
34129
|
+
new Command().name("set-value").description("Set the value of a settable accessibility element").option("--snapshot-id <id>", "Snapshot id returned by get-app-state").requiredOption("--element <id>", "Element id from get-app-state").requiredOption("--value <text>", "Value to assign").action(
|
|
34130
|
+
withErrorHandler(async (options) => {
|
|
34131
|
+
await runWriteCommand("element.set_value", options, {
|
|
34132
|
+
app: options.app,
|
|
34133
|
+
...options.snapshotId ? { snapshotId: options.snapshotId } : {},
|
|
34134
|
+
elementId: options.element,
|
|
34135
|
+
value: options.value
|
|
34136
|
+
});
|
|
34137
|
+
})
|
|
34138
|
+
)
|
|
34139
|
+
)
|
|
34837
34140
|
);
|
|
34838
|
-
var
|
|
34839
|
-
|
|
34840
|
-
|
|
34841
|
-
|
|
34842
|
-
|
|
34843
|
-
|
|
34844
|
-
|
|
34845
|
-
|
|
34846
|
-
|
|
34847
|
-
|
|
34848
|
-
endY: Number(endY)
|
|
34849
|
-
}
|
|
34850
|
-
});
|
|
34851
|
-
process.stdout.write("ok\n");
|
|
34852
|
-
}
|
|
34141
|
+
var typeTextCommand = appOption(
|
|
34142
|
+
addTargetOptions(
|
|
34143
|
+
new Command().name("type-text").description("Type literal text into the target app").requiredOption("--text <text>", "Text to type").action(
|
|
34144
|
+
withErrorHandler(async (options) => {
|
|
34145
|
+
await runWriteCommand("keyboard.type_text", options, {
|
|
34146
|
+
app: options.app,
|
|
34147
|
+
text: options.text
|
|
34148
|
+
});
|
|
34149
|
+
})
|
|
34150
|
+
)
|
|
34853
34151
|
)
|
|
34854
34152
|
);
|
|
34855
|
-
var
|
|
34856
|
-
|
|
34857
|
-
|
|
34858
|
-
|
|
34859
|
-
|
|
34860
|
-
|
|
34861
|
-
|
|
34862
|
-
|
|
34153
|
+
var pressKeyCommand = appOption(
|
|
34154
|
+
addTargetOptions(
|
|
34155
|
+
new Command().name("press-key").description("Press a key or key combination in the target app").requiredOption("--key <key>", "Key or key combination to press").action(
|
|
34156
|
+
withErrorHandler(async (options) => {
|
|
34157
|
+
await runWriteCommand("keyboard.press_key", options, {
|
|
34158
|
+
app: options.app,
|
|
34159
|
+
key: options.key
|
|
34160
|
+
});
|
|
34161
|
+
})
|
|
34162
|
+
)
|
|
34163
|
+
)
|
|
34863
34164
|
);
|
|
34864
|
-
var
|
|
34865
|
-
|
|
34866
|
-
|
|
34867
|
-
|
|
34868
|
-
|
|
34869
|
-
|
|
34870
|
-
|
|
34871
|
-
|
|
34165
|
+
var performActionCommand = appOption(
|
|
34166
|
+
addTargetOptions(
|
|
34167
|
+
new Command().name("perform-action").description("Invoke a secondary accessibility action").option("--snapshot-id <id>", "Snapshot id returned by get-app-state").requiredOption("--element <id>", "Element id from get-app-state").requiredOption("--action <name>", "Accessibility action name").action(
|
|
34168
|
+
withErrorHandler(async (options) => {
|
|
34169
|
+
await runWriteCommand("element.perform_action", options, {
|
|
34170
|
+
app: options.app,
|
|
34171
|
+
...options.snapshotId ? { snapshotId: options.snapshotId } : {},
|
|
34172
|
+
elementId: options.element,
|
|
34173
|
+
action: options.action
|
|
34174
|
+
});
|
|
34175
|
+
})
|
|
34176
|
+
)
|
|
34177
|
+
)
|
|
34872
34178
|
);
|
|
34873
|
-
var
|
|
34874
|
-
|
|
34875
|
-
|
|
34876
|
-
|
|
34877
|
-
|
|
34878
|
-
|
|
34879
|
-
|
|
34880
|
-
x: Number(x),
|
|
34881
|
-
y: Number(y),
|
|
34882
|
-
direction,
|
|
34883
|
-
...amount !== void 0 && { amount: Number(amount) }
|
|
34884
|
-
}
|
|
34885
|
-
});
|
|
34886
|
-
process.stdout.write("ok\n");
|
|
34887
|
-
}
|
|
34179
|
+
var openAppCommand = appOption(
|
|
34180
|
+
addTargetOptions(
|
|
34181
|
+
new Command().name("open-app").description("Open or activate an app on the Desktop host").action(
|
|
34182
|
+
withErrorHandler(async (options) => {
|
|
34183
|
+
await runWriteCommand("app.open", options, { app: options.app });
|
|
34184
|
+
})
|
|
34185
|
+
)
|
|
34888
34186
|
)
|
|
34889
34187
|
);
|
|
34890
|
-
var
|
|
34188
|
+
var hostsCommand = new Command().name("hosts").description("List linked Desktop Computer Use hosts").action(
|
|
34891
34189
|
withErrorHandler(async () => {
|
|
34892
|
-
const
|
|
34893
|
-
|
|
34894
|
-
process.stdout.write(data.text);
|
|
34190
|
+
const { hosts } = await listComputerUseHosts();
|
|
34191
|
+
console.log(JSON.stringify({ hosts }, null, 2));
|
|
34895
34192
|
})
|
|
34896
34193
|
);
|
|
34897
|
-
var
|
|
34898
|
-
withErrorHandler(async (
|
|
34899
|
-
await
|
|
34900
|
-
|
|
34901
|
-
body: { text }
|
|
34902
|
-
});
|
|
34903
|
-
process.stdout.write("ok\n");
|
|
34904
|
-
})
|
|
34905
|
-
);
|
|
34906
|
-
var clientKeyCommand = new Command().name("key").description("Press a key or key combination (e.g., cmd+c, return)").argument("<combo>", "Key combo string (e.g., cmd+c, ctrl+shift+s, return)").action(
|
|
34907
|
-
withErrorHandler(async (combo) => {
|
|
34908
|
-
await callHost("/keyboard", {
|
|
34909
|
-
method: "POST",
|
|
34910
|
-
body: { action: "key", keys: combo }
|
|
34911
|
-
});
|
|
34912
|
-
process.stdout.write("ok\n");
|
|
34194
|
+
var revokeHostCommand = new Command().name("revoke-host").description("Revoke a linked Desktop Computer Use host").argument("<host-id>").action(
|
|
34195
|
+
withErrorHandler(async (hostId) => {
|
|
34196
|
+
await deleteComputerUseHost(hostId);
|
|
34197
|
+
console.log(`Revoked computer-use host ${hostId}`);
|
|
34913
34198
|
})
|
|
34914
34199
|
);
|
|
34915
|
-
var
|
|
34916
|
-
withErrorHandler(async (
|
|
34917
|
-
const
|
|
34918
|
-
|
|
34919
|
-
|
|
34920
|
-
|
|
34921
|
-
|
|
34922
|
-
method: "POST",
|
|
34923
|
-
body: { action: "hold_key", keys: combo, durationMs }
|
|
34924
|
-
});
|
|
34925
|
-
process.stdout.write("ok\n");
|
|
34926
|
-
})
|
|
34927
|
-
);
|
|
34928
|
-
var clientTypeCommand = new Command().name("type").description("Type text at the current cursor position").argument("<text>", "Text to type").action(
|
|
34929
|
-
withErrorHandler(async (text) => {
|
|
34930
|
-
await callHost("/keyboard", {
|
|
34931
|
-
method: "POST",
|
|
34932
|
-
body: { action: "type", text }
|
|
34933
|
-
});
|
|
34934
|
-
process.stdout.write("ok\n");
|
|
34935
|
-
})
|
|
34936
|
-
);
|
|
34937
|
-
var clientOpenAppCommand = new Command().name("open-app").description("Open or activate a macOS application by name or bundle ID").argument(
|
|
34938
|
-
"<nameOrBundleId>",
|
|
34939
|
-
"App name (e.g., Safari) or bundle ID (e.g., com.apple.Safari)"
|
|
34940
|
-
).action(
|
|
34941
|
-
withErrorHandler(async (nameOrBundleId) => {
|
|
34942
|
-
await callHost("/open-application", {
|
|
34943
|
-
method: "POST",
|
|
34944
|
-
body: { nameOrBundleId }
|
|
34200
|
+
var auditCommand = new Command().name("audit").description("List Desktop Computer Use write-command audit events").option("--limit <count>", "Maximum events to return", "50").option("--command-id <id>", "Filter by command id").option("--host-id <id>", "Filter by host id").option("--run-id <id>", "Filter by run id").action(
|
|
34201
|
+
withErrorHandler(async (options) => {
|
|
34202
|
+
const { auditEvents } = await listComputerUseAuditEvents({
|
|
34203
|
+
limit: parseLimit4(options.limit),
|
|
34204
|
+
...options.commandId ? { commandId: options.commandId } : {},
|
|
34205
|
+
...options.hostId ? { hostId: options.hostId } : {},
|
|
34206
|
+
...options.runId ? { runId: options.runId } : {}
|
|
34945
34207
|
});
|
|
34946
|
-
|
|
34947
|
-
|
|
34948
|
-
|
|
34949
|
-
var clientMouseMoveCommand = mouseClickCommand(
|
|
34950
|
-
"mouse-move",
|
|
34951
|
-
"move",
|
|
34952
|
-
"Move mouse pointer to coordinates"
|
|
34953
|
-
);
|
|
34954
|
-
var clientCursorPositionCommand = new Command().name("cursor-position").description("Get current cursor position from the remote host").action(
|
|
34955
|
-
withErrorHandler(async () => {
|
|
34956
|
-
const response = await callHost("/cursor-position");
|
|
34957
|
-
const data = await response.json();
|
|
34958
|
-
process.stdout.write(JSON.stringify(data) + "\n");
|
|
34208
|
+
for (const event of auditEvents) {
|
|
34209
|
+
console.log(formatAuditEvent(event));
|
|
34210
|
+
}
|
|
34959
34211
|
})
|
|
34960
34212
|
);
|
|
34961
|
-
|
|
34962
|
-
// src/commands/zero/computer-use/index.ts
|
|
34963
|
-
var hostCommand = new Command().name("host").description("Manage computer-use host daemon").addCommand(hostStartCommand).addCommand(hostStopCommand);
|
|
34964
|
-
var clientCommand = new Command().name("client").description("Interact with remote computer-use host").addCommand(clientScreenshotCommand).addCommand(clientZoomCommand).addCommand(clientInfoCommand).addCommand(clientLeftClickCommand).addCommand(clientRightClickCommand).addCommand(clientMiddleClickCommand).addCommand(clientDoubleClickCommand).addCommand(clientTripleClickCommand).addCommand(clientLeftClickDragCommand).addCommand(clientLeftMouseDownCommand).addCommand(clientLeftMouseUpCommand).addCommand(clientScrollCommand).addCommand(clientReadClipboardCommand).addCommand(clientWriteClipboardCommand).addCommand(clientKeyCommand).addCommand(clientHoldKeyCommand).addCommand(clientTypeCommand).addCommand(clientOpenAppCommand).addCommand(clientMouseMoveCommand).addCommand(clientCursorPositionCommand);
|
|
34965
|
-
clientCommand.addHelpText(
|
|
34966
|
-
"after",
|
|
34967
|
-
`
|
|
34968
|
-
Coordinate System:
|
|
34969
|
-
All coordinates use macOS logical points, not physical pixels.
|
|
34970
|
-
On Retina displays, logical size = physical size / scaleFactor.
|
|
34971
|
-
Run "info" to check your screen's logical dimensions.
|
|
34972
|
-
|
|
34973
|
-
Examples:
|
|
34974
|
-
zero computer-use client screenshot
|
|
34975
|
-
zero computer-use client zoom --x 0 --y 0 --width 500 --height 500
|
|
34976
|
-
zero computer-use client info
|
|
34977
|
-
zero computer-use client left-click 500 300
|
|
34978
|
-
zero computer-use client scroll 500 300 down 5
|
|
34979
|
-
zero computer-use client key "cmd+c"`
|
|
34980
|
-
);
|
|
34981
|
-
var zeroComputerUseCommand = new Command().name("computer-use").description("Remote desktop control for cloud agents").addCommand(hostCommand).addCommand(clientCommand);
|
|
34213
|
+
var zeroComputerUseCommand = new Command().name("computer-use").description("Desktop app computer use through Zero CLI").addCommand(listAppsCommand).addCommand(getAppStateCommand).addCommand(clickCommand).addCommand(scrollCommand).addCommand(setValueCommand).addCommand(typeTextCommand).addCommand(pressKeyCommand).addCommand(performActionCommand).addCommand(openAppCommand).addCommand(hostsCommand).addCommand(revokeHostCommand).addCommand(auditCommand);
|
|
34982
34214
|
|
|
34983
34215
|
// src/commands/zero/built-in/index.ts
|
|
34984
34216
|
init_esm_shims();
|
|
@@ -35418,17 +34650,17 @@ var videoCommand = createVideoGenerateCommand({
|
|
|
35418
34650
|
init_esm_shims();
|
|
35419
34651
|
import { mkdtemp, rm } from "fs/promises";
|
|
35420
34652
|
import { readFileSync as readFileSync15 } from "fs";
|
|
35421
|
-
import { join as
|
|
35422
|
-
import { tmpdir as
|
|
34653
|
+
import { join as join6 } from "path";
|
|
34654
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
35423
34655
|
|
|
35424
34656
|
// src/lib/host/publish-static-site.ts
|
|
35425
34657
|
init_esm_shims();
|
|
35426
|
-
import { readFile as
|
|
34658
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
35427
34659
|
|
|
35428
34660
|
// src/lib/host/static-site.ts
|
|
35429
34661
|
init_esm_shims();
|
|
35430
34662
|
import { createHash } from "crypto";
|
|
35431
|
-
import { readdir, readFile
|
|
34663
|
+
import { readdir, readFile, stat } from "fs/promises";
|
|
35432
34664
|
import { extname as extname3, relative, resolve, sep, dirname, posix } from "path";
|
|
35433
34665
|
var MIME_BY_EXTENSION3 = {
|
|
35434
34666
|
".html": "text/html; charset=utf-8",
|
|
@@ -35552,7 +34784,7 @@ function collectReferences(ext, text) {
|
|
|
35552
34784
|
return isHtmlExtension(ext) ? collectHtmlReferences(text) : collectCssReferences(text);
|
|
35553
34785
|
}
|
|
35554
34786
|
async function hashFile(path) {
|
|
35555
|
-
const bytes = await
|
|
34787
|
+
const bytes = await readFile(path);
|
|
35556
34788
|
return createHash("sha256").update(bytes).digest("hex");
|
|
35557
34789
|
}
|
|
35558
34790
|
async function walk(root, dir, files) {
|
|
@@ -35592,7 +34824,7 @@ async function assertReferencesExist(files) {
|
|
|
35592
34824
|
if (!shouldValidateReferences(ext)) {
|
|
35593
34825
|
continue;
|
|
35594
34826
|
}
|
|
35595
|
-
const text = await
|
|
34827
|
+
const text = await readFile(file.absolutePath, "utf8");
|
|
35596
34828
|
const references = collectReferences(ext, text);
|
|
35597
34829
|
for (const reference of references) {
|
|
35598
34830
|
const normalized = normalizeReference(file.path, reference);
|
|
@@ -35666,7 +34898,7 @@ async function publishStaticSite(options) {
|
|
|
35666
34898
|
throw new Error(`Missing upload URL for ${file.path}`);
|
|
35667
34899
|
}
|
|
35668
34900
|
options.onProgress?.({ phase: "uploading", path: file.path });
|
|
35669
|
-
const bytes = await
|
|
34901
|
+
const bytes = await readFile2(file.absolutePath);
|
|
35670
34902
|
const response = await fetch(uploadUrl, {
|
|
35671
34903
|
method: "PUT",
|
|
35672
34904
|
headers: { "Content-Type": file.contentType },
|
|
@@ -35693,8 +34925,8 @@ async function publishStaticSite(options) {
|
|
|
35693
34925
|
init_esm_shims();
|
|
35694
34926
|
var import_react = __toESM(require_react(), 1);
|
|
35695
34927
|
var import_server = __toESM(require_server_node(), 1);
|
|
35696
|
-
import { mkdir
|
|
35697
|
-
import { join as
|
|
34928
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
34929
|
+
import { join as join5 } from "path";
|
|
35698
34930
|
function h(type, props, ...children) {
|
|
35699
34931
|
return import_react.default.createElement(type, props, ...children);
|
|
35700
34932
|
}
|
|
@@ -36344,19 +35576,19 @@ h1 {
|
|
|
36344
35576
|
}
|
|
36345
35577
|
`;
|
|
36346
35578
|
async function buildGeneratedWebsite(options) {
|
|
36347
|
-
const assetsDir =
|
|
36348
|
-
await
|
|
35579
|
+
const assetsDir = join5(options.outDir, "assets");
|
|
35580
|
+
await mkdir(assetsDir, { recursive: true });
|
|
36349
35581
|
const markup = (0, import_server.renderToStaticMarkup)(
|
|
36350
35582
|
import_react.default.createElement(WebsiteDocument, {
|
|
36351
35583
|
data: options.siteData,
|
|
36352
35584
|
templateId: options.templateId
|
|
36353
35585
|
})
|
|
36354
35586
|
);
|
|
36355
|
-
await
|
|
36356
|
-
|
|
35587
|
+
await writeFile(
|
|
35588
|
+
join5(options.outDir, "index.html"),
|
|
36357
35589
|
`<!doctype html>${markup}`
|
|
36358
35590
|
);
|
|
36359
|
-
await
|
|
35591
|
+
await writeFile(join5(assetsDir, "styles.css"), WEBSITE_CSS.trimStart());
|
|
36360
35592
|
}
|
|
36361
35593
|
|
|
36362
35594
|
// src/commands/zero/built-in/generate/website.ts
|
|
@@ -36423,8 +35655,8 @@ Notes:
|
|
|
36423
35655
|
title: options.title,
|
|
36424
35656
|
audience: options.audience
|
|
36425
35657
|
});
|
|
36426
|
-
const buildRoot = await mkdtemp(
|
|
36427
|
-
const outDir =
|
|
35658
|
+
const buildRoot = await mkdtemp(join6(tmpdir4(), "zero-website-"));
|
|
35659
|
+
const outDir = join6(buildRoot, "dist");
|
|
36428
35660
|
try {
|
|
36429
35661
|
if (!options.json) {
|
|
36430
35662
|
console.log(source_default.dim("Building React template..."));
|
|
@@ -36597,10 +35829,10 @@ init_esm_shims();
|
|
|
36597
35829
|
|
|
36598
35830
|
// src/commands/zero/web/download-file.ts
|
|
36599
35831
|
init_esm_shims();
|
|
36600
|
-
import { basename as
|
|
36601
|
-
import { tmpdir as
|
|
35832
|
+
import { basename as basename7, join as join7 } from "path";
|
|
35833
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
36602
35834
|
function defaultOutPath4(fileId) {
|
|
36603
|
-
return
|
|
35835
|
+
return join7(tmpdir5(), `web-${basename7(fileId)}`);
|
|
36604
35836
|
}
|
|
36605
35837
|
var downloadFileCommand4 = new Command().name("download-file").description("Download a web-uploaded file by id").argument("<file-id>", "File id (UUID returned by the upload API)").option(
|
|
36606
35838
|
"-o, --out <path>",
|
|
@@ -36685,7 +35917,7 @@ function sleep3(ms) {
|
|
|
36685
35917
|
setTimeout(resolve2, ms);
|
|
36686
35918
|
});
|
|
36687
35919
|
}
|
|
36688
|
-
function
|
|
35920
|
+
function parseTimeoutSeconds2(value) {
|
|
36689
35921
|
if (!value) return 30;
|
|
36690
35922
|
const seconds = Number.parseInt(value, 10);
|
|
36691
35923
|
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
@@ -36693,7 +35925,7 @@ function parseTimeoutSeconds(value) {
|
|
|
36693
35925
|
}
|
|
36694
35926
|
return seconds;
|
|
36695
35927
|
}
|
|
36696
|
-
function
|
|
35928
|
+
function parseOptionalNonNegativeInteger2(value, label) {
|
|
36697
35929
|
if (value === void 0) return void 0;
|
|
36698
35930
|
const parsed = Number.parseInt(value, 10);
|
|
36699
35931
|
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
@@ -36701,7 +35933,7 @@ function parseOptionalNonNegativeInteger(value, label) {
|
|
|
36701
35933
|
}
|
|
36702
35934
|
return parsed;
|
|
36703
35935
|
}
|
|
36704
|
-
function
|
|
35936
|
+
function parsePositiveInteger3(value, label) {
|
|
36705
35937
|
if (value === void 0) {
|
|
36706
35938
|
throw new Error(`${label} is required`);
|
|
36707
35939
|
}
|
|
@@ -36711,24 +35943,24 @@ function parsePositiveInteger2(value, label) {
|
|
|
36711
35943
|
}
|
|
36712
35944
|
return parsed;
|
|
36713
35945
|
}
|
|
36714
|
-
function
|
|
35946
|
+
function parseLimit5(value) {
|
|
36715
35947
|
if (value === void 0) {
|
|
36716
35948
|
return 50;
|
|
36717
35949
|
}
|
|
36718
|
-
const parsed =
|
|
35950
|
+
const parsed = parsePositiveInteger3(value, "limit");
|
|
36719
35951
|
if (parsed > 200) {
|
|
36720
35952
|
throw new Error("limit must be 200 or less");
|
|
36721
35953
|
}
|
|
36722
35954
|
return parsed;
|
|
36723
35955
|
}
|
|
36724
|
-
function
|
|
35956
|
+
function resultText2(command) {
|
|
36725
35957
|
if (!command.result) {
|
|
36726
35958
|
return "";
|
|
36727
35959
|
}
|
|
36728
35960
|
return JSON.stringify(command.result, null, 2);
|
|
36729
35961
|
}
|
|
36730
|
-
async function
|
|
36731
|
-
const timeoutSeconds =
|
|
35962
|
+
async function runReadCommand2(kind, options) {
|
|
35963
|
+
const timeoutSeconds = parseTimeoutSeconds2(options.timeout);
|
|
36732
35964
|
const created = await createLocalBrowserReadCommand({
|
|
36733
35965
|
kind,
|
|
36734
35966
|
timeoutMs: timeoutSeconds * 1e3,
|
|
@@ -36754,7 +35986,7 @@ async function runReadCommand(kind, options) {
|
|
|
36754
35986
|
command.error ? `${command.error.code}: ${command.error.message}` : "Local-browser command failed"
|
|
36755
35987
|
);
|
|
36756
35988
|
}
|
|
36757
|
-
const text =
|
|
35989
|
+
const text = resultText2(command);
|
|
36758
35990
|
if (text) {
|
|
36759
35991
|
console.log(text);
|
|
36760
35992
|
}
|
|
@@ -36762,7 +35994,7 @@ async function runReadCommand(kind, options) {
|
|
|
36762
35994
|
}
|
|
36763
35995
|
throw new Error(`Local-browser command timed out: ${created.commandId}`);
|
|
36764
35996
|
}
|
|
36765
|
-
async function
|
|
35997
|
+
async function waitForCommand2(commandId, timeoutSeconds) {
|
|
36766
35998
|
const deadline = Date.now() + timeoutSeconds * 1e3;
|
|
36767
35999
|
while (Date.now() <= deadline) {
|
|
36768
36000
|
const command = await getLocalBrowserReadCommand(commandId);
|
|
@@ -36781,7 +36013,7 @@ async function waitForCommand(commandId, timeoutSeconds) {
|
|
|
36781
36013
|
command.error ? `${command.error.code}: ${command.error.message}` : "Local-browser command failed"
|
|
36782
36014
|
);
|
|
36783
36015
|
}
|
|
36784
|
-
const text =
|
|
36016
|
+
const text = resultText2(command);
|
|
36785
36017
|
if (text) {
|
|
36786
36018
|
console.log(text);
|
|
36787
36019
|
}
|
|
@@ -36789,8 +36021,8 @@ async function waitForCommand(commandId, timeoutSeconds) {
|
|
|
36789
36021
|
}
|
|
36790
36022
|
throw new Error(`Local-browser command timed out: ${commandId}`);
|
|
36791
36023
|
}
|
|
36792
|
-
async function
|
|
36793
|
-
const timeoutSeconds =
|
|
36024
|
+
async function runWriteCommand2(kind, options, payload) {
|
|
36025
|
+
const timeoutSeconds = parseTimeoutSeconds2(options.timeout);
|
|
36794
36026
|
const created = await createLocalBrowserWriteCommand({
|
|
36795
36027
|
kind,
|
|
36796
36028
|
timeoutMs: timeoutSeconds * 1e3,
|
|
@@ -36799,7 +36031,7 @@ async function runWriteCommand(kind, options, payload) {
|
|
|
36799
36031
|
...options.hostId ? { hostId: options.hostId } : {},
|
|
36800
36032
|
...payload
|
|
36801
36033
|
});
|
|
36802
|
-
await
|
|
36034
|
+
await waitForCommand2(created.commandId, timeoutSeconds);
|
|
36803
36035
|
}
|
|
36804
36036
|
function addReadOptions(command) {
|
|
36805
36037
|
return command.option("--host <name>", "Run on a named local-browser host").option("--host-id <id>", "Run on a specific local-browser host id").option("--tab-id <id>", "Target a specific browser tab").option("--timeout <seconds>", "Maximum time to wait", "30");
|
|
@@ -36812,7 +36044,7 @@ function readCommand(name, kind) {
|
|
|
36812
36044
|
return addReadOptions(
|
|
36813
36045
|
new Command().name(name).description(`Run ${kind}`).action(
|
|
36814
36046
|
withErrorHandler(async (options) => {
|
|
36815
|
-
await
|
|
36047
|
+
await runReadCommand2(kind, options);
|
|
36816
36048
|
})
|
|
36817
36049
|
)
|
|
36818
36050
|
);
|
|
@@ -36831,7 +36063,7 @@ function formatHost(host) {
|
|
|
36831
36063
|
` capabilities: ${formatCapabilities(host.supportedCapabilities)}`
|
|
36832
36064
|
].join("\n");
|
|
36833
36065
|
}
|
|
36834
|
-
function
|
|
36066
|
+
function formatAuditEvent2(event) {
|
|
36835
36067
|
const parts = [
|
|
36836
36068
|
event.createdAt,
|
|
36837
36069
|
event.event,
|
|
@@ -36858,7 +36090,7 @@ function formatAuditEvent(event) {
|
|
|
36858
36090
|
}
|
|
36859
36091
|
return parts.join(" ");
|
|
36860
36092
|
}
|
|
36861
|
-
var
|
|
36093
|
+
var hostsCommand2 = new Command().name("hosts").description("List and revoke linked local-browser hosts").addCommand(
|
|
36862
36094
|
new Command().name("list").description("List linked local-browser hosts").option("--json", "Output hosts as JSON").action(
|
|
36863
36095
|
withErrorHandler(async (options) => {
|
|
36864
36096
|
const result = await listLocalBrowserHosts();
|
|
@@ -36886,11 +36118,11 @@ var hostsCommand = new Command().name("hosts").description("List and revoke link
|
|
|
36886
36118
|
})
|
|
36887
36119
|
)
|
|
36888
36120
|
);
|
|
36889
|
-
var
|
|
36121
|
+
var auditCommand2 = new Command().name("audit").description("Inspect local-browser write command audit events").addCommand(
|
|
36890
36122
|
new Command().name("list").description("List local-browser write command audit events").option("--limit <count>", "Maximum events to show", "50").option("--command-id <id>", "Filter by command id").option("--host-id <id>", "Filter by host id").option("--run-id <id>", "Filter by run id").option("--json", "Output audit events as JSON").action(
|
|
36891
36123
|
withErrorHandler(async (options) => {
|
|
36892
36124
|
const result = await listLocalBrowserAuditEvents({
|
|
36893
|
-
limit:
|
|
36125
|
+
limit: parseLimit5(options.limit),
|
|
36894
36126
|
...options.commandId ? { commandId: options.commandId } : {},
|
|
36895
36127
|
...options.hostId ? { hostId: options.hostId } : {},
|
|
36896
36128
|
...options.runId ? { runId: options.runId } : {}
|
|
@@ -36903,7 +36135,7 @@ var auditCommand = new Command().name("audit").description("Inspect local-browse
|
|
|
36903
36135
|
console.log(source_default.dim("No local-browser audit events found."));
|
|
36904
36136
|
return;
|
|
36905
36137
|
}
|
|
36906
|
-
console.log(result.auditEvents.map(
|
|
36138
|
+
console.log(result.auditEvents.map(formatAuditEvent2).join("\n"));
|
|
36907
36139
|
})
|
|
36908
36140
|
)
|
|
36909
36141
|
);
|
|
@@ -36911,7 +36143,7 @@ var tabsCommand = new Command().name("tabs").description("Read and control brows
|
|
|
36911
36143
|
addWriteOptions(
|
|
36912
36144
|
new Command().name("activate").description("Run tabs.activate").requiredOption("--tab-id <id>", "Tab to activate").action(
|
|
36913
36145
|
withErrorHandler(async (options) => {
|
|
36914
|
-
await
|
|
36146
|
+
await runWriteCommand2("tabs.activate", options, {});
|
|
36915
36147
|
})
|
|
36916
36148
|
),
|
|
36917
36149
|
{ tabId: false }
|
|
@@ -36920,7 +36152,7 @@ var tabsCommand = new Command().name("tabs").description("Read and control brows
|
|
|
36920
36152
|
addWriteOptions(
|
|
36921
36153
|
new Command().name("open").description("Run tabs.open").requiredOption("--url <url>", "URL to open").action(
|
|
36922
36154
|
withErrorHandler(async (options) => {
|
|
36923
|
-
await
|
|
36155
|
+
await runWriteCommand2("tabs.open", options, { url: options.url });
|
|
36924
36156
|
})
|
|
36925
36157
|
)
|
|
36926
36158
|
)
|
|
@@ -36928,7 +36160,7 @@ var tabsCommand = new Command().name("tabs").description("Read and control brows
|
|
|
36928
36160
|
addWriteOptions(
|
|
36929
36161
|
new Command().name("close").description("Run tabs.close").requiredOption("--tab-id <id>", "Tab to close").action(
|
|
36930
36162
|
withErrorHandler(async (options) => {
|
|
36931
|
-
await
|
|
36163
|
+
await runWriteCommand2("tabs.close", options, {});
|
|
36932
36164
|
})
|
|
36933
36165
|
),
|
|
36934
36166
|
{ tabId: false }
|
|
@@ -36938,10 +36170,10 @@ var pageCommand = new Command().name("page").description("Read and control the a
|
|
|
36938
36170
|
addWriteOptions(
|
|
36939
36171
|
new Command().name("click").description("Run page.click").option("--selector <selector>", "CSS selector to click").option("--x <pixels>", "X coordinate to click").option("--y <pixels>", "Y coordinate to click").action(
|
|
36940
36172
|
withErrorHandler(async (options) => {
|
|
36941
|
-
await
|
|
36173
|
+
await runWriteCommand2("page.click", options, {
|
|
36942
36174
|
...options.selector ? { selector: options.selector } : {},
|
|
36943
|
-
...options.x !== void 0 ? { x:
|
|
36944
|
-
...options.y !== void 0 ? { y:
|
|
36175
|
+
...options.x !== void 0 ? { x: parseOptionalNonNegativeInteger2(options.x, "x") } : {},
|
|
36176
|
+
...options.y !== void 0 ? { y: parseOptionalNonNegativeInteger2(options.y, "y") } : {}
|
|
36945
36177
|
});
|
|
36946
36178
|
})
|
|
36947
36179
|
)
|
|
@@ -36950,7 +36182,7 @@ var pageCommand = new Command().name("page").description("Read and control the a
|
|
|
36950
36182
|
addWriteOptions(
|
|
36951
36183
|
new Command().name("type").description("Run page.type").requiredOption("--selector <selector>", "CSS selector to type into").requiredOption("--text <text>", "Text to type").action(
|
|
36952
36184
|
withErrorHandler(async (options) => {
|
|
36953
|
-
await
|
|
36185
|
+
await runWriteCommand2("page.type", options, {
|
|
36954
36186
|
selector: options.selector,
|
|
36955
36187
|
text: options.text
|
|
36956
36188
|
});
|
|
@@ -36968,9 +36200,9 @@ var pageCommand = new Command().name("page").description("Read and control the a
|
|
|
36968
36200
|
if (options.direction !== "up" && options.direction !== "down") {
|
|
36969
36201
|
throw new Error("direction must be up or down");
|
|
36970
36202
|
}
|
|
36971
|
-
await
|
|
36203
|
+
await runWriteCommand2("page.scroll", options, {
|
|
36972
36204
|
direction: options.direction,
|
|
36973
|
-
amount:
|
|
36205
|
+
amount: parsePositiveInteger3(options.amount, "amount")
|
|
36974
36206
|
});
|
|
36975
36207
|
})
|
|
36976
36208
|
)
|
|
@@ -36979,7 +36211,7 @@ var pageCommand = new Command().name("page").description("Read and control the a
|
|
|
36979
36211
|
addWriteOptions(
|
|
36980
36212
|
new Command().name("navigate").description("Run page.navigate").requiredOption("--url <url>", "URL to navigate to").action(
|
|
36981
36213
|
withErrorHandler(async (options) => {
|
|
36982
|
-
await
|
|
36214
|
+
await runWriteCommand2("page.navigate", options, {
|
|
36983
36215
|
url: options.url
|
|
36984
36216
|
});
|
|
36985
36217
|
})
|
|
@@ -36997,7 +36229,7 @@ Examples:
|
|
|
36997
36229
|
Click page? zero local-browser page click --selector button
|
|
36998
36230
|
Open tab? zero local-browser tabs open --url https://example.com
|
|
36999
36231
|
Audit actions? zero local-browser audit list`
|
|
37000
|
-
).addCommand(
|
|
36232
|
+
).addCommand(hostsCommand2).addCommand(auditCommand2).addCommand(tabsCommand).addCommand(pageCommand);
|
|
37001
36233
|
|
|
37002
36234
|
// src/commands/zero/host/index.ts
|
|
37003
36235
|
init_esm_shims();
|
|
@@ -37287,7 +36519,7 @@ function registerZeroCommands(prog, commands) {
|
|
|
37287
36519
|
var program = new Command();
|
|
37288
36520
|
program.name("zero").description(
|
|
37289
36521
|
"Zero CLI \u2014 interact with the zero platform from inside the sandbox"
|
|
37290
|
-
).version("9.
|
|
36522
|
+
).version("9.158.0").addHelpText("after", () => {
|
|
37291
36523
|
return buildZeroHelpText();
|
|
37292
36524
|
});
|
|
37293
36525
|
if (process.argv[1]?.endsWith("zero.js") || process.argv[1]?.endsWith("zero.ts") || process.argv[1]?.endsWith("zero")) {
|