tesseract-agent 0.11.1 → 0.11.3

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.
@@ -37920,6 +37920,9 @@ import { spawn as spawn2, execSync, execFileSync } from "node:child_process";
37920
37920
  function setBrowserExtensionProbe(fn) {
37921
37921
  browserExtensionConnected = fn;
37922
37922
  }
37923
+ function isTurnBearingFrame(type) {
37924
+ return type === "assistant" || type === "stream_event" || type === "user";
37925
+ }
37923
37926
  function findUserInstalledClaude(binName) {
37924
37927
  const home = homedir3();
37925
37928
  const abs = [
@@ -39980,50 +39983,72 @@ ${full.slice(-8e3)}`;
39980
39983
  let cappedOut = false;
39981
39984
  let narrated = false;
39982
39985
  const turn = live.turnSeq;
39983
- for (; ; ) {
39984
- if (live.rv !== rv) {
39985
- log(`[work] quiescence wait for ${live.sessionId} dropped \u2014 another run owns the session`);
39986
- this.clearWorkNarration(live, narrated);
39987
- return;
39988
- }
39989
- if (live.reaped) {
39990
- log(`[work] quiescence wait for ${live.sessionId} dropped \u2014 the session was torn down`);
39991
- this.clearWorkNarration(live, narrated);
39992
- return;
39993
- }
39994
- if (live.turnSeq !== turn) {
39995
- log(`[work] quiescence wait for ${live.sessionId} superseded by a new turn`);
39996
- this.clearWorkNarration(live, narrated);
39997
- return;
39998
- }
39999
- const v = live.ledger.verdict(Date.now());
40000
- if (v.quiescent)
40001
- break;
40002
- if (Date.now() - started2 >= QUIESCENCE_MAX_MS) {
40003
- cappedOut = true;
40004
- break;
40005
- }
40006
- if (!narrated && live.sessionId) {
40007
- narrated = true;
40008
- this.send({
40009
- type: "agent:narration",
40010
- sessionId: live.sessionId,
40011
- text: `Waiting for ${v.waiting.length} background ${v.waiting.length === 1 ? "task" : "tasks"} to finish\u2026`,
40012
- engineLabel: "Claude Code"
40013
- }, live.origin);
39986
+ live.gradePending = true;
39987
+ try {
39988
+ for (; ; ) {
39989
+ if (live.rv !== rv) {
39990
+ log(`[work] quiescence wait for ${live.sessionId} dropped \u2014 another run owns the session`);
39991
+ this.clearWorkNarration(live, narrated);
39992
+ return;
39993
+ }
39994
+ if (live.gone) {
39995
+ log(`[work] quiescence wait for ${live.sessionId} dropped \u2014 the session was torn down`);
39996
+ this.clearWorkNarration(live, narrated);
39997
+ return;
39998
+ }
39999
+ if (live.turnSeq !== turn) {
40000
+ log(`[work] quiescence wait for ${live.sessionId} superseded by a new turn`);
40001
+ this.clearWorkNarration(live, narrated);
40002
+ return;
40003
+ }
40004
+ const v = live.ledger.verdict(Date.now());
40005
+ if (v.quiescent)
40006
+ break;
40007
+ if (Date.now() - started2 >= QUIESCENCE_MAX_MS) {
40008
+ cappedOut = true;
40009
+ break;
40010
+ }
40011
+ if (!narrated && live.sessionId) {
40012
+ narrated = true;
40013
+ this.send({
40014
+ type: "agent:narration",
40015
+ sessionId: live.sessionId,
40016
+ text: `Waiting for ${v.waiting.length} background ${v.waiting.length === 1 ? "task" : "tasks"} to finish\u2026`,
40017
+ engineLabel: "Claude Code"
40018
+ }, live.origin);
40019
+ }
40020
+ await new Promise((res) => setTimeout(res, QUIESCENCE_POLL_MS));
40014
40021
  }
40015
- await new Promise((res) => setTimeout(res, QUIESCENCE_POLL_MS));
40022
+ this.clearWorkNarration(live, narrated);
40023
+ live.ledger.sweep(Date.now());
40024
+ const outstanding = live.ledger.summarize(Date.now());
40025
+ if (cappedOut)
40026
+ log(`[work] quiescence cap reached for ${live.sessionId}; grading anyway with: ${outstanding.join("; ") || "(nothing outstanding)"}`);
40027
+ else if (Date.now() - started2 > 1e3)
40028
+ log(`[work] ${live.sessionId} settled after ${Math.round((Date.now() - started2) / 1e3)}s \u2014 grading now`);
40029
+ if (!cappedOut && outstanding.length)
40030
+ log(`[work] grading with work still in flight: ${outstanding.join("; ")}`);
40031
+ await this.runVerification(live, finalText, didWork);
40032
+ } finally {
40033
+ live.gradePending = false;
40034
+ }
40035
+ }
40036
+ /**
40037
+ * Mark a turn the engine started on its own as busy — exactly what a send does.
40038
+ *
40039
+ * Bumping `turnSeq` is deliberate: a quiescence wait for the PREVIOUS turn must see itself
40040
+ * superseded, the same way it would by a turn we started, rather than grade the old final
40041
+ * text while the new turn runs.
40042
+ */
40043
+ adoptSelfStartedTurn(live) {
40044
+ log(`[turn] ${live.sessionId || "(pending)"} started a turn on its own \u2014 marking busy`);
40045
+ live.streaming = true;
40046
+ live.turnSeq++;
40047
+ live.lastActivity = Date.now();
40048
+ if (live.idleTimer) {
40049
+ clearTimeout(live.idleTimer);
40050
+ live.idleTimer = void 0;
40016
40051
  }
40017
- this.clearWorkNarration(live, narrated);
40018
- live.ledger.sweep(Date.now());
40019
- const outstanding = live.ledger.summarize(Date.now());
40020
- if (cappedOut)
40021
- log(`[work] quiescence cap reached for ${live.sessionId}; grading anyway with: ${outstanding.join("; ") || "(nothing outstanding)"}`);
40022
- else if (Date.now() - started2 > 1e3)
40023
- log(`[work] ${live.sessionId} settled after ${Math.round((Date.now() - started2) / 1e3)}s \u2014 grading now`);
40024
- if (!cappedOut && outstanding.length)
40025
- log(`[work] grading with work still in flight: ${outstanding.join("; ")}`);
40026
- await this.runVerification(live, finalText, didWork);
40027
40052
  }
40028
40053
  /** Take down the "waiting on background work" strip, if we put one up. */
40029
40054
  clearWorkNarration(live, narrated) {
@@ -42557,6 +42582,8 @@ ${unattendedReportDirective(params)}` : ""), live.sessionId) },
42557
42582
  }
42558
42583
  const sid = live.sessionId;
42559
42584
  const parentToolUseId = message.parent_tool_use_id ?? void 0;
42585
+ if (!live.streaming && !live.reaped && isTurnBearingFrame(message.type))
42586
+ this.adoptSelfStartedTurn(live);
42560
42587
  switch (message.type) {
42561
42588
  case "stream_event": {
42562
42589
  const event = message.event;
@@ -43314,6 +43341,7 @@ ${err2.stack}` : ""}` : String(err2);
43314
43341
  emit2({ type: "agent:status", sessionId: sid, status: "error" });
43315
43342
  }
43316
43343
  } finally {
43344
+ live.gone = true;
43317
43345
  if (live.idleTimer) {
43318
43346
  clearTimeout(live.idleTimer);
43319
43347
  live.idleTimer = void 0;
@@ -43522,6 +43550,7 @@ ${err2.stack}` : ""}` : String(err2);
43522
43550
  live.abort.abort();
43523
43551
  } catch {
43524
43552
  }
43553
+ live.gone = true;
43525
43554
  }
43526
43555
  /** M7 — stop ONE background task (vs interruptSession which aborts the whole
43527
43556
  * turn). Driver-only, same origin guard. No-op if the session/task is gone. */
@@ -44247,12 +44276,45 @@ ${lines.join("\n")}
44247
44276
  /** Close an idle session's Query (→ child process + IPC freed via runConsumer's
44248
44277
  * finally), keeping the on-disk session resumable. Deletes from the live map
44249
44278
  * FIRST so a racing sendMessage misses it and cleanly re-creates with resume. */
