@swmansion/argent 0.18.1-next.0 → 0.18.1-next.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.
package/dist/cli-cmds.mjs CHANGED
@@ -204,7 +204,7 @@ function generateAuthToken() {
204
204
  return generateToken();
205
205
  }
206
206
  function findFreePort() {
207
- return new Promise((resolve8, reject) => {
207
+ return new Promise((resolve9, reject) => {
208
208
  const srv = net.createServer();
209
209
  srv.listen(0, "127.0.0.1", () => {
210
210
  const addr = srv.address();
@@ -215,7 +215,7 @@ function findFreePort() {
215
215
  const port = addr.port;
216
216
  srv.close((err) => {
217
217
  if (err) reject(err);
218
- else resolve8(port);
218
+ else resolve9(port);
219
219
  });
220
220
  });
221
221
  srv.on("error", reject);
@@ -316,7 +316,7 @@ function killSpawnedChild(child, pid) {
316
316
  }
317
317
  }
318
318
  function spawnToolsServer(paths, port, options = {}) {
319
- return new Promise((resolve8, reject) => {
319
+ return new Promise((resolve9, reject) => {
320
320
  let logFd;
321
321
  try {
322
322
  fs.mkdirSync(STATE_DIR, { recursive: true });
@@ -353,7 +353,7 @@ function spawnToolsServer(paths, port, options = {}) {
353
353
  rl.close();
354
354
  child.stdout?.resume();
355
355
  child.stdout?.unref?.();
356
- settle(() => resolve8({ port: actualPort, pid }));
356
+ settle(() => resolve9({ port: actualPort, pid }));
357
357
  }
358
358
  });
359
359
  child.on("error", (err) => {
@@ -879,9 +879,9 @@ async function tarball(sourcePath) {
879
879
  return tarPath;
880
880
  }
881
881
  function sha256File(filePath) {
882
- return new Promise((resolve8, reject) => {
882
+ return new Promise((resolve9, reject) => {
883
883
  const hash = createHash2("sha256");
884
- createReadStream(filePath).on("data", (chunk) => hash.update(chunk)).on("end", () => resolve8(hash.digest("hex"))).on("error", reject);
884
+ createReadStream(filePath).on("data", (chunk) => hash.update(chunk)).on("end", () => resolve9(hash.digest("hex"))).on("error", reject);
885
885
  });
886
886
  }
887
887
  async function uploadTar(tarPath, endpoint) {
@@ -1103,7 +1103,7 @@ function createToolsClient(options = {}) {
1103
1103
  import { copyFile, mkdir as mkdir4, readFile as readFile4, realpath, rm as rm3, stat as stat2, writeFile as writeFile4 } from "node:fs/promises";
1104
1104
  import { constants as fsConstants } from "node:fs";
1105
1105
  import { tmpdir as tmpdir2 } from "node:os";
1106
- import { basename as basename3, dirname as dirname5, extname, isAbsolute as isAbsolute3, join as join8, normalize, sep as sep2 } from "node:path";
1106
+ import { basename as basename3, dirname as dirname5, extname, isAbsolute as isAbsolute3, join as join8, normalize, resolve as resolve5, sep as sep2 } from "node:path";
1107
1107
  import { createHash as createHash3 } from "node:crypto";
1108
1108
 
1109
1109
  // ../configuration-core/src/flags.ts
@@ -1493,6 +1493,18 @@ var CONFIG_SCHEMA = [
1493
1493
  // must precede dedup) and guards on the preset staying "union".
1494
1494
  merge: "union",
1495
1495
  example: '["~/DeviceSets/ci"]'
1496
+ },
1497
+ {
1498
+ key: "recordings.directory",
1499
+ description: "Directory where finished screen recordings (mp4) are saved on the client host. Absolute, `~`-prefixed, or relative to the project root (home dir when not in a project). Unset \u21D2 `.argent/recordings` under the project root.",
1500
+ scopes: ["project", "global"],
1501
+ parse: asString,
1502
+ // A repo can pin where its recordings land; falls back to the user's global
1503
+ // preference. Resolution happens on the client (the machine the mp4 is
1504
+ // persisted to), so with a remote `argent link` tool-server it is the
1505
+ // *client's* config that decides.
1506
+ merge: "prioritize-local",
1507
+ example: "~/Movies/argent"
1496
1508
  }
1497
1509
  ];
1498
1510
  function getConfigDefinition(key, registry = CONFIG_SCHEMA) {
@@ -1654,6 +1666,21 @@ function durableBaseDir() {
1654
1666
  return projectRoot ?? dirname5(argentHomeDir());
1655
1667
  }
1656
1668
  var ALLOWED_SAVE_DIRS = /* @__PURE__ */ new Set([normalize(".argent/recordings")]);
1669
+ var RECORDINGS_SAVE_DIR = normalize(".argent/recordings");
1670
+ function configuredRecordingsDir() {
1671
+ let value;
1672
+ try {
1673
+ value = getConfigValueByKey("recordings.directory");
1674
+ } catch {
1675
+ return null;
1676
+ }
1677
+ if (typeof value !== "string") return null;
1678
+ const trimmed = value.trim();
1679
+ if (trimmed === "") return null;
1680
+ const home = dirname5(argentHomeDir());
1681
+ const expanded = trimmed === "~" ? home : trimmed.startsWith("~/") || trimmed.startsWith(`~${sep2}`) ? join8(home, trimmed.slice(2)) : trimmed;
1682
+ return resolve5(durableBaseDir(), expanded);
1683
+ }
1657
1684
  var MAX_DURABLE_BYTES = 2 * 1024 * 1024 * 1024;
1658
1685
  async function readCapped(res, cap) {
1659
1686
  const headers = res.headers;
@@ -1703,6 +1730,17 @@ function durableSaveTarget(handle) {
1703
1730
  return null;
1704
1731
  }
1705
1732
  if (!ALLOWED_SAVE_DIRS.has(rel)) return null;
1733
+ if (rel === RECORDINGS_SAVE_DIR) {
1734
+ const configured = configuredRecordingsDir();
1735
+ if (configured) {
1736
+ return {
1737
+ dir: configured,
1738
+ path: join8(configured, sanitizeSegment(handle.filename)),
1739
+ base: configured,
1740
+ rel: ""
1741
+ };
1742
+ }
1743
+ }
1706
1744
  const base = durableBaseDir();
1707
1745
  const dir = join8(base, rel);
1708
1746
  return { dir, path: join8(dir, sanitizeSegment(handle.filename)), base, rel };
@@ -4438,14 +4476,14 @@ async function addSourceContext(frames) {
4438
4476
  return frames;
4439
4477
  }
4440
4478
  function getContextLinesFromFile(path16, ranges, output) {
4441
- return new Promise((resolve8) => {
4479
+ return new Promise((resolve9) => {
4442
4480
  const stream = createReadStream2(path16);
4443
4481
  const lineReaded = createInterface2({
4444
4482
  input: stream
4445
4483
  });
4446
4484
  function destroyStreamAndResolve() {
4447
4485
  stream.destroy();
4448
- resolve8();
4486
+ resolve9();
4449
4487
  }
4450
4488
  let lineNumber = 0;
4451
4489
  let currentRangeIndex = 0;
@@ -5742,9 +5780,9 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
5742
5780
  if (!waitUntil) return;
5743
5781
  if (this.disabled || this.optedOut) return;
5744
5782
  if (!this._waitUntilCycle) {
5745
- let resolve8;
5783
+ let resolve9;
5746
5784
  const promise = new Promise((r2) => {
5747
- resolve8 = r2;
5785
+ resolve9 = r2;
5748
5786
  });
5749
5787
  try {
5750
5788
  waitUntil(promise);
@@ -5752,7 +5790,7 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
5752
5790
  return;
5753
5791
  }
5754
5792
  this._waitUntilCycle = {
5755
- resolve: resolve8,
5793
+ resolve: resolve9,
5756
5794
  startedAt: Date.now(),
5757
5795
  timer: void 0
5758
5796
  };
@@ -5776,12 +5814,12 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
5776
5814
  return cycle?.resolve;
5777
5815
  }
5778
5816
  async resolveWaitUntilFlush() {
5779
- const resolve8 = this._consumeWaitUntilCycle();
5817
+ const resolve9 = this._consumeWaitUntilCycle();
5780
5818
  try {
5781
5819
  await super.flush();
5782
5820
  } catch {
5783
5821
  } finally {
5784
- resolve8?.();
5822
+ resolve9?.();
5785
5823
  }
5786
5824
  }
5787
5825
  getPersistedProperty(key) {
@@ -5902,15 +5940,15 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
5902
5940
  async waitForLocalEvaluationReady(timeoutMs = THIRTY_SECONDS) {
5903
5941
  if (this.isLocalEvaluationReady()) return true;
5904
5942
  if (void 0 === this.featureFlagsPoller) return false;
5905
- return new Promise((resolve8) => {
5943
+ return new Promise((resolve9) => {
5906
5944
  const timeout = setTimeout(() => {
5907
5945
  cleanup();
5908
- resolve8(false);
5946
+ resolve9(false);
5909
5947
  }, timeoutMs);
5910
5948
  const cleanup = this._events.on("localEvaluationFlagsLoaded", (count) => {
5911
5949
  clearTimeout(timeout);
5912
5950
  cleanup();
5913
- resolve8(count > 0);
5951
+ resolve9(count > 0);
5914
5952
  });
5915
5953
  });
5916
5954
  }
@@ -6365,14 +6403,14 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
6365
6403
  this.context?.enter(data, options);
6366
6404
  }
6367
6405
  async _shutdown(shutdownTimeoutMs) {
6368
- const resolve8 = this._consumeWaitUntilCycle();
6406
+ const resolve9 = this._consumeWaitUntilCycle();
6369
6407
  await this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs);
6370
6408
  this.errorTracking.shutdown();
6371
6409
  try {
6372
6410
  return await super._shutdown(shutdownTimeoutMs);
6373
6411
  } finally {
6374
6412
  this.distinctIdHasSentFlagCalls = {};
6375
- resolve8?.();
6413
+ resolve9?.();
6376
6414
  }
6377
6415
  }
6378
6416
  async _requestRemoteConfigPayload(flagKey) {
@@ -8106,12 +8144,12 @@ function resolveHostFingerprint() {
8106
8144
  }
8107
8145
  }
8108
8146
  function resolveHostFingerprintAsync() {
8109
- return new Promise((resolve8) => {
8147
+ return new Promise((resolve9) => {
8110
8148
  let binary;
8111
8149
  try {
8112
8150
  binary = simulatorServerBinaryPath();
8113
8151
  } catch {
8114
- resolve8(null);
8152
+ resolve9(null);
8115
8153
  return;
8116
8154
  }
8117
8155
  let settled = false;
@@ -8124,7 +8162,7 @@ function resolveHostFingerprintAsync() {
8124
8162
  child?.kill("SIGKILL");
8125
8163
  } catch {
8126
8164
  }
8127
- resolve8(value);
8165
+ resolve9(value);
8128
8166
  };
8129
8167
  const watchdog = setTimeout(() => finish(null), FINGERPRINT_TIMEOUT_MS);
8130
8168
  watchdog.unref?.();
@@ -8366,7 +8404,7 @@ async function shutdown(timeoutMs = SHORT_FLUSH_TIMEOUT_MS) {
8366
8404
  try {
8367
8405
  await Promise.race([
8368
8406
  client2.shutdown(timeoutMs),
8369
- new Promise((resolve8) => setTimeout(resolve8, timeoutMs + 250).unref())
8407
+ new Promise((resolve9) => setTimeout(resolve9, timeoutMs + 250).unref())
8370
8408
  ]);
8371
8409
  } catch (err) {
8372
8410
  emitDebugError("shutdown failed", err);
@@ -8386,7 +8424,7 @@ async function markDisabled() {
8386
8424
  try {
8387
8425
  await Promise.race([
8388
8426
  client2.shutdown(SHORT_FLUSH_TIMEOUT_MS),
8389
- new Promise((resolve8) => setTimeout(resolve8, SHORT_FLUSH_TIMEOUT_MS).unref())
8427
+ new Promise((resolve9) => setTimeout(resolve9, SHORT_FLUSH_TIMEOUT_MS).unref())
8390
8428
  ]);
8391
8429
  } catch {
8392
8430
  }
@@ -8628,13 +8666,13 @@ function splitOptions(argv) {
8628
8666
  return { json, outPath, argvForFlags: rest };
8629
8667
  }
8630
8668
  async function readStdin() {
8631
- return new Promise((resolve8, reject) => {
8669
+ return new Promise((resolve9, reject) => {
8632
8670
  let data = "";
8633
8671
  process.stdin.setEncoding("utf8");
8634
8672
  process.stdin.on("data", (chunk) => {
8635
8673
  data += chunk;
8636
8674
  });
8637
- process.stdin.on("end", () => resolve8(data));
8675
+ process.stdin.on("end", () => resolve9(data));
8638
8676
  process.stdin.on("error", reject);
8639
8677
  });
8640
8678
  }
@@ -8996,7 +9034,7 @@ function resolveArtifactDisplayPaths(report) {
8996
9034
  }
8997
9035
  function exitAfterFlush(code, streams = [process.stdout, process.stderr]) {
8998
9036
  return Promise.all(
8999
- streams.map((s) => new Promise((resolve8) => s.write("", () => resolve8())))
9037
+ streams.map((s) => new Promise((resolve9) => s.write("", () => resolve9())))
9000
9038
  ).then(() => process.exit(code));
9001
9039
  }
9002
9040
  function renderReport(report) {
@@ -9881,7 +9919,7 @@ function ptyInjectBeats(text2) {
9881
9919
  ];
9882
9920
  }
9883
9921
  function sleep(ms) {
9884
- return new Promise((resolve8) => setTimeout(resolve8, ms));
9922
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
9885
9923
  }
9886
9924
  var DEFAULT_COLS = 80;
9887
9925
  var DEFAULT_ROWS = 24;
@@ -10102,7 +10140,7 @@ var SPAWN_GRACE_MS = 8e3;
10102
10140
  var DEATH_CONFIRMATIONS = 3;
10103
10141
  var SSE_RECONNECT_MS = 1e3;
10104
10142
  function sleep2(ms) {
10105
- return new Promise((resolve8) => setTimeout(resolve8, ms));
10143
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
10106
10144
  }
10107
10145
  var TRUST_PROMPT_RE = /trust this folder|do you trust|yes,? i trust|trust the files in this/i;
10108
10146
  async function dismissTrustPrompt(session) {
@@ -9593,13 +9593,13 @@ var require_min_release_age = __commonJS({
9593
9593
  return Number.isFinite(days) && days > 0 ? days * config_parse_1.DAY_MS : 0;
9594
9594
  }
9595
9595
  function probe(p) {
9596
- return new Promise((resolve11) => {
9596
+ return new Promise((resolve12) => {
9597
9597
  (0, node_child_process_1.exec)(p.command, { timeout: PROBE_TIMEOUT_MS2, windowsHide: true }, (err, stdout2) => {
9598
9598
  if (err) {
9599
- resolve11(0);
9599
+ resolve12(0);
9600
9600
  return;
9601
9601
  }
9602
- resolve11(p.parse(stdout2));
9602
+ resolve12(p.parse(stdout2));
9603
9603
  });
9604
9604
  });
9605
9605
  }
@@ -9632,12 +9632,12 @@ var require_registry = __commonJS({
9632
9632
  var node_https_1 = __importDefault(__require("node:https"));
9633
9633
  var REQUEST_TIMEOUT_MS = 1e4;
9634
9634
  function fetchRegistryInfo2(url) {
9635
- return new Promise((resolve11) => {
9635
+ return new Promise((resolve12) => {
9636
9636
  let resolved = false;
9637
9637
  const safeResolve = (value) => {
9638
9638
  if (!resolved) {
9639
9639
  resolved = true;
9640
- resolve11(value);
9640
+ resolve12(value);
9641
9641
  }
9642
9642
  };
9643
9643
  const req = node_https_1.default.get(url, { timeout: REQUEST_TIMEOUT_MS }, (res) => {
@@ -13702,14 +13702,14 @@ async function addSourceContext(frames) {
13702
13702
  return frames;
13703
13703
  }
13704
13704
  function getContextLinesFromFile(path20, ranges, output) {
13705
- return new Promise((resolve11) => {
13705
+ return new Promise((resolve12) => {
13706
13706
  const stream = createReadStream(path20);
13707
13707
  const lineReaded = createInterface2({
13708
13708
  input: stream
13709
13709
  });
13710
13710
  function destroyStreamAndResolve() {
13711
13711
  stream.destroy();
13712
- resolve11();
13712
+ resolve12();
13713
13713
  }
13714
13714
  let lineNumber = 0;
13715
13715
  let currentRangeIndex = 0;
@@ -15006,9 +15006,9 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
15006
15006
  if (!waitUntil) return;
15007
15007
  if (this.disabled || this.optedOut) return;
15008
15008
  if (!this._waitUntilCycle) {
15009
- let resolve11;
15009
+ let resolve12;
15010
15010
  const promise = new Promise((r2) => {
15011
- resolve11 = r2;
15011
+ resolve12 = r2;
15012
15012
  });
15013
15013
  try {
15014
15014
  waitUntil(promise);
@@ -15016,7 +15016,7 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
15016
15016
  return;
15017
15017
  }
15018
15018
  this._waitUntilCycle = {
15019
- resolve: resolve11,
15019
+ resolve: resolve12,
15020
15020
  startedAt: Date.now(),
15021
15021
  timer: void 0
15022
15022
  };
@@ -15040,12 +15040,12 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
15040
15040
  return cycle?.resolve;
15041
15041
  }
15042
15042
  async resolveWaitUntilFlush() {
15043
- const resolve11 = this._consumeWaitUntilCycle();
15043
+ const resolve12 = this._consumeWaitUntilCycle();
15044
15044
  try {
15045
15045
  await super.flush();
15046
15046
  } catch {
15047
15047
  } finally {
15048
- resolve11?.();
15048
+ resolve12?.();
15049
15049
  }
15050
15050
  }
15051
15051
  getPersistedProperty(key) {
@@ -15166,15 +15166,15 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
15166
15166
  async waitForLocalEvaluationReady(timeoutMs = THIRTY_SECONDS) {
15167
15167
  if (this.isLocalEvaluationReady()) return true;
15168
15168
  if (void 0 === this.featureFlagsPoller) return false;
15169
- return new Promise((resolve11) => {
15169
+ return new Promise((resolve12) => {
15170
15170
  const timeout = setTimeout(() => {
15171
15171
  cleanup();
15172
- resolve11(false);
15172
+ resolve12(false);
15173
15173
  }, timeoutMs);
15174
15174
  const cleanup = this._events.on("localEvaluationFlagsLoaded", (count) => {
15175
15175
  clearTimeout(timeout);
15176
15176
  cleanup();
15177
- resolve11(count > 0);
15177
+ resolve12(count > 0);
15178
15178
  });
15179
15179
  });
15180
15180
  }
@@ -15629,14 +15629,14 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
15629
15629
  this.context?.enter(data, options);
15630
15630
  }
15631
15631
  async _shutdown(shutdownTimeoutMs) {
15632
- const resolve11 = this._consumeWaitUntilCycle();
15632
+ const resolve12 = this._consumeWaitUntilCycle();
15633
15633
  await this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs);
15634
15634
  this.errorTracking.shutdown();
15635
15635
  try {
15636
15636
  return await super._shutdown(shutdownTimeoutMs);
15637
15637
  } finally {
15638
15638
  this.distinctIdHasSentFlagCalls = {};
15639
- resolve11?.();
15639
+ resolve12?.();
15640
15640
  }
15641
15641
  }
15642
15642
  async _requestRemoteConfigPayload(flagKey) {
@@ -17524,12 +17524,12 @@ function resolveHostFingerprint() {
17524
17524
  }
17525
17525
  }
17526
17526
  function resolveHostFingerprintAsync() {
17527
- return new Promise((resolve11) => {
17527
+ return new Promise((resolve12) => {
17528
17528
  let binary;
17529
17529
  try {
17530
17530
  binary = simulatorServerBinaryPath();
17531
17531
  } catch {
17532
- resolve11(null);
17532
+ resolve12(null);
17533
17533
  return;
17534
17534
  }
17535
17535
  let settled = false;
@@ -17542,7 +17542,7 @@ function resolveHostFingerprintAsync() {
17542
17542
  child?.kill("SIGKILL");
17543
17543
  } catch {
17544
17544
  }
17545
- resolve11(value);
17545
+ resolve12(value);
17546
17546
  };
17547
17547
  const watchdog = setTimeout(() => finish(null), FINGERPRINT_TIMEOUT_MS);
17548
17548
  watchdog.unref?.();
@@ -17840,7 +17840,7 @@ async function shutdown(timeoutMs = SHORT_FLUSH_TIMEOUT_MS) {
17840
17840
  try {
17841
17841
  await Promise.race([
17842
17842
  client2.shutdown(timeoutMs),
17843
- new Promise((resolve11) => setTimeout(resolve11, timeoutMs + 250).unref())
17843
+ new Promise((resolve12) => setTimeout(resolve12, timeoutMs + 250).unref())
17844
17844
  ]);
17845
17845
  } catch (err) {
17846
17846
  emitDebugError("shutdown failed", err);
@@ -20678,12 +20678,12 @@ async function isOnline(timeoutMs = PROBE_TIMEOUT_MS) {
20678
20678
  } catch {
20679
20679
  return false;
20680
20680
  }
20681
- return new Promise((resolve11) => {
20682
- const timer = setTimeout(() => resolve11(false), timeoutMs);
20681
+ return new Promise((resolve12) => {
20682
+ const timer = setTimeout(() => resolve12(false), timeoutMs);
20683
20683
  timer.unref();
20684
20684
  dns.lookup(host, (err) => {
20685
20685
  clearTimeout(timer);
20686
- resolve11(!err);
20686
+ resolve12(!err);
20687
20687
  });
20688
20688
  });
20689
20689
  }
@@ -22303,7 +22303,7 @@ var ShellCommandError = class extends Error {
22303
22303
  signal;
22304
22304
  };
22305
22305
  function runShellCommand(cmd, opts = {}) {
22306
- return new Promise((resolve11, reject) => {
22306
+ return new Promise((resolve12, reject) => {
22307
22307
  const child = spawn2(cmd.bin, cmd.args, {
22308
22308
  stdio: ["ignore", "pipe", "pipe"],
22309
22309
  shell: process.platform === "win32",
@@ -22314,7 +22314,7 @@ function runShellCommand(cmd, opts = {}) {
22314
22314
  stderr += chunk.toString();
22315
22315
  });
22316
22316
  child.on("close", (code, signal) => {
22317
- if (code === 0) resolve11();
22317
+ if (code === 0) resolve12();
22318
22318
  else
22319
22319
  reject(
22320
22320
  new ShellCommandError(
@@ -23189,7 +23189,7 @@ async function runSkillsStep(args) {
23189
23189
  return skillsMethod;
23190
23190
  }
23191
23191
  function runNpxSkills(args, interactive, cwd) {
23192
- return new Promise((resolve11, reject) => {
23192
+ return new Promise((resolve12, reject) => {
23193
23193
  const npxCmd = process.platform === "win32" ? "npx.cmd" : "npx";
23194
23194
  const child = spawn3(npxCmd, args, {
23195
23195
  stdio: interactive ? "inherit" : ["ignore", "pipe", "pipe"],
@@ -23208,7 +23208,7 @@ function runNpxSkills(args, interactive, cwd) {
23208
23208
  }
23209
23209
  child.on("close", (code) => {
23210
23210
  if (code === 0) {
23211
- resolve11();
23211
+ resolve12();
23212
23212
  } else {
23213
23213
  const output = [stderr, stdout2].filter(Boolean).join("\n").trim();
23214
23214
  reject(new Error(output || `npx skills exited with code ${code}`));
@@ -23685,9 +23685,10 @@ var MAX_CONTENT_BYTES = 32 * 1024 * 1024;
23685
23685
  import { copyFile, mkdir as mkdir4, readFile as readFile4, realpath, rm as rm3, stat as stat3, writeFile as writeFile4 } from "node:fs/promises";
23686
23686
  import { constants as fsConstants } from "node:fs";
23687
23687
  import { tmpdir as tmpdir2 } from "node:os";
23688
- import { basename as basename4, dirname as dirname13, extname as extname2, isAbsolute as isAbsolute5, join as join19, normalize, sep as sep6 } from "node:path";
23688
+ import { basename as basename4, dirname as dirname13, extname as extname2, isAbsolute as isAbsolute5, join as join19, normalize, resolve as resolve10, sep as sep6 } from "node:path";
23689
23689
  import { createHash as createHash4 } from "node:crypto";
23690
23690
  var ALLOWED_SAVE_DIRS = /* @__PURE__ */ new Set([normalize(".argent/recordings")]);
23691
+ var RECORDINGS_SAVE_DIR = normalize(".argent/recordings");
23691
23692
  var MAX_DURABLE_BYTES = 2 * 1024 * 1024 * 1024;
23692
23693
 
23693
23694
  // ../argent-installer/src/update.ts
@@ -2984,7 +2984,7 @@ var require_compile = __commonJS({
2984
2984
  const schOrFunc = root.refs[ref];
2985
2985
  if (schOrFunc)
2986
2986
  return schOrFunc;
2987
- let _sch = resolve6.call(this, root, ref);
2987
+ let _sch = resolve7.call(this, root, ref);
2988
2988
  if (_sch === void 0) {
2989
2989
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
2990
2990
  const { schemaId } = this.opts;
@@ -3011,7 +3011,7 @@ var require_compile = __commonJS({
3011
3011
  function sameSchemaEnv(s1, s2) {
3012
3012
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3013
3013
  }
3014
- function resolve6(root, ref) {
3014
+ function resolve7(root, ref) {
3015
3015
  let sch;
3016
3016
  while (typeof (sch = this.refs[ref]) == "string")
3017
3017
  ref = sch;
@@ -3642,7 +3642,7 @@ var require_fast_uri = __commonJS({
3642
3642
  }
3643
3643
  return uri;
3644
3644
  }
3645
- function resolve6(baseURI, relativeURI, options) {
3645
+ function resolve7(baseURI, relativeURI, options) {
3646
3646
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3647
3647
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3648
3648
  schemelessOptions.skipEscape = true;
@@ -3900,7 +3900,7 @@ var require_fast_uri = __commonJS({
3900
3900
  var fastUri = {
3901
3901
  SCHEMES,
3902
3902
  normalize: normalize2,
3903
- resolve: resolve6,
3903
+ resolve: resolve7,
3904
3904
  resolveComponent,
3905
3905
  equal,
3906
3906
  serialize,
@@ -13701,12 +13701,12 @@ var StdioServerTransport = class {
13701
13701
  this.onclose?.();
13702
13702
  }
13703
13703
  send(message) {
13704
- return new Promise((resolve6) => {
13704
+ return new Promise((resolve7) => {
13705
13705
  const json = serializeMessage(message);
13706
13706
  if (this._stdout.write(json)) {
13707
- resolve6();
13707
+ resolve7();
13708
13708
  } else {
13709
- this._stdout.once("drain", resolve6);
13709
+ this._stdout.once("drain", resolve7);
13710
13710
  }
13711
13711
  });
13712
13712
  }
@@ -14304,7 +14304,7 @@ var Protocol = class {
14304
14304
  return;
14305
14305
  }
14306
14306
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
14307
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
14307
+ await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
14308
14308
  options?.signal?.throwIfAborted();
14309
14309
  }
14310
14310
  } catch (error2) {
@@ -14321,7 +14321,7 @@ var Protocol = class {
14321
14321
  */
14322
14322
  request(request, resultSchema, options) {
14323
14323
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
14324
- return new Promise((resolve6, reject) => {
14324
+ return new Promise((resolve7, reject) => {
14325
14325
  const earlyReject = (error2) => {
14326
14326
  reject(error2);
14327
14327
  };
@@ -14399,7 +14399,7 @@ var Protocol = class {
14399
14399
  if (!parseResult.success) {
14400
14400
  reject(parseResult.error);
14401
14401
  } else {
14402
- resolve6(parseResult.data);
14402
+ resolve7(parseResult.data);
14403
14403
  }
14404
14404
  } catch (error2) {
14405
14405
  reject(error2);
@@ -14660,12 +14660,12 @@ var Protocol = class {
14660
14660
  }
14661
14661
  } catch {
14662
14662
  }
14663
- return new Promise((resolve6, reject) => {
14663
+ return new Promise((resolve7, reject) => {
14664
14664
  if (signal.aborted) {
14665
14665
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
14666
14666
  return;
14667
14667
  }
14668
- const timeoutId = setTimeout(resolve6, interval);
14668
+ const timeoutId = setTimeout(resolve7, interval);
14669
14669
  signal.addEventListener("abort", () => {
14670
14670
  clearTimeout(timeoutId);
14671
14671
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -15490,7 +15490,7 @@ function generateToken() {
15490
15490
  return randomBytes(AUTH_TOKEN_BYTES).toString("hex");
15491
15491
  }
15492
15492
  function findFreePort() {
15493
- return new Promise((resolve6, reject) => {
15493
+ return new Promise((resolve7, reject) => {
15494
15494
  const srv = net.createServer();
15495
15495
  srv.listen(0, "127.0.0.1", () => {
15496
15496
  const addr = srv.address();
@@ -15501,7 +15501,7 @@ function findFreePort() {
15501
15501
  const port = addr.port;
15502
15502
  srv.close((err) => {
15503
15503
  if (err) reject(err);
15504
- else resolve6(port);
15504
+ else resolve7(port);
15505
15505
  });
15506
15506
  });
15507
15507
  srv.on("error", reject);
@@ -15599,7 +15599,7 @@ function killSpawnedChild(child, pid) {
15599
15599
  }
15600
15600
  }
15601
15601
  function spawnToolsServer(paths, port, options = {}) {
15602
- return new Promise((resolve6, reject) => {
15602
+ return new Promise((resolve7, reject) => {
15603
15603
  let logFd;
15604
15604
  try {
15605
15605
  fs.mkdirSync(STATE_DIR, { recursive: true });
@@ -15636,7 +15636,7 @@ function spawnToolsServer(paths, port, options = {}) {
15636
15636
  rl.close();
15637
15637
  child.stdout?.resume();
15638
15638
  child.stdout?.unref?.();
15639
- settle(() => resolve6({ port: actualPort, pid }));
15639
+ settle(() => resolve7({ port: actualPort, pid }));
15640
15640
  }
15641
15641
  });
15642
15642
  child.on("error", (err) => {
@@ -16085,9 +16085,9 @@ async function tarball(sourcePath) {
16085
16085
  return tarPath;
16086
16086
  }
16087
16087
  function sha256File(filePath) {
16088
- return new Promise((resolve6, reject) => {
16088
+ return new Promise((resolve7, reject) => {
16089
16089
  const hash = createHash2("sha256");
16090
- createReadStream(filePath).on("data", (chunk) => hash.update(chunk)).on("end", () => resolve6(hash.digest("hex"))).on("error", reject);
16090
+ createReadStream(filePath).on("data", (chunk) => hash.update(chunk)).on("end", () => resolve7(hash.digest("hex"))).on("error", reject);
16091
16091
  });
16092
16092
  }
16093
16093
  async function uploadTar(tarPath, endpoint) {
@@ -16206,7 +16206,7 @@ async function applyClientFileDirectives(result) {
16206
16206
  import { copyFile, mkdir as mkdir4, readFile as readFile4, realpath, rm as rm3, stat as stat2, writeFile as writeFile4 } from "node:fs/promises";
16207
16207
  import { constants as fsConstants } from "node:fs";
16208
16208
  import { tmpdir as tmpdir2 } from "node:os";
16209
- import { basename as basename3, dirname as dirname5, extname, isAbsolute as isAbsolute3, join as join8, normalize, sep as sep2 } from "node:path";
16209
+ import { basename as basename3, dirname as dirname5, extname, isAbsolute as isAbsolute3, join as join8, normalize, resolve as resolve5, sep as sep2 } from "node:path";
16210
16210
  import { createHash as createHash3 } from "node:crypto";
16211
16211
 
16212
16212
  // ../configuration-core/src/flags.ts
@@ -16310,6 +16310,31 @@ function readConfigObject(scope = "global", options = {}) {
16310
16310
  }
16311
16311
  return {};
16312
16312
  }
16313
+ var FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
16314
+ function splitKey(dottedKey) {
16315
+ const parts = dottedKey.split(".");
16316
+ if (parts.length === 0 || parts.some((p) => p === "")) {
16317
+ throw new Error(`Invalid config key "${dottedKey}": empty path segment`);
16318
+ }
16319
+ for (const p of parts) {
16320
+ if (FORBIDDEN_SEGMENTS.has(p)) {
16321
+ throw new Error(`Invalid config key "${dottedKey}": forbidden segment "${p}"`);
16322
+ }
16323
+ }
16324
+ return parts;
16325
+ }
16326
+ function isPlainObject3(value) {
16327
+ return !!value && typeof value === "object" && !Array.isArray(value);
16328
+ }
16329
+ function getAtPath(obj, dottedKey) {
16330
+ const parts = splitKey(dottedKey);
16331
+ let cur = obj;
16332
+ for (const part of parts) {
16333
+ if (!isPlainObject3(cur)) return void 0;
16334
+ cur = cur[part];
16335
+ }
16336
+ return cur;
16337
+ }
16313
16338
  var LOCK_STALE_MS2 = 1e4;
16314
16339
  var LOCK_MAX_WAIT_MS = 2e3;
16315
16340
  var LOCK_RETRY_MS = 25;
@@ -16382,8 +16407,162 @@ function updateConfig(mutate, scope = "global", options = {}) {
16382
16407
  }
16383
16408
  }
16384
16409
 
16410
+ // ../configuration-core/src/merge.ts
16411
+ function mergeRestrictive(local, global2) {
16412
+ if (local === void 0) return global2;
16413
+ if (global2 === void 0) return local;
16414
+ if (typeof local === "boolean" && typeof global2 === "boolean") {
16415
+ return local && global2;
16416
+ }
16417
+ if (typeof local === "number" && typeof global2 === "number") {
16418
+ return Math.min(local, global2);
16419
+ }
16420
+ return local;
16421
+ }
16422
+ function toArray(value) {
16423
+ return Array.isArray(value) ? value : null;
16424
+ }
16425
+ function mergeUnion(local, global2) {
16426
+ const l = toArray(local);
16427
+ const g = toArray(global2);
16428
+ if (l === null && g === null) return local ?? global2;
16429
+ const merged = [...g ?? [], ...l ?? []];
16430
+ return Array.from(new Set(merged));
16431
+ }
16432
+ function mergeIntersection(local, global2) {
16433
+ const l = toArray(local);
16434
+ const g = toArray(global2);
16435
+ if (l === null && g === null) return local ?? global2;
16436
+ if (l === null) return global2;
16437
+ if (g === null) return local;
16438
+ const globalSet = new Set(g);
16439
+ return l.filter((item) => globalSet.has(item));
16440
+ }
16441
+ function applyMergePolicy(policy, local, global2) {
16442
+ if (typeof policy === "function") return policy({ local, global: global2 });
16443
+ switch (policy) {
16444
+ case "prioritize-local":
16445
+ return local ?? global2;
16446
+ case "prioritize-global":
16447
+ return global2 ?? local;
16448
+ case "prioritize-restrictive":
16449
+ return mergeRestrictive(local, global2);
16450
+ case "union":
16451
+ return mergeUnion(local, global2);
16452
+ case "intersection":
16453
+ return mergeIntersection(local, global2);
16454
+ default: {
16455
+ const _exhaustive = policy;
16456
+ return _exhaustive;
16457
+ }
16458
+ }
16459
+ }
16460
+
16461
+ // ../configuration-core/src/config-schema.ts
16462
+ function asBoolean(raw) {
16463
+ return typeof raw === "boolean" ? raw : void 0;
16464
+ }
16465
+ function asString(raw) {
16466
+ if (typeof raw !== "string") return void 0;
16467
+ const trimmed = raw.trim();
16468
+ return trimmed === "" ? void 0 : trimmed;
16469
+ }
16470
+ function asStringArray(raw) {
16471
+ if (!Array.isArray(raw)) return void 0;
16472
+ const out = [];
16473
+ for (const item of raw) {
16474
+ if (typeof item === "string" && item.trim() !== "") out.push(item.trim());
16475
+ }
16476
+ return out;
16477
+ }
16478
+ var CONFIG_SCHEMA = [
16479
+ {
16480
+ key: "telemetry.enabled",
16481
+ 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).",
16482
+ scopes: ["global"],
16483
+ parse: asBoolean,
16484
+ // A committed project file must never re-enable telemetry a user disabled
16485
+ // globally, so the more-restrictive (opt-out) value always wins.
16486
+ merge: "prioritize-restrictive",
16487
+ // Telemetry is opt-out: with nothing stored, consent.ts treats it as
16488
+ // enabled, and the config surface must report the same instead of "(unset)".
16489
+ default: true,
16490
+ // Read-only under `argent config`: opt-in/out goes through the dedicated
16491
+ // command so the live client is drained/reset, not just the file rewritten.
16492
+ manageCommand: "argent telemetry"
16493
+ },
16494
+ {
16495
+ key: "lens.agent",
16496
+ description: "Coding-agent id remembered by `argent lens` to skip the picker.",
16497
+ scopes: ["project", "global"],
16498
+ parse: asString,
16499
+ // A repo can pin the agent its screenshots should use; falls back to the
16500
+ // user's global remembered choice.
16501
+ merge: "prioritize-local",
16502
+ example: "claude"
16503
+ },
16504
+ {
16505
+ key: "ios.additionalDeviceSets",
16506
+ description: "Additional CoreSimulator device-set directories whose simulators argent should see alongside the default set. Absolute paths (or ~/\u2026); relative entries resolve against the project root (project scope) or home (global scope).",
16507
+ scopes: ["project", "global"],
16508
+ parse: asStringArray,
16509
+ // Additive: the scopes extend each other rather than shadow — a repo's
16510
+ // committed device sets are appended to the user's global ones (global
16511
+ // baseline first, project extras after, deduplicated). Note that
16512
+ // `getAdditionalIosDeviceSets` re-implements this union (path resolution
16513
+ // must precede dedup) and guards on the preset staying "union".
16514
+ merge: "union",
16515
+ example: '["~/DeviceSets/ci"]'
16516
+ },
16517
+ {
16518
+ key: "recordings.directory",
16519
+ description: "Directory where finished screen recordings (mp4) are saved on the client host. Absolute, `~`-prefixed, or relative to the project root (home dir when not in a project). Unset \u21D2 `.argent/recordings` under the project root.",
16520
+ scopes: ["project", "global"],
16521
+ parse: asString,
16522
+ // A repo can pin where its recordings land; falls back to the user's global
16523
+ // preference. Resolution happens on the client (the machine the mp4 is
16524
+ // persisted to), so with a remote `argent link` tool-server it is the
16525
+ // *client's* config that decides.
16526
+ merge: "prioritize-local",
16527
+ example: "~/Movies/argent"
16528
+ }
16529
+ ];
16530
+ function getConfigDefinition(key, registry2 = CONFIG_SCHEMA) {
16531
+ return registry2.find((def) => def.key === key);
16532
+ }
16533
+
16385
16534
  // ../configuration-core/src/config-access.ts
16386
16535
  import * as path7 from "node:path";
16536
+ function readScopeValue(def, scope, options) {
16537
+ if (!def.scopes.includes(scope)) return void 0;
16538
+ const raw = getAtPath(readConfigObject(scope, options), def.key);
16539
+ return raw === void 0 ? void 0 : def.parse(raw);
16540
+ }
16541
+ function getConfigValue(def, options = {}) {
16542
+ const local = readScopeValue(def, "project", options);
16543
+ const global2 = readScopeValue(def, "global", options);
16544
+ const merged = applyMergePolicy(def.merge, local, global2);
16545
+ return merged ?? def.default;
16546
+ }
16547
+ function getConfigValueByKey(key, options = {}, registry2 = CONFIG_SCHEMA) {
16548
+ const def = requireDefinition(key, registry2);
16549
+ return getConfigValue(def, options);
16550
+ }
16551
+ function requireDefinition(key, registry2 = CONFIG_SCHEMA) {
16552
+ const def = getConfigDefinition(key, registry2);
16553
+ if (!def) {
16554
+ throw new UnknownConfigKeyError(key);
16555
+ }
16556
+ return def;
16557
+ }
16558
+ var UnknownConfigKeyError = class extends Error {
16559
+ constructor(key) {
16560
+ super(`Unknown configuration key "${key}".`);
16561
+ this.key = key;
16562
+ this.name = "UnknownConfigKeyError";
16563
+ }
16564
+ key;
16565
+ };
16387
16566
 
16388
16567
  // ../argent-tools-client/src/artifacts.ts
16389
16568
  var ARTIFACT_MARKER = "__argentArtifact";
@@ -16420,6 +16599,21 @@ function durableBaseDir() {
16420
16599
  return projectRoot ?? dirname5(argentHomeDir());
16421
16600
  }
16422
16601
  var ALLOWED_SAVE_DIRS = /* @__PURE__ */ new Set([normalize(".argent/recordings")]);
16602
+ var RECORDINGS_SAVE_DIR = normalize(".argent/recordings");
16603
+ function configuredRecordingsDir() {
16604
+ let value;
16605
+ try {
16606
+ value = getConfigValueByKey("recordings.directory");
16607
+ } catch {
16608
+ return null;
16609
+ }
16610
+ if (typeof value !== "string") return null;
16611
+ const trimmed = value.trim();
16612
+ if (trimmed === "") return null;
16613
+ const home = dirname5(argentHomeDir());
16614
+ const expanded = trimmed === "~" ? home : trimmed.startsWith("~/") || trimmed.startsWith(`~${sep2}`) ? join8(home, trimmed.slice(2)) : trimmed;
16615
+ return resolve5(durableBaseDir(), expanded);
16616
+ }
16423
16617
  var MAX_DURABLE_BYTES = 2 * 1024 * 1024 * 1024;
16424
16618
  async function readCapped(res, cap) {
16425
16619
  const headers = res.headers;
@@ -16469,6 +16663,17 @@ function durableSaveTarget(handle) {
16469
16663
  return null;
16470
16664
  }
16471
16665
  if (!ALLOWED_SAVE_DIRS.has(rel)) return null;
16666
+ if (rel === RECORDINGS_SAVE_DIR) {
16667
+ const configured = configuredRecordingsDir();
16668
+ if (configured) {
16669
+ return {
16670
+ dir: configured,
16671
+ path: join8(configured, sanitizeSegment(handle.filename)),
16672
+ base: configured,
16673
+ rel: ""
16674
+ };
16675
+ }
16676
+ }
16472
16677
  const base = durableBaseDir();
16473
16678
  const dir = join8(base, rel);
16474
16679
  return { dir, path: join8(dir, sanitizeSegment(handle.filename)), base, rel };
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@swmansion/argent",
3
- "version": "0.18.1-next.0",
3
+ "version": "0.18.1-next.2",
4
+ "mcpName": "io.github.software-mansion/argent",
4
5
  "description": "MCP server for iOS Simulator and Android Emulator control",
5
6
  "license": "Apache-2.0",
6
7
  "repository": {
@@ -30,7 +30,7 @@ A recording does not stop itself before its `timeLimitSeconds` cap, so a forgott
30
30
  5. Call `screen-recording-stop` with the same `udid`. It returns `{ video, durationMs, wallClockMs?, trimmedMs?, warning? }`; `video` is an artifact whose resolved path points at the durably-saved file (see below). The video is already final when stop returns (the watermark is stamped during capture, not in a second pass), so stop takes well under a second.
31
31
  6. Check `warning`: it reports cap-triggered stops, early encoder exits, a dropped frame stream, and possibly-truncated containers. Verify the file plays (or at least has a sane size) before presenting it to the user.
32
32
 
33
- **Where the file lands (durable, not scratch).** Unlike most argent artifacts (which live in a disposable temp cache the OS reclaims), a finished recording is saved durably on the **client** into `<project>/.argent/recordings/` — the project being the nearest ancestor of the client's working directory with a `.git`/`package.json`/`.argent`, or `~/.argent/recordings/` when the client isn't inside a project. This holds even for a remote `argent link` tool-server: the mp4 is written on the client host, not the server. The saved name is `screen-recording-<device>-<timestamp>.mp4`; if that name is already taken the new file lands beside it as `… (2).mp4` rather than overwriting. Because these files persist in the working tree, mention the path to the user (and note that they are untracked — add them to `.gitignore` or clean them up if they shouldn't be committed).
33
+ **Where the file lands (durable, not scratch).** Unlike most argent artifacts (which live in a disposable temp cache the OS reclaims), a finished recording is saved durably on the **client** into `<project>/.argent/recordings/` — the project being the nearest ancestor of the client's working directory with a `.git`/`package.json`/`.argent`, or `~/.argent/recordings/` when the client isn't inside a project. This holds even for a remote `argent link` tool-server: the mp4 is written on the client host, not the server. The destination can be changed with the `recordings.directory` configuration (`argent config set recordings.directory <dir>`, global by default, `--scope project` for a per-repo choice; project wins when both are set) — the value may be absolute, `~`-prefixed, or relative to the project root, and is always resolved on the client host. The saved name is `screen-recording-<device>-<timestamp>.mp4`; if that name is already taken the new file lands beside it as `… (2).mp4` rather than overwriting. Because these files persist in the working tree, mention the path to the user (and note that they are untracked — add them to `.gitignore` or clean them up if they shouldn't be committed).
34
34
 
35
35
  **Static-frame trimming (on by default).** Stretches where the screen does not change are collapsed: the first second of each still stretch is kept so pauses read naturally, then unchanged frames are dropped until something moves again (a change of even a couple of pixels counts). So you can leave a recording running across slow steps, waits, or thinking time without padding the clip with dead air — a 40-second session with 5 seconds of real activity comes back as a ~5-7 second video. When trimming removed anything, stop also returns `wallClockMs` (real elapsed time) and `trimmedMs` (how much was cut); `durationMs` is always the length of the video you actually get. Pass `trimStatic: false` to `screen-recording-start` when you want a faithful real-time recording (e.g. to measure how long something took on screen).
36
36