@swmansion/argent 0.22.1-next.7 → 0.22.1-next.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli-cmds.mjs
CHANGED
|
@@ -7210,7 +7210,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
7210
7210
|
var SESSION_ID2 = randomUUID5();
|
|
7211
7211
|
function readCliVersion() {
|
|
7212
7212
|
if (true) {
|
|
7213
|
-
return "0.22.1-next.
|
|
7213
|
+
return "0.22.1-next.9";
|
|
7214
7214
|
}
|
|
7215
7215
|
return "0.0.0";
|
|
7216
7216
|
}
|
|
@@ -8409,6 +8409,78 @@ import * as fsp from "node:fs/promises";
|
|
|
8409
8409
|
import { constants as fsConstants2 } from "node:fs";
|
|
8410
8410
|
import { createHash as createHash5 } from "node:crypto";
|
|
8411
8411
|
import * as path14 from "node:path";
|
|
8412
|
+
|
|
8413
|
+
// ../argent-cli/src/command-args.ts
|
|
8414
|
+
var UsageError = class extends Error {
|
|
8415
|
+
constructor(message) {
|
|
8416
|
+
super(message);
|
|
8417
|
+
this.name = "UsageError";
|
|
8418
|
+
}
|
|
8419
|
+
};
|
|
8420
|
+
function listChoices(choices) {
|
|
8421
|
+
const quoted = choices.map((c2) => `"${c2}"`);
|
|
8422
|
+
if (quoted.length <= 1) return quoted.join("");
|
|
8423
|
+
return `${quoted.slice(0, -1).join(", ")} or ${quoted[quoted.length - 1]}`;
|
|
8424
|
+
}
|
|
8425
|
+
function resolveName(tok, specs) {
|
|
8426
|
+
if (tok.startsWith("--")) return tok.slice(2) in specs ? tok.slice(2) : null;
|
|
8427
|
+
const short = tok.slice(1);
|
|
8428
|
+
for (const [name, spec] of Object.entries(specs)) {
|
|
8429
|
+
if (spec.alias === short) return name;
|
|
8430
|
+
}
|
|
8431
|
+
return null;
|
|
8432
|
+
}
|
|
8433
|
+
function parseCommandArgs(argv, specs) {
|
|
8434
|
+
const positionals = [];
|
|
8435
|
+
const options = {};
|
|
8436
|
+
for (let i2 = 0; i2 < argv.length; i2++) {
|
|
8437
|
+
const tok = argv[i2];
|
|
8438
|
+
if (tok === "--") {
|
|
8439
|
+
positionals.push(...argv.slice(i2 + 1));
|
|
8440
|
+
break;
|
|
8441
|
+
}
|
|
8442
|
+
if (!tok.startsWith("-") || tok === "-") {
|
|
8443
|
+
positionals.push(tok);
|
|
8444
|
+
continue;
|
|
8445
|
+
}
|
|
8446
|
+
const eq = tok.startsWith("--") ? tok.indexOf("=") : -1;
|
|
8447
|
+
const flag = eq === -1 ? tok : tok.slice(0, eq);
|
|
8448
|
+
const inlineValue = eq === -1 ? void 0 : tok.slice(eq + 1);
|
|
8449
|
+
const name = resolveName(flag, specs);
|
|
8450
|
+
if (name === null) throw new UsageError(`Unknown flag: ${tok}`);
|
|
8451
|
+
const spec = specs[name];
|
|
8452
|
+
const display = `--${name}`;
|
|
8453
|
+
if (spec.kind === "boolean") {
|
|
8454
|
+
if (inlineValue !== void 0) throw new UsageError(`${display} does not take a value`);
|
|
8455
|
+
const next = argv[i2 + 1]?.trim().toLowerCase();
|
|
8456
|
+
if (next === "true" || next === "false") {
|
|
8457
|
+
throw new UsageError(
|
|
8458
|
+
`${display} does not take a value \u2014 it is a switch; omit it to leave the option off`
|
|
8459
|
+
);
|
|
8460
|
+
}
|
|
8461
|
+
options[name] = true;
|
|
8462
|
+
continue;
|
|
8463
|
+
}
|
|
8464
|
+
let value = inlineValue;
|
|
8465
|
+
if (value === void 0) {
|
|
8466
|
+
const next = argv[i2 + 1];
|
|
8467
|
+
if (next !== void 0 && (!next.startsWith("-") || next === "-")) {
|
|
8468
|
+
value = next;
|
|
8469
|
+
i2 += 1;
|
|
8470
|
+
}
|
|
8471
|
+
} else if (value === "") {
|
|
8472
|
+
value = void 0;
|
|
8473
|
+
}
|
|
8474
|
+
if (value === void 0) throw new UsageError(`${display} requires a value`);
|
|
8475
|
+
if (spec.choices && !spec.choices.includes(value)) {
|
|
8476
|
+
throw new UsageError(`${display} must be ${listChoices(spec.choices)}, got "${value}"`);
|
|
8477
|
+
}
|
|
8478
|
+
options[name] = value;
|
|
8479
|
+
}
|
|
8480
|
+
return { positionals, options };
|
|
8481
|
+
}
|
|
8482
|
+
|
|
8483
|
+
// ../argent-cli/src/flow.ts
|
|
8412
8484
|
var STATUS_GLYPH = {
|
|
8413
8485
|
pass: "\u2713",
|
|
8414
8486
|
fail: "\u2717",
|
|
@@ -8484,72 +8556,39 @@ Examples:
|
|
|
8484
8556
|
argent flow run .argent/flows --recursive
|
|
8485
8557
|
`);
|
|
8486
8558
|
}
|
|
8559
|
+
var RUN_OPTIONS = {
|
|
8560
|
+
"update-baselines": { kind: "boolean" },
|
|
8561
|
+
"json": { kind: "boolean" },
|
|
8562
|
+
"json-stream": { kind: "boolean" },
|
|
8563
|
+
"recursive": { kind: "boolean", alias: "r" },
|
|
8564
|
+
"device": { kind: "value" },
|
|
8565
|
+
"platform": { kind: "value" },
|
|
8566
|
+
"output": { kind: "value" }
|
|
8567
|
+
};
|
|
8487
8568
|
function parseRunArgs(argv) {
|
|
8569
|
+
let parsed;
|
|
8570
|
+
try {
|
|
8571
|
+
parsed = parseCommandArgs(argv, RUN_OPTIONS);
|
|
8572
|
+
} catch (err) {
|
|
8573
|
+
if (err instanceof UsageError) throw new FlagParseException(err.message);
|
|
8574
|
+
throw err;
|
|
8575
|
+
}
|
|
8576
|
+
const { positionals, options } = parsed;
|
|
8577
|
+
if (positionals.length > 1) {
|
|
8578
|
+
throw new FlagParseException(
|
|
8579
|
+
`unexpected argument ${JSON.stringify(positionals[1])}; flow run accepts one flow name, YAML file path, or directory path`
|
|
8580
|
+
);
|
|
8581
|
+
}
|
|
8488
8582
|
const out = {
|
|
8489
|
-
updateBaselines:
|
|
8490
|
-
recursive:
|
|
8491
|
-
json:
|
|
8492
|
-
jsonStream:
|
|
8583
|
+
updateBaselines: options["update-baselines"] === true,
|
|
8584
|
+
recursive: options.recursive === true,
|
|
8585
|
+
json: options.json === true,
|
|
8586
|
+
jsonStream: options["json-stream"] === true
|
|
8493
8587
|
};
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
);
|
|
8499
|
-
}
|
|
8500
|
-
out.flowRef = tok;
|
|
8501
|
-
};
|
|
8502
|
-
for (let i2 = 0; i2 < argv.length; i2++) {
|
|
8503
|
-
const tok = argv[i2];
|
|
8504
|
-
if (tok === "--") {
|
|
8505
|
-
for (const rest of argv.slice(i2 + 1)) takePositional(rest);
|
|
8506
|
-
break;
|
|
8507
|
-
}
|
|
8508
|
-
if (!tok.startsWith("-")) {
|
|
8509
|
-
takePositional(tok);
|
|
8510
|
-
continue;
|
|
8511
|
-
}
|
|
8512
|
-
const eq = tok.startsWith("--") ? tok.indexOf("=") : -1;
|
|
8513
|
-
const flag = eq === -1 ? tok : tok.slice(0, eq);
|
|
8514
|
-
const inline = eq === -1 ? void 0 : tok.slice(eq + 1);
|
|
8515
|
-
const takeValue = (name) => {
|
|
8516
|
-
if (inline !== void 0) {
|
|
8517
|
-
if (inline === "") throw new FlagParseException(`${name} requires a value`);
|
|
8518
|
-
return inline;
|
|
8519
|
-
}
|
|
8520
|
-
const v = argv[i2 + 1];
|
|
8521
|
-
if (v === void 0 || v.startsWith("-")) {
|
|
8522
|
-
throw new FlagParseException(`${name} requires a value`);
|
|
8523
|
-
}
|
|
8524
|
-
i2 += 1;
|
|
8525
|
-
return v;
|
|
8526
|
-
};
|
|
8527
|
-
const noValue = (name) => {
|
|
8528
|
-
if (inline !== void 0) throw new FlagParseException(`${name} does not take a value`);
|
|
8529
|
-
const next = argv[i2 + 1]?.trim().toLowerCase();
|
|
8530
|
-
if (next === "true" || next === "false") {
|
|
8531
|
-
throw new FlagParseException(
|
|
8532
|
-
`${name} does not take a value \u2014 it is a switch; omit it to leave the option off`
|
|
8533
|
-
);
|
|
8534
|
-
}
|
|
8535
|
-
};
|
|
8536
|
-
if (flag === "--update-baselines") {
|
|
8537
|
-
noValue("--update-baselines");
|
|
8538
|
-
out.updateBaselines = true;
|
|
8539
|
-
} else if (flag === "--json") {
|
|
8540
|
-
noValue("--json");
|
|
8541
|
-
out.json = true;
|
|
8542
|
-
} else if (flag === "--json-stream") {
|
|
8543
|
-
noValue("--json-stream");
|
|
8544
|
-
out.jsonStream = true;
|
|
8545
|
-
} else if (flag === "--recursive" || flag === "-r") {
|
|
8546
|
-
noValue("--recursive");
|
|
8547
|
-
out.recursive = true;
|
|
8548
|
-
} else if (flag === "--device") out.device = takeValue("--device");
|
|
8549
|
-
else if (flag === "--platform") out.platform = takeValue("--platform");
|
|
8550
|
-
else if (flag === "--output") out.output = takeValue("--output");
|
|
8551
|
-
else throw new FlagParseException(`unknown flag ${tok}`);
|
|
8552
|
-
}
|
|
8588
|
+
if (positionals[0] !== void 0) out.flowRef = positionals[0];
|
|
8589
|
+
if (options.device !== void 0) out.device = options.device;
|
|
8590
|
+
if (options.platform !== void 0) out.platform = options.platform;
|
|
8591
|
+
if (options.output !== void 0) out.output = options.output;
|
|
8553
8592
|
if (out.json && out.jsonStream) {
|
|
8554
8593
|
throw new FlagParseException("--json and --json-stream cannot be combined");
|
|
8555
8594
|
}
|
|
@@ -9351,67 +9390,39 @@ function logsCmd(follow) {
|
|
|
9351
9390
|
}
|
|
9352
9391
|
var StartFlagError = class extends Error {
|
|
9353
9392
|
};
|
|
9393
|
+
var START_OPTIONS = {
|
|
9394
|
+
"help": { kind: "boolean", alias: "h" },
|
|
9395
|
+
"detach": { kind: "boolean", alias: "d" },
|
|
9396
|
+
"force": { kind: "boolean" },
|
|
9397
|
+
"no-auth": { kind: "boolean" },
|
|
9398
|
+
"port": { kind: "value", alias: "p" },
|
|
9399
|
+
"host": { kind: "value" },
|
|
9400
|
+
"idle-timeout": { kind: "value" }
|
|
9401
|
+
};
|
|
9402
|
+
function parseOrStartFlagError(parse) {
|
|
9403
|
+
try {
|
|
9404
|
+
return parse();
|
|
9405
|
+
} catch (err) {
|
|
9406
|
+
if (err instanceof UsageError) throw new StartFlagError(err.message);
|
|
9407
|
+
throw err;
|
|
9408
|
+
}
|
|
9409
|
+
}
|
|
9354
9410
|
function parseStartFlags(argv) {
|
|
9355
|
-
const
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
force: false,
|
|
9361
|
-
noAuth: false,
|
|
9362
|
-
help: false
|
|
9363
|
-
};
|
|
9364
|
-
for (let i2 = 0; i2 < argv.length; i2++) {
|
|
9365
|
-
const tok = argv[i2];
|
|
9366
|
-
const takeValue = (name) => {
|
|
9367
|
-
const v = argv[i2 + 1];
|
|
9368
|
-
if (v === void 0) throw new StartFlagError(`${name} requires a value`);
|
|
9369
|
-
i2 += 1;
|
|
9370
|
-
return v;
|
|
9371
|
-
};
|
|
9372
|
-
if (tok === "--help" || tok === "-h") {
|
|
9373
|
-
flags2.help = true;
|
|
9374
|
-
continue;
|
|
9375
|
-
}
|
|
9376
|
-
if (tok === "--detach" || tok === "-d") {
|
|
9377
|
-
flags2.detach = true;
|
|
9378
|
-
continue;
|
|
9379
|
-
}
|
|
9380
|
-
if (tok === "--force") {
|
|
9381
|
-
flags2.force = true;
|
|
9382
|
-
continue;
|
|
9383
|
-
}
|
|
9384
|
-
if (tok === "--no-auth") {
|
|
9385
|
-
flags2.noAuth = true;
|
|
9386
|
-
continue;
|
|
9387
|
-
}
|
|
9388
|
-
if (tok === "--port" || tok === "-p") {
|
|
9389
|
-
flags2.port = parsePort(takeValue("--port"));
|
|
9390
|
-
continue;
|
|
9391
|
-
}
|
|
9392
|
-
if (tok.startsWith("--port=")) {
|
|
9393
|
-
flags2.port = parsePort(tok.slice("--port=".length));
|
|
9394
|
-
continue;
|
|
9395
|
-
}
|
|
9396
|
-
if (tok === "--host") {
|
|
9397
|
-
flags2.host = takeValue("--host");
|
|
9398
|
-
continue;
|
|
9399
|
-
}
|
|
9400
|
-
if (tok.startsWith("--host=")) {
|
|
9401
|
-
flags2.host = tok.slice("--host=".length);
|
|
9402
|
-
continue;
|
|
9403
|
-
}
|
|
9404
|
-
if (tok === "--idle-timeout") {
|
|
9405
|
-
flags2.idleTimeoutMinutes = parseIdle(takeValue("--idle-timeout"));
|
|
9406
|
-
continue;
|
|
9407
|
-
}
|
|
9408
|
-
if (tok.startsWith("--idle-timeout=")) {
|
|
9409
|
-
flags2.idleTimeoutMinutes = parseIdle(tok.slice("--idle-timeout=".length));
|
|
9410
|
-
continue;
|
|
9411
|
-
}
|
|
9412
|
-
throw new StartFlagError(`Unknown flag: ${tok}`);
|
|
9411
|
+
const { positionals, options } = parseOrStartFlagError(
|
|
9412
|
+
() => parseCommandArgs(argv, START_OPTIONS)
|
|
9413
|
+
);
|
|
9414
|
+
if (positionals.length > 0) {
|
|
9415
|
+
throw new StartFlagError(`Unexpected argument "${positionals[0]}"`);
|
|
9413
9416
|
}
|
|
9414
|
-
return
|
|
9417
|
+
return {
|
|
9418
|
+
port: options.port === void 0 ? null : parsePort(options.port),
|
|
9419
|
+
host: options.host ?? "127.0.0.1",
|
|
9420
|
+
idleTimeoutMinutes: options["idle-timeout"] === void 0 ? 0 : parseIdle(options["idle-timeout"]),
|
|
9421
|
+
detach: options.detach === true,
|
|
9422
|
+
force: options.force === true,
|
|
9423
|
+
noAuth: options["no-auth"] === true,
|
|
9424
|
+
help: options.help === true
|
|
9425
|
+
};
|
|
9415
9426
|
}
|
|
9416
9427
|
var NON_NEGATIVE_INT = /^\d+$/;
|
|
9417
9428
|
function parsePort(raw) {
|
|
@@ -10315,40 +10326,31 @@ function formatLensFeedback(o2) {
|
|
|
10315
10326
|
`[Argent Lens] Feedback from the preview window (round ${o2.round}). ${body}. ` + applyChosen + closing
|
|
10316
10327
|
);
|
|
10317
10328
|
}
|
|
10329
|
+
var LENS_OPTIONS = {
|
|
10330
|
+
help: { kind: "boolean", alias: "h" },
|
|
10331
|
+
forget: { kind: "boolean" },
|
|
10332
|
+
terminal: { kind: "value", alias: "t", choices: ["iterm", "terminal"] },
|
|
10333
|
+
agent: { kind: "value", alias: "a" }
|
|
10334
|
+
};
|
|
10318
10335
|
function parseArgs(argv) {
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
for (let i2 = 0; i2 < argv.length; i2++) {
|
|
10324
|
-
const tok = argv[i2];
|
|
10325
|
-
if (tok === "--help" || tok === "-h") help = true;
|
|
10326
|
-
else if (tok === "--forget") {
|
|
10327
|
-
forget = true;
|
|
10328
|
-
const next = argv[i2 + 1]?.trim().toLowerCase();
|
|
10329
|
-
if (next === "true" || next === "false") {
|
|
10330
|
-
process.stderr.write(`lens: --forget does not take a value; omit it to keep the state
|
|
10331
|
-
`);
|
|
10332
|
-
process.exit(2);
|
|
10333
|
-
}
|
|
10334
|
-
} else if (tok === "--terminal" || tok === "-t") {
|
|
10335
|
-
const v = argv[++i2];
|
|
10336
|
-
if (v === "iterm" || v === "terminal") terminal = v;
|
|
10337
|
-
else {
|
|
10338
|
-
process.stderr.write(`lens: --terminal expects "iterm" or "terminal", got "${v ?? ""}"
|
|
10339
|
-
`);
|
|
10340
|
-
process.exit(2);
|
|
10341
|
-
}
|
|
10342
|
-
} else if (tok === "--agent" || tok === "-a") {
|
|
10343
|
-
agent = argv[++i2];
|
|
10344
|
-
if (!agent) {
|
|
10345
|
-
process.stderr.write(`lens: --agent expects one of: ${agentIds().join(", ")}
|
|
10346
|
-
`);
|
|
10347
|
-
process.exit(2);
|
|
10348
|
-
}
|
|
10336
|
+
try {
|
|
10337
|
+
const { positionals, options } = parseCommandArgs(argv, LENS_OPTIONS);
|
|
10338
|
+
if (positionals.length > 0) {
|
|
10339
|
+
throw new UsageError(`Unexpected argument "${positionals[0]}"`);
|
|
10349
10340
|
}
|
|
10341
|
+
return {
|
|
10342
|
+
terminal: options.terminal,
|
|
10343
|
+
agent: options.agent,
|
|
10344
|
+
help: options.help === true,
|
|
10345
|
+
forget: options.forget === true
|
|
10346
|
+
};
|
|
10347
|
+
} catch (err) {
|
|
10348
|
+
if (!(err instanceof UsageError)) throw err;
|
|
10349
|
+
const hint = err.message.startsWith("--agent") ? ` (one of: ${agentIds().join(", ")})` : "";
|
|
10350
|
+
process.stderr.write(`lens: ${err.message}${hint}
|
|
10351
|
+
`);
|
|
10352
|
+
process.exit(2);
|
|
10350
10353
|
}
|
|
10351
|
-
return { terminal, agent, help, forget };
|
|
10352
10354
|
}
|
|
10353
10355
|
function printHelp2() {
|
|
10354
10356
|
process.stdout.write(
|
|
@@ -10649,53 +10651,22 @@ function colorState(enabled) {
|
|
|
10649
10651
|
return enabled ? import_picocolors.default.green(label) : import_picocolors.default.red(label);
|
|
10650
10652
|
}
|
|
10651
10653
|
var FLAG_NAME_RE = /^[a-zA-Z][a-zA-Z0-9._-]*$/;
|
|
10654
|
+
var TOGGLE_OPTIONS = {
|
|
10655
|
+
scope: { kind: "value", choices: ["project", "global"] }
|
|
10656
|
+
};
|
|
10652
10657
|
function parseToggleArgs(argv, command) {
|
|
10653
|
-
|
|
10654
|
-
|
|
10655
|
-
|
|
10656
|
-
|
|
10657
|
-
|
|
10658
|
-
if (positionalOnly) {
|
|
10659
|
-
if (name !== null) throw new Error(`Unexpected extra argument: "${tok}"`);
|
|
10660
|
-
name = tok;
|
|
10661
|
-
continue;
|
|
10662
|
-
}
|
|
10663
|
-
if (tok === "--") {
|
|
10664
|
-
positionalOnly = true;
|
|
10665
|
-
continue;
|
|
10666
|
-
}
|
|
10667
|
-
if (tok === "--scope") {
|
|
10668
|
-
const v = argv[i2 + 1];
|
|
10669
|
-
if (v === void 0) throw new Error("--scope requires a value (project|global)");
|
|
10670
|
-
scope = parseScope(v);
|
|
10671
|
-
i2 += 1;
|
|
10672
|
-
continue;
|
|
10673
|
-
}
|
|
10674
|
-
if (tok.startsWith("--scope=")) {
|
|
10675
|
-
scope = parseScope(tok.slice("--scope=".length));
|
|
10676
|
-
continue;
|
|
10677
|
-
}
|
|
10678
|
-
if (tok.startsWith("--")) {
|
|
10679
|
-
throw new Error(`Unknown flag: ${tok}`);
|
|
10680
|
-
}
|
|
10681
|
-
if (name !== null) {
|
|
10682
|
-
throw new Error(`Unexpected extra argument: "${tok}"`);
|
|
10683
|
-
}
|
|
10684
|
-
name = tok;
|
|
10685
|
-
}
|
|
10686
|
-
if (name === null) {
|
|
10687
|
-
throw new Error(`Usage: argent ${command} <flag-name> [--scope project|global]`);
|
|
10658
|
+
const { positionals, options } = parseCommandArgs(argv, TOGGLE_OPTIONS);
|
|
10659
|
+
const [name, extra] = positionals;
|
|
10660
|
+
if (extra !== void 0) throw new UsageError(`Unexpected extra argument: "${extra}"`);
|
|
10661
|
+
if (name === void 0) {
|
|
10662
|
+
throw new UsageError(`Usage: argent ${command} <flag-name> [--scope project|global]`);
|
|
10688
10663
|
}
|
|
10689
10664
|
if (!FLAG_NAME_RE.test(name)) {
|
|
10690
|
-
throw new
|
|
10665
|
+
throw new UsageError(
|
|
10691
10666
|
`Invalid flag name "${name}". Must start with a letter and contain only letters, digits, ".", "_", or "-".`
|
|
10692
10667
|
);
|
|
10693
10668
|
}
|
|
10694
|
-
return { name, scope };
|
|
10695
|
-
}
|
|
10696
|
-
function parseScope(raw) {
|
|
10697
|
-
if (raw === "global" || raw === "project") return raw;
|
|
10698
|
-
throw new Error(`--scope must be "project" or "global", got "${raw}"`);
|
|
10669
|
+
return { name, scope: options.scope ?? "global" };
|
|
10699
10670
|
}
|
|
10700
10671
|
function formatAvailableFlags(registry) {
|
|
10701
10672
|
if (registry.length === 0) {
|
|
@@ -10990,36 +10961,23 @@ other scope / the default on the next read.`);
|
|
|
10990
10961
|
reportError(err);
|
|
10991
10962
|
}
|
|
10992
10963
|
}
|
|
10964
|
+
var CONFIG_OPTIONS = {
|
|
10965
|
+
scope: { kind: "value", choices: ["global", "project"] },
|
|
10966
|
+
json: { kind: "boolean" }
|
|
10967
|
+
};
|
|
10993
10968
|
function parseArgs2(argv) {
|
|
10994
|
-
|
|
10995
|
-
|
|
10996
|
-
|
|
10997
|
-
|
|
10998
|
-
|
|
10999
|
-
|
|
11000
|
-
|
|
11001
|
-
|
|
11002
|
-
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
continue;
|
|
11006
|
-
}
|
|
11007
|
-
if (tok.startsWith("--scope=")) {
|
|
11008
|
-
scope = parseScope2(tok.slice("--scope=".length));
|
|
11009
|
-
continue;
|
|
11010
|
-
}
|
|
11011
|
-
if (tok.startsWith("--")) {
|
|
11012
|
-
console.error(`Error: unknown flag "${tok}".`);
|
|
11013
|
-
process.exit(2);
|
|
11014
|
-
}
|
|
11015
|
-
positionals.push(tok);
|
|
10969
|
+
try {
|
|
10970
|
+
const { positionals, options } = parseCommandArgs(argv, CONFIG_OPTIONS);
|
|
10971
|
+
return {
|
|
10972
|
+
positionals,
|
|
10973
|
+
scope: options.scope ?? null,
|
|
10974
|
+
json: options.json === true
|
|
10975
|
+
};
|
|
10976
|
+
} catch (err) {
|
|
10977
|
+
if (!(err instanceof UsageError)) throw err;
|
|
10978
|
+
console.error(`Error: ${err.message}.`);
|
|
10979
|
+
process.exit(2);
|
|
11016
10980
|
}
|
|
11017
|
-
return { positionals, scope, json };
|
|
11018
|
-
}
|
|
11019
|
-
function parseScope2(raw) {
|
|
11020
|
-
if (raw === "global" || raw === "project") return raw;
|
|
11021
|
-
console.error(`Error: --scope must be "global" or "project"${raw ? `, got "${raw}"` : ""}.`);
|
|
11022
|
-
process.exit(2);
|
|
11023
10981
|
}
|
|
11024
10982
|
function wantsHelp(argv) {
|
|
11025
10983
|
return argv.includes("--help") || argv.includes("-h");
|
|
@@ -12382,91 +12340,60 @@ function validateConnectPort(raw) {
|
|
|
12382
12340
|
}
|
|
12383
12341
|
return port;
|
|
12384
12342
|
}
|
|
12343
|
+
var LINK_OPTIONS = {
|
|
12344
|
+
"help": { kind: "boolean", alias: "h" },
|
|
12345
|
+
"yes": { kind: "boolean", alias: "y" },
|
|
12346
|
+
"no-verify": { kind: "boolean" },
|
|
12347
|
+
"host": { kind: "value" },
|
|
12348
|
+
"port": { kind: "value", alias: "p" },
|
|
12349
|
+
"token": { kind: "value" }
|
|
12350
|
+
};
|
|
12385
12351
|
function parseLinkFlags(argv) {
|
|
12352
|
+
const { positionals, options } = parseOrStartFlagError(
|
|
12353
|
+
() => parseCommandArgs(argv, LINK_OPTIONS)
|
|
12354
|
+
);
|
|
12386
12355
|
const flags2 = {
|
|
12387
12356
|
host: null,
|
|
12388
12357
|
port: null,
|
|
12389
12358
|
token: null,
|
|
12390
12359
|
url: null,
|
|
12391
|
-
yes:
|
|
12392
|
-
noVerify:
|
|
12393
|
-
help:
|
|
12360
|
+
yes: options.yes === true,
|
|
12361
|
+
noVerify: options["no-verify"] === true,
|
|
12362
|
+
help: options.help === true
|
|
12394
12363
|
};
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
|
|
12400
|
-
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
continue;
|
|
12406
|
-
}
|
|
12407
|
-
if (tok === "--yes" || tok === "-y") {
|
|
12408
|
-
flags2.yes = true;
|
|
12409
|
-
continue;
|
|
12410
|
-
}
|
|
12411
|
-
if (tok === "--no-verify") {
|
|
12412
|
-
flags2.noVerify = true;
|
|
12413
|
-
continue;
|
|
12414
|
-
}
|
|
12415
|
-
if (tok === "--host") {
|
|
12416
|
-
flags2.host = validateHost(takeValue("--host"));
|
|
12417
|
-
continue;
|
|
12418
|
-
}
|
|
12419
|
-
if (tok.startsWith("--host=")) {
|
|
12420
|
-
flags2.host = validateHost(tok.slice("--host=".length));
|
|
12421
|
-
continue;
|
|
12422
|
-
}
|
|
12423
|
-
if (tok === "--port" || tok === "-p") {
|
|
12424
|
-
flags2.port = validateConnectPort(takeValue("--port"));
|
|
12425
|
-
continue;
|
|
12426
|
-
}
|
|
12427
|
-
if (tok.startsWith("--port=")) {
|
|
12428
|
-
flags2.port = validateConnectPort(tok.slice("--port=".length));
|
|
12429
|
-
continue;
|
|
12430
|
-
}
|
|
12431
|
-
if (tok === "--token") {
|
|
12432
|
-
flags2.token = takeValue("--token");
|
|
12433
|
-
continue;
|
|
12434
|
-
}
|
|
12435
|
-
if (tok.startsWith("--token=")) {
|
|
12436
|
-
flags2.token = tok.slice("--token=".length);
|
|
12437
|
-
continue;
|
|
12438
|
-
}
|
|
12439
|
-
if (!tok.startsWith("-")) {
|
|
12440
|
-
const parsed = parseLinkTarget(tok);
|
|
12441
|
-
if (!parsed) {
|
|
12442
|
-
throw new StartFlagError(
|
|
12443
|
-
`Unrecognized argument "${tok}". Expected an argent://\u2026 pairing string, an http(s):// URL, or flags (see --help).`
|
|
12444
|
-
);
|
|
12445
|
-
}
|
|
12446
|
-
flags2.host = validateHost(parsed.host);
|
|
12447
|
-
flags2.port = validateConnectPort(String(parsed.port));
|
|
12448
|
-
flags2.url = parsed.url;
|
|
12449
|
-
if (parsed.token) flags2.token = parsed.token;
|
|
12450
|
-
continue;
|
|
12364
|
+
if (positionals.length > 1) {
|
|
12365
|
+
throw new StartFlagError(`Unexpected argument "${positionals[1]}"`);
|
|
12366
|
+
}
|
|
12367
|
+
const target = positionals[0];
|
|
12368
|
+
if (target !== void 0) {
|
|
12369
|
+
const parsed = parseLinkTarget(target);
|
|
12370
|
+
if (!parsed) {
|
|
12371
|
+
throw new StartFlagError(
|
|
12372
|
+
`Unrecognized argument "${target}". Expected an argent://\u2026 pairing string, an http(s):// URL, or flags (see --help).`
|
|
12373
|
+
);
|
|
12451
12374
|
}
|
|
12452
|
-
|
|
12375
|
+
flags2.host = validateHost(parsed.host);
|
|
12376
|
+
flags2.port = validateConnectPort(String(parsed.port));
|
|
12377
|
+
flags2.url = parsed.url;
|
|
12378
|
+
if (parsed.token) flags2.token = parsed.token;
|
|
12453
12379
|
}
|
|
12380
|
+
if (options.host !== void 0) flags2.host = validateHost(options.host);
|
|
12381
|
+
if (options.port !== void 0) flags2.port = validateConnectPort(options.port);
|
|
12382
|
+
if (options.token !== void 0) flags2.token = options.token;
|
|
12454
12383
|
return flags2;
|
|
12455
12384
|
}
|
|
12385
|
+
var UNLINK_OPTIONS = {
|
|
12386
|
+
help: { kind: "boolean", alias: "h" },
|
|
12387
|
+
yes: { kind: "boolean", alias: "y" }
|
|
12388
|
+
};
|
|
12456
12389
|
function parseUnlinkFlags(argv) {
|
|
12457
|
-
const
|
|
12458
|
-
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
}
|
|
12463
|
-
if (tok === "--yes" || tok === "-y") {
|
|
12464
|
-
flags2.yes = true;
|
|
12465
|
-
continue;
|
|
12466
|
-
}
|
|
12467
|
-
throw new StartFlagError(`Unknown flag: ${tok}`);
|
|
12390
|
+
const { positionals, options } = parseOrStartFlagError(
|
|
12391
|
+
() => parseCommandArgs(argv, UNLINK_OPTIONS)
|
|
12392
|
+
);
|
|
12393
|
+
if (positionals.length > 0) {
|
|
12394
|
+
throw new StartFlagError(`Unexpected argument "${positionals[0]}"`);
|
|
12468
12395
|
}
|
|
12469
|
-
return
|
|
12396
|
+
return { yes: options.yes === true, help: options.help === true };
|
|
12470
12397
|
}
|
|
12471
12398
|
function printLinkHelp() {
|
|
12472
12399
|
console.log(`Usage: argent link [<target>] [flags]
|
|
@@ -12824,61 +12751,6 @@ async function unlink3(argv) {
|
|
|
12824
12751
|
|
|
12825
12752
|
// ../argent-cli/src/telemetry.ts
|
|
12826
12753
|
var import_picocolors5 = __toESM(require_picocolors(), 1);
|
|
12827
|
-
|
|
12828
|
-
// ../argent-cli/src/command-args.ts
|
|
12829
|
-
var UsageError = class extends Error {
|
|
12830
|
-
constructor(message) {
|
|
12831
|
-
super(message);
|
|
12832
|
-
this.name = "UsageError";
|
|
12833
|
-
}
|
|
12834
|
-
};
|
|
12835
|
-
function parseCommandArgs(argv, specs) {
|
|
12836
|
-
const positionals = [];
|
|
12837
|
-
const options = {};
|
|
12838
|
-
for (let i2 = 0; i2 < argv.length; i2++) {
|
|
12839
|
-
const tok = argv[i2];
|
|
12840
|
-
if (tok === "--") {
|
|
12841
|
-
positionals.push(...argv.slice(i2 + 1));
|
|
12842
|
-
break;
|
|
12843
|
-
}
|
|
12844
|
-
if (!tok.startsWith("--")) {
|
|
12845
|
-
positionals.push(tok);
|
|
12846
|
-
continue;
|
|
12847
|
-
}
|
|
12848
|
-
const eq = tok.indexOf("=");
|
|
12849
|
-
const name = eq === -1 ? tok.slice(2) : tok.slice(2, eq);
|
|
12850
|
-
const inlineValue = eq === -1 ? void 0 : tok.slice(eq + 1);
|
|
12851
|
-
const spec = specs[name];
|
|
12852
|
-
if (!spec) throw new UsageError(`Unknown flag "${tok}".`);
|
|
12853
|
-
if (spec.kind === "boolean") {
|
|
12854
|
-
if (inlineValue !== void 0) throw new UsageError(`--${name} does not take a value.`);
|
|
12855
|
-
options[name] = true;
|
|
12856
|
-
continue;
|
|
12857
|
-
}
|
|
12858
|
-
let value = inlineValue;
|
|
12859
|
-
if (value === void 0) {
|
|
12860
|
-
const next = argv[i2 + 1];
|
|
12861
|
-
if (next !== void 0 && !next.startsWith("--")) {
|
|
12862
|
-
value = next;
|
|
12863
|
-
i2 += 1;
|
|
12864
|
-
}
|
|
12865
|
-
}
|
|
12866
|
-
if (value === void 0 || value === "") {
|
|
12867
|
-
throw new UsageError(
|
|
12868
|
-
`--${name} requires a value${spec.choices ? ` (${spec.choices.join("|")})` : ""}.`
|
|
12869
|
-
);
|
|
12870
|
-
}
|
|
12871
|
-
if (spec.choices && !spec.choices.includes(value)) {
|
|
12872
|
-
throw new UsageError(
|
|
12873
|
-
`--${name} must be one of ${spec.choices.map((c2) => `"${c2}"`).join(", ")} (got "${value}").`
|
|
12874
|
-
);
|
|
12875
|
-
}
|
|
12876
|
-
options[name] = value;
|
|
12877
|
-
}
|
|
12878
|
-
return { positionals, options };
|
|
12879
|
-
}
|
|
12880
|
-
|
|
12881
|
-
// ../argent-cli/src/telemetry.ts
|
|
12882
12754
|
var SCOPES = ["global", "project"];
|
|
12883
12755
|
var TELEMETRY_OPTIONS = {
|
|
12884
12756
|
scope: { kind: "value", choices: SCOPES }
|
package/dist/installer.mjs
CHANGED
|
@@ -16428,7 +16428,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
16428
16428
|
var SESSION_ID = randomUUID4();
|
|
16429
16429
|
function readCliVersion() {
|
|
16430
16430
|
if (true) {
|
|
16431
|
-
return "0.22.1-next.
|
|
16431
|
+
return "0.22.1-next.9";
|
|
16432
16432
|
}
|
|
16433
16433
|
return "0.0.0";
|
|
16434
16434
|
}
|
package/dist/tool-server.cjs
CHANGED
|
@@ -95734,7 +95734,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
95734
95734
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
95735
95735
|
function readCliVersion() {
|
|
95736
95736
|
if (true) {
|
|
95737
|
-
return "0.22.1-next.
|
|
95737
|
+
return "0.22.1-next.9";
|
|
95738
95738
|
}
|
|
95739
95739
|
return "0.0.0";
|
|
95740
95740
|
}
|
|
@@ -111155,7 +111155,7 @@ function isNativeDevtoolsBlockResult(toolId, result) {
|
|
|
111155
111155
|
async function precheckNativeDevtools(api, udid, bundleId) {
|
|
111156
111156
|
if (bundleId !== void 0 && !isInjectableBundleId(bundleId)) {
|
|
111157
111157
|
throw new FailureError(
|
|
111158
|
-
`${bundleId} is an Apple system app: it is
|
|
111158
|
+
`${bundleId} is an Apple system app: it is never the app under test, so Argent native devtools refuse to read one \u2014 treat it as unavailable rather than retrying. ` + NON_INJECTABLE_RECOVERY,
|
|
111159
111159
|
{
|
|
111160
111160
|
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_NOT_INJECTABLE,
|
|
111161
111161
|
failure_stage: "native_devtools_precheck",
|
|
@@ -111854,7 +111854,7 @@ function tcpArtifactHint(err) {
|
|
|
111854
111854
|
return /TCP-transport (?:binary|dylib) not found/.test(message) ? message : void 0;
|
|
111855
111855
|
}
|
|
111856
111856
|
var TVOS_HINT = "This is an Apple TV (tvOS) simulator, which the iOS accessibility service does not support. Use the `describe` tool to read the focused and focusable elements, `tv-remote` (up/down/left/right/select/back/menu/home) to move focus, and `keyboard` to type. See the argent-tv-interact skill.";
|
|
111857
|
-
var NON_INJECTABLE_HINT = "This is an Apple system app (com.apple.*), which
|
|
111857
|
+
var NON_INJECTABLE_HINT = "This is an Apple system app (com.apple.*), which argent's native-devtools instrumentation does not support \u2014 the native view hierarchy is unavailable and restarting the app will NOT help. Take a `screenshot` to see the screen and interact by coordinate. " + NON_INJECTABLE_NATIVE_WARNING;
|
|
111858
111858
|
function emptyTree() {
|
|
111859
111859
|
return parseDescribeResult({
|
|
111860
111860
|
role: "AXGroup",
|
|
@@ -116225,10 +116225,10 @@ Returns { envSetup, appRunning, connected, requiresRestart, state, message, next
|
|
|
116225
116225
|
- state: why devtools are or aren't live, measured from the running process. "connected"; "not_running"; "stale_process" (the process cannot reach this simulator's devtools endpoint \u2014 launched either before argent's instrumentation was in place or against an earlier tool-server's listener \u2014 so restart-app fixes it); "unregistered" (the process IS injected and pointed at this simulator's devtools endpoint yet the service never registered it, so restarting the app cannot help); "connecting" (the process IS injected but launched moments ago and is still connecting, so waiting is what helps); "indeterminate" (the process could not be inspected). Omitted when injectable is false, which is terminal on its own.
|
|
116226
116226
|
- message: the remedy for that state, in full. Omitted when connected or non-injectable. Prefer it over inferring one from the booleans \u2014 it is the only field that can tell you to stop restarting the app.
|
|
116227
116227
|
- nextLaunchWillBeInjected: if you launch this bundle now, native devtools env setup is already in place (always false for a non-injectable app)
|
|
116228
|
-
- injectable: whether
|
|
116228
|
+
- injectable: whether this app is a supported target for Argent native devtools. Apple system apps (bundle ids under com.apple.) are not: they are never the app under test, so the native tools refuse to read one.
|
|
116229
116229
|
|
|
116230
116230
|
Call this before using app-scoped native hierarchy tools or native-network-logs.
|
|
116231
|
-
If injectable is false: treat this as TERMINAL \u2014
|
|
116231
|
+
If injectable is false: treat this as TERMINAL \u2014 the app is not a supported native-devtools target, and no relaunch changes that. Do NOT restart/retry. Use the standard \`describe\` tool (its accessibility path reads the screen without injection) or \`screenshot\` (then interact by coordinate). Do not fall back to the native-devtools feature tools (native-describe-screen, native-find-views, native-full-hierarchy, native-network-logs, native-view-at-point, native-user-interactable-view-at-point) \u2014 they run the same injection precheck and fail with the same non-injectable error.
|
|
116232
116232
|
If appRunning is false and nextLaunchWillBeInjected is true: use launch-app normally.
|
|
116233
116233
|
If requiresRestart is true: call restart-app once, then proceed with the native feature. Read state before acting on a second such reading \u2014 indeterminate reaches this rule too, and its line below bounds it at that one restart.
|
|
116234
116234
|
If state is unregistered: do NOT restart the app again \u2014 it already launched under the terms a restart would recreate. Restart the tool-server (\`argent server stop && argent server start --detach\`), then retry. If it reads unregistered again after that restart, stop: the process loads argent's dylib but never dials, and no further restart on either side changes it \u2014 treat native devtools as unavailable, then use \`describe\` or \`screenshot\` and drive by coordinate.
|
|
@@ -144653,7 +144653,7 @@ var FULL_HIERARCHY_FIELDS = [
|
|
|
144653
144653
|
];
|
|
144654
144654
|
async function unreadableHierarchyReason(nativeApi, bundleId) {
|
|
144655
144655
|
if (!isInjectableBundleId(bundleId)) {
|
|
144656
|
-
return `${bundleId} is an Apple system app: it is
|
|
144656
|
+
return `${bundleId} is an Apple system app: it is never the app under test, so argent's native devtools refuse to read one, and without them a flow has no view hierarchy to resolve selectors against. Replace the selector steps with coordinate ones \u2014 \`tap: { x: 0.5, y: 0.35 }\` takes a point directly and reads no tree \u2014 or target an app argent installs.`;
|
|
144657
144657
|
}
|
|
144658
144658
|
const state3 = await nativeApi.appConnectionState(bundleId).catch(() => "indeterminate");
|
|
144659
144659
|
if (state3 === "connected") {
|
|
@@ -144661,7 +144661,10 @@ async function unreadableHierarchyReason(nativeApi, bundleId) {
|
|
|
144661
144661
|
}
|
|
144662
144662
|
return `${buildAppStateMessage(bundleId, state3)} Flows resolve selectors against the full view hierarchy native devtools serve.`;
|
|
144663
144663
|
}
|
|
144664
|
-
|
|
144664
|
+
function systemAppFlowTargetRefusal(bundleId) {
|
|
144665
|
+
return `${bundleId} is an Apple system app (com.apple.*) - never a valid flow target: it is not the app under test, and argent's native devtools refuse to read one (a system process either never services the read, or describes offscreen UI as if it were the launched app), so this flow has no view hierarchy to resolve selectors against and no relaunch or retry changes this verdict. Replace the selector steps with coordinate ones - \`tap: { x: 0.5, y: 0.35 }\` takes a point directly and reads no tree - or point this flow's \`launch\` step at the app under test.`;
|
|
144666
|
+
}
|
|
144667
|
+
async function queryFullHierarchyTree(registry2, device, target) {
|
|
144665
144668
|
let nativeApi;
|
|
144666
144669
|
try {
|
|
144667
144670
|
const ndRef = nativeDevtoolsRef(device);
|
|
@@ -144672,22 +144675,113 @@ async function queryFullHierarchyTree(registry2, device, launchedNativeApp) {
|
|
|
144672
144675
|
{ cause: err }
|
|
144673
144676
|
);
|
|
144674
144677
|
}
|
|
144675
|
-
|
|
144676
|
-
|
|
144677
|
-
|
|
144678
|
-
|
|
144679
|
-
|
|
144680
|
-
|
|
144681
|
-
|
|
144682
|
-
|
|
144683
|
-
|
|
144684
|
-
|
|
144685
|
-
}
|
|
144686
|
-
|
|
144678
|
+
let bundleId;
|
|
144679
|
+
if (target?.pinned) {
|
|
144680
|
+
bundleId = target.bundleId;
|
|
144681
|
+
if (!isInjectableBundleId(bundleId)) {
|
|
144682
|
+
throw new FailureError(systemAppFlowTargetRefusal(bundleId), {
|
|
144683
|
+
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_NOT_INJECTABLE,
|
|
144684
|
+
failure_stage: "flow_tree_pinned_target",
|
|
144685
|
+
failure_area: "tool_server",
|
|
144686
|
+
error_kind: "validation"
|
|
144687
|
+
});
|
|
144688
|
+
}
|
|
144689
|
+
if (!nativeApi.isConnected(bundleId)) {
|
|
144690
|
+
throw new FailureError(
|
|
144691
|
+
`${bundleId} lost its devtools connection after launch (the app crashed, was terminated, or its socket closed) - restart it (restart-app, or a flow \`launch\` step) so the full view hierarchy is readable; launch-app recovers only the causes that killed the process, since on iOS it just foregrounds one that is still alive`,
|
|
144692
|
+
{
|
|
144693
|
+
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_NOT_CONNECTED,
|
|
144694
|
+
failure_stage: "flow_tree_pinned_target",
|
|
144695
|
+
failure_area: "tool_server",
|
|
144696
|
+
error_kind: "not_found"
|
|
144697
|
+
}
|
|
144698
|
+
);
|
|
144687
144699
|
}
|
|
144700
|
+
let pinnedState;
|
|
144701
|
+
try {
|
|
144702
|
+
pinnedState = await nativeApi.getAppState(bundleId);
|
|
144703
|
+
target.probeAnswered = true;
|
|
144704
|
+
} catch (err) {
|
|
144705
|
+
if (getFailureSignal(err)?.error_code !== FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT) {
|
|
144706
|
+
throw err;
|
|
144707
|
+
}
|
|
144708
|
+
if (target.probeAnswered) {
|
|
144709
|
+
throw new FailureError(
|
|
144710
|
+
`${bundleId} (the launched app) stopped answering Application.getState - the probe timed out although an earlier one in this run answered, so the app's main queue is no longer being serviced. For a pinned app that is usually the suspension iOS applies once a flow leaves it (e.g. a tap that opened another app), and a suspended app's hierarchy is not what is on screen; in-app work blocking the main thread past the probe timeout looks the same from here. Reading anyway parks on that same unserviced queue: certain to time out if the app is suspended, and paying the longer hierarchy timeout to find that out if it is not. If this flow's subject IS the other app, give the flow a \`launch:\` step for that app - it re-pins reads to it, and a pinned read probes only the app it names, so the silent ${bundleId} is never touched; \`tool: launch-app\` or \`tool: restart-app\` naming that app works too - each re-targets reads at the app it starts, and is how a recorded flow switches apps. A foreground-NEUTRAL raw \`tool:\` step does not work here, because demoting the pin sends reads back to auto-resolve, which probes every connection at once and is sunk by this same silent one. If the flow left ${bundleId} but is still about it, make it return before reading the UI. If it never left, the main thread is busy: raise the step's \`timeout:\` so the poll re-reads past the work, or \`launch\` ${bundleId} again.`,
|
|
144711
|
+
{
|
|
144712
|
+
// The timeout's own code, NOT
|
|
144713
|
+
// NATIVE_TARGET_SINGLE_APP_NOT_FOREGROUND: nothing answered, so no
|
|
144714
|
+
// app state was observed to classify it by.
|
|
144715
|
+
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT,
|
|
144716
|
+
failure_stage: "flow_tree_pinned_target",
|
|
144717
|
+
failure_area: "tool_server",
|
|
144718
|
+
error_kind: "timeout"
|
|
144719
|
+
},
|
|
144720
|
+
err instanceof Error ? { cause: err } : void 0
|
|
144721
|
+
);
|
|
144722
|
+
}
|
|
144723
|
+
}
|
|
144724
|
+
if (pinnedState && !chooseFrontmostConnectedApp([pinnedState])) {
|
|
144725
|
+
throw new FailureError(
|
|
144726
|
+
`${bundleId} (the launched app) has no foreground presence at all (applicationState=${pinnedState.applicationState}, foregroundActiveScenes=${pinnedState.foregroundActiveSceneCount}, foregroundInactiveScenes=${pinnedState.foregroundInactiveSceneCount}) - a step in this flow left the app (e.g. a tap that opened another app), so a read of its hierarchy would describe a screen that is not on screen. Transitional states are NOT refused here: an \`inactive\` app, or one still holding a foreground scene, is read as usual - under a system alert or mid-transition it is still the app on screen. If this flow's subject IS another app, give the flow a \`launch:\` step for that app - it re-pins reads to it, and no wedged sibling connection can sink a pinned read; a raw \`tool:\` step demotes the pin and returns reads to frontmost auto-resolve, which works when the app on screen answers but is sunk by a single wedged connection. Otherwise make the flow return to ${bundleId} before reading the UI, or \`launch\` it again.`,
|
|
144727
|
+
{
|
|
144728
|
+
error_code: FAILURE_CODES.NATIVE_TARGET_SINGLE_APP_NOT_FOREGROUND,
|
|
144729
|
+
failure_stage: "flow_tree_pinned_target",
|
|
144730
|
+
failure_area: "tool_server",
|
|
144731
|
+
error_kind: "validation"
|
|
144732
|
+
}
|
|
144733
|
+
);
|
|
144734
|
+
}
|
|
144735
|
+
} else {
|
|
144736
|
+
if (target && nativeApi.listConnectedBundleIds().length === 0) {
|
|
144737
|
+
throw new Error(await unreadableHierarchyReason(nativeApi, target.bundleId));
|
|
144738
|
+
}
|
|
144739
|
+
let resolved;
|
|
144740
|
+
try {
|
|
144741
|
+
resolved = await resolveNativeTargetApp(nativeApi, void 0);
|
|
144742
|
+
} catch (err) {
|
|
144743
|
+
const timedOut = getFailureSignal(err)?.error_code === FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT;
|
|
144744
|
+
if (!timedOut || !target) throw err;
|
|
144745
|
+
if (!isInjectableBundleId(target.bundleId)) {
|
|
144746
|
+
throw new FailureError(
|
|
144747
|
+
systemAppFlowTargetRefusal(target.bundleId),
|
|
144748
|
+
{
|
|
144749
|
+
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_NOT_INJECTABLE,
|
|
144750
|
+
failure_stage: "flow_tree_unpinned_hint",
|
|
144751
|
+
failure_area: "tool_server",
|
|
144752
|
+
error_kind: "validation"
|
|
144753
|
+
},
|
|
144754
|
+
err instanceof Error ? { cause: err } : void 0
|
|
144755
|
+
);
|
|
144756
|
+
}
|
|
144757
|
+
if (!nativeApi.listConnectedBundleIds().includes(target.bundleId)) throw err;
|
|
144758
|
+
let hintState;
|
|
144759
|
+
try {
|
|
144760
|
+
hintState = await nativeApi.getAppState(target.bundleId);
|
|
144761
|
+
} catch (probeErr) {
|
|
144762
|
+
if (getFailureSignal(probeErr)?.error_code === FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT) {
|
|
144763
|
+
throw err;
|
|
144764
|
+
}
|
|
144765
|
+
throw probeErr;
|
|
144766
|
+
}
|
|
144767
|
+
if (!chooseFrontmostConnectedApp([hintState])) {
|
|
144768
|
+
throw new FailureError(
|
|
144769
|
+
`${target.bundleId} (the launched app) has no foreground presence at all (applicationState=${hintState.applicationState}, foregroundActiveScenes=${hintState.foregroundActiveSceneCount}, foregroundInactiveScenes=${hintState.foregroundInactiveSceneCount}) - auto-resolve's probe of every connection timed out and the read fell back to the launched app, but a step in this flow left it (e.g. a tap that opened another app), so reading its hierarchy would describe a screen that is not on screen. Transitional states are NOT refused here: an \`inactive\` app, or one still holding a foreground scene, is read as usual. If this flow's subject IS another app, give the flow a \`launch:\` step for that app; otherwise make the flow return to ${target.bundleId} before reading the UI, or \`launch\` it again.`,
|
|
144770
|
+
{
|
|
144771
|
+
error_code: FAILURE_CODES.NATIVE_TARGET_SINGLE_APP_NOT_FOREGROUND,
|
|
144772
|
+
failure_stage: "flow_tree_unpinned_hint",
|
|
144773
|
+
failure_area: "tool_server",
|
|
144774
|
+
error_kind: "validation"
|
|
144775
|
+
},
|
|
144776
|
+
err instanceof Error ? { cause: err } : void 0
|
|
144777
|
+
);
|
|
144778
|
+
}
|
|
144779
|
+
resolved = { bundleId: target.bundleId };
|
|
144780
|
+
}
|
|
144781
|
+
bundleId = resolved.bundleId;
|
|
144688
144782
|
}
|
|
144689
144783
|
const rawResult = await nativeApi.queryViewHierarchy(
|
|
144690
|
-
|
|
144784
|
+
bundleId,
|
|
144691
144785
|
"ViewHierarchy.getFullHierarchy",
|
|
144692
144786
|
{
|
|
144693
144787
|
fields: FULL_HIERARCHY_FIELDS,
|
|
@@ -144695,11 +144789,11 @@ async function queryFullHierarchyTree(registry2, device, launchedNativeApp) {
|
|
|
144695
144789
|
}
|
|
144696
144790
|
);
|
|
144697
144791
|
if (rawResult.error) {
|
|
144698
|
-
throw new Error(`getFullHierarchy failed for ${
|
|
144792
|
+
throw new Error(`getFullHierarchy failed for ${bundleId}: ${rawResult.error}`);
|
|
144699
144793
|
}
|
|
144700
144794
|
if (!Array.isArray(rawResult.windows) || rawResult.windows.length === 0) {
|
|
144701
144795
|
throw new Error(
|
|
144702
|
-
`getFullHierarchy returned no windows for ${
|
|
144796
|
+
`getFullHierarchy returned no windows for ${bundleId} - it has no window attached to read (backgrounded, or its first window not attached yet), so flows cannot resolve selectors against its view hierarchy; foreground or relaunch it, and if that bundle id is a com.apple.* system process the read resolved to a background system app rather than the app under test, so give this flow a \`launch\` step to pin reads to the right app`
|
|
144703
144797
|
);
|
|
144704
144798
|
}
|
|
144705
144799
|
const { tree, screen } = adaptFullHierarchy(rawResult);
|
|
@@ -144906,13 +145000,15 @@ async function queryVegaTree(device) {
|
|
|
144906
145000
|
}
|
|
144907
145001
|
|
|
144908
145002
|
// ../tool-server/src/tools/flows/flow-tree.ts
|
|
144909
|
-
async function fetchFlowTree(registry2, device,
|
|
145003
|
+
async function fetchFlowTree(registry2, device, target) {
|
|
144910
145004
|
const source = FLOW_TREE_SOURCES[device.platform];
|
|
144911
145005
|
if (!source) return fetchTree(registry2, device);
|
|
144912
|
-
return source(registry2, device,
|
|
145006
|
+
return source(registry2, device, target);
|
|
144913
145007
|
}
|
|
144914
145008
|
var FLOW_TREE_SOURCES = {
|
|
144915
|
-
|
|
145009
|
+
// Only iOS consumes the target: the platforms below resolve their tree
|
|
145010
|
+
// source per-device and never auto-resolve.
|
|
145011
|
+
ios: (registry2, device, target) => queryFullHierarchyTree(registry2, device, target),
|
|
144916
145012
|
android: (registry2, device) => queryAndroidFullHierarchy(registry2, device),
|
|
144917
145013
|
chromium: (registry2, device) => queryChromiumTree(registry2, device),
|
|
144918
145014
|
vega: (_registry, device) => queryVegaTree(device)
|
|
@@ -145118,7 +145214,7 @@ function provenTreeOutage(env) {
|
|
|
145118
145214
|
return proven && proven.deviceId === env.device.id ? proven.error : void 0;
|
|
145119
145215
|
}
|
|
145120
145216
|
function readFlowTree(env) {
|
|
145121
|
-
return fetchFlowTree(env.registry, env.device, env.
|
|
145217
|
+
return fetchFlowTree(env.registry, env.device, env.treeTarget).then((data) => {
|
|
145122
145218
|
if (env.treeOutage) env.treeOutage.proven = void 0;
|
|
145123
145219
|
return data;
|
|
145124
145220
|
});
|
|
@@ -146173,7 +146269,11 @@ async function captureTapSelector(registry2, session, udid, point) {
|
|
|
146173
146269
|
try {
|
|
146174
146270
|
const device = resolveDevice(udid);
|
|
146175
146271
|
const launched = recordedLaunchedApp(session, device.platform);
|
|
146176
|
-
const { tree, source } = await fetchFlowTree(
|
|
146272
|
+
const { tree, source } = await fetchFlowTree(
|
|
146273
|
+
registry2,
|
|
146274
|
+
device,
|
|
146275
|
+
launched ? { bundleId: launched, pinned: false, probeAnswered: false } : void 0
|
|
146276
|
+
);
|
|
146177
146277
|
const node = nodeAtPoint(tree, point);
|
|
146178
146278
|
if (!node) return { warning: "no element found under the tap; kept coordinates (brittle)" };
|
|
146179
146279
|
const selector = deriveSelector(node);
|
|
@@ -149111,6 +149211,7 @@ async function runLaunch(state3, app) {
|
|
|
149111
149211
|
reason: `no app id declared for platform "${device.platform}" \u2014 add a launch entry for it`
|
|
149112
149212
|
};
|
|
149113
149213
|
}
|
|
149214
|
+
state3.treeTarget = void 0;
|
|
149114
149215
|
let restart;
|
|
149115
149216
|
try {
|
|
149116
149217
|
restart = await invokeOnDevice(env, "restart-app", { bundleId });
|
|
@@ -149125,7 +149226,7 @@ async function runLaunch(state3, app) {
|
|
|
149125
149226
|
const gate = await treeSourceGate(registry2, device, bundleId, signal);
|
|
149126
149227
|
if (signal?.aborted) return ABORTED_OUTCOME;
|
|
149127
149228
|
if (gate) return { ok: false, reason: gate };
|
|
149128
|
-
state3.
|
|
149229
|
+
state3.treeTarget = { bundleId, pinned: true, probeAnswered: false };
|
|
149129
149230
|
return { ok: true };
|
|
149130
149231
|
}
|
|
149131
149232
|
async function runChromiumLaunch(state3, app) {
|
|
@@ -149312,7 +149413,11 @@ function createRunFlowTool(registry2) {
|
|
|
149312
149413
|
},
|
|
149313
149414
|
description: `Run a saved flow from the .argent/flows/ directory, or an explicit boundary-managed flow_path.
|
|
149314
149415
|
Steps run in order: \`launch\` starts an app from scratch (terminate + relaunch) and waits until it is
|
|
149315
|
-
ready
|
|
149416
|
+
ready (on iOS it also pins later element lookups to that app rather than auto-detecting the frontmost
|
|
149417
|
+
one); \`tool\` calls dispatch through the registry (a raw \`tool\` step ends that iOS pin, so lookups
|
|
149418
|
+
auto-detect again until the next \`launch\`, though a tool that cannot change the foreground app leaves the
|
|
149419
|
+
launched id as a fallback for a timed-out auto-detect, and \`launch-app\`/\`restart-app\` leave the id they
|
|
149420
|
+
started as that fallback instead); \`tap\`/\`long-press\`/\`type\` resolve a selector to an
|
|
149316
149421
|
element and act on it (\`tap: { on, times: 2 }\` double-taps; \`long-press: { on, duration }\` presses and
|
|
149317
149422
|
holds; \`tap\`/\`long-press\` alternatively take a raw normalized point \u2014 bare \`{ x, y }\` or \`on: { x, y }\`;
|
|
149318
149423
|
any selector may scope its matches geometrically, the CSS combinators read off frames: \`within: <selector>\`
|
|
@@ -150021,14 +150126,17 @@ async function execLeafStep(state3, step, index, scope) {
|
|
|
150021
150126
|
if (step.delayMs && !await sleepOrAbort(step.delayMs, signal)) {
|
|
150022
150127
|
return { ...base, status: "skip", tool: step.name, reason: "run aborted during delay" };
|
|
150023
150128
|
}
|
|
150129
|
+
if (FOREGROUND_CHANGING_TOOLS.has(step.name)) {
|
|
150130
|
+
state3.treeTarget = void 0;
|
|
150131
|
+
if (state3.treeOutage) state3.treeOutage.proven = void 0;
|
|
150132
|
+
} else if (state3.treeTarget?.pinned) {
|
|
150133
|
+
state3.treeTarget = { ...state3.treeTarget, pinned: false };
|
|
150134
|
+
if (state3.treeOutage) state3.treeOutage.proven = void 0;
|
|
150135
|
+
}
|
|
150136
|
+
if (isNestedOrchestratorTool(step.name) && state3.treeOutage) {
|
|
150137
|
+
state3.treeOutage.proven = void 0;
|
|
150138
|
+
}
|
|
150024
150139
|
try {
|
|
150025
|
-
if (FOREGROUND_CHANGING_TOOLS.has(step.name)) {
|
|
150026
|
-
state3.launchedNativeApp = void 0;
|
|
150027
|
-
if (state3.treeOutage) state3.treeOutage.proven = void 0;
|
|
150028
|
-
}
|
|
150029
|
-
if (isNestedOrchestratorTool(step.name) && state3.treeOutage) {
|
|
150030
|
-
state3.treeOutage.proven = void 0;
|
|
150031
|
-
}
|
|
150032
150140
|
const result = await invokeSubTool(registry2, ctx, step.name, args);
|
|
150033
150141
|
if (isUnmetUiWaitResult(step.name, result)) {
|
|
150034
150142
|
const note = result.note;
|
|
@@ -150075,7 +150183,9 @@ async function execLeafStep(state3, step, index, scope) {
|
|
|
150075
150183
|
}
|
|
150076
150184
|
if (step.name === "launch-app" || step.name === "restart-app") {
|
|
150077
150185
|
const launched = args.bundleId;
|
|
150078
|
-
if (typeof launched === "string")
|
|
150186
|
+
if (typeof launched === "string") {
|
|
150187
|
+
state3.treeTarget = { bundleId: launched, pinned: false, probeAnswered: false };
|
|
150188
|
+
}
|
|
150079
150189
|
}
|
|
150080
150190
|
return { ...base, status: "pass", tool: step.name, result, outputHint, args };
|
|
150081
150191
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -70,6 +70,12 @@ On iOS, Android, and Chromium, an id absent from `describe` can still resolve in
|
|
|
70
70
|
|
|
71
71
|
The recorder rechecks each successful `await-ui-element` against the runner tree. Follow any `message` warning and replay each conversion. On Vega, a mismatch usually means the screen changed. A `text` check can also select different elements from the same source. See [Live waits and checks](live-authoring.md#live-waits-and-checks).
|
|
72
72
|
|
|
73
|
+
**On iOS, a `launch:` step also decides which app the runner reads.** A successful `launch:` pins later runner-tree reads to that app, so a read probes only that app instead of fanning out over every connected one to find the frontmost. A pinned read still refuses, naming the reason, when the app has no foreground presence left, when it stops answering after an earlier read got through, when its devtools connection dropped, or when the pinned id is a `com.apple.*` system app.
|
|
74
|
+
|
|
75
|
+
Any raw `tool:` step ends the pin, because its effect on the screen is opaque to the runner, and reads auto-detect the frontmost app again until the next `launch:` re-pins. A tool that cannot change the foreground app leaves the launched id as an unpinned fallback, which takes the read only when auto-detection times out and the launched app vouches for itself with a probe of its own. `launch-app`, `restart-app`, `reinstall-app`, `open-url`, and `button` drop even that; `launch-app` and `restart-app` replace it with the app they just started, still unpinned. Nested `run:` fragments inherit both the pin and its clearing.
|
|
76
|
+
|
|
77
|
+
So on iOS recording and replay can read different apps, not only different projections: recording has no run state and always auto-detects the frontmost connected app, while a replay read between a `launch:` and the next raw `tool:` step reads the launched app.
|
|
78
|
+
|
|
73
79
|
**On iOS, never copy a `role` from `describe` into a flow selector.** The runner derives iOS roles from the UIView class name and `describe` from accessibility traits, so a React Native `Pressable` (class `RCTView`) is `AXGroup` to the runner and `AXButton` to `describe`. Select on `id`/`text`, or confirm the role against the runner's own tree.
|
|
74
80
|
|
|
75
81
|
When several nodes match, the directive decides:
|
|
@@ -99,7 +105,7 @@ Scopes can combine and nest, with at most six scope keys. Use strict selectors f
|
|
|
99
105
|
|
|
100
106
|
Directives stop the flow on failure and skip later steps. `flow-execute` documents their shapes. The available directives are `launch`, `tap`, `long-press`, `type`, `scroll-to`, `pinch`, `rotate`, `await`, `assert`, `wait`, `snapshot`, `run`, `when`, `echo`, and `tool`.
|
|
101
107
|
|
|
102
|
-
Use the launch map for cross-platform flows. A bare launch applies everywhere and becomes an app path on Chromium. The map takes `native:`, `ios:`, `android:`, `vega:`, and `chromium:`. `native:` is one id shared by iOS, Android, and Vega, and a per-platform key overrides it for that platform. `chromium:` accepts a relative or absolute app path. A launch that declares no id for the run's platform is an error, not a cue to switch platforms.
|
|
108
|
+
Use the launch map for cross-platform flows. A bare launch applies everywhere and becomes an app path on Chromium. The map takes `native:`, `ios:`, `android:`, `vega:`, and `chromium:`. `native:` is one id shared by iOS, Android, and Vega, and a per-platform key overrides it for that platform. `chromium:` accepts a relative or absolute app path. A launch that declares no id for the run's platform is an error, not a cue to switch platforms. On iOS, a successful launch also pins later tree reads to that app until the next raw `tool:` step, so read [The runner tree is not the discovery tree](#the-runner-tree-is-not-the-discovery-tree) when a read describes the wrong screen.
|
|
103
109
|
|
|
104
110
|
```yaml
|
|
105
111
|
- launch: { native: com.acme.app, chromium: ../../app }
|
|
@@ -53,9 +53,9 @@ Use the same explicit UDID throughout. Multiple booted simulators are not an inj
|
|
|
53
53
|
|
|
54
54
|
This fallback applies only to `com.apple.*` system apps. A connection failure in another app never authorizes it.
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
Argent refuses `com.apple.*` bundle ids at every native-devtools read that names one, because a system app is never the app under test. The instrumentation has been seen both loading and not loading into one, depending on the simulator runtime — either way it is no basis for a selector. `restart-app`, `launch-app`, and `describe` still work on one; it just never gets a flow tree.
|
|
57
57
|
|
|
58
|
-
Give the flow a `launch:` step as usual. On iOS the launch waits the full devtools budget out, then passes for one of these bundle ids: starting the app is all that step is for, and a coordinate-driven flow needs nothing more. The flow stays e2e; it just pays roughly sixteen seconds at the launch. Where the
|
|
58
|
+
Give the flow a `launch:` step as usual. On iOS the launch waits the full devtools budget out, then passes for one of these bundle ids: starting the app is all that step is for, and a coordinate-driven flow needs nothing more. The flow stays e2e; it just pays roughly sixteen seconds at the launch. Where the refusal bites is selector resolution, and the first selector step reports it there — terminally, naming the coordinate remedy — rather than as a launch failure. The rest of the tree-free form:
|
|
59
59
|
|
|
60
60
|
- Raw `tool: await-ui-element` accessibility checks.
|
|
61
61
|
- Point taps or long-presses derived from `describe`, each named by an echo.
|
|
@@ -66,9 +66,9 @@ Every point tap or long-press in such a flow passes **carrying a warning** for a
|
|
|
66
66
|
|
|
67
67
|
A recorded wait carries a different warning: it adds about one second and reports that the runner tree is unavailable. That warning is expected too. Keep the wait as a raw `tool:` step.
|
|
68
68
|
|
|
69
|
-
Report that the flow
|
|
69
|
+
Report that the flow has no flow tree and its coordinates are not portable. It cannot satisfy the QA contract. Report the artifact and platform blocker instead.
|
|
70
70
|
|
|
71
|
-
A normally injectable app that is broken in the environment gets the same coordinate-only treatment, but not the same launch: there the `launch:` step fails, since the gate withholds its verdict only for a bundle id
|
|
71
|
+
A normally injectable app that is broken in the environment gets the same coordinate-only treatment, but not the same launch: there the `launch:` step fails, since the gate withholds its verdict only for a bundle id argent refuses outright. Start such a flow with a raw `tool: restart-app`, which terminates and relaunches without the readiness gate, and accept that the result is a **fragment** — its first non-echo step is not `launch:`, so the runner never classifies it as e2e, and it cannot complete `argent-qa-flows`, which requires a leading `launch:`. Report the blocker rather than labeling that fallback a completed QA test.
|
|
72
72
|
|
|
73
73
|
## Tree source recovery on Android, Chromium, and Vega
|
|
74
74
|
|