@swmansion/argent 0.25.2-next.8 → 0.25.2

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.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/cli-cmds.mjs CHANGED
@@ -21872,7 +21872,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
21872
21872
  var SESSION_ID2 = randomUUID5();
21873
21873
  function readCliVersion() {
21874
21874
  if (true) {
21875
- return "0.25.2-next.8";
21875
+ return "0.25.2";
21876
21876
  }
21877
21877
  return "0.0.0";
21878
21878
  }
@@ -23182,6 +23182,13 @@ function stepIndent(depth) {
23182
23182
  if (typeof depth !== "number" || !Number.isInteger(depth) || depth <= 0) return "";
23183
23183
  return " ".repeat(Math.min(depth, MAX_RENDER_DEPTH));
23184
23184
  }
23185
+ function durationSuffix(ms) {
23186
+ if (typeof ms !== "number" || !Number.isFinite(ms) || ms < 0) return "";
23187
+ const tenths = Math.round(ms / 100);
23188
+ if (tenths < 600) return ` (${(tenths / 10).toFixed(1)}s)`;
23189
+ const seconds = Math.round(ms / 1e3);
23190
+ return ` (${Math.floor(seconds / 60)}m ${seconds % 60}s)`;
23191
+ }
23185
23192
  function printHelp(toStderr = false) {
23186
23193
  const print = toStderr ? console.error : console.log;
23187
23194
  print(`Usage: argent flow <subcommand> [options]
@@ -23305,7 +23312,7 @@ function renderStepLine(s, n2, topFlow) {
23305
23312
  const label = what ? `${s.kind} ${what}` : s.kind;
23306
23313
  const reason = s.reason ? ` \u2014 ${s.reason}` : "";
23307
23314
  const glyph = s.status === "pass" && s.warning ? "\u26A0" : STATUS_GLYPH[s.status];
23308
- return ` ${glyph} ${String(n2).padStart(2)} ${stepIndent(s.depth)}${label}${where}${reason}`;
23315
+ return ` ${glyph} ${String(n2).padStart(2)} ${stepIndent(s.depth)}${label}${where}${durationSuffix(s.durationMs)}${reason}`;
23309
23316
  }
23310
23317
  function renderUnderStepLine(s, n2, text2) {
23311
23318
  return `${" ".repeat(5 + Math.max(2, String(n2).length))}${stepIndent(s.depth)}${text2}`;
@@ -23328,7 +23335,7 @@ function renderSummary(report, opts = {}) {
23328
23335
  const where = opts.withDevice && report.device ? ` (started on ${report.device})` : "";
23329
23336
  const nothingCounted = report.ok && report.passed + report.failed + report.errored + report.skipped === 0;
23330
23337
  const note = nothingCounted ? " (no test steps)" : "";
23331
- return `${report.ok ? "PASS" : "FAIL"}${where} \u2014 ${report.passed} passed, ${report.failed} failed, ${report.errored} errored, ${report.skipped} skipped${warningsNote}${note}`;
23338
+ return `${report.ok ? "PASS" : "FAIL"}${where} \u2014 ${report.passed} passed, ${report.failed} failed, ${report.errored} errored, ${report.skipped} skipped${warningsNote}${note}${durationSuffix(report.durationMs)}`;
23332
23339
  }
23333
23340
  function renderArtifactLines(report) {
23334
23341
  const lines = [];
@@ -23366,8 +23373,8 @@ function renderFailedSteps(report) {
23366
23373
  }
23367
23374
  return lines;
23368
23375
  }
23369
- function renderBatchSummary(counts) {
23370
- return `${counts.failed === 0 ? "PASS" : "FAIL"} \u2014 ${counts.total} flow${counts.total === 1 ? "" : "s"}: ${counts.passed} passed, ${counts.failed} failed, ${counts.skipped} skipped`;
23376
+ function renderBatchSummary(counts, durationMs) {
23377
+ return `${counts.failed === 0 ? "PASS" : "FAIL"} \u2014 ${counts.total} flow${counts.total === 1 ? "" : "s"}: ${counts.passed} passed, ${counts.failed} failed, ${counts.skipped} skipped${durationSuffix(durationMs)}`;
23371
23378
  }
23372
23379
  var SAFE_ARTIFACT_NAME = FLOW_NAME_PATTERN;
23373
23380
  var SAFE_FLOW_NAME = FLOW_NAME_PATTERN;
@@ -23682,6 +23689,7 @@ async function runFlowDirectory(dir, args, projectRoot, options) {
23682
23689
  const { callTool, baseUrl } = createToolsClient({ paths: options.paths });
23683
23690
  const outputBase = args.output ? path14.resolve(args.output) : void 0;
23684
23691
  const results = [];
23692
+ const batchStartedAt = Date.now();
23685
23693
  let stopped = false;
23686
23694
  for (const [i2, rel] of flows.entries()) {
23687
23695
  if (!args.json) console.log(`[${i2 + 1}/${flows.length}] ${rel}`);
@@ -23737,11 +23745,14 @@ async function runFlowDirectory(dir, args, projectRoot, options) {
23737
23745
  failed: results.filter((r2) => r2.status === "fail").length,
23738
23746
  skipped: results.filter((r2) => r2.status === "skip").length
23739
23747
  };
23748
+ const durationMs = Date.now() - batchStartedAt;
23740
23749
  if (args.json) {
23741
- console.log(JSON.stringify({ ok: counts.failed === 0, ...counts, flows: results }, null, 2));
23750
+ console.log(
23751
+ JSON.stringify({ ok: counts.failed === 0, ...counts, durationMs, flows: results }, null, 2)
23752
+ );
23742
23753
  } else {
23743
23754
  console.log(`
23744
- ${renderBatchSummary(counts)}`);
23755
+ ${renderBatchSummary(counts, durationMs)}`);
23745
23756
  }
