@frod.io/bridge 0.7.0 → 0.7.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.
Files changed (3) hide show
  1. package/README.md +5 -4
  2. package/dist/cli.js +54 -90
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -146,10 +146,11 @@ platform side (`apps/orchestration-api/src/services/session-doors.ts`):
146
146
  - below bridge `0.2.0` — connectors are never attached (an older bridge
147
147
  couldn't gate them, so it isn't offered the choice);
148
148
  - below `0.6.0` — no repository tools;
149
- - below `0.7.0` — the Inbox and orchestration tools run from a checkout of the
150
- platform as separate processes on your computer; at or above, they're
151
- served by the platform the same way the Knowledge Hub and the Library are,
152
- which is exactly why the package needs no checkout at all.
149
+ - the Inbox and orchestration tools are served by the platform to every
150
+ bridge, the same way the Knowledge Hub and the Library are — which is
151
+ exactly why the package needs no checkout at all (a bridge older than
152
+ 0.7.0 used to start them from a checkout; that path was removed in 0.7.1,
153
+ after the last such bridge was upgraded).
153
154
 
154
155
  The rule behind these lines: one is only ever raised in the same commit as
155
156
  the feature that needs it, and a computer running an older bridge is told, in
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // @frod.io/bridge 0.7.0 — built 2026-09-06T08:00:27.683Z. One file; do not edit: the source is apps/bridge/src in the platform repository.
2
+ // @frod.io/bridge 0.7.2. One file; do not edit: the source is apps/bridge/src in the platform repository.
3
3
  import { createRequire as __createRequire } from 'node:module';
4
4
  const require = __createRequire(import.meta.url);
5
5
  var __create = Object.create;
