@swmansion/argent 0.20.1-next.8 → 0.21.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/bin/argent-android-devtools-0.1.0.apk +0 -0
- package/bin/darwin/ax-service +0 -0
- package/bin/darwin/simulator-server +0 -0
- package/bin/darwin/tvos-ax-service +0 -0
- package/bin/darwin/tvos-hid-daemon +0 -0
- package/bin/linux/simulator-server +0 -0
- package/bin/linux-arm64/simulator-server +0 -0
- package/bin/tcp/ax-service +0 -0
- package/bin/win32/simulator-server.exe +0 -0
- package/dist/cli-cmds.mjs +6 -1
- package/dist/installer.mjs +2 -1
- package/dist/mcp-server.mjs +2 -1
- package/dist/tool-server.cjs +499 -152
- package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/libKeyboardPatch.dylib +0 -0
- package/dylibs/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tcp/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tcp/libKeyboardPatch.dylib +0 -0
- package/dylibs/tcp/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tvos/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tvos/libKeyboardPatch.dylib +0 -0
- package/dylibs/tvos/libNativeDevtoolsIos.dylib +0 -0
- package/package.json +1 -1
- package/skills/argent-create-flow/references/flow-yaml.md +1 -1
- package/skills/argent-create-flow/references/live-authoring.md +11 -11
- package/skills/argent-create-flow/references/reliability-and-recovery.md +1 -1
- package/skills/argent-device-interact/SKILL.md +7 -5
- package/skills/argent-tv-interact/SKILL.md +1 -1
package/dist/tool-server.cjs
CHANGED
|
@@ -758,6 +758,7 @@ var init_failure_codes = __esm({
|
|
|
758
758
|
CHROMIUM_ELECTRON_EXITED_BEFORE_READY: "CHROMIUM_ELECTRON_EXITED_BEFORE_READY",
|
|
759
759
|
KEYBOARD_KEY_UNSUPPORTED: "KEYBOARD_KEY_UNSUPPORTED",
|
|
760
760
|
KEYBOARD_CHARACTER_UNSUPPORTED: "KEYBOARD_CHARACTER_UNSUPPORTED",
|
|
761
|
+
KEYBOARD_TEXT_AND_KEY_COMBINED: "KEYBOARD_TEXT_AND_KEY_COMBINED",
|
|
761
762
|
SECRET_PLACEHOLDER_UNKNOWN: "SECRET_PLACEHOLDER_UNKNOWN",
|
|
762
763
|
SCREENSHOT_DIFF_INPUT_INVALID: "SCREENSHOT_DIFF_INPUT_INVALID",
|
|
763
764
|
BOOT_DEVICE_TARGET_SELECTION_INVALID: "BOOT_DEVICE_TARGET_SELECTION_INVALID",
|
|
@@ -85070,7 +85071,7 @@ var require_log = __commonJS({
|
|
|
85070
85071
|
if (logLevel === "debug")
|
|
85071
85072
|
console.log(...messages);
|
|
85072
85073
|
}
|
|
85073
|
-
function
|
|
85074
|
+
function warn2(logLevel, warning) {
|
|
85074
85075
|
if (logLevel === "debug" || logLevel === "warn") {
|
|
85075
85076
|
if (typeof node_process.emitWarning === "function")
|
|
85076
85077
|
node_process.emitWarning(warning);
|
|
@@ -85079,7 +85080,7 @@ var require_log = __commonJS({
|
|
|
85079
85080
|
}
|
|
85080
85081
|
}
|
|
85081
85082
|
exports2.debug = debug;
|
|
85082
|
-
exports2.warn =
|
|
85083
|
+
exports2.warn = warn2;
|
|
85083
85084
|
}
|
|
85084
85085
|
});
|
|
85085
85086
|
|
|
@@ -90785,6 +90786,10 @@ var FLAG_REGISTRY = [
|
|
|
90785
90786
|
name: "tool-server-event-log",
|
|
90786
90787
|
description: "Write structured tool-server lifecycle events to a JSONL file."
|
|
90787
90788
|
},
|
|
90789
|
+
{
|
|
90790
|
+
name: "microinteractions",
|
|
90791
|
+
description: "Amplify device actions with matching animations of the host window, so what happens on the guest is also visible on the desktop. Purely cosmetic, macOS only, and never affects whether the underlying action succeeds. Off by default."
|
|
90792
|
+
},
|
|
90788
90793
|
{
|
|
90789
90794
|
name: "video-watermark",
|
|
90790
90795
|
description: "Overlay the argent corner watermark on recorded screen videos. On by default; turn it off with `argent disable video-watermark`.",
|
|
@@ -96800,7 +96805,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
96800
96805
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
96801
96806
|
function readCliVersion() {
|
|
96802
96807
|
if (true) {
|
|
96803
|
-
return "0.
|
|
96808
|
+
return "0.21.0";
|
|
96804
96809
|
}
|
|
96805
96810
|
return "0.0.0";
|
|
96806
96811
|
}
|
|
@@ -98188,7 +98193,7 @@ var import_node_path5 = __toESM(require("node:path"));
|
|
|
98188
98193
|
var import_semver = __toESM(require_semver2());
|
|
98189
98194
|
|
|
98190
98195
|
// ../tool-server/package.json
|
|
98191
|
-
var version3 = "0.
|
|
98196
|
+
var version3 = "0.21.0";
|
|
98192
98197
|
|
|
98193
98198
|
// ../tool-server/src/utils/update-checker.ts
|
|
98194
98199
|
var import_update_core = __toESM(require_dist4());
|
|
@@ -99604,9 +99609,9 @@ ${debugInfo}`);
|
|
|
99604
99609
|
this.#dispose.length = 0;
|
|
99605
99610
|
if (this.#async.length > 0) {
|
|
99606
99611
|
try {
|
|
99607
|
-
let
|
|
99612
|
+
let warn2;
|
|
99608
99613
|
const timeout = new Promise((resolve11) => {
|
|
99609
|
-
|
|
99614
|
+
warn2 = setTimeout(() => {
|
|
99610
99615
|
if (DEV) {
|
|
99611
99616
|
console.warn("spawn is still running after 5s; continuing anyway", this.#stack);
|
|
99612
99617
|
}
|
|
@@ -99614,8 +99619,8 @@ ${debugInfo}`);
|
|
|
99614
99619
|
}, 5e3);
|
|
99615
99620
|
});
|
|
99616
99621
|
await Promise.race([Promise.all(this.#async), timeout]);
|
|
99617
|
-
if (
|
|
99618
|
-
clearTimeout(
|
|
99622
|
+
if (warn2)
|
|
99623
|
+
clearTimeout(warn2);
|
|
99619
99624
|
this.#async.length = 0;
|
|
99620
99625
|
} catch (error52) {
|
|
99621
99626
|
console.error("async effect error", error52);
|
|
@@ -119222,6 +119227,18 @@ function readMb(config2, key2) {
|
|
|
119222
119227
|
return (m[2] || "M").toLowerCase().startsWith("g") ? n * 1024 : n;
|
|
119223
119228
|
}
|
|
119224
119229
|
|
|
119230
|
+
// ../tool-server/src/utils/no-window-env.ts
|
|
119231
|
+
var TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes"]);
|
|
119232
|
+
function truthyEnv(name) {
|
|
119233
|
+
return TRUTHY.has((process.env[name] ?? "").trim().toLowerCase());
|
|
119234
|
+
}
|
|
119235
|
+
function iosHeadlessFromEnv() {
|
|
119236
|
+
return truthyEnv("ARGENT_SIMULATOR_NO_WINDOW");
|
|
119237
|
+
}
|
|
119238
|
+
function androidHeadlessFromEnv() {
|
|
119239
|
+
return truthyEnv("ARGENT_EMULATOR_NO_WINDOW");
|
|
119240
|
+
}
|
|
119241
|
+
|
|
119225
119242
|
// ../tool-server/src/utils/vega-vvd.ts
|
|
119226
119243
|
init_src();
|
|
119227
119244
|
init_adb();
|
|
@@ -119764,14 +119781,6 @@ function selectGpuMode() {
|
|
|
119764
119781
|
}
|
|
119765
119782
|
return process.platform === "linux" ? "swiftshader" : "auto";
|
|
119766
119783
|
}
|
|
119767
|
-
function selectExtraEmulatorArgs() {
|
|
119768
|
-
const trimmed = (process.env.ARGENT_EMULATOR_NO_WINDOW ?? "").trim().toLowerCase();
|
|
119769
|
-
return ["1", "true", "yes"].includes(trimmed) ? ["-no-window"] : [];
|
|
119770
|
-
}
|
|
119771
|
-
function iosHeadlessFromEnv() {
|
|
119772
|
-
const trimmed = (process.env.ARGENT_SIMULATOR_NO_WINDOW ?? "").trim().toLowerCase();
|
|
119773
|
-
return ["1", "true", "yes"].includes(trimmed);
|
|
119774
|
-
}
|
|
119775
119784
|
var BOOT_POLL_INTERVALS_MS = {
|
|
119776
119785
|
serialByAvd: 1500,
|
|
119777
119786
|
// findSerialByAvdName: re-scan when >1 new emulator appeared
|
|
@@ -120155,7 +120164,7 @@ async function bootAndroidImpl(params) {
|
|
|
120155
120164
|
await ensureDep("adb");
|
|
120156
120165
|
await ensureDep("emulator");
|
|
120157
120166
|
const gpuMode = selectGpuMode();
|
|
120158
|
-
const extraEmulatorArgs =
|
|
120167
|
+
const extraEmulatorArgs = androidHeadlessFromEnv() ? ["-no-window"] : [];
|
|
120159
120168
|
for (const msg of linuxBootDiagnostics(params.avdName) ?? []) {
|
|
120160
120169
|
console.warn(`[boot-device:linux] ${msg}`);
|
|
120161
120170
|
}
|
|
@@ -122653,6 +122662,7 @@ Fails if the device backend is not reachable \u2014 the simulator-server for iOS
|
|
|
122653
122662
|
|
|
122654
122663
|
// ../tool-server/src/tools/keyboard/index.ts
|
|
122655
122664
|
init_zod();
|
|
122665
|
+
init_src();
|
|
122656
122666
|
|
|
122657
122667
|
// ../tool-server/src/utils/secrets.ts
|
|
122658
122668
|
init_src();
|
|
@@ -122813,21 +122823,6 @@ async function typeSimulatorServer(registry2, device, params) {
|
|
|
122813
122823
|
}
|
|
122814
122824
|
keysPressed++;
|
|
122815
122825
|
};
|
|
122816
|
-
let namedKeyCode;
|
|
122817
|
-
if (params.key) {
|
|
122818
|
-
const lower = params.key.toLowerCase();
|
|
122819
|
-
namedKeyCode = Object.hasOwn(NAMED_KEYS, lower) ? NAMED_KEYS[lower] : void 0;
|
|
122820
|
-
if (namedKeyCode == null) {
|
|
122821
|
-
throw new InvalidToolInputError(
|
|
122822
|
-
`Unknown key "${params.key}". Supported: ${Object.keys(NAMED_KEYS).join(", ")}`,
|
|
122823
|
-
{
|
|
122824
|
-
error_code: FAILURE_CODES.KEYBOARD_KEY_UNSUPPORTED,
|
|
122825
|
-
failure_stage: "keyboard_named_key_simulator",
|
|
122826
|
-
error_kind: "unsupported"
|
|
122827
|
-
}
|
|
122828
|
-
);
|
|
122829
|
-
}
|
|
122830
|
-
}
|
|
122831
122826
|
if (params.text) {
|
|
122832
122827
|
for (const char of params.text) {
|
|
122833
122828
|
const press = charToKeyPress(char);
|
|
@@ -122841,7 +122836,19 @@ async function typeSimulatorServer(registry2, device, params) {
|
|
|
122841
122836
|
await sleep8(delay2);
|
|
122842
122837
|
}
|
|
122843
122838
|
}
|
|
122844
|
-
if (
|
|
122839
|
+
if (params.key) {
|
|
122840
|
+
const lower = params.key.toLowerCase();
|
|
122841
|
+
const namedKeyCode = Object.hasOwn(NAMED_KEYS, lower) ? NAMED_KEYS[lower] : void 0;
|
|
122842
|
+
if (namedKeyCode == null) {
|
|
122843
|
+
throw new InvalidToolInputError(
|
|
122844
|
+
`Unknown key "${params.key}". Supported: ${Object.keys(NAMED_KEYS).join(", ")}`,
|
|
122845
|
+
{
|
|
122846
|
+
error_code: FAILURE_CODES.KEYBOARD_KEY_UNSUPPORTED,
|
|
122847
|
+
failure_stage: "keyboard_named_key_simulator",
|
|
122848
|
+
error_kind: "unsupported"
|
|
122849
|
+
}
|
|
122850
|
+
);
|
|
122851
|
+
}
|
|
122845
122852
|
await pressKeyCode(namedKeyCode);
|
|
122846
122853
|
}
|
|
122847
122854
|
return { typed: params.text ?? params.key ?? "", keys: keysPressed };
|
|
@@ -122890,15 +122897,14 @@ function makeIosRemoteImpl(registry2) {
|
|
|
122890
122897
|
init_adb();
|
|
122891
122898
|
async function typeAndroidPhone(device, params) {
|
|
122892
122899
|
let keysPressed = 0;
|
|
122893
|
-
if (params.text) assertTypeableAndroidText(params.text);
|
|
122894
|
-
if (params.key) {
|
|
122895
|
-
await injectAndroidNamedKey(device.id, params.key);
|
|
122896
|
-
keysPressed++;
|
|
122897
|
-
}
|
|
122898
122900
|
if (params.text) {
|
|
122899
122901
|
await injectAndroidText(device.id, params.text);
|
|
122900
122902
|
keysPressed += params.text.length;
|
|
122901
122903
|
}
|
|
122904
|
+
if (params.key) {
|
|
122905
|
+
await injectAndroidNamedKey(device.id, params.key);
|
|
122906
|
+
keysPressed++;
|
|
122907
|
+
}
|
|
122902
122908
|
return { typed: params.text ?? params.key ?? "", keys: keysPressed };
|
|
122903
122909
|
}
|
|
122904
122910
|
function makeAndroidImpl(registry2) {
|
|
@@ -122987,21 +122993,6 @@ var sleep9 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
122987
122993
|
async function runChromium(api, params) {
|
|
122988
122994
|
const delay2 = params.delayMs ?? 50;
|
|
122989
122995
|
let keysPressed = 0;
|
|
122990
|
-
let named;
|
|
122991
|
-
if (params.key) {
|
|
122992
|
-
const lower = params.key.toLowerCase();
|
|
122993
|
-
named = Object.hasOwn(CHROMIUM_NAMED_KEYS, lower) ? CHROMIUM_NAMED_KEYS[lower] : void 0;
|
|
122994
|
-
if (!named) {
|
|
122995
|
-
throw new InvalidToolInputError(
|
|
122996
|
-
`Unknown key "${params.key}". Supported: ${Object.keys(CHROMIUM_NAMED_KEYS).join(", ")}`,
|
|
122997
|
-
{
|
|
122998
|
-
error_code: FAILURE_CODES.KEYBOARD_KEY_UNSUPPORTED,
|
|
122999
|
-
failure_stage: "keyboard_named_key_chromium",
|
|
123000
|
-
error_kind: "unsupported"
|
|
123001
|
-
}
|
|
123002
|
-
);
|
|
123003
|
-
}
|
|
123004
|
-
}
|
|
123005
122996
|
if (params.text) {
|
|
123006
122997
|
for (const char of params.text) {
|
|
123007
122998
|
const desc = charToChromiumKey(char);
|
|
@@ -123029,7 +123020,19 @@ async function runChromium(api, params) {
|
|
|
123029
123020
|
await sleep9(delay2);
|
|
123030
123021
|
}
|
|
123031
123022
|
}
|
|
123032
|
-
if (
|
|
123023
|
+
if (params.key) {
|
|
123024
|
+
const lower = params.key.toLowerCase();
|
|
123025
|
+
const named = Object.hasOwn(CHROMIUM_NAMED_KEYS, lower) ? CHROMIUM_NAMED_KEYS[lower] : void 0;
|
|
123026
|
+
if (!named) {
|
|
123027
|
+
throw new InvalidToolInputError(
|
|
123028
|
+
`Unknown key "${params.key}". Supported: ${Object.keys(CHROMIUM_NAMED_KEYS).join(", ")}`,
|
|
123029
|
+
{
|
|
123030
|
+
error_code: FAILURE_CODES.KEYBOARD_KEY_UNSUPPORTED,
|
|
123031
|
+
failure_stage: "keyboard_named_key_chromium",
|
|
123032
|
+
error_kind: "unsupported"
|
|
123033
|
+
}
|
|
123034
|
+
);
|
|
123035
|
+
}
|
|
123033
123036
|
await api.dispatchKeyEvent({
|
|
123034
123037
|
type: "keyDown",
|
|
123035
123038
|
key: named.key,
|
|
@@ -123128,7 +123131,7 @@ async function injectViaInputd(subcommands) {
|
|
|
123128
123131
|
async function injectVegaButtons(buttons) {
|
|
123129
123132
|
await injectViaInputd(remoteButtonsToKeycodes(buttons).map((code) => `button_press ${code}`));
|
|
123130
123133
|
}
|
|
123131
|
-
function
|
|
123134
|
+
async function injectVegaNamedKey(name) {
|
|
123132
123135
|
const lower = name.toLowerCase();
|
|
123133
123136
|
const code = Object.hasOwn(NAMED_KEYCODES, lower) ? NAMED_KEYCODES[lower] : void 0;
|
|
123134
123137
|
if (!code) {
|
|
@@ -123141,10 +123144,7 @@ function resolveVegaNamedKeycode(name) {
|
|
|
123141
123144
|
}
|
|
123142
123145
|
);
|
|
123143
123146
|
}
|
|
123144
|
-
|
|
123145
|
-
}
|
|
123146
|
-
async function injectVegaNamedKey(name) {
|
|
123147
|
-
await injectViaInputd([`button_press ${resolveVegaNamedKeycode(name)}`]);
|
|
123147
|
+
await injectViaInputd([`button_press ${code}`]);
|
|
123148
123148
|
}
|
|
123149
123149
|
async function injectVegaText(text) {
|
|
123150
123150
|
if (/[\n\r]/.test(text)) {
|
|
@@ -123160,7 +123160,6 @@ async function injectVegaText(text) {
|
|
|
123160
123160
|
// ../tool-server/src/tools/keyboard/platforms/vega.ts
|
|
123161
123161
|
async function runVega2(params) {
|
|
123162
123162
|
let keysPressed = 0;
|
|
123163
|
-
if (params.key) resolveVegaNamedKeycode(params.key);
|
|
123164
123163
|
if (params.text) {
|
|
123165
123164
|
await injectVegaText(params.text);
|
|
123166
123165
|
keysPressed += [...params.text].length;
|
|
@@ -123182,10 +123181,10 @@ var zodSchema24 = external_exports.object({
|
|
|
123182
123181
|
"Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, or Chromium id)."
|
|
123183
123182
|
),
|
|
123184
123183
|
text: external_exports.string().optional().describe(
|
|
123185
|
-
'Text to type character by character. Handles uppercase and common punctuation. To type a credential without its plaintext ever entering your context, use a secret placeholder: `{{secret:<NAME>}}` \u2014 e.g. text: "{{secret:APP_PASSWORD}}". The value is resolved on the machine running the tool-server, from the first source that defines the name: the `ARGENT_SECRET_<NAME>` environment variable, `.argent/secrets.env` in the project, the project\'s `.env.local` / `.env` (only their `ARGENT_SECRET_`-prefixed keys), then `~/.argent/secrets.env`. Nothing else on the host is reachable. Placeholders can be embedded in longer text and are never echoed back resolved. If the secret you need is not set, the failure lists the available names and every source it looked in \u2014 ask the user to add it to one of them (a secrets file applies immediately; an env var needs a restart), NEVER ask the user to paste the secret value into the conversation.'
|
|
123184
|
+
'Text to type character by character. Cannot be combined with `key` in one call \u2014 one call per action; to type and then press a key, put two `keyboard` steps in one `run-sequence`. Handles uppercase and common punctuation. To type a credential without its plaintext ever entering your context, use a secret placeholder: `{{secret:<NAME>}}` \u2014 e.g. text: "{{secret:APP_PASSWORD}}". The value is resolved on the machine running the tool-server, from the first source that defines the name: the `ARGENT_SECRET_<NAME>` environment variable, `.argent/secrets.env` in the project, the project\'s `.env.local` / `.env` (only their `ARGENT_SECRET_`-prefixed keys), then `~/.argent/secrets.env`. Nothing else on the host is reachable. Placeholders can be embedded in longer text and are never echoed back resolved. If the secret you need is not set, the failure lists the available names and every source it looked in \u2014 ask the user to add it to one of them (a secrets file applies immediately; an env var needs a restart), NEVER ask the user to paste the secret value into the conversation.'
|
|
123186
123185
|
),
|
|
123187
123186
|
key: external_exports.string().optional().describe(
|
|
123188
|
-
"Named key to press: enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1\u2013f12.
|
|
123187
|
+
"Named key to press: enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1\u2013f12. Cannot be combined with `text` in one call \u2014 one call per action; to type and then press a key, put two `keyboard` steps in one `run-sequence`. Not supported on TV targets \u2014 move focus with `tv-remote` (up/down/left/right) instead."
|
|
123189
123188
|
),
|
|
123190
123189
|
delayMs: external_exports.number().optional().describe(
|
|
123191
123190
|
"Delay in ms between key presses (default 50). Ignored on Android phones/tablets (typed via `adb input text`, which has no per-key cadence), on Vega (text/keys injected in a single shot), and on TV targets (Apple TV / Android TV type the whole string at the daemon's own cadence)."
|
|
@@ -123214,25 +123213,29 @@ function createKeyboardTool(registry2) {
|
|
|
123214
123213
|
// Treat both text and key as sensitive. `key` is an unrestricted string at
|
|
123215
123214
|
// this boundary, so a value must not reach the event log before execution
|
|
123216
123215
|
// validates whether it is a supported named key.
|
|
123216
|
+
//
|
|
123217
|
+
// `startedMsg` still words a text+key request because it renders BEFORE
|
|
123218
|
+
// `execute` rejects the combination — and likewise words `{ key: "" }` as
|
|
123219
|
+
// a key press, which `execute` also rejects. `completedMsg` runs only
|
|
123220
|
+
// after a call that succeeded, so it sees neither. Each formatter
|
|
123221
|
+
// therefore has to word a different set of shapes, and the empty
|
|
123222
|
+
// request — neither parameter, a documented no-op — reaches both.
|
|
123217
123223
|
startedMsg: ({ params }) => {
|
|
123218
123224
|
if (params.text === void 0) return "Pressing a key";
|
|
123219
123225
|
if (params.key === void 0) return "Entering text";
|
|
123220
123226
|
return "Entering text and pressing a key";
|
|
123221
123227
|
},
|
|
123222
|
-
completedMsg: ({ params }) =>
|
|
123223
|
-
if (params.text === void 0) return "Pressed a key";
|
|
123224
|
-
if (params.key === void 0) return "Entered text";
|
|
123225
|
-
return "Entered text and pressed a key";
|
|
123226
|
-
},
|
|
123228
|
+
completedMsg: ({ params }) => params.text === void 0 ? "Pressed a key" : "Entered text",
|
|
123227
123229
|
failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to use keyboard: ${failureSignal2.error_code}`
|
|
123228
123230
|
},
|
|
123229
123231
|
description: `Type text or press special keys on the device (iOS simulator, Android emulator or device, Chromium app, Vega Virtual Device, or Apple TV / Android TV) using keyboard events.
|
|
123230
123232
|
Use when you need to enter text or trigger a named key such as enter, escape, or arrow keys. On Vega and Apple TV / Android TV, prefer the remote tools for D-pad navigation; use keyboard to type into a focused text field (e.g. a search or login box).
|
|
123231
|
-
Returns { typed: string, keys: number }. Fails if an unsupported key name is provided or the device's input backend is not reachable.
|
|
123232
|
-
|
|
123233
|
+
Returns { typed: string, keys: number }. Fails if text and key are both given in one call (rejected before anything is typed), if an unsupported key name is provided, or if the device's input backend is not reachable.
|
|
123234
|
+
A failure is not rolled back. An unsupported key name is always rejected before anything is sent. Un-typeable text is not: the iOS simulator and Chromium reject it mid-string and leave the characters before it in the field (Android, Vega and TV targets check the whole string up front). A transport failure partway also leaves the text already sent. On a retry, read the field's actual contents \u2014 do not assume it is unchanged.
|
|
123235
|
+
- text: types a string (supports uppercase, digits, common punctuation). To type a credential, use \`{{secret:<NAME>}}\` \u2014 resolved server-side from the \`ARGENT_SECRET_<NAME>\` env var or an argent secrets file (\`.argent/secrets.env\` in the project, \`~/.argent/secrets.env\`, or an \`ARGENT_SECRET_\`-prefixed key in the project's \`.env\`/\`.env.local\`), so the plaintext never enters agent context; the result echoes the placeholder, not the value, and the after-typing auto-screenshot is skipped. To submit after typing a secret, put both steps in ONE \`run-sequence\` \u2014 that keeps the skip covering the Enter, which a second bare \`keyboard\` call would not.
|
|
123233
123236
|
- key: presses a single named key (enter, escape, backspace, tab, arrow-up/down/left/right, f1\u2013f12) \u2014 NOT supported on TV targets; move focus with \`tv-remote\` instead.
|
|
123234
123237
|
On a TV target (runtimeKind 'tv') only \`text\` applies \u2014 focus a text field first (with \`tv-remote\`), then type into it (injected HID keyboard on Apple TV, \`adb input text\` on Android TV).
|
|
123235
|
-
|
|
123238
|
+
One call does one action: pass text OR key, never both. To type and then press a key, send two \`keyboard\` steps in one \`run-sequence\` \u2014 { text: "hello" } then { key: "enter" } \u2014 which also keeps it to a single round-trip.`,
|
|
123236
123239
|
zodSchema: zodSchema24,
|
|
123237
123240
|
capability: capability16,
|
|
123238
123241
|
searchHint: "type text keyboard input named key enter escape arrow tv vega fire tv search field hid leanback",
|
|
@@ -123241,6 +123244,46 @@ Provide text, key, or both \u2014 when both are given, the text is typed first a
|
|
|
123241
123244
|
// async and a tvOS udid must never resolve simulator-server.
|
|
123242
123245
|
services: () => ({}),
|
|
123243
123246
|
execute: async (services, params, options) => {
|
|
123247
|
+
if (params.text !== void 0 && params.key !== void 0) {
|
|
123248
|
+
throw new InvalidToolInputError(
|
|
123249
|
+
// Says what did NOT happen, so the caller retries instead of first
|
|
123250
|
+
// inspecting the field — and spells the retry out with a literal
|
|
123251
|
+
// example rather than an ellipsis the Android backend can't type.
|
|
123252
|
+
//
|
|
123253
|
+
// The TV caveat is carried statically rather than by probing the
|
|
123254
|
+
// target: this guard runs above the dispatch precisely so a combined
|
|
123255
|
+
// request reaches no device, and distinguishing a TV kind is an async
|
|
123256
|
+
// probe. Without it the prescribed `{ key: "enter" }` is a retry that
|
|
123257
|
+
// cannot succeed on a TV, where `key` is rejected outright
|
|
123258
|
+
// (platforms/tv.ts) — which is the diagnosis this guard would
|
|
123259
|
+
// otherwise pre-empt.
|
|
123260
|
+
'keyboard takes `text` or `key`, not both \u2014 nothing was typed. To type and then press a key, send two `keyboard` steps in one `run-sequence`: { text: "hello" } followed by { key: "enter" }. On a TV target (Apple TV / Android TV) `key` is not supported at all \u2014 type with `text` and move focus with `tv-remote` (up/down/left/right/select).' + // The one-`run-sequence` form and two bare calls are NOT equivalent
|
|
123261
|
+
// once the text carries a placeholder, and this message is where an
|
|
123262
|
+
// agent converts a combined secret call — the tool description's
|
|
123263
|
+
// caveat is read long before that moment, if at all. The check is
|
|
123264
|
+
// syntactic (the same `.includes` flow-utils.ts uses), so the guard
|
|
123265
|
+
// still resolves nothing.
|
|
123266
|
+
(params.text.includes(SECRET_PLACEHOLDER_MARKER) ? " This `text` carries a `" + SECRET_PLACEHOLDER_MARKER + '...}}` placeholder, so keep both steps in that ONE `run-sequence` rather than splitting them into two bare calls: the auto-screenshot skip is decided per tool call from the whole request, and a separate { key: "enter" } call carries no placeholder \u2014 its screenshot is taken after the key lands and can capture the still-visible secret.' : ""),
|
|
123267
|
+
{
|
|
123268
|
+
error_code: FAILURE_CODES.KEYBOARD_TEXT_AND_KEY_COMBINED,
|
|
123269
|
+
failure_stage: "keyboard_text_and_key_combined"
|
|
123270
|
+
}
|
|
123271
|
+
);
|
|
123272
|
+
}
|
|
123273
|
+
if (params.key === "") {
|
|
123274
|
+
throw new InvalidToolInputError(
|
|
123275
|
+
// Names the omission as the alternative, because a caller that sent an
|
|
123276
|
+
// empty string usually built the value from something absent.
|
|
123277
|
+
"`key` is an empty string, which names no key \u2014 nothing was pressed. Pass a named key (enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1\u2013f12), or omit `key` if there is nothing to press.",
|
|
123278
|
+
{
|
|
123279
|
+
// The same code an unknown name gets, because that is what this is:
|
|
123280
|
+
// one telemetry bucket for every unusable `key` value.
|
|
123281
|
+
error_code: FAILURE_CODES.KEYBOARD_KEY_UNSUPPORTED,
|
|
123282
|
+
failure_stage: "keyboard_named_key_empty",
|
|
123283
|
+
error_kind: "unsupported"
|
|
123284
|
+
}
|
|
123285
|
+
);
|
|
123286
|
+
}
|
|
123244
123287
|
if (params.text === void 0) return dispatch(services, params, options);
|
|
123245
123288
|
const { text, secrets } = resolveSecretPlaceholders(params.text);
|
|
123246
123289
|
if (secrets.length === 0) return dispatch(services, params, options);
|
|
@@ -123300,10 +123343,211 @@ var shakeZodSchema = external_exports.object({
|
|
|
123300
123343
|
});
|
|
123301
123344
|
|
|
123302
123345
|
// ../tool-server/src/tools/shake/platforms/ios.ts
|
|
123303
|
-
var
|
|
123346
|
+
var import_node_child_process27 = require("node:child_process");
|
|
123304
123347
|
var import_node_util20 = require("node:util");
|
|
123305
123348
|
init_src();
|
|
123306
|
-
|
|
123349
|
+
|
|
123350
|
+
// ../tool-server/src/utils/window-shake.ts
|
|
123351
|
+
var import_node_child_process26 = require("node:child_process");
|
|
123352
|
+
var MICROINTERACTIONS_FLAG = "microinteractions";
|
|
123353
|
+
var AMPLITUDE = 22;
|
|
123354
|
+
var STEPS = 60;
|
|
123355
|
+
var OSASCRIPT_TIMEOUT_MS = 5e3;
|
|
123356
|
+
var ORIGIN_MARKER = "ARGENT_WINDOW_ORIGIN:";
|
|
123357
|
+
function warn(detail) {
|
|
123358
|
+
process.stderr.write(`[shake:window] skipped the window animation: ${detail}
|
|
123359
|
+
`);
|
|
123360
|
+
}
|
|
123361
|
+
function asStringLiteral(value) {
|
|
123362
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
123363
|
+
}
|
|
123364
|
+
function titleMatchLookup(procNamesExpr, needles) {
|
|
123365
|
+
return `
|
|
123366
|
+
set win to missing value
|
|
123367
|
+
set needles to {${needles.map(asStringLiteral).join(", ")}}
|
|
123368
|
+
set procNames to ${procNamesExpr}
|
|
123369
|
+
repeat with procRef in procNames
|
|
123370
|
+
set procName to contents of procRef
|
|
123371
|
+
set winTitles to {}
|
|
123372
|
+
try
|
|
123373
|
+
tell process procName to set winTitles to name of every window
|
|
123374
|
+
end try
|
|
123375
|
+
repeat with idx from 1 to (count of winTitles)
|
|
123376
|
+
set winTitle to item idx of winTitles
|
|
123377
|
+
-- A GUI app can carry an unnamed helper window alongside the real one.
|
|
123378
|
+
if winTitle is not missing value then
|
|
123379
|
+
repeat with needle in needles
|
|
123380
|
+
if winTitle contains (contents of needle) then
|
|
123381
|
+
set win to window idx of process procName
|
|
123382
|
+
exit repeat
|
|
123383
|
+
end if
|
|
123384
|
+
end repeat
|
|
123385
|
+
end if
|
|
123386
|
+
if win is not missing value then exit repeat
|
|
123387
|
+
end repeat
|
|
123388
|
+
if win is not missing value then exit repeat
|
|
123389
|
+
end repeat`;
|
|
123390
|
+
}
|
|
123391
|
+
function windowLookup(needles) {
|
|
123392
|
+
if (needles.length === 0) {
|
|
123393
|
+
return `
|
|
123394
|
+
set win to missing value
|
|
123395
|
+
repeat with procRef in {"Simulator", "Device Hub"}
|
|
123396
|
+
-- A repeat variable is a reference into the list and "process <ref>"
|
|
123397
|
+
-- will not coerce one, so without "contents of" the lookup misses.
|
|
123398
|
+
set procName to contents of procRef
|
|
123399
|
+
if exists (process procName) then
|
|
123400
|
+
tell process procName
|
|
123401
|
+
if (count of windows) > 0 then set win to window 1
|
|
123402
|
+
end tell
|
|
123403
|
+
end if
|
|
123404
|
+
if win is not missing value then exit repeat
|
|
123405
|
+
end repeat`;
|
|
123406
|
+
}
|
|
123407
|
+
return titleMatchLookup(
|
|
123408
|
+
`name of every process whose name is "Simulator" or name is "Device Hub"`,
|
|
123409
|
+
needles
|
|
123410
|
+
);
|
|
123411
|
+
}
|
|
123412
|
+
var WOBBLE_OFFSETS = Array.from(
|
|
123413
|
+
{ length: STEPS + 1 },
|
|
123414
|
+
(_, i) => {
|
|
123415
|
+
const t = i / STEPS;
|
|
123416
|
+
const decay = Math.exp(-4 * t);
|
|
123417
|
+
const phase = 2 * Math.PI * 3.5 * t;
|
|
123418
|
+
return [
|
|
123419
|
+
Math.round(AMPLITUDE * decay * Math.sin(phase)),
|
|
123420
|
+
Math.round(AMPLITUDE / 3 * decay * Math.sin(2 * phase))
|
|
123421
|
+
];
|
|
123422
|
+
}
|
|
123423
|
+
);
|
|
123424
|
+
var OFFSETS_LITERAL = WOBBLE_OFFSETS.map(([dx, dy]) => `{${dx}, ${dy}}`).join(", ");
|
|
123425
|
+
function animationScript(needles) {
|
|
123426
|
+
return `on run
|
|
123427
|
+
tell application "System Events"${windowLookup(needles)}
|
|
123428
|
+
if win is missing value then error "no matching window"
|
|
123429
|
+
set origin to position of win
|
|
123430
|
+
set ox to item 1 of origin
|
|
123431
|
+
set oy to item 2 of origin
|
|
123432
|
+
log "${ORIGIN_MARKER}" & ox & "," & oy
|
|
123433
|
+
|
|
123434
|
+
set offsets to {${OFFSETS_LITERAL}}
|
|
123435
|
+
repeat with pair in offsets
|
|
123436
|
+
set position of win to {ox + (item 1 of pair), oy + (item 2 of pair)}
|
|
123437
|
+
end repeat
|
|
123438
|
+
|
|
123439
|
+
set position of win to {ox, oy}
|
|
123440
|
+
delay 0.05
|
|
123441
|
+
set position of win to {ox, oy}
|
|
123442
|
+
end tell
|
|
123443
|
+
end run
|
|
123444
|
+
`;
|
|
123445
|
+
}
|
|
123446
|
+
function restoreScript(needles, ox, oy) {
|
|
123447
|
+
return `on run
|
|
123448
|
+
tell application "System Events"${windowLookup(needles)}
|
|
123449
|
+
if win is missing value then error "no matching window"
|
|
123450
|
+
set position of win to {${ox}, ${oy}}
|
|
123451
|
+
end tell
|
|
123452
|
+
end run
|
|
123453
|
+
`;
|
|
123454
|
+
}
|
|
123455
|
+
function runOsascript(script) {
|
|
123456
|
+
return new Promise((resolve11, reject) => {
|
|
123457
|
+
const child = (0, import_node_child_process26.execFile)(
|
|
123458
|
+
"/usr/bin/osascript",
|
|
123459
|
+
["-"],
|
|
123460
|
+
{ timeout: OSASCRIPT_TIMEOUT_MS },
|
|
123461
|
+
(err, _stdout, stderr) => {
|
|
123462
|
+
if (err) {
|
|
123463
|
+
const raw = String(stderr ?? "");
|
|
123464
|
+
const detail = raw.split("\n").filter((line) => !line.includes(ORIGIN_MARKER)).join("\n").trim() || err.message || "osascript failed";
|
|
123465
|
+
const failure = new Error(detail);
|
|
123466
|
+
failure.wasKilled = Boolean(
|
|
123467
|
+
err.killed || err.signal
|
|
123468
|
+
);
|
|
123469
|
+
failure.rawStderr = raw;
|
|
123470
|
+
reject(failure);
|
|
123471
|
+
return;
|
|
123472
|
+
}
|
|
123473
|
+
resolve11();
|
|
123474
|
+
}
|
|
123475
|
+
);
|
|
123476
|
+
child.on("error", (err) => reject(err));
|
|
123477
|
+
child.stdin?.on("error", () => {
|
|
123478
|
+
});
|
|
123479
|
+
child.stdin?.end(script);
|
|
123480
|
+
});
|
|
123481
|
+
}
|
|
123482
|
+
async function restoreOriginAfterKill(needles, rawStderr) {
|
|
123483
|
+
const match = rawStderr.match(new RegExp(`${ORIGIN_MARKER}\\s*(-?\\d+)\\s*,\\s*(-?\\d+)`));
|
|
123484
|
+
if (!match) return;
|
|
123485
|
+
try {
|
|
123486
|
+
await runOsascript(restoreScript(needles, Number(match[1]), Number(match[2])));
|
|
123487
|
+
} catch {
|
|
123488
|
+
}
|
|
123489
|
+
}
|
|
123490
|
+
async function prepareHostWindowShake(target) {
|
|
123491
|
+
const inert = { begin: () => {
|
|
123492
|
+
}, settle: () => Promise.resolve() };
|
|
123493
|
+
try {
|
|
123494
|
+
if (!isFeatureEnabled(MICROINTERACTIONS_FLAG)) return inert;
|
|
123495
|
+
if (process.platform !== "darwin") return inert;
|
|
123496
|
+
if (iosHeadlessFromEnv()) return inert;
|
|
123497
|
+
let deviceSet = null;
|
|
123498
|
+
try {
|
|
123499
|
+
deviceSet = await deviceSetForUdid(target.udid);
|
|
123500
|
+
} catch {
|
|
123501
|
+
}
|
|
123502
|
+
if (deviceSet !== null) return inert;
|
|
123503
|
+
const needles = target.name ? [target.name] : [];
|
|
123504
|
+
const script = animationScript(needles);
|
|
123505
|
+
let inFlight = null;
|
|
123506
|
+
let dead = false;
|
|
123507
|
+
let warned2 = false;
|
|
123508
|
+
const warnOnce = (detail) => {
|
|
123509
|
+
if (warned2) return;
|
|
123510
|
+
warned2 = true;
|
|
123511
|
+
warn(detail);
|
|
123512
|
+
};
|
|
123513
|
+
return {
|
|
123514
|
+
begin() {
|
|
123515
|
+
if (dead || inFlight !== null) return;
|
|
123516
|
+
let run2;
|
|
123517
|
+
try {
|
|
123518
|
+
run2 = runOsascript(script);
|
|
123519
|
+
} catch (err) {
|
|
123520
|
+
dead = true;
|
|
123521
|
+
warnOnce(err instanceof Error ? err.message : String(err));
|
|
123522
|
+
return;
|
|
123523
|
+
}
|
|
123524
|
+
inFlight = run2.then(
|
|
123525
|
+
() => {
|
|
123526
|
+
inFlight = null;
|
|
123527
|
+
},
|
|
123528
|
+
async (err) => {
|
|
123529
|
+
dead = true;
|
|
123530
|
+
const failure = err;
|
|
123531
|
+
if (failure.wasKilled && typeof failure.rawStderr === "string") {
|
|
123532
|
+
await restoreOriginAfterKill(needles, failure.rawStderr);
|
|
123533
|
+
}
|
|
123534
|
+
warnOnce(err instanceof Error ? err.message : String(err));
|
|
123535
|
+
inFlight = null;
|
|
123536
|
+
}
|
|
123537
|
+
);
|
|
123538
|
+
},
|
|
123539
|
+
settle() {
|
|
123540
|
+
return inFlight ?? Promise.resolve();
|
|
123541
|
+
}
|
|
123542
|
+
};
|
|
123543
|
+
} catch (err) {
|
|
123544
|
+
warn(err instanceof Error ? err.message : String(err));
|
|
123545
|
+
return inert;
|
|
123546
|
+
}
|
|
123547
|
+
}
|
|
123548
|
+
|
|
123549
|
+
// ../tool-server/src/tools/shake/platforms/ios.ts
|
|
123550
|
+
var execFileAsync20 = (0, import_node_util20.promisify)(import_node_child_process27.execFile);
|
|
123307
123551
|
var SHAKE_NOTIFICATION = "com.apple.UIKit.SimulatorShake";
|
|
123308
123552
|
var SHAKE_INTERVAL_MS = 400;
|
|
123309
123553
|
var sleep10 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -123339,8 +123583,10 @@ var iosImpl4 = {
|
|
|
123339
123583
|
const count2 = params.count ?? 1;
|
|
123340
123584
|
if (await isTvOsSimulator(udid)) rejectTv("shake", device);
|
|
123341
123585
|
const args = await simctlArgsForUdid(udid, ["spawn", udid, ...NOTIFYUTIL_ARGV]);
|
|
123586
|
+
const shaker = await prepareHostWindowShake({ kind: "ios", udid, name: device.name });
|
|
123342
123587
|
for (let i = 0; i < count2; i++) {
|
|
123343
123588
|
if (i > 0) await sleep10(SHAKE_INTERVAL_MS);
|
|
123589
|
+
shaker.begin();
|
|
123344
123590
|
try {
|
|
123345
123591
|
await execFileAsync20("xcrun", args, { timeout: 15e3 });
|
|
123346
123592
|
} catch (err) {
|
|
@@ -123348,6 +123594,7 @@ var iosImpl4 = {
|
|
|
123348
123594
|
throw shakeFailure(udid, error52.message, error52);
|
|
123349
123595
|
}
|
|
123350
123596
|
}
|
|
123597
|
+
await shaker.settle();
|
|
123351
123598
|
return { shaken: true, count: count2 };
|
|
123352
123599
|
}
|
|
123353
123600
|
};
|
|
@@ -130065,7 +130312,8 @@ function createRunSequenceTool(registry2) {
|
|
|
130065
130312
|
Use when you need sequential actions and do NOT need to observe the screen between them
|
|
130066
130313
|
(e.g. scrolling multiple times, typing then pressing enter, rotating back and forth).
|
|
130067
130314
|
Returns { completed, total, steps } with per-step results. Fails if an unrecognised tool name is used in a step (error returned at that step, execution stops).
|
|
130068
|
-
|
|
130315
|
+
One screenshot is captured automatically after the whole sequence (not per step) \u2014 call screenshot separately only for a baseline BEFORE it, or to observe an intermediate step.
|
|
130316
|
+
That single capture is also why a secret belongs in this call rather than in two bare ones: the skip is decided from the whole request, so a \`{{secret:...}}\` in any step suppresses the capture that would otherwise follow the submit.
|
|
130069
130317
|
|
|
130070
130318
|
ONLY use this when every step is known in advance. If any step depends on the
|
|
130071
130319
|
result of a previous one (e.g. tapping a menu item that only appears after
|
|
@@ -130081,7 +130329,7 @@ Allowed tools and their args (udid is auto-injected, do NOT include it in args):
|
|
|
130081
130329
|
gesture-pinch: { centerX: number, centerY: number, startDistance: number, endDistance: number, endCenterX?: number, endCenterY?: number, angle?: number, durationMs?: number } [ios/android]
|
|
130082
130330
|
gesture-rotate: { centerX: number, centerY: number, radius?: number, radiusX?: number, radiusY?: number, startAngle: number, endAngle: number, durationMs?: number } [ios/android]
|
|
130083
130331
|
button: { button: "home"|"back"|"power"|"volumeUp"|"volumeDown"|"appSwitch"|"actionButton" } [ios/android]
|
|
130084
|
-
keyboard: { text?: string, key?: string, delayMs?: number } (key
|
|
130332
|
+
keyboard: { text?: string, key?: string, delayMs?: number } (text OR key per step, never both; TV: text only) [ios/android/chromium/vega/tv]
|
|
130085
130333
|
text supports {{secret:<NAME>}} placeholders, resolved server-side from ARGENT_SECRET_<NAME> env vars or an argent secrets file \u2014 credentials never enter agent context
|
|
130086
130334
|
rotate: { orientation: "Portrait"|"LandscapeLeft"|"LandscapeRight"|"PortraitUpsideDown" } [ios/android]
|
|
130087
130335
|
shake: { count?: number } [ios sim/android emu]
|
|
@@ -130096,9 +130344,10 @@ Example \u2014 scroll down three times (use gesture-scroll with positive deltaY
|
|
|
130096
130344
|
{ "tool": "gesture-swipe", "args": { "fromX": 0.5, "fromY": 0.7, "toX": 0.5, "toY": 0.3 } }
|
|
130097
130345
|
]}
|
|
130098
130346
|
|
|
130099
|
-
Example \u2014 type text and submit (
|
|
130347
|
+
Example \u2014 type text and submit (two keyboard steps; one call cannot carry both):
|
|
130100
130348
|
{ "udid": "<UDID>", "steps": [
|
|
130101
|
-
{ "tool": "keyboard", "args": { "text": "hello world"
|
|
130349
|
+
{ "tool": "keyboard", "args": { "text": "hello world" } },
|
|
130350
|
+
{ "tool": "keyboard", "args": { "key": "enter" } }
|
|
130102
130351
|
]}
|
|
130103
130352
|
|
|
130104
130353
|
Example \u2014 TV: move focus right twice then activate (one tv-remote step with a path is cheaper):
|
|
@@ -133798,89 +134047,130 @@ var ARGENT_PROFILER_PREFIX = "__argent_";
|
|
|
133798
134047
|
function isArgentProfilerFunction(name) {
|
|
133799
134048
|
return name.startsWith(ARGENT_PROFILER_PREFIX);
|
|
133800
134049
|
}
|
|
133801
|
-
function buildCpuSampleIndex(cpuProfile
|
|
134050
|
+
function buildCpuSampleIndex(cpuProfile) {
|
|
133802
134051
|
const { nodes, samples, timeDeltas, startTime, endTime } = cpuProfile;
|
|
133803
134052
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
133804
134053
|
for (const node of nodes) {
|
|
133805
134054
|
nodeMap.set(node.id, node);
|
|
133806
134055
|
}
|
|
133807
|
-
const cpuStartMs = startTime / 1e3;
|
|
133808
|
-
let clockOffsetMs = 0;
|
|
133809
|
-
if (firstCommitTimestampMs !== null && firstCommitTimestampMs > 0) {
|
|
133810
|
-
const diff = firstCommitTimestampMs - cpuStartMs;
|
|
133811
|
-
if (Math.abs(diff) > 1e3) {
|
|
133812
|
-
clockOffsetMs = diff;
|
|
133813
|
-
}
|
|
133814
|
-
}
|
|
133815
134056
|
const timestampsMs = new Float64Array(samples.length);
|
|
133816
|
-
|
|
134057
|
+
const intervalStartsMs = new Float64Array(samples.length);
|
|
134058
|
+
let accumulatedUs = 0;
|
|
133817
134059
|
for (let i = 0; i < samples.length; i++) {
|
|
133818
|
-
|
|
133819
|
-
|
|
134060
|
+
const raw = timeDeltas[i];
|
|
134061
|
+
const delta = Number.isFinite(raw) && (raw ?? 0) >= 0 ? raw : 0;
|
|
134062
|
+
intervalStartsMs[i] = accumulatedUs / 1e3;
|
|
134063
|
+
accumulatedUs += delta;
|
|
134064
|
+
timestampsMs[i] = accumulatedUs / 1e3;
|
|
133820
134065
|
}
|
|
133821
134066
|
return {
|
|
134067
|
+
childToParent: buildChildToParent(nodeMap),
|
|
133822
134068
|
timestampsMs,
|
|
134069
|
+
intervalStartsMs,
|
|
133823
134070
|
sampleNodeIds: samples,
|
|
133824
134071
|
nodeMap,
|
|
133825
134072
|
durationMs: (endTime - startTime) / 1e3
|
|
133826
134073
|
};
|
|
133827
134074
|
}
|
|
134075
|
+
var IDLE_FRAME_NAMES = /* @__PURE__ */ new Set(["(idle)", "(program)", "(root)", "[idle]", "[root]"]);
|
|
134076
|
+
function isIdleFrame(name) {
|
|
134077
|
+
return !name || IDLE_FRAME_NAMES.has(name);
|
|
134078
|
+
}
|
|
133828
134079
|
function queryCpuWindow(index, startMs, endMs, topN2 = 5) {
|
|
133829
|
-
const { timestampsMs, sampleNodeIds, nodeMap } = index;
|
|
134080
|
+
const { timestampsMs, intervalStartsMs, sampleNodeIds, nodeMap } = index;
|
|
134081
|
+
const n = timestampsMs.length;
|
|
134082
|
+
const sampleRangeMs = {
|
|
134083
|
+
start: n > 0 ? intervalStartsMs[0] : 0,
|
|
134084
|
+
end: n > 0 ? timestampsMs[n - 1] : 0
|
|
134085
|
+
};
|
|
134086
|
+
const empty2 = {
|
|
134087
|
+
hotspots: [],
|
|
134088
|
+
samplesInWindow: 0,
|
|
134089
|
+
coveredMs: 0,
|
|
134090
|
+
idleMs: 0,
|
|
134091
|
+
sampleRangeMs,
|
|
134092
|
+
medianIntervalMs: 0,
|
|
134093
|
+
maxIntervalMs: 0
|
|
134094
|
+
};
|
|
134095
|
+
if (n === 0) return empty2;
|
|
133830
134096
|
let lo = 0;
|
|
133831
|
-
let hi =
|
|
134097
|
+
let hi = n;
|
|
133832
134098
|
while (lo < hi) {
|
|
133833
134099
|
const mid = lo + hi >>> 1;
|
|
133834
134100
|
if (timestampsMs[mid] < startMs) lo = mid + 1;
|
|
133835
134101
|
else hi = mid;
|
|
133836
134102
|
}
|
|
133837
|
-
const
|
|
133838
|
-
|
|
133839
|
-
|
|
133840
|
-
|
|
134103
|
+
const selfMsByNode = /* @__PURE__ */ new Map();
|
|
134104
|
+
const intervals = [];
|
|
134105
|
+
let samplesInWindow = 0;
|
|
134106
|
+
let coveredMs = 0;
|
|
134107
|
+
let idleMs = 0;
|
|
134108
|
+
let maxIntervalMs = 0;
|
|
134109
|
+
for (let i = lo; i < n; i++) {
|
|
134110
|
+
const from2 = intervalStartsMs[i];
|
|
134111
|
+
if (from2 > endMs) break;
|
|
134112
|
+
const overlap = Math.min(endMs, timestampsMs[i]) - Math.max(startMs, from2);
|
|
134113
|
+
if (overlap <= 0) continue;
|
|
134114
|
+
samplesInWindow++;
|
|
134115
|
+
coveredMs += overlap;
|
|
134116
|
+
intervals.push(timestampsMs[i] - from2);
|
|
134117
|
+
if (timestampsMs[i] - from2 > maxIntervalMs) maxIntervalMs = timestampsMs[i] - from2;
|
|
133841
134118
|
const nodeId = sampleNodeIds[i];
|
|
133842
|
-
|
|
133843
|
-
|
|
133844
|
-
|
|
133845
|
-
|
|
133846
|
-
const windowDurationMs = endMs - startMs;
|
|
133847
|
-
const avgIntervalMs = totalSamples > 1 ? windowDurationMs / totalSamples : 1;
|
|
133848
|
-
const childToParent = /* @__PURE__ */ new Map();
|
|
133849
|
-
for (const node of nodeMap.values()) {
|
|
133850
|
-
for (const childId of node.children ?? []) {
|
|
133851
|
-
childToParent.set(childId, node.id);
|
|
134119
|
+
const node = nodeMap.get(nodeId);
|
|
134120
|
+
if (isIdleFrame(node?.callFrame.functionName)) {
|
|
134121
|
+
idleMs += overlap;
|
|
134122
|
+
continue;
|
|
133852
134123
|
}
|
|
134124
|
+
selfMsByNode.set(nodeId, (selfMsByNode.get(nodeId) ?? 0) + overlap);
|
|
133853
134125
|
}
|
|
133854
|
-
|
|
133855
|
-
|
|
133856
|
-
|
|
134126
|
+
if (samplesInWindow === 0) return { ...empty2, sampleRangeMs };
|
|
134127
|
+
intervals.sort((a, b) => a - b);
|
|
134128
|
+
const medianIntervalMs = intervals[Math.floor(intervals.length / 2)] ?? 0;
|
|
134129
|
+
const childToParent = index.childToParent ?? buildChildToParent(nodeMap);
|
|
134130
|
+
const totalMsByNode = /* @__PURE__ */ new Map();
|
|
134131
|
+
for (const [nodeId, ms] of selfMsByNode) {
|
|
134132
|
+
totalMsByNode.set(nodeId, (totalMsByNode.get(nodeId) ?? 0) + ms);
|
|
133857
134133
|
let current = nodeId;
|
|
134134
|
+
const seen = /* @__PURE__ */ new Set([current]);
|
|
133858
134135
|
while (childToParent.has(current)) {
|
|
133859
134136
|
const parent = childToParent.get(current);
|
|
133860
|
-
|
|
134137
|
+
if (seen.has(parent)) break;
|
|
134138
|
+
seen.add(parent);
|
|
134139
|
+
totalMsByNode.set(parent, (totalMsByNode.get(parent) ?? 0) + ms);
|
|
133861
134140
|
current = parent;
|
|
133862
134141
|
}
|
|
133863
134142
|
}
|
|
133864
134143
|
const entries = [];
|
|
133865
|
-
for (const [nodeId,
|
|
134144
|
+
for (const [nodeId, ms] of selfMsByNode) {
|
|
133866
134145
|
const node = nodeMap.get(nodeId);
|
|
133867
134146
|
if (!node) continue;
|
|
133868
134147
|
const name = node.callFrame.functionName;
|
|
133869
|
-
if (!name || name === "(idle)" || name === "(program)" || name === "(root)" || name === "[idle]" || name === "[root]")
|
|
133870
|
-
continue;
|
|
133871
134148
|
if (isArgentProfilerFunction(name)) continue;
|
|
133872
|
-
const selfMs = Math.round(hits * avgIntervalMs * 100) / 100;
|
|
133873
|
-
const totalMs = Math.round((totalHits.get(nodeId) ?? hits) * avgIntervalMs * 100) / 100;
|
|
133874
134149
|
entries.push({
|
|
133875
134150
|
name,
|
|
133876
|
-
selfMs,
|
|
133877
|
-
totalMs,
|
|
134151
|
+
selfMs: Math.round(ms * 100) / 100,
|
|
134152
|
+
totalMs: Math.round((totalMsByNode.get(nodeId) ?? ms) * 100) / 100,
|
|
133878
134153
|
url: node.callFrame.url || void 0,
|
|
133879
134154
|
lineNumber: node.callFrame.lineNumber >= 0 ? node.callFrame.lineNumber : void 0
|
|
133880
134155
|
});
|
|
133881
134156
|
}
|
|
133882
134157
|
entries.sort((a, b) => b.selfMs - a.selfMs);
|
|
133883
|
-
return
|
|
134158
|
+
return {
|
|
134159
|
+
hotspots: entries.slice(0, topN2),
|
|
134160
|
+
samplesInWindow,
|
|
134161
|
+
coveredMs,
|
|
134162
|
+
idleMs,
|
|
134163
|
+
sampleRangeMs,
|
|
134164
|
+
medianIntervalMs,
|
|
134165
|
+
maxIntervalMs
|
|
134166
|
+
};
|
|
134167
|
+
}
|
|
134168
|
+
function buildChildToParent(nodeMap) {
|
|
134169
|
+
const childToParent = /* @__PURE__ */ new Map();
|
|
134170
|
+
for (const node of nodeMap.values()) {
|
|
134171
|
+
for (const childId of node.children ?? []) childToParent.set(childId, node.id);
|
|
134172
|
+
}
|
|
134173
|
+
return childToParent;
|
|
133884
134174
|
}
|
|
133885
134175
|
function correlateCpuWithCommits(summaries, index, topNPerCommit = 5) {
|
|
133886
134176
|
if (!index) return summaries;
|
|
@@ -133888,28 +134178,35 @@ function correlateCpuWithCommits(summaries, index, topNPerCommit = 5) {
|
|
|
133888
134178
|
if (summary.isMargin) return summary;
|
|
133889
134179
|
const startMs = summary.timestampMs;
|
|
133890
134180
|
const endMs = summary.timestampMs + summary.totalRenderMs;
|
|
133891
|
-
const hotspots = queryCpuWindow(index, startMs, endMs, topNPerCommit);
|
|
134181
|
+
const { hotspots } = queryCpuWindow(index, startMs, endMs, topNPerCommit);
|
|
133892
134182
|
if (hotspots.length === 0) return summary;
|
|
133893
134183
|
return { ...summary, cpuHotspots: hotspots };
|
|
133894
134184
|
});
|
|
133895
134185
|
}
|
|
133896
134186
|
function serializeCpuSampleIndex(index) {
|
|
133897
134187
|
return {
|
|
134188
|
+
version: 2,
|
|
133898
134189
|
timestampsMs: Array.from(index.timestampsMs),
|
|
134190
|
+
intervalStartsMs: Array.from(index.intervalStartsMs),
|
|
133899
134191
|
sampleNodeIds: index.sampleNodeIds,
|
|
133900
134192
|
nodes: [...index.nodeMap.values()],
|
|
133901
134193
|
durationMs: index.durationMs
|
|
133902
134194
|
};
|
|
133903
134195
|
}
|
|
133904
134196
|
function deserializeCpuSampleIndex(raw) {
|
|
134197
|
+
if (raw?.version !== 2 || !Array.isArray(raw.timestampsMs) || !Array.isArray(raw.nodes)) {
|
|
134198
|
+
throw new Error("unsupported CPU sample index format");
|
|
134199
|
+
}
|
|
133905
134200
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
133906
134201
|
for (const node of raw.nodes) {
|
|
133907
134202
|
nodeMap.set(node.id, node);
|
|
133908
134203
|
}
|
|
133909
134204
|
return {
|
|
133910
134205
|
timestampsMs: new Float64Array(raw.timestampsMs),
|
|
134206
|
+
intervalStartsMs: new Float64Array(raw.intervalStartsMs ?? []),
|
|
133911
134207
|
sampleNodeIds: raw.sampleNodeIds,
|
|
133912
134208
|
nodeMap,
|
|
134209
|
+
childToParent: buildChildToParent(nodeMap),
|
|
133913
134210
|
durationMs: raw.durationMs
|
|
133914
134211
|
};
|
|
133915
134212
|
}
|
|
@@ -134212,8 +134509,7 @@ async function runPipeline(input, options) {
|
|
|
134212
134509
|
hotCommitIndices,
|
|
134213
134510
|
input.sessionMeta.unattributedByCommit
|
|
134214
134511
|
);
|
|
134215
|
-
const
|
|
134216
|
-
const cpuSampleIndex = input.flamegraph ? buildCpuSampleIndex(input.flamegraph, firstCommitTs) : null;
|
|
134512
|
+
const cpuSampleIndex = input.flamegraph ? buildCpuSampleIndex(input.flamegraph) : null;
|
|
134217
134513
|
const hotCommitSummaries = correlateCpuWithCommits(rawHotCommitSummaries, cpuSampleIndex);
|
|
134218
134514
|
const preprocessedCommitTree = { ...input.commitTree, commits: preprocessed };
|
|
134219
134515
|
const reduceOutput = reduce(
|
|
@@ -140619,8 +140915,12 @@ init_zod();
|
|
|
140619
140915
|
init_src();
|
|
140620
140916
|
var import_fs18 = require("fs");
|
|
140621
140917
|
var timeWindowSchema = external_exports.object({
|
|
140622
|
-
start: external_exports.coerce.number().describe(
|
|
140623
|
-
|
|
140918
|
+
start: external_exports.coerce.number().describe(
|
|
140919
|
+
"Start of window in ms since profiling started \u2014 the same clock profiler-commit-query prints"
|
|
140920
|
+
),
|
|
140921
|
+
end: external_exports.coerce.number().describe(
|
|
140922
|
+
"End of window in ms since profiling started \u2014 the same clock profiler-commit-query prints"
|
|
140923
|
+
)
|
|
140624
140924
|
});
|
|
140625
140925
|
var zodSchema53 = external_exports.object({
|
|
140626
140926
|
port: external_exports.coerce.number().default(8081).describe("Metro server port"),
|
|
@@ -140630,7 +140930,9 @@ var zodSchema53 = external_exports.object({
|
|
|
140630
140930
|
mode: external_exports.enum(["top_functions", "time_window", "call_tree", "component_cpu"]).describe(
|
|
140631
140931
|
"Query mode: top_functions (global hotspots), time_window (CPU in a time range), call_tree (callers/callees of a function), component_cpu (CPU during a component's commits)"
|
|
140632
140932
|
),
|
|
140633
|
-
time_window_ms: timeWindowSchema.optional().describe(
|
|
140933
|
+
time_window_ms: timeWindowSchema.optional().describe(
|
|
140934
|
+
"Time window filter for time_window mode (ms since profiling started \u2014 the same clock profiler-commit-query prints)"
|
|
140935
|
+
),
|
|
140634
140936
|
component_name: external_exports.string().optional().describe("Component name for component_cpu mode"),
|
|
140635
140937
|
function_name: external_exports.string().optional().describe("Function name for call_tree mode"),
|
|
140636
140938
|
top_n: external_exports.coerce.number().int().positive().default(15).describe("Number of results to return (default 15)"),
|
|
@@ -140663,31 +140965,60 @@ async function getIndex(sessionPaths) {
|
|
|
140663
140965
|
}
|
|
140664
140966
|
const cpuProfile = await readCpuProfile(sessionPaths.cpuProfilePath);
|
|
140665
140967
|
let commitTree = null;
|
|
140666
|
-
let firstCommitTs = null;
|
|
140667
140968
|
if (sessionPaths.commitsPath) {
|
|
140668
140969
|
const onDisk = await readCommitTree(sessionPaths.commitsPath);
|
|
140669
140970
|
commitTree = { commits: onDisk.commits };
|
|
140670
|
-
firstCommitTs = onDisk.commits[0]?.timestamp ?? null;
|
|
140671
140971
|
}
|
|
140672
|
-
return { index: buildCpuSampleIndex(cpuProfile
|
|
140972
|
+
return { index: buildCpuSampleIndex(cpuProfile), commitTree };
|
|
140973
|
+
}
|
|
140974
|
+
function explainEmptyWindow(res, startMs, endMs) {
|
|
140975
|
+
const range = `${res.sampleRangeMs.start.toFixed(1)}\u2013${res.sampleRangeMs.end.toFixed(1)}ms`;
|
|
140976
|
+
const window2 = `${startMs.toFixed(1)}\u2013${endMs.toFixed(1)}ms`;
|
|
140977
|
+
if (res.sampleRangeMs.end === 0 && res.samplesInWindow === 0) {
|
|
140978
|
+
return "_The CPU profile contains no samples. Sampling produced no data for this session \u2014 that is a capture failure, not a measurement of idleness._";
|
|
140979
|
+
}
|
|
140980
|
+
if (endMs < res.sampleRangeMs.start || startMs > res.sampleRangeMs.end) {
|
|
140981
|
+
return `_No CPU samples exist in ${window2} \u2014 that is outside the recorded sample range (${range}). This is a coverage gap, not a measurement: nothing can be concluded about CPU cost here. Sample times are ms since profiling started, the same clock \`profiler-commit-query\` prints._`;
|
|
140982
|
+
}
|
|
140983
|
+
if (res.samplesInWindow > 0) {
|
|
140984
|
+
return `_${res.samplesInWindow} sample(s) covering ${res.coveredMs.toFixed(1)}ms fell inside ${window2}, and all of them were idle \u2014 the JS thread was not executing during this window. Native or UI-thread work would not appear here; use \`native-profiler-start\` for that._`;
|
|
140985
|
+
}
|
|
140986
|
+
return `_No CPU samples fell inside ${window2} (${(endMs - startMs).toFixed(1)}ms wide), although it lies within the recorded range (${range}). The sampler runs roughly every ${res.medianIntervalMs > 0 ? res.medianIntervalMs.toFixed(1) : "13"}ms, so a window this narrow can contain none at all. **Absence of samples is not evidence that this commit was cheap.** Widen the window, or use \`mode=component_cpu\`._`;
|
|
140987
|
+
}
|
|
140988
|
+
function coverageNote(res, startMs, endMs) {
|
|
140989
|
+
const widthMs = endMs - startMs;
|
|
140990
|
+
const lines = [
|
|
140991
|
+
`**Window:** ${startMs.toFixed(1)}ms \u2192 ${endMs.toFixed(1)}ms (${widthMs.toFixed(1)}ms)`,
|
|
140992
|
+
`**Samples:** ${res.samplesInWindow} covering ${res.coveredMs.toFixed(1)}ms` + (res.idleMs > 0 ? `, of which ${res.idleMs.toFixed(1)}ms idle` : "") + ` \u2014 sampling interval ~${res.medianIntervalMs.toFixed(1)}ms. Self-times sum to sampled coverage, not to the window width.`
|
|
140993
|
+
];
|
|
140994
|
+
if (widthMs > 0 && widthMs < 3 * res.medianIntervalMs) {
|
|
140995
|
+
lines.push(
|
|
140996
|
+
`> This window is narrower than ~3 sampling intervals, so every figure carries \xB11 sample (\u2248${res.medianIntervalMs.toFixed(1)}ms).`
|
|
140997
|
+
);
|
|
140998
|
+
}
|
|
140999
|
+
if (res.maxIntervalMs > 50 && res.maxIntervalMs > 5 * res.medianIntervalMs) {
|
|
141000
|
+
lines.push(
|
|
141001
|
+
`> The sampler stalled for ${res.maxIntervalMs.toFixed(1)}ms inside this window; that whole gap is attributed to whichever function was caught by the sample that ended it.`
|
|
141002
|
+
);
|
|
141003
|
+
}
|
|
141004
|
+
return lines.join("\n\n");
|
|
140673
141005
|
}
|
|
140674
141006
|
function renderTopFunctions(index, topN2, startMs, endMs) {
|
|
140675
|
-
const windowStart = startMs ?? index.
|
|
140676
|
-
const windowEnd = endMs ?? index.timestampsMs[index.timestampsMs.length - 1];
|
|
140677
|
-
const
|
|
140678
|
-
if (hotspots.length === 0) return
|
|
141007
|
+
const windowStart = startMs ?? index.intervalStartsMs[0] ?? 0;
|
|
141008
|
+
const windowEnd = endMs ?? index.timestampsMs[index.timestampsMs.length - 1] ?? 0;
|
|
141009
|
+
const res = queryCpuWindow(index, windowStart, windowEnd, topN2);
|
|
141010
|
+
if (res.hotspots.length === 0) return explainEmptyWindow(res, windowStart, windowEnd);
|
|
140679
141011
|
const header = "| Function | Self (ms) | Total (ms) | Location |";
|
|
140680
141012
|
const sep7 = "|---|---|---|---|";
|
|
140681
|
-
const rows = hotspots.map((hs) => {
|
|
141013
|
+
const rows = res.hotspots.map((hs) => {
|
|
140682
141014
|
const loc = hs.url ? `${shortenUrl(hs.url)}${hs.lineNumber != null ? `:${hs.lineNumber}` : ""}` : "\u2014";
|
|
140683
141015
|
return `| \`${hs.name}\` | ${hs.selfMs} | ${hs.totalMs} | ${loc} |`;
|
|
140684
141016
|
});
|
|
140685
|
-
const rangeNote = startMs != null ? `**Window:** ${startMs.toFixed(1)}ms \u2192 ${endMs.toFixed(1)}ms
|
|
140686
|
-
|
|
140687
|
-
` : "";
|
|
140688
141017
|
return `## CPU Hotspots
|
|
140689
141018
|
|
|
140690
|
-
${
|
|
141019
|
+
${coverageNote(res, windowStart, windowEnd)}
|
|
141020
|
+
|
|
141021
|
+
${header}
|
|
140691
141022
|
${sep7}
|
|
140692
141023
|
${rows.join("\n")}`;
|
|
140693
141024
|
}
|
|
@@ -140817,7 +141148,7 @@ function renderComponentCpu(index, commitTree, componentName, topN2) {
|
|
|
140817
141148
|
}
|
|
140818
141149
|
const aggregated = /* @__PURE__ */ new Map();
|
|
140819
141150
|
for (const window2 of commitWindows.values()) {
|
|
140820
|
-
const hotspots = queryCpuWindow(index, window2.start, window2.end, 50);
|
|
141151
|
+
const { hotspots } = queryCpuWindow(index, window2.start, window2.end, 50);
|
|
140821
141152
|
for (const hs of hotspots) {
|
|
140822
141153
|
const existing = aggregated.get(hs.name);
|
|
140823
141154
|
if (existing) {
|
|
@@ -140871,6 +141202,10 @@ Requires react-profiler-stop (and ideally react-profiler-analyze) to have been c
|
|
|
140871
141202
|
Modes:
|
|
140872
141203
|
- top_functions: Global CPU hotspots ranked by self-time. Optional time_window_ms to filter.
|
|
140873
141204
|
- time_window: CPU breakdown for a specific time range (e.g. during a slow commit or hang).
|
|
141205
|
+
|
|
141206
|
+
Self-times are the summed sampling intervals of the samples that landed in the window, so they
|
|
141207
|
+
measure sampled coverage rather than the window's width and do not change if you widen the query.
|
|
141208
|
+
Every table states how many samples it covers and how much of that was idle.
|
|
140874
141209
|
- call_tree: For a given function_name, show its callees and optionally callers.
|
|
140875
141210
|
- component_cpu: For a given component_name, aggregate CPU activity across all its commits.
|
|
140876
141211
|
Use when investigating JS CPU hotspots or correlating CPU cost with specific components.
|
|
@@ -140956,8 +141291,12 @@ Fails if no CPU profile is stored \u2014 run react-profiler-stop first.`,
|
|
|
140956
141291
|
init_zod();
|
|
140957
141292
|
init_src();
|
|
140958
141293
|
var timeRangeSchema = external_exports.object({
|
|
140959
|
-
start: external_exports.coerce.number().describe(
|
|
140960
|
-
|
|
141294
|
+
start: external_exports.coerce.number().describe(
|
|
141295
|
+
"Start of range in ms since profiling started \u2014 the same clock profiler-commit-query prints"
|
|
141296
|
+
),
|
|
141297
|
+
end: external_exports.coerce.number().describe(
|
|
141298
|
+
"End of range in ms since profiling started \u2014 the same clock profiler-commit-query prints"
|
|
141299
|
+
)
|
|
140961
141300
|
});
|
|
140962
141301
|
var zodSchema54 = external_exports.object({
|
|
140963
141302
|
port: external_exports.coerce.number().default(8081).describe("Metro server port"),
|
|
@@ -142643,7 +142982,7 @@ Returns { stopped } - the URNs of the services that were actually live and got s
|
|
|
142643
142982
|
|
|
142644
142983
|
// ../tool-server/src/tools/simulator/stop-metro.ts
|
|
142645
142984
|
init_zod();
|
|
142646
|
-
var
|
|
142985
|
+
var import_node_child_process28 = require("node:child_process");
|
|
142647
142986
|
function parseNetstatListeningPids(netstatOutput, port) {
|
|
142648
142987
|
const pids = /* @__PURE__ */ new Set();
|
|
142649
142988
|
for (const line of netstatOutput.split(/\r?\n/)) {
|
|
@@ -142659,7 +142998,7 @@ function parseNetstatListeningPids(netstatOutput, port) {
|
|
|
142659
142998
|
}
|
|
142660
142999
|
function listeningPids(port) {
|
|
142661
143000
|
if (process.platform === "win32") {
|
|
142662
|
-
const output2 = (0,
|
|
143001
|
+
const output2 = (0, import_node_child_process28.execFileSync)("netstat", ["-ano"], {
|
|
142663
143002
|
encoding: "utf-8",
|
|
142664
143003
|
timeout: 5e3,
|
|
142665
143004
|
// `netstat -ano` dumps every socket on the host; a busy box easily
|
|
@@ -142669,7 +143008,7 @@ function listeningPids(port) {
|
|
|
142669
143008
|
});
|
|
142670
143009
|
return parseNetstatListeningPids(output2, port);
|
|
142671
143010
|
}
|
|
142672
|
-
const output = (0,
|
|
143011
|
+
const output = (0, import_node_child_process28.execFileSync)("lsof", ["-ti", `tcp:${port}`, "-sTCP:LISTEN"], {
|
|
142673
143012
|
encoding: "utf-8",
|
|
142674
143013
|
timeout: 5e3
|
|
142675
143014
|
}).trim();
|
|
@@ -148670,10 +149009,10 @@ async function runSnapshot(env, opts) {
|
|
|
148670
149009
|
}
|
|
148671
149010
|
|
|
148672
149011
|
// ../tool-server/src/utils/status-bar.ts
|
|
148673
|
-
var
|
|
149012
|
+
var import_node_child_process29 = require("node:child_process");
|
|
148674
149013
|
var import_node_util21 = require("node:util");
|
|
148675
149014
|
init_adb();
|
|
148676
|
-
var execFileAsync23 = (0, import_node_util21.promisify)(
|
|
149015
|
+
var execFileAsync23 = (0, import_node_util21.promisify)(import_node_child_process29.execFile);
|
|
148677
149016
|
var DEMO_BROADCAST = "am broadcast -a com.android.systemui.demo";
|
|
148678
149017
|
async function pinStatusBar(device) {
|
|
148679
149018
|
try {
|
|
@@ -150025,7 +150364,7 @@ init_zod();
|
|
|
150025
150364
|
// ../tool-server/src/utils/workspace-reader.ts
|
|
150026
150365
|
var import_promises12 = require("node:fs/promises");
|
|
150027
150366
|
var import_node_path21 = require("node:path");
|
|
150028
|
-
var
|
|
150367
|
+
var import_node_child_process30 = require("node:child_process");
|
|
150029
150368
|
var import_dotenv2 = __toESM(require_main());
|
|
150030
150369
|
async function exists(path40) {
|
|
150031
150370
|
try {
|
|
@@ -150069,7 +150408,7 @@ async function listDir(path40) {
|
|
|
150069
150408
|
var COMMAND_TIMEOUT_MS = 3e3;
|
|
150070
150409
|
function runVersionCommand(cmd, args, cwd) {
|
|
150071
150410
|
return new Promise((resolve11) => {
|
|
150072
|
-
const child = (0,
|
|
150411
|
+
const child = (0, import_node_child_process30.execFile)(cmd, args, { cwd, timeout: COMMAND_TIMEOUT_MS }, (err, stdout) => {
|
|
150073
150412
|
if (err) {
|
|
150074
150413
|
resolve11(null);
|
|
150075
150414
|
return;
|
|
@@ -150358,7 +150697,7 @@ Fails if the workspacePath is not an absolute path or the directory cannot be ac
|
|
|
150358
150697
|
};
|
|
150359
150698
|
|
|
150360
150699
|
// ../tool-server/src/tools/system/update-argent.ts
|
|
150361
|
-
var
|
|
150700
|
+
var import_node_child_process31 = require("node:child_process");
|
|
150362
150701
|
var fs51 = __toESM(require("node:fs"));
|
|
150363
150702
|
var path36 = __toESM(require("node:path"));
|
|
150364
150703
|
init_zod();
|
|
@@ -150493,7 +150832,7 @@ var updateArgentTool = {
|
|
|
150493
150832
|
if (spawnRoot) updateArgs.push("--project-root", spawnRoot);
|
|
150494
150833
|
const cmd = cliEntry ? process.execPath : "argent";
|
|
150495
150834
|
const args = cliEntry ? [cliEntry, ...updateArgs] : updateArgs;
|
|
150496
|
-
const child = (0,
|
|
150835
|
+
const child = (0, import_node_child_process31.spawn)(cmd, args, {
|
|
150497
150836
|
detached: true,
|
|
150498
150837
|
stdio: "ignore",
|
|
150499
150838
|
env: { ...process.env, ARGENT_UPDATE_TRIGGER: "mcp_update" }
|
|
@@ -150633,6 +150972,14 @@ async function resolveOutputDir(params, options) {
|
|
|
150633
150972
|
if (params.outputDir && (probe3 === void 0 || probe3.presentOnHost)) {
|
|
150634
150973
|
return params.outputDir;
|
|
150635
150974
|
}
|
|
150975
|
+
if (params.outputDir) {
|
|
150976
|
+
try {
|
|
150977
|
+
await import_promises13.default.mkdir(params.outputDir);
|
|
150978
|
+
return params.outputDir;
|
|
150979
|
+
} catch (err) {
|
|
150980
|
+
if (err.code === "EEXIST") return params.outputDir;
|
|
150981
|
+
}
|
|
150982
|
+
}
|
|
150636
150983
|
const dir = import_path10.default.join(
|
|
150637
150984
|
import_node_os13.default.tmpdir(),
|
|
150638
150985
|
"argent-screenshot-diff",
|
|
@@ -151411,9 +151758,9 @@ function probeArgentToolServer(host, port, timeoutMs = 500) {
|
|
|
151411
151758
|
|
|
151412
151759
|
// ../tool-server/src/utils/simulator-watcher.ts
|
|
151413
151760
|
var fs53 = __toESM(require("node:fs"));
|
|
151414
|
-
var
|
|
151761
|
+
var import_node_child_process32 = require("node:child_process");
|
|
151415
151762
|
var import_node_util22 = require("node:util");
|
|
151416
|
-
var execFileAsync24 = (0, import_node_util22.promisify)(
|
|
151763
|
+
var execFileAsync24 = (0, import_node_util22.promisify)(import_node_child_process32.execFile);
|
|
151417
151764
|
var POLL_INTERVAL_MS2 = 1e4;
|
|
151418
151765
|
async function getBootedUdidsInSet(deviceSet) {
|
|
151419
151766
|
const { stdout } = await execFileAsync24("xcrun", [
|
|
@@ -151489,7 +151836,7 @@ function startSimulatorWatcher(registry2) {
|
|
|
151489
151836
|
}
|
|
151490
151837
|
|
|
151491
151838
|
// ../tool-server/src/utils/preview-window.ts
|
|
151492
|
-
var
|
|
151839
|
+
var import_node_child_process33 = require("node:child_process");
|
|
151493
151840
|
var fs54 = __toESM(require("node:fs"));
|
|
151494
151841
|
var os14 = __toESM(require("node:os"));
|
|
151495
151842
|
var path38 = __toESM(require("node:path"));
|
|
@@ -151521,10 +151868,10 @@ function ensureLensAppBundle(electronBin) {
|
|
|
151521
151868
|
fs54.copyFileSync(path38.join(realContents, "Info.plist"), plist);
|
|
151522
151869
|
const setPlist = (entry, value) => {
|
|
151523
151870
|
try {
|
|
151524
|
-
(0,
|
|
151871
|
+
(0, import_node_child_process33.execFileSync)("/usr/libexec/PlistBuddy", ["-c", `Set :${entry} ${value}`, plist]);
|
|
151525
151872
|
} catch {
|
|
151526
151873
|
try {
|
|
151527
|
-
(0,
|
|
151874
|
+
(0, import_node_child_process33.execFileSync)("/usr/libexec/PlistBuddy", ["-c", `Add :${entry} string ${value}`, plist]);
|
|
151528
151875
|
} catch {
|
|
151529
151876
|
}
|
|
151530
151877
|
}
|
|
@@ -151583,7 +151930,7 @@ function createPreviewWindowManager(opts = {}) {
|
|
|
151583
151930
|
const wrapperBin = ensureLensAppBundle(electronBin);
|
|
151584
151931
|
const launchBin = wrapperBin ?? electronBin;
|
|
151585
151932
|
const launchArgs = wrapperBin ? ["--no-sandbox", mainScript] : [mainScript];
|
|
151586
|
-
const next = (0,
|
|
151933
|
+
const next = (0, import_node_child_process33.spawn)(launchBin, launchArgs, {
|
|
151587
151934
|
// Strip ELECTRON_RUN_AS_NODE so the child boots as a GUI Electron app, not
|
|
151588
151935
|
// a bare Node runtime (see electronGuiChildEnv). An Electron-based MCP
|
|
151589
151936
|
// host puts it in our env, and inheriting it makes main.cjs crash at
|