44279
+ /**
44280
+ * Is this session, whatever `streaming` says, still in the middle of something?
44281
+ *
44282
+ * `streaming` only tracks turns WE started. A CLI running its own background monitor
44283
+ * starts turns by itself when events land, and between two of those — or during a long
44284
+ * tool call inside one — the session reads as idle here while the engine is hard at work.
44285
+ * The memory monitor's moderate-pressure reclaim reaped exactly such a session, 60 seconds
44286
+ * "idle" with a batch barrier that had just reported "1 still running": the CLI carried on
44287
+ * for another 69 minutes, un-mapped and ungradeable. Work the ledger is awaiting, a job
44288
+ * that is running, a wait inside `await_jobs`, or a turn-end grade still waiting for
44289
+ * quiescence — any of these means the session is busy.
44290
+ */
44291
+ hasWorkInFlight(live) {
44292
+ if (live.gradePending)
44293
+ return true;
44294
+ if ((live.awaitDepth ?? 0) > 0)
44295
+ return true;
44296
+ try {
44297
+ if (live.ledger.verdict(Date.now()).waiting.length > 0)
44298
+ return true;
44299
+ } catch {
44300
+ }
44301
+ try {
44302
+ if (live.jobs?.list().some((j) => j.status === "running"))
44303
+ return true;
44304
+ } catch {
44305
+ }
44306
+ return false;
44307
+ }
44250
44308
  reapIdleSession(sessionId, force = false) {
44251
44309
  const live = this.liveSessions.get(sessionId);
44252
44310
  if (!live || live.streaming || live.reaped)
44253
44311
  return false;
44254
44312
  if (this.isHeld(live) && !force)
44255
44313
  return false;
44314
+ if (!force && this.hasWorkInFlight(live)) {
44315
+ log(`[reap] ${sessionId} looks idle but has work in flight \u2014 left alone`);
44316
+ return false;
44317
+ }
44256
44318
  log("Reaping idle session:", sessionId);
44257
44319
  live.reaped = true;
44258
44320
  this.emitLifecycle("SessionEnd", { sessionId, reason: force ? "forced" : "idle-reap" });
@@ -44301,6 +44363,10 @@ ${lines.join("\n")}
44301
44363
  let n = 0;
44302
44364
  for (const live of [...this.liveSessions.values()]) {
44303
44365
  if (!live.streaming && !live.reaped) {
44366
+ if (this.hasWorkInFlight(live)) {
44367
+ log(`[reap] critical reclaim spared ${live.sessionId}: work in flight`);
44368
+ continue;
44369
+ }
44304
44370
  if (this.reapIdleSession(live.sessionId, true))
44305
44371
  n++;
44306
44372
  }
@@ -44325,6 +44391,7 @@ ${lines.join("\n")}
44325
44391
  if (live.reaped)
44326
44392
  continue;
44327
44393
  live.reaped = true;
44394
+ live.gone = true;
44328
44395
  if (live.idleTimer) {
44329
44396
  clearTimeout(live.idleTimer);
44330
44397
  live.idleTimer = void 0;
@@ -79502,7 +79569,7 @@ function alphabet(letters) {
79502
79569
  };
79503
79570
  }
79504
79571
  // @__NO_SIDE_EFFECTS__
79505
- function join68(separator = "") {
79572
+ function join67(separator = "") {
79506
79573
  astr("join", separator);
79507
79574
  return {
79508
79575
  encode: (from) => {
@@ -79707,7 +79774,7 @@ var init_esm = __esm({
79707
79774
  convertRadix2,
79708
79775
  radix,
79709
79776
  radix2,
79710
- join: join68,
79777
+ join: join67,
79711
79778
  padding
79712
79779
  };
79713
79780
  }
@@ -81254,7 +81321,7 @@ var mesh_ca_exports = {};
81254
81321
  __export(mesh_ca_exports, {
81255
81322
  netCaCommand: () => netCaCommand
81256
81323
  });
81257
- import { chmodSync as chmodSync8, existsSync as existsSync48, mkdirSync as mkdirSync45, readFileSync as readFileSync53, writeFileSync as writeFileSync46 } from "node:fs";
81324
+ import { chmodSync as chmodSync8, existsSync as existsSync49, mkdirSync as mkdirSync45, readFileSync as readFileSync54, writeFileSync as writeFileSync46 } from "node:fs";
81258
81325
  import { join as join87, resolve as resolve13 } from "node:path";
81259
81326
  import { createInterface as createInterface2 } from "node:readline";
81260
81327
  function die(msg) {
@@ -81302,12 +81369,12 @@ function ensureDir(p) {
81302
81369
  async function openCa(dir2) {
81303
81370
  const gPath = join87(dir2, "genesis.bin");
81304
81371
  const sPath = join87(dir2, "ca.sealed");
81305
- if (!existsSync48(gPath) || !existsSync48(sPath)) {
81372
+ if (!existsSync49(gPath) || !existsSync49(sPath)) {
81306
81373
  die(`No mesh CA in ${dir2}. Run: tesseract-agent net init --org NAME --ca-dir ${dir2}`);
81307
81374
  }
81308
- const genesisBytes = new Uint8Array(readFileSync53(gPath));
81375
+ const genesisBytes = new Uint8Array(readFileSync54(gPath));
81309
81376
  const genesis = decodeGenesis(genesisBytes);
81310
- const sealed = decodeRecoveryBundle(new Uint8Array(readFileSync53(sPath)));
81377
+ const sealed = decodeRecoveryBundle(new Uint8Array(readFileSync54(sPath)));
81311
81378
  const pass = await readPassphrase("CA passphrase: ");
81312
81379
  let seed;
81313
81380
  try {
@@ -81323,7 +81390,7 @@ async function netCaCommand(rest, flags) {
81323
81390
  if (sub === "init") {
81324
81391
  const org = typeof flags.org === "string" ? flags.org.trim() : "";
81325
81392
  if (!org) die("Usage: tesseract-agent net init --org NAME [--ca-dir DIR]");
81326
- if (existsSync48(join87(caDir, "ca.sealed"))) {
81393
+ if (existsSync49(join87(caDir, "ca.sealed"))) {
81327
81394
  die(`A mesh CA already exists in ${caDir}. Refusing to overwrite it.`);
81328
81395
  }
81329
81396
  const pass = await readPassphrase("New CA passphrase: ");
@@ -81481,7 +81548,7 @@ var init_mesh_ca = __esm({
81481
81548
 
81482
81549
  // src/cli.ts
81483
81550
  init_base_paths();
81484
- import { existsSync as existsSync49, mkdtempSync as mkdtempSync4, readFileSync as readFileSync55, rmSync as rmSync13 } from "node:fs";
81551
+ import { existsSync as existsSync50, mkdtempSync as mkdtempSync4, readFileSync as readFileSync56, rmSync as rmSync13 } from "node:fs";
81485
81552
  import { createHash as createHash9 } from "node:crypto";
81486
81553
  import { execFileSync as execFileSync6, spawn as spawn14 } from "node:child_process";
81487
81554
  import { fileURLToPath as fileURLToPath8 } from "node:url";
@@ -109557,7 +109624,7 @@ async function startLocalServer(opts) {
109557
109624
 
109558
109625
  // ../../packages/host-core/dist/daemon-runtime.js
109559
109626
  init_base_paths();
109560
- import { readFileSync as readFileSync49, writeFileSync as writeFileSync40, existsSync as existsSync38, mkdirSync as mkdirSync41, unlinkSync as unlinkSync6, chmodSync as chmodSync7, statSync as statSync14 } from "node:fs";
109627
+ import { readFileSync as readFileSync49, writeFileSync as writeFileSync40, existsSync as existsSync39, mkdirSync as mkdirSync41, unlinkSync as unlinkSync6, chmodSync as chmodSync7, statSync as statSync14 } from "node:fs";
109561
109628
  import { execFileSync as execFileSync5 } from "node:child_process";
109562
109629
  import { join as join77 } from "node:path";
109563
109630
  import { randomBytes as randomBytes10 } from "node:crypto";
@@ -109819,6 +109886,7 @@ async function getPeerIdentityPub(opts) {
109819
109886
  // ../../packages/host-core/dist/mesh/node.js
109820
109887
  import { createSocket as createSocket2 } from "node:dgram";
109821
109888
  import { createRequire as createRequire4 } from "node:module";
109889
+ init_credentials();
109822
109890
 
109823
109891
  // ../../packages/host-core/dist/mesh/bundle-gossip.js
109824
109892
  var BundleOfferLedger = class {
@@ -109850,15 +109918,429 @@ var BundleOfferLedger = class {
109850
109918
  }
109851
109919
  };
109852
109920
 
109921
+ // ../../packages/host-core/dist/mesh/netd-engine.js
109922
+ init_dist6();
109923
+
109924
+ // ../../packages/host-core/dist/mesh/netd-client.js
109925
+ import { connect as connect2 } from "node:net";
109926
+ import { platform as platform3 } from "node:process";
109927
+ var NETD_WIRE_VERSION = 2;
109928
+ var MAX_LINE = 1024 * 1024;
109929
+ var TIMEOUT_MS3 = 15e3;
109930
+ function netdEndpoint() {
109931
+ if (platform3 === "win32")
109932
+ return "\\\\.\\pipe\\tesseract-netd";
109933
+ if (platform3 === "darwin")
109934
+ return "/var/run/tesseract-netd.sock";
109935
+ return "/run/tesseract-netd.sock";
109936
+ }
109937
+ function endpointOverride() {
109938
+ return process.env.CCB_NETD_SOCKET ?? null;
109939
+ }
109940
+ function fail2(code, detail) {
109941
+ return { ok: false, code, detail };
109942
+ }
109943
+ async function call(line) {
109944
+ const endpoint2 = endpointOverride() ?? netdEndpoint();
109945
+ return new Promise((resolve16) => {
109946
+ let settled = false;
109947
+ const done = (r) => {
109948
+ if (settled)
109949
+ return;
109950
+ settled = true;
109951
+ try {
109952
+ sock.destroy();
109953
+ } catch {
109954
+ }
109955
+ resolve16(r);
109956
+ };
109957
+ let sock;
109958
+ try {
109959
+ sock = connect2(endpoint2);
109960
+ } catch (e) {
109961
+ return resolve16(fail2("not-installed", e instanceof Error ? e.message : String(e)));
109962
+ }
109963
+ const timer2 = setTimeout(() => {
109964
+ done(fail2("timeout", `no answer from the network helper in ${TIMEOUT_MS3}ms`));
109965
+ }, TIMEOUT_MS3);
109966
+ timer2.unref?.();
109967
+ sock.setTimeout(TIMEOUT_MS3);
109968
+ sock.on("timeout", () => {
109969
+ clearTimeout(timer2);
109970
+ done(fail2("timeout", "socket idle"));
109971
+ });
109972
+ sock.on("error", (e) => {
109973
+ clearTimeout(timer2);
109974
+ const absent = e.code === "ENOENT" || e.code === "ECONNREFUSED";
109975
+ done(fail2(absent ? "not-installed" : "io", e.message));
109976
+ });
109977
+ let buf = "";
109978
+ sock.on("data", (chunk) => {
109979
+ buf += chunk.toString("utf8");
109980
+ if (buf.length > MAX_LINE) {
109981
+ clearTimeout(timer2);
109982
+ done(fail2("protocol", "the helper sent an oversized line"));
109983
+ return;
109984
+ }
109985
+ const nl = buf.indexOf("\n");
109986
+ if (nl === -1)
109987
+ return;
109988
+ clearTimeout(timer2);
109989
+ done({ ok: true, line: buf.slice(0, nl) });
109990
+ });
109991
+ sock.on("close", () => {
109992
+ clearTimeout(timer2);
109993
+ done(fail2("protocol", "the helper closed the connection without answering"));
109994
+ });
109995
+ sock.on("connect", () => {
109996
+ sock.write(`${line}
109997
+ `);
109998
+ });
109999
+ });
110000
+ }
110001
+ function parse3(line) {
110002
+ let body;
110003
+ try {
110004
+ body = JSON.parse(line);
110005
+ } catch {
110006
+ return fail2("protocol", "the helper sent something that is not JSON");
110007
+ }
110008
+ if (typeof body !== "object" || body === null) {
110009
+ return fail2("protocol", "the helper sent something that is not an object");
110010
+ }
110011
+ const o = body;
110012
+ if (o.ok === false) {
110013
+ const code = typeof o.code === "string" ? o.code : "refused";
110014
+ const detail = typeof o.detail === "string" ? o.detail : "the helper refused";
110015
+ return fail2(code, detail);
110016
+ }
110017
+ if (o.ok !== true)
110018
+ return fail2("protocol", "the helper did not say whether it succeeded");
110019
+ return { ok: true, body: o };
110020
+ }
110021
+ async function request(line) {
110022
+ const r = await call(line);
110023
+ return r.ok ? parse3(r.line) : r;
110024
+ }
110025
+ function hex4(b) {
110026
+ return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
110027
+ }
110028
+ async function netdHello() {
110029
+ const r = await request(JSON.stringify({ op: "hello", wireVersion: NETD_WIRE_VERSION }));
110030
+ if (!r.ok)
110031
+ return r;
110032
+ const v = r.body.wireVersion;
110033
+ if (typeof v !== "number")
110034
+ return fail2("protocol", "the helper did not report a wire version");
110035
+ if (v !== NETD_WIRE_VERSION) {
110036
+ return fail2("wire-version", `the helper speaks ${v}, this build speaks ${NETD_WIRE_VERSION}`);
110037
+ }
110038
+ return { ok: true, wireVersion: v };
110039
+ }
110040
+ async function netdStatus() {
110041
+ const r = await request(JSON.stringify({ op: "status" }));
110042
+ if (!r.ok)
110043
+ return r;
110044
+ const meshes = Array.isArray(r.body.meshes) ? r.body.meshes.filter((m) => typeof m === "string") : [];
110045
+ const iface = typeof r.body.interface === "string" ? r.body.interface : null;
110046
+ return { ok: true, meshes, interface: iface };
110047
+ }
110048
+ async function netdUp(args) {
110049
+ const r = await request(JSON.stringify({
110050
+ op: "up",
110051
+ meshId: args.meshId,
110052
+ genesis: hex4(args.genesis),
110053
+ cert: hex4(args.cert),
110054
+ fullTunnel: args.fullTunnel === true,
110055
+ ...args.staticPrivate ? { staticPrivate: hex4(args.staticPrivate) } : {},
110056
+ ...args.bundle ? { bundle: hex4(args.bundle) } : {}
110057
+ }));
110058
+ return r.ok ? { ok: true } : r;
110059
+ }
110060
+ async function netdHint(args) {
110061
+ const r = await request(JSON.stringify({
110062
+ op: "hint",
110063
+ meshId: args.meshId,
110064
+ staticPub: hex4(args.staticPub),
110065
+ endpoints: args.endpoints,
110066
+ relayed: args.relayed === true
110067
+ }));
110068
+ return r.ok ? { ok: true } : r;
110069
+ }
110070
+ async function netdBundle(meshId, bundle) {
110071
+ const r = await request(JSON.stringify({ op: "bundle", meshId, bundle: hex4(bundle) }));
110072
+ return r.ok ? { ok: true } : r;
110073
+ }
110074
+ async function netdSetExitNode(meshId, staticPub) {
110075
+ const r = await request(JSON.stringify({
110076
+ op: "setExitNode",
110077
+ meshId,
110078
+ staticPub: staticPub ? hex4(staticPub) : null
110079
+ }));
110080
+ return r.ok ? { ok: true } : r;
110081
+ }
110082
+ async function netdNodeStatus(meshId) {
110083
+ const r = await request(JSON.stringify({ op: "nodeStatus", meshId }));
110084
+ if (!r.ok)
110085
+ return r;
110086
+ const b = r.body;
110087
+ const num2 = (k) => typeof b[k] === "number" ? b[k] : 0;
110088
+ const strs = (k) => Array.isArray(b[k]) ? b[k].filter((x) => typeof x === "string") : [];
110089
+ if (b.node !== true)
110090
+ return fail2("protocol", "the helper did not answer with a node status");
110091
+ const pubHex = typeof b.localStaticPub === "string" ? b.localStaticPub : "";
110092
+ return {
110093
+ ok: true,
110094
+ node: {
110095
+ running: b.running === true,
110096
+ port: num2("port"),
110097
+ peers: num2("peers"),
110098
+ established: strs("established"),
110099
+ relayed: strs("relayed"),
110100
+ policyEpoch: num2("policyEpoch"),
110101
+ revoked: b.revoked === true,
110102
+ lastError: typeof b.lastError === "string" ? b.lastError : null,
110103
+ localStaticPub: Uint8Array.from(Buffer.from(pubHex, "hex")),
110104
+ meter: {
110105
+ txDirectBytes: num2("txDirectBytes"),
110106
+ txDirectPackets: num2("txDirectPackets"),
110107
+ txRelayedBytes: num2("txRelayedBytes"),
110108
+ txRelayedPackets: num2("txRelayedPackets"),
110109
+ rxDirectBytes: num2("rxDirectBytes"),
110110
+ rxDirectPackets: num2("rxDirectPackets"),
110111
+ rxRelayedBytes: num2("rxRelayedBytes"),
110112
+ rxRelayedPackets: num2("rxRelayedPackets"),
110113
+ forwardedBytes: num2("forwardedBytes"),
110114
+ forwardedPackets: num2("forwardedPackets")
110115
+ }
110116
+ }
110117
+ };
110118
+ }
110119
+ async function netdDown(meshId) {
110120
+ const r = await request(JSON.stringify({ op: "down", meshId }));
110121
+ return r.ok ? { ok: true } : r;
110122
+ }
110123
+ async function netdAvailable() {
110124
+ return (await netdHello()).ok;
110125
+ }
110126
+
110127
+ // ../../packages/host-core/dist/mesh/netd-engine.js
110128
+ var NETD_POLL_MS = 1e3;
110129
+ var NetdEngine = class {
110130
+ #meshDir;
110131
+ #meshId;
110132
+ #localStaticPub;
110133
+ #hooks;
110134
+ #snapshot = null;
110135
+ #pendingEndpoints = /* @__PURE__ */ new Map();
110136
+ #events = [];
110137
+ #seenEstablished = /* @__PURE__ */ new Set();
110138
+ #seenRelayed = /* @__PURE__ */ new Set();
110139
+ #timer = null;
110140
+ #announced = false;
110141
+ #lastError = null;
110142
+ #stopped = false;
110143
+ constructor(meshDir2, cert, hooks2) {
110144
+ this.#meshDir = meshDir2;
110145
+ this.#hooks = hooks2;
110146
+ const d = decodeCert(cert).details;
110147
+ this.#meshId = hex3(d.genesisFp);
110148
+ this.#localStaticPub = Buffer.from(d.staticPub);
110149
+ this.#timer = setInterval(() => void this.#poll(), NETD_POLL_MS);
110150
+ this.#timer.unref?.();
110151
+ void this.#poll();
110152
+ }
110153
+ /** Read netd's view now rather than waiting for the timer. Exposed for tests and for
110154
+ * the moment right after `up` so status does not lag a full interval. */
110155
+ async refresh() {
110156
+ await this.#poll();
110157
+ }
110158
+ async #poll() {
110159
+ if (this.#stopped)
110160
+ return;
110161
+ const r = await netdNodeStatus(this.#meshDir);
110162
+ if (!r.ok) {
110163
+ if (r.code !== "not-running" && r.code !== "not-installed") {
110164
+ this.#lastError = `netd: ${r.detail}`;
110165
+ }
110166
+ return;
110167
+ }
110168
+ const s = r.node;
110169
+ const prev = this.#snapshot;
110170
+ this.#snapshot = s;
110171
+ if (s.lastError)
110172
+ this.#lastError = s.lastError;
110173
+ for (const k of s.established) {
110174
+ if (!this.#seenEstablished.has(k)) {
110175
+ this.#seenEstablished.add(k);
110176
+ this.#events.push({ kind: "peerEstablished", staticPub: Buffer.from(k, "hex") });
110177
+ }
110178
+ }
110179
+ const relayedNow = new Set(s.relayed);
110180
+ for (const k of relayedNow) {
110181
+ if (!this.#seenRelayed.has(k)) {
110182
+ this.#seenRelayed.add(k);
110183
+ this.#events.push({ kind: "pathChanged", staticPub: Buffer.from(k, "hex"), detail: "relay" });
110184
+ }
110185
+ }
110186
+ for (const k of [...this.#seenRelayed]) {
110187
+ if (!relayedNow.has(k)) {
110188
+ this.#seenRelayed.delete(k);
110189
+ this.#events.push({ kind: "pathChanged", staticPub: Buffer.from(k, "hex"), detail: "direct" });
110190
+ }
110191
+ }
110192
+ if (s.running && s.port > 0 && !this.#announced) {
110193
+ this.#announced = true;
110194
+ this.#hooks.log?.(`mesh: node running in the network helper on UDP port ${s.port}`);
110195
+ this.#hooks.onUp(s.port);
110196
+ }
110197
+ void prev;
110198
+ }
110199
+ // ── MeshEngineNative ────────────────────────────────────────────────────────────
110200
+ setBundle(b, _nowMs) {
110201
+ void netdBundle(this.#meshDir, b).then((r) => {
110202
+ if (!r.ok)
110203
+ this.#lastError = `bundle: ${r.detail}`;
110204
+ });
110205
+ }
110206
+ addPeer(staticPub, _overlayV4, _overlayV6) {
110207
+ const k = staticPub.toString("hex");
110208
+ if (!this.#pendingEndpoints.has(k))
110209
+ this.#pendingEndpoints.set(k, { endpoints: [], relayed: false });
110210
+ }
110211
+ addPath(staticPub, endpoint2, relayed) {
110212
+ const k = staticPub.toString("hex");
110213
+ const p = this.#pendingEndpoints.get(k) ?? { endpoints: [], relayed: false };
110214
+ if (!p.endpoints.includes(endpoint2))
110215
+ p.endpoints.push(endpoint2);
110216
+ p.relayed = p.relayed || relayed;
110217
+ this.#pendingEndpoints.set(k, p);
110218
+ }
110219
+ #flushHint(staticPub) {
110220
+ const k = staticPub.toString("hex");
110221
+ const p = this.#pendingEndpoints.get(k) ?? { endpoints: [], relayed: false };
110222
+ this.#pendingEndpoints.delete(k);
110223
+ void netdHint({ meshId: this.#meshDir, staticPub, endpoints: p.endpoints, relayed: p.relayed }).then((r) => {
110224
+ if (!r.ok)
110225
+ this.#lastError = `hint: ${r.detail}`;
110226
+ });
110227
+ }
110228
+ startHandshake(staticPub, _nowMs) {
110229
+ this.#flushHint(staticPub);
110230
+ }
110231
+ ensureHandshake(staticPub, _nowMs) {
110232
+ this.#flushHint(staticPub);
110233
+ }
110234
+ /** No UDP here — netd owns the socket. Never called in practice; kept for the interface. */
110235
+ injectUdp(_pkt, _from, _nowMs) {
110236
+ }
110237
+ tick(_nowMs) {
110238
+ }
110239
+ pollSend() {
110240
+ return null;
110241
+ }
110242
+ pollEvent() {
110243
+ return this.#events.shift() ?? null;
110244
+ }
110245
+ meter() {
110246
+ const m = this.#snapshot?.meter;
110247
+ const tx = (m?.txDirectBytes ?? 0) + (m?.txRelayedBytes ?? 0);
110248
+ return {
110249
+ txDirectBytes: m?.txDirectBytes ?? 0,
110250
+ txRelayedBytes: m?.txRelayedBytes ?? 0,
110251
+ rxDirectBytes: m?.rxDirectBytes ?? 0,
110252
+ rxRelayedBytes: m?.rxRelayedBytes ?? 0,
110253
+ forwardedBytes: m?.forwardedBytes ?? 0,
110254
+ forwardedPackets: m?.forwardedPackets ?? 0,
110255
+ relayedTxFraction: tx === 0 ? 0 : (m?.txRelayedBytes ?? 0) / tx
110256
+ };
110257
+ }
110258
+ localStaticPub() {
110259
+ return this.#localStaticPub;
110260
+ }
110261
+ meshId() {
110262
+ return this.#meshId;
110263
+ }
110264
+ policyEpoch() {
110265
+ return this.#snapshot?.policyEpoch ?? 0;
110266
+ }
110267
+ selfRevoked() {
110268
+ return this.#snapshot?.revoked === true;
110269
+ }
110270
+ peerCount() {
110271
+ return this.#snapshot?.peers ?? 0;
110272
+ }
110273
+ setExitNode(staticPub) {
110274
+ void netdSetExitNode(this.#meshDir, staticPub).then((r) => {
110275
+ if (!r.ok)
110276
+ this.#lastError = `exit node: ${r.detail}`;
110277
+ });
110278
+ }
110279
+ /** Is netd running the node right now, and on which port? */
110280
+ get running() {
110281
+ return this.#snapshot?.running === true;
110282
+ }
110283
+ get port() {
110284
+ return this.#snapshot && this.#snapshot.port > 0 ? this.#snapshot.port : null;
110285
+ }
110286
+ get lastError() {
110287
+ return this.#lastError;
110288
+ }
110289
+ stop() {
110290
+ this.#stopped = true;
110291
+ if (this.#timer) {
110292
+ clearInterval(this.#timer);
110293
+ this.#timer = null;
110294
+ }
110295
+ }
110296
+ };
110297
+
110298
+ // ../../packages/host-core/dist/mesh/node.js
110299
+ import { existsSync as existsSync34 } from "node:fs";
110300
+
110301
+ // ../../packages/host-core/dist/mesh/bundle-refresh.js
110302
+ init_dist6();
110303
+ var CONTEXT = "tsr-bundle-fetch-v1";
110304
+ var REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
110305
+ async function fetchBundle(args) {
110306
+ const none = { bundle: null, epoch: null };
110307
+ try {
110308
+ const ts = Date.now();
110309
+ const { privateKey } = await signingKeyFromSeed(args.sigPrivate);
110310
+ const message = new TextEncoder().encode(`${CONTEXT}
110311
+ ${args.genesisFp.toLowerCase()}
110312
+ ${args.staticPub.toLowerCase()}
110313
+ ${ts}`);
110314
+ const sig = await sign2(privateKey, message);
110315
+ const doFetch = args.fetchImpl ?? fetch;
110316
+ const res = await doFetch(`${args.origin.replace(/\/$/, "")}/api/core/tesseract/mesh/meshes/by-genesis/${encodeURIComponent(args.genesisFp.toLowerCase())}/bundle`, {
110317
+ method: "GET",
110318
+ headers: {
110319
+ "x-tsr-node": args.staticPub.toLowerCase(),
110320
+ "x-tsr-ts": String(ts),
110321
+ "x-tsr-sig": Buffer.from(sig).toString("base64")
110322
+ }
110323
+ });
110324
+ if (!res.ok)
110325
+ return none;
110326
+ const body = await res.json();
110327
+ if (!body.bundle)
110328
+ return none;
110329
+ return { bundle: Uint8Array.from(Buffer.from(body.bundle, "hex")), epoch: body.epoch ?? null };
110330
+ } catch {
110331
+ return none;
110332
+ }
110333
+ }
110334
+
109853
110335
  // ../../packages/host-core/dist/mesh/config.js
109854
110336
  init_credentials();
109855
110337
  import { chmodSync as chmodSync5, existsSync as existsSync33, mkdirSync as mkdirSync36, readdirSync as readdirSync12, readFileSync as readFileSync47, renameSync as renameSync11, rmSync as rmSync7, writeFileSync as writeFileSync35 } from "node:fs";
109856
- import { dirname as dirname31, join as join67 } from "node:path";
110338
+ import { dirname as dirname31, join as join68 } from "node:path";
109857
110339
  function meshDir() {
109858
- return join67(daemonDir2(), "mesh");
110340
+ return join68(daemonDir2(), "mesh");
109859
110341
  }
109860
110342
  function meshesDir() {
109861
- return join67(meshDir(), "meshes");
110343
+ return join68(meshDir(), "meshes");
109862
110344
  }
109863
110345
  var MESH_DIR_NAME_BYTES = 8;
109864
110346
  function meshDirName(genesisFp) {
@@ -109876,7 +110358,7 @@ function oneMeshDir(meshId) {
109876
110358
  if (!isMeshId(meshId)) {
109877
110359
  throw new Error(`not a mesh id: ${JSON.stringify(meshId.slice(0, 64))}`);
109878
110360
  }
109879
- return join67(meshesDir(), meshId);
110361
+ return join68(meshesDir(), meshId);
109880
110362
  }
109881
110363
  var LEGACY_MESH_ID = "legacy";
109882
110364
  var CERT_GENESIS_FP_OFFSET = 19;
@@ -109894,7 +110376,7 @@ var FILES = {
109894
110376
  local: "local.json"
109895
110377
  };
109896
110378
  function readIfPresent(name, dir2 = meshDir()) {
109897
- const p = join67(dir2, name);
110379
+ const p = join68(dir2, name);
109898
110380
  try {
109899
110381
  return existsSync33(p) ? new Uint8Array(readFileSync47(p)) : void 0;
109900
110382
  } catch {
@@ -109902,7 +110384,7 @@ function readIfPresent(name, dir2 = meshDir()) {
109902
110384
  }
109903
110385
  }
109904
110386
  function migrateSingleMesh() {
109905
- const legacyGenesis = join67(meshDir(), FILES.genesis);
110387
+ const legacyGenesis = join68(meshDir(), FILES.genesis);
109906
110388
  if (!existsSync33(legacyGenesis))
109907
110389
  return null;
109908
110390
  const id = meshIdFromCert(readIfPresent(FILES.cert)) ?? LEGACY_MESH_ID;
@@ -109911,11 +110393,11 @@ function migrateSingleMesh() {
109911
110393
  mkdirSync36(dest, { recursive: true, mode: 448 });
109912
110394
  chmodSync5(dest, 448);
109913
110395
  for (const f of Object.values(FILES)) {
109914
- const from = join67(meshDir(), f);
110396
+ const from = join68(meshDir(), f);
109915
110397
  if (existsSync33(from))
109916
- renameSync11(from, join67(dest, f));
110398
+ renameSync11(from, join68(dest, f));
109917
110399
  }
109918
- const key2 = join67(dest, FILES.key);
110400
+ const key2 = join68(dest, FILES.key);
109919
110401
  if (existsSync33(key2))
109920
110402
  chmodSync5(key2, 384);
109921
110403
  } catch {
@@ -109939,7 +110421,7 @@ function listMeshIds() {
109939
110421
  }
109940
110422
  function readLocal(meshId) {
109941
110423
  try {
109942
- return JSON.parse(readFileSync47(join67(oneMeshDir(meshId), FILES.local), "utf8"));
110424
+ return JSON.parse(readFileSync47(join68(oneMeshDir(meshId), FILES.local), "utf8"));
109943
110425
  } catch {
109944
110426
  return null;
109945
110427
  }
@@ -109947,7 +110429,7 @@ function readLocal(meshId) {
109947
110429
  function writeLocal(meshId, state3) {
109948
110430
  try {
109949
110431
  mkdirSync36(oneMeshDir(meshId), { recursive: true, mode: 448 });
109950
- writeFileSync35(join67(oneMeshDir(meshId), FILES.local), JSON.stringify(state3, null, 2), { mode: 384 });
110432
+ writeFileSync35(join68(oneMeshDir(meshId), FILES.local), JSON.stringify(state3, null, 2), { mode: 384 });
109951
110433
  } catch {
109952
110434
  }
109953
110435
  }
@@ -109980,7 +110462,7 @@ function loadMeshIdentity() {
109980
110462
  return first === void 0 ? null : loadMesh(first);
109981
110463
  }
109982
110464
  function writeSecret(name, bytes2, dir2 = meshDir()) {
109983
- const p = join67(dir2, name);
110465
+ const p = join68(dir2, name);
109984
110466
  writeFileSync35(p, bytes2, { mode: 384 });
109985
110467
  try {
109986
110468
  chmodSync5(p, 384);
@@ -109998,11 +110480,11 @@ function saveMeshIdentity(id) {
109998
110480
  chmodSync5(meshDir(), 448);
109999
110481
  } catch {
110000
110482
  }
110001
- writeFileSync35(join67(meshDir(), FILES.genesis), id.genesis);
110002
- writeFileSync35(join67(meshDir(), FILES.cert), id.cert);
110483
+ writeFileSync35(join68(meshDir(), FILES.genesis), id.genesis);
110484
+ writeFileSync35(join68(meshDir(), FILES.cert), id.cert);
110003
110485
  writeSecret(FILES.key, id.staticPrivate);
110004
110486
  if (id.bundle)
110005
- writeFileSync35(join67(meshDir(), FILES.bundle), id.bundle);
110487
+ writeFileSync35(join68(meshDir(), FILES.bundle), id.bundle);
110006
110488
  }
110007
110489
  function saveMesh(meshId, id) {
110008
110490
  const dir2 = oneMeshDir(meshId);
@@ -110019,13 +110501,13 @@ function saveMesh(meshId, id) {
110019
110501
  chmodSync5(dir2, 448);
110020
110502
  } catch {
110021
110503
  }
110022
- writeFileSync35(join67(dir2, FILES.genesis), id.genesis);
110023
- writeFileSync35(join67(dir2, FILES.cert), id.cert);
110504
+ writeFileSync35(join68(dir2, FILES.genesis), id.genesis);
110505
+ writeFileSync35(join68(dir2, FILES.cert), id.cert);
110024
110506
  writeSecret(FILES.key, id.staticPrivate, dir2);
110025
110507
  if (id.sigPrivate)
110026
110508
  writeSecret(FILES.sigKey, id.sigPrivate, dir2);
110027
110509
  if (id.bundle)
110028
- writeFileSync35(join67(dir2, FILES.bundle), id.bundle);
110510
+ writeFileSync35(join68(dir2, FILES.bundle), id.bundle);
110029
110511
  if (readLocal(meshId) === null)
110030
110512
  writeLocal(meshId, { joinedAt: Date.now() });
110031
110513
  }
@@ -110035,7 +110517,7 @@ function removeMesh(meshId) {
110035
110517
  var STATUS_REFRESH_MS = 5e3;
110036
110518
  var STATUS_STALE_MS = 2e4;
110037
110519
  function statusPath(meshId) {
110038
- return meshId === void 0 ? join67(meshDir(), "status.json") : join67(oneMeshDir(meshId), "status.json");
110520
+ return meshId === void 0 ? join68(meshDir(), "status.json") : join68(oneMeshDir(meshId), "status.json");
110039
110521
  }
110040
110522
  function writeStatus(status2, meshId) {
110041
110523
  try {
@@ -110066,7 +110548,7 @@ function cacheBundle(bundle, meshId) {
110066
110548
  const id = meshId ?? listMeshIds()[0];
110067
110549
  const dir2 = id === void 0 ? meshDir() : oneMeshDir(id);
110068
110550
  mkdirSync36(dir2, { recursive: true, mode: 448 });
110069
- writeFileSync35(join67(dir2, FILES.bundle), bundle);
110551
+ writeFileSync35(join68(dir2, FILES.bundle), bundle);
110070
110552
  } catch {
110071
110553
  }
110072
110554
  }
@@ -110222,6 +110704,9 @@ function loadMeshCore() {
110222
110704
  }
110223
110705
  var meshCore = loadMeshCore();
110224
110706
  function meshEngineStatus() {
110707
+ if (process.env.CCB_MESH_IN_DAEMON !== "1" && existsSync34(netdEndpoint())) {
110708
+ return { available: true, reason: null };
110709
+ }
110225
110710
  return { available: meshCore.available === true, reason: meshCore.loadError ?? null };
110226
110711
  }
110227
110712
  var TICK_MS2 = 1e3;
@@ -110317,6 +110802,8 @@ var MeshNode = class {
110317
110802
  #engine = null;
110318
110803
  #sock = null;
110319
110804
  #timer = null;
110805
+ /** The policy refresh poll. See `bundle-refresh.ts` — this is what makes eviction real. */
110806
+ #refreshTimer = null;
110320
110807
  #unsubscribe = null;
110321
110808
  #identity = null;
110322
110809
  /** The policy bundle we hold, as bytes, so we can OFFER it to a peer that is behind.
@@ -110389,8 +110876,11 @@ var MeshNode = class {
110389
110876
  }
110390
110877
  status() {
110391
110878
  return {
110392
- available: meshCore.available,
110393
- loadError: meshCore.loadError,
110879
+ // In netd mode the engine is the helper's, and whether THIS process could load a
110880
+ // prebuilt is beside the point — a CLI install whose native fetch failed still has a
110881
+ // working network when the helper runs it, and must not say "engine unavailable".
110882
+ available: this.#engine instanceof NetdEngine ? true : meshCore.available,
110883
+ loadError: this.#engine instanceof NetdEngine ? null : meshCore.loadError,
110394
110884
  enrolled: this.#identity !== null || this.#loadIdentity() !== null,
110395
110885
  running: this.#engine !== null,
110396
110886
  port: this.#port,
@@ -110407,7 +110897,22 @@ var MeshNode = class {
110407
110897
  this.#established.has(hex6) ? this.#relayed.has(hex6) ? "relay" : "direct" : "none"
110408
110898
  ])),
110409
110899
  meter: this.#engine?.meter() ?? null,
110410
- lastError: this.#lastError,
110900
+ // THE POLICY EPOCH this node is actually running on.
110901
+ //
110902
+ // Read from the ENGINE, never from `bundle.bin`: the engine installs a bundle the
110903
+ // moment it verifies one, and a bundle pulled from a peer over the Noise session
110904
+ // never touches that file. Judging freshness by the file — which is what there was
110905
+ // to judge by — therefore reports a node as stale when it is current, and is how a
110906
+ // machine came to look a dozen epochs behind while carrying the right policy.
110907
+ //
110908
+ // It is the one number that answers "why is nothing passing": a node whose policy
110909
+ // predates a peer has no roster entry for it and drops every packet, correctly, with
110910
+ // nothing anywhere saying so.
110911
+ policyEpoch: this.#engine?.policyEpoch() ?? null,
110912
+ lastError: this.#lastError ?? (this.#engine instanceof NetdEngine ? this.#engine.lastError : null),
110913
+ // In netd mode "running" means netd runs it: an adapter with no node behind it yet
110914
+ // (routes not synced, helper just installed) is not a running node.
110915
+ ...this.#engine instanceof NetdEngine && !this.#engine.running ? { running: false } : {},
110411
110916
  // Surfaced so `net status` can say "removed" rather than "not running". Those look
110412
110917
  // identical from outside and have completely different remedies.
110413
110918
  removed: this.#revoked
@@ -110429,7 +110934,8 @@ var MeshNode = class {
110429
110934
  reason: "this machine was removed from the network \u2014 ask an admin for a new invitation"
110430
110935
  };
110431
110936
  }
110432
- if (!meshCore.available) {
110937
+ const inNetd = process.env.CCB_MESH_IN_DAEMON !== "1" && existsSync34(netdEndpoint());
110938
+ if (!inNetd && !meshCore.available) {
110433
110939
  return { ok: false, reason: `mesh engine unavailable: ${meshCore.loadError ?? "no prebuilt"}` };
110434
110940
  }
110435
110941
  const id = this.#loadIdentity();
@@ -110439,20 +110945,46 @@ var MeshNode = class {
110439
110945
  reason: this.#wantMeshId === null ? "this machine is not enrolled in a mesh" : `this machine is not enrolled in mesh ${this.#wantMeshId}`
110440
110946
  };
110441
110947
  }
110442
- try {
110443
- this.#engine = new meshCore.MeshEngine(Buffer.from(id.genesis), Buffer.from(id.staticPrivate), Buffer.from(id.cert), 0, Buffer.alloc(16));
110444
- if (id.bundle) {
110445
- this.#engine.setBundle(Buffer.from(id.bundle), Date.now());
110446
- this.#bundle = id.bundle;
110948
+ if (inNetd) {
110949
+ const meshDir2 = this.#wantMeshId ?? listMeshIds()[0] ?? null;
110950
+ if (!meshDir2)
110951
+ return { ok: false, reason: "this machine is not enrolled in a mesh" };
110952
+ try {
110953
+ this.#engine = new NetdEngine(meshDir2, id.cert, {
110954
+ // netd reports the node up: now this side has a port to advertise, and can
110955
+ // publish itself to the relay and the LAN. Before that there is nothing to say.
110956
+ onUp: (port) => {
110957
+ this.#port = port;
110958
+ this.#join();
110959
+ this.#startLan();
110960
+ this.#publishStatus();
110961
+ }
110962
+ });
110963
+ if (id.bundle)
110964
+ this.#bundle = id.bundle;
110965
+ } catch (e) {
110966
+ this.#engine = null;
110967
+ this.#lastError = e instanceof Error ? e.message : String(e);
110968
+ return { ok: false, reason: this.#lastError };
110969
+ }
110970
+ } else {
110971
+ try {
110972
+ this.#engine = new meshCore.MeshEngine(Buffer.from(id.genesis), Buffer.from(id.staticPrivate), Buffer.from(id.cert), 0, Buffer.alloc(16));
110973
+ if (id.bundle) {
110974
+ this.#engine.setBundle(Buffer.from(id.bundle), Date.now());
110975
+ this.#bundle = id.bundle;
110976
+ }
110977
+ } catch (e) {
110978
+ this.#engine = null;
110979
+ this.#lastError = e instanceof Error ? e.message : String(e);
110980
+ return { ok: false, reason: this.#lastError };
110447
110981
  }
110448
- } catch (e) {
110449
- this.#engine = null;
110450
- this.#lastError = e instanceof Error ? e.message : String(e);
110451
- return { ok: false, reason: this.#lastError };
110452
110982
  }
110453
110983
  this.#identity = id;
110454
- this.#openSocket();
110455
- this.#startLan();
110984
+ if (!inNetd) {
110985
+ this.#openSocket();
110986
+ this.#startLan();
110987
+ }
110456
110988
  this.#offRejoin = onMeshRejoin(() => this.#join());
110457
110989
  this.#publishStatus();
110458
110990
  this.#statusTimer = setInterval(() => this.#publishStatus(), STATUS_REFRESH_MS);
@@ -110460,6 +110992,9 @@ var MeshNode = class {
110460
110992
  this.#subscribe();
110461
110993
  this.#timer = setInterval(() => this.#tick(), TICK_MS2);
110462
110994
  this.#timer.unref?.();
110995
+ void this.#refreshBundle();
110996
+ this.#refreshTimer = setInterval(() => void this.#refreshBundle(), REFRESH_INTERVAL_MS);
110997
+ this.#refreshTimer.unref?.();
110463
110998
  return { ok: true };
110464
110999
  }
110465
111000
  /** Tear down. Idempotent, and safe to call from a signal handler. */
@@ -110468,6 +111003,16 @@ var MeshNode = class {
110468
111003
  clearInterval(this.#timer);
110469
111004
  this.#timer = null;
110470
111005
  }
111006
+ if (this.#refreshTimer) {
111007
+ clearInterval(this.#refreshTimer);
111008
+ this.#refreshTimer = null;
111009
+ }
111010
+ if (this.#engine instanceof NetdEngine) {
111011
+ try {
111012
+ this.#engine.stop();
111013
+ } catch {
111014
+ }
111015
+ }
110471
111016
  this.#unsubscribe?.();
110472
111017
  this.#unsubscribe = null;
110473
111018
  this.#offRejoin?.();
@@ -110567,26 +111112,34 @@ var MeshNode = class {
110567
111112
  staticPub: engine.localStaticPub(),
110568
111113
  port: () => this.#port,
110569
111114
  epoch: () => engine.policyEpoch(),
110570
- onPeer: (staticPub, address, port) => this.#learnLanPeer(staticPub, address, port)
111115
+ onPeer: (staticPub, address, port, epoch2) => this.#learnLanPeer(staticPub, address, port, epoch2)
110571
111116
  });
110572
111117
  this.#lan.start();
110573
111118
  } catch {
110574
111119
  this.#lan = null;
110575
111120
  }
110576
111121
  }
110577
- /** A beacon arrived. Treated exactly like a relay hint — a candidate, not a fact. */
110578
- #learnLanPeer(staticPub, address, port) {
111122
+ /** A beacon arrived. Treated exactly like a relay hint — a candidate, not a fact.
111123
+ *
111124
+ * Including its EPOCH. The beacon has always carried one (`lan.ts`: "port(2) | epoch(8)")
111125
+ * for the same reason the relay hint does — so a peer that is behind can be brought up
111126
+ * to date — and, like the relay hint's, it was thrown away here. Two laptops on one
111127
+ * Wi-Fi with the internet down are the demonstration that the overlay is not
111128
+ * relay-dependent; without this they would find each other and then never agree on who
111129
+ * else exists. */
111130
+ #learnLanPeer(staticPub, address, port, epoch2 = 0) {
110579
111131
  const engine = this.#engine;
110580
111132
  if (!engine)
110581
111133
  return;
110582
111134
  const hex6 = staticPub.toString("hex");
111135
+ this.#offerBundleTo(hex6, epoch2);
110583
111136
  try {
110584
111137
  if (!this.#peers.has(hex6)) {
110585
111138
  engine.addPeer(staticPub, 0, Buffer.alloc(16));
110586
111139
  this.#peers.set(hex6, staticPub);
110587
111140
  }
110588
111141
  engine.addPath(staticPub, `${address}:${port}`, false);
110589
- engine.startHandshake(staticPub, Date.now());
111142
+ engine.ensureHandshake(staticPub, Date.now());
110590
111143
  this.#drain();
110591
111144
  } catch (e) {
110592
111145
  this.#lastError = e instanceof Error ? e.message : String(e);
@@ -110671,7 +111224,7 @@ var MeshNode = class {
110671
111224
  } catch {
110672
111225
  }
110673
111226
  }
110674
- engine.startHandshake(pk, Date.now());
111227
+ engine.ensureHandshake(pk, Date.now());
110675
111228
  break;
110676
111229
  }
110677
111230
  case "mesh:gone": {
@@ -110701,6 +111254,47 @@ var MeshNode = class {
110701
111254
  this.#lastError = e instanceof Error ? e.message : String(e);
110702
111255
  }
110703
111256
  }
111257
+ /**
111258
+ * Ask the server for current policy, and adopt it if it is newer.
111259
+ *
111260
+ * The one path that can deliver an EVICTION. Gossip only ever moves a network to the
111261
+ * newest bundle some connected node already holds, and after an eviction no node holds
111262
+ * the new one — so without this, "remove that laptop" changed a database row and nothing
111263
+ * else until somebody happened to join.
111264
+ *
111265
+ * Applied through exactly the same steps as a gossiped bundle, deliberately: the engine
111266
+ * verifies it against genesis (so a hostile server cannot widen policy any more than a
111267
+ * hostile relay can), `#stopIfRevoked` reads our own serial out of it, it is cached to
111268
+ * disk, and the offer ledger resets so our peers hear about it too. That last part is
111269
+ * what makes one node's poll benefit the whole network.
111270
+ */
111271
+ async #refreshBundle() {
111272
+ const engine = this.#engine;
111273
+ const id = this.#identity;
111274
+ if (!engine || !id?.sigPrivate)
111275
+ return;
111276
+ const meshDir2 = meshDirNameFromWireId(engine.meshId());
111277
+ const origin = (meshDir2 ? readLocal(meshDir2)?.origin : void 0) ?? readCredentials()?.apiOrigin;
111278
+ if (!origin)
111279
+ return;
111280
+ const got = await fetchBundle({
111281
+ origin,
111282
+ genesisFp: engine.meshId(),
111283
+ staticPub: Buffer.from(engine.localStaticPub()).toString("hex"),
111284
+ sigPrivate: id.sigPrivate
111285
+ });
111286
+ if (!got.bundle)
111287
+ return;
111288
+ try {
111289
+ engine.setBundle(Buffer.from(got.bundle), Date.now());
111290
+ } catch {
111291
+ return;
111292
+ }
111293
+ this.#stopIfRevoked(engine);
111294
+ cacheBundle(got.bundle, meshDir2 ?? void 0);
111295
+ this.#bundle = got.bundle;
111296
+ this.#bundleOffered.reset();
111297
+ }
110704
111298
  /**
110705
111299
  * Offer our policy bundle to a peer whose epoch is older than ours.
110706
111300
  *
@@ -110774,7 +111368,7 @@ var MeshNode = class {
110774
111368
  for (const e of p.endpoints)
110775
111369
  engine.addPath(pk, `${p.observedIp}:${e.port}`, false);
110776
111370
  }
110777
- engine.startHandshake(pk, Date.now());
111371
+ engine.ensureHandshake(pk, Date.now());
110778
111372
  const meshId = this.#engine?.meshId();
110779
111373
  try {
110780
111374
  if (meshId)
@@ -110795,10 +111389,12 @@ var MeshNode = class {
110795
111389
  /** Flush everything the engine wants to send, and absorb its events. */
110796
111390
  #drain() {
110797
111391
  const engine = this.#engine;
110798
- const sock = this.#sock;
110799
- if (!engine || !sock)
111392
+ if (!engine)
110800
111393
  return;
111394
+ const sock = this.#sock;
110801
111395
  for (; ; ) {
111396
+ if (!sock)
111397
+ break;
110802
111398
  const item = engine.pollSend();
110803
111399
  if (!item)
110804
111400
  break;
@@ -110946,148 +111542,6 @@ function meshManager() {
110946
111542
  return singleton3;
110947
111543
  }
110948
111544
 
110949
- // ../../packages/host-core/dist/mesh/netd-client.js
110950
- import { connect as connect2 } from "node:net";
110951
- import { platform as platform3 } from "node:process";
110952
- var NETD_WIRE_VERSION = 1;
110953
- var MAX_LINE = 1024 * 1024;
110954
- var TIMEOUT_MS3 = 15e3;
110955
- function netdEndpoint() {
110956
- if (platform3 === "win32")
110957
- return "\\\\.\\pipe\\tesseract-netd";
110958
- if (platform3 === "darwin")
110959
- return "/var/run/tesseract-netd.sock";
110960
- return "/run/tesseract-netd.sock";
110961
- }
110962
- function endpointOverride() {
110963
- return process.env.CCB_NETD_SOCKET ?? null;
110964
- }
110965
- function fail2(code, detail) {
110966
- return { ok: false, code, detail };
110967
- }
110968
- async function call(line) {
110969
- const endpoint2 = endpointOverride() ?? netdEndpoint();
110970
- return new Promise((resolve16) => {
110971
- let settled = false;
110972
- const done = (r) => {
110973
- if (settled)
110974
- return;
110975
- settled = true;
110976
- try {
110977
- sock.destroy();
110978
- } catch {
110979
- }
110980
- resolve16(r);
110981
- };
110982
- let sock;
110983
- try {
110984
- sock = connect2(endpoint2);
110985
- } catch (e) {
110986
- return resolve16(fail2("not-installed", e instanceof Error ? e.message : String(e)));
110987
- }
110988
- const timer2 = setTimeout(() => {
110989
- done(fail2("timeout", `no answer from the network helper in ${TIMEOUT_MS3}ms`));
110990
- }, TIMEOUT_MS3);
110991
- timer2.unref?.();
110992
- sock.setTimeout(TIMEOUT_MS3);
110993
- sock.on("timeout", () => {
110994
- clearTimeout(timer2);
110995
- done(fail2("timeout", "socket idle"));
110996
- });
110997
- sock.on("error", (e) => {
110998
- clearTimeout(timer2);
110999
- const absent = e.code === "ENOENT" || e.code === "ECONNREFUSED";
111000
- done(fail2(absent ? "not-installed" : "io", e.message));
111001
- });
111002
- let buf = "";
111003
- sock.on("data", (chunk) => {
111004
- buf += chunk.toString("utf8");
111005
- if (buf.length > MAX_LINE) {
111006
- clearTimeout(timer2);
111007
- done(fail2("protocol", "the helper sent an oversized line"));
111008
- return;
111009
- }
111010
- const nl = buf.indexOf("\n");
111011
- if (nl === -1)
111012
- return;
111013
- clearTimeout(timer2);
111014
- done({ ok: true, line: buf.slice(0, nl) });
111015
- });
111016
- sock.on("close", () => {
111017
- clearTimeout(timer2);
111018
- done(fail2("protocol", "the helper closed the connection without answering"));
111019
- });
111020
- sock.on("connect", () => {
111021
- sock.write(`${line}
111022
- `);
111023
- });
111024
- });
111025
- }
111026
- function parse3(line) {
111027
- let body;
111028
- try {
111029
- body = JSON.parse(line);
111030
- } catch {
111031
- return fail2("protocol", "the helper sent something that is not JSON");
111032
- }
111033
- if (typeof body !== "object" || body === null) {
111034
- return fail2("protocol", "the helper sent something that is not an object");
111035
- }
111036
- const o = body;
111037
- if (o.ok === false) {
111038
- const code = typeof o.code === "string" ? o.code : "refused";
111039
- const detail = typeof o.detail === "string" ? o.detail : "the helper refused";
111040
- return fail2(code, detail);
111041
- }
111042
- if (o.ok !== true)
111043
- return fail2("protocol", "the helper did not say whether it succeeded");
111044
- return { ok: true, body: o };
111045
- }
111046
- async function request(line) {
111047
- const r = await call(line);
111048
- return r.ok ? parse3(r.line) : r;
111049
- }
111050
- function hex4(b) {
111051
- return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
111052
- }
111053
- async function netdHello() {
111054
- const r = await request(JSON.stringify({ op: "hello", wireVersion: NETD_WIRE_VERSION }));
111055
- if (!r.ok)
111056
- return r;
111057
- const v = r.body.wireVersion;
111058
- if (typeof v !== "number")
111059
- return fail2("protocol", "the helper did not report a wire version");
111060
- if (v !== NETD_WIRE_VERSION) {
111061
- return fail2("wire-version", `the helper speaks ${v}, this build speaks ${NETD_WIRE_VERSION}`);
111062
- }
111063
- return { ok: true, wireVersion: v };
111064
- }
111065
- async function netdStatus() {
111066
- const r = await request(JSON.stringify({ op: "status" }));
111067
- if (!r.ok)
111068
- return r;
111069
- const meshes = Array.isArray(r.body.meshes) ? r.body.meshes.filter((m) => typeof m === "string") : [];
111070
- const iface = typeof r.body.interface === "string" ? r.body.interface : null;
111071
- return { ok: true, meshes, interface: iface };
111072
- }
111073
- async function netdUp(args) {
111074
- const r = await request(JSON.stringify({
111075
- op: "up",
111076
- meshId: args.meshId,
111077
- genesis: hex4(args.genesis),
111078
- cert: hex4(args.cert),
111079
- fullTunnel: args.fullTunnel === true
111080
- }));
111081
- return r.ok ? { ok: true } : r;
111082
- }
111083
- async function netdDown(meshId) {
111084
- const r = await request(JSON.stringify({ op: "down", meshId }));
111085
- return r.ok ? { ok: true } : r;
111086
- }
111087
- async function netdAvailable() {
111088
- return (await netdHello()).ok;
111089
- }
111090
-
111091
111545
  // ../../packages/host-core/dist/mesh/route-sync.js
111092
111546
  async function syncRoutes(running2, log9 = () => {
111093
111547
  }) {
@@ -111116,7 +111570,14 @@ async function syncRoutes(running2, log9 = () => {
111116
111570
  // The per-machine choice, and `=== true` rather than a truthiness check: an absent or
111117
111571
  // malformed field must read as NO. netd unions this across meshes itself — any mesh
111118
111572
  // asking for a full tunnel makes it one for the machine — so this stays per mesh here.
111119
- fullTunnel: readLocal(meshId)?.fullTunnel === true
111573
+ fullTunnel: readLocal(meshId)?.fullTunnel === true,
111574
+ // THE KEY, and the bundle. With these netd runs the node — the data plane — rather
111575
+ // than only pointing routes at a device nothing reads. The crate's own doc always
111576
+ // described the enrolment as "a genesis block, a certificate and a key"; this is the
111577
+ // first caller to send the third. Over the helper's authenticated socket, to the one
111578
+ // process on the machine that already holds the device the traffic goes into.
111579
+ staticPrivate: identity.staticPrivate,
111580
+ ...identity.bundle ? { bundle: identity.bundle } : {}
111120
111581
  });
111121
111582
  if (r.ok) {
111122
111583
  out.up.push(meshId);
@@ -111142,22 +111603,63 @@ async function syncRoutes(running2, log9 = () => {
111142
111603
  }
111143
111604
 
111144
111605
  // ../../packages/host-core/dist/mesh/start-meshes.js
111145
- function startMeshes(log9) {
111606
+ var ROUTE_RETRY_MS = 6e4;
111607
+ function startMeshes(log9, opts = {}) {
111146
111608
  const running2 = startNodes(log9);
111147
- void syncRoutes(running2, log9).then((r) => {
111609
+ const retryMs = opts.retryMs ?? ROUTE_RETRY_MS;
111610
+ let timer2 = null;
111611
+ let stopped2 = false;
111612
+ let lastSummary = "";
111613
+ const attempt = async () => {
111614
+ let r;
111615
+ try {
111616
+ r = await syncRoutes(running2, log9);
111617
+ } catch (e) {
111618
+ log9(`routes: sync threw \u2014 ${e instanceof Error ? e.message : String(e)}`);
111619
+ return false;
111620
+ }
111148
111621
  if (r.helperAbsent) {
111149
- log9("routes: the network helper is not installed \u2014 the overlay carries no traffic yet");
111150
- return;
111622
+ const msg = "routes: the network helper is not installed \u2014 the overlay carries no traffic yet";
111623
+ if (msg !== lastSummary)
111624
+ log9(msg);
111625
+ lastSummary = msg;
111626
+ return false;
111151
111627
  }
111152
111628
  if (r.up.length)
111153
111629
  log9(`routes: installed for ${r.up.join(", ")}`);
111154
111630
  if (r.down.length)
111155
111631
  log9(`routes: removed for ${r.down.join(", ")}`);
111156
- for (const f of r.failed)
111157
- log9(`routes: ${f.meshId} refused \u2014 ${f.reason}`);
111158
- }).catch((e) => {
111159
- log9(`routes: sync threw \u2014 ${e instanceof Error ? e.message : String(e)}`);
111632
+ const failures2 = r.failed.map((f) => `routes: ${f.meshId} refused \u2014 ${f.reason}`).join("\n");
111633
+ if (failures2 && failures2 !== lastSummary)
111634
+ log9(failures2);
111635
+ lastSummary = failures2;
111636
+ return r.failed.length === 0;
111637
+ };
111638
+ const schedule = () => {
111639
+ if (stopped2 || running2.length === 0)
111640
+ return;
111641
+ timer2 = setTimeout(() => {
111642
+ timer2 = null;
111643
+ void attempt().then((done) => {
111644
+ if (!done)
111645
+ schedule();
111646
+ });
111647
+ }, retryMs);
111648
+ timer2.unref?.();
111649
+ };
111650
+ void attempt().then((done) => {
111651
+ if (!done)
111652
+ schedule();
111160
111653
  });
111654
+ return {
111655
+ stop() {
111656
+ stopped2 = true;
111657
+ if (timer2) {
111658
+ clearTimeout(timer2);
111659
+ timer2 = null;
111660
+ }
111661
+ }
111662
+ };
111161
111663
  }
111162
111664
  function startNodes(log9) {
111163
111665
  const ids = listMeshIds();
@@ -112487,7 +112989,7 @@ function appendRdeskAudit(e) {
112487
112989
 
112488
112990
  // ../../packages/host-core/dist/fileprovider-bridge.js
112489
112991
  init_managed_spawn();
112490
- import { writeFileSync as writeFileSync36, mkdirSync as mkdirSync37, rmSync as rmSync8, existsSync as existsSync34 } from "node:fs";
112992
+ import { writeFileSync as writeFileSync36, mkdirSync as mkdirSync37, rmSync as rmSync8, existsSync as existsSync35 } from "node:fs";
112491
112993
  import { homedir as homedir10 } from "node:os";
112492
112994
  import { join as join73 } from "node:path";
112493
112995
  init_safe_kill();
@@ -112658,7 +113160,7 @@ function stopFileProviderBridge() {
112658
113160
 
112659
113161
  // ../../packages/host-core/dist/vault/autofill-bridge.js
112660
113162
  import { randomBytes as randomBytes9 } from "node:crypto";
112661
- import { existsSync as existsSync35, mkdirSync as mkdirSync38, writeFileSync as writeFileSync37, rmSync as rmSync9 } from "node:fs";
113163
+ import { existsSync as existsSync36, mkdirSync as mkdirSync38, writeFileSync as writeFileSync37, rmSync as rmSync9 } from "node:fs";
112662
113164
  import { homedir as homedir11 } from "node:os";
112663
113165
  import { join as join74 } from "node:path";
112664
113166
 
@@ -112733,7 +113235,7 @@ function writeBridgeInfo(port, token3, log9) {
112733
113235
  return;
112734
113236
  const containerData = appexContainerData();
112735
113237
  try {
112736
- if (!existsSync35(containerData)) {
113238
+ if (!existsSync36(containerData)) {
112737
113239
  if (!infoLoggedOnce) {
112738
113240
  infoLoggedOnce = true;
112739
113241
  log9("autofill bridge-info SKIP: no appex container yet at", containerData);
@@ -112982,7 +113484,7 @@ function createNodePowerController() {
112982
113484
 
112983
113485
  // ../../packages/host-core/dist/native-host.js
112984
113486
  import { execFileSync as execFileSync4 } from "node:child_process";
112985
- import { chmodSync as chmodSync6, existsSync as existsSync36, mkdirSync as mkdirSync39, writeFileSync as writeFileSync38 } from "node:fs";
113487
+ import { chmodSync as chmodSync6, existsSync as existsSync37, mkdirSync as mkdirSync39, writeFileSync as writeFileSync38 } from "node:fs";
112986
113488
  import { homedir as homedir12, platform as platform5 } from "node:os";
112987
113489
  import { dirname as dirname34, join as join75, resolve as resolve12 } from "node:path";
112988
113490
  import { fileURLToPath as fileURLToPath6 } from "node:url";
@@ -113013,7 +113515,7 @@ function resolveBridge(log9) {
113013
113515
  } catch {
113014
113516
  }
113015
113517
  for (const c of candidates)
113016
- if (c && existsSync36(c))
113518
+ if (c && existsSync37(c))
113017
113519
  return c;
113018
113520
  log9("companion native-host: bridge not found; skipping registration (looked in", candidates.filter(Boolean).join(" , "), ")");
113019
113521
  return null;
@@ -113055,7 +113557,7 @@ function registerNativeHost(opts) {
113055
113557
  return registerWindows(opts.daemonDir, manifestJson, log9);
113056
113558
  let wrote = 0;
113057
113559
  for (const profile of browserProfileDirs()) {
113058
- if (!existsSync36(profile))
113560
+ if (!existsSync37(profile))
113059
113561
  continue;
113060
113562
  try {
113061
113563
  const dir2 = join75(profile, "NativeMessagingHosts");
@@ -113091,7 +113593,7 @@ init_daemon_settings();
113091
113593
 
113092
113594
  // ../../packages/host-core/dist/activity/activity-tracker.js
113093
113595
  init_credentials();
113094
- import { promises as fsp, writeFileSync as writeFileSync39, mkdirSync as mkdirSync40, existsSync as existsSync37 } from "node:fs";
113596
+ import { promises as fsp, writeFileSync as writeFileSync39, mkdirSync as mkdirSync40, existsSync as existsSync38 } from "node:fs";
113095
113597
  import { join as join76 } from "node:path";
113096
113598
  function activityProfilePath() {
113097
113599
  return join76(daemonDir2(), "activity-profile.json");
@@ -113169,7 +113671,7 @@ function createActivityTracker(opts = {}) {
113169
113671
  return;
113170
113672
  const key2 = front.bundleId || front.name;
113171
113673
  await ensureLoaded();
113172
- if (profile && Object.keys(profile.apps).length && !existsSync37(activityProfilePath())) {
113674
+ if (profile && Object.keys(profile.apps).length && !existsSync38(activityProfilePath())) {
113173
113675
  log9("activity profile cleared externally \u2014 resetting in-memory");
113174
113676
  profile = { version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), apps: {} };
113175
113677
  }
@@ -113402,7 +113904,7 @@ function backoff2() {
113402
113904
 
113403
113905
  // ../../packages/host-core/dist/daemon-runtime.js
113404
113906
  var daemonClipboardSync = null;
113405
- var REFRESH_INTERVAL_MS = 12 * 60 * 60 * 1e3;
113907
+ var REFRESH_INTERVAL_MS2 = 12 * 60 * 60 * 1e3;
113406
113908
  async function runDaemon(opts = {}) {
113407
113909
  const VERSION3 = opts.version ?? process.env.CCB_DAEMON_VERSION ?? "dev";
113408
113910
  const TAG = opts.tag ?? "host-daemon";
@@ -113443,7 +113945,7 @@ async function runDaemon(opts = {}) {
113443
113945
  function acquireLock2() {
113444
113946
  const f = pidfile();
113445
113947
  try {
113446
- if (!existsSync38(daemonDir2()))
113948
+ if (!existsSync39(daemonDir2()))
113447
113949
  mkdirSync41(daemonDir2(), { recursive: true, mode: 448 });
113448
113950
  } catch {
113449
113951
  }
@@ -113469,7 +113971,7 @@ async function runDaemon(opts = {}) {
113469
113971
  function releaseLock() {
113470
113972
  try {
113471
113973
  const f = pidfile();
113472
- if (existsSync38(f) && Number(readFileSync49(f, "utf-8").trim()) === process.pid)
113974
+ if (existsSync39(f) && Number(readFileSync49(f, "utf-8").trim()) === process.pid)
113473
113975
  unlinkSync6(f);
113474
113976
  } catch {
113475
113977
  }
@@ -113511,6 +114013,7 @@ async function runDaemon(opts = {}) {
113511
114013
  }
113512
114014
  }
113513
114015
  let stopGatewayKeyRefresh = null;
114016
+ let meshRoutes = null;
113514
114017
  let timer2 = null;
113515
114018
  let identityWatch = null;
113516
114019
  let refreshRetry = null;
@@ -113605,6 +114108,10 @@ async function runDaemon(opts = {}) {
113605
114108
  stopGatewayKeyRefresh();
113606
114109
  stopGatewayKeyRefresh = null;
113607
114110
  }
114111
+ if (meshRoutes) {
114112
+ meshRoutes.stop();
114113
+ meshRoutes = null;
114114
+ }
113608
114115
  try {
113609
114116
  offRecovery?.();
113610
114117
  offRecovery = null;
@@ -114296,7 +114803,7 @@ async function runDaemon(opts = {}) {
114296
114803
  } catch {
114297
114804
  }
114298
114805
  try {
114299
- startMeshes(log9);
114806
+ meshRoutes = startMeshes(log9);
114300
114807
  } catch {
114301
114808
  }
114302
114809
  writeStatus2({ deviceId: c.deviceId, connected: true });
@@ -114575,7 +115082,7 @@ async function runDaemon(opts = {}) {
114575
115082
  const daemonLogFile = join77(daemonDir2(), "daemon.log");
114576
115083
  const capTimer = setInterval(() => {
114577
115084
  try {
114578
- if (existsSync38(daemonLogFile) && statSync14(daemonLogFile).size > DAEMON_LOG_CAP) {
115085
+ if (existsSync39(daemonLogFile) && statSync14(daemonLogFile).size > DAEMON_LOG_CAP) {
114579
115086
  const buf = readFileSync49(daemonLogFile, "utf-8");
114580
115087
  const kept = `\u2026[daemon.log capped at ${(/* @__PURE__ */ new Date()).toISOString()}]\u2026
114581
115088
  ` + buf.slice(-DAEMON_LOG_CAP / 2);
@@ -114592,7 +115099,7 @@ async function runDaemon(opts = {}) {
114592
115099
  startAutofillBridge(log9);
114593
115100
  }
114594
115101
  if (ownsMachine)
114595
- startMeshes(log9);
115102
+ meshRoutes = startMeshes(log9);
114596
115103
  if (ownsMachine) {
114597
115104
  try {
114598
115105
  stopGatewayKeyRefresh = startGatewayKeyRefresh();
@@ -114604,7 +115111,7 @@ async function runDaemon(opts = {}) {
114604
115111
  await refresh();
114605
115112
  timer2 = setInterval(() => {
114606
115113
  void refresh();
114607
- }, REFRESH_INTERVAL_MS);
115114
+ }, REFRESH_INTERVAL_MS2);
114608
115115
  }
114609
115116
 
114610
115117
  // ../../packages/host-core/dist/host-daemon-entry.js
@@ -114714,7 +115221,7 @@ async function evictNode(args) {
114714
115221
  const r = await call2(`/meshes/${encodeURIComponent(args.meshId)}/nodes/${encodeURIComponent(args.nodeId)}`, { method: "DELETE", body: {} });
114715
115222
  return r.ok ? { ok: true, epoch: r.data.epoch } : r;
114716
115223
  }
114717
- async function enrolWith(requested, post) {
115224
+ async function enrolWith(requested, post, origin) {
114718
115225
  const name = normaliseNodeName(requested);
114719
115226
  if (!name) {
114720
115227
  return fail3("bad-name", `"${requested}" cannot be a machine name \u2014 names are DNS labels, so letters, digits and hyphens only`);
@@ -114738,7 +115245,7 @@ async function enrolWith(requested, post) {
114738
115245
  }
114739
115246
  try {
114740
115247
  saveMesh(meshId, { genesis, cert, staticPrivate, sigPrivate });
114741
- writeLocal(meshId, { joinedAt: Date.now(), enabled: true });
115248
+ writeLocal(meshId, { joinedAt: Date.now(), enabled: true, ...origin ? { origin } : {} });
114742
115249
  if (r.data.bundle) {
114743
115250
  try {
114744
115251
  cacheBundle(fromHexOrB64(r.data.bundle), meshId);
@@ -114760,16 +115267,24 @@ async function enrolWith(requested, post) {
114760
115267
  };
114761
115268
  }
114762
115269
  async function enrol(args) {
114763
- return enrolWith(args.name, (staticPub, sigPub, name) => call2(`/meshes/${encodeURIComponent(args.meshId)}/nodes`, {
114764
- method: "POST",
114765
- body: { name, staticPub, sigPub, ...args.exitNode === true ? { exitNode: true } : {} }
114766
- }));
115270
+ return enrolWith(
115271
+ args.name,
115272
+ (staticPub, sigPub, name) => call2(`/meshes/${encodeURIComponent(args.meshId)}/nodes`, {
115273
+ method: "POST",
115274
+ body: { name, staticPub, sigPub, ...args.exitNode === true ? { exitNode: true } : {} }
115275
+ }),
115276
+ // This path is authenticated, so the origin is necessarily the signed-in one.
115277
+ resolveOrigin(void 0)
115278
+ );
114767
115279
  }
114768
115280
  async function joinWithToken(args) {
114769
- return enrolWith(args.name, async (staticPub, sigPub, name) => postJoin(args.origin, { token: args.token, name, staticPub, sigPub }));
115281
+ return enrolWith(args.name, async (staticPub, sigPub, name) => postJoin(args.origin, { token: args.token, name, staticPub, sigPub }), resolveOrigin(args.origin));
115282
+ }
115283
+ function resolveOrigin(origin) {
115284
+ return origin ?? readCredentials()?.apiOrigin ?? void 0;
114770
115285
  }
114771
115286
  async function postJoin(origin, body) {
114772
- const base = origin ?? readCredentials()?.apiOrigin;
115287
+ const base = resolveOrigin(origin);
114773
115288
  if (!base) {
114774
115289
  return fail3("not-signed-in", "this machine has never signed in \u2014 pass --origin https://\u2026 so it knows which server to ask");
114775
115290
  }
@@ -114797,7 +115312,7 @@ async function postJoin(origin, body) {
114797
115312
  return { ok: true, data: JSON.parse(text12) };
114798
115313
  }
114799
115314
  async function enrolWithCode(args) {
114800
- return enrolWith(args.name, async (staticPub, sigPub, name) => postJoin(args.origin, { code: args.code, name, staticPub, sigPub }));
115315
+ return enrolWith(args.name, async (staticPub, sigPub, name) => postJoin(args.origin, { code: args.code, name, staticPub, sigPub }), resolveOrigin(args.origin));
114801
115316
  }
114802
115317
  async function rawPrivate(key2) {
114803
115318
  const pkcs8 = new Uint8Array(await crypto.subtle.exportKey("pkcs8", key2));
@@ -114881,13 +115396,13 @@ init_credentials();
114881
115396
 
114882
115397
  // ../../packages/host-core/dist/net-service/ensure-installed.js
114883
115398
  import { execFile as execFile14 } from "node:child_process";
114884
- import { existsSync as existsSync42 } from "node:fs";
115399
+ import { existsSync as existsSync43 } from "node:fs";
114885
115400
  import { dirname as dirname36, join as join81 } from "node:path";
114886
115401
  import { promisify as promisify10 } from "node:util";
114887
115402
 
114888
115403
  // ../../packages/host-core/dist/net-service/launchd-system.js
114889
115404
  import { spawn as spawn9 } from "node:child_process";
114890
- import { existsSync as existsSync39, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync41, rmSync as rmSync10, readFileSync as readFileSync50 } from "node:fs";
115405
+ import { existsSync as existsSync40, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync41, rmSync as rmSync10, readFileSync as readFileSync50 } from "node:fs";
114891
115406
  import { tmpdir as tmpdir6 } from "node:os";
114892
115407
  import { join as join78 } from "node:path";
114893
115408
 
@@ -115058,7 +115573,7 @@ ${plist}TESSERACT_PLIST_EOF`,
115058
115573
  };
115059
115574
  },
115060
115575
  async state() {
115061
- if (!existsSync39(PLIST))
115576
+ if (!existsSync40(PLIST))
115062
115577
  return { installed: false, running: false };
115063
115578
  const owner = parseOwnerUid(readFileSync50(PLIST, "utf8"));
115064
115579
  const running2 = await netdAvailable();
@@ -115078,7 +115593,7 @@ ${plist}TESSERACT_PLIST_EOF`,
115078
115593
 
115079
115594
  // ../../packages/host-core/dist/net-service/systemd-system.js
115080
115595
  import { spawn as spawn11 } from "node:child_process";
115081
- import { existsSync as existsSync40, readFileSync as readFileSync51 } from "node:fs";
115596
+ import { existsSync as existsSync41, readFileSync as readFileSync51 } from "node:fs";
115082
115597
 
115083
115598
  // ../../packages/host-core/dist/service/exec.js
115084
115599
  import { execFile as execFile11, spawn as spawn10 } from "node:child_process";
@@ -115221,7 +115736,7 @@ function makeSystemdSystem() {
115221
115736
  return code === 0 ? { ok: true } : { ok: false, detail: `removing the helper failed (exit ${code})` };
115222
115737
  },
115223
115738
  async state() {
115224
- if (!existsSync40(UNIT_PATH))
115739
+ if (!existsSync41(UNIT_PATH))
115225
115740
  return { installed: false, running: false };
115226
115741
  const owner = parseOwnerUid(readFileSync51(UNIT_PATH, "utf8"));
115227
115742
  const active = await tryRun("systemctl", ["is-active", UNIT]);
@@ -115484,7 +115999,7 @@ function makeNetService() {
115484
115999
  }
115485
116000
 
115486
116001
  // ../../packages/host-core/dist/net-service/netd-binary.js
115487
- import { existsSync as existsSync41 } from "node:fs";
116002
+ import { existsSync as existsSync42 } from "node:fs";
115488
116003
  import { dirname as dirname35, join as join80 } from "node:path";
115489
116004
  import { fileURLToPath as fileURLToPath7 } from "node:url";
115490
116005
  function netdBinaryName() {
@@ -115492,7 +116007,7 @@ function netdBinaryName() {
115492
116007
  }
115493
116008
  function override2() {
115494
116009
  const p = process.env.CCB_NETD_BIN;
115495
- return p && existsSync41(p) ? p : null;
116010
+ return p && existsSync42(p) ? p : null;
115496
116011
  }
115497
116012
  function netdBinaryPath() {
115498
116013
  const fromEnv = override2();
@@ -115519,7 +116034,7 @@ function netdBinaryPath() {
115519
116034
  candidates.push(join80(process.cwd(), "crates", "target", "release", name));
115520
116035
  for (const c of candidates) {
115521
116036
  try {
115522
- if (existsSync41(c))
116037
+ if (existsSync42(c))
115523
116038
  return c;
115524
116039
  } catch {
115525
116040
  }
@@ -115561,7 +116076,7 @@ async function ensureNetHelperInstalled() {
115561
116076
  if (st.installed && !mismatched)
115562
116077
  return { ok: true, already: true };
115563
116078
  const exec2 = netdBinaryPath();
115564
- if (exec2 && process.platform === "win32" && !existsSync42(join81(dirname36(exec2), "wintun.dll"))) {
116079
+ if (exec2 && process.platform === "win32" && !existsSync43(join81(dirname36(exec2), "wintun.dll"))) {
115565
116080
  return {
115566
116081
  ok: false,
115567
116082
  reason: "no-binary",
@@ -115613,7 +116128,7 @@ import { platform as platform6 } from "node:os";
115613
116128
 
115614
116129
  // ../../packages/host-core/dist/service/launchd.js
115615
116130
  init_credentials();
115616
- import { writeFileSync as writeFileSync43, existsSync as existsSync43, mkdirSync as mkdirSync42, unlinkSync as unlinkSync7 } from "node:fs";
116131
+ import { writeFileSync as writeFileSync43, existsSync as existsSync44, mkdirSync as mkdirSync42, unlinkSync as unlinkSync7 } from "node:fs";
115617
116132
  import { dirname as dirname37, join as join82 } from "node:path";
115618
116133
  import { homedir as homedir13, userInfo as userInfo2 } from "node:os";
115619
116134
  function xml2(s) {
@@ -115668,6 +116183,36 @@ ${env}
115668
116183
  </plist>
115669
116184
  `;
115670
116185
  }
116186
+ const loaded = async () => {
116187
+ try {
116188
+ await run5("launchctl", ["print", serviceTarget()]);
116189
+ return true;
116190
+ } catch {
116191
+ return false;
116192
+ }
116193
+ };
116194
+ const untilGone = async () => {
116195
+ for (let i = 0; i < 25; i++) {
116196
+ if (!await loaded())
116197
+ return;
116198
+ await new Promise((r) => setTimeout(r, 200));
116199
+ }
116200
+ };
116201
+ const bootstrapWithRetry = async () => {
116202
+ let last;
116203
+ for (let i = 0; i < 5; i++) {
116204
+ try {
116205
+ await run5("launchctl", ["bootstrap", domainTarget(), plistPath()]);
116206
+ return;
116207
+ } catch (e) {
116208
+ last = e;
116209
+ if (await loaded())
116210
+ return;
116211
+ await new Promise((r) => setTimeout(r, 300 * (i + 1)));
116212
+ }
116213
+ }
116214
+ throw last instanceof Error ? last : new Error(String(last));
116215
+ };
115671
116216
  return {
115672
116217
  async install(spec) {
115673
116218
  mkdirSync42(workDirOf(spec), { recursive: true });
@@ -115676,16 +116221,20 @@ ${env}
115676
116221
  writeFileSync43(plistPath(), buildPlist2(spec));
115677
116222
  await run5("launchctl", ["bootout", serviceTarget()]).catch(() => {
115678
116223
  });
115679
- await run5("launchctl", ["bootstrap", domainTarget(), plistPath()]);
116224
+ await untilGone();
116225
+ await bootstrapWithRetry();
115680
116226
  await run5("launchctl", ["enable", serviceTarget()]).catch(() => {
115681
116227
  });
115682
116228
  await run5("launchctl", ["kickstart", "-k", serviceTarget()]).catch(() => {
115683
116229
  });
116230
+ if (!await loaded()) {
116231
+ throw new Error(`the service ${label} is not loaded after install \u2014 check \`launchctl print ${serviceTarget()}\``);
116232
+ }
115684
116233
  },
115685
116234
  async uninstall() {
115686
116235
  await run5("launchctl", ["bootout", serviceTarget()]).catch(() => {
115687
116236
  });
115688
- if (existsSync43(plistPath())) {
116237
+ if (existsSync44(plistPath())) {
115689
116238
  try {
115690
116239
  unlinkSync7(plistPath());
115691
116240
  } catch {
@@ -115696,7 +116245,7 @@ ${env}
115696
116245
  await this.install(spec);
115697
116246
  },
115698
116247
  async isInstalled() {
115699
- return existsSync43(plistPath());
116248
+ return existsSync44(plistPath());
115700
116249
  },
115701
116250
  async start() {
115702
116251
  await run5("launchctl", ["bootstrap", domainTarget(), plistPath()]).catch(() => {
@@ -115713,7 +116262,7 @@ ${env}
115713
116262
  },
115714
116263
  async logs(follow) {
115715
116264
  const f = logFile();
115716
- if (!existsSync43(f)) {
116265
+ if (!existsSync44(f)) {
115717
116266
  console.log(`No log yet at ${f} \u2014 has the service started?`);
115718
116267
  return;
115719
116268
  }
@@ -115724,7 +116273,7 @@ ${env}
115724
116273
 
115725
116274
  // ../../packages/host-core/dist/service/systemd.js
115726
116275
  init_credentials();
115727
- import { writeFileSync as writeFileSync44, existsSync as existsSync44, mkdirSync as mkdirSync43, unlinkSync as unlinkSync8 } from "node:fs";
116276
+ import { writeFileSync as writeFileSync44, existsSync as existsSync45, mkdirSync as mkdirSync43, unlinkSync as unlinkSync8 } from "node:fs";
115728
116277
  import { dirname as dirname38, join as join83 } from "node:path";
115729
116278
  import { homedir as homedir14, userInfo as userInfo3 } from "node:os";
115730
116279
  function unitVal2(v) {
@@ -115780,7 +116329,7 @@ WantedBy=default.target
115780
116329
  async uninstall() {
115781
116330
  await run5("systemctl", ["--user", "disable", "--now", unit]).catch(() => {
115782
116331
  });
115783
- if (existsSync44(unitPath())) {
116332
+ if (existsSync45(unitPath())) {
115784
116333
  try {
115785
116334
  unlinkSync8(unitPath());
115786
116335
  } catch {
@@ -115793,7 +116342,7 @@ WantedBy=default.target
115793
116342
  await this.install(spec);
115794
116343
  },
115795
116344
  async isInstalled() {
115796
- return existsSync44(unitPath());
116345
+ return existsSync45(unitPath());
115797
116346
  },
115798
116347
  async start() {
115799
116348
  await run5("systemctl", ["--user", "start", unit]);
@@ -115806,7 +116355,7 @@ WantedBy=default.target
115806
116355
  },
115807
116356
  async logs(follow) {
115808
116357
  const f = join83(daemonDir2(), "daemon.log");
115809
- if (existsSync44(f)) {
116358
+ if (existsSync45(f)) {
115810
116359
  await runInherit("tail", follow ? ["-f", f] : ["-n", "200", f]);
115811
116360
  return;
115812
116361
  }
@@ -115818,8 +116367,20 @@ WantedBy=default.target
115818
116367
 
115819
116368
  // ../../packages/host-core/dist/service/windows.js
115820
116369
  init_credentials();
115821
- import { writeFileSync as writeFileSync45, existsSync as existsSync45, mkdirSync as mkdirSync44, unlinkSync as unlinkSync9 } from "node:fs";
116370
+ import { writeFileSync as writeFileSync45, existsSync as existsSync46, mkdirSync as mkdirSync44, unlinkSync as unlinkSync9, readFileSync as readFileSync52 } from "node:fs";
115822
116371
  import { dirname as dirname39, join as join84 } from "node:path";
116372
+ function publishedDaemonPid(dir2) {
116373
+ for (const f of ["status.json", "daemon.lock"]) {
116374
+ try {
116375
+ const raw = readFileSync52(join84(dir2, f), "utf-8");
116376
+ const pid = f === "status.json" ? Number(JSON.parse(raw).pid) : Number(raw.trim());
116377
+ if (Number.isInteger(pid) && pid > 0)
116378
+ return pid;
116379
+ } catch {
116380
+ }
116381
+ }
116382
+ return null;
116383
+ }
115823
116384
  function makeWindowsTask(instanceId) {
115824
116385
  const suffix = instanceSuffix(instanceId);
115825
116386
  const task = `Fineguide\\TesseractHost${suffix ? `-${suffix}` : ""}`;
@@ -115851,14 +116412,20 @@ start "" /b ${cmd} >> "${oneLine2(spec.logPath).replace(/"/g, "")}" 2>&1\r
115851
116412
  async uninstall() {
115852
116413
  await run5("schtasks", ["/Delete", "/TN", task, "/F"]).catch(() => {
115853
116414
  });
115854
- if (existsSync45(wrapperPath())) {
116415
+ if (existsSync46(wrapperPath())) {
115855
116416
  try {
115856
116417
  unlinkSync9(wrapperPath());
115857
116418
  } catch {
115858
116419
  }
115859
116420
  }
115860
116421
  },
116422
+ // STOP FIRST. `install` alone re-creates the task and runs it, which on Windows spawns a
116423
+ // SECOND daemon beside the one that is still running (see stop()); the newcomer finds the
116424
+ // pid lock held and stands down, so "restart" restarted nothing — the old process kept
116425
+ // running the old code. That is how a machine upgraded to a fixed CLI kept exhibiting the
116426
+ // bug the upgrade fixed, and how `tesseract-agent restart` reported success while doing it.
115861
116427
  async restart(spec) {
116428
+ await this.stop();
115862
116429
  await this.install(spec);
115863
116430
  },
115864
116431
  async isInstalled() {
@@ -115872,16 +116439,54 @@ start "" /b ${cmd} >> "${oneLine2(spec.logPath).replace(/"/g, "")}" 2>&1\r
115872
116439
  async start() {
115873
116440
  await run5("schtasks", ["/Run", "/TN", task]);
115874
116441
  },
116442
+ /**
116443
+ * Stop the daemon. Not the task — the daemon.
116444
+ *
116445
+ * `schtasks /End` ends the TASK's process, and the task's process is the .cmd wrapper, which
116446
+ * exited the instant it ran `start "" /b` to detach the daemon. So /End ended a task that had
116447
+ * already finished, reported success, and the daemon ran on. Every `stop`, `restart` and
116448
+ * self-update on Windows has been a no-op against a running daemon since the wrapper was
116449
+ * written; observed directly as two node processes surviving stop, restart, and the task's
116450
+ * own /End, with start times from the original launch.
116451
+ *
116452
+ * The daemon publishes its own pid (status.json / daemon.lock), and that is what gets ended.
116453
+ * Its PARENT is ended instead when the parent is the CLI supervisor (`cli.js run`), because
116454
+ * killing only the worker makes the supervisor respawn it; `/T` takes the whole tree either
116455
+ * way. The parent is checked by command line before being touched: the lock can be held by a
116456
+ * process that is not ours to kill — the desktop app owning the machine — and a supervisor
116457
+ * that is not `cli.js` is left alone, worker only.
116458
+ *
116459
+ * Best-effort throughout, like the other controllers' stop: a daemon that is already gone
116460
+ * is the desired state, not an error.
116461
+ */
115875
116462
  async stop() {
115876
116463
  await run5("schtasks", ["/End", "/TN", task]).catch(() => {
115877
116464
  });
116465
+ const pid = publishedDaemonPid(daemonDir2());
116466
+ if (pid === null)
116467
+ return;
116468
+ let target = pid;
116469
+ try {
116470
+ const { stdout } = await run5("powershell", [
116471
+ "-NoProfile",
116472
+ "-Command",
116473
+ `$p = Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}"; if ($p) { $pp = Get-CimInstance Win32_Process -Filter "ProcessId = $($p.ParentProcessId)"; if ($pp) { "$($pp.ProcessId)|$($pp.CommandLine)" } }`
116474
+ ]);
116475
+ const [ppidRaw, cmd = ""] = String(stdout).trim().split("|");
116476
+ const ppid = Number(ppidRaw);
116477
+ if (Number.isInteger(ppid) && ppid > 0 && /cli\.js["']?\s+run\b/.test(cmd))
116478
+ target = ppid;
116479
+ } catch {
116480
+ }
116481
+ await run5("taskkill", ["/pid", String(target), "/T", "/F"]).catch(() => {
116482
+ });
115878
116483
  },
115879
116484
  async status() {
115880
116485
  await runInherit("schtasks", ["/Query", "/TN", task, "/V", "/FO", "LIST"]);
115881
116486
  },
115882
116487
  async logs(follow) {
115883
116488
  const f = logFile();
115884
- if (!existsSync45(f)) {
116489
+ if (!existsSync46(f)) {
115885
116490
  console.log(`No log yet at ${f} \u2014 has the task run?`);
115886
116491
  return;
115887
116492
  }
@@ -115913,7 +116518,7 @@ init_safe_kill();
115913
116518
  // src/auth.ts
115914
116519
  var import_qrcode_terminal = __toESM(require_main(), 1);
115915
116520
  import { randomUUID as randomUUID30 } from "node:crypto";
115916
- import { existsSync as existsSync46, rmSync as rmSync12 } from "node:fs";
116521
+ import { existsSync as existsSync47, rmSync as rmSync12 } from "node:fs";
115917
116522
  import { join as join85 } from "node:path";
115918
116523
 
115919
116524
  // src/config.ts
@@ -116116,7 +116721,7 @@ async function logout() {
116116
116721
  for (const f of ["credentials.json", "status.json", "daemon.lock"]) {
116117
116722
  try {
116118
116723
  const p = join85(daemonDir2(), f);
116119
- if (existsSync46(p)) rmSync12(p);
116724
+ if (existsSync47(p)) rmSync12(p);
116120
116725
  } catch {
116121
116726
  }
116122
116727
  }
@@ -116276,7 +116881,7 @@ function startSelfUpdater(current) {
116276
116881
  // src/service.ts
116277
116882
  import { join as join86 } from "node:path";
116278
116883
  import { platform as platform7 } from "node:os";
116279
- import { existsSync as existsSync47, readFileSync as readFileSync52 } from "node:fs";
116884
+ import { existsSync as existsSync48, readFileSync as readFileSync53 } from "node:fs";
116280
116885
  function controller(instanceId) {
116281
116886
  const c = getServiceController(instanceId);
116282
116887
  if (!c) {
@@ -116303,8 +116908,8 @@ var isAlive = (pid) => {
116303
116908
  function runningDaemonInfo() {
116304
116909
  const lock = join86(daemonDir2(), "daemon.lock");
116305
116910
  try {
116306
- if (!existsSync47(lock)) return null;
116307
- const pid = Number(readFileSync52(lock, "utf-8").trim());
116911
+ if (!existsSync48(lock)) return null;
116912
+ const pid = Number(readFileSync53(lock, "utf-8").trim());
116308
116913
  if (!pid || !isAlive(pid)) return null;
116309
116914
  return { pid, device: readCredentials()?.deviceId };
116310
116915
  } catch {
@@ -116338,7 +116943,7 @@ async function logs(instanceId, follow) {
116338
116943
  }
116339
116944
 
116340
116945
  // src/net.ts
116341
- import { readFileSync as readFileSync54 } from "node:fs";
116946
+ import { readFileSync as readFileSync55 } from "node:fs";
116342
116947
  import { randomBytes as randomBytes11 } from "node:crypto";
116343
116948
  import { hostname as hostname7 } from "node:os";
116344
116949
  import { resolve as resolve14 } from "node:path";
@@ -116403,7 +117008,7 @@ function readOrDie(label, p) {
116403
117008
  process.exit(1);
116404
117009
  }
116405
117010
  try {
116406
- return new Uint8Array(readFileSync54(resolve14(p)));
117011
+ return new Uint8Array(readFileSync55(resolve14(p)));
116407
117012
  } catch (e) {
116408
117013
  console.error(`net enroll: could not read --${label} ${p}: ${e instanceof Error ? e.message : e}`);
116409
117014
  process.exit(1);
@@ -116475,6 +117080,9 @@ async function netCommand(rest, flags) {
116475
117080
  const established = s.established ?? [];
116476
117081
  const relayed = s.relayed ?? [];
116477
117082
  console.log(`peers: ${s.peers ?? 0} known, ${established.length} with a live session`);
117083
+ if (typeof s.policyEpoch === "number") {
117084
+ console.log(`policy: epoch ${s.policyEpoch}${s.policyEpoch === 0 ? " \u2014 no policy yet, so NO traffic will pass" : ""}`);
117085
+ }
116478
117086
  if (relayed.length) {
116479
117087
  console.log(`relayed: ${relayed.length} (${relayed.join(", ")}) \u2014 these are NOT direct`);
116480
117088
  } else if (established.length) {
@@ -116954,7 +117562,7 @@ async function netCommand(rest, flags) {
116954
117562
  function pkgVersion() {
116955
117563
  try {
116956
117564
  const p = join88(dirname40(fileURLToPath8(import.meta.url)), "..", "package.json");
116957
- return JSON.parse(readFileSync55(p, "utf8")).version ?? "0.0.0";
117565
+ return JSON.parse(readFileSync56(p, "utf8")).version ?? "0.0.0";
116958
117566
  } catch {
116959
117567
  return "0.0.0";
116960
117568
  }
@@ -117200,7 +117808,7 @@ async function captureClaudeSubscription(flags) {
117200
117808
  });
117201
117809
  if (code !== 0) throw new Error(`Claude sign-in did not complete (exit ${code}).`);
117202
117810
  const file2 = join88(home, ".credentials.json");
117203
- if (existsSync49(file2)) return readFileSync55(file2, "utf8");
117811
+ if (existsSync50(file2)) return readFileSync56(file2, "utf8");
117204
117812
  if (process.platform === "darwin") {
117205
117813
  const value = execFileSync6("security", [
117206
117814
  "find-generic-password",
@@ -117282,7 +117890,7 @@ async function runnerCommand(rest, flags) {
117282
117890
  process.exit(1);
117283
117891
  }
117284
117892
  const oauthFilePath = typeof flags["oauth-file"] === "string" ? resolve15(flags["oauth-file"]) : void 0;
117285
- const oauthAuthFile = oauthFilePath ? readFileSync55(oauthFilePath, "utf8") : void 0;
117893
+ const oauthAuthFile = oauthFilePath ? readFileSync56(oauthFilePath, "utf8") : void 0;
117286
117894
  if (oauthAuthFile) {
117287
117895
  try {
117288
117896
  JSON.parse(oauthAuthFile);