@swmansion/argent 0.26.1-next.8 → 0.27.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 +77 -33
- package/dist/client.d.ts +52 -0
- package/dist/client.js +2417 -0
- package/dist/installer.mjs +27 -13
- package/dist/mcp-server.mjs +39 -17
- package/dist/tool-server.cjs +125 -34
- 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 +27 -1
- package/skills/argent-device-interact/SKILL.md +1 -1
package/dist/client.js
ADDED
|
@@ -0,0 +1,2417 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from 'node:module'; import { fileURLToPath as __fileURLToPath } from 'node:url'; import { dirname as __pathDirname } from 'node:path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename);
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
try {
|
|
16
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
|
+
} catch (e) {
|
|
18
|
+
throw mod = 0, e;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
30
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
31
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
32
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
33
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
34
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
35
|
+
mod
|
|
36
|
+
));
|
|
37
|
+
|
|
38
|
+
// ../../node_modules/dotenv/lib/main.js
|
|
39
|
+
var require_main = __commonJS({
|
|
40
|
+
"../../node_modules/dotenv/lib/main.js"(exports, module) {
|
|
41
|
+
var fs6 = __require("fs");
|
|
42
|
+
var path10 = __require("path");
|
|
43
|
+
var os2 = __require("os");
|
|
44
|
+
var crypto2 = __require("crypto");
|
|
45
|
+
var TIPS = [
|
|
46
|
+
"\u25C8 encrypted .env [www.dotenvx.com]",
|
|
47
|
+
"\u25C8 secrets for agents [www.dotenvx.com]",
|
|
48
|
+
"\u2301 auth for agents [www.vestauth.com]",
|
|
49
|
+
"\u2318 custom filepath { path: '/custom/path/.env' }",
|
|
50
|
+
"\u2318 enable debugging { debug: true }",
|
|
51
|
+
"\u2318 override existing { override: true }",
|
|
52
|
+
"\u2318 suppress logs { quiet: true }",
|
|
53
|
+
"\u2318 multiple files { path: ['.env.local', '.env'] }"
|
|
54
|
+
];
|
|
55
|
+
function _getRandomTip() {
|
|
56
|
+
return TIPS[Math.floor(Math.random() * TIPS.length)];
|
|
57
|
+
}
|
|
58
|
+
function parseBoolean(value) {
|
|
59
|
+
if (typeof value === "string") {
|
|
60
|
+
return !["false", "0", "no", "off", ""].includes(value.toLowerCase());
|
|
61
|
+
}
|
|
62
|
+
return Boolean(value);
|
|
63
|
+
}
|
|
64
|
+
function supportsAnsi() {
|
|
65
|
+
return process.stdout.isTTY;
|
|
66
|
+
}
|
|
67
|
+
function dim(text) {
|
|
68
|
+
return supportsAnsi() ? `\x1B[2m${text}\x1B[0m` : text;
|
|
69
|
+
}
|
|
70
|
+
var LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;
|
|
71
|
+
function parse(src) {
|
|
72
|
+
const obj = {};
|
|
73
|
+
let lines = src.toString();
|
|
74
|
+
lines = lines.replace(/\r\n?/mg, "\n");
|
|
75
|
+
let match;
|
|
76
|
+
while ((match = LINE.exec(lines)) != null) {
|
|
77
|
+
const key = match[1];
|
|
78
|
+
let value = match[2] || "";
|
|
79
|
+
value = value.trim();
|
|
80
|
+
const maybeQuote = value[0];
|
|
81
|
+
value = value.replace(/^(['"`])([\s\S]*)\1$/mg, "$2");
|
|
82
|
+
if (maybeQuote === '"') {
|
|
83
|
+
value = value.replace(/\\n/g, "\n");
|
|
84
|
+
value = value.replace(/\\r/g, "\r");
|
|
85
|
+
}
|
|
86
|
+
obj[key] = value;
|
|
87
|
+
}
|
|
88
|
+
return obj;
|
|
89
|
+
}
|
|
90
|
+
function _parseVault(options) {
|
|
91
|
+
options = options || {};
|
|
92
|
+
const vaultPath = _vaultPath(options);
|
|
93
|
+
options.path = vaultPath;
|
|
94
|
+
const result = DotenvModule.configDotenv(options);
|
|
95
|
+
if (!result.parsed) {
|
|
96
|
+
const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
|
|
97
|
+
err.code = "MISSING_DATA";
|
|
98
|
+
throw err;
|
|
99
|
+
}
|
|
100
|
+
const keys = _dotenvKey(options).split(",");
|
|
101
|
+
const length = keys.length;
|
|
102
|
+
let decrypted;
|
|
103
|
+
for (let i = 0; i < length; i++) {
|
|
104
|
+
try {
|
|
105
|
+
const key = keys[i].trim();
|
|
106
|
+
const attrs = _instructions(result, key);
|
|
107
|
+
decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
|
|
108
|
+
break;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (i + 1 >= length) {
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return DotenvModule.parse(decrypted);
|
|
116
|
+
}
|
|
117
|
+
function _warn(message) {
|
|
118
|
+
console.error(`\u26A0 ${message}`);
|
|
119
|
+
}
|
|
120
|
+
function _debug(message) {
|
|
121
|
+
console.log(`\u2506 ${message}`);
|
|
122
|
+
}
|
|
123
|
+
function _log(message) {
|
|
124
|
+
console.log(`\u25C7 ${message}`);
|
|
125
|
+
}
|
|
126
|
+
function _dotenvKey(options) {
|
|
127
|
+
if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
|
|
128
|
+
return options.DOTENV_KEY;
|
|
129
|
+
}
|
|
130
|
+
if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {
|
|
131
|
+
return process.env.DOTENV_KEY;
|
|
132
|
+
}
|
|
133
|
+
return "";
|
|
134
|
+
}
|
|
135
|
+
function _instructions(result, dotenvKey) {
|
|
136
|
+
let uri;
|
|
137
|
+
try {
|
|
138
|
+
uri = new URL(dotenvKey);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (error.code === "ERR_INVALID_URL") {
|
|
141
|
+
const err = new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");
|
|
142
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
const key = uri.password;
|
|
148
|
+
if (!key) {
|
|
149
|
+
const err = new Error("INVALID_DOTENV_KEY: Missing key part");
|
|
150
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
151
|
+
throw err;
|
|
152
|
+
}
|
|
153
|
+
const environment = uri.searchParams.get("environment");
|
|
154
|
+
if (!environment) {
|
|
155
|
+
const err = new Error("INVALID_DOTENV_KEY: Missing environment part");
|
|
156
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
157
|
+
throw err;
|
|
158
|
+
}
|
|
159
|
+
const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
|
|
160
|
+
const ciphertext = result.parsed[environmentKey];
|
|
161
|
+
if (!ciphertext) {
|
|
162
|
+
const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
|
|
163
|
+
err.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
|
|
164
|
+
throw err;
|
|
165
|
+
}
|
|
166
|
+
return { ciphertext, key };
|
|
167
|
+
}
|
|
168
|
+
function _vaultPath(options) {
|
|
169
|
+
let possibleVaultPath = null;
|
|
170
|
+
if (options && options.path && options.path.length > 0) {
|
|
171
|
+
if (Array.isArray(options.path)) {
|
|
172
|
+
for (const filepath of options.path) {
|
|
173
|
+
if (fs6.existsSync(filepath)) {
|
|
174
|
+
possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
possibleVaultPath = path10.resolve(process.cwd(), ".env.vault");
|
|
182
|
+
}
|
|
183
|
+
if (fs6.existsSync(possibleVaultPath)) {
|
|
184
|
+
return possibleVaultPath;
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
function _resolveHome(envPath) {
|
|
189
|
+
return envPath[0] === "~" ? path10.join(os2.homedir(), envPath.slice(1)) : envPath;
|
|
190
|
+
}
|
|
191
|
+
function _configVault(options) {
|
|
192
|
+
const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
193
|
+
const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
194
|
+
if (debug || !quiet) {
|
|
195
|
+
_log("loading env from encrypted .env.vault");
|
|
196
|
+
}
|
|
197
|
+
const parsed = DotenvModule._parseVault(options);
|
|
198
|
+
let processEnv = process.env;
|
|
199
|
+
if (options && options.processEnv != null) {
|
|
200
|
+
processEnv = options.processEnv;
|
|
201
|
+
}
|
|
202
|
+
DotenvModule.populate(processEnv, parsed, options);
|
|
203
|
+
return { parsed };
|
|
204
|
+
}
|
|
205
|
+
function configDotenv(options) {
|
|
206
|
+
const dotenvPath = path10.resolve(process.cwd(), ".env");
|
|
207
|
+
let encoding = "utf8";
|
|
208
|
+
let processEnv = process.env;
|
|
209
|
+
if (options && options.processEnv != null) {
|
|
210
|
+
processEnv = options.processEnv;
|
|
211
|
+
}
|
|
212
|
+
let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
213
|
+
let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
214
|
+
if (options && options.encoding) {
|
|
215
|
+
encoding = options.encoding;
|
|
216
|
+
} else {
|
|
217
|
+
if (debug) {
|
|
218
|
+
_debug("no encoding is specified (UTF-8 is used by default)");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
let optionPaths = [dotenvPath];
|
|
222
|
+
if (options && options.path) {
|
|
223
|
+
if (!Array.isArray(options.path)) {
|
|
224
|
+
optionPaths = [_resolveHome(options.path)];
|
|
225
|
+
} else {
|
|
226
|
+
optionPaths = [];
|
|
227
|
+
for (const filepath of options.path) {
|
|
228
|
+
optionPaths.push(_resolveHome(filepath));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
let lastError;
|
|
233
|
+
const parsedAll = {};
|
|
234
|
+
for (const path11 of optionPaths) {
|
|
235
|
+
try {
|
|
236
|
+
const parsed = DotenvModule.parse(fs6.readFileSync(path11, { encoding }));
|
|
237
|
+
DotenvModule.populate(parsedAll, parsed, options);
|
|
238
|
+
} catch (e) {
|
|
239
|
+
if (debug) {
|
|
240
|
+
_debug(`failed to load ${path11} ${e.message}`);
|
|
241
|
+
}
|
|
242
|
+
lastError = e;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const populated = DotenvModule.populate(processEnv, parsedAll, options);
|
|
246
|
+
debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
|
|
247
|
+
quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
|
|
248
|
+
if (debug || !quiet) {
|
|
249
|
+
const keysCount = Object.keys(populated).length;
|
|
250
|
+
const shortPaths = [];
|
|
251
|
+
for (const filePath of optionPaths) {
|
|
252
|
+
try {
|
|
253
|
+
const relative2 = path10.relative(process.cwd(), filePath);
|
|
254
|
+
shortPaths.push(relative2);
|
|
255
|
+
} catch (e) {
|
|
256
|
+
if (debug) {
|
|
257
|
+
_debug(`failed to load ${filePath} ${e.message}`);
|
|
258
|
+
}
|
|
259
|
+
lastError = e;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
_log(`injected env (${keysCount}) from ${shortPaths.join(",")} ${dim(`// tip: ${_getRandomTip()}`)}`);
|
|
263
|
+
}
|
|
264
|
+
if (lastError) {
|
|
265
|
+
return { parsed: parsedAll, error: lastError };
|
|
266
|
+
} else {
|
|
267
|
+
return { parsed: parsedAll };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function config(options) {
|
|
271
|
+
if (_dotenvKey(options).length === 0) {
|
|
272
|
+
return DotenvModule.configDotenv(options);
|
|
273
|
+
}
|
|
274
|
+
const vaultPath = _vaultPath(options);
|
|
275
|
+
if (!vaultPath) {
|
|
276
|
+
_warn(`you set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}`);
|
|
277
|
+
return DotenvModule.configDotenv(options);
|
|
278
|
+
}
|
|
279
|
+
return DotenvModule._configVault(options);
|
|
280
|
+
}
|
|
281
|
+
function decrypt(encrypted, keyStr) {
|
|
282
|
+
const key = Buffer.from(keyStr.slice(-64), "hex");
|
|
283
|
+
let ciphertext = Buffer.from(encrypted, "base64");
|
|
284
|
+
const nonce = ciphertext.subarray(0, 12);
|
|
285
|
+
const authTag = ciphertext.subarray(-16);
|
|
286
|
+
ciphertext = ciphertext.subarray(12, -16);
|
|
287
|
+
try {
|
|
288
|
+
const aesgcm = crypto2.createDecipheriv("aes-256-gcm", key, nonce);
|
|
289
|
+
aesgcm.setAuthTag(authTag);
|
|
290
|
+
return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
|
|
291
|
+
} catch (error) {
|
|
292
|
+
const isRange = error instanceof RangeError;
|
|
293
|
+
const invalidKeyLength = error.message === "Invalid key length";
|
|
294
|
+
const decryptionFailed = error.message === "Unsupported state or unable to authenticate data";
|
|
295
|
+
if (isRange || invalidKeyLength) {
|
|
296
|
+
const err = new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
|
|
297
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
298
|
+
throw err;
|
|
299
|
+
} else if (decryptionFailed) {
|
|
300
|
+
const err = new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
|
|
301
|
+
err.code = "DECRYPTION_FAILED";
|
|
302
|
+
throw err;
|
|
303
|
+
} else {
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function populate(processEnv, parsed, options = {}) {
|
|
309
|
+
const debug = Boolean(options && options.debug);
|
|
310
|
+
const override = Boolean(options && options.override);
|
|
311
|
+
const populated = {};
|
|
312
|
+
if (typeof parsed !== "object") {
|
|
313
|
+
const err = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
|
|
314
|
+
err.code = "OBJECT_REQUIRED";
|
|
315
|
+
throw err;
|
|
316
|
+
}
|
|
317
|
+
for (const key of Object.keys(parsed)) {
|
|
318
|
+
if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
|
|
319
|
+
if (override === true) {
|
|
320
|
+
processEnv[key] = parsed[key];
|
|
321
|
+
populated[key] = parsed[key];
|
|
322
|
+
}
|
|
323
|
+
if (debug) {
|
|
324
|
+
if (override === true) {
|
|
325
|
+
_debug(`"${key}" is already defined and WAS overwritten`);
|
|
326
|
+
} else {
|
|
327
|
+
_debug(`"${key}" is already defined and was NOT overwritten`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
processEnv[key] = parsed[key];
|
|
332
|
+
populated[key] = parsed[key];
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return populated;
|
|
336
|
+
}
|
|
337
|
+
var DotenvModule = {
|
|
338
|
+
configDotenv,
|
|
339
|
+
_configVault,
|
|
340
|
+
_parseVault,
|
|
341
|
+
config,
|
|
342
|
+
decrypt,
|
|
343
|
+
parse,
|
|
344
|
+
populate
|
|
345
|
+
};
|
|
346
|
+
module.exports.configDotenv = DotenvModule.configDotenv;
|
|
347
|
+
module.exports._configVault = DotenvModule._configVault;
|
|
348
|
+
module.exports._parseVault = DotenvModule._parseVault;
|
|
349
|
+
module.exports.config = DotenvModule.config;
|
|
350
|
+
module.exports.decrypt = DotenvModule.decrypt;
|
|
351
|
+
module.exports.parse = DotenvModule.parse;
|
|
352
|
+
module.exports.populate = DotenvModule.populate;
|
|
353
|
+
module.exports = DotenvModule;
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// ../configuration-core/src/flags.ts
|
|
358
|
+
import * as fs from "node:fs";
|
|
359
|
+
import * as path from "node:path";
|
|
360
|
+
import { homedir } from "node:os";
|
|
361
|
+
var FLAG_REGISTRY = [
|
|
362
|
+
{
|
|
363
|
+
name: "disable-auto-screenshot",
|
|
364
|
+
description: "Disable the automatic screenshot captured after interaction tools."
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
name: "disable-auto-describe",
|
|
368
|
+
description: "Disable the accessibility element tree appended after interaction tools."
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
name: "argent-lens",
|
|
372
|
+
description: "Argent Lens \u2014 the propose_variant / await_user_selection tools and the Electron preview window for staging UI design variants and letting a human pick among them. Off by default while the feature is in development."
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: "artifacts-list-endpoint",
|
|
376
|
+
description: "Expose GET /artifacts for remote artifact inventory consumers."
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
name: "tool-server-event-log",
|
|
380
|
+
description: "Write structured tool-server lifecycle events to a JSONL file."
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
name: "boot-sound",
|
|
384
|
+
description: "Default boot-device's `sound` argument to true so Android emulators boot with audio output instead of muted. Only the argument's default changes \u2014 an explicit `sound: false` on a call still boots muted."
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
name: "microinteractions",
|
|
388
|
+
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."
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
name: "video-watermark",
|
|
392
|
+
description: "Overlay the argent corner watermark on recorded screen videos. On by default; turn it off with `argent disable video-watermark`.",
|
|
393
|
+
defaultEnabled: true
|
|
394
|
+
}
|
|
395
|
+
];
|
|
396
|
+
function getFlagDefinition(name, registry = FLAG_REGISTRY) {
|
|
397
|
+
return registry.find((def) => def.name === name);
|
|
398
|
+
}
|
|
399
|
+
var PROJECT_MARKERS = [".argent", ".git", "package.json"];
|
|
400
|
+
function findProjectRoot(startDir) {
|
|
401
|
+
let current = path.resolve(startDir);
|
|
402
|
+
while (true) {
|
|
403
|
+
for (const marker of PROJECT_MARKERS) {
|
|
404
|
+
if (fs.existsSync(path.join(current, marker))) return current;
|
|
405
|
+
}
|
|
406
|
+
const parent = path.dirname(current);
|
|
407
|
+
if (parent === current) return null;
|
|
408
|
+
current = parent;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
function resolveProjectRoot(startDir) {
|
|
412
|
+
return findProjectRoot(startDir) ?? path.resolve(startDir);
|
|
413
|
+
}
|
|
414
|
+
function getFlagsPath(scope, options = {}) {
|
|
415
|
+
const home = options.homeDir ?? homedir();
|
|
416
|
+
if (scope === "global") {
|
|
417
|
+
return path.join(home, ".argent", "flags.json");
|
|
418
|
+
}
|
|
419
|
+
const cwd = options.cwd ?? process.cwd();
|
|
420
|
+
return path.join(resolveProjectRoot(cwd), ".argent", "flags.json");
|
|
421
|
+
}
|
|
422
|
+
function readFlagsFile(filePath) {
|
|
423
|
+
let raw;
|
|
424
|
+
try {
|
|
425
|
+
raw = fs.readFileSync(filePath, "utf8");
|
|
426
|
+
} catch {
|
|
427
|
+
return {};
|
|
428
|
+
}
|
|
429
|
+
let parsed;
|
|
430
|
+
try {
|
|
431
|
+
parsed = JSON.parse(raw);
|
|
432
|
+
} catch {
|
|
433
|
+
return {};
|
|
434
|
+
}
|
|
435
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
436
|
+
const flags = parsed.flags;
|
|
437
|
+
if (!flags || typeof flags !== "object" || Array.isArray(flags)) return {};
|
|
438
|
+
const out = {};
|
|
439
|
+
for (const [k, v] of Object.entries(flags)) {
|
|
440
|
+
if (typeof v === "boolean") out[k] = v;
|
|
441
|
+
}
|
|
442
|
+
return out;
|
|
443
|
+
}
|
|
444
|
+
function readFlags(scope, options = {}) {
|
|
445
|
+
return readFlagsFile(getFlagsPath(scope, options));
|
|
446
|
+
}
|
|
447
|
+
function isFlagEnabled(name, options = {}) {
|
|
448
|
+
const projectFlags = readFlags("project", options);
|
|
449
|
+
if (Object.hasOwn(projectFlags, name)) return projectFlags[name];
|
|
450
|
+
const globalFlags = readFlags("global", options);
|
|
451
|
+
if (Object.hasOwn(globalFlags, name)) return globalFlags[name];
|
|
452
|
+
return options.default ?? false;
|
|
453
|
+
}
|
|
454
|
+
function isFeatureEnabled(name, options = {}, registry = FLAG_REGISTRY) {
|
|
455
|
+
return isFlagEnabled(name, {
|
|
456
|
+
...options,
|
|
457
|
+
default: getFlagDefinition(name, registry)?.defaultEnabled ?? false
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// ../configuration-core/src/paths.ts
|
|
462
|
+
import * as os from "node:os";
|
|
463
|
+
import * as path2 from "node:path";
|
|
464
|
+
function nonEmpty(value) {
|
|
465
|
+
if (value == void 0) return null;
|
|
466
|
+
const trimmed = value.trim();
|
|
467
|
+
return trimmed === "" ? null : value;
|
|
468
|
+
}
|
|
469
|
+
function resolveHomeDir(options = {}) {
|
|
470
|
+
if (options.homeDir) return options.homeDir;
|
|
471
|
+
return process.platform === "win32" ? nonEmpty(process.env.USERPROFILE) ?? os.homedir() : nonEmpty(process.env.HOME) ?? os.homedir();
|
|
472
|
+
}
|
|
473
|
+
function argentHomeDir() {
|
|
474
|
+
return path2.join(resolveHomeDir(), ".argent");
|
|
475
|
+
}
|
|
476
|
+
function configDir(scope = "global", options = {}) {
|
|
477
|
+
if (scope === "global") {
|
|
478
|
+
return path2.join(resolveHomeDir(options), ".argent");
|
|
479
|
+
}
|
|
480
|
+
const cwd = options.cwd ?? process.cwd();
|
|
481
|
+
return path2.join(resolveProjectRoot(cwd), ".argent");
|
|
482
|
+
}
|
|
483
|
+
function configFilePath(scope = "global", options = {}) {
|
|
484
|
+
return path2.join(configDir(scope, options), "config.json");
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ../configuration-core/src/config.ts
|
|
488
|
+
import * as crypto from "node:crypto";
|
|
489
|
+
import * as fs2 from "node:fs";
|
|
490
|
+
import * as path3 from "node:path";
|
|
491
|
+
function readConfigObject(scope = "global", options = {}) {
|
|
492
|
+
try {
|
|
493
|
+
const raw = fs2.readFileSync(configFilePath(scope, options), "utf8");
|
|
494
|
+
const json = JSON.parse(raw);
|
|
495
|
+
if (json && typeof json === "object" && !Array.isArray(json)) {
|
|
496
|
+
return json;
|
|
497
|
+
}
|
|
498
|
+
} catch {
|
|
499
|
+
}
|
|
500
|
+
return {};
|
|
501
|
+
}
|
|
502
|
+
var FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
503
|
+
function splitKey(dottedKey) {
|
|
504
|
+
const parts = dottedKey.split(".");
|
|
505
|
+
if (parts.length === 0 || parts.some((p) => p === "")) {
|
|
506
|
+
throw new Error(`Invalid config key "${dottedKey}": empty path segment`);
|
|
507
|
+
}
|
|
508
|
+
for (const p of parts) {
|
|
509
|
+
if (FORBIDDEN_SEGMENTS.has(p)) {
|
|
510
|
+
throw new Error(`Invalid config key "${dottedKey}": forbidden segment "${p}"`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return parts;
|
|
514
|
+
}
|
|
515
|
+
function isPlainObject(value) {
|
|
516
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
517
|
+
}
|
|
518
|
+
function getAtPath(obj, dottedKey) {
|
|
519
|
+
const parts = splitKey(dottedKey);
|
|
520
|
+
let cur = obj;
|
|
521
|
+
for (const part of parts) {
|
|
522
|
+
if (!isPlainObject(cur)) return void 0;
|
|
523
|
+
cur = cur[part];
|
|
524
|
+
}
|
|
525
|
+
return cur;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// ../configuration-core/src/secrets.ts
|
|
529
|
+
var import_dotenv = __toESM(require_main(), 1);
|
|
530
|
+
import * as fs3 from "node:fs";
|
|
531
|
+
import * as path4 from "node:path";
|
|
532
|
+
|
|
533
|
+
// ../configuration-core/src/merge.ts
|
|
534
|
+
function mergeRestrictive(local, global) {
|
|
535
|
+
if (local === void 0) return global;
|
|
536
|
+
if (global === void 0) return local;
|
|
537
|
+
if (typeof local === "boolean" && typeof global === "boolean") {
|
|
538
|
+
return local && global;
|
|
539
|
+
}
|
|
540
|
+
if (typeof local === "number" && typeof global === "number") {
|
|
541
|
+
return Math.min(local, global);
|
|
542
|
+
}
|
|
543
|
+
return local;
|
|
544
|
+
}
|
|
545
|
+
function toArray(value) {
|
|
546
|
+
return Array.isArray(value) ? value : null;
|
|
547
|
+
}
|
|
548
|
+
function mergeUnion(local, global) {
|
|
549
|
+
const l = toArray(local);
|
|
550
|
+
const g = toArray(global);
|
|
551
|
+
if (l === null && g === null) return local ?? global;
|
|
552
|
+
const merged = [...g ?? [], ...l ?? []];
|
|
553
|
+
return Array.from(new Set(merged));
|
|
554
|
+
}
|
|
555
|
+
function mergeIntersection(local, global) {
|
|
556
|
+
const l = toArray(local);
|
|
557
|
+
const g = toArray(global);
|
|
558
|
+
if (l === null && g === null) return local ?? global;
|
|
559
|
+
if (l === null) return global;
|
|
560
|
+
if (g === null) return local;
|
|
561
|
+
const globalSet = new Set(g);
|
|
562
|
+
return l.filter((item) => globalSet.has(item));
|
|
563
|
+
}
|
|
564
|
+
function applyMergePolicy(policy, local, global) {
|
|
565
|
+
if (typeof policy === "function") return policy({ local, global });
|
|
566
|
+
switch (policy) {
|
|
567
|
+
case "prioritize-local":
|
|
568
|
+
return local ?? global;
|
|
569
|
+
case "prioritize-global":
|
|
570
|
+
return global ?? local;
|
|
571
|
+
case "prioritize-restrictive":
|
|
572
|
+
return mergeRestrictive(local, global);
|
|
573
|
+
case "union":
|
|
574
|
+
return mergeUnion(local, global);
|
|
575
|
+
case "intersection":
|
|
576
|
+
return mergeIntersection(local, global);
|
|
577
|
+
default: {
|
|
578
|
+
const _exhaustive = policy;
|
|
579
|
+
return _exhaustive;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ../configuration-core/src/config-schema.ts
|
|
585
|
+
import { isAbsolute } from "node:path";
|
|
586
|
+
function asBoolean(raw) {
|
|
587
|
+
return typeof raw === "boolean" ? raw : void 0;
|
|
588
|
+
}
|
|
589
|
+
function asString(raw) {
|
|
590
|
+
if (typeof raw !== "string") return void 0;
|
|
591
|
+
const trimmed = raw.trim();
|
|
592
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
593
|
+
}
|
|
594
|
+
function asPositiveInteger(raw) {
|
|
595
|
+
return typeof raw === "number" && Number.isSafeInteger(raw) && raw > 0 ? raw : void 0;
|
|
596
|
+
}
|
|
597
|
+
var MIN_SCRIPT_HEAP_LIMIT_MB = 32;
|
|
598
|
+
var MIN_SCRIPT_TIMEOUT_MS = 100;
|
|
599
|
+
function asStringArray(raw) {
|
|
600
|
+
if (!Array.isArray(raw)) return void 0;
|
|
601
|
+
const out = [];
|
|
602
|
+
for (const item of raw) {
|
|
603
|
+
if (typeof item === "string" && item.trim() !== "") out.push(item.trim());
|
|
604
|
+
}
|
|
605
|
+
return out;
|
|
606
|
+
}
|
|
607
|
+
var CONFIG_SCHEMA = [
|
|
608
|
+
{
|
|
609
|
+
key: "telemetry.enabled",
|
|
610
|
+
description: "Whether anonymous opt-out telemetry is enabled (on by default; environment opt-outs like DO_NOT_TRACK are not reflected here \u2014 `argent telemetry status` shows effective consent). `false` in either scope wins, so a committed project opt-out holds for every teammate.",
|
|
611
|
+
scopes: ["project", "global"],
|
|
612
|
+
parse: asBoolean,
|
|
613
|
+
merge: "prioritize-restrictive",
|
|
614
|
+
// Opt-out: consent.ts reads an unstored value as enabled, so the config
|
|
615
|
+
// surface must show the same rather than "(unset)".
|
|
616
|
+
default: true,
|
|
617
|
+
// Opt-in/out goes through the dedicated command so the live client is
|
|
618
|
+
// drained/reset, not just the file rewritten.
|
|
619
|
+
manageCommand: "argent telemetry"
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
key: "allowlist.enabled",
|
|
623
|
+
description: "Whether `argent update` re-applies editor auto-approve allowlist rules. Unset (the default) keeps the current behavior: update refreshes the rules for editors that already have argent configured. Set to `false` to keep update from touching editor allowlists. `false` in either scope wins, so a committed project opt-out holds for every teammate.",
|
|
624
|
+
scopes: ["project", "global"],
|
|
625
|
+
parse: asBoolean,
|
|
626
|
+
merge: "prioritize-restrictive",
|
|
627
|
+
example: "false"
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
key: "lens.agent",
|
|
631
|
+
description: "Coding-agent id remembered by `argent lens` to skip the picker.",
|
|
632
|
+
scopes: ["project", "global"],
|
|
633
|
+
parse: asString,
|
|
634
|
+
merge: "prioritize-local",
|
|
635
|
+
example: "claude"
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
key: "ios.additionalDeviceSets",
|
|
639
|
+
description: "Additional CoreSimulator device-set directories whose simulators argent should see alongside the default set. Absolute paths (or ~/\u2026); relative entries resolve against the project root (project scope) or home (global scope).",
|
|
640
|
+
scopes: ["project", "global"],
|
|
641
|
+
parse: asStringArray,
|
|
642
|
+
// Additive rather than shadowing: global baseline first, project extras
|
|
643
|
+
// after, deduplicated. `getAdditionalIosDeviceSets` re-implements this union
|
|
644
|
+
// (path resolution must precede dedup) and guards on the preset staying "union".
|
|
645
|
+
merge: "union",
|
|
646
|
+
example: '["~/DeviceSets/ci"]'
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
key: "android.sdkRoot",
|
|
650
|
+
description: "Android SDK root argent uses instead of `ANDROID_HOME`, for hosts whose SDK is not where the environment says (Nix, custom layouts). Absolute or `~/`-prefixed. Applies to the tool-server and to the simulator-server it starts. `adb` on PATH still wins, as it does with the environment variable.",
|
|
651
|
+
scopes: ["project", "global"],
|
|
652
|
+
parse: (raw) => {
|
|
653
|
+
const value = asString(raw);
|
|
654
|
+
return value !== void 0 && (value === "~" || value.startsWith("~/") || isAbsolute(value)) ? value : void 0;
|
|
655
|
+
},
|
|
656
|
+
expected: "an absolute path, or one starting with `~/`",
|
|
657
|
+
merge: "prioritize-local",
|
|
658
|
+
example: "~/Library/Android/sdk"
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
key: "recordings.directory",
|
|
662
|
+
description: "Directory where finished screen recordings (mp4) are saved on the client host. Absolute, `~`-prefixed, or relative to the project root (home dir when not in a project). Unset \u21D2 `.argent/recordings` under the project root.",
|
|
663
|
+
scopes: ["project", "global"],
|
|
664
|
+
parse: asString,
|
|
665
|
+
// Resolved on the client (the machine the mp4 is persisted to), so with a
|
|
666
|
+
// remote `argent link` tool-server it is the *client's* config that decides.
|
|
667
|
+
merge: "prioritize-local",
|
|
668
|
+
example: "~/Movies/argent"
|
|
669
|
+
},
|
|
670
|
+
// Global-scope only: a checked-in `.argent/config.json` must not raise the
|
|
671
|
+
// ceiling on how much of the machine a script step may occupy. `merge` is
|
|
672
|
+
// nominal here — the project scope of a global-only key is never read.
|
|
673
|
+
{
|
|
674
|
+
key: "scripts.maxTimeoutMs",
|
|
675
|
+
description: `Upper bound, in milliseconds, on the time limit a flow \`script\` step may ask for (default 300000 \u2014 five minutes). Bounds how long one script can occupy the host. Values below ${MIN_SCRIPT_TIMEOUT_MS} ms are refused: the step starts a Node process before the script runs, so a smaller ceiling ends a script that did nothing wrong.`,
|
|
676
|
+
scopes: ["global"],
|
|
677
|
+
parse: (raw) => {
|
|
678
|
+
const value = asPositiveInteger(raw);
|
|
679
|
+
return value !== void 0 && value >= MIN_SCRIPT_TIMEOUT_MS ? value : void 0;
|
|
680
|
+
},
|
|
681
|
+
expected: `a whole number of milliseconds, at least ${MIN_SCRIPT_TIMEOUT_MS}`,
|
|
682
|
+
merge: "prioritize-global",
|
|
683
|
+
default: 5 * 6e4,
|
|
684
|
+
example: "300000"
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
key: "scripts.heapLimitMb",
|
|
688
|
+
description: `Old-space heap limit, in MiB, given to each flow \`script\` process (default 512). Values below ${MIN_SCRIPT_HEAP_LIMIT_MB} MiB are refused: that is already below what importing a real npm dependency needs, and under about 5 MiB the process dies inside V8's own startup before any script runs.`,
|
|
689
|
+
scopes: ["global"],
|
|
690
|
+
parse: (raw) => {
|
|
691
|
+
const value = asPositiveInteger(raw);
|
|
692
|
+
return value !== void 0 && value >= MIN_SCRIPT_HEAP_LIMIT_MB ? value : void 0;
|
|
693
|
+
},
|
|
694
|
+
expected: `a whole number of MiB, at least ${MIN_SCRIPT_HEAP_LIMIT_MB}`,
|
|
695
|
+
merge: "prioritize-global",
|
|
696
|
+
default: 512,
|
|
697
|
+
example: "512"
|
|
698
|
+
}
|
|
699
|
+
];
|
|
700
|
+
function getConfigDefinition(key, registry = CONFIG_SCHEMA) {
|
|
701
|
+
return registry.find((def) => def.key === key);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// ../configuration-core/src/config-access.ts
|
|
705
|
+
import * as path5 from "node:path";
|
|
706
|
+
function readScopeValue(def, scope, options) {
|
|
707
|
+
if (!def.scopes.includes(scope)) return void 0;
|
|
708
|
+
const raw = getAtPath(readConfigObject(scope, options), def.key);
|
|
709
|
+
return raw === void 0 ? void 0 : def.parse(raw);
|
|
710
|
+
}
|
|
711
|
+
function getConfigValue(def, options = {}) {
|
|
712
|
+
const local = readScopeValue(def, "project", options);
|
|
713
|
+
const global = readScopeValue(def, "global", options);
|
|
714
|
+
const merged = applyMergePolicy(def.merge, local, global);
|
|
715
|
+
return merged ?? def.default;
|
|
716
|
+
}
|
|
717
|
+
function getConfigValueByKey(key, options = {}, registry = CONFIG_SCHEMA) {
|
|
718
|
+
const def = requireDefinition(key, registry);
|
|
719
|
+
return getConfigValue(def, options);
|
|
720
|
+
}
|
|
721
|
+
function requireDefinition(key, registry = CONFIG_SCHEMA) {
|
|
722
|
+
const def = getConfigDefinition(key, registry);
|
|
723
|
+
if (!def) {
|
|
724
|
+
throw new UnknownConfigKeyError(key);
|
|
725
|
+
}
|
|
726
|
+
return def;
|
|
727
|
+
}
|
|
728
|
+
var UnknownConfigKeyError = class extends Error {
|
|
729
|
+
constructor(key) {
|
|
730
|
+
super(`Unknown configuration key "${key}".`);
|
|
731
|
+
this.key = key;
|
|
732
|
+
this.name = "UnknownConfigKeyError";
|
|
733
|
+
}
|
|
734
|
+
key;
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
// ../argent-tools-client/src/launcher.ts
|
|
738
|
+
import * as net from "node:net";
|
|
739
|
+
import * as fs4 from "node:fs";
|
|
740
|
+
import * as path6 from "node:path";
|
|
741
|
+
import * as readline from "node:readline";
|
|
742
|
+
import { homedir as homedir3 } from "node:os";
|
|
743
|
+
import { spawn, execFileSync } from "node:child_process";
|
|
744
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
745
|
+
import { mkdir, writeFile, readFile, readdir, unlink, rename, chmod } from "node:fs/promises";
|
|
746
|
+
var STATE_DIR = path6.join(homedir3(), ".argent");
|
|
747
|
+
var STATE_FILE = path6.join(STATE_DIR, "tool-server.json");
|
|
748
|
+
var LOG_FILE = path6.join(STATE_DIR, "tool-server.log");
|
|
749
|
+
var LOCK_FILE = path6.join(STATE_DIR, "tool-server.lock");
|
|
750
|
+
var AUTH_TOKEN_BYTES = 32;
|
|
751
|
+
var AUTH_TOKEN_ENV = "ARGENT_AUTH_TOKEN";
|
|
752
|
+
var AUTOSPAWN_IDLE_TIMEOUT_MINUTES = 30;
|
|
753
|
+
function buildToolsServerEnv(paths, port, baseEnv = process.env, options = {}) {
|
|
754
|
+
const env = {
|
|
755
|
+
...baseEnv,
|
|
756
|
+
ARGENT_PORT: String(port),
|
|
757
|
+
ARGENT_SIMULATOR_SERVER_DIR: paths.simulatorServerDir,
|
|
758
|
+
ARGENT_NATIVE_DEVTOOLS_DIR: paths.nativeDevtoolsDir
|
|
759
|
+
};
|
|
760
|
+
if (options.host !== void 0) env.ARGENT_HOST = options.host;
|
|
761
|
+
if (options.idleTimeoutMinutes !== void 0) {
|
|
762
|
+
env.ARGENT_IDLE_TIMEOUT_MINUTES = String(options.idleTimeoutMinutes);
|
|
763
|
+
}
|
|
764
|
+
if (options.token) env[AUTH_TOKEN_ENV] = options.token;
|
|
765
|
+
if (paths.installKind) env.ARGENT_INSTALL_KIND = paths.installKind;
|
|
766
|
+
if (paths.installProjectRoot) env.ARGENT_PROJECT_ROOT = paths.installProjectRoot;
|
|
767
|
+
return env;
|
|
768
|
+
}
|
|
769
|
+
function generateToken() {
|
|
770
|
+
return randomBytes(AUTH_TOKEN_BYTES).toString("hex");
|
|
771
|
+
}
|
|
772
|
+
function findFreePort() {
|
|
773
|
+
return new Promise((resolve7, reject) => {
|
|
774
|
+
const srv = net.createServer();
|
|
775
|
+
srv.listen(0, "127.0.0.1", () => {
|
|
776
|
+
const addr = srv.address();
|
|
777
|
+
if (!addr || typeof addr !== "object") {
|
|
778
|
+
srv.close(() => reject(new Error("Could not bind to find free port")));
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
const port = addr.port;
|
|
782
|
+
srv.close((err) => {
|
|
783
|
+
if (err) reject(err);
|
|
784
|
+
else resolve7(port);
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
srv.on("error", reject);
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
function isVersionNewer(a, b) {
|
|
791
|
+
const parse = (v) => {
|
|
792
|
+
const [core = "", ...preParts] = v.split("+")[0].split("-");
|
|
793
|
+
const nums = core.split(".").map((n) => Number.parseInt(n, 10));
|
|
794
|
+
if (nums.length === 0 || nums.some((n) => Number.isNaN(n))) return null;
|
|
795
|
+
return { nums, pre: preParts.length > 0 ? preParts.join("-").split(".") : [] };
|
|
796
|
+
};
|
|
797
|
+
const pa = parse(a);
|
|
798
|
+
const pb = parse(b);
|
|
799
|
+
if (!pa || !pb) return false;
|
|
800
|
+
for (let i = 0; i < 3; i++) {
|
|
801
|
+
const x = pa.nums[i] ?? 0;
|
|
802
|
+
const y = pb.nums[i] ?? 0;
|
|
803
|
+
if (x !== y) return x > y;
|
|
804
|
+
}
|
|
805
|
+
if (pa.pre.length === 0 && pb.pre.length === 0) return false;
|
|
806
|
+
if (pa.pre.length === 0) return true;
|
|
807
|
+
if (pb.pre.length === 0) return false;
|
|
808
|
+
for (let i = 0; i < Math.max(pa.pre.length, pb.pre.length); i++) {
|
|
809
|
+
const x = pa.pre[i];
|
|
810
|
+
const y = pb.pre[i];
|
|
811
|
+
if (x === void 0) return false;
|
|
812
|
+
if (y === void 0) return true;
|
|
813
|
+
if (x === y) continue;
|
|
814
|
+
const xn = /^\d+$/.test(x) ? Number.parseInt(x, 10) : null;
|
|
815
|
+
const yn = /^\d+$/.test(y) ? Number.parseInt(y, 10) : null;
|
|
816
|
+
if (xn !== null && yn !== null) return xn > yn;
|
|
817
|
+
if (xn !== null) return false;
|
|
818
|
+
if (yn !== null) return true;
|
|
819
|
+
return x > y;
|
|
820
|
+
}
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
function readBundlePackageVersion(bundlePath) {
|
|
824
|
+
try {
|
|
825
|
+
const pkg = JSON.parse(
|
|
826
|
+
fs4.readFileSync(path6.join(path6.dirname(bundlePath), "..", "package.json"), "utf8")
|
|
827
|
+
);
|
|
828
|
+
return typeof pkg.version === "string" ? pkg.version : void 0;
|
|
829
|
+
} catch {
|
|
830
|
+
return void 0;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
function isProcessAlive(pid) {
|
|
834
|
+
try {
|
|
835
|
+
process.kill(pid, 0);
|
|
836
|
+
return true;
|
|
837
|
+
} catch {
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
function healthCheckHost(host) {
|
|
842
|
+
if (host === "0.0.0.0" || host === "") return "127.0.0.1";
|
|
843
|
+
if (host === "::" || host === "::0") return "::1";
|
|
844
|
+
return host;
|
|
845
|
+
}
|
|
846
|
+
function formatUrl(host, port) {
|
|
847
|
+
const h = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
848
|
+
return `http://${h}:${port}`;
|
|
849
|
+
}
|
|
850
|
+
function authHeaders(token) {
|
|
851
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
852
|
+
}
|
|
853
|
+
async function isToolsServerHealthy(port, host = "127.0.0.1", timeoutMs = 2e3, token) {
|
|
854
|
+
const controller = new AbortController();
|
|
855
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
856
|
+
try {
|
|
857
|
+
const res = await fetch(`${formatUrl(healthCheckHost(host), port)}/tools`, {
|
|
858
|
+
signal: controller.signal,
|
|
859
|
+
headers: authHeaders(token)
|
|
860
|
+
});
|
|
861
|
+
await res.body?.cancel().catch(() => {
|
|
862
|
+
});
|
|
863
|
+
return res.ok;
|
|
864
|
+
} catch {
|
|
865
|
+
return false;
|
|
866
|
+
} finally {
|
|
867
|
+
clearTimeout(timer);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
var SPAWN_READY_TIMEOUT_MS = 15e3;
|
|
871
|
+
function killSpawnedChild(child, pid) {
|
|
872
|
+
try {
|
|
873
|
+
process.kill(-pid, "SIGKILL");
|
|
874
|
+
} catch {
|
|
875
|
+
try {
|
|
876
|
+
child.kill("SIGKILL");
|
|
877
|
+
} catch {
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
function spawnToolsServer(paths, port, options = {}) {
|
|
882
|
+
return new Promise((resolve7, reject) => {
|
|
883
|
+
let logFd;
|
|
884
|
+
try {
|
|
885
|
+
fs4.mkdirSync(STATE_DIR, { recursive: true });
|
|
886
|
+
logFd = fs4.openSync(LOG_FILE, "a");
|
|
887
|
+
} catch {
|
|
888
|
+
logFd = fs4.openSync("/dev/null", "w");
|
|
889
|
+
}
|
|
890
|
+
const { bun, deno, electron } = process.versions;
|
|
891
|
+
const nodeBin = !(bun || deno || electron) && fs4.existsSync(process.execPath) ? process.execPath : "node";
|
|
892
|
+
const child = spawn(nodeBin, [paths.bundlePath, "start"], {
|
|
893
|
+
detached: true,
|
|
894
|
+
stdio: ["ignore", "pipe", logFd],
|
|
895
|
+
env: buildToolsServerEnv(paths, port, process.env, options)
|
|
896
|
+
});
|
|
897
|
+
child.unref();
|
|
898
|
+
const pid = child.pid;
|
|
899
|
+
if (!pid) {
|
|
900
|
+
child.once(
|
|
901
|
+
"error",
|
|
902
|
+
(err) => reject(
|
|
903
|
+
new Error(
|
|
904
|
+
err.code === "ENOENT" && nodeBin === "node" ? "Could not start the argent tool-server: `node` was not found on PATH. Install Node.js 20+ or add it to PATH." : `Could not start the argent tool-server: ${err.message}`
|
|
905
|
+
)
|
|
906
|
+
)
|
|
907
|
+
);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
let settled = false;
|
|
911
|
+
const settle = (fn) => {
|
|
912
|
+
if (settled) return;
|
|
913
|
+
settled = true;
|
|
914
|
+
fn();
|
|
915
|
+
};
|
|
916
|
+
const rejectAndKill = (err) => settle(() => {
|
|
917
|
+
killSpawnedChild(child, pid);
|
|
918
|
+
reject(err);
|
|
919
|
+
});
|
|
920
|
+
const rl = readline.createInterface({ input: child.stdout });
|
|
921
|
+
rl.on("line", (line) => {
|
|
922
|
+
const match = line.match(/Tools server listening on http:\/\/.+:(\d+)/);
|
|
923
|
+
if (match) {
|
|
924
|
+
const actualPort = parseInt(match[1], 10);
|
|
925
|
+
rl.close();
|
|
926
|
+
child.stdout?.resume();
|
|
927
|
+
child.stdout?.unref?.();
|
|
928
|
+
settle(() => resolve7({ port: actualPort, pid }));
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
child.on("error", (err) => {
|
|
932
|
+
rl.close();
|
|
933
|
+
rejectAndKill(err);
|
|
934
|
+
});
|
|
935
|
+
child.on("exit", (code) => {
|
|
936
|
+
rl.close();
|
|
937
|
+
settle(() => reject(new Error(`tool-server exited with code ${code} before becoming ready`)));
|
|
938
|
+
});
|
|
939
|
+
const timer = setTimeout(() => {
|
|
940
|
+
rl.close();
|
|
941
|
+
rejectAndKill(new Error("Timed out waiting for tools server to become ready"));
|
|
942
|
+
}, options.readyTimeoutMs ?? SPAWN_READY_TIMEOUT_MS);
|
|
943
|
+
rl.on("close", () => clearTimeout(timer));
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
function stateFileForBundle(bundlePath) {
|
|
947
|
+
const key = createHash("sha256").update(bundlePath).digest("hex").slice(0, 12);
|
|
948
|
+
return path6.join(STATE_DIR, `tool-server-${key}.json`);
|
|
949
|
+
}
|
|
950
|
+
async function readStateFile(file) {
|
|
951
|
+
try {
|
|
952
|
+
const raw = await readFile(file, "utf8");
|
|
953
|
+
return JSON.parse(raw);
|
|
954
|
+
} catch {
|
|
955
|
+
return null;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
async function readToolsServerState(bundlePath) {
|
|
959
|
+
if (bundlePath === void 0) return readStateFile(STATE_FILE);
|
|
960
|
+
const own = await readStateFile(stateFileForBundle(bundlePath));
|
|
961
|
+
if (own) return own;
|
|
962
|
+
const legacy = await readStateFile(STATE_FILE);
|
|
963
|
+
return legacy && legacy.bundlePath === bundlePath ? legacy : null;
|
|
964
|
+
}
|
|
965
|
+
async function writeToolsServerState(state) {
|
|
966
|
+
const target = stateFileForBundle(state.bundlePath);
|
|
967
|
+
await mkdir(STATE_DIR, { recursive: true });
|
|
968
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
969
|
+
try {
|
|
970
|
+
await writeFile(tmp, JSON.stringify(state, null, 2) + "\n", {
|
|
971
|
+
encoding: "utf8",
|
|
972
|
+
mode: 384
|
|
973
|
+
});
|
|
974
|
+
await chmod(tmp, 384);
|
|
975
|
+
await rename(tmp, target);
|
|
976
|
+
} catch (err) {
|
|
977
|
+
await unlink(tmp).catch(() => {
|
|
978
|
+
});
|
|
979
|
+
throw err;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
async function clearToolsServerState(bundlePath) {
|
|
983
|
+
const files = [STATE_FILE];
|
|
984
|
+
if (bundlePath !== void 0) {
|
|
985
|
+
files[0] = stateFileForBundle(bundlePath);
|
|
986
|
+
const legacy = await readStateFile(STATE_FILE);
|
|
987
|
+
if (legacy && legacy.bundlePath === bundlePath) files.push(STATE_FILE);
|
|
988
|
+
}
|
|
989
|
+
for (const file of files) {
|
|
990
|
+
try {
|
|
991
|
+
await unlink(file);
|
|
992
|
+
} catch {
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
var STATE_FILE_RE = /^tool-server(-[0-9a-f]{12})?\.json$/;
|
|
997
|
+
async function readAllToolsServerStates() {
|
|
998
|
+
let names;
|
|
999
|
+
try {
|
|
1000
|
+
names = await readdir(STATE_DIR);
|
|
1001
|
+
} catch {
|
|
1002
|
+
return [];
|
|
1003
|
+
}
|
|
1004
|
+
const out = [];
|
|
1005
|
+
for (const name of names) {
|
|
1006
|
+
if (!STATE_FILE_RE.test(name)) continue;
|
|
1007
|
+
const file = path6.join(STATE_DIR, name);
|
|
1008
|
+
const state = await readStateFile(file);
|
|
1009
|
+
if (state) out.push({ file, state });
|
|
1010
|
+
}
|
|
1011
|
+
return out;
|
|
1012
|
+
}
|
|
1013
|
+
async function sweepDeadStateFiles() {
|
|
1014
|
+
for (const { file } of await readAllToolsServerStates()) {
|
|
1015
|
+
if (file === STATE_FILE) continue;
|
|
1016
|
+
const fresh = await readStateFile(file);
|
|
1017
|
+
if (!fresh) continue;
|
|
1018
|
+
if (!isProcessAlive(fresh.pid)) {
|
|
1019
|
+
await unlink(file).catch(() => {
|
|
1020
|
+
});
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
if (fs4.existsSync(fresh.bundlePath)) continue;
|
|
1024
|
+
if (!couldBeOurToolServer(fresh.pid, fresh.bundlePath)) continue;
|
|
1025
|
+
await unlink(file).catch(() => {
|
|
1026
|
+
});
|
|
1027
|
+
void terminatePid(fresh.pid, () => couldBeOurToolServer(fresh.pid, fresh.bundlePath));
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
var readState = readToolsServerState;
|
|
1031
|
+
var writeState = writeToolsServerState;
|
|
1032
|
+
var SIGTERM_GRACE_MS = 6e3;
|
|
1033
|
+
var SIGKILL_GRACE_MS = 1e3;
|
|
1034
|
+
var KILL_POLL_MS = 100;
|
|
1035
|
+
async function waitForExit(pid, timeoutMs) {
|
|
1036
|
+
const deadline = Date.now() + timeoutMs;
|
|
1037
|
+
while (Date.now() < deadline) {
|
|
1038
|
+
if (!isProcessAlive(pid)) return true;
|
|
1039
|
+
await new Promise((r) => setTimeout(r, KILL_POLL_MS));
|
|
1040
|
+
}
|
|
1041
|
+
return !isProcessAlive(pid);
|
|
1042
|
+
}
|
|
1043
|
+
async function terminatePid(pid, stillOurs) {
|
|
1044
|
+
if (!isProcessAlive(pid)) return;
|
|
1045
|
+
if (stillOurs && !stillOurs()) return;
|
|
1046
|
+
try {
|
|
1047
|
+
process.kill(pid, "SIGTERM");
|
|
1048
|
+
} catch {
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
if (await waitForExit(pid, SIGTERM_GRACE_MS)) return;
|
|
1052
|
+
if (stillOurs && !(isProcessAlive(pid) && stillOurs())) return;
|
|
1053
|
+
try {
|
|
1054
|
+
process.kill(pid, "SIGKILL");
|
|
1055
|
+
} catch {
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
await waitForExit(pid, SIGKILL_GRACE_MS);
|
|
1059
|
+
}
|
|
1060
|
+
async function killToolServer(bundlePath) {
|
|
1061
|
+
const state = await readState(bundlePath);
|
|
1062
|
+
if (!state) return false;
|
|
1063
|
+
const stillOurs = () => couldBeOurToolServer(state.pid, state.bundlePath);
|
|
1064
|
+
const alive = isProcessAlive(state.pid);
|
|
1065
|
+
if (alive && !stillOurs()) return false;
|
|
1066
|
+
if (alive) await terminatePid(state.pid, stillOurs);
|
|
1067
|
+
await clearToolsServerState(bundlePath ?? state.bundlePath);
|
|
1068
|
+
return alive;
|
|
1069
|
+
}
|
|
1070
|
+
var PS_BIN = ["/bin/ps", "/usr/bin/ps"].find((p) => fs4.existsSync(p)) ?? "ps";
|
|
1071
|
+
var PS_WIDTH_FLAGS = ["-ww"];
|
|
1072
|
+
var PS_LOCALE = process.platform === "darwin" ? "en_US.UTF-8" : "C.UTF-8";
|
|
1073
|
+
function readProcessCommandLine(pid, flags = PS_WIDTH_FLAGS, env = { ...process.env, LC_ALL: PS_LOCALE }) {
|
|
1074
|
+
return execFileSync(PS_BIN, [...flags, "-p", String(pid), "-o", "command="], {
|
|
1075
|
+
env,
|
|
1076
|
+
encoding: "utf8",
|
|
1077
|
+
timeout: 2e3,
|
|
1078
|
+
// A recycled pid can sit on a process with an argv past Node's 1 MiB exec
|
|
1079
|
+
// default, where the overrun surfaces as ENOBUFS instead of a command line.
|
|
1080
|
+
// Same ceiling as tool-server's vega-process ps probes.
|
|
1081
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
1082
|
+
// Piping ps's stderr is what puts a rejected flag ("ps: invalid option --
|
|
1083
|
+
// 'w'") into the thrown error's message.
|
|
1084
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1085
|
+
}).trim();
|
|
1086
|
+
}
|
|
1087
|
+
function couldBeOurToolServer(pid, marker) {
|
|
1088
|
+
if (!marker) return false;
|
|
1089
|
+
if (process.platform === "win32") return true;
|
|
1090
|
+
const escaped = marker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1091
|
+
const ours = new RegExp(`(?:^|[\\t\\n\\v\\f\\r ])${escaped} start(?:[\\t\\n\\v\\f\\r ]|$)`);
|
|
1092
|
+
const nonAscii = [...marker].some((c) => c.charCodeAt(0) > 127);
|
|
1093
|
+
let failure;
|
|
1094
|
+
for (const env of [void 0, process.env]) {
|
|
1095
|
+
try {
|
|
1096
|
+
if (ours.test(readProcessCommandLine(pid, PS_WIDTH_FLAGS, env))) return true;
|
|
1097
|
+
failure = void 0;
|
|
1098
|
+
if (!nonAscii) break;
|
|
1099
|
+
} catch (err) {
|
|
1100
|
+
failure = String(err);
|
|
1101
|
+
if (err.code === "ETIMEDOUT") break;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
if (failure !== void 0) {
|
|
1105
|
+
process.stderr.write(
|
|
1106
|
+
`[launcher] ps could not read pid ${pid}'s command line; leaving it alone: ${failure}
|
|
1107
|
+
`
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
var LOCK_WAIT_TIMEOUT_MS = 3e4;
|
|
1113
|
+
var LOCK_STALE_MS = 45e3;
|
|
1114
|
+
var LOCK_POLL_MS = 100;
|
|
1115
|
+
function spawnLockIsStale() {
|
|
1116
|
+
try {
|
|
1117
|
+
const { pid, ts } = JSON.parse(fs4.readFileSync(LOCK_FILE, "utf8"));
|
|
1118
|
+
if (typeof pid === "number" && pid > 0 && !isProcessAlive(pid)) return true;
|
|
1119
|
+
if (typeof ts === "number" && Date.now() - ts > LOCK_STALE_MS) return true;
|
|
1120
|
+
return false;
|
|
1121
|
+
} catch {
|
|
1122
|
+
try {
|
|
1123
|
+
return Date.now() - fs4.statSync(LOCK_FILE).mtimeMs > LOCK_STALE_MS;
|
|
1124
|
+
} catch {
|
|
1125
|
+
return true;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
async function acquireSpawnLock() {
|
|
1130
|
+
try {
|
|
1131
|
+
fs4.mkdirSync(STATE_DIR, { recursive: true });
|
|
1132
|
+
} catch {
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
const nonce = randomBytes(8).toString("hex");
|
|
1136
|
+
const deadline = Date.now() + LOCK_WAIT_TIMEOUT_MS;
|
|
1137
|
+
for (; ; ) {
|
|
1138
|
+
try {
|
|
1139
|
+
const fd = fs4.openSync(LOCK_FILE, "wx");
|
|
1140
|
+
try {
|
|
1141
|
+
fs4.writeSync(fd, JSON.stringify({ pid: process.pid, nonce, ts: Date.now() }));
|
|
1142
|
+
} finally {
|
|
1143
|
+
fs4.closeSync(fd);
|
|
1144
|
+
}
|
|
1145
|
+
let released = false;
|
|
1146
|
+
return {
|
|
1147
|
+
release: () => {
|
|
1148
|
+
if (released) return;
|
|
1149
|
+
released = true;
|
|
1150
|
+
try {
|
|
1151
|
+
const cur = JSON.parse(fs4.readFileSync(LOCK_FILE, "utf8"));
|
|
1152
|
+
if (cur.pid === process.pid && cur.nonce === nonce) fs4.unlinkSync(LOCK_FILE);
|
|
1153
|
+
} catch {
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
};
|
|
1157
|
+
} catch (err) {
|
|
1158
|
+
if (err.code !== "EEXIST") return null;
|
|
1159
|
+
if (spawnLockIsStale()) {
|
|
1160
|
+
try {
|
|
1161
|
+
fs4.unlinkSync(LOCK_FILE);
|
|
1162
|
+
} catch {
|
|
1163
|
+
}
|
|
1164
|
+
if (!fs4.existsSync(LOCK_FILE)) continue;
|
|
1165
|
+
}
|
|
1166
|
+
if (Date.now() >= deadline) return null;
|
|
1167
|
+
await new Promise((r) => setTimeout(r, LOCK_POLL_MS));
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
async function reusableHandle(state, wantBundlePath, wantVersion) {
|
|
1172
|
+
if (!state || !isProcessAlive(state.pid)) return null;
|
|
1173
|
+
if (wantBundlePath !== void 0 && state.bundlePath !== wantBundlePath) return null;
|
|
1174
|
+
if (wantBundlePath !== void 0 && state.version !== void 0) {
|
|
1175
|
+
const diskVersion = readBundlePackageVersion(wantBundlePath);
|
|
1176
|
+
if (diskVersion !== void 0) {
|
|
1177
|
+
if (diskVersion !== state.version) return null;
|
|
1178
|
+
} else if (wantVersion !== void 0 && state.version !== wantVersion && isVersionNewer(wantVersion, state.version)) {
|
|
1179
|
+
return null;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
const host = state.host ?? "127.0.0.1";
|
|
1183
|
+
const healthy = await isToolsServerHealthy(state.port, host, 2e3, state.token);
|
|
1184
|
+
if (!healthy) return null;
|
|
1185
|
+
return { url: formatUrl(healthCheckHost(host), state.port), token: state.token ?? "" };
|
|
1186
|
+
}
|
|
1187
|
+
async function ensureToolsServer(paths) {
|
|
1188
|
+
const fast = await reusableHandle(
|
|
1189
|
+
await readState(paths.bundlePath),
|
|
1190
|
+
paths.bundlePath,
|
|
1191
|
+
paths.version
|
|
1192
|
+
);
|
|
1193
|
+
if (fast) return fast;
|
|
1194
|
+
const lock = await acquireSpawnLock();
|
|
1195
|
+
try {
|
|
1196
|
+
const state = await readState(paths.bundlePath);
|
|
1197
|
+
const reuse = await reusableHandle(state, paths.bundlePath, paths.version);
|
|
1198
|
+
if (reuse) return reuse;
|
|
1199
|
+
if (state && state.managed === "autospawn" && state.bundlePath === paths.bundlePath && isProcessAlive(state.pid) && couldBeOurToolServer(state.pid, state.bundlePath)) {
|
|
1200
|
+
await terminatePid(state.pid, () => couldBeOurToolServer(state.pid, state.bundlePath));
|
|
1201
|
+
}
|
|
1202
|
+
await clearToolsServerState(paths.bundlePath);
|
|
1203
|
+
await sweepDeadStateFiles();
|
|
1204
|
+
if (!fs4.existsSync(paths.bundlePath)) {
|
|
1205
|
+
throw new Error(
|
|
1206
|
+
`The argent install serving this session is gone from disk (${paths.bundlePath}) \u2014 it was likely updated or removed. Restart the editor's MCP connection to reconnect.`
|
|
1207
|
+
);
|
|
1208
|
+
}
|
|
1209
|
+
const diskVersion = readBundlePackageVersion(paths.bundlePath) ?? paths.version;
|
|
1210
|
+
const token = generateToken();
|
|
1211
|
+
const port = await findFreePort();
|
|
1212
|
+
const { port: actualPort, pid } = await spawnToolsServer(paths, port, {
|
|
1213
|
+
token,
|
|
1214
|
+
idleTimeoutMinutes: AUTOSPAWN_IDLE_TIMEOUT_MINUTES
|
|
1215
|
+
});
|
|
1216
|
+
await writeState({
|
|
1217
|
+
port: actualPort,
|
|
1218
|
+
pid,
|
|
1219
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1220
|
+
bundlePath: paths.bundlePath,
|
|
1221
|
+
version: diskVersion,
|
|
1222
|
+
host: "127.0.0.1",
|
|
1223
|
+
token,
|
|
1224
|
+
managed: "autospawn"
|
|
1225
|
+
});
|
|
1226
|
+
return { url: formatUrl("127.0.0.1", actualPort), token };
|
|
1227
|
+
} finally {
|
|
1228
|
+
lock?.release();
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// ../argent-tools-client/src/link-config.ts
|
|
1233
|
+
import * as path7 from "node:path";
|
|
1234
|
+
import { homedir as homedir4 } from "node:os";
|
|
1235
|
+
import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile2, unlink as unlink2, chmod as chmod2 } from "node:fs/promises";
|
|
1236
|
+
var LINK_DIR = path7.join(homedir4(), ".argent");
|
|
1237
|
+
var LINK_FILE = path7.join(LINK_DIR, "link.json");
|
|
1238
|
+
async function readLinkConfig() {
|
|
1239
|
+
try {
|
|
1240
|
+
const raw = await readFile2(LINK_FILE, "utf8");
|
|
1241
|
+
const parsed = JSON.parse(raw);
|
|
1242
|
+
if (!parsed || typeof parsed.url !== "string" || typeof parsed.host !== "string" || typeof parsed.port !== "number" || typeof parsed.createdAt !== "string") {
|
|
1243
|
+
return null;
|
|
1244
|
+
}
|
|
1245
|
+
return {
|
|
1246
|
+
url: parsed.url,
|
|
1247
|
+
host: parsed.host,
|
|
1248
|
+
port: parsed.port,
|
|
1249
|
+
createdAt: parsed.createdAt,
|
|
1250
|
+
...typeof parsed.token === "string" ? { token: parsed.token } : {}
|
|
1251
|
+
};
|
|
1252
|
+
} catch {
|
|
1253
|
+
return null;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
async function getResolvedToolsUrl() {
|
|
1257
|
+
const envUrl = process.env.ARGENT_TOOLS_URL;
|
|
1258
|
+
if (envUrl) {
|
|
1259
|
+
const link2 = await readLinkConfig();
|
|
1260
|
+
const envToken = process.env[AUTH_TOKEN_ENV];
|
|
1261
|
+
return {
|
|
1262
|
+
url: envUrl,
|
|
1263
|
+
source: "env",
|
|
1264
|
+
...envToken ? { token: envToken } : {},
|
|
1265
|
+
...link2 ? { shadowedLink: link2 } : {}
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
const link = await readLinkConfig();
|
|
1269
|
+
if (link) {
|
|
1270
|
+
return { url: link.url, source: "link", ...link.token ? { token: link.token } : {} };
|
|
1271
|
+
}
|
|
1272
|
+
return { url: null, source: "none" };
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
// ../argent-tools-client/src/file-inputs.ts
|
|
1276
|
+
import { createHash as createHash2, randomUUID as randomUUID4 } from "node:crypto";
|
|
1277
|
+
import { createReadStream } from "node:fs";
|
|
1278
|
+
import { mkdir as mkdir3, readFile as readFile3, rm as rm2, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
|
|
1279
|
+
import { tmpdir } from "node:os";
|
|
1280
|
+
import * as path8 from "node:path";
|
|
1281
|
+
|
|
1282
|
+
// ../archive/src/index.ts
|
|
1283
|
+
import { execFile } from "node:child_process";
|
|
1284
|
+
import { rm, readdir as readdir2 } from "node:fs/promises";
|
|
1285
|
+
import { basename, dirname as dirname3, join as join7, posix, resolve as resolve5, sep } from "node:path";
|
|
1286
|
+
import { promisify } from "node:util";
|
|
1287
|
+
var execFileAsync = promisify(execFile);
|
|
1288
|
+
var ArchiveError = class extends Error {
|
|
1289
|
+
};
|
|
1290
|
+
function createTarGzArgs(sourcePath, target) {
|
|
1291
|
+
return ["-czf", target, "-C", dirname3(sourcePath), basename(sourcePath)];
|
|
1292
|
+
}
|
|
1293
|
+
async function createTarGzFile(sourcePath, tarPath) {
|
|
1294
|
+
try {
|
|
1295
|
+
await execFileAsync("tar", createTarGzArgs(sourcePath, tarPath));
|
|
1296
|
+
} catch (err) {
|
|
1297
|
+
await rm(tarPath, { force: true }).catch(() => {
|
|
1298
|
+
});
|
|
1299
|
+
throw err;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
function normalizeTarMemberPath(memberPath) {
|
|
1303
|
+
return memberPath.replace(/^\.\//, "").replace(/\\/g, "/");
|
|
1304
|
+
}
|
|
1305
|
+
function isSafeTarMember(memberPath, destDir) {
|
|
1306
|
+
const normalized = normalizeTarMemberPath(memberPath);
|
|
1307
|
+
if (!normalized || normalized === "." || normalized === "./") return false;
|
|
1308
|
+
if (normalized.startsWith("/") || /^[A-Za-z]:[\\/]/.test(normalized)) return false;
|
|
1309
|
+
const relative2 = posix.normalize(normalized);
|
|
1310
|
+
if (relative2 === ".." || relative2.startsWith("../") || relative2.split("/").includes("..")) {
|
|
1311
|
+
return false;
|
|
1312
|
+
}
|
|
1313
|
+
const root = resolve5(destDir);
|
|
1314
|
+
const resolved = resolve5(destDir, relative2);
|
|
1315
|
+
return resolved === root || resolved.startsWith(root + sep);
|
|
1316
|
+
}
|
|
1317
|
+
async function listTarMembers(tarPath) {
|
|
1318
|
+
const { stdout } = await execFileAsync("tar", ["-tzf", tarPath]);
|
|
1319
|
+
return stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
1320
|
+
}
|
|
1321
|
+
function isEscapingLinkTarget(target) {
|
|
1322
|
+
if (target.startsWith("/") || /^[A-Za-z]:[\\/]/.test(target)) return true;
|
|
1323
|
+
return posix.normalize(target.replace(/\\/g, "/")).split("/").includes("..");
|
|
1324
|
+
}
|
|
1325
|
+
async function assertSafeMemberTypes(tarPath) {
|
|
1326
|
+
const { stdout } = await execFileAsync("tar", ["-tzvf", tarPath]);
|
|
1327
|
+
for (const line of stdout.split("\n")) {
|
|
1328
|
+
if (!line.trim()) continue;
|
|
1329
|
+
const type = line[0];
|
|
1330
|
+
if (type === "-" || type === "d") continue;
|
|
1331
|
+
if (type === "l") {
|
|
1332
|
+
const parts = line.split(" -> ");
|
|
1333
|
+
const target = parts.length === 2 ? parts[1].trim() : "";
|
|
1334
|
+
if (parts.length !== 2 || !target || isEscapingLinkTarget(target)) {
|
|
1335
|
+
throw new ArchiveError(
|
|
1336
|
+
`Archive contains a symlink whose target could not be confirmed safe: "${line.trim()}".`
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
throw new ArchiveError(
|
|
1342
|
+
`Archive contains an unsupported member type "${type}" (hardlink/device/\u2026) \u2014 refusing extraction.`
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
async function assertSafeArchive(tarPath, destDir) {
|
|
1347
|
+
let members;
|
|
1348
|
+
try {
|
|
1349
|
+
members = await listTarMembers(tarPath);
|
|
1350
|
+
} catch (err) {
|
|
1351
|
+
throw new ArchiveError(
|
|
1352
|
+
`Could not read archive: ${err instanceof Error ? err.message : String(err)}`
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
if (members.length === 0) {
|
|
1356
|
+
throw new ArchiveError("Archive is empty.");
|
|
1357
|
+
}
|
|
1358
|
+
for (const member of members) {
|
|
1359
|
+
if (!isSafeTarMember(member, destDir)) {
|
|
1360
|
+
throw new ArchiveError(`Archive contains an unsafe path "${member}" \u2014 refusing extraction.`);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
await assertSafeMemberTypes(tarPath);
|
|
1364
|
+
}
|
|
1365
|
+
async function resolveMember(destDir, expectedName) {
|
|
1366
|
+
const entries = await readdir2(destDir);
|
|
1367
|
+
if (entries.includes(expectedName)) {
|
|
1368
|
+
return join7(destDir, expectedName);
|
|
1369
|
+
}
|
|
1370
|
+
const real = entries.filter((e) => !e.startsWith("._"));
|
|
1371
|
+
if (real.length !== 1) {
|
|
1372
|
+
throw new ArchiveError(
|
|
1373
|
+
`Could not identify the extracted member (expected "${expectedName}", found ${real.length} entries).`
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
return join7(destDir, real[0]);
|
|
1377
|
+
}
|
|
1378
|
+
async function safeExtractTarGz(tarPath, destDir, expectedName) {
|
|
1379
|
+
await assertSafeArchive(tarPath, destDir);
|
|
1380
|
+
await execFileAsync("tar", ["-xzf", tarPath, "-C", destDir]);
|
|
1381
|
+
return resolveMember(destDir, expectedName);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// ../registry/src/artifacts.ts
|
|
1385
|
+
import { stat } from "node:fs/promises";
|
|
1386
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1387
|
+
import { basename as basename2, extname } from "node:path";
|
|
1388
|
+
|
|
1389
|
+
// ../registry/src/file-inputs.ts
|
|
1390
|
+
var FLOW_NAME_CHARSET = "[A-Za-z0-9_-]+";
|
|
1391
|
+
var FLOW_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}$`);
|
|
1392
|
+
var FLOW_FILE_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}\\.yaml$`);
|
|
1393
|
+
var SCRIPT_FILE_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}\\.mjs$`);
|
|
1394
|
+
|
|
1395
|
+
// ../registry/src/failure-codes.ts
|
|
1396
|
+
var FAILURE_CODES = {
|
|
1397
|
+
ARGENT_UNCLASSIFIED_FAILURE: "ARGENT_UNCLASSIFIED_FAILURE",
|
|
1398
|
+
REGISTRY_SERVICE_NOT_FOUND: "REGISTRY_SERVICE_NOT_FOUND",
|
|
1399
|
+
REGISTRY_SERVICE_INITIALIZATION_FAILED: "REGISTRY_SERVICE_INITIALIZATION_FAILED",
|
|
1400
|
+
REGISTRY_TOOL_NOT_FOUND: "REGISTRY_TOOL_NOT_FOUND",
|
|
1401
|
+
REGISTRY_TOOL_EXECUTION_FAILED: "REGISTRY_TOOL_EXECUTION_FAILED",
|
|
1402
|
+
REGISTRY_TOOL_FAILURE_UNCLASSIFIED: "REGISTRY_TOOL_FAILURE_UNCLASSIFIED",
|
|
1403
|
+
REGISTRY_SERVICE_TERMINATING: "REGISTRY_SERVICE_TERMINATING",
|
|
1404
|
+
HTTP_TOOL_NOT_FOUND: "HTTP_TOOL_NOT_FOUND",
|
|
1405
|
+
HTTP_ZOD_VALIDATION_FAILED: "HTTP_ZOD_VALIDATION_FAILED",
|
|
1406
|
+
HTTP_CAPABILITY_UNSUPPORTED_OPERATION: "HTTP_CAPABILITY_UNSUPPORTED_OPERATION",
|
|
1407
|
+
HTTP_DEVICE_RESOLUTION_FAILED: "HTTP_DEVICE_RESOLUTION_FAILED",
|
|
1408
|
+
HTTP_DEPENDENCY_PREFLIGHT_MISSING: "HTTP_DEPENDENCY_PREFLIGHT_MISSING",
|
|
1409
|
+
CLI_RUN_TOOL_NOT_FOUND: "CLI_RUN_TOOL_NOT_FOUND",
|
|
1410
|
+
CLI_RUN_FLAG_PARSE_FAILED: "CLI_RUN_FLAG_PARSE_FAILED",
|
|
1411
|
+
CLI_RUN_ARGS_NOT_OBJECT: "CLI_RUN_ARGS_NOT_OBJECT",
|
|
1412
|
+
CLI_RUN_ARGS_JSON_INVALID: "CLI_RUN_ARGS_JSON_INVALID",
|
|
1413
|
+
CLI_RUN_INPUT_VALIDATION_FAILED: "CLI_RUN_INPUT_VALIDATION_FAILED",
|
|
1414
|
+
CLI_RUN_TOOL_CALL_FAILED: "CLI_RUN_TOOL_CALL_FAILED",
|
|
1415
|
+
CLI_RUN_SAVE_IMAGE_FAILED: "CLI_RUN_SAVE_IMAGE_FAILED",
|
|
1416
|
+
TOOL_CAPABILITY_UNSUPPORTED_OPERATION: "TOOL_CAPABILITY_UNSUPPORTED_OPERATION",
|
|
1417
|
+
TOOL_PLATFORM_NOT_IMPLEMENTED: "TOOL_PLATFORM_NOT_IMPLEMENTED",
|
|
1418
|
+
TOOL_DEPENDENCY_MISSING: "TOOL_DEPENDENCY_MISSING",
|
|
1419
|
+
TOOL_INPUT_INVALID: "TOOL_INPUT_INVALID",
|
|
1420
|
+
TOOLSERVER_UNHANDLED_REJECTION: "TOOLSERVER_UNHANDLED_REJECTION",
|
|
1421
|
+
TOOLSERVER_UNCAUGHT_EXCEPTION: "TOOLSERVER_UNCAUGHT_EXCEPTION",
|
|
1422
|
+
INSTALL_GLOBAL_PACKAGE_FAILED: "INSTALL_GLOBAL_PACKAGE_FAILED",
|
|
1423
|
+
INSTALL_LOCAL_PACKAGE_FAILED: "INSTALL_LOCAL_PACKAGE_FAILED",
|
|
1424
|
+
INSTALL_LOCAL_PRECONDITION_FAILED: "INSTALL_LOCAL_PRECONDITION_FAILED",
|
|
1425
|
+
INSTALL_MODE_FLAG_CONFLICT: "INSTALL_MODE_FLAG_CONFLICT",
|
|
1426
|
+
INSTALL_FROM_TAR_PACKAGE_FAILED: "INSTALL_FROM_TAR_PACKAGE_FAILED",
|
|
1427
|
+
INSTALL_INIT_TRIGGERED_UPDATE_FAILED: "INSTALL_INIT_TRIGGERED_UPDATE_FAILED",
|
|
1428
|
+
INSTALL_SKILLS_REFRESH_FAILED: "INSTALL_SKILLS_REFRESH_FAILED",
|
|
1429
|
+
INSTALL_UNCLASSIFIED_FAILED: "INSTALL_UNCLASSIFIED_FAILED",
|
|
1430
|
+
UPDATE_INSTALLED_VERSION_DETECT_FAILED: "UPDATE_INSTALLED_VERSION_DETECT_FAILED",
|
|
1431
|
+
UPDATE_INVALID_TARGET_VERSION: "UPDATE_INVALID_TARGET_VERSION",
|
|
1432
|
+
UPDATE_REGISTRY_CHECK_FAILED: "UPDATE_REGISTRY_CHECK_FAILED",
|
|
1433
|
+
UPDATE_TOOLSERVER_STOP_FAILED: "UPDATE_TOOLSERVER_STOP_FAILED",
|
|
1434
|
+
UPDATE_PACKAGE_ACTION_FAILED: "UPDATE_PACKAGE_ACTION_FAILED",
|
|
1435
|
+
UPDATE_UNCLASSIFIED_FAILED: "UPDATE_UNCLASSIFIED_FAILED",
|
|
1436
|
+
UNINSTALL_TOOLSERVER_STOP_FAILED: "UNINSTALL_TOOLSERVER_STOP_FAILED",
|
|
1437
|
+
UNINSTALL_PACKAGE_ACTION_FAILED: "UNINSTALL_PACKAGE_ACTION_FAILED",
|
|
1438
|
+
UNINSTALL_UNCLASSIFIED_FAILED: "UNINSTALL_UNCLASSIFIED_FAILED",
|
|
1439
|
+
VEGA_CLI_COMMAND_FAILED: "VEGA_CLI_COMMAND_FAILED",
|
|
1440
|
+
VEGA_INPUT_UNAVAILABLE: "VEGA_INPUT_UNAVAILABLE",
|
|
1441
|
+
VEGA_DEVICE_NOT_FOUND: "VEGA_DEVICE_NOT_FOUND",
|
|
1442
|
+
VEGA_DEVICE_NOT_REGISTERED: "VEGA_DEVICE_NOT_REGISTERED",
|
|
1443
|
+
VEGA_MULTIPLE_DEVICES: "VEGA_MULTIPLE_DEVICES",
|
|
1444
|
+
VEGA_BOOT_TIMEOUT: "VEGA_BOOT_TIMEOUT",
|
|
1445
|
+
VEGA_IMAGE_NOT_FOUND: "VEGA_IMAGE_NOT_FOUND",
|
|
1446
|
+
VEGA_ALREADY_RUNNING: "VEGA_ALREADY_RUNNING",
|
|
1447
|
+
VEGA_SCREENSHOT_FAILED: "VEGA_SCREENSHOT_FAILED",
|
|
1448
|
+
VEGA_TEXT_INVALID: "VEGA_TEXT_INVALID",
|
|
1449
|
+
ANDROID_ADB_NOT_FOUND: "ANDROID_ADB_NOT_FOUND",
|
|
1450
|
+
ANDROID_EMULATOR_NOT_FOUND: "ANDROID_EMULATOR_NOT_FOUND",
|
|
1451
|
+
ANDROID_ADB_COMMAND_FAILED: "ANDROID_ADB_COMMAND_FAILED",
|
|
1452
|
+
ANDROID_ADB_BOOT_TERMINAL_STATE: "ANDROID_ADB_BOOT_TERMINAL_STATE",
|
|
1453
|
+
ANDROID_ADB_BOOT_TIMEOUT: "ANDROID_ADB_BOOT_TIMEOUT",
|
|
1454
|
+
SIMULATOR_NETWORK_TIMEOUT: "SIMULATOR_NETWORK_TIMEOUT",
|
|
1455
|
+
SIMULATOR_NETWORK_CONNECTION_REFUSED: "SIMULATOR_NETWORK_CONNECTION_REFUSED",
|
|
1456
|
+
SIMULATOR_NETWORK_CONNECTION_RESET: "SIMULATOR_NETWORK_CONNECTION_RESET",
|
|
1457
|
+
SIMULATOR_NETWORK_ERROR: "SIMULATOR_NETWORK_ERROR",
|
|
1458
|
+
SIMULATOR_NON_JSON_RESPONSE: "SIMULATOR_NON_JSON_RESPONSE",
|
|
1459
|
+
SIMULATOR_HTTP_ERROR_RESPONSE: "SIMULATOR_HTTP_ERROR_RESPONSE",
|
|
1460
|
+
SIMULATOR_MISSING_RESPONSE_FIELDS: "SIMULATOR_MISSING_RESPONSE_FIELDS",
|
|
1461
|
+
SIMULATOR_SCREENSHOT_FAILED: "SIMULATOR_SCREENSHOT_FAILED",
|
|
1462
|
+
SIMULATOR_SERVER_FACTORY_OPTIONS_MISSING: "SIMULATOR_SERVER_FACTORY_OPTIONS_MISSING",
|
|
1463
|
+
SIMULATOR_SERVER_DEVICE_ID_INVALID: "SIMULATOR_SERVER_DEVICE_ID_INVALID",
|
|
1464
|
+
SIMULATOR_SERVER_READY_EXITED: "SIMULATOR_SERVER_READY_EXITED",
|
|
1465
|
+
SIMULATOR_SERVER_READY_TIMEOUT: "SIMULATOR_SERVER_READY_TIMEOUT",
|
|
1466
|
+
SIMULATOR_SERVER_PROCESS_ERROR: "SIMULATOR_SERVER_PROCESS_ERROR",
|
|
1467
|
+
SIMULATOR_SERVER_TERMINATED: "SIMULATOR_SERVER_TERMINATED",
|
|
1468
|
+
SIMULATOR_COMMAND_REJECTED: "SIMULATOR_COMMAND_REJECTED",
|
|
1469
|
+
SIMULATOR_COMMAND_ACK_TIMEOUT: "SIMULATOR_COMMAND_ACK_TIMEOUT",
|
|
1470
|
+
SIMULATOR_COMMAND_TRANSPORT_FAILED: "SIMULATOR_COMMAND_TRANSPORT_FAILED",
|
|
1471
|
+
// Physical iOS devices: the XCUITest-runner blueprint lifecycle, the runner
|
|
1472
|
+
// command client, and the `xcrun devicectl` subprocess wrapper (simulators
|
|
1473
|
+
// use the SIMULATOR_* set).
|
|
1474
|
+
IOS_DEVICE_RUNNER_FACTORY_OPTIONS_MISSING: "IOS_DEVICE_RUNNER_FACTORY_OPTIONS_MISSING",
|
|
1475
|
+
IOS_DEVICE_RUNNER_NOT_READY: "IOS_DEVICE_RUNNER_NOT_READY",
|
|
1476
|
+
IOS_DEVICE_RUNNER_TERMINATED: "IOS_DEVICE_RUNNER_TERMINATED",
|
|
1477
|
+
IOS_DEVICE_RUNNER_EXITED: "IOS_DEVICE_RUNNER_EXITED",
|
|
1478
|
+
// The runner answered a command with an ok:false envelope.
|
|
1479
|
+
IOS_DEVICE_RUNNER_COMMAND_FAILED: "IOS_DEVICE_RUNNER_COMMAND_FAILED",
|
|
1480
|
+
// The usbmux or HTTP exchange with a reachable runner failed.
|
|
1481
|
+
IOS_DEVICE_RUNNER_TRANSPORT_FAILED: "IOS_DEVICE_RUNNER_TRANSPORT_FAILED",
|
|
1482
|
+
IOS_DEVICECTL_COMMAND_FAILED: "IOS_DEVICECTL_COMMAND_FAILED",
|
|
1483
|
+
AX_QUERY_TIMEOUT: "AX_QUERY_TIMEOUT",
|
|
1484
|
+
AX_DAEMON_READY_TIMEOUT: "AX_DAEMON_READY_TIMEOUT",
|
|
1485
|
+
AX_DAEMON_EXITED_BEFORE_READY: "AX_DAEMON_EXITED_BEFORE_READY",
|
|
1486
|
+
AX_DAEMON_PROCESS_ERROR: "AX_DAEMON_PROCESS_ERROR",
|
|
1487
|
+
AX_FACTORY_OPTIONS_MISSING: "AX_FACTORY_OPTIONS_MISSING",
|
|
1488
|
+
AX_WRONG_PLATFORM: "AX_WRONG_PLATFORM",
|
|
1489
|
+
AX_DEVICE_ID_INVALID: "AX_DEVICE_ID_INVALID",
|
|
1490
|
+
AX_DESCRIBE_ERROR: "AX_DESCRIBE_ERROR",
|
|
1491
|
+
AX_QUERY_FAILED: "AX_QUERY_FAILED",
|
|
1492
|
+
ANDROID_LAUNCH_ACTIVITY_RESOLVE_FAILED: "ANDROID_LAUNCH_ACTIVITY_RESOLVE_FAILED",
|
|
1493
|
+
ANDROID_LAUNCH_AM_START_FAILED: "ANDROID_LAUNCH_AM_START_FAILED",
|
|
1494
|
+
ANDROID_OPEN_URL_FAILED: "ANDROID_OPEN_URL_FAILED",
|
|
1495
|
+
ANDROID_REINSTALL_INSTALL_FAILED: "ANDROID_REINSTALL_INSTALL_FAILED",
|
|
1496
|
+
ANDROID_RESTART_FAILED: "ANDROID_RESTART_FAILED",
|
|
1497
|
+
ANDROID_SETTINGS_PERMISSION_FAILED: "ANDROID_SETTINGS_PERMISSION_FAILED",
|
|
1498
|
+
IOS_LAUNCH_SIMCTL_FAILED: "IOS_LAUNCH_SIMCTL_FAILED",
|
|
1499
|
+
IOS_OPEN_URL_FAILED: "IOS_OPEN_URL_FAILED",
|
|
1500
|
+
IOS_REINSTALL_INSTALL_FAILED: "IOS_REINSTALL_INSTALL_FAILED",
|
|
1501
|
+
IOS_RESTART_LAUNCH_FAILED: "IOS_RESTART_LAUNCH_FAILED",
|
|
1502
|
+
IOS_SETTINGS_PERMISSION_FAILED: "IOS_SETTINGS_PERMISSION_FAILED",
|
|
1503
|
+
IOS_SHAKE_FAILED: "IOS_SHAKE_FAILED",
|
|
1504
|
+
ANDROID_SHAKE_FAILED: "ANDROID_SHAKE_FAILED",
|
|
1505
|
+
// The hinge of a foldable iOS simulator: the server refused the request on a
|
|
1506
|
+
// foldable (a bad angle, a hinge that would not move) vs. nothing to fold —
|
|
1507
|
+
// a device that is not foldable, no hinge route, a remote simulator.
|
|
1508
|
+
IOS_FOLD_FAILED: "IOS_FOLD_FAILED",
|
|
1509
|
+
IOS_FOLD_UNSUPPORTED: "IOS_FOLD_UNSUPPORTED",
|
|
1510
|
+
PASTE_CLIPBOARD_SET_FAILED: "PASTE_CLIPBOARD_SET_FAILED",
|
|
1511
|
+
PASTE_CLIPBOARD_UNSUPPORTED: "PASTE_CLIPBOARD_UNSUPPORTED",
|
|
1512
|
+
SETTINGS_PERMISSION_UNSUPPORTED: "SETTINGS_PERMISSION_UNSUPPORTED",
|
|
1513
|
+
NATIVE_DEVTOOLS_DESCRIBE_ERROR: "NATIVE_DEVTOOLS_DESCRIBE_ERROR",
|
|
1514
|
+
NATIVE_DEVTOOLS_VIEW_AT_POINT_ERROR: "NATIVE_DEVTOOLS_VIEW_AT_POINT_ERROR",
|
|
1515
|
+
NATIVE_DEVTOOLS_USER_INTERACTABLE_VIEW_AT_POINT_ERROR: "NATIVE_DEVTOOLS_USER_INTERACTABLE_VIEW_AT_POINT_ERROR",
|
|
1516
|
+
NATIVE_DEVTOOLS_FIND_VIEWS_ERROR: "NATIVE_DEVTOOLS_FIND_VIEWS_ERROR",
|
|
1517
|
+
NATIVE_DEVTOOLS_FACTORY_OPTIONS_MISSING: "NATIVE_DEVTOOLS_FACTORY_OPTIONS_MISSING",
|
|
1518
|
+
NATIVE_DEVTOOLS_WRONG_PLATFORM: "NATIVE_DEVTOOLS_WRONG_PLATFORM",
|
|
1519
|
+
NATIVE_DEVTOOLS_NOT_INJECTABLE: "NATIVE_DEVTOOLS_NOT_INJECTABLE",
|
|
1520
|
+
NATIVE_DEVTOOLS_NOT_CONNECTED: "NATIVE_DEVTOOLS_NOT_CONNECTED",
|
|
1521
|
+
NATIVE_DEVTOOLS_RPC_TIMEOUT: "NATIVE_DEVTOOLS_RPC_TIMEOUT",
|
|
1522
|
+
NATIVE_DEVTOOLS_RPC_ERROR: "NATIVE_DEVTOOLS_RPC_ERROR",
|
|
1523
|
+
NATIVE_DEVTOOLS_SERVICE_DISPOSED: "NATIVE_DEVTOOLS_SERVICE_DISPOSED",
|
|
1524
|
+
NATIVE_DEVTOOLS_SOCKET_BIND_FAILED: "NATIVE_DEVTOOLS_SOCKET_BIND_FAILED",
|
|
1525
|
+
NATIVE_DEVTOOLS_SOCKET_NOT_PUBLISHED: "NATIVE_DEVTOOLS_SOCKET_NOT_PUBLISHED",
|
|
1526
|
+
NATIVE_DEVTOOLS_ATTACH_FAILED: "NATIVE_DEVTOOLS_ATTACH_FAILED",
|
|
1527
|
+
NATIVE_DEVTOOLS_ATTACH_LOST: "NATIVE_DEVTOOLS_ATTACH_LOST",
|
|
1528
|
+
NATIVE_TARGET_NO_CONNECTED_APPS: "NATIVE_TARGET_NO_CONNECTED_APPS",
|
|
1529
|
+
NATIVE_TARGET_SINGLE_APP_NOT_FOREGROUND: "NATIVE_TARGET_SINGLE_APP_NOT_FOREGROUND",
|
|
1530
|
+
NATIVE_TARGET_MULTIPLE_APPS_AMBIGUOUS: "NATIVE_TARGET_MULTIPLE_APPS_AMBIGUOUS",
|
|
1531
|
+
ANDROID_DEVTOOLS_ADB_NOT_FOUND: "ANDROID_DEVTOOLS_ADB_NOT_FOUND",
|
|
1532
|
+
ANDROID_DEVTOOLS_ADB_FORWARD_UNEXPECTED: "ANDROID_DEVTOOLS_ADB_FORWARD_UNEXPECTED",
|
|
1533
|
+
ANDROID_DEVTOOLS_HELPER_EXITED_BEFORE_READY: "ANDROID_DEVTOOLS_HELPER_EXITED_BEFORE_READY",
|
|
1534
|
+
ANDROID_DEVTOOLS_HELPER_PROCESS_ERROR: "ANDROID_DEVTOOLS_HELPER_PROCESS_ERROR",
|
|
1535
|
+
ANDROID_DEVTOOLS_HELPER_READY_TIMEOUT: "ANDROID_DEVTOOLS_HELPER_READY_TIMEOUT",
|
|
1536
|
+
ANDROID_DEVTOOLS_FACTORY_OPTIONS_MISSING: "ANDROID_DEVTOOLS_FACTORY_OPTIONS_MISSING",
|
|
1537
|
+
ANDROID_DEVTOOLS_WRONG_PLATFORM: "ANDROID_DEVTOOLS_WRONG_PLATFORM",
|
|
1538
|
+
ANDROID_DEVTOOLS_DEVICE_ID_INVALID: "ANDROID_DEVTOOLS_DEVICE_ID_INVALID",
|
|
1539
|
+
ANDROID_DEVTOOLS_HELPER_TERMINATED: "ANDROID_DEVTOOLS_HELPER_TERMINATED",
|
|
1540
|
+
ANDROID_DEVTOOLS_HELPER_INSTALL_FAILED: "ANDROID_DEVTOOLS_HELPER_INSTALL_FAILED",
|
|
1541
|
+
ANDROID_DEVTOOLS_HELPER_REPAIR_FAILED: "ANDROID_DEVTOOLS_HELPER_REPAIR_FAILED",
|
|
1542
|
+
ANDROID_DEVTOOLS_RPC_CLIENT_CLOSED: "ANDROID_DEVTOOLS_RPC_CLIENT_CLOSED",
|
|
1543
|
+
ANDROID_DEVTOOLS_RPC_ERROR: "ANDROID_DEVTOOLS_RPC_ERROR",
|
|
1544
|
+
ANDROID_DEVTOOLS_RPC_TIMEOUT: "ANDROID_DEVTOOLS_RPC_TIMEOUT",
|
|
1545
|
+
ANDROID_DEVTOOLS_SOCKET_CLOSED: "ANDROID_DEVTOOLS_SOCKET_CLOSED",
|
|
1546
|
+
ANDROID_SCREEN_SIZE_PARSE_FAILED: "ANDROID_SCREEN_SIZE_PARSE_FAILED",
|
|
1547
|
+
ANDROID_SCREEN_SIZE_NON_POSITIVE: "ANDROID_SCREEN_SIZE_NON_POSITIVE",
|
|
1548
|
+
ANDROID_UIAUTOMATOR_PARSE_FAILED: "ANDROID_UIAUTOMATOR_PARSE_FAILED",
|
|
1549
|
+
ANDROID_UIAUTOMATOR_CAPTURE_FAILED: "ANDROID_UIAUTOMATOR_CAPTURE_FAILED",
|
|
1550
|
+
DEBUGGER_METRO_NOT_RUNNING: "DEBUGGER_METRO_NOT_RUNNING",
|
|
1551
|
+
DEBUGGER_METRO_NO_TARGETS: "DEBUGGER_METRO_NO_TARGETS",
|
|
1552
|
+
DEBUGGER_CDP_RUNTIME_EXCEPTION: "DEBUGGER_CDP_RUNTIME_EXCEPTION",
|
|
1553
|
+
DEBUGGER_CDP_BINDING_TIMEOUT: "DEBUGGER_CDP_BINDING_TIMEOUT",
|
|
1554
|
+
DEBUGGER_CDP_BINDING_UNAVAILABLE: "DEBUGGER_CDP_BINDING_UNAVAILABLE",
|
|
1555
|
+
DEBUGGER_CDP_PROTOCOL_ERROR: "DEBUGGER_CDP_PROTOCOL_ERROR",
|
|
1556
|
+
DEBUGGER_CDP_CONNECT_FAILED: "DEBUGGER_CDP_CONNECT_FAILED",
|
|
1557
|
+
DEBUGGER_CDP_SOCKET_CLOSED_BEFORE_OPEN: "DEBUGGER_CDP_SOCKET_CLOSED_BEFORE_OPEN",
|
|
1558
|
+
DEBUGGER_CDP_NOT_CONNECTED: "DEBUGGER_CDP_NOT_CONNECTED",
|
|
1559
|
+
DEBUGGER_CDP_REQUEST_TIMEOUT: "DEBUGGER_CDP_REQUEST_TIMEOUT",
|
|
1560
|
+
DEBUGGER_CDP_CONNECTION_CLOSED: "DEBUGGER_CDP_CONNECTION_CLOSED",
|
|
1561
|
+
DEBUGGER_TARGET_DEVICE_MISMATCH: "DEBUGGER_TARGET_DEVICE_MISMATCH",
|
|
1562
|
+
DEBUGGER_EVALUATE_EXPRESSION_THREW: "DEBUGGER_EVALUATE_EXPRESSION_THREW",
|
|
1563
|
+
DEBUGGER_RELOAD_FAILED: "DEBUGGER_RELOAD_FAILED",
|
|
1564
|
+
JS_RUNTIME_CONSOLE_SERVER_BIND_FAILED: "JS_RUNTIME_CONSOLE_SERVER_BIND_FAILED",
|
|
1565
|
+
JS_RUNTIME_PAYLOAD_INVALID: "JS_RUNTIME_PAYLOAD_INVALID",
|
|
1566
|
+
JS_RUNTIME_PAYLOAD_DEVICE_MISSING: "JS_RUNTIME_PAYLOAD_DEVICE_MISSING",
|
|
1567
|
+
JS_RUNTIME_PAYLOAD_PORT_INVALID: "JS_RUNTIME_PAYLOAD_PORT_INVALID",
|
|
1568
|
+
JS_RUNTIME_CDP_DISCONNECTED: "JS_RUNTIME_CDP_DISCONNECTED",
|
|
1569
|
+
JS_RUNTIME_PAUSED: "JS_RUNTIME_PAUSED",
|
|
1570
|
+
NETWORK_INSPECTOR_CDP_DISCONNECTED: "NETWORK_INSPECTOR_CDP_DISCONNECTED",
|
|
1571
|
+
REACT_PROFILER_NO_ACTIVE_SESSION: "REACT_PROFILER_NO_ACTIVE_SESSION",
|
|
1572
|
+
REACT_PROFILER_CDP_CONNECTION_LOST: "REACT_PROFILER_CDP_CONNECTION_LOST",
|
|
1573
|
+
REACT_PROFILER_NO_CPU_PROFILE: "REACT_PROFILER_NO_CPU_PROFILE",
|
|
1574
|
+
REACT_PROFILER_RUNTIME_EXCEPTION: "REACT_PROFILER_RUNTIME_EXCEPTION",
|
|
1575
|
+
REACT_PROFILER_NO_RUNTIME_DATA: "REACT_PROFILER_NO_RUNTIME_DATA",
|
|
1576
|
+
REACT_PROFILER_SESSION_PAYLOAD_INVALID: "REACT_PROFILER_SESSION_PAYLOAD_INVALID",
|
|
1577
|
+
REACT_PROFILER_SESSION_PAYLOAD_DEVICE_MISSING: "REACT_PROFILER_SESSION_PAYLOAD_DEVICE_MISSING",
|
|
1578
|
+
REACT_PROFILER_SESSION_CDP_DISCONNECTED: "REACT_PROFILER_SESSION_CDP_DISCONNECTED",
|
|
1579
|
+
REACT_PROFILER_CDP_NOT_CONNECTED: "REACT_PROFILER_CDP_NOT_CONNECTED",
|
|
1580
|
+
REACT_PROFILER_STATE_READ_FAILED: "REACT_PROFILER_STATE_READ_FAILED",
|
|
1581
|
+
REACT_PROFILER_DEVTOOLS_HOOK_MISSING: "REACT_PROFILER_DEVTOOLS_HOOK_MISSING",
|
|
1582
|
+
REACT_PROFILER_DEVTOOLS_BACKEND_ATTACH_FAILED: "REACT_PROFILER_DEVTOOLS_BACKEND_ATTACH_FAILED",
|
|
1583
|
+
REACT_PROFILER_DEVTOOLS_BACKEND_BOOTSTRAP_FAILED: "REACT_PROFILER_DEVTOOLS_BACKEND_BOOTSTRAP_FAILED",
|
|
1584
|
+
REACT_PROFILER_DEVTOOLS_RENDERER_MISSING: "REACT_PROFILER_DEVTOOLS_RENDERER_MISSING",
|
|
1585
|
+
REACT_PROFILER_START_FAILED: "REACT_PROFILER_START_FAILED",
|
|
1586
|
+
REACT_PROFILER_START_VERIFY_FAILED: "REACT_PROFILER_START_VERIFY_FAILED",
|
|
1587
|
+
REACT_PROFILER_HOOK_ERROR: "REACT_PROFILER_HOOK_ERROR",
|
|
1588
|
+
REACT_PROFILER_ANALYZE_NO_DATA: "REACT_PROFILER_ANALYZE_NO_DATA",
|
|
1589
|
+
NATIVE_PROFILER_FACTORY_OPTIONS_MISSING: "NATIVE_PROFILER_FACTORY_OPTIONS_MISSING",
|
|
1590
|
+
NATIVE_PROFILER_WRONG_PLATFORM: "NATIVE_PROFILER_WRONG_PLATFORM",
|
|
1591
|
+
NATIVE_PROFILER_APP_PROCESS_LIST_FAILED: "NATIVE_PROFILER_APP_PROCESS_LIST_FAILED",
|
|
1592
|
+
NATIVE_PROFILER_APP_LIST_FAILED: "NATIVE_PROFILER_APP_LIST_FAILED",
|
|
1593
|
+
NATIVE_PROFILER_NO_RUNNING_APPS: "NATIVE_PROFILER_NO_RUNNING_APPS",
|
|
1594
|
+
NATIVE_PROFILER_NO_RUNNING_USER_APPS: "NATIVE_PROFILER_NO_RUNNING_USER_APPS",
|
|
1595
|
+
NATIVE_PROFILER_MULTIPLE_RUNNING_USER_APPS: "NATIVE_PROFILER_MULTIPLE_RUNNING_USER_APPS",
|
|
1596
|
+
NATIVE_PROFILER_MALLOC_DEGRADED_XCODE: "NATIVE_PROFILER_MALLOC_DEGRADED_XCODE",
|
|
1597
|
+
NATIVE_PROFILER_MALLOC_STRATEGY_OVERRIDE: "NATIVE_PROFILER_MALLOC_STRATEGY_OVERRIDE",
|
|
1598
|
+
NATIVE_PROFILER_APP_BUNDLE_PATH_FAILED: "NATIVE_PROFILER_APP_BUNDLE_PATH_FAILED",
|
|
1599
|
+
NATIVE_PROFILER_LAUNCH_APP_NOT_FOUND: "NATIVE_PROFILER_LAUNCH_APP_NOT_FOUND",
|
|
1600
|
+
NATIVE_PROFILER_LAUNCH_APP_AMBIGUOUS: "NATIVE_PROFILER_LAUNCH_APP_AMBIGUOUS",
|
|
1601
|
+
NATIVE_PROFILER_SESSION_ALREADY_RUNNING: "NATIVE_PROFILER_SESSION_ALREADY_RUNNING",
|
|
1602
|
+
NATIVE_PROFILER_XCTRACE_NO_PID: "NATIVE_PROFILER_XCTRACE_NO_PID",
|
|
1603
|
+
NATIVE_PROFILER_XCTRACE_PROCESS_NOT_FOUND: "NATIVE_PROFILER_XCTRACE_PROCESS_NOT_FOUND",
|
|
1604
|
+
NATIVE_PROFILER_XCTRACE_READY_EXITED: "NATIVE_PROFILER_XCTRACE_READY_EXITED",
|
|
1605
|
+
NATIVE_PROFILER_XCTRACE_PROCESS_ERROR: "NATIVE_PROFILER_XCTRACE_PROCESS_ERROR",
|
|
1606
|
+
NATIVE_PROFILER_XCTRACE_READY_TIMEOUT: "NATIVE_PROFILER_XCTRACE_READY_TIMEOUT",
|
|
1607
|
+
NATIVE_PROFILER_TRACE_TEMPLATE_MISSING: "NATIVE_PROFILER_TRACE_TEMPLATE_MISSING",
|
|
1608
|
+
NATIVE_PROFILER_NO_ACTIVE_SESSION: "NATIVE_PROFILER_NO_ACTIVE_SESSION",
|
|
1609
|
+
NATIVE_PROFILER_SESSION_TORN_DOWN: "NATIVE_PROFILER_SESSION_TORN_DOWN",
|
|
1610
|
+
NATIVE_PROFILER_APP_PROCESS_NOT_FOUND: "NATIVE_PROFILER_APP_PROCESS_NOT_FOUND",
|
|
1611
|
+
NATIVE_PROFILER_NO_EXPORTED_TRACE: "NATIVE_PROFILER_NO_EXPORTED_TRACE",
|
|
1612
|
+
// Android perfetto start failures, mirroring the iOS xctrace set above.
|
|
1613
|
+
NATIVE_PROFILER_PERFETTO_PROCESS_ERROR: "NATIVE_PROFILER_PERFETTO_PROCESS_ERROR",
|
|
1614
|
+
NATIVE_PROFILER_PERFETTO_READY_TIMEOUT: "NATIVE_PROFILER_PERFETTO_READY_TIMEOUT",
|
|
1615
|
+
NATIVE_PROFILER_PERFETTO_READY_EXITED: "NATIVE_PROFILER_PERFETTO_READY_EXITED",
|
|
1616
|
+
// iOS and Android share one capture path (simulator-server frames into
|
|
1617
|
+
// ffmpeg), so these name the failing stage, not the device family.
|
|
1618
|
+
SCREEN_RECORDING_FACTORY_OPTIONS_MISSING: "SCREEN_RECORDING_FACTORY_OPTIONS_MISSING",
|
|
1619
|
+
SCREEN_RECORDING_WRONG_PLATFORM: "SCREEN_RECORDING_WRONG_PLATFORM",
|
|
1620
|
+
SCREEN_RECORDING_ALREADY_ACTIVE: "SCREEN_RECORDING_ALREADY_ACTIVE",
|
|
1621
|
+
SCREEN_RECORDING_NO_ACTIVE_SESSION: "SCREEN_RECORDING_NO_ACTIVE_SESSION",
|
|
1622
|
+
SCREEN_RECORDING_STOP_IN_PROGRESS: "SCREEN_RECORDING_STOP_IN_PROGRESS",
|
|
1623
|
+
SCREEN_RECORDING_START_EXITED: "SCREEN_RECORDING_START_EXITED",
|
|
1624
|
+
SCREEN_RECORDING_START_TIMEOUT: "SCREEN_RECORDING_START_TIMEOUT",
|
|
1625
|
+
SCREEN_RECORDING_PROCESS_ERROR: "SCREEN_RECORDING_PROCESS_ERROR",
|
|
1626
|
+
SCREEN_RECORDING_OUTPUT_MISSING: "SCREEN_RECORDING_OUTPUT_MISSING",
|
|
1627
|
+
SCREEN_RECORDING_SERVER_SHUTTING_DOWN: "SCREEN_RECORDING_SERVER_SHUTTING_DOWN",
|
|
1628
|
+
SCREEN_RECORDING_STREAM_UNAVAILABLE: "SCREEN_RECORDING_STREAM_UNAVAILABLE",
|
|
1629
|
+
SCREEN_RECORDING_FFMPEG_NOT_FOUND: "SCREEN_RECORDING_FFMPEG_NOT_FOUND",
|
|
1630
|
+
FLOW_PROJECT_ROOT_INVALID: "FLOW_PROJECT_ROOT_INVALID",
|
|
1631
|
+
FLOW_NAME_INVALID: "FLOW_NAME_INVALID",
|
|
1632
|
+
FLOW_NO_ACTIVE_RECORDING: "FLOW_NO_ACTIVE_RECORDING",
|
|
1633
|
+
FLOW_FILE_INVALID: "FLOW_FILE_INVALID",
|
|
1634
|
+
FLOW_FILE_WRITE_FAILED: "FLOW_FILE_WRITE_FAILED",
|
|
1635
|
+
FLOW_ENTRY_UNRECOGNIZED: "FLOW_ENTRY_UNRECOGNIZED",
|
|
1636
|
+
FLOW_E2E_HAS_PREREQUISITE: "FLOW_E2E_HAS_PREREQUISITE",
|
|
1637
|
+
FLOW_DEVICE_RESOLUTION: "FLOW_DEVICE_RESOLUTION",
|
|
1638
|
+
PROFILER_QUERY_MODE_INVALID: "PROFILER_QUERY_MODE_INVALID",
|
|
1639
|
+
PROFILER_QUERY_REQUIRED_PARAM_MISSING: "PROFILER_QUERY_REQUIRED_PARAM_MISSING",
|
|
1640
|
+
PROFILER_DATA_NOT_LOADED: "PROFILER_DATA_NOT_LOADED",
|
|
1641
|
+
PROFILER_NATIVE_TRACE_MISSING: "PROFILER_NATIVE_TRACE_MISSING",
|
|
1642
|
+
PROFILER_NATIVE_METADATA_MISSING: "PROFILER_NATIVE_METADATA_MISSING",
|
|
1643
|
+
PROFILER_NATIVE_METADATA_INVALID: "PROFILER_NATIVE_METADATA_INVALID",
|
|
1644
|
+
CHROMIUM_CDP_UNREACHABLE: "CHROMIUM_CDP_UNREACHABLE",
|
|
1645
|
+
CHROMIUM_CDP_NO_PAGE_TARGET: "CHROMIUM_CDP_NO_PAGE_TARGET",
|
|
1646
|
+
CHROMIUM_CDP_INVALID_RESPONSE: "CHROMIUM_CDP_INVALID_RESPONSE",
|
|
1647
|
+
CHROMIUM_DEVICE_ID_INVALID: "CHROMIUM_DEVICE_ID_INVALID",
|
|
1648
|
+
CHROMIUM_PARAM_INVALID: "CHROMIUM_PARAM_INVALID",
|
|
1649
|
+
CHROMIUM_INPUT_INVALID: "CHROMIUM_INPUT_INVALID",
|
|
1650
|
+
CHROMIUM_WINDOW_HIDDEN: "CHROMIUM_WINDOW_HIDDEN",
|
|
1651
|
+
CHROMIUM_VIEWPORT_READ_FAILED: "CHROMIUM_VIEWPORT_READ_FAILED",
|
|
1652
|
+
CHROMIUM_SCREENSHOT_FAILED: "CHROMIUM_SCREENSHOT_FAILED",
|
|
1653
|
+
CHROMIUM_STORAGE_EVAL_FAILED: "CHROMIUM_STORAGE_EVAL_FAILED",
|
|
1654
|
+
CHROMIUM_TAB_NOT_FOUND: "CHROMIUM_TAB_NOT_FOUND",
|
|
1655
|
+
CHROMIUM_TAB_OPEN_FAILED: "CHROMIUM_TAB_OPEN_FAILED",
|
|
1656
|
+
CHROMIUM_DESCRIBE_FAILED: "CHROMIUM_DESCRIBE_FAILED",
|
|
1657
|
+
CHROMIUM_ELECTRON_APP_PATH_INVALID: "CHROMIUM_ELECTRON_APP_PATH_INVALID",
|
|
1658
|
+
CHROMIUM_ELECTRON_SPAWN_FAILED: "CHROMIUM_ELECTRON_SPAWN_FAILED",
|
|
1659
|
+
CHROMIUM_ELECTRON_CDP_TIMEOUT: "CHROMIUM_ELECTRON_CDP_TIMEOUT",
|
|
1660
|
+
CHROMIUM_ELECTRON_EXITED_BEFORE_READY: "CHROMIUM_ELECTRON_EXITED_BEFORE_READY",
|
|
1661
|
+
KEYBOARD_KEY_UNSUPPORTED: "KEYBOARD_KEY_UNSUPPORTED",
|
|
1662
|
+
KEYBOARD_CHARACTER_UNSUPPORTED: "KEYBOARD_CHARACTER_UNSUPPORTED",
|
|
1663
|
+
KEYBOARD_TEXT_AND_KEY_COMBINED: "KEYBOARD_TEXT_AND_KEY_COMBINED",
|
|
1664
|
+
KEYBOARD_INPUT_NOT_FOCUSED: "KEYBOARD_INPUT_NOT_FOCUSED",
|
|
1665
|
+
SECRET_PLACEHOLDER_UNKNOWN: "SECRET_PLACEHOLDER_UNKNOWN",
|
|
1666
|
+
SCREENSHOT_DIFF_INPUT_INVALID: "SCREENSHOT_DIFF_INPUT_INVALID",
|
|
1667
|
+
BOOT_DEVICE_TARGET_SELECTION_INVALID: "BOOT_DEVICE_TARGET_SELECTION_INVALID",
|
|
1668
|
+
BOOT_IOS_UNSUPPORTED_HOST: "BOOT_IOS_UNSUPPORTED_HOST",
|
|
1669
|
+
BOOT_ANDROID_NO_AVDS: "BOOT_ANDROID_NO_AVDS",
|
|
1670
|
+
BOOT_ANDROID_AVD_NOT_FOUND: "BOOT_ANDROID_AVD_NOT_FOUND",
|
|
1671
|
+
BOOT_ANDROID_ADB_UNAVAILABLE: "BOOT_ANDROID_ADB_UNAVAILABLE",
|
|
1672
|
+
BOOT_ANDROID_GPU_MODE_INVALID: "BOOT_ANDROID_GPU_MODE_INVALID",
|
|
1673
|
+
BOOT_ANDROID_ADB_REGISTER_TIMEOUT: "BOOT_ANDROID_ADB_REGISTER_TIMEOUT",
|
|
1674
|
+
BOOT_ANDROID_HOT_BOOT_FRAME_UNUSABLE: "BOOT_ANDROID_HOT_BOOT_FRAME_UNUSABLE",
|
|
1675
|
+
BOOT_ANDROID_FIRST_FRAME_TIMEOUT: "BOOT_ANDROID_FIRST_FRAME_TIMEOUT",
|
|
1676
|
+
BOOT_ANDROID_PACKAGE_MANAGER_UNAVAILABLE: "BOOT_ANDROID_PACKAGE_MANAGER_UNAVAILABLE",
|
|
1677
|
+
BOOT_ANDROID_COLD_BOOT_FAILED: "BOOT_ANDROID_COLD_BOOT_FAILED",
|
|
1678
|
+
/**
|
|
1679
|
+
* External device providers
|
|
1680
|
+
* @see [external-devices.ts](../../tool-server/src/utils/external-devices.ts)
|
|
1681
|
+
*/
|
|
1682
|
+
EXTERNAL_DEVICE_CAPABILITY_DENIED: "EXTERNAL_DEVICE_CAPABILITY_DENIED",
|
|
1683
|
+
EXTERNAL_DEVICE_ENDPOINT_FORBIDDEN: "EXTERNAL_DEVICE_ENDPOINT_FORBIDDEN",
|
|
1684
|
+
EXTERNAL_DEVICE_LIFECYCLE_REFUSED: "EXTERNAL_DEVICE_LIFECYCLE_REFUSED",
|
|
1685
|
+
EXTERNAL_DEVICE_REVOCATION_INCOMPLETE: "EXTERNAL_DEVICE_REVOCATION_INCOMPLETE",
|
|
1686
|
+
EXTERNAL_DEVICE_UNAVAILABLE: "EXTERNAL_DEVICE_UNAVAILABLE"
|
|
1687
|
+
};
|
|
1688
|
+
|
|
1689
|
+
// ../registry/src/errors.ts
|
|
1690
|
+
var FAILURE_SIGNAL_NAMES = [
|
|
1691
|
+
"SIGABRT",
|
|
1692
|
+
"SIGHUP",
|
|
1693
|
+
"SIGINT",
|
|
1694
|
+
"SIGKILL",
|
|
1695
|
+
"SIGQUIT",
|
|
1696
|
+
"SIGTERM"
|
|
1697
|
+
];
|
|
1698
|
+
var FAILURE_SPAWN_CODES = ["EACCES", "ENOENT", "EPERM", "ETIMEDOUT"];
|
|
1699
|
+
var FALLBACK_SIGNAL = {
|
|
1700
|
+
error_code: FAILURE_CODES.ARGENT_UNCLASSIFIED_FAILURE,
|
|
1701
|
+
failure_stage: "unclassified",
|
|
1702
|
+
failure_area: "registry",
|
|
1703
|
+
error_kind: "unknown"
|
|
1704
|
+
};
|
|
1705
|
+
var FAILURE_SIGNAL_NAME_SET = new Set(FAILURE_SIGNAL_NAMES);
|
|
1706
|
+
var FAILURE_SPAWN_CODE_SET = new Set(FAILURE_SPAWN_CODES);
|
|
1707
|
+
|
|
1708
|
+
// ../registry/src/registry.ts
|
|
1709
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
1710
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
1711
|
+
var resolvedDuringInvocation = new AsyncLocalStorage();
|
|
1712
|
+
|
|
1713
|
+
// ../argent-tools-client/src/file-inputs.ts
|
|
1714
|
+
var FILE_INPUT_MARKER2 = "__argentFileInput";
|
|
1715
|
+
var CLIENT_FILE_MARKER2 = "__argentClientFile";
|
|
1716
|
+
var MAX_CONTENT_BYTES = 32 * 1024 * 1024;
|
|
1717
|
+
function interpolatePath(template, args) {
|
|
1718
|
+
let missing = false;
|
|
1719
|
+
const out = template.replace(/\$\{([A-Za-z0-9_]+)\}/g, (_m, name) => {
|
|
1720
|
+
const v = args[name];
|
|
1721
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
1722
|
+
missing = true;
|
|
1723
|
+
return "";
|
|
1724
|
+
}
|
|
1725
|
+
return v;
|
|
1726
|
+
});
|
|
1727
|
+
return missing ? null : out;
|
|
1728
|
+
}
|
|
1729
|
+
async function tarball(sourcePath) {
|
|
1730
|
+
const tarPath = path8.join(tmpdir(), `argent-upload-${randomUUID4()}.tar.gz`);
|
|
1731
|
+
await createTarGzFile(sourcePath, tarPath);
|
|
1732
|
+
return tarPath;
|
|
1733
|
+
}
|
|
1734
|
+
function sha256File(filePath) {
|
|
1735
|
+
return new Promise((resolve7, reject) => {
|
|
1736
|
+
const hash = createHash2("sha256");
|
|
1737
|
+
createReadStream(filePath).on("data", (chunk) => hash.update(chunk)).on("end", () => resolve7(hash.digest("hex"))).on("error", reject);
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
async function uploadTar(tarPath, endpoint, signal) {
|
|
1741
|
+
const init = {
|
|
1742
|
+
method: "POST",
|
|
1743
|
+
headers: {
|
|
1744
|
+
"content-type": "application/gzip",
|
|
1745
|
+
...endpoint.token ? { Authorization: `Bearer ${endpoint.token}` } : {}
|
|
1746
|
+
},
|
|
1747
|
+
body: createReadStream(tarPath),
|
|
1748
|
+
duplex: "half",
|
|
1749
|
+
signal
|
|
1750
|
+
};
|
|
1751
|
+
const res = await fetch(`${endpoint.url}/upload`, init);
|
|
1752
|
+
if (!res.ok) {
|
|
1753
|
+
throw new Error(`Upload to ${endpoint.url}/upload failed: ${res.status} ${res.statusText}`);
|
|
1754
|
+
}
|
|
1755
|
+
const json = await res.json();
|
|
1756
|
+
return json.uploadId;
|
|
1757
|
+
}
|
|
1758
|
+
async function prepareFileInputs(specs, args, opts) {
|
|
1759
|
+
if (!specs || specs.length === 0 || typeof args !== "object" || args === null) {
|
|
1760
|
+
return args;
|
|
1761
|
+
}
|
|
1762
|
+
const record = args;
|
|
1763
|
+
let out = null;
|
|
1764
|
+
for (const spec of specs) {
|
|
1765
|
+
if (spec.skipWhenSet && record[spec.skipWhenSet] !== void 0) continue;
|
|
1766
|
+
if (spec.target in record && typeof record[spec.target] !== "string") continue;
|
|
1767
|
+
const filePath = interpolatePath(spec.path, record);
|
|
1768
|
+
if (filePath === null) continue;
|
|
1769
|
+
if (spec.target in record && record[spec.target] !== filePath) continue;
|
|
1770
|
+
const wire = { [FILE_INPUT_MARKER2]: true, path: filePath };
|
|
1771
|
+
if (spec.kind === "file") {
|
|
1772
|
+
try {
|
|
1773
|
+
const st = await stat2(filePath);
|
|
1774
|
+
if (st.isFile()) {
|
|
1775
|
+
wire.size = st.size;
|
|
1776
|
+
wire.mtimeMs = st.mtimeMs;
|
|
1777
|
+
if (opts.includeContent && st.size <= MAX_CONTENT_BYTES) {
|
|
1778
|
+
wire.content = (await readFile3(filePath)).toString("base64");
|
|
1779
|
+
} else if (opts.includeContent) {
|
|
1780
|
+
wire.contentOmitted = "size-limit";
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
} catch {
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
if (spec.kind === "tar-upload") {
|
|
1787
|
+
const st = await stat2(filePath).catch(() => null);
|
|
1788
|
+
if (st) {
|
|
1789
|
+
wire.size = st.size;
|
|
1790
|
+
wire.mtimeMs = st.mtimeMs;
|
|
1791
|
+
}
|
|
1792
|
+
if (opts.uploadEndpoint && st) {
|
|
1793
|
+
let tarPath = null;
|
|
1794
|
+
try {
|
|
1795
|
+
process.stderr.write(
|
|
1796
|
+
`Uploading ${path8.basename(filePath)} to the remote tool-server...
|
|
1797
|
+
`
|
|
1798
|
+
);
|
|
1799
|
+
tarPath = await tarball(filePath);
|
|
1800
|
+
wire.contentHash = await sha256File(tarPath);
|
|
1801
|
+
wire.uploadId = await uploadTar(tarPath, opts.uploadEndpoint, opts.signal);
|
|
1802
|
+
} finally {
|
|
1803
|
+
if (tarPath) await rm2(tarPath, { force: true }).catch(() => {
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
out = out ?? { ...record };
|
|
1809
|
+
out[spec.target] = wire;
|
|
1810
|
+
}
|
|
1811
|
+
return out ?? args;
|
|
1812
|
+
}
|
|
1813
|
+
function isAllowedClientFilePath(p) {
|
|
1814
|
+
if (!path8.isAbsolute(p)) return false;
|
|
1815
|
+
const segments = p.split(/[\\/]+/);
|
|
1816
|
+
if (segments.includes("..")) return false;
|
|
1817
|
+
const file = segments[segments.length - 1] ?? "";
|
|
1818
|
+
if (!FLOW_FILE_NAME_PATTERN.test(file)) return false;
|
|
1819
|
+
return segments[segments.length - 3] === ".argent" && segments[segments.length - 2] === "flows";
|
|
1820
|
+
}
|
|
1821
|
+
function isClientFileDirective2(value) {
|
|
1822
|
+
return !!value && typeof value === "object" && value[CLIENT_FILE_MARKER2] === true && typeof value.path === "string" && typeof value.content === "string";
|
|
1823
|
+
}
|
|
1824
|
+
async function applyClientFileDirectives(result) {
|
|
1825
|
+
const written = [];
|
|
1826
|
+
async function walk(value) {
|
|
1827
|
+
if (isClientFileDirective2(value)) {
|
|
1828
|
+
if (!isAllowedClientFilePath(value.path)) return null;
|
|
1829
|
+
try {
|
|
1830
|
+
await mkdir3(path8.dirname(value.path), { recursive: true });
|
|
1831
|
+
await writeFile3(value.path, value.content, "utf8");
|
|
1832
|
+
written.push(value.path);
|
|
1833
|
+
return value.path;
|
|
1834
|
+
} catch {
|
|
1835
|
+
return null;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
if (Array.isArray(value)) {
|
|
1839
|
+
return Promise.all(value.map(walk));
|
|
1840
|
+
}
|
|
1841
|
+
if (value && typeof value === "object") {
|
|
1842
|
+
const out = {};
|
|
1843
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1844
|
+
out[k] = await walk(v);
|
|
1845
|
+
}
|
|
1846
|
+
return out;
|
|
1847
|
+
}
|
|
1848
|
+
return value;
|
|
1849
|
+
}
|
|
1850
|
+
const rewritten = await walk(result);
|
|
1851
|
+
return { result: rewritten, written };
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
// ../argent-tools-client/src/tools-client.ts
|
|
1855
|
+
var ToolInvocationError = class extends Error {
|
|
1856
|
+
errorCode;
|
|
1857
|
+
errorKind;
|
|
1858
|
+
issues;
|
|
1859
|
+
constructor(message, signal) {
|
|
1860
|
+
super(message);
|
|
1861
|
+
this.name = "ToolInvocationError";
|
|
1862
|
+
this.errorCode = signal?.errorCode;
|
|
1863
|
+
this.errorKind = signal?.errorKind;
|
|
1864
|
+
this.issues = signal?.issues;
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
function authHeaders2(token) {
|
|
1868
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
1869
|
+
}
|
|
1870
|
+
async function consumeToolStream(body, onProgress, signal) {
|
|
1871
|
+
let final;
|
|
1872
|
+
const handleLine = (line) => {
|
|
1873
|
+
if (!line.trim()) return;
|
|
1874
|
+
const msg = JSON.parse(line);
|
|
1875
|
+
if (msg.event === "progress") onProgress(msg.data);
|
|
1876
|
+
else if (msg.event === "result") final = { data: msg.data, note: msg.note };
|
|
1877
|
+
else if (msg.event === "error") {
|
|
1878
|
+
throw new ToolInvocationError(msg.error ?? "tool invocation failed", {
|
|
1879
|
+
errorCode: msg.error_code,
|
|
1880
|
+
errorKind: msg.error_kind
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
};
|
|
1884
|
+
const reader = body.getReader();
|
|
1885
|
+
const decoder = new TextDecoder();
|
|
1886
|
+
let buffered = "";
|
|
1887
|
+
try {
|
|
1888
|
+
for (; ; ) {
|
|
1889
|
+
const { done, value } = await reader.read();
|
|
1890
|
+
if (done) break;
|
|
1891
|
+
buffered += decoder.decode(value, { stream: true });
|
|
1892
|
+
let newline;
|
|
1893
|
+
while ((newline = buffered.indexOf("\n")) !== -1) {
|
|
1894
|
+
signal?.throwIfAborted();
|
|
1895
|
+
const line = buffered.slice(0, newline);
|
|
1896
|
+
buffered = buffered.slice(newline + 1);
|
|
1897
|
+
handleLine(line);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
buffered += decoder.decode();
|
|
1901
|
+
if (buffered.trim()) handleLine(buffered);
|
|
1902
|
+
} catch (err) {
|
|
1903
|
+
void reader.cancel().catch(() => {
|
|
1904
|
+
});
|
|
1905
|
+
throw err;
|
|
1906
|
+
}
|
|
1907
|
+
signal?.throwIfAborted();
|
|
1908
|
+
if (!final) {
|
|
1909
|
+
throw new Error("tool stream ended without a result \u2014 connection lost mid-run?");
|
|
1910
|
+
}
|
|
1911
|
+
const { result: data } = await applyClientFileDirectives(final.data);
|
|
1912
|
+
return { data, note: final.note };
|
|
1913
|
+
}
|
|
1914
|
+
function errorBodyMessage(body) {
|
|
1915
|
+
if (Array.isArray(body.issues) && typeof body.message === "string") return body.message;
|
|
1916
|
+
return body.error ?? body.message;
|
|
1917
|
+
}
|
|
1918
|
+
function createToolsClient(options = {}) {
|
|
1919
|
+
let cached = null;
|
|
1920
|
+
async function baseUrl() {
|
|
1921
|
+
const resolved = await getResolvedToolsUrl();
|
|
1922
|
+
if (resolved.url) {
|
|
1923
|
+
return { url: resolved.url, token: resolved.token ?? "" };
|
|
1924
|
+
}
|
|
1925
|
+
if (cached) return cached;
|
|
1926
|
+
if (!options.paths) {
|
|
1927
|
+
throw new Error(
|
|
1928
|
+
"tools-client: cannot spawn tool-server without `paths`; set ARGENT_TOOLS_URL or pass paths to createToolsClient()"
|
|
1929
|
+
);
|
|
1930
|
+
}
|
|
1931
|
+
cached = await ensureToolsServer(options.paths);
|
|
1932
|
+
return cached;
|
|
1933
|
+
}
|
|
1934
|
+
async function fetchTools(opts) {
|
|
1935
|
+
opts?.signal?.throwIfAborted();
|
|
1936
|
+
const { url, token } = await baseUrl();
|
|
1937
|
+
const res = await fetch(`${url}/tools`, {
|
|
1938
|
+
headers: authHeaders2(token),
|
|
1939
|
+
signal: opts?.signal
|
|
1940
|
+
});
|
|
1941
|
+
if (!res.ok) throw new Error(`GET /tools failed: ${res.status} ${res.statusText}`);
|
|
1942
|
+
const json = await res.json();
|
|
1943
|
+
return json.tools;
|
|
1944
|
+
}
|
|
1945
|
+
async function fetchTool(name, opts) {
|
|
1946
|
+
const tools = await fetchTools(opts);
|
|
1947
|
+
return tools.find((t) => t.name === name) ?? null;
|
|
1948
|
+
}
|
|
1949
|
+
async function callTool(name, args, opts) {
|
|
1950
|
+
opts?.signal?.throwIfAborted();
|
|
1951
|
+
const { url, token } = await baseUrl();
|
|
1952
|
+
let finalArgs = args;
|
|
1953
|
+
const meta = await fetchTool(name, { signal: opts?.signal });
|
|
1954
|
+
if (meta?.fileInputs?.length) {
|
|
1955
|
+
const { url: routedUrl } = await getResolvedToolsUrl();
|
|
1956
|
+
const isRemote = routedUrl !== null;
|
|
1957
|
+
finalArgs = await prepareFileInputs(meta.fileInputs, args ?? {}, {
|
|
1958
|
+
includeContent: isRemote,
|
|
1959
|
+
uploadEndpoint: isRemote ? { url, token } : void 0,
|
|
1960
|
+
signal: opts?.signal
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
const res = await fetch(`${url}/tools/${encodeURIComponent(name)}`, {
|
|
1964
|
+
method: "POST",
|
|
1965
|
+
headers: {
|
|
1966
|
+
"Content-Type": "application/json",
|
|
1967
|
+
...opts?.onProgress ? { Accept: "application/x-ndjson" } : {},
|
|
1968
|
+
...authHeaders2(token)
|
|
1969
|
+
},
|
|
1970
|
+
body: JSON.stringify(finalArgs ?? {}),
|
|
1971
|
+
signal: opts?.signal
|
|
1972
|
+
});
|
|
1973
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
1974
|
+
if (opts?.onProgress && res.ok && res.body && contentType.includes("application/x-ndjson")) {
|
|
1975
|
+
return consumeToolStream(res.body, opts.onProgress, opts.signal);
|
|
1976
|
+
}
|
|
1977
|
+
const json = await res.json().catch(() => {
|
|
1978
|
+
opts?.signal?.throwIfAborted();
|
|
1979
|
+
return {};
|
|
1980
|
+
});
|
|
1981
|
+
if (!res.ok) {
|
|
1982
|
+
throw new ToolInvocationError(errorBodyMessage(json) ?? `${res.status} ${res.statusText}`, {
|
|
1983
|
+
errorCode: json.error_code,
|
|
1984
|
+
errorKind: json.error_kind,
|
|
1985
|
+
issues: Array.isArray(json.issues) ? json.issues : void 0
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1988
|
+
opts?.signal?.throwIfAborted();
|
|
1989
|
+
const { result: data } = await applyClientFileDirectives(json.data);
|
|
1990
|
+
return { data, note: json.note };
|
|
1991
|
+
}
|
|
1992
|
+
return { fetchTools, fetchTool, callTool, baseUrl };
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
// ../argent-tools-client/src/artifacts.ts
|
|
1996
|
+
import { copyFile, mkdir as mkdir4, readFile as readFile4, realpath, rm as rm3, stat as stat3, writeFile as writeFile4 } from "node:fs/promises";
|
|
1997
|
+
import { constants as fsConstants } from "node:fs";
|
|
1998
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
1999
|
+
import { basename as basename4, dirname as dirname5, extname as extname2, isAbsolute as isAbsolute4, join as join9, normalize, resolve as resolve6, sep as sep2 } from "node:path";
|
|
2000
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2001
|
+
var ARTIFACT_MARKER2 = "__argentArtifact";
|
|
2002
|
+
function isArtifactHandle(value) {
|
|
2003
|
+
return !!value && typeof value === "object" && value[ARTIFACT_MARKER2] === true && typeof value.id === "string" && typeof value.filename === "string";
|
|
2004
|
+
}
|
|
2005
|
+
var SESSION_ID = null;
|
|
2006
|
+
function sessionId() {
|
|
2007
|
+
if (!SESSION_ID) {
|
|
2008
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, (m) => m === "T" ? "-" : "").slice(0, 15);
|
|
2009
|
+
SESSION_ID = `${stamp}-${process.pid}`;
|
|
2010
|
+
}
|
|
2011
|
+
return SESSION_ID;
|
|
2012
|
+
}
|
|
2013
|
+
function sanitizeSegment(segment) {
|
|
2014
|
+
return segment.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
2015
|
+
}
|
|
2016
|
+
function projectSlug() {
|
|
2017
|
+
const cwd = process.cwd();
|
|
2018
|
+
const hash = createHash3("sha1").update(cwd).digest("hex").slice(0, 6);
|
|
2019
|
+
const name = sanitizeSegment(basename4(cwd)) || "root";
|
|
2020
|
+
return `${name}-${hash}`;
|
|
2021
|
+
}
|
|
2022
|
+
function artifactsRoot() {
|
|
2023
|
+
return process.env.ARGENT_ARTIFACTS_DIR ?? join9(tmpdir2(), "argent-artifacts");
|
|
2024
|
+
}
|
|
2025
|
+
function artifactDir(deviceId) {
|
|
2026
|
+
const parts = [artifactsRoot(), projectSlug(), sessionId()];
|
|
2027
|
+
if (deviceId) parts.push(sanitizeSegment(deviceId));
|
|
2028
|
+
return join9(...parts);
|
|
2029
|
+
}
|
|
2030
|
+
function durableBaseDir() {
|
|
2031
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
2032
|
+
return projectRoot ?? dirname5(argentHomeDir());
|
|
2033
|
+
}
|
|
2034
|
+
var ALLOWED_SAVE_DIRS = /* @__PURE__ */ new Set([normalize(".argent/recordings")]);
|
|
2035
|
+
var RECORDINGS_SAVE_DIR = normalize(".argent/recordings");
|
|
2036
|
+
function configuredRecordingsDir() {
|
|
2037
|
+
let value;
|
|
2038
|
+
try {
|
|
2039
|
+
value = getConfigValueByKey("recordings.directory");
|
|
2040
|
+
} catch {
|
|
2041
|
+
return null;
|
|
2042
|
+
}
|
|
2043
|
+
if (typeof value !== "string") return null;
|
|
2044
|
+
const trimmed = value.trim();
|
|
2045
|
+
if (trimmed === "") return null;
|
|
2046
|
+
const home = dirname5(argentHomeDir());
|
|
2047
|
+
const expanded = trimmed === "~" ? home : trimmed.startsWith("~/") || trimmed.startsWith(`~${sep2}`) ? join9(home, trimmed.slice(2)) : trimmed;
|
|
2048
|
+
return resolve6(durableBaseDir(), expanded);
|
|
2049
|
+
}
|
|
2050
|
+
var MAX_DURABLE_BYTES = 2 * 1024 * 1024 * 1024;
|
|
2051
|
+
async function readCapped(res, cap) {
|
|
2052
|
+
const headers = res.headers;
|
|
2053
|
+
const declared = Number(headers?.get?.("content-length"));
|
|
2054
|
+
if (Number.isFinite(declared) && declared > cap) return null;
|
|
2055
|
+
const body = res.body;
|
|
2056
|
+
if (!body?.getReader) {
|
|
2057
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
2058
|
+
return buf.length > cap ? null : buf;
|
|
2059
|
+
}
|
|
2060
|
+
const reader = body.getReader();
|
|
2061
|
+
const chunks = [];
|
|
2062
|
+
let total = 0;
|
|
2063
|
+
for (; ; ) {
|
|
2064
|
+
const { done, value } = await reader.read();
|
|
2065
|
+
if (done) break;
|
|
2066
|
+
total += value.byteLength;
|
|
2067
|
+
if (total > cap) {
|
|
2068
|
+
await reader.cancel().catch(() => {
|
|
2069
|
+
});
|
|
2070
|
+
return null;
|
|
2071
|
+
}
|
|
2072
|
+
chunks.push(Buffer.from(value));
|
|
2073
|
+
}
|
|
2074
|
+
return Buffer.concat(chunks);
|
|
2075
|
+
}
|
|
2076
|
+
async function writeDurableUnique(dir, filename, write) {
|
|
2077
|
+
const ext = extname2(filename);
|
|
2078
|
+
const stem = filename.slice(0, filename.length - ext.length);
|
|
2079
|
+
for (let i = 1; i <= 1e3; i++) {
|
|
2080
|
+
const candidate = i === 1 ? filename : `${stem} (${i})${ext}`;
|
|
2081
|
+
const path10 = join9(dir, candidate);
|
|
2082
|
+
try {
|
|
2083
|
+
await write(path10);
|
|
2084
|
+
return path10;
|
|
2085
|
+
} catch (err) {
|
|
2086
|
+
if (err?.code === "EEXIST") continue;
|
|
2087
|
+
throw err;
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
return null;
|
|
2091
|
+
}
|
|
2092
|
+
function durableSaveTarget(handle) {
|
|
2093
|
+
if (typeof handle.saveDir !== "string" || !handle.saveDir || handle.archive) return null;
|
|
2094
|
+
const rel = normalize(handle.saveDir);
|
|
2095
|
+
if (isAbsolute4(rel) || rel === ".." || rel.startsWith(`..${sep2}`) || rel.split(sep2).includes("..")) {
|
|
2096
|
+
return null;
|
|
2097
|
+
}
|
|
2098
|
+
if (!ALLOWED_SAVE_DIRS.has(rel)) return null;
|
|
2099
|
+
if (rel === RECORDINGS_SAVE_DIR) {
|
|
2100
|
+
const configured = configuredRecordingsDir();
|
|
2101
|
+
if (configured) {
|
|
2102
|
+
return {
|
|
2103
|
+
dir: configured,
|
|
2104
|
+
path: join9(configured, sanitizeSegment(handle.filename)),
|
|
2105
|
+
base: configured,
|
|
2106
|
+
rel: ""
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
const base = durableBaseDir();
|
|
2111
|
+
const dir = join9(base, rel);
|
|
2112
|
+
return { dir, path: join9(dir, sanitizeSegment(handle.filename)), base, rel };
|
|
2113
|
+
}
|
|
2114
|
+
async function confineToRealBase(dir, base, rel) {
|
|
2115
|
+
try {
|
|
2116
|
+
const realDir = await realpath(dir);
|
|
2117
|
+
const realBase = await realpath(base);
|
|
2118
|
+
return realDir === join9(realBase, rel);
|
|
2119
|
+
} catch {
|
|
2120
|
+
return false;
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
async function resolveLocalFile(handle) {
|
|
2124
|
+
if (!handle.hostPath) return null;
|
|
2125
|
+
try {
|
|
2126
|
+
const st = await stat3(handle.hostPath);
|
|
2127
|
+
if (handle.archive) {
|
|
2128
|
+
return st.isDirectory() ? handle.hostPath : null;
|
|
2129
|
+
}
|
|
2130
|
+
if (!st.isFile()) return null;
|
|
2131
|
+
if (st.size !== handle.size) return null;
|
|
2132
|
+
if (handle.mtimeMs != null && Math.round(st.mtimeMs) !== Math.round(handle.mtimeMs)) {
|
|
2133
|
+
return null;
|
|
2134
|
+
}
|
|
2135
|
+
return handle.hostPath;
|
|
2136
|
+
} catch {
|
|
2137
|
+
return null;
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
async function downloadAndExtractArchive(handle, data, dir) {
|
|
2141
|
+
const tarball2 = join9(dir, `${sanitizeSegment(handle.filename)}.tar.gz`);
|
|
2142
|
+
try {
|
|
2143
|
+
await writeFile4(tarball2, data);
|
|
2144
|
+
return await safeExtractTarGz(tarball2, dir, handle.filename);
|
|
2145
|
+
} catch {
|
|
2146
|
+
return null;
|
|
2147
|
+
} finally {
|
|
2148
|
+
await rm3(tarball2, { force: true }).catch(() => {
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
async function materializeArtifacts(result, ctx) {
|
|
2153
|
+
const images = [];
|
|
2154
|
+
const fetchFn = ctx.fetchImpl ?? fetch;
|
|
2155
|
+
const authHeaders3 = ctx.authToken ? { Authorization: `Bearer ${ctx.authToken}` } : {};
|
|
2156
|
+
const dir = artifactDir(ctx.deviceId);
|
|
2157
|
+
let dirReady = false;
|
|
2158
|
+
async function ensureDir() {
|
|
2159
|
+
if (!dirReady) {
|
|
2160
|
+
await mkdir4(dir, { recursive: true });
|
|
2161
|
+
dirReady = true;
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
async function walk(value) {
|
|
2165
|
+
if (isArtifactHandle(value)) {
|
|
2166
|
+
const localPath = await resolveLocalFile(value);
|
|
2167
|
+
const saveTarget = durableSaveTarget(value);
|
|
2168
|
+
if (saveTarget) {
|
|
2169
|
+
const filename = basename4(saveTarget.path);
|
|
2170
|
+
try {
|
|
2171
|
+
await mkdir4(saveTarget.dir, { recursive: true });
|
|
2172
|
+
if (!await confineToRealBase(saveTarget.dir, saveTarget.base, saveTarget.rel)) {
|
|
2173
|
+
return null;
|
|
2174
|
+
}
|
|
2175
|
+
if (localPath) {
|
|
2176
|
+
const finalPath2 = await writeDurableUnique(
|
|
2177
|
+
saveTarget.dir,
|
|
2178
|
+
filename,
|
|
2179
|
+
(p) => copyFile(localPath, p, fsConstants.COPYFILE_EXCL)
|
|
2180
|
+
);
|
|
2181
|
+
if (!finalPath2) return null;
|
|
2182
|
+
if (value.mimeType.startsWith("image/")) {
|
|
2183
|
+
images.push({
|
|
2184
|
+
localPath: finalPath2,
|
|
2185
|
+
data: await readFile4(finalPath2),
|
|
2186
|
+
mimeType: value.mimeType
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
2189
|
+
return finalPath2;
|
|
2190
|
+
}
|
|
2191
|
+
if (!Number.isInteger(value.size) || value.size <= 0 || value.size > MAX_DURABLE_BYTES) {
|
|
2192
|
+
return null;
|
|
2193
|
+
}
|
|
2194
|
+
const res = await fetchFn(`${ctx.toolsUrl}/artifacts/${value.id}`, {
|
|
2195
|
+
headers: authHeaders3,
|
|
2196
|
+
signal: ctx.signal
|
|
2197
|
+
});
|
|
2198
|
+
if (!res.ok) return null;
|
|
2199
|
+
const data = await readCapped(res, value.size);
|
|
2200
|
+
if (!data || data.length !== value.size) return null;
|
|
2201
|
+
const finalPath = await writeDurableUnique(
|
|
2202
|
+
saveTarget.dir,
|
|
2203
|
+
filename,
|
|
2204
|
+
(p) => writeFile4(p, data, { flag: "wx" })
|
|
2205
|
+
);
|
|
2206
|
+
if (!finalPath) return null;
|
|
2207
|
+
if (value.mimeType.startsWith("image/")) {
|
|
2208
|
+
images.push({ localPath: finalPath, data, mimeType: value.mimeType });
|
|
2209
|
+
}
|
|
2210
|
+
return finalPath;
|
|
2211
|
+
} catch {
|
|
2212
|
+
return null;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
if (localPath) {
|
|
2216
|
+
if (value.mimeType.startsWith("image/")) {
|
|
2217
|
+
images.push({ localPath, data: await readFile4(localPath), mimeType: value.mimeType });
|
|
2218
|
+
}
|
|
2219
|
+
return localPath;
|
|
2220
|
+
}
|
|
2221
|
+
try {
|
|
2222
|
+
const res = await fetchFn(`${ctx.toolsUrl}/artifacts/${value.id}`, {
|
|
2223
|
+
headers: authHeaders3,
|
|
2224
|
+
signal: ctx.signal
|
|
2225
|
+
});
|
|
2226
|
+
if (!res.ok) return null;
|
|
2227
|
+
const data = Buffer.from(await res.arrayBuffer());
|
|
2228
|
+
await ensureDir();
|
|
2229
|
+
if (value.archive === "tar.gz") {
|
|
2230
|
+
return await downloadAndExtractArchive(value, data, dir);
|
|
2231
|
+
}
|
|
2232
|
+
if (value.size > 0 && data.length !== value.size) return null;
|
|
2233
|
+
const downloadedPath = join9(dir, sanitizeSegment(value.filename));
|
|
2234
|
+
await writeFile4(downloadedPath, data);
|
|
2235
|
+
if (value.mimeType.startsWith("image/")) {
|
|
2236
|
+
images.push({ localPath: downloadedPath, data, mimeType: value.mimeType });
|
|
2237
|
+
}
|
|
2238
|
+
return downloadedPath;
|
|
2239
|
+
} catch {
|
|
2240
|
+
return null;
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
if (Array.isArray(value)) {
|
|
2244
|
+
return Promise.all(value.map(walk));
|
|
2245
|
+
}
|
|
2246
|
+
if (value && typeof value === "object") {
|
|
2247
|
+
const out = {};
|
|
2248
|
+
for (const [k, v] of Object.entries(value)) {
|
|
2249
|
+
out[k] = await walk(v);
|
|
2250
|
+
}
|
|
2251
|
+
return out;
|
|
2252
|
+
}
|
|
2253
|
+
return value;
|
|
2254
|
+
}
|
|
2255
|
+
const rewritten = await walk(result);
|
|
2256
|
+
return { result: rewritten, images };
|
|
2257
|
+
}
|
|
2258
|
+
function getDeviceIdFromArgs(args) {
|
|
2259
|
+
if (!args || typeof args !== "object") return void 0;
|
|
2260
|
+
const rec = args;
|
|
2261
|
+
if (typeof rec.udid === "string") return rec.udid;
|
|
2262
|
+
if (typeof rec.device_id === "string") return rec.device_id;
|
|
2263
|
+
return void 0;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
// src/bundled-paths.ts
|
|
2267
|
+
import * as fs5 from "node:fs";
|
|
2268
|
+
import * as path9 from "node:path";
|
|
2269
|
+
function readPackageVersion() {
|
|
2270
|
+
try {
|
|
2271
|
+
const pkg = JSON.parse(
|
|
2272
|
+
fs5.readFileSync(path9.join(import.meta.dirname, "..", "package.json"), "utf8")
|
|
2273
|
+
);
|
|
2274
|
+
return pkg.version;
|
|
2275
|
+
} catch {
|
|
2276
|
+
return void 0;
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
var PACKAGE_NAME = "@swmansion/argent";
|
|
2280
|
+
function findDeclaringRoot(startDir) {
|
|
2281
|
+
let dir = startDir;
|
|
2282
|
+
for (; ; ) {
|
|
2283
|
+
if (fs5.existsSync(path9.join(dir, ".argent", "install.json"))) return dir;
|
|
2284
|
+
try {
|
|
2285
|
+
const manifest = JSON.parse(fs5.readFileSync(path9.join(dir, "package.json"), "utf8"));
|
|
2286
|
+
if (manifest.devDependencies?.[PACKAGE_NAME] || manifest.dependencies?.[PACKAGE_NAME] || manifest.optionalDependencies?.[PACKAGE_NAME]) {
|
|
2287
|
+
return dir;
|
|
2288
|
+
}
|
|
2289
|
+
} catch {
|
|
2290
|
+
}
|
|
2291
|
+
const parent = path9.dirname(dir);
|
|
2292
|
+
if (parent === dir) return null;
|
|
2293
|
+
dir = parent;
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
function findStablePackageDir(startDir, packageRoot) {
|
|
2297
|
+
let dir = startDir;
|
|
2298
|
+
for (; ; ) {
|
|
2299
|
+
const candidate = path9.join(dir, "node_modules", PACKAGE_NAME);
|
|
2300
|
+
try {
|
|
2301
|
+
if (fs5.realpathSync(candidate) === packageRoot) return candidate;
|
|
2302
|
+
} catch {
|
|
2303
|
+
}
|
|
2304
|
+
const parent = path9.dirname(dir);
|
|
2305
|
+
if (parent === dir) return void 0;
|
|
2306
|
+
dir = parent;
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
function classifyInstall() {
|
|
2310
|
+
let packageRoot;
|
|
2311
|
+
let dir;
|
|
2312
|
+
let cwd;
|
|
2313
|
+
try {
|
|
2314
|
+
packageRoot = fs5.realpathSync(path9.join(import.meta.dirname, ".."));
|
|
2315
|
+
cwd = process.cwd();
|
|
2316
|
+
dir = cwd;
|
|
2317
|
+
} catch {
|
|
2318
|
+
return { kind: "global" };
|
|
2319
|
+
}
|
|
2320
|
+
for (; ; ) {
|
|
2321
|
+
try {
|
|
2322
|
+
const nmReal = fs5.realpathSync(path9.join(dir, "node_modules"));
|
|
2323
|
+
if (packageRoot === nmReal || packageRoot.startsWith(nmReal + path9.sep)) {
|
|
2324
|
+
return {
|
|
2325
|
+
kind: "local",
|
|
2326
|
+
projectRoot: findDeclaringRoot(cwd) ?? dir,
|
|
2327
|
+
stablePackageDir: findStablePackageDir(cwd, packageRoot)
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
} catch {
|
|
2331
|
+
}
|
|
2332
|
+
const parent = path9.dirname(dir);
|
|
2333
|
+
if (parent === dir) break;
|
|
2334
|
+
dir = parent;
|
|
2335
|
+
}
|
|
2336
|
+
if (packageRoot.includes(`${path9.sep}.yarn${path9.sep}`)) {
|
|
2337
|
+
const declRoot = findDeclaringRoot(cwd);
|
|
2338
|
+
if (declRoot) return { kind: "local", projectRoot: declRoot };
|
|
2339
|
+
}
|
|
2340
|
+
return { kind: "global" };
|
|
2341
|
+
}
|
|
2342
|
+
var classifiedInstall = classifyInstall();
|
|
2343
|
+
var packageDir = classifiedInstall.stablePackageDir ?? path9.join(import.meta.dirname, "..");
|
|
2344
|
+
var BUNDLED_RUNTIME_PATHS = {
|
|
2345
|
+
bundlePath: path9.join(packageDir, "dist", "tool-server.cjs"),
|
|
2346
|
+
simulatorServerDir: path9.join(packageDir, "bin"),
|
|
2347
|
+
nativeDevtoolsDir: path9.join(packageDir, "dylibs"),
|
|
2348
|
+
version: readPackageVersion(),
|
|
2349
|
+
installKind: classifiedInstall.kind,
|
|
2350
|
+
installProjectRoot: classifiedInstall.projectRoot
|
|
2351
|
+
};
|
|
2352
|
+
|
|
2353
|
+
// src/client.ts
|
|
2354
|
+
var ArgentToolError = class extends Error {
|
|
2355
|
+
/** Stable failure code, when the server sent one. */
|
|
2356
|
+
code;
|
|
2357
|
+
/** Failure category, e.g. "subprocess", when the server sent one. */
|
|
2358
|
+
kind;
|
|
2359
|
+
/** Schema issues of a rejected argument object; set only on a validation failure. */
|
|
2360
|
+
issues;
|
|
2361
|
+
constructor(message, details = {}) {
|
|
2362
|
+
super(message);
|
|
2363
|
+
this.name = "ArgentToolError";
|
|
2364
|
+
this.code = details.code;
|
|
2365
|
+
this.kind = details.kind;
|
|
2366
|
+
this.issues = details.issues;
|
|
2367
|
+
}
|
|
2368
|
+
};
|
|
2369
|
+
function listFlags() {
|
|
2370
|
+
return FLAG_REGISTRY.map(({ name, description }) => ({
|
|
2371
|
+
name,
|
|
2372
|
+
description,
|
|
2373
|
+
enabled: isFeatureEnabled(name)
|
|
2374
|
+
}));
|
|
2375
|
+
}
|
|
2376
|
+
function createArgentClient() {
|
|
2377
|
+
let client = createToolsClient({ paths: BUNDLED_RUNTIME_PATHS });
|
|
2378
|
+
async function stopServer() {
|
|
2379
|
+
const stopped = await killToolServer(BUNDLED_RUNTIME_PATHS.bundlePath);
|
|
2380
|
+
client = createToolsClient({ paths: BUNDLED_RUNTIME_PATHS });
|
|
2381
|
+
return stopped;
|
|
2382
|
+
}
|
|
2383
|
+
async function listTools(options) {
|
|
2384
|
+
const tools = await client.fetchTools({ signal: options?.signal });
|
|
2385
|
+
return tools.map(({ name, description, inputSchema }) => ({ name, description, inputSchema }));
|
|
2386
|
+
}
|
|
2387
|
+
async function callTool(name, args = {}, options) {
|
|
2388
|
+
try {
|
|
2389
|
+
const response = await client.callTool(name, args, options);
|
|
2390
|
+
const { url, token } = await client.baseUrl();
|
|
2391
|
+
options?.signal?.throwIfAborted();
|
|
2392
|
+
const { result } = await materializeArtifacts(response.data, {
|
|
2393
|
+
toolsUrl: url,
|
|
2394
|
+
authToken: token,
|
|
2395
|
+
deviceId: getDeviceIdFromArgs(args),
|
|
2396
|
+
signal: options?.signal
|
|
2397
|
+
});
|
|
2398
|
+
options?.signal?.throwIfAborted();
|
|
2399
|
+
return { data: result, note: response.note };
|
|
2400
|
+
} catch (err) {
|
|
2401
|
+
if (err instanceof ToolInvocationError) {
|
|
2402
|
+
throw new ArgentToolError(err.message, {
|
|
2403
|
+
code: err.errorCode,
|
|
2404
|
+
kind: err.errorKind,
|
|
2405
|
+
issues: err.issues
|
|
2406
|
+
});
|
|
2407
|
+
}
|
|
2408
|
+
throw err;
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
return { listTools, callTool, stopServer };
|
|
2412
|
+
}
|
|
2413
|
+
export {
|
|
2414
|
+
ArgentToolError,
|
|
2415
|
+
createArgentClient,
|
|
2416
|
+
listFlags
|
|
2417
|
+
};
|