@swmansion/argent 0.31.2-next.1 → 0.31.2-next.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/argent-android-devtools-0.3.0.apk +0 -0
- package/bin/darwin/ax-service +0 -0
- package/bin/darwin/tvos-ax-service +0 -0
- package/bin/darwin/tvos-hid-daemon +0 -0
- package/bin/network-inspector/arm64-v8a/libjvmti_network_inspector.so +0 -0
- package/bin/network-inspector/network-inspector.jar +0 -0
- package/bin/network-inspector/x86_64/libjvmti_network_inspector.so +0 -0
- package/bin/tcp/ax-service +0 -0
- package/dist/cli-cmds.mjs +106 -24
- package/dist/client.js +83 -14
- package/dist/installer.mjs +25 -1
- package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/ArgentExceptionGuard.h +3 -0
- package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/ArgentExceptionGuard.m +11 -3
- package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/ArgentRunnerSession+Gestures.swift +27 -19
- package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/ArgentRunnerSession+Quiescence.swift +95 -0
- package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/ArgentRunnerSession+TextEntry.swift +2 -2
- package/dist/ios-device-runner/ArgentRunner/ArgentRunnerUITests/RunnerProtocol.swift +5 -5
- package/dist/mcp-server.mjs +87 -17
- package/dist/tool-args.generated.d.ts +26 -14
- package/dist/tool-server.cjs +4604 -1860
- package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/libKeyboardPatch.dylib +0 -0
- package/dylibs/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tcp/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tcp/libKeyboardPatch.dylib +0 -0
- package/dylibs/tcp/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tvos/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tvos/libKeyboardPatch.dylib +0 -0
- package/dylibs/tvos/libNativeDevtoolsIos.dylib +0 -0
- package/package.json +1 -1
- package/skills/argent-create-e2e/references/live-authoring.md +3 -3
- package/skills/argent-ios-device-setup/SKILL.md +1 -1
|
Binary file
|
package/bin/darwin/ax-service
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/tcp/ax-service
CHANGED
|
Binary file
|
package/dist/cli-cmds.mjs
CHANGED
|
@@ -10310,6 +10310,36 @@ var TOOL_ID_ALIASES = Object.freeze({
|
|
|
10310
10310
|
function canonicalToolId(id) {
|
|
10311
10311
|
return Object.hasOwn(TOOL_ID_ALIASES, id) ? TOOL_ID_ALIASES[id] : id;
|
|
10312
10312
|
}
|
|
10313
|
+
var TOOL_PARAM_ALIASES = Object.freeze({
|
|
10314
|
+
"e2e-execute": Object.freeze({ flow_path: "e2e_test_path", flow_file: "e2e_test_file" }),
|
|
10315
|
+
"e2e-read-prerequisite": Object.freeze({
|
|
10316
|
+
flow_path: "e2e_test_path",
|
|
10317
|
+
flow_file: "e2e_test_file"
|
|
10318
|
+
})
|
|
10319
|
+
});
|
|
10320
|
+
var ToolParamAliasError = class extends Error {
|
|
10321
|
+
constructor(former, current) {
|
|
10322
|
+
super(`Pass ${current} or its former name ${former}, not both.`);
|
|
10323
|
+
this.name = "ToolParamAliasError";
|
|
10324
|
+
}
|
|
10325
|
+
};
|
|
10326
|
+
function toolParamAliases(tool) {
|
|
10327
|
+
const id = canonicalToolId(tool);
|
|
10328
|
+
return Object.hasOwn(TOOL_PARAM_ALIASES, id) ? TOOL_PARAM_ALIASES[id] : {};
|
|
10329
|
+
}
|
|
10330
|
+
function canonicalToolArgs(tool, args) {
|
|
10331
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) return args;
|
|
10332
|
+
const record2 = args;
|
|
10333
|
+
let out;
|
|
10334
|
+
for (const [former, current] of Object.entries(toolParamAliases(tool))) {
|
|
10335
|
+
if (record2[former] === void 0) continue;
|
|
10336
|
+
if (record2[current] !== void 0) throw new ToolParamAliasError(former, current);
|
|
10337
|
+
out ??= { ...record2 };
|
|
10338
|
+
out[current] = out[former];
|
|
10339
|
+
delete out[former];
|
|
10340
|
+
}
|
|
10341
|
+
return out ?? args;
|
|
10342
|
+
}
|
|
10313
10343
|
|
|
10314
10344
|
// ../registry/src/flow-file-refs.ts
|
|
10315
10345
|
async function canonicalFlowPath(p) {
|
|
@@ -10379,7 +10409,13 @@ async function classifyOnDiskSpelling(dir, base, addressable = FLOW_FILE_NAME_PA
|
|
|
10379
10409
|
}
|
|
10380
10410
|
function nestedFlowTarget(args) {
|
|
10381
10411
|
if (typeof args !== "object" || args === null || Array.isArray(args)) return void 0;
|
|
10382
|
-
|
|
10412
|
+
let canonical;
|
|
10413
|
+
try {
|
|
10414
|
+
canonical = canonicalToolArgs("e2e-execute", args);
|
|
10415
|
+
} catch {
|
|
10416
|
+
return void 0;
|
|
10417
|
+
}
|
|
10418
|
+
const { name, project_root: projectRoot, e2e_test_path: flowPath } = canonical;
|
|
10383
10419
|
if (flowPath === void 0) {
|
|
10384
10420
|
if (typeof name !== "string" || !FLOW_NAME_PATTERN.test(name)) return void 0;
|
|
10385
10421
|
if (!isResolvedAbsolute(projectRoot)) return void 0;
|
|
@@ -10388,7 +10424,7 @@ function nestedFlowTarget(args) {
|
|
|
10388
10424
|
}
|
|
10389
10425
|
if (name !== void 0 || !isResolvedAbsolute(flowPath)) return void 0;
|
|
10390
10426
|
if (!FLOW_FILE_NAME_PATTERN.test(path8.basename(flowPath))) return void 0;
|
|
10391
|
-
return { kind: "
|
|
10427
|
+
return { kind: "e2e_test_path", path: flowPath };
|
|
10392
10428
|
}
|
|
10393
10429
|
function isResolvedAbsolute(value) {
|
|
10394
10430
|
return typeof value === "string" && path8.isAbsolute(value) && !value.split(/[\\/]+/).includes("..");
|
|
@@ -10420,6 +10456,8 @@ var FAILURE_CODES = {
|
|
|
10420
10456
|
TOOL_CAPABILITY_UNSUPPORTED_OPERATION: "TOOL_CAPABILITY_UNSUPPORTED_OPERATION",
|
|
10421
10457
|
TOOL_PLATFORM_NOT_IMPLEMENTED: "TOOL_PLATFORM_NOT_IMPLEMENTED",
|
|
10422
10458
|
TOOL_DEPENDENCY_MISSING: "TOOL_DEPENDENCY_MISSING",
|
|
10459
|
+
// Xcode's simctl or devicectl wrapper would start its component install and wait for a password.
|
|
10460
|
+
TOOL_XCODE_FIRST_LAUNCH_PENDING: "TOOL_XCODE_FIRST_LAUNCH_PENDING",
|
|
10423
10461
|
TOOL_INPUT_INVALID: "TOOL_INPUT_INVALID",
|
|
10424
10462
|
TOOLSERVER_UNHANDLED_REJECTION: "TOOLSERVER_UNHANDLED_REJECTION",
|
|
10425
10463
|
TOOLSERVER_UNCAUGHT_EXCEPTION: "TOOLSERVER_UNCAUGHT_EXCEPTION",
|
|
@@ -10592,6 +10630,16 @@ var FAILURE_CODES = {
|
|
|
10592
10630
|
JS_RUNTIME_CDP_DISCONNECTED: "JS_RUNTIME_CDP_DISCONNECTED",
|
|
10593
10631
|
JS_RUNTIME_PAUSED: "JS_RUNTIME_PAUSED",
|
|
10594
10632
|
NETWORK_INSPECTOR_CDP_DISCONNECTED: "NETWORK_INSPECTOR_CDP_DISCONNECTED",
|
|
10633
|
+
ANDROID_NETWORK_INSPECTOR_FACTORY_OPTIONS_INVALID: "ANDROID_NETWORK_INSPECTOR_FACTORY_OPTIONS_INVALID",
|
|
10634
|
+
ANDROID_NETWORK_INSPECTOR_WRONG_PLATFORM: "ANDROID_NETWORK_INSPECTOR_WRONG_PLATFORM",
|
|
10635
|
+
ANDROID_NETWORK_INSPECTOR_UNKNOWN_PACKAGE: "ANDROID_NETWORK_INSPECTOR_UNKNOWN_PACKAGE",
|
|
10636
|
+
ANDROID_NETWORK_INSPECTOR_RUN_AS_FAILED: "ANDROID_NETWORK_INSPECTOR_RUN_AS_FAILED",
|
|
10637
|
+
ANDROID_NETWORK_INSPECTOR_DEVICE_IN_USE: "ANDROID_NETWORK_INSPECTOR_DEVICE_IN_USE",
|
|
10638
|
+
ANDROID_NETWORK_INSPECTOR_NO_DEVICE_PORT: "ANDROID_NETWORK_INSPECTOR_NO_DEVICE_PORT",
|
|
10639
|
+
NATIVE_NETWORK_LOGS_STOP_UNSUPPORTED: "NATIVE_NETWORK_LOGS_STOP_UNSUPPORTED",
|
|
10640
|
+
NDJSON_CDP_REQUEST_TIMEOUT: "NDJSON_CDP_REQUEST_TIMEOUT",
|
|
10641
|
+
NDJSON_CDP_REQUEST_FAILED: "NDJSON_CDP_REQUEST_FAILED",
|
|
10642
|
+
NDJSON_CDP_CONNECTION_CLOSED: "NDJSON_CDP_CONNECTION_CLOSED",
|
|
10595
10643
|
REACT_PROFILER_NO_ACTIVE_SESSION: "REACT_PROFILER_NO_ACTIVE_SESSION",
|
|
10596
10644
|
REACT_PROFILER_CDP_CONNECTION_LOST: "REACT_PROFILER_CDP_CONNECTION_LOST",
|
|
10597
10645
|
REACT_PROFILER_NO_CPU_PROFILE: "REACT_PROFILER_NO_CPU_PROFILE",
|
|
@@ -31111,12 +31159,18 @@ function isResolvedAbsolute2(value) {
|
|
|
31111
31159
|
return typeof value === "string" && path11.isAbsolute(value) && !value.split(/[\\/]+/).includes("..");
|
|
31112
31160
|
}
|
|
31113
31161
|
function namesValidFlow(args) {
|
|
31114
|
-
|
|
31162
|
+
let canonical;
|
|
31163
|
+
try {
|
|
31164
|
+
canonical = canonicalToolArgs("e2e-execute", args);
|
|
31165
|
+
} catch {
|
|
31166
|
+
return false;
|
|
31167
|
+
}
|
|
31168
|
+
const { project_root, e2e_test_path: testPath, name } = canonical;
|
|
31115
31169
|
if (!isResolvedAbsolute2(project_root)) return false;
|
|
31116
31170
|
if (name === void 0) {
|
|
31117
|
-
return isResolvedAbsolute2(
|
|
31171
|
+
return isResolvedAbsolute2(testPath) && FLOW_FILE_NAME_PATTERN.test(path11.basename(testPath));
|
|
31118
31172
|
}
|
|
31119
|
-
return
|
|
31173
|
+
return testPath === void 0 && typeof name === "string" && FLOW_NAME_PATTERN.test(name);
|
|
31120
31174
|
}
|
|
31121
31175
|
function savedFlowProject(file2) {
|
|
31122
31176
|
if (!path11.isAbsolute(file2)) return null;
|
|
@@ -31290,8 +31344,8 @@ function memberCollector(roots, opts) {
|
|
|
31290
31344
|
`));
|
|
31291
31345
|
const emit = (member, sent) => {
|
|
31292
31346
|
members2.push(member);
|
|
31293
|
-
const subject = member.role === "flow" ? member.canonical : member.key;
|
|
31294
|
-
if (logging) log2(printable(`[
|
|
31347
|
+
const [label, subject] = member.role === "flow" ? ["e2e test", member.canonical] : [member.role, member.key];
|
|
31348
|
+
if (logging) log2(printable(`[e2e-files] ${label} ${subject}: ${sent}`));
|
|
31295
31349
|
};
|
|
31296
31350
|
async function flow(anchorDir, target, onlyPresent) {
|
|
31297
31351
|
const key = flowMemberKey(anchorDir, target);
|
|
@@ -31457,7 +31511,7 @@ function recordedStep(args) {
|
|
|
31457
31511
|
if (typeof text2 !== "string") return void 0;
|
|
31458
31512
|
let parsed;
|
|
31459
31513
|
try {
|
|
31460
|
-
parsed = JSON.parse(text2);
|
|
31514
|
+
parsed = canonicalToolArgs(command, JSON.parse(text2));
|
|
31461
31515
|
} catch {
|
|
31462
31516
|
return void 0;
|
|
31463
31517
|
}
|
|
@@ -31480,7 +31534,7 @@ async function collectStepMembers(args, opts, recordingDirFor = e2eDirFor) {
|
|
|
31480
31534
|
const self2 = await collector.flow(path11.dirname(recording), path11.basename(recording));
|
|
31481
31535
|
let runArgs = step.args;
|
|
31482
31536
|
const named = nestedFlowTarget(runArgs);
|
|
31483
|
-
if (named?.kind === "
|
|
31537
|
+
if (named?.kind === "e2e_test_path") {
|
|
31484
31538
|
const stem = path11.basename(named.path, ".yaml");
|
|
31485
31539
|
const base = path11.basename(named.path);
|
|
31486
31540
|
const dir = path11.resolve(path11.dirname(named.path));
|
|
@@ -31489,7 +31543,7 @@ async function collectStepMembers(args, opts, recordingDirFor = e2eDirFor) {
|
|
|
31489
31543
|
if (dirs.some((d) => path11.resolve(d) === dir)) {
|
|
31490
31544
|
await collector.flow(path11.dirname(named.path), base);
|
|
31491
31545
|
runArgs = { ...runArgs, name: stem };
|
|
31492
|
-
delete runArgs.
|
|
31546
|
+
delete runArgs.e2e_test_path;
|
|
31493
31547
|
}
|
|
31494
31548
|
}
|
|
31495
31549
|
const target = nestedFlowTarget(runArgs);
|
|
@@ -31517,7 +31571,21 @@ async function nameToE2ePathForOlderServer(tool, fileInputs, args) {
|
|
|
31517
31571
|
const picked = await pickE2eFile(target.paths);
|
|
31518
31572
|
if (!picked.exists || picked.legacy) return args;
|
|
31519
31573
|
const { name: _name, ...rest } = args;
|
|
31520
|
-
return { ...rest,
|
|
31574
|
+
return { ...rest, e2e_test_path: picked.path };
|
|
31575
|
+
}
|
|
31576
|
+
function formerParamsForOlderServer(meta3, args) {
|
|
31577
|
+
const listed = meta3.inputSchema.properties;
|
|
31578
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) return args;
|
|
31579
|
+
if (typeof listed !== "object" || listed === null) return args;
|
|
31580
|
+
const record2 = args;
|
|
31581
|
+
let out;
|
|
31582
|
+
for (const [former, current] of Object.entries(toolParamAliases(meta3.name))) {
|
|
31583
|
+
if (record2[current] === void 0 || current in listed || !(former in listed)) continue;
|
|
31584
|
+
out ??= { ...record2 };
|
|
31585
|
+
out[former] = out[current];
|
|
31586
|
+
delete out[current];
|
|
31587
|
+
}
|
|
31588
|
+
return out ?? args;
|
|
31521
31589
|
}
|
|
31522
31590
|
async function stepForOlderServer(args, listedId) {
|
|
31523
31591
|
if (typeof args !== "object" || args === null || Array.isArray(args)) return args;
|
|
@@ -31775,12 +31843,13 @@ function createToolsClient(options = {}) {
|
|
|
31775
31843
|
async function callTool(name, args, opts) {
|
|
31776
31844
|
opts?.signal?.throwIfAborted();
|
|
31777
31845
|
const { url: url2, token, remote } = await route();
|
|
31778
|
-
let finalArgs = args;
|
|
31846
|
+
let finalArgs = canonicalToolArgs(name, args);
|
|
31779
31847
|
const tools2 = await fetchTools({ signal: opts?.signal });
|
|
31780
31848
|
const meta3 = findTool(tools2, name);
|
|
31781
31849
|
const olderServer = meta3 !== null && !tools2.some((t2) => t2.name === canonicalToolId(meta3.name));
|
|
31782
31850
|
if (meta3) {
|
|
31783
|
-
finalArgs = await nameToE2ePathForOlderServer(meta3.name, meta3.fileInputs,
|
|
31851
|
+
finalArgs = await nameToE2ePathForOlderServer(meta3.name, meta3.fileInputs, finalArgs);
|
|
31852
|
+
finalArgs = formerParamsForOlderServer(meta3, finalArgs);
|
|
31784
31853
|
}
|
|
31785
31854
|
if (olderServer && canonicalToolId(meta3.name) === "e2e-add-step") {
|
|
31786
31855
|
finalArgs = await stepForOlderServer(finalArgs, (tool) => findTool(tools2, tool)?.name);
|
|
@@ -31808,7 +31877,7 @@ function createToolsClient(options = {}) {
|
|
|
31808
31877
|
if (filesLogEnabled()) {
|
|
31809
31878
|
for (const file2 of applied.written) {
|
|
31810
31879
|
if (baselineDirs.includes(path12.dirname(file2))) {
|
|
31811
|
-
diagnose(`[
|
|
31880
|
+
diagnose(`[e2e-files] baseline ${file2}: written`);
|
|
31812
31881
|
}
|
|
31813
31882
|
}
|
|
31814
31883
|
}
|
|
@@ -35740,7 +35809,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
35740
35809
|
var SESSION_ID2 = randomUUID5();
|
|
35741
35810
|
function readCliVersion() {
|
|
35742
35811
|
if (true) {
|
|
35743
|
-
return "0.31.2-next.
|
|
35812
|
+
return "0.31.2-next.10";
|
|
35744
35813
|
}
|
|
35745
35814
|
return "0.0.0";
|
|
35746
35815
|
}
|
|
@@ -37203,8 +37272,12 @@ function renderEchoLine(s) {
|
|
|
37203
37272
|
}
|
|
37204
37273
|
return ` ${indent}\u203A ${s.message}`;
|
|
37205
37274
|
}
|
|
37275
|
+
function e2eTestOf(r2) {
|
|
37276
|
+
return r2.e2eTest ?? r2.flow;
|
|
37277
|
+
}
|
|
37206
37278
|
function stepLabel(s, topFlow) {
|
|
37207
|
-
const
|
|
37279
|
+
const owner = e2eTestOf(s);
|
|
37280
|
+
const where = owner && owner !== topFlow ? ` [${owner}]` : "";
|
|
37208
37281
|
const what = s.tool ?? s.target;
|
|
37209
37282
|
return `${what ? `${s.kind} ${what}` : s.kind}${where}`;
|
|
37210
37283
|
}
|
|
@@ -37261,7 +37334,7 @@ function renderFailedSteps(report) {
|
|
|
37261
37334
|
if (s.status !== "fail" && s.status !== "error" && !s.warning && !scriptNote && scriptLog.length === 0) {
|
|
37262
37335
|
continue;
|
|
37263
37336
|
}
|
|
37264
|
-
lines.push(renderStepLine(s, n3, report
|
|
37337
|
+
lines.push(renderStepLine(s, n3, e2eTestOf(report)));
|
|
37265
37338
|
if (s.warning) lines.push(renderUnderStepLine(s, n3, `\u26A0 ${s.warning}`));
|
|
37266
37339
|
lines.push(...scriptLog);
|
|
37267
37340
|
if (s.artifacts && typeof s.artifacts === "object") {
|
|
@@ -37282,7 +37355,7 @@ function summarizeFailure(report) {
|
|
|
37282
37355
|
n3++;
|
|
37283
37356
|
if (s.status === "fail" || s.status === "error") {
|
|
37284
37357
|
const detail = s.reason ? String(s.reason) : void 0;
|
|
37285
|
-
return { headline: `step ${n3} ${stepLabel(s, report
|
|
37358
|
+
return { headline: `step ${n3} ${stepLabel(s, e2eTestOf(report))}`, detail };
|
|
37286
37359
|
}
|
|
37287
37360
|
}
|
|
37288
37361
|
return { headline: "failed with no failing step" };
|
|
@@ -37322,11 +37395,15 @@ function rerunCommand(flowPath, rel, projectRoot, args) {
|
|
|
37322
37395
|
if (args.output) parts.push("--output", pathArg(path18.join(args.output, path18.dirname(rel))));
|
|
37323
37396
|
return parts.join(" ");
|
|
37324
37397
|
}
|
|
37325
|
-
var EXPORT_SOURCE_MARKER = ".argent-
|
|
37398
|
+
var EXPORT_SOURCE_MARKER = ".argent-e2e-source";
|
|
37399
|
+
var LEGACY_EXPORT_SOURCE_MARKER = ".argent-flow-source";
|
|
37326
37400
|
var MARKER_READ_RETRIES = 4;
|
|
37327
37401
|
var MARKER_READ_DELAY_MS = 5;
|
|
37328
37402
|
async function readExportMarker(dir) {
|
|
37329
|
-
const
|
|
37403
|
+
const owner = await readMarkerFile(path18.join(dir, EXPORT_SOURCE_MARKER));
|
|
37404
|
+
return owner !== void 0 ? owner : await readMarkerFile(path18.join(dir, LEGACY_EXPORT_SOURCE_MARKER));
|
|
37405
|
+
}
|
|
37406
|
+
async function readMarkerFile(marker) {
|
|
37330
37407
|
for (let attempt = 0; ; attempt++) {
|
|
37331
37408
|
let owner;
|
|
37332
37409
|
try {
|
|
@@ -37469,7 +37546,7 @@ function exitAfterFlush(code, streams = [process.stdout, process.stderr]) {
|
|
|
37469
37546
|
}
|
|
37470
37547
|
function renderReport(report) {
|
|
37471
37548
|
const lines = [];
|
|
37472
|
-
lines.push(`E2E test "${report
|
|
37549
|
+
lines.push(`E2E test "${e2eTestOf(report)}"${report.device ? ` on ${report.device}` : ""}`);
|
|
37473
37550
|
if (report.executionPrerequisite) {
|
|
37474
37551
|
lines.push(` assumes: ${report.executionPrerequisite}`);
|
|
37475
37552
|
}
|
|
@@ -37481,7 +37558,7 @@ function renderReport(report) {
|
|
|
37481
37558
|
continue;
|
|
37482
37559
|
}
|
|
37483
37560
|
n3++;
|
|
37484
|
-
lines.push(renderStepLine(s, n3, report
|
|
37561
|
+
lines.push(renderStepLine(s, n3, e2eTestOf(report)));
|
|
37485
37562
|
if (s.warning) lines.push(renderUnderStepLine(s, n3, `\u26A0 ${s.warning}`));
|
|
37486
37563
|
lines.push(...renderScriptLogLines(s, n3));
|
|
37487
37564
|
if (s.artifacts && typeof s.artifacts === "object") {
|
|
@@ -37575,7 +37652,7 @@ async function collectFlowFiles(dir, recursive2) {
|
|
|
37575
37652
|
var RUN_TOOL_ID = "e2e-execute";
|
|
37576
37653
|
function buildRunPayload(flowPath, projectRoot, args) {
|
|
37577
37654
|
const payload = {
|
|
37578
|
-
|
|
37655
|
+
e2e_test_path: flowPath,
|
|
37579
37656
|
project_root: projectRoot,
|
|
37580
37657
|
// Headless runs never block on the LLM prerequisite handshake.
|
|
37581
37658
|
prerequisiteAcknowledged: true
|
|
@@ -37785,7 +37862,12 @@ async function runFlowDirectory(dir, args, projectRoot, options) {
|
|
|
37785
37862
|
const durationMs = Date.now() - batchStartedAt;
|
|
37786
37863
|
if (args.json) {
|
|
37787
37864
|
console.log(
|
|
37788
|
-
JSON.stringify(
|
|
37865
|
+
JSON.stringify(
|
|
37866
|
+
// `flows` is the deprecated spelling of `e2eTests`.
|
|
37867
|
+
{ ok: counts.failed === 0, ...counts, durationMs, e2eTests: results, flows: results },
|
|
37868
|
+
null,
|
|
37869
|
+
2
|
|
37870
|
+
)
|
|
37789
37871
|
);
|
|
37790
37872
|
} else {
|
|
37791
37873
|
for (const line of renderFailedFlows(failures)) console.log(line);
|
package/dist/client.js
CHANGED
|
@@ -8883,6 +8883,36 @@ var TOOL_ID_ALIASES = Object.freeze({
|
|
|
8883
8883
|
function canonicalToolId(id) {
|
|
8884
8884
|
return Object.hasOwn(TOOL_ID_ALIASES, id) ? TOOL_ID_ALIASES[id] : id;
|
|
8885
8885
|
}
|
|
8886
|
+
var TOOL_PARAM_ALIASES = Object.freeze({
|
|
8887
|
+
"e2e-execute": Object.freeze({ flow_path: "e2e_test_path", flow_file: "e2e_test_file" }),
|
|
8888
|
+
"e2e-read-prerequisite": Object.freeze({
|
|
8889
|
+
flow_path: "e2e_test_path",
|
|
8890
|
+
flow_file: "e2e_test_file"
|
|
8891
|
+
})
|
|
8892
|
+
});
|
|
8893
|
+
var ToolParamAliasError = class extends Error {
|
|
8894
|
+
constructor(former, current) {
|
|
8895
|
+
super(`Pass ${current} or its former name ${former}, not both.`);
|
|
8896
|
+
this.name = "ToolParamAliasError";
|
|
8897
|
+
}
|
|
8898
|
+
};
|
|
8899
|
+
function toolParamAliases(tool) {
|
|
8900
|
+
const id = canonicalToolId(tool);
|
|
8901
|
+
return Object.hasOwn(TOOL_PARAM_ALIASES, id) ? TOOL_PARAM_ALIASES[id] : {};
|
|
8902
|
+
}
|
|
8903
|
+
function canonicalToolArgs(tool, args) {
|
|
8904
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) return args;
|
|
8905
|
+
const record = args;
|
|
8906
|
+
let out;
|
|
8907
|
+
for (const [former, current] of Object.entries(toolParamAliases(tool))) {
|
|
8908
|
+
if (record[former] === void 0) continue;
|
|
8909
|
+
if (record[current] !== void 0) throw new ToolParamAliasError(former, current);
|
|
8910
|
+
out ??= { ...record };
|
|
8911
|
+
out[current] = out[former];
|
|
8912
|
+
delete out[former];
|
|
8913
|
+
}
|
|
8914
|
+
return out ?? args;
|
|
8915
|
+
}
|
|
8886
8916
|
|
|
8887
8917
|
// ../registry/src/flow-file-refs.ts
|
|
8888
8918
|
async function canonicalFlowPath(p) {
|
|
@@ -8952,7 +8982,13 @@ async function classifyOnDiskSpelling(dir, base, addressable = FLOW_FILE_NAME_PA
|
|
|
8952
8982
|
}
|
|
8953
8983
|
function nestedFlowTarget(args) {
|
|
8954
8984
|
if (typeof args !== "object" || args === null || Array.isArray(args)) return void 0;
|
|
8955
|
-
|
|
8985
|
+
let canonical;
|
|
8986
|
+
try {
|
|
8987
|
+
canonical = canonicalToolArgs("e2e-execute", args);
|
|
8988
|
+
} catch {
|
|
8989
|
+
return void 0;
|
|
8990
|
+
}
|
|
8991
|
+
const { name, project_root: projectRoot, e2e_test_path: flowPath } = canonical;
|
|
8956
8992
|
if (flowPath === void 0) {
|
|
8957
8993
|
if (typeof name !== "string" || !FLOW_NAME_PATTERN.test(name)) return void 0;
|
|
8958
8994
|
if (!isResolvedAbsolute(projectRoot)) return void 0;
|
|
@@ -8961,7 +8997,7 @@ function nestedFlowTarget(args) {
|
|
|
8961
8997
|
}
|
|
8962
8998
|
if (name !== void 0 || !isResolvedAbsolute(flowPath)) return void 0;
|
|
8963
8999
|
if (!FLOW_FILE_NAME_PATTERN.test(path8.basename(flowPath))) return void 0;
|
|
8964
|
-
return { kind: "
|
|
9000
|
+
return { kind: "e2e_test_path", path: flowPath };
|
|
8965
9001
|
}
|
|
8966
9002
|
function isResolvedAbsolute(value) {
|
|
8967
9003
|
return typeof value === "string" && path8.isAbsolute(value) && !value.split(/[\\/]+/).includes("..");
|
|
@@ -8993,6 +9029,8 @@ var FAILURE_CODES = {
|
|
|
8993
9029
|
TOOL_CAPABILITY_UNSUPPORTED_OPERATION: "TOOL_CAPABILITY_UNSUPPORTED_OPERATION",
|
|
8994
9030
|
TOOL_PLATFORM_NOT_IMPLEMENTED: "TOOL_PLATFORM_NOT_IMPLEMENTED",
|
|
8995
9031
|
TOOL_DEPENDENCY_MISSING: "TOOL_DEPENDENCY_MISSING",
|
|
9032
|
+
// Xcode's simctl or devicectl wrapper would start its component install and wait for a password.
|
|
9033
|
+
TOOL_XCODE_FIRST_LAUNCH_PENDING: "TOOL_XCODE_FIRST_LAUNCH_PENDING",
|
|
8996
9034
|
TOOL_INPUT_INVALID: "TOOL_INPUT_INVALID",
|
|
8997
9035
|
TOOLSERVER_UNHANDLED_REJECTION: "TOOLSERVER_UNHANDLED_REJECTION",
|
|
8998
9036
|
TOOLSERVER_UNCAUGHT_EXCEPTION: "TOOLSERVER_UNCAUGHT_EXCEPTION",
|
|
@@ -9165,6 +9203,16 @@ var FAILURE_CODES = {
|
|
|
9165
9203
|
JS_RUNTIME_CDP_DISCONNECTED: "JS_RUNTIME_CDP_DISCONNECTED",
|
|
9166
9204
|
JS_RUNTIME_PAUSED: "JS_RUNTIME_PAUSED",
|
|
9167
9205
|
NETWORK_INSPECTOR_CDP_DISCONNECTED: "NETWORK_INSPECTOR_CDP_DISCONNECTED",
|
|
9206
|
+
ANDROID_NETWORK_INSPECTOR_FACTORY_OPTIONS_INVALID: "ANDROID_NETWORK_INSPECTOR_FACTORY_OPTIONS_INVALID",
|
|
9207
|
+
ANDROID_NETWORK_INSPECTOR_WRONG_PLATFORM: "ANDROID_NETWORK_INSPECTOR_WRONG_PLATFORM",
|
|
9208
|
+
ANDROID_NETWORK_INSPECTOR_UNKNOWN_PACKAGE: "ANDROID_NETWORK_INSPECTOR_UNKNOWN_PACKAGE",
|
|
9209
|
+
ANDROID_NETWORK_INSPECTOR_RUN_AS_FAILED: "ANDROID_NETWORK_INSPECTOR_RUN_AS_FAILED",
|
|
9210
|
+
ANDROID_NETWORK_INSPECTOR_DEVICE_IN_USE: "ANDROID_NETWORK_INSPECTOR_DEVICE_IN_USE",
|
|
9211
|
+
ANDROID_NETWORK_INSPECTOR_NO_DEVICE_PORT: "ANDROID_NETWORK_INSPECTOR_NO_DEVICE_PORT",
|
|
9212
|
+
NATIVE_NETWORK_LOGS_STOP_UNSUPPORTED: "NATIVE_NETWORK_LOGS_STOP_UNSUPPORTED",
|
|
9213
|
+
NDJSON_CDP_REQUEST_TIMEOUT: "NDJSON_CDP_REQUEST_TIMEOUT",
|
|
9214
|
+
NDJSON_CDP_REQUEST_FAILED: "NDJSON_CDP_REQUEST_FAILED",
|
|
9215
|
+
NDJSON_CDP_CONNECTION_CLOSED: "NDJSON_CDP_CONNECTION_CLOSED",
|
|
9168
9216
|
REACT_PROFILER_NO_ACTIVE_SESSION: "REACT_PROFILER_NO_ACTIVE_SESSION",
|
|
9169
9217
|
REACT_PROFILER_CDP_CONNECTION_LOST: "REACT_PROFILER_CDP_CONNECTION_LOST",
|
|
9170
9218
|
REACT_PROFILER_NO_CPU_PROFILE: "REACT_PROFILER_NO_CPU_PROFILE",
|
|
@@ -9857,12 +9905,18 @@ function isResolvedAbsolute2(value) {
|
|
|
9857
9905
|
return typeof value === "string" && path11.isAbsolute(value) && !value.split(/[\\/]+/).includes("..");
|
|
9858
9906
|
}
|
|
9859
9907
|
function namesValidFlow(args) {
|
|
9860
|
-
|
|
9908
|
+
let canonical;
|
|
9909
|
+
try {
|
|
9910
|
+
canonical = canonicalToolArgs("e2e-execute", args);
|
|
9911
|
+
} catch {
|
|
9912
|
+
return false;
|
|
9913
|
+
}
|
|
9914
|
+
const { project_root, e2e_test_path: testPath, name } = canonical;
|
|
9861
9915
|
if (!isResolvedAbsolute2(project_root)) return false;
|
|
9862
9916
|
if (name === void 0) {
|
|
9863
|
-
return isResolvedAbsolute2(
|
|
9917
|
+
return isResolvedAbsolute2(testPath) && FLOW_FILE_NAME_PATTERN.test(path11.basename(testPath));
|
|
9864
9918
|
}
|
|
9865
|
-
return
|
|
9919
|
+
return testPath === void 0 && typeof name === "string" && FLOW_NAME_PATTERN.test(name);
|
|
9866
9920
|
}
|
|
9867
9921
|
function savedFlowProject(file) {
|
|
9868
9922
|
if (!path11.isAbsolute(file)) return null;
|
|
@@ -10036,8 +10090,8 @@ function memberCollector(roots, opts) {
|
|
|
10036
10090
|
`));
|
|
10037
10091
|
const emit = (member, sent) => {
|
|
10038
10092
|
members.push(member);
|
|
10039
|
-
const subject = member.role === "flow" ? member.canonical : member.key;
|
|
10040
|
-
if (logging) log(printable(`[
|
|
10093
|
+
const [label, subject] = member.role === "flow" ? ["e2e test", member.canonical] : [member.role, member.key];
|
|
10094
|
+
if (logging) log(printable(`[e2e-files] ${label} ${subject}: ${sent}`));
|
|
10041
10095
|
};
|
|
10042
10096
|
async function flow(anchorDir, target, onlyPresent) {
|
|
10043
10097
|
const key = flowMemberKey(anchorDir, target);
|
|
@@ -10203,7 +10257,7 @@ function recordedStep(args) {
|
|
|
10203
10257
|
if (typeof text !== "string") return void 0;
|
|
10204
10258
|
let parsed;
|
|
10205
10259
|
try {
|
|
10206
|
-
parsed = JSON.parse(text);
|
|
10260
|
+
parsed = canonicalToolArgs(command, JSON.parse(text));
|
|
10207
10261
|
} catch {
|
|
10208
10262
|
return void 0;
|
|
10209
10263
|
}
|
|
@@ -10226,7 +10280,7 @@ async function collectStepMembers(args, opts, recordingDirFor = e2eDirFor) {
|
|
|
10226
10280
|
const self = await collector.flow(path11.dirname(recording), path11.basename(recording));
|
|
10227
10281
|
let runArgs = step.args;
|
|
10228
10282
|
const named = nestedFlowTarget(runArgs);
|
|
10229
|
-
if (named?.kind === "
|
|
10283
|
+
if (named?.kind === "e2e_test_path") {
|
|
10230
10284
|
const stem = path11.basename(named.path, ".yaml");
|
|
10231
10285
|
const base = path11.basename(named.path);
|
|
10232
10286
|
const dir = path11.resolve(path11.dirname(named.path));
|
|
@@ -10235,7 +10289,7 @@ async function collectStepMembers(args, opts, recordingDirFor = e2eDirFor) {
|
|
|
10235
10289
|
if (dirs.some((d) => path11.resolve(d) === dir)) {
|
|
10236
10290
|
await collector.flow(path11.dirname(named.path), base);
|
|
10237
10291
|
runArgs = { ...runArgs, name: stem };
|
|
10238
|
-
delete runArgs.
|
|
10292
|
+
delete runArgs.e2e_test_path;
|
|
10239
10293
|
}
|
|
10240
10294
|
}
|
|
10241
10295
|
const target = nestedFlowTarget(runArgs);
|
|
@@ -10263,7 +10317,21 @@ async function nameToE2ePathForOlderServer(tool, fileInputs, args) {
|
|
|
10263
10317
|
const picked = await pickE2eFile(target.paths);
|
|
10264
10318
|
if (!picked.exists || picked.legacy) return args;
|
|
10265
10319
|
const { name: _name, ...rest } = args;
|
|
10266
|
-
return { ...rest,
|
|
10320
|
+
return { ...rest, e2e_test_path: picked.path };
|
|
10321
|
+
}
|
|
10322
|
+
function formerParamsForOlderServer(meta, args) {
|
|
10323
|
+
const listed = meta.inputSchema.properties;
|
|
10324
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) return args;
|
|
10325
|
+
if (typeof listed !== "object" || listed === null) return args;
|
|
10326
|
+
const record = args;
|
|
10327
|
+
let out;
|
|
10328
|
+
for (const [former, current] of Object.entries(toolParamAliases(meta.name))) {
|
|
10329
|
+
if (record[current] === void 0 || current in listed || !(former in listed)) continue;
|
|
10330
|
+
out ??= { ...record };
|
|
10331
|
+
out[former] = out[current];
|
|
10332
|
+
delete out[current];
|
|
10333
|
+
}
|
|
10334
|
+
return out ?? args;
|
|
10267
10335
|
}
|
|
10268
10336
|
async function stepForOlderServer(args, listedId) {
|
|
10269
10337
|
if (typeof args !== "object" || args === null || Array.isArray(args)) return args;
|
|
@@ -10521,12 +10589,13 @@ function createToolsClient(options = {}) {
|
|
|
10521
10589
|
async function callTool(name, args, opts) {
|
|
10522
10590
|
opts?.signal?.throwIfAborted();
|
|
10523
10591
|
const { url, token, remote } = await route();
|
|
10524
|
-
let finalArgs = args;
|
|
10592
|
+
let finalArgs = canonicalToolArgs(name, args);
|
|
10525
10593
|
const tools = await fetchTools({ signal: opts?.signal });
|
|
10526
10594
|
const meta = findTool(tools, name);
|
|
10527
10595
|
const olderServer = meta !== null && !tools.some((t) => t.name === canonicalToolId(meta.name));
|
|
10528
10596
|
if (meta) {
|
|
10529
|
-
finalArgs = await nameToE2ePathForOlderServer(meta.name, meta.fileInputs,
|
|
10597
|
+
finalArgs = await nameToE2ePathForOlderServer(meta.name, meta.fileInputs, finalArgs);
|
|
10598
|
+
finalArgs = formerParamsForOlderServer(meta, finalArgs);
|
|
10530
10599
|
}
|
|
10531
10600
|
if (olderServer && canonicalToolId(meta.name) === "e2e-add-step") {
|
|
10532
10601
|
finalArgs = await stepForOlderServer(finalArgs, (tool) => findTool(tools, tool)?.name);
|
|
@@ -10554,7 +10623,7 @@ function createToolsClient(options = {}) {
|
|
|
10554
10623
|
if (filesLogEnabled()) {
|
|
10555
10624
|
for (const file of applied.written) {
|
|
10556
10625
|
if (baselineDirs.includes(path12.dirname(file))) {
|
|
10557
|
-
diagnose(`[
|
|
10626
|
+
diagnose(`[e2e-files] baseline ${file}: written`);
|
|
10558
10627
|
}
|
|
10559
10628
|
}
|
|
10560
10629
|
}
|
package/dist/installer.mjs
CHANGED
|
@@ -15759,6 +15759,13 @@ var TOOL_ID_ALIASES = Object.freeze({
|
|
|
15759
15759
|
"flow-read-prerequisite": "e2e-read-prerequisite",
|
|
15760
15760
|
"flow-execute": "e2e-execute"
|
|
15761
15761
|
});
|
|
15762
|
+
var TOOL_PARAM_ALIASES = Object.freeze({
|
|
15763
|
+
"e2e-execute": Object.freeze({ flow_path: "e2e_test_path", flow_file: "e2e_test_file" }),
|
|
15764
|
+
"e2e-read-prerequisite": Object.freeze({
|
|
15765
|
+
flow_path: "e2e_test_path",
|
|
15766
|
+
flow_file: "e2e_test_file"
|
|
15767
|
+
})
|
|
15768
|
+
});
|
|
15762
15769
|
|
|
15763
15770
|
// ../registry/src/failure-codes.ts
|
|
15764
15771
|
var FAILURE_CODES = {
|
|
@@ -15786,6 +15793,8 @@ var FAILURE_CODES = {
|
|
|
15786
15793
|
TOOL_CAPABILITY_UNSUPPORTED_OPERATION: "TOOL_CAPABILITY_UNSUPPORTED_OPERATION",
|
|
15787
15794
|
TOOL_PLATFORM_NOT_IMPLEMENTED: "TOOL_PLATFORM_NOT_IMPLEMENTED",
|
|
15788
15795
|
TOOL_DEPENDENCY_MISSING: "TOOL_DEPENDENCY_MISSING",
|
|
15796
|
+
// Xcode's simctl or devicectl wrapper would start its component install and wait for a password.
|
|
15797
|
+
TOOL_XCODE_FIRST_LAUNCH_PENDING: "TOOL_XCODE_FIRST_LAUNCH_PENDING",
|
|
15789
15798
|
TOOL_INPUT_INVALID: "TOOL_INPUT_INVALID",
|
|
15790
15799
|
TOOLSERVER_UNHANDLED_REJECTION: "TOOLSERVER_UNHANDLED_REJECTION",
|
|
15791
15800
|
TOOLSERVER_UNCAUGHT_EXCEPTION: "TOOLSERVER_UNCAUGHT_EXCEPTION",
|
|
@@ -15958,6 +15967,16 @@ var FAILURE_CODES = {
|
|
|
15958
15967
|
JS_RUNTIME_CDP_DISCONNECTED: "JS_RUNTIME_CDP_DISCONNECTED",
|
|
15959
15968
|
JS_RUNTIME_PAUSED: "JS_RUNTIME_PAUSED",
|
|
15960
15969
|
NETWORK_INSPECTOR_CDP_DISCONNECTED: "NETWORK_INSPECTOR_CDP_DISCONNECTED",
|
|
15970
|
+
ANDROID_NETWORK_INSPECTOR_FACTORY_OPTIONS_INVALID: "ANDROID_NETWORK_INSPECTOR_FACTORY_OPTIONS_INVALID",
|
|
15971
|
+
ANDROID_NETWORK_INSPECTOR_WRONG_PLATFORM: "ANDROID_NETWORK_INSPECTOR_WRONG_PLATFORM",
|
|
15972
|
+
ANDROID_NETWORK_INSPECTOR_UNKNOWN_PACKAGE: "ANDROID_NETWORK_INSPECTOR_UNKNOWN_PACKAGE",
|
|
15973
|
+
ANDROID_NETWORK_INSPECTOR_RUN_AS_FAILED: "ANDROID_NETWORK_INSPECTOR_RUN_AS_FAILED",
|
|
15974
|
+
ANDROID_NETWORK_INSPECTOR_DEVICE_IN_USE: "ANDROID_NETWORK_INSPECTOR_DEVICE_IN_USE",
|
|
15975
|
+
ANDROID_NETWORK_INSPECTOR_NO_DEVICE_PORT: "ANDROID_NETWORK_INSPECTOR_NO_DEVICE_PORT",
|
|
15976
|
+
NATIVE_NETWORK_LOGS_STOP_UNSUPPORTED: "NATIVE_NETWORK_LOGS_STOP_UNSUPPORTED",
|
|
15977
|
+
NDJSON_CDP_REQUEST_TIMEOUT: "NDJSON_CDP_REQUEST_TIMEOUT",
|
|
15978
|
+
NDJSON_CDP_REQUEST_FAILED: "NDJSON_CDP_REQUEST_FAILED",
|
|
15979
|
+
NDJSON_CDP_CONNECTION_CLOSED: "NDJSON_CDP_CONNECTION_CLOSED",
|
|
15961
15980
|
REACT_PROFILER_NO_ACTIVE_SESSION: "REACT_PROFILER_NO_ACTIVE_SESSION",
|
|
15962
15981
|
REACT_PROFILER_CDP_CONNECTION_LOST: "REACT_PROFILER_CDP_CONNECTION_LOST",
|
|
15963
15982
|
REACT_PROFILER_NO_CPU_PROFILE: "REACT_PROFILER_NO_CPU_PROFILE",
|
|
@@ -16915,7 +16934,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
16915
16934
|
var SESSION_ID = randomUUID4();
|
|
16916
16935
|
function readCliVersion() {
|
|
16917
16936
|
if (true) {
|
|
16918
|
-
return "0.31.2-next.
|
|
16937
|
+
return "0.31.2-next.10";
|
|
16919
16938
|
}
|
|
16920
16939
|
return "0.0.0";
|
|
16921
16940
|
}
|
|
@@ -22711,6 +22730,11 @@ async function runGlobal(opts) {
|
|
|
22711
22730
|
try {
|
|
22712
22731
|
await runShellCommand(cmd);
|
|
22713
22732
|
spinner3.stop(import_picocolors4.default.green("Installed globally."));
|
|
22733
|
+
if (!isGloballyInstalled()) {
|
|
22734
|
+
log.warn(
|
|
22735
|
+
`${import_picocolors4.default.cyan("argent")} is not on PATH, so editors can't start it. Add your package manager's global bin directory to PATH.`
|
|
22736
|
+
);
|
|
22737
|
+
}
|
|
22714
22738
|
version = getGloballyInstalledVersion() ?? getInstalledVersion() ?? version;
|
|
22715
22739
|
await tel.trackPackageAction("fresh_install", packageActionStartedAt2, true);
|
|
22716
22740
|
} catch (err) {
|
|
@@ -11,6 +11,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
11
11
|
/// Returns nil when the block completed, otherwise "Name: reason".
|
|
12
12
|
+ (NSString *_Nullable)runCatching:(NS_NOESCAPE dispatch_block_t)block;
|
|
13
13
|
|
|
14
|
+
/// Returns nil when the block completed, otherwise the exception it raised.
|
|
15
|
+
+ (NSException *_Nullable)exceptionFrom:(NS_NOESCAPE dispatch_block_t)block;
|
|
16
|
+
|
|
14
17
|
@end
|
|
15
18
|
|
|
16
19
|
NS_ASSUME_NONNULL_END
|
|
@@ -3,13 +3,21 @@
|
|
|
3
3
|
@implementation ArgentExceptionGuard
|
|
4
4
|
|
|
5
5
|
+ (NSString *_Nullable)runCatching:(NS_NOESCAPE dispatch_block_t)block {
|
|
6
|
+
NSException *exception = [self exceptionFrom:block];
|
|
7
|
+
if (exception == nil) {
|
|
8
|
+
return nil;
|
|
9
|
+
}
|
|
10
|
+
NSString *name = exception.name ?: @"NSException";
|
|
11
|
+
NSString *reason = exception.reason ?: @"unhandled Objective-C exception";
|
|
12
|
+
return [NSString stringWithFormat:@"%@: %@", name, reason];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
+ (NSException *_Nullable)exceptionFrom:(NS_NOESCAPE dispatch_block_t)block {
|
|
6
16
|
@try {
|
|
7
17
|
block();
|
|
8
18
|
return nil;
|
|
9
19
|
} @catch (NSException *exception) {
|
|
10
|
-
|
|
11
|
-
NSString *reason = exception.reason ?: @"unhandled Objective-C exception";
|
|
12
|
-
return [NSString stringWithFormat:@"%@: %@", name, reason];
|
|
20
|
+
return exception;
|
|
13
21
|
}
|
|
14
22
|
}
|
|
15
23
|
|