@swmansion/argent 0.9.0 → 0.10.0

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.
@@ -3225,8 +3225,8 @@ var require_utils = __commonJS({
3225
3225
  }
3226
3226
  return ind;
3227
3227
  }
3228
- function removeDotSegments(path2) {
3229
- let input = path2;
3228
+ function removeDotSegments(path3) {
3229
+ let input = path3;
3230
3230
  const output = [];
3231
3231
  let nextSlash = -1;
3232
3232
  let len = 0;
@@ -3478,8 +3478,8 @@ var require_schemes = __commonJS({
3478
3478
  wsComponent.secure = void 0;
3479
3479
  }
3480
3480
  if (wsComponent.resourceName) {
3481
- const [path2, query] = wsComponent.resourceName.split("?");
3482
- wsComponent.path = path2 && path2 !== "/" ? path2 : void 0;
3481
+ const [path3, query] = wsComponent.resourceName.split("?");
3482
+ wsComponent.path = path3 && path3 !== "/" ? path3 : void 0;
3483
3483
  wsComponent.query = query;
3484
3484
  wsComponent.resourceName = void 0;
3485
3485
  }
@@ -6886,9 +6886,9 @@ var require_dist = __commonJS({
6886
6886
  });
6887
6887
 
6888
6888
  // ../argent-mcp/src/mcp-server.ts
6889
- import { appendFile, mkdir as mkdir2 } from "node:fs/promises";
6889
+ import { appendFile, mkdir as mkdir3 } from "node:fs/promises";
6890
6890
  import { dirname } from "node:path";
6891
- import { homedir as homedir2 } from "node:os";
6891
+ import { homedir as homedir3 } from "node:os";
6892
6892
 
6893
6893
  // ../../node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
6894
6894
  import process3 from "node:process";
@@ -7133,10 +7133,10 @@ function mergeDefs(...defs) {
7133
7133
  function cloneDef(schema) {
7134
7134
  return mergeDefs(schema._zod.def);
7135
7135
  }
7136
- function getElementAtPath(obj, path2) {
7137
- if (!path2)
7136
+ function getElementAtPath(obj, path3) {
7137
+ if (!path3)
7138
7138
  return obj;
7139
- return path2.reduce((acc, key) => acc?.[key], obj);
7139
+ return path3.reduce((acc, key) => acc?.[key], obj);
7140
7140
  }
7141
7141
  function promiseAllObject(promisesObj) {
7142
7142
  const keys = Object.keys(promisesObj);
@@ -7545,11 +7545,11 @@ function explicitlyAborted(x, startIndex = 0) {
7545
7545
  }
7546
7546
  return false;
7547
7547
  }
7548
- function prefixIssues(path2, issues) {
7548
+ function prefixIssues(path3, issues) {
7549
7549
  return issues.map((iss) => {
7550
7550
  var _a3;
7551
7551
  (_a3 = iss).path ?? (_a3.path = []);
7552
- iss.path.unshift(path2);
7552
+ iss.path.unshift(path3);
7553
7553
  return iss;
7554
7554
  });
7555
7555
  }
@@ -7696,16 +7696,16 @@ function flattenError(error2, mapper = (issue2) => issue2.message) {
7696
7696
  }
7697
7697
  function formatError(error2, mapper = (issue2) => issue2.message) {
7698
7698
  const fieldErrors = { _errors: [] };
7699
- const processError = (error3, path2 = []) => {
7699
+ const processError = (error3, path3 = []) => {
7700
7700
  for (const issue2 of error3.issues) {
7701
7701
  if (issue2.code === "invalid_union" && issue2.errors.length) {
7702
- issue2.errors.map((issues) => processError({ issues }, [...path2, ...issue2.path]));
7702
+ issue2.errors.map((issues) => processError({ issues }, [...path3, ...issue2.path]));
7703
7703
  } else if (issue2.code === "invalid_key") {
7704
- processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
7704
+ processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
7705
7705
  } else if (issue2.code === "invalid_element") {
7706
- processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
7706
+ processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
7707
7707
  } else {
7708
- const fullpath = [...path2, ...issue2.path];
7708
+ const fullpath = [...path3, ...issue2.path];
7709
7709
  if (fullpath.length === 0) {
7710
7710
  fieldErrors._errors.push(mapper(issue2));
7711
7711
  } else {
@@ -15457,17 +15457,30 @@ import * as path from "node:path";
15457
15457
  import * as readline from "node:readline";
15458
15458
  import { homedir } from "node:os";
15459
15459
  import { spawn } from "node:child_process";
15460
- import { mkdir, writeFile, readFile, unlink } from "node:fs/promises";
15460
+ import { randomBytes } from "node:crypto";
15461
+ import { mkdir, writeFile, readFile, unlink, rename, chmod } from "node:fs/promises";
15461
15462
  var STATE_DIR = path.join(homedir(), ".argent");
15462
15463
  var STATE_FILE = path.join(STATE_DIR, "tool-server.json");
15463
15464
  var LOG_FILE = path.join(STATE_DIR, "tool-server.log");
15464
- function buildToolsServerEnv(paths, port, baseEnv = process.env) {
15465
- return {
15465
+ var AUTH_TOKEN_BYTES = 32;
15466
+ var AUTH_TOKEN_ENV = "ARGENT_AUTH_TOKEN";
15467
+ var AUTOSPAWN_IDLE_TIMEOUT_MINUTES = 30;
15468
+ function buildToolsServerEnv(paths, port, baseEnv = process.env, options = {}) {
15469
+ const env = {
15466
15470
  ...baseEnv,
15467
- PORT: String(port),
15471
+ ARGENT_PORT: String(port),
15468
15472
  ARGENT_SIMULATOR_SERVER_DIR: paths.simulatorServerDir,
15469
15473
  ARGENT_NATIVE_DEVTOOLS_DIR: paths.nativeDevtoolsDir
15470
15474
  };
15475
+ if (options.host !== void 0) env.ARGENT_HOST = options.host;
15476
+ if (options.idleTimeoutMinutes !== void 0) {
15477
+ env.ARGENT_IDLE_TIMEOUT_MINUTES = String(options.idleTimeoutMinutes);
15478
+ }
15479
+ if (options.token) env[AUTH_TOKEN_ENV] = options.token;
15480
+ return env;
15481
+ }
15482
+ function generateToken() {
15483
+ return randomBytes(AUTH_TOKEN_BYTES).toString("hex");
15471
15484
  }
15472
15485
  function findFreePort() {
15473
15486
  return new Promise((resolve, reject) => {
@@ -15495,12 +15508,25 @@ function isProcessAlive(pid) {
15495
15508
  return false;
15496
15509
  }
15497
15510
  }
15498
- async function isHealthy(port) {
15511
+ function healthCheckHost(host) {
15512
+ if (host === "0.0.0.0" || host === "") return "127.0.0.1";
15513
+ if (host === "::" || host === "::0") return "::1";
15514
+ return host;
15515
+ }
15516
+ function formatUrl(host, port) {
15517
+ const h = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
15518
+ return `http://${h}:${port}`;
15519
+ }
15520
+ function authHeaders(token) {
15521
+ return token ? { Authorization: `Bearer ${token}` } : {};
15522
+ }
15523
+ async function isToolsServerHealthy(port, host = "127.0.0.1", timeoutMs = 2e3, token) {
15499
15524
  const controller = new AbortController();
15500
- const timer = setTimeout(() => controller.abort(), 2e3);
15525
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
15501
15526
  try {
15502
- const res = await fetch(`http://127.0.0.1:${port}/tools`, {
15503
- signal: controller.signal
15527
+ const res = await fetch(`${formatUrl(healthCheckHost(host), port)}/tools`, {
15528
+ signal: controller.signal,
15529
+ headers: authHeaders(token)
15504
15530
  });
15505
15531
  return res.ok;
15506
15532
  } catch {
@@ -15509,7 +15535,7 @@ async function isHealthy(port) {
15509
15535
  clearTimeout(timer);
15510
15536
  }
15511
15537
  }
15512
- function spawnToolsServer(paths, port) {
15538
+ function spawnToolsServer(paths, port, options = {}) {
15513
15539
  return new Promise((resolve, reject) => {
15514
15540
  let logFd;
15515
15541
  try {
@@ -15521,7 +15547,7 @@ function spawnToolsServer(paths, port) {
15521
15547
  const child = spawn("node", [paths.bundlePath, "start"], {
15522
15548
  detached: true,
15523
15549
  stdio: ["ignore", "pipe", logFd],
15524
- env: buildToolsServerEnv(paths, port)
15550
+ env: buildToolsServerEnv(paths, port, process.env, options)
15525
15551
  });
15526
15552
  child.unref();
15527
15553
  const pid = child.pid;
@@ -15537,7 +15563,7 @@ function spawnToolsServer(paths, port) {
15537
15563
  };
15538
15564
  const rl = readline.createInterface({ input: child.stdout });
15539
15565
  rl.on("line", (line) => {
15540
- const match = line.match(/Tools server listening on http:\/\/127\.0\.0\.1:(\d+)/);
15566
+ const match = line.match(/Tools server listening on http:\/\/.+:(\d+)/);
15541
15567
  if (match) {
15542
15568
  const actualPort = parseInt(match[1], 10);
15543
15569
  rl.close();
@@ -15560,7 +15586,7 @@ function spawnToolsServer(paths, port) {
15560
15586
  rl.on("close", () => clearTimeout(timer));
15561
15587
  });
15562
15588
  }
15563
- async function readState() {
15589
+ async function readToolsServerState() {
15564
15590
  try {
15565
15591
  const raw = await readFile(STATE_FILE, "utf8");
15566
15592
  return JSON.parse(raw);
@@ -15568,41 +15594,113 @@ async function readState() {
15568
15594
  return null;
15569
15595
  }
15570
15596
  }
15571
- async function writeState(state) {
15597
+ async function writeToolsServerState(state) {
15572
15598
  await mkdir(STATE_DIR, { recursive: true });
15573
- await writeFile(STATE_FILE, JSON.stringify(state, null, 2) + "\n", "utf8");
15599
+ const tmp = `${STATE_FILE}.${process.pid}.tmp`;
15600
+ try {
15601
+ await writeFile(tmp, JSON.stringify(state, null, 2) + "\n", {
15602
+ encoding: "utf8",
15603
+ mode: 384
15604
+ });
15605
+ await chmod(tmp, 384);
15606
+ await rename(tmp, STATE_FILE);
15607
+ } catch (err) {
15608
+ await unlink(tmp).catch(() => {
15609
+ });
15610
+ throw err;
15611
+ }
15574
15612
  }
15575
- async function clearState() {
15613
+ async function clearToolsServerState() {
15576
15614
  try {
15577
15615
  await unlink(STATE_FILE);
15578
15616
  } catch {
15579
15617
  }
15580
15618
  }
15619
+ var readState = readToolsServerState;
15620
+ var writeState = writeToolsServerState;
15621
+ var clearState = clearToolsServerState;
15581
15622
  async function ensureToolsServer(paths) {
15582
15623
  const state = await readState();
15583
15624
  if (state) {
15584
15625
  const alive = isProcessAlive(state.pid);
15585
15626
  if (alive) {
15586
- const healthy = await isHealthy(state.port);
15627
+ const host = state.host ?? "127.0.0.1";
15628
+ const healthy = await isToolsServerHealthy(state.port, host, 2e3, state.token);
15587
15629
  if (healthy) {
15588
- return `http://127.0.0.1:${state.port}`;
15630
+ return {
15631
+ url: formatUrl(healthCheckHost(host), state.port),
15632
+ token: state.token ?? ""
15633
+ };
15589
15634
  }
15590
15635
  }
15591
15636
  await clearState();
15592
15637
  }
15638
+ const token = generateToken();
15593
15639
  const port = await findFreePort();
15594
- const { port: actualPort, pid } = await spawnToolsServer(paths, port);
15640
+ const { port: actualPort, pid } = await spawnToolsServer(paths, port, {
15641
+ token,
15642
+ idleTimeoutMinutes: AUTOSPAWN_IDLE_TIMEOUT_MINUTES
15643
+ });
15595
15644
  await writeState({
15596
15645
  port: actualPort,
15597
15646
  pid,
15598
15647
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
15599
- bundlePath: paths.bundlePath
15648
+ bundlePath: paths.bundlePath,
15649
+ host: "127.0.0.1",
15650
+ token
15600
15651
  });
15601
- return `http://127.0.0.1:${actualPort}`;
15652
+ return { url: formatUrl("127.0.0.1", actualPort), token };
15653
+ }
15654
+
15655
+ // ../argent-tools-client/src/link-config.ts
15656
+ import * as path2 from "node:path";
15657
+ import { homedir as homedir2 } from "node:os";
15658
+ import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile2, unlink as unlink2, chmod as chmod2 } from "node:fs/promises";
15659
+ var LINK_DIR = path2.join(homedir2(), ".argent");
15660
+ var LINK_FILE = path2.join(LINK_DIR, "link.json");
15661
+ async function readLinkConfig() {
15662
+ try {
15663
+ const raw = await readFile2(LINK_FILE, "utf8");
15664
+ const parsed = JSON.parse(raw);
15665
+ if (!parsed || typeof parsed.url !== "string" || typeof parsed.host !== "string" || typeof parsed.port !== "number" || typeof parsed.createdAt !== "string") {
15666
+ return null;
15667
+ }
15668
+ return {
15669
+ url: parsed.url,
15670
+ host: parsed.host,
15671
+ port: parsed.port,
15672
+ createdAt: parsed.createdAt,
15673
+ ...typeof parsed.token === "string" ? { token: parsed.token } : {}
15674
+ };
15675
+ } catch {
15676
+ return null;
15677
+ }
15678
+ }
15679
+ async function getResolvedToolsUrl() {
15680
+ const envUrl = process.env.ARGENT_TOOLS_URL;
15681
+ if (envUrl) {
15682
+ const link2 = await readLinkConfig();
15683
+ const envToken = process.env[AUTH_TOKEN_ENV];
15684
+ return {
15685
+ url: envUrl,
15686
+ source: "env",
15687
+ ...envToken ? { token: envToken } : {},
15688
+ ...link2 ? { shadowedLink: link2 } : {}
15689
+ };
15690
+ }
15691
+ const link = await readLinkConfig();
15692
+ if (link) {
15693
+ return { url: link.url, source: "link", ...link.token ? { token: link.token } : {} };
15694
+ }
15695
+ return { url: null, source: "none" };
15696
+ }
15697
+ async function isRemoteRouted() {
15698
+ const { url } = await getResolvedToolsUrl();
15699
+ return url !== null;
15602
15700
  }
15603
15701
 
15604
15702
  // ../argent-mcp/src/content.ts
15605
- import { readFile as readFile2 } from "node:fs/promises";
15703
+ import { readFile as readFile3 } from "node:fs/promises";
15606
15704
  var PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
15607
15705
  async function fetchPngBytes(url) {
15608
15706
  try {
@@ -15652,7 +15750,7 @@ function isScreenshotDiffResult(value) {
15652
15750
  async function screenshotDiffToMcpContent(result) {
15653
15751
  const blocks = [];
15654
15752
  if (typeof result.contextDiffPath === "string") {
15655
- const buf = await readFile2(result.contextDiffPath);
15753
+ const buf = await readFile3(result.contextDiffPath);
15656
15754
  blocks.push({
15657
15755
  type: "image",
15658
15756
  data: buf.toString("base64"),
@@ -15800,35 +15898,44 @@ async function fetchWithReconnect(getUrl, reconnect, config2) {
15800
15898
  }
15801
15899
  async function startMcpServer(options) {
15802
15900
  let TOOLS_URL;
15803
- if (process.env.ARGENT_TOOLS_URL) {
15804
- TOOLS_URL = process.env.ARGENT_TOOLS_URL;
15901
+ let AUTH_TOKEN;
15902
+ const resolved = await getResolvedToolsUrl();
15903
+ if (resolved.url) {
15904
+ TOOLS_URL = resolved.url;
15905
+ AUTH_TOKEN = resolved.token ?? "";
15805
15906
  } else {
15806
15907
  try {
15807
- TOOLS_URL = await ensureToolsServer(options.paths);
15908
+ const handle = await ensureToolsServer(options.paths);
15909
+ TOOLS_URL = handle.url;
15910
+ AUTH_TOKEN = handle.token;
15808
15911
  } catch (err) {
15809
15912
  process.stderr.write(`[argent] Failed to start tools server: ${err}
15810
15913
  `);
15811
15914
  process.exit(1);
15812
15915
  }
15813
15916
  }
15917
+ function authHeader() {
15918
+ return AUTH_TOKEN ? { Authorization: `Bearer ${AUTH_TOKEN}` } : {};
15919
+ }
15814
15920
  let reconnectPromise = null;
15815
15921
  async function reconnect() {
15816
- if (process.env.ARGENT_TOOLS_URL) return;
15922
+ if (await isRemoteRouted()) return;
15817
15923
  if (!reconnectPromise) {
15818
- reconnectPromise = ensureToolsServer(options.paths).then((url) => {
15819
- TOOLS_URL = url;
15924
+ reconnectPromise = ensureToolsServer(options.paths).then((handle) => {
15925
+ TOOLS_URL = handle.url;
15926
+ AUTH_TOKEN = handle.token;
15820
15927
  }).finally(() => {
15821
15928
  reconnectPromise = null;
15822
15929
  });
15823
15930
  }
15824
15931
  return reconnectPromise;
15825
15932
  }
15826
- const LOG_FILE2 = process.env.ARGENT_MCP_LOG ?? `${homedir2()}/.argent/mcp-calls.log`;
15933
+ const LOG_FILE2 = process.env.ARGENT_MCP_LOG ?? `${homedir3()}/.argent/mcp-calls.log`;
15827
15934
  let logDirReady = false;
15828
15935
  async function spyLog(entry) {
15829
15936
  try {
15830
15937
  if (!logDirReady) {
15831
- await mkdir2(dirname(LOG_FILE2), { recursive: true });
15938
+ await mkdir3(dirname(LOG_FILE2), { recursive: true });
15832
15939
  logDirReady = true;
15833
15940
  }
15834
15941
  await appendFile(LOG_FILE2, JSON.stringify(entry) + "\n");
@@ -15836,7 +15943,9 @@ async function startMcpServer(options) {
15836
15943
  }
15837
15944
  }
15838
15945
  async function fetchTools() {
15839
- const res = await fetchWithReconnect(() => `${TOOLS_URL}/tools`, reconnect);
15946
+ const res = await fetchWithReconnect(() => `${TOOLS_URL}/tools`, reconnect, {
15947
+ init: { headers: authHeader() }
15948
+ });
15840
15949
  const json = await res.json();
15841
15950
  return json.tools;
15842
15951
  }
@@ -15846,7 +15955,7 @@ async function startMcpServer(options) {
15846
15955
  const res = await fetchWithReconnect(() => `${TOOLS_URL}/tools/${name}`, reconnect, {
15847
15956
  init: {
15848
15957
  method: "POST",
15849
- headers: { "Content-Type": "application/json" },
15958
+ headers: { "Content-Type": "application/json", ...authHeader() },
15850
15959
  body: JSON.stringify(args ?? {})
15851
15960
  },
15852
15961
  fetchTimeoutMs: meta2?.longRunning ? null : FETCH_TIMEOUT_MS
@@ -15951,14 +16060,17 @@ async function startMcpServer(options) {
15951
16060
  }
15952
16061
  });
15953
16062
  await server.connect(new StdioServerTransport());
15954
- if (!process.env.ARGENT_TOOLS_URL) {
16063
+ if (!await isRemoteRouted()) {
15955
16064
  const HEALTH_INTERVAL_MS = 3e4;
15956
16065
  const healthInterval = setInterval(async () => {
15957
16066
  try {
15958
16067
  const controller = new AbortController();
15959
16068
  const timer = setTimeout(() => controller.abort(), 3e3);
15960
16069
  try {
15961
- const res = await fetch(`${TOOLS_URL}/tools`, { signal: controller.signal });
16070
+ const res = await fetch(`${TOOLS_URL}/tools`, {
16071
+ signal: controller.signal,
16072
+ headers: authHeader()
16073
+ });
15962
16074
  if (!res.ok) throw new Error(`health check returned ${res.status}`);
15963
16075
  } finally {
15964
16076
  clearTimeout(timer);
@@ -15973,7 +16085,7 @@ async function startMcpServer(options) {
15973
16085
  }
15974
16086
  if (process.env.ARGENT_AUTO_SHUTDOWN === "1") {
15975
16087
  process.stdin.on("close", () => {
15976
- fetch(`${TOOLS_URL}/shutdown`, { method: "POST" }).catch(() => {
16088
+ fetch(`${TOOLS_URL}/shutdown`, { method: "POST", headers: authHeader() }).catch(() => {
15977
16089
  });
15978
16090
  setTimeout(() => process.exit(0), 5e3);
15979
16091
  });