23746
23757
  return exitAfterFlush(counts.failed === 0 ? 0 : 1);
23747
23758
  }
@@ -16725,7 +16725,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
16725
16725
  var SESSION_ID = randomUUID4();
16726
16726
  function readCliVersion() {
16727
16727
  if (true) {
16728
- return "0.25.2-next.8";
16728
+ return "0.25.2";
16729
16729
  }
16730
16730
  return "0.0.0";
16731
16731
  }
@@ -18542,6 +18542,13 @@ function stepIndent(depth) {
18542
18542
  if (typeof depth !== "number" || !Number.isInteger(depth) || depth <= 0) return "";
18543
18543
  return " ".repeat(Math.min(depth, MAX_RENDER_DEPTH));
18544
18544
  }
18545
+ function durationSuffix(ms) {
18546
+ if (typeof ms !== "number" || !Number.isFinite(ms) || ms < 0) return "";
18547
+ const tenths = Math.round(ms / 100);
18548
+ if (tenths < 600) return ` (${(tenths / 10).toFixed(1)}s)`;
18549
+ const seconds = Math.round(ms / 1e3);
18550
+ return ` (${Math.floor(seconds / 60)}m ${seconds % 60}s)`;
18551
+ }
18545
18552
  function stepLabel(step) {
18546
18553
  if (step.kind === "echo") return step.message ?? "";
18547
18554
  if (step.tool) return step.tool;
@@ -18565,9 +18572,10 @@ async function flowRunToMcpContent(result, ctx) {
18565
18572
  const reason = step.reason ?? step.error;
18566
18573
  const suffix = reason ? ` \u2014 ${reason}` : "";
18567
18574
  const warning = step.warning ? ` \u26A0 ${step.warning}` : "";
18575
+ const timing = step.kind === "echo" ? "" : durationSuffix(step.durationMs);
18568
18576
  blocks.push({
18569
18577
  type: "text",
18570
- text: `[${num}] ${glyph}${stepIndent(step.depth)}${stepLabel(step)}${suffix}${warning}`
18578
+ text: `[${num}] ${glyph}${stepIndent(step.depth)}${stepLabel(step)}${timing}${suffix}${warning}`
18571
18579
  });
18572
18580
  const scriptLog = typeof step.scriptLog === "string" ? step.scriptLog : "";
18573
18581
  const scriptLogTruncated = step.scriptLogTruncated === true;
@@ -18589,7 +18597,7 @@ async function flowRunToMcpContent(result, ctx) {
18589
18597
  const note = result.ok && counted === 0 ? " (no test steps)" : "";
18590
18598
  blocks.push({
18591
18599
  type: "text",
18592
- text: `${result.ok ? "PASS" : "FAIL"} \u2014 ${result.passed ?? 0} passed, ${result.failed ?? 0} failed, ${result.errored ?? 0} errored, ${result.skipped ?? 0} skipped${note}`
18600
+ text: `${result.ok ? "PASS" : "FAIL"} \u2014 ${result.passed ?? 0} passed, ${result.failed ?? 0} failed, ${result.errored ?? 0} errored, ${result.skipped ?? 0} skipped${note}${durationSuffix(result.durationMs)}`
18593
18601
  });
18594
18602
  } else {
18595
18603
  blocks.push({ type: "text", text: `Flow "${result.flow}" complete.` });
@@ -70657,8 +70657,13 @@ function defaultAndroidRoots() {
70657
70657
  "/opt/android-sdk",
70658
70658
  "/usr/lib/android-sdk",
70659
70659
  // Debian/Ubuntu `android-sdk` apt package
70660
- "/usr/local/share/android-sdk"
70661
- // Homebrew cask
70660
+ "/usr/local/share/android-sdk",
70661
+ // Homebrew cask (Intel prefix)
70662
+ // Where `brew install --cask android-commandlinetools` roots the SDK, so an
70663
+ // `sdkmanager`-installed platform-tools is found without setting ANDROID_HOME
70664
+ // — otherwise following the missing-adb hint still leaves the tool failing.
70665
+ "/opt/homebrew/share/android-commandlinetools",
70666
+ "/usr/local/share/android-commandlinetools"
70662
70667
  ];
70663
70668
  if (process.platform === "win32") {
70664
70669
  const localAppData = process.env.LOCALAPPDATA?.trim();
@@ -94799,7 +94804,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
94799
94804
  var SESSION_ID = (0, import_node_crypto3.randomUUID)();
94800
94805
  function readCliVersion() {
94801
94806
  if (true) {
94802
- return "0.25.2-next.8";
94807
+ return "0.25.2";
94803
94808
  }
94804
94809
  return "0.0.0";
94805
94810
  }
@@ -96058,8 +96063,8 @@ var DependencyMissingError = class extends Error {
96058
96063
  var CACHE_TTL_MS2 = 6e4;
96059
96064
  var cache3 = /* @__PURE__ */ new Map();
96060
96065
  var INSTALL_HINTS = {
96061
- "xcrun": "Xcode command-line tools are not installed. Run `xcode-select --install` (or install Xcode from the App Store) and retry. Only required for iOS simulators.",
96062
- "adb": "Android SDK Platform Tools not found. Install with `brew install --cask android-platform-tools` or via Android Studio \u2192 SDK Manager. If installed, ensure `adb` is on PATH or point `$ANDROID_HOME` (or the `android.sdkRoot` config key) at the SDK root (the resolver checks `<root>/platform-tools/adb`). Only required for Android devices and emulators.",
96066
+ "xcrun": "Xcode command-line tools not found \u2014 iOS/tvOS simulators need a macOS host with them installed. On macOS, run `xcode-select --install` (or install Xcode from the App Store) and retry.",
96067
+ "adb": "Android SDK Platform Tools not found. Install `platform-tools` via Android Studio \u2192 SDK Manager or `sdkmanager 'platform-tools'` (`brew install --cask android-platform-tools` on macOS). If installed, ensure `adb` is on PATH or point `$ANDROID_HOME` (or the `android.sdkRoot` config key) at the SDK root (the resolver checks `<root>/platform-tools/adb`). Only required for Android devices and emulators.",
96063
96068
  "emulator": "Android Emulator not found. Install via Android Studio \u2192 SDK Manager \u2192 Emulator, or `sdkmanager 'emulator'`. If installed, ensure `emulator` is on PATH or point `$ANDROID_HOME` (or the `android.sdkRoot` config key) at the SDK root (the resolver checks `<root>/emulator/emulator`). Only required to launch new Android emulators via `boot-device`.",
96064
96069
  "sim-remote": "`sim-remote` CLI not found on PATH. Install via the radon-cloud project (see its README) and run `sim-remote login` before invoking any ios-remote tool. Only required for remote iOS simulators.",
96065
96070
  "vega": "Vega SDK CLI not found. Install the Amazon Vega SDK and run `source ~/vega/env` so `vega` (or its `kepler` alias) is on PATH; the resolver also checks `~/vega/bin/vega`. Only required for Vega (Fire TV) devices."
@@ -96159,7 +96164,7 @@ var import_node_path6 = __toESM(require("node:path"));
96159
96164
  var import_semver2 = __toESM(require_semver2());
96160
96165
 
96161
96166
  // ../tool-server/package.json
96162
- var version2 = "0.25.1";
96167
+ var version2 = "0.25.2";
96163
96168
 
96164
96169
  // ../tool-server/src/utils/update-checker.ts
96165
96170
  var import_update_core = __toESM(require_dist4());
@@ -142732,7 +142737,7 @@ async function startCaptureLocked(api, params) {
142732
142737
  const ffmpeg = await resolveFfmpeg();
142733
142738
  if (!ffmpeg) {
142734
142739
  throw new FailureError(
142735
- "`ffmpeg` was not found on PATH. Install it (e.g. `brew install ffmpeg`) to record the screen.",
142740
+ "`ffmpeg` was not found on PATH. Install a build with libx264 (`brew install ffmpeg` on macOS, `apt install ffmpeg` on Debian/Ubuntu) and retry.",
142736
142741
  {
142737
142742
  error_code: FAILURE_CODES.SCREEN_RECORDING_FFMPEG_NOT_FOUND,
142738
142743
  failure_stage: "screen_recording_resolve_ffmpeg",
@@ -154793,6 +154798,7 @@ Returns a per-step report: the first failure stops the run and the rest report a
154793
154798
  fileInputs: fileInputs3,
154794
154799
  services: () => ({}),
154795
154800
  async execute(_services, params, ctx) {
154801
+ const runStartedAt = Date.now();
154796
154802
  const signal = ctx?.signal;
154797
154803
  const { filePath, flowName, viaUpload } = await resolveFlowSource(
154798
154804
  params,
@@ -154835,6 +154841,7 @@ Returns a per-step report: the first failure stops the run and the rest report a
154835
154841
  executionPrerequisite: flow.executionPrerequisite
154836
154842
  };
154837
154843
  }
154844
+ const resolveStartedAt = Date.now();
154838
154845
  const resolved = await resolveRunDevice(
154839
154846
  registry2,
154840
154847
  ctx,
@@ -154867,6 +154874,7 @@ Returns a per-step report: the first failure stops the run and the rest report a
154867
154874
  stopped: false,
154868
154875
  pinned: statusBarPinned,
154869
154876
  owned: resolved.booted ? [resolved.booted] : [],
154877
+ ...resolved.booted ? { hoistedBootMs: Date.now() - resolveStartedAt } : {},
154870
154878
  chromiumLaunched: false,
154871
154879
  snapshotApps: /* @__PURE__ */ new Map(),
154872
154880
  projectRoot: params.project_root,
@@ -154892,7 +154900,8 @@ Returns a per-step report: the first failure stops the run and the rest report a
154892
154900
  device?.id ?? "",
154893
154901
  flow.executionPrerequisite,
154894
154902
  state3.reports,
154895
- aborted2
154903
+ aborted2,
154904
+ { startedAt: runStartedAt, durationMs: Date.now() - runStartedAt }
154896
154905
  );
154897
154906
  }
154898
154907
  };
@@ -155033,7 +155042,7 @@ async function frontChromiumPage(registry2, device) {
155033
155042
  } catch {
155034
155043
  }
155035
155044
  }
155036
- function summarize(flowName, deviceId, executionPrerequisite, steps, aborted2) {
155045
+ function summarize(flowName, deviceId, executionPrerequisite, steps, aborted2, timing) {
155037
155046
  let passed = 0;
155038
155047
  let failed2 = 0;
155039
155048
  let skipped = 0;
@@ -155059,7 +155068,8 @@ function summarize(flowName, deviceId, executionPrerequisite, steps, aborted2) {
155059
155068
  failed: failed2,
155060
155069
  skipped,
155061
155070
  errored,
155062
- steps
155071
+ steps,
155072
+ ...timing
155063
155073
  };
155064
155074
  }
155065
155075
  function pushReport(state3, report) {
@@ -155150,7 +155160,13 @@ async function execSteps(state3, steps, scope) {
155150
155160
  await execBlockStep(state3, step, scope);
155151
155161
  continue;
155152
155162
  }
155163
+ let startedAt = Date.now();
155164
+ if (step.kind === "launch" && state3.hoistedBootMs !== void 0) {
155165
+ startedAt -= state3.hoistedBootMs;
155166
+ state3.hoistedBootMs = void 0;
155167
+ }
155153
155168
  const report = await execLeafStep(state3, step, index, scope);
155169
+ if (report.status !== "skip") report.durationMs = Date.now() - startedAt;
155154
155170
  pushReport(state3, report);
155155
155171
  if (report.status === "fail" || report.status === "error") state3.stopped = true;
155156
155172
  }
@@ -155193,6 +155209,7 @@ async function execWhenStep(state3, step, scope) {
155193
155209
  ...depthOf(scope)
155194
155210
  };
155195
155211
  const inner = childScope(scope);
155212
+ const guardStartedAt = Date.now();
155196
155213
  let met;
155197
155214
  if (step.condition.kind === "platform") {
155198
155215
  const guardEnv = deviceEnv(state3);
@@ -155209,7 +155226,8 @@ async function execWhenStep(state3, step, scope) {
155209
155226
  pushReport(state3, {
155210
155227
  ...marker,
155211
155228
  status: "error",
155212
- reason: `could not evaluate when guard (${label}): ${probe3.reason}`
155229
+ reason: `could not evaluate when guard (${label}): ${probe3.reason}`,
155230
+ durationMs: Date.now() - guardStartedAt
155213
155231
  });
155214
155232
  state3.stopped = true;
155215
155233
  reportBlockSkipped(state3, step.steps, inner, "when guard errored");
@@ -155222,12 +155240,18 @@ async function execWhenStep(state3, step, scope) {
155222
155240
  pushReport(state3, {
155223
155241
  ...marker,
155224
155242
  status: "skip",
155225
- reason: `condition not met (${label}) \u2014 block skipped (${n} step${n === 1 ? "" : "s"})`
155243
+ reason: `condition not met (${label}) \u2014 block skipped (${n} step${n === 1 ? "" : "s"})`,
155244
+ durationMs: Date.now() - guardStartedAt
155226
155245
  });
155227
155246
  reportBlockSkipped(state3, step.steps, inner, "when block skipped");
155228
155247
  return;
155229
155248
  }
155230
- pushReport(state3, { ...marker, status: "pass", reason: `condition met (${label})` });
155249
+ pushReport(state3, {
155250
+ ...marker,
155251
+ status: "pass",
155252
+ reason: `condition met (${label})`,
155253
+ durationMs: Date.now() - guardStartedAt
155254
+ });
155231
155255
  await execSteps(state3, step.steps, inner);
155232
155256
  }
155233
155257
  function baselineKeyFor(canonicalPath, flowName) {
@@ -155238,6 +155262,7 @@ async function execRunStep(state3, step, scope) {
155238
155262
  const index = state3.reports.length;
155239
155263
  const target = step.flow;
155240
155264
  const display = runDisplayName(target, scope);
155265
+ const startedAt = Date.now();
155241
155266
  const fail = (reason) => {
155242
155267
  pushReport(state3, {
155243
155268
  index,
@@ -155246,7 +155271,8 @@ async function execRunStep(state3, step, scope) {
155246
155271
  flow: display,
155247
155272
  target,
155248
155273
  reason,
155249
- ...depthOf(scope)
155274
+ ...depthOf(scope),
155275
+ durationMs: Date.now() - startedAt
155250
155276
  });
155251
155277
  state3.stopped = true;
155252
155278
  };
@@ -155284,7 +155310,8 @@ async function execRunStep(state3, step, scope) {
155284
155310
  status: "pass",
155285
155311
  flow: display,
155286
155312
  target,
155287
- ...depthOf(scope)
155313
+ ...depthOf(scope),
155314
+ durationMs: Date.now() - startedAt
155288
155315
  });
155289
155316
  await execSteps(
155290
155317
  state3,
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/argent",
3
- "version": "0.25.2-next.8",
3
+ "version": "0.25.2",
4
4
  "mcpName": "io.github.software-mansion/argent",
5
5
  "description": "MCP server for iOS Simulator and Android Emulator control",
6
6
  "license": "Apache-2.0",
@@ -44,5 +44,5 @@ A recording does not stop itself before its `timeLimitSeconds` cap, so a forgott
44
44
  - **The timeline is paced to a steady 30 fps**: a device only emits a frame when its screen changes, so captured frames are re-paced onto a fixed timeline rather than bunching up. With static-frame trimming off (`trimStatic: false`) that timeline is wall-clock accurate — a completely still screen still comes back as a full-length video (compressing to almost nothing) and `durationMs` matches the time you actually recorded. With trimming on (the default, see §3) still stretches past the grace window are collapsed, so `durationMs` is the trimmed video length and `wallClockMs` carries the real elapsed time.
45
45
  - **Android**: records at the device's native resolution; secure screens (DRM, some password fields) come out black.
46
46
  - **Unsupported**: tvOS simulators, physical iPhones, Chromium apps, Vega/Fire TV, and remote (`remote:`-prefixed) simulators — none of them expose a readable frame stream. For a single still frame use `screenshot`; for a replayable interaction script use `argent-create-flow` instead of a video.
47
- - **ffmpeg is required**: it is the encoder, so `screen-recording-start` fails up front with an install hint if it is missing (`brew install ffmpeg`). It is resolved from `PATH` plus the usual Homebrew prefixes.
47
+ - **ffmpeg is required**: it is the encoder, so `screen-recording-start` fails up front with an install hint if it is missing (`brew install ffmpeg` on macOS). It is resolved from `PATH` plus the usual Homebrew prefixes.
48
48
  - **Watermark**: the Argent logo + "By @swmansion" is stamped bottom-left while encoding, faint (20% opacity) and per-pixel contrast-matched to the background (light logo over dark UI, dark logo over light UI). On by default — turn it off with `argent disable video-watermark` (re-enable with `argent enable video-watermark`). The flag is read when the recording starts.