@@ -3708,7 +3708,7 @@ var require_websocket_server = __commonJS({
3708
3708
  import { execFileSync as execFileSync7 } from "node:child_process";
3709
3709
  import { existsSync as existsSync12, mkdirSync as mkdirSync9 } from "node:fs";
3710
3710
  import { homedir as homedir7, hostname as hostname2, platform as platform6 } from "node:os";
3711
- import { dirname as dirname5, join as join12, resolve as resolve6 } from "node:path";
3711
+ import { dirname as dirname5, join as join11, resolve as resolve6 } from "node:path";
3712
3712
  import { spawn as spawn6 } from "node:child_process";
3713
3713
 
3714
3714
  // src/config.ts
@@ -3732,7 +3732,6 @@ function migrateConfig(raw) {
3732
3732
  maxSessions: old.maxSessions,
3733
3733
  adapters: old.adapters ?? ["claude"],
3734
3734
  claudePath: old.claudePath,
3735
- platformCheckout: old.platformCheckout,
3736
3735
  tokenStore: old.tokenStore,
3737
3736
  keychainAccount: old.agentId,
3738
3737
  joinedAt: old.joinedAt,
@@ -4381,9 +4380,6 @@ var PermissionServer = class {
4381
4380
  // src/adapters/claude.ts
4382
4381
  var PRE_APPROVAL_TTL_MS = 24 * 60 * 6e4;
4383
4382
  var HOOK_MARGIN_MS = 12e4;
4384
- function needsCheckout(args) {
4385
- return args.some((a) => a.includes("{{platform_checkout}}"));
4386
- }
4387
4383
  var KILL_GRACE_MS = 5e3;
4388
4384
  var STDERR_TAIL_LINES = 10;
4389
4385
  function exitWords(code, stderrTail) {
@@ -4553,16 +4549,7 @@ var ClaudeAdapter = class {
4553
4549
  const raw = { mcpServers: {} };
4554
4550
  for (const [name, server] of Object.entries(s.mcp_servers)) {
4555
4551
  if (server.type === "stdio") {
4556
- if (needsCheckout(server.args) && !this.opts.platformCheckout) {
4557
- log.warn(`${name} needs a checkout of the platform on this computer and there is none \u2014 not started (the platform serves it from 0.7.0 on).`);
4558
- continue;
4559
- }
4560
- raw.mcpServers[name] = {
4561
- type: "stdio",
4562
- command: server.command,
4563
- args: server.args.map((a) => a.replace("{{platform_checkout}}", this.opts.platformCheckout ?? "")),
4564
- env: server.env
4565
- };
4552
+ raw.mcpServers[name] = { type: "stdio", command: server.command, args: server.args, env: server.env };
4566
4553
  } else {
4567
4554
  raw.mcpServers[name] = { type: "http", url: server.url, headers: server.headers };
4568
4555
  }
@@ -5002,24 +4989,19 @@ function approvalFor(serverName, policy, neverAsk) {
5002
4989
  }
5003
4990
  return { default_tools_approval_mode: "prompt", ...Object.keys(tools).length > 0 ? { tools } : {} };
5004
4991
  }
5005
- function mcpConfig(spec, opts = {}) {
4992
+ function mcpConfig(spec) {
5006
4993
  const policy = policyFromSpec(spec);
5007
4994
  const neverAsk = spec.permission_mode === "bypassPermissions";
5008
4995
  const servers = {};
5009
4996
  for (const [name, server] of Object.entries(spec.mcp_servers ?? {})) {
5010
- if (server.type === "stdio" && !opts.platformCheckout && server.args.some((a) => a.includes("{{platform_checkout}}"))) continue;
5011
- const transport = server.type === "stdio" ? {
5012
- command: server.command,
5013
- args: server.args.map((a) => opts.platformCheckout ? a.replace("{{platform_checkout}}", opts.platformCheckout) : a),
5014
- env: server.env
5015
- } : { url: server.url, http_headers: server.headers };
4997
+ const transport = server.type === "stdio" ? { command: server.command, args: server.args, env: server.env } : { url: server.url, http_headers: server.headers };
5016
4998
  servers[name] = { ...transport, ...approvalFor(name, policy, neverAsk) };
5017
4999
  }
5018
5000
  return servers;
5019
5001
  }
5020
- function threadStartParams(spec, cwd, opts = {}) {
5002
+ function threadStartParams(spec, cwd) {
5021
5003
  const { approvalPolicy, sandbox } = permissionFor(spec.permission_mode);
5022
- const servers = mcpConfig(spec, opts);
5004
+ const servers = mcpConfig(spec);
5023
5005
  const params = { cwd, approvalPolicy, sandbox };
5024
5006
  if (spec.model?.name) params.model = spec.model.name;
5025
5007
  if (spec.system_prompt) params.developerInstructions = spec.system_prompt;
@@ -5113,7 +5095,7 @@ var CodexAdapter = class _CodexAdapter {
5113
5095
  clientInfo: { name: "frod-bridge", version: "1" }
5114
5096
  });
5115
5097
  this.notify(live, "initialized", {});
5116
- const params = threadStartParams(spec, o.sessionDir, { platformCheckout: this.opts.platformCheckout });
5098
+ const params = threadStartParams(spec, o.sessionDir);
5117
5099
  const resumeId = spec.resume_session_id ?? null;
5118
5100
  const started = resumeId ? await this.request(live, "thread/resume", { ...params, threadId: resumeId }) : await this.request(live, "thread/start", params);
5119
5101
  const threadId = readThreadId(started);
@@ -5458,7 +5440,6 @@ function describeError(err) {
5458
5440
  // src/adapters/grok.ts
5459
5441
  import { spawn as spawn3, spawnSync as spawnSync2, execFileSync as execFileSync4 } from "node:child_process";
5460
5442
  import { existsSync as existsSync5, mkdirSync as mkdirSync4 } from "node:fs";
5461
- import { join as join7 } from "node:path";
5462
5443
 
5463
5444
  // src/adapters/grok-spec.ts
5464
5445
  import { isAbsolute as isAbsolute2, resolve as resolve2, sep as sep2, join as join6 } from "node:path";
@@ -5476,19 +5457,12 @@ function grokEnv(base) {
5476
5457
  for (const [k, v] of Object.entries(base)) if (v !== void 0) out[k] = v;
5477
5458
  return { ...out, GROK_SANDBOX: GROK_SANDBOX_PROFILE, GROK_DISABLE_AUTOUPDATER: "1" };
5478
5459
  }
5479
- function mcpServersFor(spec, opts = {}) {
5460
+ function mcpServersFor(spec) {
5480
5461
  const pairs = (o) => Object.entries(o ?? {}).map(([name, value]) => ({ name, value }));
5481
5462
  const out = [];
5482
5463
  for (const [name, server] of Object.entries(spec.mcp_servers ?? {})) {
5483
5464
  if (server.type === "stdio") {
5484
- if (!opts.platformCheckout && server.args.some((a) => a.includes("{{platform_checkout}}"))) continue;
5485
- let command = server.command;
5486
- let args = server.args.map((a) => opts.platformCheckout ? a.replace("{{platform_checkout}}", opts.platformCheckout) : a);
5487
- if (opts.tsxPath && command === "npx" && args[0] === "tsx") {
5488
- command = opts.tsxPath;
5489
- args = args.slice(1);
5490
- }
5491
- out.push({ name, command, args, env: pairs(server.env) });
5465
+ out.push({ name, command: server.command, args: server.args, env: pairs(server.env) });
5492
5466
  } else {
5493
5467
  out.push({ type: "http", name, url: server.url, headers: pairs(server.headers) });
5494
5468
  }
@@ -5803,12 +5777,6 @@ ${res.stderr ?? ""}`.trim();
5803
5777
  const c = _GrokAdapter.check(this.opts.grokPath);
5804
5778
  return c.loggedIn ? "subscription" : "api_key";
5805
5779
  }
5806
- /** The checkout's own `tsx`, so a door starts without `npx` needing to write to `~/.npm` under the sandbox. */
5807
- tsxPath() {
5808
- if (!this.opts.platformCheckout) return null;
5809
- const p = join7(this.opts.platformCheckout, "node_modules", ".bin", "tsx");
5810
- return existsSync5(p) ? p : null;
5811
- }
5812
5780
  async start(spec, o) {
5813
5781
  const live = {
5814
5782
  spec,
@@ -5848,11 +5816,7 @@ ${res.stderr ?? ""}`.trim();
5848
5816
  }
5849
5817
  throw new Error(`${err instanceof Error ? err.message : String(err)}${tail ? ` \u2014 it said: ${tail}` : ""}`);
5850
5818
  }
5851
- const tsx = this.tsxPath();
5852
- const mcpServers = mcpServersFor(spec, { platformCheckout: this.opts.platformCheckout, tsxPath: tsx });
5853
- if (!tsx && mcpServers.some((s) => "command" in s && s.command === "npx")) {
5854
- log.warn(`[${spec.session_id.slice(0, 8)}] grok: the checkout has no node_modules/.bin/tsx \u2014 the platform doors start with npx, which the sandbox may refuse (grok-spec.ts).`);
5855
- }
5819
+ const mcpServers = mcpServersFor(spec);
5856
5820
  const params = { cwd: o.sessionDir, mcpServers, _meta: sessionMeta(spec) };
5857
5821
  const resumeId = spec.resume_session_id ?? null;
5858
5822
  let resumed = false;
@@ -5880,8 +5844,7 @@ ${res.stderr ?? ""}`.trim();
5880
5844
  model: live.model,
5881
5845
  context_window: live.contextWindow,
5882
5846
  permission_mode: spec.permission_mode,
5883
- doors: mcpServers.length,
5884
- tsx: !!tsx
5847
+ doors: mcpServers.length
5885
5848
  });
5886
5849
  return { id: sessionId, cwd: o.sessionDir, credentialTier: this.credentialTier(), resumed };
5887
5850
  }
@@ -6147,7 +6110,7 @@ import { hostname, platform as platform2 } from "node:os";
6147
6110
  // src/repo.ts
6148
6111
  import { spawn as spawn4 } from "node:child_process";
6149
6112
  import { existsSync as existsSync6, mkdirSync as mkdirSync5, readdirSync, rmSync, statSync } from "node:fs";
6150
- import { join as join8, resolve as resolve3 } from "node:path";
6113
+ import { join as join7, resolve as resolve3 } from "node:path";
6151
6114
  var PLATFORM_COMMIT_ENV = {
6152
6115
  GIT_AUTHOR_NAME: "AgentOrchestrator",
6153
6116
  GIT_AUTHOR_EMAIL: "orchestrator@agentic",
@@ -6160,13 +6123,13 @@ var DEFAULT_TIMEOUT_MS = 12e4;
6160
6123
  var RUN_ID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6161
6124
  function runDirFor(workdir, runId) {
6162
6125
  if (runId === "." || runId === ".." || !RUN_ID_SHAPE.test(runId)) throw new Error(`The run id "${String(runId).slice(0, 20)}" is not a run id this computer will make a folder for`);
6163
- return join8(workdir, "runs", runId);
6126
+ return join7(workdir, "runs", runId);
6164
6127
  }
6165
6128
  var SLUG_SHAPE = /^[a-z0-9][a-z0-9-]{0,39}$/;
6166
6129
  function checkoutDirFor(sessionDir, slug) {
6167
6130
  if (!sessionDir || !resolve3(sessionDir).startsWith("/")) throw new Error("The session has no folder on this computer to check a repository out into");
6168
6131
  if (!SLUG_SHAPE.test(slug)) throw new Error(`"${String(slug).slice(0, 40)}" is not a repository name this computer will make a folder for`);
6169
- return join8(sessionDir, "repos", slug);
6132
+ return join7(sessionDir, "repos", slug);
6170
6133
  }
6171
6134
  function assertRepoUrl(gitUrl) {
6172
6135
  const fileOk = process.env.FROD_ALLOW_FILE_REPOS === "1";
@@ -6247,7 +6210,7 @@ async function canReach(gitUrl, timeoutMs = 3e4) {
6247
6210
  return { ok: false, detail: firstLine(r.stderr) || `git exited ${r.code}`, ms: r.ms };
6248
6211
  }
6249
6212
  async function originOf(dir) {
6250
- if (!existsSync6(join8(dir, ".git"))) return null;
6213
+ if (!existsSync6(join7(dir, ".git"))) return null;
6251
6214
  const r = await git(["-C", dir, "remote", "get-url", "origin"], void 0, { timeoutMs: 1e4 });
6252
6215
  return r.code === 0 ? r.stdout.trim() : null;
6253
6216
  }
@@ -6347,7 +6310,7 @@ async function commitAndPush(dir, repo, computerName, log2, opts = {}) {
6347
6310
  function pruneRunDirs(workdir, maxAgeMs = 7 * 24 * 60 * 6e4) {
6348
6311
  let removed = 0;
6349
6312
  for (const sub of ["runs"]) {
6350
- const root = join8(workdir, sub);
6313
+ const root = join7(workdir, sub);
6351
6314
  if (!existsSync6(root)) continue;
6352
6315
  for (const name of readdirSync(root)) {
6353
6316
  const p = resolve3(root, name);
@@ -6945,11 +6908,11 @@ function plainWords(message) {
6945
6908
  import { execFileSync as execFileSync6 } from "node:child_process";
6946
6909
  import { existsSync as existsSync10, mkdirSync as mkdirSync8, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2 } from "node:fs";
6947
6910
  import { homedir as homedir5, platform as platform4, userInfo } from "node:os";
6948
- import { dirname as dirname4, join as join10 } from "node:path";
6911
+ import { dirname as dirname4, join as join9 } from "node:path";
6949
6912
 
6950
6913
  // src/updater.ts
6951
6914
  import { existsSync as existsSync9, mkdirSync as mkdirSync7, rmSync as rmSync2, readdirSync as readdirSync2, renameSync, symlinkSync, readFileSync as readFileSync5, lstatSync, realpathSync as realpathSync3, readlinkSync, cpSync, writeFileSync as writeFileSync5, statSync as statSync2 } from "node:fs";
6952
- import { join as join9, dirname as dirname3, basename, resolve as resolve5, sep as sep4 } from "node:path";
6915
+ import { join as join8, dirname as dirname3, basename, resolve as resolve5, sep as sep4 } from "node:path";
6953
6916
  import { platform as platform3 } from "node:os";
6954
6917
  import { execFileSync as execFileSync5 } from "node:child_process";
6955
6918
  import { fileURLToPath } from "node:url";
@@ -7003,9 +6966,9 @@ function judgeRelease(release, tarball, keys = RELEASE_PUBLIC_KEYS) {
7003
6966
  }
7004
6967
 
7005
6968
  // src/updater.ts
7006
- var BRIDGE_DIR = join9(FROD_DIR, "bridge");
7007
- var VERSIONS_DIR = join9(BRIDGE_DIR, "versions");
7008
- var CURRENT_LINK = join9(BRIDGE_DIR, "current");
6969
+ var BRIDGE_DIR = join8(FROD_DIR, "bridge");
6970
+ var VERSIONS_DIR = join8(BRIDGE_DIR, "versions");
6971
+ var CURRENT_LINK = join8(BRIDGE_DIR, "current");
7009
6972
  var RESTART_EXIT_CODE = 75;
7010
6973
  var KEEP_VERSIONS = 2;
7011
6974
  var MAX_TARBALL_BYTES = 50 * 1024 * 1024;
@@ -7059,7 +7022,7 @@ function currentVersion(currentLink = CURRENT_LINK) {
7059
7022
  }
7060
7023
  function packageVersionAt(root) {
7061
7024
  try {
7062
- const pkg = JSON.parse(readFileSync5(join9(root, "package.json"), "utf8"));
7025
+ const pkg = JSON.parse(readFileSync5(join8(root, "package.json"), "utf8"));
7063
7026
  return pkg.name && pkg.version ? { name: pkg.name, version: pkg.version } : null;
7064
7027
  } catch {
7065
7028
  return null;
@@ -7069,17 +7032,17 @@ function stagePackage(loc, expectedVersion, versionsDir = VERSIONS_DIR) {
7069
7032
  const pkg = packageVersionAt(loc.packageRoot);
7070
7033
  if (!pkg || pkg.name !== "@frod.io/bridge") return { ok: false, reason: `${loc.packageRoot} is not the @frod.io/bridge package` };
7071
7034
  if (pkg.version !== expectedVersion) return { ok: false, reason: `the package at ${loc.packageRoot} is ${pkg.version}, this bridge is ${expectedVersion}` };
7072
- const dest = join9(versionsDir, pkg.version);
7035
+ const dest = join8(versionsDir, pkg.version);
7073
7036
  if (loc.installedVersion === pkg.version && safeReal(loc.packageRoot) === safeReal(dest)) return { ok: true, dir: dest, copied: false };
7074
- if (!existsSync9(join9(loc.packageRoot, "dist", "cli.js"))) {
7037
+ if (!existsSync9(join8(loc.packageRoot, "dist", "cli.js"))) {
7075
7038
  return { ok: false, reason: `${loc.packageRoot} has no dist/cli.js \u2014 run \`npm run build -w @frod.io/bridge\` in the checkout, or run the published package: npx @frod.io/bridge@latest install` };
7076
7039
  }
7077
- if (existsSync9(join9(dest, "dist", "cli.js"))) return { ok: true, dir: dest, copied: false };
7040
+ if (existsSync9(join8(dest, "dist", "cli.js"))) return { ok: true, dir: dest, copied: false };
7078
7041
  const partial = `${dest}.partial`;
7079
7042
  rmSync2(partial, { recursive: true, force: true });
7080
7043
  mkdirSync7(partial, { recursive: true, mode: 493 });
7081
- for (const f of ["package.json", "README.md"]) if (existsSync9(join9(loc.packageRoot, f))) cpSync(join9(loc.packageRoot, f), join9(partial, f));
7082
- cpSync(join9(loc.packageRoot, "dist"), join9(partial, "dist"), { recursive: true });
7044
+ for (const f of ["package.json", "README.md"]) if (existsSync9(join8(loc.packageRoot, f))) cpSync(join8(loc.packageRoot, f), join8(partial, f));
7045
+ cpSync(join8(loc.packageRoot, "dist"), join8(partial, "dist"), { recursive: true });
7083
7046
  rmSync2(dest, { recursive: true, force: true });
7084
7047
  renameSync(partial, dest);
7085
7048
  return { ok: true, dir: dest, copied: true };
@@ -7107,7 +7070,7 @@ async function stageRelease(release, opts = {}) {
7107
7070
  const versionsDir = opts.versionsDir ?? VERSIONS_DIR;
7108
7071
  const v = release.current;
7109
7072
  if (!SEMVER.test(v)) return { ok: false, reason: `"${v}" is not a version` };
7110
- const dest = join9(versionsDir, v);
7073
+ const dest = join8(versionsDir, v);
7111
7074
  const partial = `${dest}.partial`;
7112
7075
  const refuse = (reason) => {
7113
7076
  rmSync2(partial, { recursive: true, force: true });
@@ -7127,18 +7090,18 @@ async function stageRelease(release, opts = {}) {
7127
7090
  try {
7128
7091
  rmSync2(partial, { recursive: true, force: true });
7129
7092
  mkdirSync7(partial, { recursive: true, mode: 493 });
7130
- const tgz = join9(partial, "bridge.tgz");
7093
+ const tgz = join8(partial, "bridge.tgz");
7131
7094
  writeFileSync5(tgz, bytes, { mode: 420 });
7132
7095
  (opts.extract ?? extractTarball)(tgz, partial);
7133
7096
  rmSync2(tgz, { force: true });
7134
7097
  } catch (err) {
7135
7098
  return refuse(`the tarball could not be extracted (${err instanceof Error ? err.message : String(err)})`);
7136
7099
  }
7137
- const root = join9(partial, "package");
7100
+ const root = join8(partial, "package");
7138
7101
  const pkg = packageVersionAt(root);
7139
7102
  if (!pkg || pkg.name !== "@frod.io/bridge") return refuse("the tarball is not the @frod.io/bridge package");
7140
7103
  if (pkg.version !== v) return refuse(`the tarball says it is ${pkg.version}, not ${v}`);
7141
- if (!existsSync9(join9(root, "dist", "cli.js"))) return refuse("the tarball has no dist/cli.js");
7104
+ if (!existsSync9(join8(root, "dist", "cli.js"))) return refuse("the tarball has no dist/cli.js");
7142
7105
  try {
7143
7106
  rmSync2(dest, { recursive: true, force: true });
7144
7107
  renameSync(root, dest);
@@ -7149,8 +7112,8 @@ async function stageRelease(release, opts = {}) {
7149
7112
  return { ok: true, dir: dest };
7150
7113
  }
7151
7114
  function swapCurrent(version, versionsDir = VERSIONS_DIR, currentLink = CURRENT_LINK) {
7152
- const target = join9(versionsDir, version);
7153
- if (!existsSync9(join9(target, "dist", "cli.js"))) throw new Error(`${target} has no dist/cli.js \u2014 not swapping`);
7115
+ const target = join8(versionsDir, version);
7116
+ if (!existsSync9(join8(target, "dist", "cli.js"))) throw new Error(`${target} has no dist/cli.js \u2014 not swapping`);
7154
7117
  mkdirSync7(dirname3(currentLink), { recursive: true });
7155
7118
  const tmp = `${currentLink}.tmp-${process.pid}`;
7156
7119
  rmSync2(tmp, { recursive: true, force: true });
@@ -7172,7 +7135,7 @@ function pruneVersions(opts = {}) {
7172
7135
  for (const v of versions.slice(0, opts.keep ?? KEEP_VERSIONS)) keepSet.add(v);
7173
7136
  const removed = [];
7174
7137
  for (const e of entries) {
7175
- const p = join9(versionsDir, e);
7138
+ const p = join8(versionsDir, e);
7176
7139
  if (SEMVER.test(e)) {
7177
7140
  if (keepSet.has(e)) continue;
7178
7141
  } else if (/\.partial$/.test(e)) {
@@ -7321,13 +7284,13 @@ var LAUNCHD_LABEL = "io.frod.bridge";
7321
7284
  var SYSTEMD_UNIT = "frod-bridge.service";
7322
7285
  var WINDOWS_TASK = "Frod.io bridge";
7323
7286
  function plistPath(home = homedir5()) {
7324
- return join10(home, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
7287
+ return join9(home, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
7325
7288
  }
7326
7289
  function xml(s) {
7327
7290
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
7328
7291
  }
7329
7292
  function defaultTarget() {
7330
- return { node: process.execPath, cli: join10(CURRENT_LINK, "dist", "cli.js"), workdir: BRIDGE_DIR, home: homedir5(), frodDir: FROD_DIR, logPath: LOG_PATH };
7293
+ return { node: process.execPath, cli: join9(CURRENT_LINK, "dist", "cli.js"), workdir: BRIDGE_DIR, home: homedir5(), frodDir: FROD_DIR, logPath: LOG_PATH };
7331
7294
  }
7332
7295
  var realIo = {
7333
7296
  exec: (cmd, args) => {
@@ -7341,7 +7304,7 @@ var realIo = {
7341
7304
  }
7342
7305
  };
7343
7306
  function renderLaunchdPlist(t) {
7344
- const pathEnv = [dirname4(t.node), join10(t.home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].join(":");
7307
+ const pathEnv = [dirname4(t.node), join9(t.home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].join(":");
7345
7308
  return `<?xml version="1.0" encoding="UTF-8"?>
7346
7309
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
7347
7310
  <plist version="1.0">
@@ -7442,7 +7405,7 @@ function renderSystemdUnit(t) {
7442
7405
  `WorkingDirectory=${t.workdir}`,
7443
7406
  `Environment="HOME=${t.home}"`,
7444
7407
  `Environment="FROD_HOME=${t.frodDir}"`,
7445
- `Environment="PATH=${[dirname4(t.node), join10(t.home, ".local", "bin"), "/usr/local/bin", "/usr/bin", "/bin"].join(":")}"`,
7408
+ `Environment="PATH=${[dirname4(t.node), join9(t.home, ".local", "bin"), "/usr/local/bin", "/usr/bin", "/bin"].join(":")}"`,
7446
7409
  // A clean exit is a final goodbye (left, disconnected, refused): do not restart it — review H-2, the same rule as launchd's SuccessfulExit=false. The updater exits 75, which IS restarted.
7447
7410
  "Restart=on-failure",
7448
7411
  "RestartSec=10",
@@ -7455,7 +7418,7 @@ function renderSystemdUnit(t) {
7455
7418
  ].join("\n");
7456
7419
  }
7457
7420
  function systemdUnitPath(home = homedir5()) {
7458
- return join10(home, ".config", "systemd", "user", SYSTEMD_UNIT);
7421
+ return join9(home, ".config", "systemd", "user", SYSTEMD_UNIT);
7459
7422
  }
7460
7423
  function linuxManualPath(unitPath = systemdUnitPath()) {
7461
7424
  return [
@@ -7536,7 +7499,7 @@ function installStub(t = defaultTarget()) {
7536
7499
  import { execFile, spawn as spawn5 } from "node:child_process";
7537
7500
  import { existsSync as existsSync11, mkdtempSync, readFileSync as readFileSync6 } from "node:fs";
7538
7501
  import { homedir as homedir6, platform as platform5, tmpdir as tmpdir2 } from "node:os";
7539
- import { join as join11 } from "node:path";
7502
+ import { join as join10 } from "node:path";
7540
7503
  import { createInterface } from "node:readline";
7541
7504
  var SAMPLE_INTERVAL_MS = 30 * 6e4;
7542
7505
  var READER_TIMEOUT_MS = 25e3;
@@ -7571,7 +7534,7 @@ function readClaudeCredential(deps = {}) {
7571
7534
  });
7572
7535
  });
7573
7536
  }
7574
- const file = join11(home, ".claude", ".credentials.json");
7537
+ const file = join10(home, ".claude", ".credentials.json");
7575
7538
  if (!existsSync11(file)) return Promise.resolve({ problem: "Claude Code is not signed in on this computer" });
7576
7539
  try {
7577
7540
  return Promise.resolve(parse(readFileSync6(file, "utf8")));
@@ -7713,7 +7676,7 @@ function parseGrokBilling(result) {
7713
7676
  }
7714
7677
  async function readGrokCapacity(grokPath, deps = {}) {
7715
7678
  const ask = deps.ask ?? askChild;
7716
- const cwd = deps.cwd ?? mkdtempSync(join11(tmpdir2(), "frod-capacity-"));
7679
+ const cwd = deps.cwd ?? mkdtempSync(join10(tmpdir2(), "frod-capacity-"));
7717
7680
  const r = await ask({
7718
7681
  command: grokPath,
7719
7682
  args: ["agent", "--no-leader", "stdio"],
@@ -7816,13 +7779,13 @@ function samplesInWords(samples) {
7816
7779
  }
7817
7780
 
7818
7781
  // src/cli.ts
7819
- var VERSION = "0.7.0";
7782
+ var VERSION = "0.7.2";
7820
7783
  function arg(flag, argv) {
7821
7784
  const i = argv.indexOf(flag);
7822
7785
  return i >= 0 ? argv[i + 1] : void 0;
7823
7786
  }
7824
7787
  function findClaude() {
7825
- const candidates = [process.env.FROD_CLAUDE_PATH, join12(homedir7(), ".local", "bin", "claude"), "/opt/homebrew/bin/claude", "/usr/local/bin/claude"].filter((c) => !!c);
7788
+ const candidates = [process.env.FROD_CLAUDE_PATH, join11(homedir7(), ".local", "bin", "claude"), "/opt/homebrew/bin/claude", "/usr/local/bin/claude"].filter((c) => !!c);
7826
7789
  for (const c of candidates) if (existsSync12(c)) return c;
7827
7790
  try {
7828
7791
  return execFileSync7("/usr/bin/which", ["claude"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
@@ -7831,7 +7794,7 @@ function findClaude() {
7831
7794
  throw new Error("claude was not found. Install Claude Code and sign in (claude auth login), then try again.");
7832
7795
  }
7833
7796
  function findCodex() {
7834
- const candidates = [process.env.FROD_CODEX_PATH, join12(homedir7(), ".local", "bin", "codex"), "/opt/homebrew/bin/codex", "/usr/local/bin/codex"].filter((c) => !!c);
7797
+ const candidates = [process.env.FROD_CODEX_PATH, join11(homedir7(), ".local", "bin", "codex"), "/opt/homebrew/bin/codex", "/usr/local/bin/codex"].filter((c) => !!c);
7835
7798
  for (const c of candidates) if (existsSync12(c)) return c;
7836
7799
  try {
7837
7800
  const found = execFileSync7("/usr/bin/which", ["codex"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
@@ -7841,7 +7804,7 @@ function findCodex() {
7841
7804
  }
7842
7805
  }
7843
7806
  function findGrok() {
7844
- const candidates = [process.env.FROD_GROK_PATH, join12(homedir7(), ".local", "bin", "grok"), join12(homedir7(), ".grok", "bin", "grok"), "/opt/homebrew/bin/grok", "/usr/local/bin/grok"].filter((c) => !!c);
7807
+ const candidates = [process.env.FROD_GROK_PATH, join11(homedir7(), ".local", "bin", "grok"), join11(homedir7(), ".grok", "bin", "grok"), "/opt/homebrew/bin/grok", "/usr/local/bin/grok"].filter((c) => !!c);
7845
7808
  for (const c of candidates) if (existsSync12(c)) return c;
7846
7809
  try {
7847
7810
  const found = execFileSync7("/usr/bin/which", ["grok"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
@@ -7852,7 +7815,7 @@ function findGrok() {
7852
7815
  }
7853
7816
  function buildAdapters(cfg, permissions) {
7854
7817
  const adapters = {
7855
- claude: new ClaudeAdapter({ claudePath: cfg.claudePath, platformCheckout: cfg.platformCheckout, permissions })
7818
+ claude: new ClaudeAdapter({ claudePath: cfg.claudePath, permissions })
7856
7819
  };
7857
7820
  const codexPath = cfg.codexPath ?? findCodex();
7858
7821
  let codexDetail = "not installed on this computer";
@@ -7861,7 +7824,7 @@ function buildAdapters(cfg, permissions) {
7861
7824
  const c = CodexAdapter.check(codexPath);
7862
7825
  codexDetail = `${c.version ?? "unknown version"} \u2014 ${c.detail}`;
7863
7826
  if (c.loggedIn) {
7864
- adapters.codex = new CodexAdapter({ codexPath, platformCheckout: cfg.platformCheckout });
7827
+ adapters.codex = new CodexAdapter({ codexPath });
7865
7828
  codexVersion = c.version;
7866
7829
  }
7867
7830
  }
@@ -7872,7 +7835,7 @@ function buildAdapters(cfg, permissions) {
7872
7835
  const g = GrokAdapter.check(grokPath);
7873
7836
  grokDetail = `${g.version ?? "unknown version"} \u2014 ${g.detail}`;
7874
7837
  if (g.loggedIn) {
7875
- adapters.grok = new GrokAdapter({ grokPath, platformCheckout: cfg.platformCheckout });
7838
+ adapters.grok = new GrokAdapter({ grokPath });
7876
7839
  grokVersion = g.version;
7877
7840
  }
7878
7841
  }
@@ -7899,7 +7862,7 @@ async function cmdJoin(argv) {
7899
7862
  if (refusal) throw new Error(refusal);
7900
7863
  const safeApi = assertSafeEndpoint(apiUrl);
7901
7864
  const name = arg("--name", argv) ?? defaultName();
7902
- const workdir = resolve6(arg("--workdir", argv) ?? join12(homedir7(), "frod-work"));
7865
+ const workdir = resolve6(arg("--workdir", argv) ?? join11(homedir7(), "frod-work"));
7903
7866
  const maxSessions = Math.max(1, Math.min(20, Number(arg("--max-sessions", argv) ?? 2)));
7904
7867
  const claudePath = findClaude();
7905
7868
  const check = ClaudeAdapter.check(claudePath);
@@ -8033,7 +7996,7 @@ async function cmdRun() {
8033
7996
  });
8034
7997
  if (platform6() === "win32") {
8035
7998
  try {
8036
- const child = spawn6(process.execPath, [join12(CURRENT_LINK, "dist", "cli.js"), "run"], { detached: true, stdio: "ignore", env: process.env });
7999
+ const child = spawn6(process.execPath, [join11(CURRENT_LINK, "dist", "cli.js"), "run"], { detached: true, stdio: "ignore", env: process.env });
8037
8000
  child.unref();
8038
8001
  log.info(`Windows: started ${to} detached (pid ${child.pid}).`);
8039
8002
  } catch (err) {
@@ -8166,7 +8129,8 @@ async function cmdLeave() {
8166
8129
  const r = uninstallWindows();
8167
8130
  if (r.removed) log.info("Removed the scheduled task.");
8168
8131
  }
8169
- log.info(`Left. ${CONFIG_PATH} is kept for reference; delete it if you like.`);
8132
+ writeConfig({ ...cfg, dormant: { reason: "left", at: (/* @__PURE__ */ new Date()).toISOString() } });
8133
+ log.info(`Left. ${CONFIG_PATH} is kept for reference and marked as left; a new join code starts over.`);
8170
8134
  }
8171
8135
  async function cmdInstall() {
8172
8136
  const cfg = readConfig();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frod.io/bridge",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "type": "module",
5
5
  "description": "Your computer is a desk: hosts a Frod.io agent's sessions with your own Claude Code, Codex or Grok Build. Installs and updates itself.",
6
6
  "license": "UNLICENSED",