@swmansion/argent 0.22.1-next.6 → 0.22.1-next.8
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 +173 -54
- package/dist/installer.mjs +67 -33
- package/dist/mcp-server.mjs +41 -25
- package/dist/tool-server.cjs +188 -62
- package/package.json +1 -1
- package/skills/argent-create-flow/references/flow-yaml.md +7 -1
- package/skills/argent-create-flow/references/reliability-and-recovery.md +4 -4
package/dist/cli-cmds.mjs
CHANGED
|
@@ -3274,8 +3274,8 @@ function describeExpectedValue(def) {
|
|
|
3274
3274
|
var CONFIG_SCHEMA = [
|
|
3275
3275
|
{
|
|
3276
3276
|
key: "telemetry.enabled",
|
|
3277
|
-
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).",
|
|
3278
|
-
scopes: ["global"],
|
|
3277
|
+
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.",
|
|
3278
|
+
scopes: ["project", "global"],
|
|
3279
3279
|
parse: asBoolean,
|
|
3280
3280
|
merge: "prioritize-restrictive",
|
|
3281
3281
|
// Opt-out: consent.ts reads an unstored value as enabled, so the config
|
|
@@ -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.8";
|
|
7214
7214
|
}
|
|
7215
7215
|
return "0.0.0";
|
|
7216
7216
|
}
|
|
@@ -7572,32 +7572,30 @@ function resolveHostFingerprintAsync() {
|
|
|
7572
7572
|
|
|
7573
7573
|
// ../telemetry/src/consent.ts
|
|
7574
7574
|
import * as fs9 from "node:fs";
|
|
7575
|
-
var cache =
|
|
7575
|
+
var cache = /* @__PURE__ */ new Map();
|
|
7576
7576
|
var sessionOverride = null;
|
|
7577
|
-
function
|
|
7577
|
+
function readConfigOverrideAt(filePath) {
|
|
7578
|
+
const miss = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
7578
7579
|
let stats;
|
|
7579
7580
|
try {
|
|
7580
|
-
stats = fs9.lstatSync(
|
|
7581
|
-
} catch
|
|
7582
|
-
|
|
7583
|
-
cache.current = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
7584
|
-
return null;
|
|
7585
|
-
}
|
|
7586
|
-
cache.current = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
7581
|
+
stats = fs9.lstatSync(filePath);
|
|
7582
|
+
} catch {
|
|
7583
|
+
cache.set(filePath, miss);
|
|
7587
7584
|
return null;
|
|
7588
7585
|
}
|
|
7589
7586
|
if (!stats.isFile()) {
|
|
7590
|
-
cache.
|
|
7587
|
+
cache.set(filePath, miss);
|
|
7591
7588
|
return null;
|
|
7592
7589
|
}
|
|
7593
7590
|
const fingerprint = `${stats.dev}:${stats.ino}:${stats.size}`;
|
|
7594
7591
|
const mtimeMs = stats.mtimeMs;
|
|
7595
|
-
|
|
7596
|
-
|
|
7592
|
+
const cached2 = cache.get(filePath);
|
|
7593
|
+
if (cached2 && cached2.fingerprint === fingerprint && cached2.mtimeMs === mtimeMs) {
|
|
7594
|
+
return cached2.enabledOverride;
|
|
7597
7595
|
}
|
|
7598
7596
|
let parsedEnabled = null;
|
|
7599
7597
|
try {
|
|
7600
|
-
const raw = fs9.readFileSync(
|
|
7598
|
+
const raw = fs9.readFileSync(filePath, "utf8");
|
|
7601
7599
|
const json = JSON.parse(raw);
|
|
7602
7600
|
if (json && typeof json === "object") {
|
|
7603
7601
|
const t2 = json.telemetry;
|
|
@@ -7608,9 +7606,27 @@ function readConfigOverride() {
|
|
|
7608
7606
|
}
|
|
7609
7607
|
} catch {
|
|
7610
7608
|
}
|
|
7611
|
-
cache.
|
|
7609
|
+
cache.set(filePath, { mtimeMs, fingerprint, enabledOverride: parsedEnabled });
|
|
7612
7610
|
return parsedEnabled;
|
|
7613
7611
|
}
|
|
7612
|
+
function readPersistedConsent(cwd) {
|
|
7613
|
+
const globalPath = configFilePath("global");
|
|
7614
|
+
const projectPath = configFilePath("project", { cwd });
|
|
7615
|
+
const global2 = readConfigOverrideAt(globalPath);
|
|
7616
|
+
const project = projectPath === globalPath ? null : readConfigOverrideAt(projectPath);
|
|
7617
|
+
if (project === false && global2 === false) {
|
|
7618
|
+
return { enabled: false, detail: "config.json (project and global)" };
|
|
7619
|
+
}
|
|
7620
|
+
if (project === false) return { enabled: false, detail: "config.json (project)" };
|
|
7621
|
+
if (global2 === false) return { enabled: false, detail: "config.json (global)" };
|
|
7622
|
+
if (project === true || global2 === true) {
|
|
7623
|
+
return {
|
|
7624
|
+
enabled: true,
|
|
7625
|
+
detail: project === true ? "config.json (project)" : "config.json (global)"
|
|
7626
|
+
};
|
|
7627
|
+
}
|
|
7628
|
+
return null;
|
|
7629
|
+
}
|
|
7614
7630
|
function parseFalsy(value) {
|
|
7615
7631
|
if (value === void 0) return false;
|
|
7616
7632
|
const v = value.trim().toLowerCase();
|
|
@@ -7620,7 +7636,7 @@ function isDoNotTrackSet(value) {
|
|
|
7620
7636
|
if (value === void 0 || value.trim() === "") return false;
|
|
7621
7637
|
return !parseFalsy(value);
|
|
7622
7638
|
}
|
|
7623
|
-
function getConsentState(env = process.env) {
|
|
7639
|
+
function getConsentState(env = process.env, cwd = process.cwd()) {
|
|
7624
7640
|
if (isDoNotTrackSet(env.DO_NOT_TRACK)) {
|
|
7625
7641
|
return {
|
|
7626
7642
|
enabled: false,
|
|
@@ -7637,24 +7653,28 @@ function getConsentState(env = process.env) {
|
|
|
7637
7653
|
if (sessionOverride !== null) {
|
|
7638
7654
|
return { enabled: sessionOverride, source: { source: "session_override" } };
|
|
7639
7655
|
}
|
|
7640
|
-
const persisted =
|
|
7641
|
-
if (persisted
|
|
7642
|
-
return {
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7656
|
+
const persisted = readPersistedConsent(cwd);
|
|
7657
|
+
if (persisted !== null) {
|
|
7658
|
+
return {
|
|
7659
|
+
enabled: persisted.enabled,
|
|
7660
|
+
source: { source: "config_file", detail: persisted.detail }
|
|
7661
|
+
};
|
|
7646
7662
|
}
|
|
7647
7663
|
return { enabled: true, source: { source: "default" } };
|
|
7648
7664
|
}
|
|
7649
|
-
function isEnabled(env = process.env) {
|
|
7650
|
-
return getConsentState(env).enabled;
|
|
7665
|
+
function isEnabled(env = process.env, cwd) {
|
|
7666
|
+
return getConsentState(env, cwd).enabled;
|
|
7651
7667
|
}
|
|
7652
|
-
function writeConsentFlag(enabled) {
|
|
7653
|
-
updateConfig(
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7668
|
+
function writeConsentFlag(enabled, scope = "global", options = {}) {
|
|
7669
|
+
updateConfig(
|
|
7670
|
+
(config2) => {
|
|
7671
|
+
const telemetryBlock = typeof config2.telemetry === "object" && config2.telemetry ? config2.telemetry : {};
|
|
7672
|
+
config2.telemetry = { ...telemetryBlock, enabled };
|
|
7673
|
+
},
|
|
7674
|
+
scope,
|
|
7675
|
+
options
|
|
7676
|
+
);
|
|
7677
|
+
cache.clear();
|
|
7658
7678
|
}
|
|
7659
7679
|
|
|
7660
7680
|
// ../telemetry/src/notice.ts
|
|
@@ -7750,13 +7770,13 @@ async function shutdown(timeoutMs = SHORT_FLUSH_TIMEOUT_MS) {
|
|
|
7750
7770
|
state = null;
|
|
7751
7771
|
}
|
|
7752
7772
|
}
|
|
7753
|
-
function markEnabled() {
|
|
7754
|
-
writeConsentFlag(true);
|
|
7773
|
+
function markEnabled(scope = "global") {
|
|
7774
|
+
writeConsentFlag(true, scope);
|
|
7755
7775
|
}
|
|
7756
|
-
async function markDisabled() {
|
|
7776
|
+
async function markDisabled(scope = "global") {
|
|
7757
7777
|
try {
|
|
7758
7778
|
const client2 = getConstructedClient();
|
|
7759
|
-
writeConsentFlag(false);
|
|
7779
|
+
writeConsentFlag(false, scope);
|
|
7760
7780
|
if (client2) {
|
|
7761
7781
|
try {
|
|
7762
7782
|
await raceDrain(client2, SHORT_FLUSH_TIMEOUT_MS);
|
|
@@ -10803,8 +10823,8 @@ Options:
|
|
|
10803
10823
|
console.log("Feature flags (project overrides global):");
|
|
10804
10824
|
const maxName = registryView.reduce((m, f) => Math.max(m, f.name.length), 0);
|
|
10805
10825
|
for (const f of registryView) {
|
|
10806
|
-
const
|
|
10807
|
-
console.log(` ${f.name.padEnd(maxName, " ")} ${colorState(f.enabled)}${
|
|
10826
|
+
const scopeLabel3 = f.scope ? ` (${f.scope})` : "";
|
|
10827
|
+
console.log(` ${f.name.padEnd(maxName, " ")} ${colorState(f.enabled)}${scopeLabel3}`);
|
|
10808
10828
|
console.log(` ${" ".repeat(maxName)} ${f.description}`);
|
|
10809
10829
|
}
|
|
10810
10830
|
}
|
|
@@ -12804,8 +12824,80 @@ async function unlink3(argv) {
|
|
|
12804
12824
|
|
|
12805
12825
|
// ../argent-cli/src/telemetry.ts
|
|
12806
12826
|
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
|
+
var SCOPES = ["global", "project"];
|
|
12883
|
+
var TELEMETRY_OPTIONS = {
|
|
12884
|
+
scope: { kind: "value", choices: SCOPES }
|
|
12885
|
+
};
|
|
12807
12886
|
async function telemetry(args) {
|
|
12808
12887
|
const sub = args[0];
|
|
12888
|
+
let scope = "global";
|
|
12889
|
+
try {
|
|
12890
|
+
const { positionals, options } = parseCommandArgs(args.slice(1), TELEMETRY_OPTIONS);
|
|
12891
|
+
if (positionals.length > 0) {
|
|
12892
|
+
throw new UsageError(`Unexpected argument "${positionals[0]}".`);
|
|
12893
|
+
}
|
|
12894
|
+
if (options.scope !== void 0) scope = options.scope;
|
|
12895
|
+
} catch (err) {
|
|
12896
|
+
if (!(err instanceof UsageError)) throw err;
|
|
12897
|
+
console.error(`Error: ${err.message}`);
|
|
12898
|
+
printUsage3();
|
|
12899
|
+
process.exit(2);
|
|
12900
|
+
}
|
|
12809
12901
|
init("cli");
|
|
12810
12902
|
switch (sub) {
|
|
12811
12903
|
case void 0:
|
|
@@ -12817,10 +12909,10 @@ async function telemetry(args) {
|
|
|
12817
12909
|
await shutdown();
|
|
12818
12910
|
return;
|
|
12819
12911
|
case "enable":
|
|
12820
|
-
await cmdEnable();
|
|
12912
|
+
await cmdEnable(scope);
|
|
12821
12913
|
return;
|
|
12822
12914
|
case "disable":
|
|
12823
|
-
await cmdDisable();
|
|
12915
|
+
await cmdDisable(scope);
|
|
12824
12916
|
return;
|
|
12825
12917
|
case "--help":
|
|
12826
12918
|
case "-h":
|
|
@@ -12835,37 +12927,64 @@ async function telemetry(args) {
|
|
|
12835
12927
|
}
|
|
12836
12928
|
function printUsage3() {
|
|
12837
12929
|
console.log(`Usage:
|
|
12838
|
-
argent telemetry status
|
|
12839
|
-
argent telemetry enable
|
|
12840
|
-
argent telemetry disable Disable telemetry
|
|
12930
|
+
argent telemetry status Show telemetry state and device id
|
|
12931
|
+
argent telemetry enable [--scope global|project] Enable telemetry
|
|
12932
|
+
argent telemetry disable [--scope global|project] Disable telemetry
|
|
12933
|
+
|
|
12934
|
+
The default scope is global (~/.argent/config.json). \`--scope project\` writes
|
|
12935
|
+
<project-root>/.argent/config.json instead \u2014 commit it and telemetry stays off
|
|
12936
|
+
for everyone who clones the repository. \`false\` in either scope wins.
|
|
12841
12937
|
`);
|
|
12842
12938
|
}
|
|
12939
|
+
function scopeLabel2(scope) {
|
|
12940
|
+
return scope === "project" ? "project scope" : "global scope";
|
|
12941
|
+
}
|
|
12843
12942
|
function printStatus() {
|
|
12844
12943
|
const s = status();
|
|
12845
12944
|
const idLabel = s.anonIdPrefix ? `${s.anonIdPrefix}...` : s.hasAnonIdOnDisk ? "present" : "not created";
|
|
12846
12945
|
console.log("telemetry:");
|
|
12847
12946
|
console.log(` state: ${s.enabled ? "enabled" : "disabled"}`);
|
|
12947
|
+
console.log(` source: ${describeSource(s.source)}`);
|
|
12848
12948
|
console.log(` device id: ${idLabel}`);
|
|
12849
12949
|
}
|
|
12850
|
-
|
|
12950
|
+
function describeSource(source) {
|
|
12951
|
+
switch (source.source) {
|
|
12952
|
+
case "env_do_not_track":
|
|
12953
|
+
case "env_argent_telemetry":
|
|
12954
|
+
return `environment (${source.detail ?? source.source})`;
|
|
12955
|
+
case "config_file":
|
|
12956
|
+
return source.detail ?? "config.json";
|
|
12957
|
+
case "session_override":
|
|
12958
|
+
return "this session";
|
|
12959
|
+
case "default":
|
|
12960
|
+
return "default (no opt-out set)";
|
|
12961
|
+
}
|
|
12962
|
+
}
|
|
12963
|
+
async function cmdEnable(scope) {
|
|
12851
12964
|
const wasEnabled = isEnabled();
|
|
12852
|
-
markEnabled();
|
|
12853
|
-
|
|
12854
|
-
|
|
12965
|
+
markEnabled(scope);
|
|
12966
|
+
const nowEnabled = isEnabled();
|
|
12967
|
+
if (!nowEnabled) {
|
|
12968
|
+
console.log(
|
|
12969
|
+
import_picocolors5.default.yellow(
|
|
12970
|
+
`Telemetry set to enabled at ${scopeLabel2(scope)}, but it stays disabled: ${describeSource(status().source)} still opts out.`
|
|
12971
|
+
)
|
|
12972
|
+
);
|
|
12973
|
+
} else if (wasEnabled) {
|
|
12974
|
+
console.log(import_picocolors5.default.dim(`Telemetry was already enabled (written at ${scopeLabel2(scope)}).`));
|
|
12855
12975
|
} else {
|
|
12856
|
-
console.log(import_picocolors5.default.green(
|
|
12976
|
+
console.log(import_picocolors5.default.green(`Telemetry enabled (${scopeLabel2(scope)}).`));
|
|
12857
12977
|
}
|
|
12858
12978
|
await shutdown();
|
|
12859
12979
|
}
|
|
12860
|
-
async function cmdDisable() {
|
|
12980
|
+
async function cmdDisable(scope) {
|
|
12861
12981
|
const wasEnabled = isEnabled();
|
|
12982
|
+
await markDisabled(scope);
|
|
12862
12983
|
if (!wasEnabled) {
|
|
12863
|
-
console.log(import_picocolors5.default.dim(
|
|
12864
|
-
|
|
12865
|
-
|
|
12984
|
+
console.log(import_picocolors5.default.dim(`Telemetry was already disabled (written at ${scopeLabel2(scope)}).`));
|
|
12985
|
+
} else {
|
|
12986
|
+
console.log(import_picocolors5.default.red(`Telemetry disabled (${scopeLabel2(scope)}).`));
|
|
12866
12987
|
}
|
|
12867
|
-
await markDisabled();
|
|
12868
|
-
console.log(import_picocolors5.default.red("Telemetry disabled."));
|
|
12869
12988
|
await shutdown();
|
|
12870
12989
|
}
|
|
12871
12990
|
export {
|
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.8";
|
|
16432
16432
|
}
|
|
16433
16433
|
return "0.0.0";
|
|
16434
16434
|
}
|
|
@@ -16806,32 +16806,30 @@ function resolveHostFingerprintAsync() {
|
|
|
16806
16806
|
|
|
16807
16807
|
// ../telemetry/src/consent.ts
|
|
16808
16808
|
import * as fs8 from "node:fs";
|
|
16809
|
-
var cache =
|
|
16809
|
+
var cache = /* @__PURE__ */ new Map();
|
|
16810
16810
|
var sessionOverride = null;
|
|
16811
|
-
function
|
|
16811
|
+
function readConfigOverrideAt(filePath) {
|
|
16812
|
+
const miss = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
16812
16813
|
let stats;
|
|
16813
16814
|
try {
|
|
16814
|
-
stats = fs8.lstatSync(
|
|
16815
|
-
} catch
|
|
16816
|
-
|
|
16817
|
-
cache.current = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
16818
|
-
return null;
|
|
16819
|
-
}
|
|
16820
|
-
cache.current = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
16815
|
+
stats = fs8.lstatSync(filePath);
|
|
16816
|
+
} catch {
|
|
16817
|
+
cache.set(filePath, miss);
|
|
16821
16818
|
return null;
|
|
16822
16819
|
}
|
|
16823
16820
|
if (!stats.isFile()) {
|
|
16824
|
-
cache.
|
|
16821
|
+
cache.set(filePath, miss);
|
|
16825
16822
|
return null;
|
|
16826
16823
|
}
|
|
16827
16824
|
const fingerprint = `${stats.dev}:${stats.ino}:${stats.size}`;
|
|
16828
16825
|
const mtimeMs = stats.mtimeMs;
|
|
16829
|
-
|
|
16830
|
-
|
|
16826
|
+
const cached2 = cache.get(filePath);
|
|
16827
|
+
if (cached2 && cached2.fingerprint === fingerprint && cached2.mtimeMs === mtimeMs) {
|
|
16828
|
+
return cached2.enabledOverride;
|
|
16831
16829
|
}
|
|
16832
16830
|
let parsedEnabled = null;
|
|
16833
16831
|
try {
|
|
16834
|
-
const raw = fs8.readFileSync(
|
|
16832
|
+
const raw = fs8.readFileSync(filePath, "utf8");
|
|
16835
16833
|
const json = JSON.parse(raw);
|
|
16836
16834
|
if (json && typeof json === "object") {
|
|
16837
16835
|
const t2 = json.telemetry;
|
|
@@ -16842,9 +16840,27 @@ function readConfigOverride() {
|
|
|
16842
16840
|
}
|
|
16843
16841
|
} catch {
|
|
16844
16842
|
}
|
|
16845
|
-
cache.
|
|
16843
|
+
cache.set(filePath, { mtimeMs, fingerprint, enabledOverride: parsedEnabled });
|
|
16846
16844
|
return parsedEnabled;
|
|
16847
16845
|
}
|
|
16846
|
+
function readPersistedConsent(cwd) {
|
|
16847
|
+
const globalPath = configFilePath("global");
|
|
16848
|
+
const projectPath = configFilePath("project", { cwd });
|
|
16849
|
+
const global2 = readConfigOverrideAt(globalPath);
|
|
16850
|
+
const project = projectPath === globalPath ? null : readConfigOverrideAt(projectPath);
|
|
16851
|
+
if (project === false && global2 === false) {
|
|
16852
|
+
return { enabled: false, detail: "config.json (project and global)" };
|
|
16853
|
+
}
|
|
16854
|
+
if (project === false) return { enabled: false, detail: "config.json (project)" };
|
|
16855
|
+
if (global2 === false) return { enabled: false, detail: "config.json (global)" };
|
|
16856
|
+
if (project === true || global2 === true) {
|
|
16857
|
+
return {
|
|
16858
|
+
enabled: true,
|
|
16859
|
+
detail: project === true ? "config.json (project)" : "config.json (global)"
|
|
16860
|
+
};
|
|
16861
|
+
}
|
|
16862
|
+
return null;
|
|
16863
|
+
}
|
|
16848
16864
|
function parseFalsy(value) {
|
|
16849
16865
|
if (value === void 0) return false;
|
|
16850
16866
|
const v = value.trim().toLowerCase();
|
|
@@ -16854,7 +16870,7 @@ function isDoNotTrackSet(value) {
|
|
|
16854
16870
|
if (value === void 0 || value.trim() === "") return false;
|
|
16855
16871
|
return !parseFalsy(value);
|
|
16856
16872
|
}
|
|
16857
|
-
function getConsentState(env = process.env) {
|
|
16873
|
+
function getConsentState(env = process.env, cwd = process.cwd()) {
|
|
16858
16874
|
if (isDoNotTrackSet(env.DO_NOT_TRACK)) {
|
|
16859
16875
|
return {
|
|
16860
16876
|
enabled: false,
|
|
@@ -16871,24 +16887,28 @@ function getConsentState(env = process.env) {
|
|
|
16871
16887
|
if (sessionOverride !== null) {
|
|
16872
16888
|
return { enabled: sessionOverride, source: { source: "session_override" } };
|
|
16873
16889
|
}
|
|
16874
|
-
const persisted =
|
|
16875
|
-
if (persisted
|
|
16876
|
-
return {
|
|
16877
|
-
|
|
16878
|
-
|
|
16879
|
-
|
|
16890
|
+
const persisted = readPersistedConsent(cwd);
|
|
16891
|
+
if (persisted !== null) {
|
|
16892
|
+
return {
|
|
16893
|
+
enabled: persisted.enabled,
|
|
16894
|
+
source: { source: "config_file", detail: persisted.detail }
|
|
16895
|
+
};
|
|
16880
16896
|
}
|
|
16881
16897
|
return { enabled: true, source: { source: "default" } };
|
|
16882
16898
|
}
|
|
16883
|
-
function isEnabled(env = process.env) {
|
|
16884
|
-
return getConsentState(env).enabled;
|
|
16899
|
+
function isEnabled(env = process.env, cwd) {
|
|
16900
|
+
return getConsentState(env, cwd).enabled;
|
|
16885
16901
|
}
|
|
16886
|
-
function writeConsentFlag(enabled) {
|
|
16887
|
-
updateConfig(
|
|
16888
|
-
|
|
16889
|
-
|
|
16890
|
-
|
|
16891
|
-
|
|
16902
|
+
function writeConsentFlag(enabled, scope = "global", options = {}) {
|
|
16903
|
+
updateConfig(
|
|
16904
|
+
(config) => {
|
|
16905
|
+
const telemetryBlock = typeof config.telemetry === "object" && config.telemetry ? config.telemetry : {};
|
|
16906
|
+
config.telemetry = { ...telemetryBlock, enabled };
|
|
16907
|
+
},
|
|
16908
|
+
scope,
|
|
16909
|
+
options
|
|
16910
|
+
);
|
|
16911
|
+
cache.clear();
|
|
16892
16912
|
}
|
|
16893
16913
|
function setSessionConsentOverride(enabled) {
|
|
16894
16914
|
sessionOverride = enabled;
|
|
@@ -22455,6 +22475,18 @@ async function init2(args) {
|
|
|
22455
22475
|
}
|
|
22456
22476
|
tel.installMode = modeFromFlags ?? await promptInstallMode(recordedMode ?? "global");
|
|
22457
22477
|
track("installation:install_mode_decision", { install_mode: tel.installMode });
|
|
22478
|
+
let wroteProjectTelemetryOptOut = false;
|
|
22479
|
+
if (parsed.noTelemetry && tel.installMode === "local") {
|
|
22480
|
+
try {
|
|
22481
|
+
writeConsentFlag(false, "project", { cwd: initProjectRoot });
|
|
22482
|
+
wroteProjectTelemetryOptOut = true;
|
|
22483
|
+
log.info(
|
|
22484
|
+
`${import_picocolors10.default.bold("Telemetry")} ${import_picocolors10.default.dim("also disabled for this project \u2014")} ${import_picocolors10.default.cyan(".argent/config.json")} ${import_picocolors10.default.dim("(commit it so the opt-out applies to every clone).")}`
|
|
22485
|
+
);
|
|
22486
|
+
} catch (err) {
|
|
22487
|
+
log.warn(`Could not write the project telemetry opt-out: ${err}`);
|
|
22488
|
+
}
|
|
22489
|
+
}
|
|
22458
22490
|
version = await runInstall({
|
|
22459
22491
|
installMode: tel.installMode,
|
|
22460
22492
|
fromTar: parsed.fromTar,
|
|
@@ -22582,7 +22614,8 @@ async function init2(args) {
|
|
|
22582
22614
|
scope,
|
|
22583
22615
|
allowlistEnabled: allowlist.enabled,
|
|
22584
22616
|
skillsMethod,
|
|
22585
|
-
copiedRules: copyResults.length > 0
|
|
22617
|
+
copiedRules: copyResults.length > 0,
|
|
22618
|
+
wroteProjectTelemetryOptOut
|
|
22586
22619
|
});
|
|
22587
22620
|
note(
|
|
22588
22621
|
[
|
|
@@ -22620,7 +22653,8 @@ function printSummary({
|
|
|
22620
22653
|
scope,
|
|
22621
22654
|
allowlistEnabled,
|
|
22622
22655
|
skillsMethod,
|
|
22623
|
-
copiedRules
|
|
22656
|
+
copiedRules,
|
|
22657
|
+
wroteProjectTelemetryOptOut
|
|
22624
22658
|
}) {
|
|
22625
22659
|
const summaryLines = [
|
|
22626
22660
|
`${import_picocolors10.default.green("Install mode")} ${installMode === "local" ? "local (devDependency)" : "global"}`,
|
|
@@ -22638,7 +22672,7 @@ function printSummary({
|
|
|
22638
22672
|
`${import_picocolors10.default.bold("Commit")} so your team shares the same setup:`,
|
|
22639
22673
|
` ${import_picocolors10.default.cyan("package.json")} + your lockfile`,
|
|
22640
22674
|
` the written MCP config (.mcp.json, .cursor/mcp.json, \u2026)`,
|
|
22641
|
-
` ${import_picocolors10.default.cyan(".argent/install.json")}, and the skills/rules/agents files`,
|
|
22675
|
+
` ${import_picocolors10.default.cyan(".argent/install.json")}${wroteProjectTelemetryOptOut ? ` + ${import_picocolors10.default.cyan(".argent/config.json")}` : ""}, and the skills/rules/agents files`,
|
|
22642
22676
|
"",
|
|
22643
22677
|
`Teammates then get argent on ${import_picocolors10.default.cyan("npm install")} \u2014 no global install, no ${import_picocolors10.default.cyan("argent init")}.`,
|
|
22644
22678
|
import_picocolors10.default.dim(
|
package/dist/mcp-server.mjs
CHANGED
|
@@ -17142,8 +17142,8 @@ function asStringArray(raw) {
|
|
|
17142
17142
|
var CONFIG_SCHEMA = [
|
|
17143
17143
|
{
|
|
17144
17144
|
key: "telemetry.enabled",
|
|
17145
|
-
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).",
|
|
17146
|
-
scopes: ["global"],
|
|
17145
|
+
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.",
|
|
17146
|
+
scopes: ["project", "global"],
|
|
17147
17147
|
parse: asBoolean,
|
|
17148
17148
|
merge: "prioritize-restrictive",
|
|
17149
17149
|
// Opt-out: consent.ts reads an unstored value as enabled, so the config
|
|
@@ -18177,32 +18177,30 @@ var DYLIB_TVOS_DIR = path11.join(DYLIB_DIR, "tvos");
|
|
|
18177
18177
|
|
|
18178
18178
|
// ../telemetry/src/consent.ts
|
|
18179
18179
|
import * as fs8 from "node:fs";
|
|
18180
|
-
var cache =
|
|
18180
|
+
var cache = /* @__PURE__ */ new Map();
|
|
18181
18181
|
var sessionOverride = null;
|
|
18182
|
-
function
|
|
18182
|
+
function readConfigOverrideAt(filePath) {
|
|
18183
|
+
const miss = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
18183
18184
|
let stats;
|
|
18184
18185
|
try {
|
|
18185
|
-
stats = fs8.lstatSync(
|
|
18186
|
-
} catch
|
|
18187
|
-
|
|
18188
|
-
cache.current = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
18189
|
-
return null;
|
|
18190
|
-
}
|
|
18191
|
-
cache.current = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
18186
|
+
stats = fs8.lstatSync(filePath);
|
|
18187
|
+
} catch {
|
|
18188
|
+
cache.set(filePath, miss);
|
|
18192
18189
|
return null;
|
|
18193
18190
|
}
|
|
18194
18191
|
if (!stats.isFile()) {
|
|
18195
|
-
cache.
|
|
18192
|
+
cache.set(filePath, miss);
|
|
18196
18193
|
return null;
|
|
18197
18194
|
}
|
|
18198
18195
|
const fingerprint = `${stats.dev}:${stats.ino}:${stats.size}`;
|
|
18199
18196
|
const mtimeMs = stats.mtimeMs;
|
|
18200
|
-
|
|
18201
|
-
|
|
18197
|
+
const cached2 = cache.get(filePath);
|
|
18198
|
+
if (cached2 && cached2.fingerprint === fingerprint && cached2.mtimeMs === mtimeMs) {
|
|
18199
|
+
return cached2.enabledOverride;
|
|
18202
18200
|
}
|
|
18203
18201
|
let parsedEnabled = null;
|
|
18204
18202
|
try {
|
|
18205
|
-
const raw = fs8.readFileSync(
|
|
18203
|
+
const raw = fs8.readFileSync(filePath, "utf8");
|
|
18206
18204
|
const json = JSON.parse(raw);
|
|
18207
18205
|
if (json && typeof json === "object") {
|
|
18208
18206
|
const t = json.telemetry;
|
|
@@ -18213,9 +18211,27 @@ function readConfigOverride() {
|
|
|
18213
18211
|
}
|
|
18214
18212
|
} catch {
|
|
18215
18213
|
}
|
|
18216
|
-
cache.
|
|
18214
|
+
cache.set(filePath, { mtimeMs, fingerprint, enabledOverride: parsedEnabled });
|
|
18217
18215
|
return parsedEnabled;
|
|
18218
18216
|
}
|
|
18217
|
+
function readPersistedConsent(cwd) {
|
|
18218
|
+
const globalPath = configFilePath("global");
|
|
18219
|
+
const projectPath = configFilePath("project", { cwd });
|
|
18220
|
+
const global = readConfigOverrideAt(globalPath);
|
|
18221
|
+
const project = projectPath === globalPath ? null : readConfigOverrideAt(projectPath);
|
|
18222
|
+
if (project === false && global === false) {
|
|
18223
|
+
return { enabled: false, detail: "config.json (project and global)" };
|
|
18224
|
+
}
|
|
18225
|
+
if (project === false) return { enabled: false, detail: "config.json (project)" };
|
|
18226
|
+
if (global === false) return { enabled: false, detail: "config.json (global)" };
|
|
18227
|
+
if (project === true || global === true) {
|
|
18228
|
+
return {
|
|
18229
|
+
enabled: true,
|
|
18230
|
+
detail: project === true ? "config.json (project)" : "config.json (global)"
|
|
18231
|
+
};
|
|
18232
|
+
}
|
|
18233
|
+
return null;
|
|
18234
|
+
}
|
|
18219
18235
|
function parseFalsy(value) {
|
|
18220
18236
|
if (value === void 0) return false;
|
|
18221
18237
|
const v = value.trim().toLowerCase();
|
|
@@ -18225,7 +18241,7 @@ function isDoNotTrackSet(value) {
|
|
|
18225
18241
|
if (value === void 0 || value.trim() === "") return false;
|
|
18226
18242
|
return !parseFalsy(value);
|
|
18227
18243
|
}
|
|
18228
|
-
function getConsentState(env = process.env) {
|
|
18244
|
+
function getConsentState(env = process.env, cwd = process.cwd()) {
|
|
18229
18245
|
if (isDoNotTrackSet(env.DO_NOT_TRACK)) {
|
|
18230
18246
|
return {
|
|
18231
18247
|
enabled: false,
|
|
@@ -18242,17 +18258,17 @@ function getConsentState(env = process.env) {
|
|
|
18242
18258
|
if (sessionOverride !== null) {
|
|
18243
18259
|
return { enabled: sessionOverride, source: { source: "session_override" } };
|
|
18244
18260
|
}
|
|
18245
|
-
const persisted =
|
|
18246
|
-
if (persisted
|
|
18247
|
-
return {
|
|
18248
|
-
|
|
18249
|
-
|
|
18250
|
-
|
|
18261
|
+
const persisted = readPersistedConsent(cwd);
|
|
18262
|
+
if (persisted !== null) {
|
|
18263
|
+
return {
|
|
18264
|
+
enabled: persisted.enabled,
|
|
18265
|
+
source: { source: "config_file", detail: persisted.detail }
|
|
18266
|
+
};
|
|
18251
18267
|
}
|
|
18252
18268
|
return { enabled: true, source: { source: "default" } };
|
|
18253
18269
|
}
|
|
18254
|
-
function isEnabled(env = process.env) {
|
|
18255
|
-
return getConsentState(env).enabled;
|
|
18270
|
+
function isEnabled(env = process.env, cwd) {
|
|
18271
|
+
return getConsentState(env, cwd).enabled;
|
|
18256
18272
|
}
|
|
18257
18273
|
|
|
18258
18274
|
// ../telemetry/src/notice.ts
|
package/dist/tool-server.cjs
CHANGED
|
@@ -92132,8 +92132,8 @@ function asStringArray(raw) {
|
|
|
92132
92132
|
var CONFIG_SCHEMA = [
|
|
92133
92133
|
{
|
|
92134
92134
|
key: "telemetry.enabled",
|
|
92135
|
-
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).",
|
|
92136
|
-
scopes: ["global"],
|
|
92135
|
+
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.",
|
|
92136
|
+
scopes: ["project", "global"],
|
|
92137
92137
|
parse: asBoolean,
|
|
92138
92138
|
merge: "prioritize-restrictive",
|
|
92139
92139
|
// Opt-out: consent.ts reads an unstored value as enabled, so the config
|
|
@@ -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.8";
|
|
95738
95738
|
}
|
|
95739
95739
|
return "0.0.0";
|
|
95740
95740
|
}
|
|
@@ -96201,32 +96201,30 @@ function resolveHostFingerprintAsync() {
|
|
|
96201
96201
|
|
|
96202
96202
|
// ../telemetry/src/consent.ts
|
|
96203
96203
|
var fs8 = __toESM(require("node:fs"));
|
|
96204
|
-
var cache =
|
|
96204
|
+
var cache = /* @__PURE__ */ new Map();
|
|
96205
96205
|
var sessionOverride = null;
|
|
96206
|
-
function
|
|
96206
|
+
function readConfigOverrideAt(filePath) {
|
|
96207
|
+
const miss = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
96207
96208
|
let stats;
|
|
96208
96209
|
try {
|
|
96209
|
-
stats = fs8.lstatSync(
|
|
96210
|
-
} catch
|
|
96211
|
-
|
|
96212
|
-
cache.current = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
96213
|
-
return null;
|
|
96214
|
-
}
|
|
96215
|
-
cache.current = { mtimeMs: null, fingerprint: null, enabledOverride: null };
|
|
96210
|
+
stats = fs8.lstatSync(filePath);
|
|
96211
|
+
} catch {
|
|
96212
|
+
cache.set(filePath, miss);
|
|
96216
96213
|
return null;
|
|
96217
96214
|
}
|
|
96218
96215
|
if (!stats.isFile()) {
|
|
96219
|
-
cache.
|
|
96216
|
+
cache.set(filePath, miss);
|
|
96220
96217
|
return null;
|
|
96221
96218
|
}
|
|
96222
96219
|
const fingerprint = `${stats.dev}:${stats.ino}:${stats.size}`;
|
|
96223
96220
|
const mtimeMs = stats.mtimeMs;
|
|
96224
|
-
|
|
96225
|
-
|
|
96221
|
+
const cached3 = cache.get(filePath);
|
|
96222
|
+
if (cached3 && cached3.fingerprint === fingerprint && cached3.mtimeMs === mtimeMs) {
|
|
96223
|
+
return cached3.enabledOverride;
|
|
96226
96224
|
}
|
|
96227
96225
|
let parsedEnabled = null;
|
|
96228
96226
|
try {
|
|
96229
|
-
const raw = fs8.readFileSync(
|
|
96227
|
+
const raw = fs8.readFileSync(filePath, "utf8");
|
|
96230
96228
|
const json2 = JSON.parse(raw);
|
|
96231
96229
|
if (json2 && typeof json2 === "object") {
|
|
96232
96230
|
const t = json2.telemetry;
|
|
@@ -96237,9 +96235,27 @@ function readConfigOverride() {
|
|
|
96237
96235
|
}
|
|
96238
96236
|
} catch {
|
|
96239
96237
|
}
|
|
96240
|
-
cache.
|
|
96238
|
+
cache.set(filePath, { mtimeMs, fingerprint, enabledOverride: parsedEnabled });
|
|
96241
96239
|
return parsedEnabled;
|
|
96242
96240
|
}
|
|
96241
|
+
function readPersistedConsent(cwd) {
|
|
96242
|
+
const globalPath = configFilePath("global");
|
|
96243
|
+
const projectPath = configFilePath("project", { cwd });
|
|
96244
|
+
const global2 = readConfigOverrideAt(globalPath);
|
|
96245
|
+
const project = projectPath === globalPath ? null : readConfigOverrideAt(projectPath);
|
|
96246
|
+
if (project === false && global2 === false) {
|
|
96247
|
+
return { enabled: false, detail: "config.json (project and global)" };
|
|
96248
|
+
}
|
|
96249
|
+
if (project === false) return { enabled: false, detail: "config.json (project)" };
|
|
96250
|
+
if (global2 === false) return { enabled: false, detail: "config.json (global)" };
|
|
96251
|
+
if (project === true || global2 === true) {
|
|
96252
|
+
return {
|
|
96253
|
+
enabled: true,
|
|
96254
|
+
detail: project === true ? "config.json (project)" : "config.json (global)"
|
|
96255
|
+
};
|
|
96256
|
+
}
|
|
96257
|
+
return null;
|
|
96258
|
+
}
|
|
96243
96259
|
function parseFalsy(value) {
|
|
96244
96260
|
if (value === void 0) return false;
|
|
96245
96261
|
const v = value.trim().toLowerCase();
|
|
@@ -96249,7 +96265,7 @@ function isDoNotTrackSet(value) {
|
|
|
96249
96265
|
if (value === void 0 || value.trim() === "") return false;
|
|
96250
96266
|
return !parseFalsy(value);
|
|
96251
96267
|
}
|
|
96252
|
-
function getConsentState(env = process.env) {
|
|
96268
|
+
function getConsentState(env = process.env, cwd = process.cwd()) {
|
|
96253
96269
|
if (isDoNotTrackSet(env.DO_NOT_TRACK)) {
|
|
96254
96270
|
return {
|
|
96255
96271
|
enabled: false,
|
|
@@ -96266,17 +96282,17 @@ function getConsentState(env = process.env) {
|
|
|
96266
96282
|
if (sessionOverride !== null) {
|
|
96267
96283
|
return { enabled: sessionOverride, source: { source: "session_override" } };
|
|
96268
96284
|
}
|
|
96269
|
-
const persisted =
|
|
96270
|
-
if (persisted
|
|
96271
|
-
return {
|
|
96272
|
-
|
|
96273
|
-
|
|
96274
|
-
|
|
96285
|
+
const persisted = readPersistedConsent(cwd);
|
|
96286
|
+
if (persisted !== null) {
|
|
96287
|
+
return {
|
|
96288
|
+
enabled: persisted.enabled,
|
|
96289
|
+
source: { source: "config_file", detail: persisted.detail }
|
|
96290
|
+
};
|
|
96275
96291
|
}
|
|
96276
96292
|
return { enabled: true, source: { source: "default" } };
|
|
96277
96293
|
}
|
|
96278
|
-
function isEnabled(env = process.env) {
|
|
96279
|
-
return getConsentState(env).enabled;
|
|
96294
|
+
function isEnabled(env = process.env, cwd) {
|
|
96295
|
+
return getConsentState(env, cwd).enabled;
|
|
96280
96296
|
}
|
|
96281
96297
|
|
|
96282
96298
|
// ../telemetry/src/notice.ts
|
|
@@ -111139,7 +111155,7 @@ function isNativeDevtoolsBlockResult(toolId, result) {
|
|
|
111139
111155
|
async function precheckNativeDevtools(api, udid, bundleId) {
|
|
111140
111156
|
if (bundleId !== void 0 && !isInjectableBundleId(bundleId)) {
|
|
111141
111157
|
throw new FailureError(
|
|
111142
|
-
`${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,
|
|
111143
111159
|
{
|
|
111144
111160
|
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_NOT_INJECTABLE,
|
|
111145
111161
|
failure_stage: "native_devtools_precheck",
|
|
@@ -111838,7 +111854,7 @@ function tcpArtifactHint(err) {
|
|
|
111838
111854
|
return /TCP-transport (?:binary|dylib) not found/.test(message) ? message : void 0;
|
|
111839
111855
|
}
|
|
111840
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.";
|
|
111841
|
-
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;
|
|
111842
111858
|
function emptyTree() {
|
|
111843
111859
|
return parseDescribeResult({
|
|
111844
111860
|
role: "AXGroup",
|
|
@@ -116209,10 +116225,10 @@ Returns { envSetup, appRunning, connected, requiresRestart, state, message, next
|
|
|
116209
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.
|
|
116210
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.
|
|
116211
116227
|
- nextLaunchWillBeInjected: if you launch this bundle now, native devtools env setup is already in place (always false for a non-injectable app)
|
|
116212
|
-
- 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.
|
|
116213
116229
|
|
|
116214
116230
|
Call this before using app-scoped native hierarchy tools or native-network-logs.
|
|
116215
|
-
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.
|
|
116216
116232
|
If appRunning is false and nextLaunchWillBeInjected is true: use launch-app normally.
|
|
116217
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.
|
|
116218
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.
|
|
@@ -144637,7 +144653,7 @@ var FULL_HIERARCHY_FIELDS = [
|
|
|
144637
144653
|
];
|
|
144638
144654
|
async function unreadableHierarchyReason(nativeApi, bundleId) {
|
|
144639
144655
|
if (!isInjectableBundleId(bundleId)) {
|
|
144640
|
-
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.`;
|
|
144641
144657
|
}
|
|
144642
144658
|
const state3 = await nativeApi.appConnectionState(bundleId).catch(() => "indeterminate");
|
|
144643
144659
|
if (state3 === "connected") {
|
|
@@ -144645,7 +144661,10 @@ async function unreadableHierarchyReason(nativeApi, bundleId) {
|
|
|
144645
144661
|
}
|
|
144646
144662
|
return `${buildAppStateMessage(bundleId, state3)} Flows resolve selectors against the full view hierarchy native devtools serve.`;
|
|
144647
144663
|
}
|
|
144648
|
-
|
|
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) {
|
|
144649
144668
|
let nativeApi;
|
|
144650
144669
|
try {
|
|
144651
144670
|
const ndRef = nativeDevtoolsRef(device);
|
|
@@ -144656,22 +144675,113 @@ async function queryFullHierarchyTree(registry2, device, launchedNativeApp) {
|
|
|
144656
144675
|
{ cause: err }
|
|
144657
144676
|
);
|
|
144658
144677
|
}
|
|
144659
|
-
|
|
144660
|
-
|
|
144661
|
-
|
|
144662
|
-
|
|
144663
|
-
|
|
144664
|
-
|
|
144665
|
-
|
|
144666
|
-
|
|
144667
|
-
|
|
144668
|
-
|
|
144669
|
-
}
|
|
144670
|
-
|
|
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
|
+
);
|
|
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 };
|
|
144671
144780
|
}
|
|
144781
|
+
bundleId = resolved.bundleId;
|
|
144672
144782
|
}
|
|
144673
144783
|
const rawResult = await nativeApi.queryViewHierarchy(
|
|
144674
|
-
|
|
144784
|
+
bundleId,
|
|
144675
144785
|
"ViewHierarchy.getFullHierarchy",
|
|
144676
144786
|
{
|
|
144677
144787
|
fields: FULL_HIERARCHY_FIELDS,
|
|
@@ -144679,11 +144789,11 @@ async function queryFullHierarchyTree(registry2, device, launchedNativeApp) {
|
|
|
144679
144789
|
}
|
|
144680
144790
|
);
|
|
144681
144791
|
if (rawResult.error) {
|
|
144682
|
-
throw new Error(`getFullHierarchy failed for ${
|
|
144792
|
+
throw new Error(`getFullHierarchy failed for ${bundleId}: ${rawResult.error}`);
|
|
144683
144793
|
}
|
|
144684
144794
|
if (!Array.isArray(rawResult.windows) || rawResult.windows.length === 0) {
|
|
144685
144795
|
throw new Error(
|
|
144686
|
-
`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`
|
|
144687
144797
|
);
|
|
144688
144798
|
}
|
|
144689
144799
|
const { tree, screen } = adaptFullHierarchy(rawResult);
|
|
@@ -144890,13 +145000,15 @@ async function queryVegaTree(device) {
|
|
|
144890
145000
|
}
|
|
144891
145001
|
|
|
144892
145002
|
// ../tool-server/src/tools/flows/flow-tree.ts
|
|
144893
|
-
async function fetchFlowTree(registry2, device,
|
|
145003
|
+
async function fetchFlowTree(registry2, device, target) {
|
|
144894
145004
|
const source = FLOW_TREE_SOURCES[device.platform];
|
|
144895
145005
|
if (!source) return fetchTree(registry2, device);
|
|
144896
|
-
return source(registry2, device,
|
|
145006
|
+
return source(registry2, device, target);
|
|
144897
145007
|
}
|
|
144898
145008
|
var FLOW_TREE_SOURCES = {
|
|
144899
|
-
|
|
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),
|
|
144900
145012
|
android: (registry2, device) => queryAndroidFullHierarchy(registry2, device),
|
|
144901
145013
|
chromium: (registry2, device) => queryChromiumTree(registry2, device),
|
|
144902
145014
|
vega: (_registry, device) => queryVegaTree(device)
|
|
@@ -145102,7 +145214,7 @@ function provenTreeOutage(env) {
|
|
|
145102
145214
|
return proven && proven.deviceId === env.device.id ? proven.error : void 0;
|
|
145103
145215
|
}
|
|
145104
145216
|
function readFlowTree(env) {
|
|
145105
|
-
return fetchFlowTree(env.registry, env.device, env.
|
|
145217
|
+
return fetchFlowTree(env.registry, env.device, env.treeTarget).then((data) => {
|
|
145106
145218
|
if (env.treeOutage) env.treeOutage.proven = void 0;
|
|
145107
145219
|
return data;
|
|
145108
145220
|
});
|
|
@@ -146157,7 +146269,11 @@ async function captureTapSelector(registry2, session, udid, point) {
|
|
|
146157
146269
|
try {
|
|
146158
146270
|
const device = resolveDevice(udid);
|
|
146159
146271
|
const launched = recordedLaunchedApp(session, device.platform);
|
|
146160
|
-
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
|
+
);
|
|
146161
146277
|
const node = nodeAtPoint(tree, point);
|
|
146162
146278
|
if (!node) return { warning: "no element found under the tap; kept coordinates (brittle)" };
|
|
146163
146279
|
const selector = deriveSelector(node);
|
|
@@ -149095,6 +149211,7 @@ async function runLaunch(state3, app) {
|
|
|
149095
149211
|
reason: `no app id declared for platform "${device.platform}" \u2014 add a launch entry for it`
|
|
149096
149212
|
};
|
|
149097
149213
|
}
|
|
149214
|
+
state3.treeTarget = void 0;
|
|
149098
149215
|
let restart;
|
|
149099
149216
|
try {
|
|
149100
149217
|
restart = await invokeOnDevice(env, "restart-app", { bundleId });
|
|
@@ -149109,7 +149226,7 @@ async function runLaunch(state3, app) {
|
|
|
149109
149226
|
const gate = await treeSourceGate(registry2, device, bundleId, signal);
|
|
149110
149227
|
if (signal?.aborted) return ABORTED_OUTCOME;
|
|
149111
149228
|
if (gate) return { ok: false, reason: gate };
|
|
149112
|
-
state3.
|
|
149229
|
+
state3.treeTarget = { bundleId, pinned: true, probeAnswered: false };
|
|
149113
149230
|
return { ok: true };
|
|
149114
149231
|
}
|
|
149115
149232
|
async function runChromiumLaunch(state3, app) {
|
|
@@ -149296,7 +149413,11 @@ function createRunFlowTool(registry2) {
|
|
|
149296
149413
|
},
|
|
149297
149414
|
description: `Run a saved flow from the .argent/flows/ directory, or an explicit boundary-managed flow_path.
|
|
149298
149415
|
Steps run in order: \`launch\` starts an app from scratch (terminate + relaunch) and waits until it is
|
|
149299
|
-
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
|
|
149300
149421
|
element and act on it (\`tap: { on, times: 2 }\` double-taps; \`long-press: { on, duration }\` presses and
|
|
149301
149422
|
holds; \`tap\`/\`long-press\` alternatively take a raw normalized point \u2014 bare \`{ x, y }\` or \`on: { x, y }\`;
|
|
149302
149423
|
any selector may scope its matches geometrically, the CSS combinators read off frames: \`within: <selector>\`
|
|
@@ -150005,14 +150126,17 @@ async function execLeafStep(state3, step, index, scope) {
|
|
|
150005
150126
|
if (step.delayMs && !await sleepOrAbort(step.delayMs, signal)) {
|
|
150006
150127
|
return { ...base, status: "skip", tool: step.name, reason: "run aborted during delay" };
|
|
150007
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
|
+
}
|
|
150008
150139
|
try {
|
|
150009
|
-
if (FOREGROUND_CHANGING_TOOLS.has(step.name)) {
|
|
150010
|
-
state3.launchedNativeApp = void 0;
|
|
150011
|
-
if (state3.treeOutage) state3.treeOutage.proven = void 0;
|
|
150012
|
-
}
|
|
150013
|
-
if (isNestedOrchestratorTool(step.name) && state3.treeOutage) {
|
|
150014
|
-
state3.treeOutage.proven = void 0;
|
|
150015
|
-
}
|
|
150016
150140
|
const result = await invokeSubTool(registry2, ctx, step.name, args);
|
|
150017
150141
|
if (isUnmetUiWaitResult(step.name, result)) {
|
|
150018
150142
|
const note = result.note;
|
|
@@ -150059,7 +150183,9 @@ async function execLeafStep(state3, step, index, scope) {
|
|
|
150059
150183
|
}
|
|
150060
150184
|
if (step.name === "launch-app" || step.name === "restart-app") {
|
|
150061
150185
|
const launched = args.bundleId;
|
|
150062
|
-
if (typeof launched === "string")
|
|
150186
|
+
if (typeof launched === "string") {
|
|
150187
|
+
state3.treeTarget = { bundleId: launched, pinned: false, probeAnswered: false };
|
|
150188
|
+
}
|
|
150063
150189
|
}
|
|
150064
150190
|
return { ...base, status: "pass", tool: step.name, result, outputHint, args };
|
|
150065
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
|
|