tesseract-agent 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-bundle/cli.js +872 -254
- package/dist-bundle/mesh-core/package.json +1 -1
- package/package.json +1 -1
package/dist-bundle/cli.js
CHANGED
|
@@ -79502,7 +79502,7 @@ function alphabet(letters) {
|
|
|
79502
79502
|
};
|
|
79503
79503
|
}
|
|
79504
79504
|
// @__NO_SIDE_EFFECTS__
|
|
79505
|
-
function
|
|
79505
|
+
function join67(separator = "") {
|
|
79506
79506
|
astr("join", separator);
|
|
79507
79507
|
return {
|
|
79508
79508
|
encode: (from) => {
|
|
@@ -79707,7 +79707,7 @@ var init_esm = __esm({
|
|
|
79707
79707
|
convertRadix2,
|
|
79708
79708
|
radix,
|
|
79709
79709
|
radix2,
|
|
79710
|
-
join:
|
|
79710
|
+
join: join67,
|
|
79711
79711
|
padding
|
|
79712
79712
|
};
|
|
79713
79713
|
}
|
|
@@ -81254,7 +81254,7 @@ var mesh_ca_exports = {};
|
|
|
81254
81254
|
__export(mesh_ca_exports, {
|
|
81255
81255
|
netCaCommand: () => netCaCommand
|
|
81256
81256
|
});
|
|
81257
|
-
import { chmodSync as chmodSync8, existsSync as
|
|
81257
|
+
import { chmodSync as chmodSync8, existsSync as existsSync49, mkdirSync as mkdirSync45, readFileSync as readFileSync54, writeFileSync as writeFileSync46 } from "node:fs";
|
|
81258
81258
|
import { join as join87, resolve as resolve13 } from "node:path";
|
|
81259
81259
|
import { createInterface as createInterface2 } from "node:readline";
|
|
81260
81260
|
function die(msg) {
|
|
@@ -81302,12 +81302,12 @@ function ensureDir(p) {
|
|
|
81302
81302
|
async function openCa(dir2) {
|
|
81303
81303
|
const gPath = join87(dir2, "genesis.bin");
|
|
81304
81304
|
const sPath = join87(dir2, "ca.sealed");
|
|
81305
|
-
if (!
|
|
81305
|
+
if (!existsSync49(gPath) || !existsSync49(sPath)) {
|
|
81306
81306
|
die(`No mesh CA in ${dir2}. Run: tesseract-agent net init --org NAME --ca-dir ${dir2}`);
|
|
81307
81307
|
}
|
|
81308
|
-
const genesisBytes = new Uint8Array(
|
|
81308
|
+
const genesisBytes = new Uint8Array(readFileSync54(gPath));
|
|
81309
81309
|
const genesis = decodeGenesis(genesisBytes);
|
|
81310
|
-
const sealed = decodeRecoveryBundle(new Uint8Array(
|
|
81310
|
+
const sealed = decodeRecoveryBundle(new Uint8Array(readFileSync54(sPath)));
|
|
81311
81311
|
const pass = await readPassphrase("CA passphrase: ");
|
|
81312
81312
|
let seed;
|
|
81313
81313
|
try {
|
|
@@ -81323,7 +81323,7 @@ async function netCaCommand(rest, flags) {
|
|
|
81323
81323
|
if (sub === "init") {
|
|
81324
81324
|
const org = typeof flags.org === "string" ? flags.org.trim() : "";
|
|
81325
81325
|
if (!org) die("Usage: tesseract-agent net init --org NAME [--ca-dir DIR]");
|
|
81326
|
-
if (
|
|
81326
|
+
if (existsSync49(join87(caDir, "ca.sealed"))) {
|
|
81327
81327
|
die(`A mesh CA already exists in ${caDir}. Refusing to overwrite it.`);
|
|
81328
81328
|
}
|
|
81329
81329
|
const pass = await readPassphrase("New CA passphrase: ");
|
|
@@ -81481,7 +81481,7 @@ var init_mesh_ca = __esm({
|
|
|
81481
81481
|
|
|
81482
81482
|
// src/cli.ts
|
|
81483
81483
|
init_base_paths();
|
|
81484
|
-
import { existsSync as
|
|
81484
|
+
import { existsSync as existsSync50, mkdtempSync as mkdtempSync4, readFileSync as readFileSync56, rmSync as rmSync13 } from "node:fs";
|
|
81485
81485
|
import { createHash as createHash9 } from "node:crypto";
|
|
81486
81486
|
import { execFileSync as execFileSync6, spawn as spawn14 } from "node:child_process";
|
|
81487
81487
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
@@ -109557,7 +109557,7 @@ async function startLocalServer(opts) {
|
|
|
109557
109557
|
|
|
109558
109558
|
// ../../packages/host-core/dist/daemon-runtime.js
|
|
109559
109559
|
init_base_paths();
|
|
109560
|
-
import { readFileSync as readFileSync49, writeFileSync as writeFileSync40, existsSync as
|
|
109560
|
+
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
109561
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
109562
109562
|
import { join as join77 } from "node:path";
|
|
109563
109563
|
import { randomBytes as randomBytes10 } from "node:crypto";
|
|
@@ -109819,16 +109819,455 @@ async function getPeerIdentityPub(opts) {
|
|
|
109819
109819
|
// ../../packages/host-core/dist/mesh/node.js
|
|
109820
109820
|
import { createSocket as createSocket2 } from "node:dgram";
|
|
109821
109821
|
import { createRequire as createRequire4 } from "node:module";
|
|
109822
|
+
init_credentials();
|
|
109823
|
+
|
|
109824
|
+
// ../../packages/host-core/dist/mesh/bundle-gossip.js
|
|
109825
|
+
var BundleOfferLedger = class {
|
|
109826
|
+
#offered = /* @__PURE__ */ new Set();
|
|
109827
|
+
/**
|
|
109828
|
+
* Should we offer epoch `mine` to this peer, and record that we did?
|
|
109829
|
+
*
|
|
109830
|
+
* Returns false when we are not ahead (including equal — an offer would tell them
|
|
109831
|
+
* nothing), and false for a peer already offered THIS epoch. Keyed by epoch as well as
|
|
109832
|
+
* peer, so a newer bundle is offered afresh to someone we have already talked to.
|
|
109833
|
+
*/
|
|
109834
|
+
claim(peerHex, peerEpoch, mine) {
|
|
109835
|
+
if (!(mine > peerEpoch))
|
|
109836
|
+
return false;
|
|
109837
|
+
const key2 = `${peerHex}:${mine}`;
|
|
109838
|
+
if (this.#offered.has(key2))
|
|
109839
|
+
return false;
|
|
109840
|
+
this.#offered.add(key2);
|
|
109841
|
+
return true;
|
|
109842
|
+
}
|
|
109843
|
+
/**
|
|
109844
|
+
* Forget everything, because we just adopted a newer bundle and are now the node that is
|
|
109845
|
+
* ahead. Without this the ledger would still be keyed to the epoch we no longer hold —
|
|
109846
|
+
* harmless for correctness (the key carries the epoch) but it would grow without bound on
|
|
109847
|
+
* a long-lived node, one entry per peer per epoch forever.
|
|
109848
|
+
*/
|
|
109849
|
+
reset() {
|
|
109850
|
+
this.#offered.clear();
|
|
109851
|
+
}
|
|
109852
|
+
};
|
|
109853
|
+
|
|
109854
|
+
// ../../packages/host-core/dist/mesh/netd-engine.js
|
|
109855
|
+
init_dist6();
|
|
109856
|
+
|
|
109857
|
+
// ../../packages/host-core/dist/mesh/netd-client.js
|
|
109858
|
+
import { connect as connect2 } from "node:net";
|
|
109859
|
+
import { platform as platform3 } from "node:process";
|
|
109860
|
+
var NETD_WIRE_VERSION = 2;
|
|
109861
|
+
var MAX_LINE = 1024 * 1024;
|
|
109862
|
+
var TIMEOUT_MS3 = 15e3;
|
|
109863
|
+
function netdEndpoint() {
|
|
109864
|
+
if (platform3 === "win32")
|
|
109865
|
+
return "\\\\.\\pipe\\tesseract-netd";
|
|
109866
|
+
if (platform3 === "darwin")
|
|
109867
|
+
return "/var/run/tesseract-netd.sock";
|
|
109868
|
+
return "/run/tesseract-netd.sock";
|
|
109869
|
+
}
|
|
109870
|
+
function endpointOverride() {
|
|
109871
|
+
return process.env.CCB_NETD_SOCKET ?? null;
|
|
109872
|
+
}
|
|
109873
|
+
function fail2(code, detail) {
|
|
109874
|
+
return { ok: false, code, detail };
|
|
109875
|
+
}
|
|
109876
|
+
async function call(line) {
|
|
109877
|
+
const endpoint2 = endpointOverride() ?? netdEndpoint();
|
|
109878
|
+
return new Promise((resolve16) => {
|
|
109879
|
+
let settled = false;
|
|
109880
|
+
const done = (r) => {
|
|
109881
|
+
if (settled)
|
|
109882
|
+
return;
|
|
109883
|
+
settled = true;
|
|
109884
|
+
try {
|
|
109885
|
+
sock.destroy();
|
|
109886
|
+
} catch {
|
|
109887
|
+
}
|
|
109888
|
+
resolve16(r);
|
|
109889
|
+
};
|
|
109890
|
+
let sock;
|
|
109891
|
+
try {
|
|
109892
|
+
sock = connect2(endpoint2);
|
|
109893
|
+
} catch (e) {
|
|
109894
|
+
return resolve16(fail2("not-installed", e instanceof Error ? e.message : String(e)));
|
|
109895
|
+
}
|
|
109896
|
+
const timer2 = setTimeout(() => {
|
|
109897
|
+
done(fail2("timeout", `no answer from the network helper in ${TIMEOUT_MS3}ms`));
|
|
109898
|
+
}, TIMEOUT_MS3);
|
|
109899
|
+
timer2.unref?.();
|
|
109900
|
+
sock.setTimeout(TIMEOUT_MS3);
|
|
109901
|
+
sock.on("timeout", () => {
|
|
109902
|
+
clearTimeout(timer2);
|
|
109903
|
+
done(fail2("timeout", "socket idle"));
|
|
109904
|
+
});
|
|
109905
|
+
sock.on("error", (e) => {
|
|
109906
|
+
clearTimeout(timer2);
|
|
109907
|
+
const absent = e.code === "ENOENT" || e.code === "ECONNREFUSED";
|
|
109908
|
+
done(fail2(absent ? "not-installed" : "io", e.message));
|
|
109909
|
+
});
|
|
109910
|
+
let buf = "";
|
|
109911
|
+
sock.on("data", (chunk) => {
|
|
109912
|
+
buf += chunk.toString("utf8");
|
|
109913
|
+
if (buf.length > MAX_LINE) {
|
|
109914
|
+
clearTimeout(timer2);
|
|
109915
|
+
done(fail2("protocol", "the helper sent an oversized line"));
|
|
109916
|
+
return;
|
|
109917
|
+
}
|
|
109918
|
+
const nl = buf.indexOf("\n");
|
|
109919
|
+
if (nl === -1)
|
|
109920
|
+
return;
|
|
109921
|
+
clearTimeout(timer2);
|
|
109922
|
+
done({ ok: true, line: buf.slice(0, nl) });
|
|
109923
|
+
});
|
|
109924
|
+
sock.on("close", () => {
|
|
109925
|
+
clearTimeout(timer2);
|
|
109926
|
+
done(fail2("protocol", "the helper closed the connection without answering"));
|
|
109927
|
+
});
|
|
109928
|
+
sock.on("connect", () => {
|
|
109929
|
+
sock.write(`${line}
|
|
109930
|
+
`);
|
|
109931
|
+
});
|
|
109932
|
+
});
|
|
109933
|
+
}
|
|
109934
|
+
function parse3(line) {
|
|
109935
|
+
let body;
|
|
109936
|
+
try {
|
|
109937
|
+
body = JSON.parse(line);
|
|
109938
|
+
} catch {
|
|
109939
|
+
return fail2("protocol", "the helper sent something that is not JSON");
|
|
109940
|
+
}
|
|
109941
|
+
if (typeof body !== "object" || body === null) {
|
|
109942
|
+
return fail2("protocol", "the helper sent something that is not an object");
|
|
109943
|
+
}
|
|
109944
|
+
const o = body;
|
|
109945
|
+
if (o.ok === false) {
|
|
109946
|
+
const code = typeof o.code === "string" ? o.code : "refused";
|
|
109947
|
+
const detail = typeof o.detail === "string" ? o.detail : "the helper refused";
|
|
109948
|
+
return fail2(code, detail);
|
|
109949
|
+
}
|
|
109950
|
+
if (o.ok !== true)
|
|
109951
|
+
return fail2("protocol", "the helper did not say whether it succeeded");
|
|
109952
|
+
return { ok: true, body: o };
|
|
109953
|
+
}
|
|
109954
|
+
async function request(line) {
|
|
109955
|
+
const r = await call(line);
|
|
109956
|
+
return r.ok ? parse3(r.line) : r;
|
|
109957
|
+
}
|
|
109958
|
+
function hex4(b) {
|
|
109959
|
+
return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
109960
|
+
}
|
|
109961
|
+
async function netdHello() {
|
|
109962
|
+
const r = await request(JSON.stringify({ op: "hello", wireVersion: NETD_WIRE_VERSION }));
|
|
109963
|
+
if (!r.ok)
|
|
109964
|
+
return r;
|
|
109965
|
+
const v = r.body.wireVersion;
|
|
109966
|
+
if (typeof v !== "number")
|
|
109967
|
+
return fail2("protocol", "the helper did not report a wire version");
|
|
109968
|
+
if (v !== NETD_WIRE_VERSION) {
|
|
109969
|
+
return fail2("wire-version", `the helper speaks ${v}, this build speaks ${NETD_WIRE_VERSION}`);
|
|
109970
|
+
}
|
|
109971
|
+
return { ok: true, wireVersion: v };
|
|
109972
|
+
}
|
|
109973
|
+
async function netdStatus() {
|
|
109974
|
+
const r = await request(JSON.stringify({ op: "status" }));
|
|
109975
|
+
if (!r.ok)
|
|
109976
|
+
return r;
|
|
109977
|
+
const meshes = Array.isArray(r.body.meshes) ? r.body.meshes.filter((m) => typeof m === "string") : [];
|
|
109978
|
+
const iface = typeof r.body.interface === "string" ? r.body.interface : null;
|
|
109979
|
+
return { ok: true, meshes, interface: iface };
|
|
109980
|
+
}
|
|
109981
|
+
async function netdUp(args) {
|
|
109982
|
+
const r = await request(JSON.stringify({
|
|
109983
|
+
op: "up",
|
|
109984
|
+
meshId: args.meshId,
|
|
109985
|
+
genesis: hex4(args.genesis),
|
|
109986
|
+
cert: hex4(args.cert),
|
|
109987
|
+
fullTunnel: args.fullTunnel === true,
|
|
109988
|
+
...args.staticPrivate ? { staticPrivate: hex4(args.staticPrivate) } : {},
|
|
109989
|
+
...args.bundle ? { bundle: hex4(args.bundle) } : {}
|
|
109990
|
+
}));
|
|
109991
|
+
return r.ok ? { ok: true } : r;
|
|
109992
|
+
}
|
|
109993
|
+
async function netdHint(args) {
|
|
109994
|
+
const r = await request(JSON.stringify({
|
|
109995
|
+
op: "hint",
|
|
109996
|
+
meshId: args.meshId,
|
|
109997
|
+
staticPub: hex4(args.staticPub),
|
|
109998
|
+
endpoints: args.endpoints,
|
|
109999
|
+
relayed: args.relayed === true
|
|
110000
|
+
}));
|
|
110001
|
+
return r.ok ? { ok: true } : r;
|
|
110002
|
+
}
|
|
110003
|
+
async function netdBundle(meshId, bundle) {
|
|
110004
|
+
const r = await request(JSON.stringify({ op: "bundle", meshId, bundle: hex4(bundle) }));
|
|
110005
|
+
return r.ok ? { ok: true } : r;
|
|
110006
|
+
}
|
|
110007
|
+
async function netdSetExitNode(meshId, staticPub) {
|
|
110008
|
+
const r = await request(JSON.stringify({
|
|
110009
|
+
op: "setExitNode",
|
|
110010
|
+
meshId,
|
|
110011
|
+
staticPub: staticPub ? hex4(staticPub) : null
|
|
110012
|
+
}));
|
|
110013
|
+
return r.ok ? { ok: true } : r;
|
|
110014
|
+
}
|
|
110015
|
+
async function netdNodeStatus(meshId) {
|
|
110016
|
+
const r = await request(JSON.stringify({ op: "nodeStatus", meshId }));
|
|
110017
|
+
if (!r.ok)
|
|
110018
|
+
return r;
|
|
110019
|
+
const b = r.body;
|
|
110020
|
+
const num2 = (k) => typeof b[k] === "number" ? b[k] : 0;
|
|
110021
|
+
const strs = (k) => Array.isArray(b[k]) ? b[k].filter((x) => typeof x === "string") : [];
|
|
110022
|
+
if (b.node !== true)
|
|
110023
|
+
return fail2("protocol", "the helper did not answer with a node status");
|
|
110024
|
+
const pubHex = typeof b.localStaticPub === "string" ? b.localStaticPub : "";
|
|
110025
|
+
return {
|
|
110026
|
+
ok: true,
|
|
110027
|
+
node: {
|
|
110028
|
+
running: b.running === true,
|
|
110029
|
+
port: num2("port"),
|
|
110030
|
+
peers: num2("peers"),
|
|
110031
|
+
established: strs("established"),
|
|
110032
|
+
relayed: strs("relayed"),
|
|
110033
|
+
policyEpoch: num2("policyEpoch"),
|
|
110034
|
+
revoked: b.revoked === true,
|
|
110035
|
+
lastError: typeof b.lastError === "string" ? b.lastError : null,
|
|
110036
|
+
localStaticPub: Uint8Array.from(Buffer.from(pubHex, "hex")),
|
|
110037
|
+
meter: {
|
|
110038
|
+
txDirectBytes: num2("txDirectBytes"),
|
|
110039
|
+
txDirectPackets: num2("txDirectPackets"),
|
|
110040
|
+
txRelayedBytes: num2("txRelayedBytes"),
|
|
110041
|
+
txRelayedPackets: num2("txRelayedPackets"),
|
|
110042
|
+
rxDirectBytes: num2("rxDirectBytes"),
|
|
110043
|
+
rxDirectPackets: num2("rxDirectPackets"),
|
|
110044
|
+
rxRelayedBytes: num2("rxRelayedBytes"),
|
|
110045
|
+
rxRelayedPackets: num2("rxRelayedPackets"),
|
|
110046
|
+
forwardedBytes: num2("forwardedBytes"),
|
|
110047
|
+
forwardedPackets: num2("forwardedPackets")
|
|
110048
|
+
}
|
|
110049
|
+
}
|
|
110050
|
+
};
|
|
110051
|
+
}
|
|
110052
|
+
async function netdDown(meshId) {
|
|
110053
|
+
const r = await request(JSON.stringify({ op: "down", meshId }));
|
|
110054
|
+
return r.ok ? { ok: true } : r;
|
|
110055
|
+
}
|
|
110056
|
+
async function netdAvailable() {
|
|
110057
|
+
return (await netdHello()).ok;
|
|
110058
|
+
}
|
|
110059
|
+
|
|
110060
|
+
// ../../packages/host-core/dist/mesh/netd-engine.js
|
|
110061
|
+
var NETD_POLL_MS = 1e3;
|
|
110062
|
+
var NetdEngine = class {
|
|
110063
|
+
#meshDir;
|
|
110064
|
+
#meshId;
|
|
110065
|
+
#localStaticPub;
|
|
110066
|
+
#hooks;
|
|
110067
|
+
#snapshot = null;
|
|
110068
|
+
#pendingEndpoints = /* @__PURE__ */ new Map();
|
|
110069
|
+
#events = [];
|
|
110070
|
+
#seenEstablished = /* @__PURE__ */ new Set();
|
|
110071
|
+
#seenRelayed = /* @__PURE__ */ new Set();
|
|
110072
|
+
#timer = null;
|
|
110073
|
+
#announced = false;
|
|
110074
|
+
#lastError = null;
|
|
110075
|
+
#stopped = false;
|
|
110076
|
+
constructor(meshDir2, cert, hooks2) {
|
|
110077
|
+
this.#meshDir = meshDir2;
|
|
110078
|
+
this.#hooks = hooks2;
|
|
110079
|
+
const d = decodeCert(cert).details;
|
|
110080
|
+
this.#meshId = hex3(d.genesisFp);
|
|
110081
|
+
this.#localStaticPub = Buffer.from(d.staticPub);
|
|
110082
|
+
this.#timer = setInterval(() => void this.#poll(), NETD_POLL_MS);
|
|
110083
|
+
this.#timer.unref?.();
|
|
110084
|
+
void this.#poll();
|
|
110085
|
+
}
|
|
110086
|
+
/** Read netd's view now rather than waiting for the timer. Exposed for tests and for
|
|
110087
|
+
* the moment right after `up` so status does not lag a full interval. */
|
|
110088
|
+
async refresh() {
|
|
110089
|
+
await this.#poll();
|
|
110090
|
+
}
|
|
110091
|
+
async #poll() {
|
|
110092
|
+
if (this.#stopped)
|
|
110093
|
+
return;
|
|
110094
|
+
const r = await netdNodeStatus(this.#meshDir);
|
|
110095
|
+
if (!r.ok) {
|
|
110096
|
+
if (r.code !== "not-running" && r.code !== "not-installed") {
|
|
110097
|
+
this.#lastError = `netd: ${r.detail}`;
|
|
110098
|
+
}
|
|
110099
|
+
return;
|
|
110100
|
+
}
|
|
110101
|
+
const s = r.node;
|
|
110102
|
+
const prev = this.#snapshot;
|
|
110103
|
+
this.#snapshot = s;
|
|
110104
|
+
if (s.lastError)
|
|
110105
|
+
this.#lastError = s.lastError;
|
|
110106
|
+
for (const k of s.established) {
|
|
110107
|
+
if (!this.#seenEstablished.has(k)) {
|
|
110108
|
+
this.#seenEstablished.add(k);
|
|
110109
|
+
this.#events.push({ kind: "peerEstablished", staticPub: Buffer.from(k, "hex") });
|
|
110110
|
+
}
|
|
110111
|
+
}
|
|
110112
|
+
const relayedNow = new Set(s.relayed);
|
|
110113
|
+
for (const k of relayedNow) {
|
|
110114
|
+
if (!this.#seenRelayed.has(k)) {
|
|
110115
|
+
this.#seenRelayed.add(k);
|
|
110116
|
+
this.#events.push({ kind: "pathChanged", staticPub: Buffer.from(k, "hex"), detail: "relay" });
|
|
110117
|
+
}
|
|
110118
|
+
}
|
|
110119
|
+
for (const k of [...this.#seenRelayed]) {
|
|
110120
|
+
if (!relayedNow.has(k)) {
|
|
110121
|
+
this.#seenRelayed.delete(k);
|
|
110122
|
+
this.#events.push({ kind: "pathChanged", staticPub: Buffer.from(k, "hex"), detail: "direct" });
|
|
110123
|
+
}
|
|
110124
|
+
}
|
|
110125
|
+
if (s.running && s.port > 0 && !this.#announced) {
|
|
110126
|
+
this.#announced = true;
|
|
110127
|
+
this.#hooks.log?.(`mesh: node running in the network helper on UDP port ${s.port}`);
|
|
110128
|
+
this.#hooks.onUp(s.port);
|
|
110129
|
+
}
|
|
110130
|
+
void prev;
|
|
110131
|
+
}
|
|
110132
|
+
// ── MeshEngineNative ────────────────────────────────────────────────────────────
|
|
110133
|
+
setBundle(b, _nowMs) {
|
|
110134
|
+
void netdBundle(this.#meshDir, b).then((r) => {
|
|
110135
|
+
if (!r.ok)
|
|
110136
|
+
this.#lastError = `bundle: ${r.detail}`;
|
|
110137
|
+
});
|
|
110138
|
+
}
|
|
110139
|
+
addPeer(staticPub, _overlayV4, _overlayV6) {
|
|
110140
|
+
const k = staticPub.toString("hex");
|
|
110141
|
+
if (!this.#pendingEndpoints.has(k))
|
|
110142
|
+
this.#pendingEndpoints.set(k, { endpoints: [], relayed: false });
|
|
110143
|
+
}
|
|
110144
|
+
addPath(staticPub, endpoint2, relayed) {
|
|
110145
|
+
const k = staticPub.toString("hex");
|
|
110146
|
+
const p = this.#pendingEndpoints.get(k) ?? { endpoints: [], relayed: false };
|
|
110147
|
+
if (!p.endpoints.includes(endpoint2))
|
|
110148
|
+
p.endpoints.push(endpoint2);
|
|
110149
|
+
p.relayed = p.relayed || relayed;
|
|
110150
|
+
this.#pendingEndpoints.set(k, p);
|
|
110151
|
+
}
|
|
110152
|
+
startHandshake(staticPub, _nowMs) {
|
|
110153
|
+
const k = staticPub.toString("hex");
|
|
110154
|
+
const p = this.#pendingEndpoints.get(k) ?? { endpoints: [], relayed: false };
|
|
110155
|
+
this.#pendingEndpoints.delete(k);
|
|
110156
|
+
void netdHint({ meshId: this.#meshDir, staticPub, endpoints: p.endpoints, relayed: p.relayed }).then((r) => {
|
|
110157
|
+
if (!r.ok)
|
|
110158
|
+
this.#lastError = `hint: ${r.detail}`;
|
|
110159
|
+
});
|
|
110160
|
+
}
|
|
110161
|
+
/** No UDP here — netd owns the socket. Never called in practice; kept for the interface. */
|
|
110162
|
+
injectUdp(_pkt, _from, _nowMs) {
|
|
110163
|
+
}
|
|
110164
|
+
tick(_nowMs) {
|
|
110165
|
+
}
|
|
110166
|
+
pollSend() {
|
|
110167
|
+
return null;
|
|
110168
|
+
}
|
|
110169
|
+
pollEvent() {
|
|
110170
|
+
return this.#events.shift() ?? null;
|
|
110171
|
+
}
|
|
110172
|
+
meter() {
|
|
110173
|
+
const m = this.#snapshot?.meter;
|
|
110174
|
+
const tx = (m?.txDirectBytes ?? 0) + (m?.txRelayedBytes ?? 0);
|
|
110175
|
+
return {
|
|
110176
|
+
txDirectBytes: m?.txDirectBytes ?? 0,
|
|
110177
|
+
txRelayedBytes: m?.txRelayedBytes ?? 0,
|
|
110178
|
+
rxDirectBytes: m?.rxDirectBytes ?? 0,
|
|
110179
|
+
rxRelayedBytes: m?.rxRelayedBytes ?? 0,
|
|
110180
|
+
forwardedBytes: m?.forwardedBytes ?? 0,
|
|
110181
|
+
forwardedPackets: m?.forwardedPackets ?? 0,
|
|
110182
|
+
relayedTxFraction: tx === 0 ? 0 : (m?.txRelayedBytes ?? 0) / tx
|
|
110183
|
+
};
|
|
110184
|
+
}
|
|
110185
|
+
localStaticPub() {
|
|
110186
|
+
return this.#localStaticPub;
|
|
110187
|
+
}
|
|
110188
|
+
meshId() {
|
|
110189
|
+
return this.#meshId;
|
|
110190
|
+
}
|
|
110191
|
+
policyEpoch() {
|
|
110192
|
+
return this.#snapshot?.policyEpoch ?? 0;
|
|
110193
|
+
}
|
|
110194
|
+
selfRevoked() {
|
|
110195
|
+
return this.#snapshot?.revoked === true;
|
|
110196
|
+
}
|
|
110197
|
+
peerCount() {
|
|
110198
|
+
return this.#snapshot?.peers ?? 0;
|
|
110199
|
+
}
|
|
110200
|
+
setExitNode(staticPub) {
|
|
110201
|
+
void netdSetExitNode(this.#meshDir, staticPub).then((r) => {
|
|
110202
|
+
if (!r.ok)
|
|
110203
|
+
this.#lastError = `exit node: ${r.detail}`;
|
|
110204
|
+
});
|
|
110205
|
+
}
|
|
110206
|
+
/** Is netd running the node right now, and on which port? */
|
|
110207
|
+
get running() {
|
|
110208
|
+
return this.#snapshot?.running === true;
|
|
110209
|
+
}
|
|
110210
|
+
get port() {
|
|
110211
|
+
return this.#snapshot && this.#snapshot.port > 0 ? this.#snapshot.port : null;
|
|
110212
|
+
}
|
|
110213
|
+
get lastError() {
|
|
110214
|
+
return this.#lastError;
|
|
110215
|
+
}
|
|
110216
|
+
stop() {
|
|
110217
|
+
this.#stopped = true;
|
|
110218
|
+
if (this.#timer) {
|
|
110219
|
+
clearInterval(this.#timer);
|
|
110220
|
+
this.#timer = null;
|
|
110221
|
+
}
|
|
110222
|
+
}
|
|
110223
|
+
};
|
|
110224
|
+
|
|
110225
|
+
// ../../packages/host-core/dist/mesh/node.js
|
|
110226
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
110227
|
+
|
|
110228
|
+
// ../../packages/host-core/dist/mesh/bundle-refresh.js
|
|
110229
|
+
init_dist6();
|
|
110230
|
+
var CONTEXT = "tsr-bundle-fetch-v1";
|
|
110231
|
+
var REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
110232
|
+
async function fetchBundle(args) {
|
|
110233
|
+
const none = { bundle: null, epoch: null };
|
|
110234
|
+
try {
|
|
110235
|
+
const ts = Date.now();
|
|
110236
|
+
const { privateKey } = await signingKeyFromSeed(args.sigPrivate);
|
|
110237
|
+
const message = new TextEncoder().encode(`${CONTEXT}
|
|
110238
|
+
${args.genesisFp.toLowerCase()}
|
|
110239
|
+
${args.staticPub.toLowerCase()}
|
|
110240
|
+
${ts}`);
|
|
110241
|
+
const sig = await sign2(privateKey, message);
|
|
110242
|
+
const doFetch = args.fetchImpl ?? fetch;
|
|
110243
|
+
const res = await doFetch(`${args.origin.replace(/\/$/, "")}/api/core/tesseract/mesh/meshes/by-genesis/${encodeURIComponent(args.genesisFp.toLowerCase())}/bundle`, {
|
|
110244
|
+
method: "GET",
|
|
110245
|
+
headers: {
|
|
110246
|
+
"x-tsr-node": args.staticPub.toLowerCase(),
|
|
110247
|
+
"x-tsr-ts": String(ts),
|
|
110248
|
+
"x-tsr-sig": Buffer.from(sig).toString("base64")
|
|
110249
|
+
}
|
|
110250
|
+
});
|
|
110251
|
+
if (!res.ok)
|
|
110252
|
+
return none;
|
|
110253
|
+
const body = await res.json();
|
|
110254
|
+
if (!body.bundle)
|
|
110255
|
+
return none;
|
|
110256
|
+
return { bundle: Uint8Array.from(Buffer.from(body.bundle, "hex")), epoch: body.epoch ?? null };
|
|
110257
|
+
} catch {
|
|
110258
|
+
return none;
|
|
110259
|
+
}
|
|
110260
|
+
}
|
|
109822
110261
|
|
|
109823
110262
|
// ../../packages/host-core/dist/mesh/config.js
|
|
109824
110263
|
init_credentials();
|
|
109825
110264
|
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";
|
|
109826
|
-
import { dirname as dirname31, join as
|
|
110265
|
+
import { dirname as dirname31, join as join68 } from "node:path";
|
|
109827
110266
|
function meshDir() {
|
|
109828
|
-
return
|
|
110267
|
+
return join68(daemonDir2(), "mesh");
|
|
109829
110268
|
}
|
|
109830
110269
|
function meshesDir() {
|
|
109831
|
-
return
|
|
110270
|
+
return join68(meshDir(), "meshes");
|
|
109832
110271
|
}
|
|
109833
110272
|
var MESH_DIR_NAME_BYTES = 8;
|
|
109834
110273
|
function meshDirName(genesisFp) {
|
|
@@ -109846,7 +110285,7 @@ function oneMeshDir(meshId) {
|
|
|
109846
110285
|
if (!isMeshId(meshId)) {
|
|
109847
110286
|
throw new Error(`not a mesh id: ${JSON.stringify(meshId.slice(0, 64))}`);
|
|
109848
110287
|
}
|
|
109849
|
-
return
|
|
110288
|
+
return join68(meshesDir(), meshId);
|
|
109850
110289
|
}
|
|
109851
110290
|
var LEGACY_MESH_ID = "legacy";
|
|
109852
110291
|
var CERT_GENESIS_FP_OFFSET = 19;
|
|
@@ -109864,7 +110303,7 @@ var FILES = {
|
|
|
109864
110303
|
local: "local.json"
|
|
109865
110304
|
};
|
|
109866
110305
|
function readIfPresent(name, dir2 = meshDir()) {
|
|
109867
|
-
const p =
|
|
110306
|
+
const p = join68(dir2, name);
|
|
109868
110307
|
try {
|
|
109869
110308
|
return existsSync33(p) ? new Uint8Array(readFileSync47(p)) : void 0;
|
|
109870
110309
|
} catch {
|
|
@@ -109872,7 +110311,7 @@ function readIfPresent(name, dir2 = meshDir()) {
|
|
|
109872
110311
|
}
|
|
109873
110312
|
}
|
|
109874
110313
|
function migrateSingleMesh() {
|
|
109875
|
-
const legacyGenesis =
|
|
110314
|
+
const legacyGenesis = join68(meshDir(), FILES.genesis);
|
|
109876
110315
|
if (!existsSync33(legacyGenesis))
|
|
109877
110316
|
return null;
|
|
109878
110317
|
const id = meshIdFromCert(readIfPresent(FILES.cert)) ?? LEGACY_MESH_ID;
|
|
@@ -109881,11 +110320,11 @@ function migrateSingleMesh() {
|
|
|
109881
110320
|
mkdirSync36(dest, { recursive: true, mode: 448 });
|
|
109882
110321
|
chmodSync5(dest, 448);
|
|
109883
110322
|
for (const f of Object.values(FILES)) {
|
|
109884
|
-
const from =
|
|
110323
|
+
const from = join68(meshDir(), f);
|
|
109885
110324
|
if (existsSync33(from))
|
|
109886
|
-
renameSync11(from,
|
|
110325
|
+
renameSync11(from, join68(dest, f));
|
|
109887
110326
|
}
|
|
109888
|
-
const key2 =
|
|
110327
|
+
const key2 = join68(dest, FILES.key);
|
|
109889
110328
|
if (existsSync33(key2))
|
|
109890
110329
|
chmodSync5(key2, 384);
|
|
109891
110330
|
} catch {
|
|
@@ -109909,7 +110348,7 @@ function listMeshIds() {
|
|
|
109909
110348
|
}
|
|
109910
110349
|
function readLocal(meshId) {
|
|
109911
110350
|
try {
|
|
109912
|
-
return JSON.parse(readFileSync47(
|
|
110351
|
+
return JSON.parse(readFileSync47(join68(oneMeshDir(meshId), FILES.local), "utf8"));
|
|
109913
110352
|
} catch {
|
|
109914
110353
|
return null;
|
|
109915
110354
|
}
|
|
@@ -109917,7 +110356,7 @@ function readLocal(meshId) {
|
|
|
109917
110356
|
function writeLocal(meshId, state3) {
|
|
109918
110357
|
try {
|
|
109919
110358
|
mkdirSync36(oneMeshDir(meshId), { recursive: true, mode: 448 });
|
|
109920
|
-
writeFileSync35(
|
|
110359
|
+
writeFileSync35(join68(oneMeshDir(meshId), FILES.local), JSON.stringify(state3, null, 2), { mode: 384 });
|
|
109921
110360
|
} catch {
|
|
109922
110361
|
}
|
|
109923
110362
|
}
|
|
@@ -109950,7 +110389,7 @@ function loadMeshIdentity() {
|
|
|
109950
110389
|
return first === void 0 ? null : loadMesh(first);
|
|
109951
110390
|
}
|
|
109952
110391
|
function writeSecret(name, bytes2, dir2 = meshDir()) {
|
|
109953
|
-
const p =
|
|
110392
|
+
const p = join68(dir2, name);
|
|
109954
110393
|
writeFileSync35(p, bytes2, { mode: 384 });
|
|
109955
110394
|
try {
|
|
109956
110395
|
chmodSync5(p, 384);
|
|
@@ -109968,11 +110407,11 @@ function saveMeshIdentity(id) {
|
|
|
109968
110407
|
chmodSync5(meshDir(), 448);
|
|
109969
110408
|
} catch {
|
|
109970
110409
|
}
|
|
109971
|
-
writeFileSync35(
|
|
109972
|
-
writeFileSync35(
|
|
110410
|
+
writeFileSync35(join68(meshDir(), FILES.genesis), id.genesis);
|
|
110411
|
+
writeFileSync35(join68(meshDir(), FILES.cert), id.cert);
|
|
109973
110412
|
writeSecret(FILES.key, id.staticPrivate);
|
|
109974
110413
|
if (id.bundle)
|
|
109975
|
-
writeFileSync35(
|
|
110414
|
+
writeFileSync35(join68(meshDir(), FILES.bundle), id.bundle);
|
|
109976
110415
|
}
|
|
109977
110416
|
function saveMesh(meshId, id) {
|
|
109978
110417
|
const dir2 = oneMeshDir(meshId);
|
|
@@ -109989,13 +110428,13 @@ function saveMesh(meshId, id) {
|
|
|
109989
110428
|
chmodSync5(dir2, 448);
|
|
109990
110429
|
} catch {
|
|
109991
110430
|
}
|
|
109992
|
-
writeFileSync35(
|
|
109993
|
-
writeFileSync35(
|
|
110431
|
+
writeFileSync35(join68(dir2, FILES.genesis), id.genesis);
|
|
110432
|
+
writeFileSync35(join68(dir2, FILES.cert), id.cert);
|
|
109994
110433
|
writeSecret(FILES.key, id.staticPrivate, dir2);
|
|
109995
110434
|
if (id.sigPrivate)
|
|
109996
110435
|
writeSecret(FILES.sigKey, id.sigPrivate, dir2);
|
|
109997
110436
|
if (id.bundle)
|
|
109998
|
-
writeFileSync35(
|
|
110437
|
+
writeFileSync35(join68(dir2, FILES.bundle), id.bundle);
|
|
109999
110438
|
if (readLocal(meshId) === null)
|
|
110000
110439
|
writeLocal(meshId, { joinedAt: Date.now() });
|
|
110001
110440
|
}
|
|
@@ -110005,7 +110444,7 @@ function removeMesh(meshId) {
|
|
|
110005
110444
|
var STATUS_REFRESH_MS = 5e3;
|
|
110006
110445
|
var STATUS_STALE_MS = 2e4;
|
|
110007
110446
|
function statusPath(meshId) {
|
|
110008
|
-
return meshId === void 0 ?
|
|
110447
|
+
return meshId === void 0 ? join68(meshDir(), "status.json") : join68(oneMeshDir(meshId), "status.json");
|
|
110009
110448
|
}
|
|
110010
110449
|
function writeStatus(status2, meshId) {
|
|
110011
110450
|
try {
|
|
@@ -110036,7 +110475,7 @@ function cacheBundle(bundle, meshId) {
|
|
|
110036
110475
|
const id = meshId ?? listMeshIds()[0];
|
|
110037
110476
|
const dir2 = id === void 0 ? meshDir() : oneMeshDir(id);
|
|
110038
110477
|
mkdirSync36(dir2, { recursive: true, mode: 448 });
|
|
110039
|
-
writeFileSync35(
|
|
110478
|
+
writeFileSync35(join68(dir2, FILES.bundle), bundle);
|
|
110040
110479
|
} catch {
|
|
110041
110480
|
}
|
|
110042
110481
|
}
|
|
@@ -110192,6 +110631,9 @@ function loadMeshCore() {
|
|
|
110192
110631
|
}
|
|
110193
110632
|
var meshCore = loadMeshCore();
|
|
110194
110633
|
function meshEngineStatus() {
|
|
110634
|
+
if (process.env.CCB_MESH_IN_DAEMON !== "1" && existsSync34(netdEndpoint())) {
|
|
110635
|
+
return { available: true, reason: null };
|
|
110636
|
+
}
|
|
110195
110637
|
return { available: meshCore.available === true, reason: meshCore.loadError ?? null };
|
|
110196
110638
|
}
|
|
110197
110639
|
var TICK_MS2 = 1e3;
|
|
@@ -110287,8 +110729,16 @@ var MeshNode = class {
|
|
|
110287
110729
|
#engine = null;
|
|
110288
110730
|
#sock = null;
|
|
110289
110731
|
#timer = null;
|
|
110732
|
+
/** The policy refresh poll. See `bundle-refresh.ts` — this is what makes eviction real. */
|
|
110733
|
+
#refreshTimer = null;
|
|
110290
110734
|
#unsubscribe = null;
|
|
110291
110735
|
#identity = null;
|
|
110736
|
+
/** The policy bundle we hold, as bytes, so we can OFFER it to a peer that is behind.
|
|
110737
|
+
* Kept beside the engine because the engine exposes an epoch but not the bytes. */
|
|
110738
|
+
#bundle = null;
|
|
110739
|
+
/** Who has already been offered which epoch — see `bundle-gossip.ts` for why a ledger
|
|
110740
|
+
* is needed at all (the relay broadcasts to the whole mesh, not to one peer). */
|
|
110741
|
+
#bundleOffered = new BundleOfferLedger();
|
|
110292
110742
|
#port = null;
|
|
110293
110743
|
#lastError = null;
|
|
110294
110744
|
/**
|
|
@@ -110353,8 +110803,11 @@ var MeshNode = class {
|
|
|
110353
110803
|
}
|
|
110354
110804
|
status() {
|
|
110355
110805
|
return {
|
|
110356
|
-
|
|
110357
|
-
|
|
110806
|
+
// In netd mode the engine is the helper's, and whether THIS process could load a
|
|
110807
|
+
// prebuilt is beside the point — a CLI install whose native fetch failed still has a
|
|
110808
|
+
// working network when the helper runs it, and must not say "engine unavailable".
|
|
110809
|
+
available: this.#engine instanceof NetdEngine ? true : meshCore.available,
|
|
110810
|
+
loadError: this.#engine instanceof NetdEngine ? null : meshCore.loadError,
|
|
110358
110811
|
enrolled: this.#identity !== null || this.#loadIdentity() !== null,
|
|
110359
110812
|
running: this.#engine !== null,
|
|
110360
110813
|
port: this.#port,
|
|
@@ -110371,7 +110824,22 @@ var MeshNode = class {
|
|
|
110371
110824
|
this.#established.has(hex6) ? this.#relayed.has(hex6) ? "relay" : "direct" : "none"
|
|
110372
110825
|
])),
|
|
110373
110826
|
meter: this.#engine?.meter() ?? null,
|
|
110374
|
-
|
|
110827
|
+
// THE POLICY EPOCH this node is actually running on.
|
|
110828
|
+
//
|
|
110829
|
+
// Read from the ENGINE, never from `bundle.bin`: the engine installs a bundle the
|
|
110830
|
+
// moment it verifies one, and a bundle pulled from a peer over the Noise session
|
|
110831
|
+
// never touches that file. Judging freshness by the file — which is what there was
|
|
110832
|
+
// to judge by — therefore reports a node as stale when it is current, and is how a
|
|
110833
|
+
// machine came to look a dozen epochs behind while carrying the right policy.
|
|
110834
|
+
//
|
|
110835
|
+
// It is the one number that answers "why is nothing passing": a node whose policy
|
|
110836
|
+
// predates a peer has no roster entry for it and drops every packet, correctly, with
|
|
110837
|
+
// nothing anywhere saying so.
|
|
110838
|
+
policyEpoch: this.#engine?.policyEpoch() ?? null,
|
|
110839
|
+
lastError: this.#lastError ?? (this.#engine instanceof NetdEngine ? this.#engine.lastError : null),
|
|
110840
|
+
// In netd mode "running" means netd runs it: an adapter with no node behind it yet
|
|
110841
|
+
// (routes not synced, helper just installed) is not a running node.
|
|
110842
|
+
...this.#engine instanceof NetdEngine && !this.#engine.running ? { running: false } : {},
|
|
110375
110843
|
// Surfaced so `net status` can say "removed" rather than "not running". Those look
|
|
110376
110844
|
// identical from outside and have completely different remedies.
|
|
110377
110845
|
removed: this.#revoked
|
|
@@ -110393,7 +110861,8 @@ var MeshNode = class {
|
|
|
110393
110861
|
reason: "this machine was removed from the network \u2014 ask an admin for a new invitation"
|
|
110394
110862
|
};
|
|
110395
110863
|
}
|
|
110396
|
-
|
|
110864
|
+
const inNetd = process.env.CCB_MESH_IN_DAEMON !== "1" && existsSync34(netdEndpoint());
|
|
110865
|
+
if (!inNetd && !meshCore.available) {
|
|
110397
110866
|
return { ok: false, reason: `mesh engine unavailable: ${meshCore.loadError ?? "no prebuilt"}` };
|
|
110398
110867
|
}
|
|
110399
110868
|
const id = this.#loadIdentity();
|
|
@@ -110403,18 +110872,46 @@ var MeshNode = class {
|
|
|
110403
110872
|
reason: this.#wantMeshId === null ? "this machine is not enrolled in a mesh" : `this machine is not enrolled in mesh ${this.#wantMeshId}`
|
|
110404
110873
|
};
|
|
110405
110874
|
}
|
|
110406
|
-
|
|
110407
|
-
|
|
110408
|
-
if (
|
|
110409
|
-
|
|
110410
|
-
|
|
110411
|
-
|
|
110412
|
-
|
|
110413
|
-
|
|
110875
|
+
if (inNetd) {
|
|
110876
|
+
const meshDir2 = this.#wantMeshId ?? listMeshIds()[0] ?? null;
|
|
110877
|
+
if (!meshDir2)
|
|
110878
|
+
return { ok: false, reason: "this machine is not enrolled in a mesh" };
|
|
110879
|
+
try {
|
|
110880
|
+
this.#engine = new NetdEngine(meshDir2, id.cert, {
|
|
110881
|
+
// netd reports the node up: now this side has a port to advertise, and can
|
|
110882
|
+
// publish itself to the relay and the LAN. Before that there is nothing to say.
|
|
110883
|
+
onUp: (port) => {
|
|
110884
|
+
this.#port = port;
|
|
110885
|
+
this.#join();
|
|
110886
|
+
this.#startLan();
|
|
110887
|
+
this.#publishStatus();
|
|
110888
|
+
}
|
|
110889
|
+
});
|
|
110890
|
+
if (id.bundle)
|
|
110891
|
+
this.#bundle = id.bundle;
|
|
110892
|
+
} catch (e) {
|
|
110893
|
+
this.#engine = null;
|
|
110894
|
+
this.#lastError = e instanceof Error ? e.message : String(e);
|
|
110895
|
+
return { ok: false, reason: this.#lastError };
|
|
110896
|
+
}
|
|
110897
|
+
} else {
|
|
110898
|
+
try {
|
|
110899
|
+
this.#engine = new meshCore.MeshEngine(Buffer.from(id.genesis), Buffer.from(id.staticPrivate), Buffer.from(id.cert), 0, Buffer.alloc(16));
|
|
110900
|
+
if (id.bundle) {
|
|
110901
|
+
this.#engine.setBundle(Buffer.from(id.bundle), Date.now());
|
|
110902
|
+
this.#bundle = id.bundle;
|
|
110903
|
+
}
|
|
110904
|
+
} catch (e) {
|
|
110905
|
+
this.#engine = null;
|
|
110906
|
+
this.#lastError = e instanceof Error ? e.message : String(e);
|
|
110907
|
+
return { ok: false, reason: this.#lastError };
|
|
110908
|
+
}
|
|
110414
110909
|
}
|
|
110415
110910
|
this.#identity = id;
|
|
110416
|
-
|
|
110417
|
-
|
|
110911
|
+
if (!inNetd) {
|
|
110912
|
+
this.#openSocket();
|
|
110913
|
+
this.#startLan();
|
|
110914
|
+
}
|
|
110418
110915
|
this.#offRejoin = onMeshRejoin(() => this.#join());
|
|
110419
110916
|
this.#publishStatus();
|
|
110420
110917
|
this.#statusTimer = setInterval(() => this.#publishStatus(), STATUS_REFRESH_MS);
|
|
@@ -110422,6 +110919,9 @@ var MeshNode = class {
|
|
|
110422
110919
|
this.#subscribe();
|
|
110423
110920
|
this.#timer = setInterval(() => this.#tick(), TICK_MS2);
|
|
110424
110921
|
this.#timer.unref?.();
|
|
110922
|
+
void this.#refreshBundle();
|
|
110923
|
+
this.#refreshTimer = setInterval(() => void this.#refreshBundle(), REFRESH_INTERVAL_MS);
|
|
110924
|
+
this.#refreshTimer.unref?.();
|
|
110425
110925
|
return { ok: true };
|
|
110426
110926
|
}
|
|
110427
110927
|
/** Tear down. Idempotent, and safe to call from a signal handler. */
|
|
@@ -110430,6 +110930,16 @@ var MeshNode = class {
|
|
|
110430
110930
|
clearInterval(this.#timer);
|
|
110431
110931
|
this.#timer = null;
|
|
110432
110932
|
}
|
|
110933
|
+
if (this.#refreshTimer) {
|
|
110934
|
+
clearInterval(this.#refreshTimer);
|
|
110935
|
+
this.#refreshTimer = null;
|
|
110936
|
+
}
|
|
110937
|
+
if (this.#engine instanceof NetdEngine) {
|
|
110938
|
+
try {
|
|
110939
|
+
this.#engine.stop();
|
|
110940
|
+
} catch {
|
|
110941
|
+
}
|
|
110942
|
+
}
|
|
110433
110943
|
this.#unsubscribe?.();
|
|
110434
110944
|
this.#unsubscribe = null;
|
|
110435
110945
|
this.#offRejoin?.();
|
|
@@ -110529,19 +111039,27 @@ var MeshNode = class {
|
|
|
110529
111039
|
staticPub: engine.localStaticPub(),
|
|
110530
111040
|
port: () => this.#port,
|
|
110531
111041
|
epoch: () => engine.policyEpoch(),
|
|
110532
|
-
onPeer: (staticPub, address, port) => this.#learnLanPeer(staticPub, address, port)
|
|
111042
|
+
onPeer: (staticPub, address, port, epoch2) => this.#learnLanPeer(staticPub, address, port, epoch2)
|
|
110533
111043
|
});
|
|
110534
111044
|
this.#lan.start();
|
|
110535
111045
|
} catch {
|
|
110536
111046
|
this.#lan = null;
|
|
110537
111047
|
}
|
|
110538
111048
|
}
|
|
110539
|
-
/** A beacon arrived. Treated exactly like a relay hint — a candidate, not a fact.
|
|
110540
|
-
|
|
111049
|
+
/** A beacon arrived. Treated exactly like a relay hint — a candidate, not a fact.
|
|
111050
|
+
*
|
|
111051
|
+
* Including its EPOCH. The beacon has always carried one (`lan.ts`: "port(2) | epoch(8)")
|
|
111052
|
+
* for the same reason the relay hint does — so a peer that is behind can be brought up
|
|
111053
|
+
* to date — and, like the relay hint's, it was thrown away here. Two laptops on one
|
|
111054
|
+
* Wi-Fi with the internet down are the demonstration that the overlay is not
|
|
111055
|
+
* relay-dependent; without this they would find each other and then never agree on who
|
|
111056
|
+
* else exists. */
|
|
111057
|
+
#learnLanPeer(staticPub, address, port, epoch2 = 0) {
|
|
110541
111058
|
const engine = this.#engine;
|
|
110542
111059
|
if (!engine)
|
|
110543
111060
|
return;
|
|
110544
111061
|
const hex6 = staticPub.toString("hex");
|
|
111062
|
+
this.#offerBundleTo(hex6, epoch2);
|
|
110545
111063
|
try {
|
|
110546
111064
|
if (!this.#peers.has(hex6)) {
|
|
110547
111065
|
engine.addPeer(staticPub, 0, Buffer.alloc(16));
|
|
@@ -110653,6 +111171,8 @@ var MeshNode = class {
|
|
|
110653
111171
|
engine.setBundle(bytes2, Date.now());
|
|
110654
111172
|
this.#stopIfRevoked(engine);
|
|
110655
111173
|
cacheBundle(new Uint8Array(bytes2), meshDirNameFromWireId(engine.meshId()) ?? void 0);
|
|
111174
|
+
this.#bundle = new Uint8Array(bytes2);
|
|
111175
|
+
this.#bundleOffered.reset();
|
|
110656
111176
|
break;
|
|
110657
111177
|
}
|
|
110658
111178
|
}
|
|
@@ -110661,6 +111181,89 @@ var MeshNode = class {
|
|
|
110661
111181
|
this.#lastError = e instanceof Error ? e.message : String(e);
|
|
110662
111182
|
}
|
|
110663
111183
|
}
|
|
111184
|
+
/**
|
|
111185
|
+
* Ask the server for current policy, and adopt it if it is newer.
|
|
111186
|
+
*
|
|
111187
|
+
* The one path that can deliver an EVICTION. Gossip only ever moves a network to the
|
|
111188
|
+
* newest bundle some connected node already holds, and after an eviction no node holds
|
|
111189
|
+
* the new one — so without this, "remove that laptop" changed a database row and nothing
|
|
111190
|
+
* else until somebody happened to join.
|
|
111191
|
+
*
|
|
111192
|
+
* Applied through exactly the same steps as a gossiped bundle, deliberately: the engine
|
|
111193
|
+
* verifies it against genesis (so a hostile server cannot widen policy any more than a
|
|
111194
|
+
* hostile relay can), `#stopIfRevoked` reads our own serial out of it, it is cached to
|
|
111195
|
+
* disk, and the offer ledger resets so our peers hear about it too. That last part is
|
|
111196
|
+
* what makes one node's poll benefit the whole network.
|
|
111197
|
+
*/
|
|
111198
|
+
async #refreshBundle() {
|
|
111199
|
+
const engine = this.#engine;
|
|
111200
|
+
const id = this.#identity;
|
|
111201
|
+
if (!engine || !id?.sigPrivate)
|
|
111202
|
+
return;
|
|
111203
|
+
const meshDir2 = meshDirNameFromWireId(engine.meshId());
|
|
111204
|
+
const origin = (meshDir2 ? readLocal(meshDir2)?.origin : void 0) ?? readCredentials()?.apiOrigin;
|
|
111205
|
+
if (!origin)
|
|
111206
|
+
return;
|
|
111207
|
+
const got = await fetchBundle({
|
|
111208
|
+
origin,
|
|
111209
|
+
genesisFp: engine.meshId(),
|
|
111210
|
+
staticPub: Buffer.from(engine.localStaticPub()).toString("hex"),
|
|
111211
|
+
sigPrivate: id.sigPrivate
|
|
111212
|
+
});
|
|
111213
|
+
if (!got.bundle)
|
|
111214
|
+
return;
|
|
111215
|
+
try {
|
|
111216
|
+
engine.setBundle(Buffer.from(got.bundle), Date.now());
|
|
111217
|
+
} catch {
|
|
111218
|
+
return;
|
|
111219
|
+
}
|
|
111220
|
+
this.#stopIfRevoked(engine);
|
|
111221
|
+
cacheBundle(got.bundle, meshDir2 ?? void 0);
|
|
111222
|
+
this.#bundle = got.bundle;
|
|
111223
|
+
this.#bundleOffered.reset();
|
|
111224
|
+
}
|
|
111225
|
+
/**
|
|
111226
|
+
* Offer our policy bundle to a peer whose epoch is older than ours.
|
|
111227
|
+
*
|
|
111228
|
+
* ## The half of gossip that was never written
|
|
111229
|
+
*
|
|
111230
|
+
* `MeshPeerHint.epoch` is documented in the protocol as "the epoch the node advertises,
|
|
111231
|
+
* SO A LAGGING PEER KNOWS TO PULL", and the relay has always carried it. Nothing read
|
|
111232
|
+
* it, and — the part that made this total — nothing in the product ever SENT a
|
|
111233
|
+
* `mesh:bundle`. The broker forwarded a message with no sender anywhere.
|
|
111234
|
+
*
|
|
111235
|
+
* So a node's roster was frozen at the instant it joined. `joinWithToken` caches the
|
|
111236
|
+
* bundle the server returns, which is why a machine works at all; but every machine that
|
|
111237
|
+
* joins LATER is invisible to it forever, because the only thing that would have told it
|
|
111238
|
+
* is the gossip that never happened. On a four-machine network built in join order, the
|
|
111239
|
+
* first machine knew nobody, the last knew everyone, and every one of them reported
|
|
111240
|
+
* itself healthy. That is what this fixes.
|
|
111241
|
+
*
|
|
111242
|
+
* PUSH, not pull, because push needs no new message type and the protocol already hands
|
|
111243
|
+
* us exactly the fact we need — the peer's epoch. For any pair, whoever is ahead offers,
|
|
111244
|
+
* so the direction takes care of itself and a new joiner (who always has the newest
|
|
111245
|
+
* bundle, straight from the server) feeds it to everyone already there.
|
|
111246
|
+
*
|
|
111247
|
+
* Safe by construction: the bundle is CA-signed, so `setBundle` on the receiving side
|
|
111248
|
+
* verifies it against genesis and refuses anything that does not chain. Offering is
|
|
111249
|
+
* therefore never a policy change — the worst a wrong offer costs a peer is a parse.
|
|
111250
|
+
*/
|
|
111251
|
+
#offerBundleTo(peerHex, peerEpoch) {
|
|
111252
|
+
const engine = this.#engine;
|
|
111253
|
+
const bundle = this.#bundle;
|
|
111254
|
+
if (!engine || !bundle)
|
|
111255
|
+
return;
|
|
111256
|
+
if (!this.#bundleOffered.claim(peerHex, peerEpoch, engine.policyEpoch()))
|
|
111257
|
+
return;
|
|
111258
|
+
try {
|
|
111259
|
+
sendMesh({
|
|
111260
|
+
type: "mesh:bundle",
|
|
111261
|
+
meshId: engine.meshId(),
|
|
111262
|
+
bundle: Buffer.from(bundle).toString("base64")
|
|
111263
|
+
});
|
|
111264
|
+
} catch {
|
|
111265
|
+
}
|
|
111266
|
+
}
|
|
110664
111267
|
#learnPeer(p) {
|
|
110665
111268
|
const engine = this.#engine;
|
|
110666
111269
|
if (!engine)
|
|
@@ -110669,6 +111272,7 @@ var MeshNode = class {
|
|
|
110669
111272
|
if (pk.length !== 32)
|
|
110670
111273
|
return;
|
|
110671
111274
|
const hex6 = pk.toString("hex");
|
|
111275
|
+
this.#offerBundleTo(hex6, p.epoch ?? 0);
|
|
110672
111276
|
if (p.cert) {
|
|
110673
111277
|
try {
|
|
110674
111278
|
const bytes2 = Buffer.from(p.cert, "base64");
|
|
@@ -110712,10 +111316,12 @@ var MeshNode = class {
|
|
|
110712
111316
|
/** Flush everything the engine wants to send, and absorb its events. */
|
|
110713
111317
|
#drain() {
|
|
110714
111318
|
const engine = this.#engine;
|
|
110715
|
-
|
|
110716
|
-
if (!engine || !sock)
|
|
111319
|
+
if (!engine)
|
|
110717
111320
|
return;
|
|
111321
|
+
const sock = this.#sock;
|
|
110718
111322
|
for (; ; ) {
|
|
111323
|
+
if (!sock)
|
|
111324
|
+
break;
|
|
110719
111325
|
const item = engine.pollSend();
|
|
110720
111326
|
if (!item)
|
|
110721
111327
|
break;
|
|
@@ -110863,148 +111469,6 @@ function meshManager() {
|
|
|
110863
111469
|
return singleton3;
|
|
110864
111470
|
}
|
|
110865
111471
|
|
|
110866
|
-
// ../../packages/host-core/dist/mesh/netd-client.js
|
|
110867
|
-
import { connect as connect2 } from "node:net";
|
|
110868
|
-
import { platform as platform3 } from "node:process";
|
|
110869
|
-
var NETD_WIRE_VERSION = 1;
|
|
110870
|
-
var MAX_LINE = 1024 * 1024;
|
|
110871
|
-
var TIMEOUT_MS3 = 15e3;
|
|
110872
|
-
function netdEndpoint() {
|
|
110873
|
-
if (platform3 === "win32")
|
|
110874
|
-
return "\\\\.\\pipe\\tesseract-netd";
|
|
110875
|
-
if (platform3 === "darwin")
|
|
110876
|
-
return "/var/run/tesseract-netd.sock";
|
|
110877
|
-
return "/run/tesseract-netd.sock";
|
|
110878
|
-
}
|
|
110879
|
-
function endpointOverride() {
|
|
110880
|
-
return process.env.CCB_NETD_SOCKET ?? null;
|
|
110881
|
-
}
|
|
110882
|
-
function fail2(code, detail) {
|
|
110883
|
-
return { ok: false, code, detail };
|
|
110884
|
-
}
|
|
110885
|
-
async function call(line) {
|
|
110886
|
-
const endpoint2 = endpointOverride() ?? netdEndpoint();
|
|
110887
|
-
return new Promise((resolve16) => {
|
|
110888
|
-
let settled = false;
|
|
110889
|
-
const done = (r) => {
|
|
110890
|
-
if (settled)
|
|
110891
|
-
return;
|
|
110892
|
-
settled = true;
|
|
110893
|
-
try {
|
|
110894
|
-
sock.destroy();
|
|
110895
|
-
} catch {
|
|
110896
|
-
}
|
|
110897
|
-
resolve16(r);
|
|
110898
|
-
};
|
|
110899
|
-
let sock;
|
|
110900
|
-
try {
|
|
110901
|
-
sock = connect2(endpoint2);
|
|
110902
|
-
} catch (e) {
|
|
110903
|
-
return resolve16(fail2("not-installed", e instanceof Error ? e.message : String(e)));
|
|
110904
|
-
}
|
|
110905
|
-
const timer2 = setTimeout(() => {
|
|
110906
|
-
done(fail2("timeout", `no answer from the network helper in ${TIMEOUT_MS3}ms`));
|
|
110907
|
-
}, TIMEOUT_MS3);
|
|
110908
|
-
timer2.unref?.();
|
|
110909
|
-
sock.setTimeout(TIMEOUT_MS3);
|
|
110910
|
-
sock.on("timeout", () => {
|
|
110911
|
-
clearTimeout(timer2);
|
|
110912
|
-
done(fail2("timeout", "socket idle"));
|
|
110913
|
-
});
|
|
110914
|
-
sock.on("error", (e) => {
|
|
110915
|
-
clearTimeout(timer2);
|
|
110916
|
-
const absent = e.code === "ENOENT" || e.code === "ECONNREFUSED";
|
|
110917
|
-
done(fail2(absent ? "not-installed" : "io", e.message));
|
|
110918
|
-
});
|
|
110919
|
-
let buf = "";
|
|
110920
|
-
sock.on("data", (chunk) => {
|
|
110921
|
-
buf += chunk.toString("utf8");
|
|
110922
|
-
if (buf.length > MAX_LINE) {
|
|
110923
|
-
clearTimeout(timer2);
|
|
110924
|
-
done(fail2("protocol", "the helper sent an oversized line"));
|
|
110925
|
-
return;
|
|
110926
|
-
}
|
|
110927
|
-
const nl = buf.indexOf("\n");
|
|
110928
|
-
if (nl === -1)
|
|
110929
|
-
return;
|
|
110930
|
-
clearTimeout(timer2);
|
|
110931
|
-
done({ ok: true, line: buf.slice(0, nl) });
|
|
110932
|
-
});
|
|
110933
|
-
sock.on("close", () => {
|
|
110934
|
-
clearTimeout(timer2);
|
|
110935
|
-
done(fail2("protocol", "the helper closed the connection without answering"));
|
|
110936
|
-
});
|
|
110937
|
-
sock.on("connect", () => {
|
|
110938
|
-
sock.write(`${line}
|
|
110939
|
-
`);
|
|
110940
|
-
});
|
|
110941
|
-
});
|
|
110942
|
-
}
|
|
110943
|
-
function parse3(line) {
|
|
110944
|
-
let body;
|
|
110945
|
-
try {
|
|
110946
|
-
body = JSON.parse(line);
|
|
110947
|
-
} catch {
|
|
110948
|
-
return fail2("protocol", "the helper sent something that is not JSON");
|
|
110949
|
-
}
|
|
110950
|
-
if (typeof body !== "object" || body === null) {
|
|
110951
|
-
return fail2("protocol", "the helper sent something that is not an object");
|
|
110952
|
-
}
|
|
110953
|
-
const o = body;
|
|
110954
|
-
if (o.ok === false) {
|
|
110955
|
-
const code = typeof o.code === "string" ? o.code : "refused";
|
|
110956
|
-
const detail = typeof o.detail === "string" ? o.detail : "the helper refused";
|
|
110957
|
-
return fail2(code, detail);
|
|
110958
|
-
}
|
|
110959
|
-
if (o.ok !== true)
|
|
110960
|
-
return fail2("protocol", "the helper did not say whether it succeeded");
|
|
110961
|
-
return { ok: true, body: o };
|
|
110962
|
-
}
|
|
110963
|
-
async function request(line) {
|
|
110964
|
-
const r = await call(line);
|
|
110965
|
-
return r.ok ? parse3(r.line) : r;
|
|
110966
|
-
}
|
|
110967
|
-
function hex4(b) {
|
|
110968
|
-
return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
110969
|
-
}
|
|
110970
|
-
async function netdHello() {
|
|
110971
|
-
const r = await request(JSON.stringify({ op: "hello", wireVersion: NETD_WIRE_VERSION }));
|
|
110972
|
-
if (!r.ok)
|
|
110973
|
-
return r;
|
|
110974
|
-
const v = r.body.wireVersion;
|
|
110975
|
-
if (typeof v !== "number")
|
|
110976
|
-
return fail2("protocol", "the helper did not report a wire version");
|
|
110977
|
-
if (v !== NETD_WIRE_VERSION) {
|
|
110978
|
-
return fail2("wire-version", `the helper speaks ${v}, this build speaks ${NETD_WIRE_VERSION}`);
|
|
110979
|
-
}
|
|
110980
|
-
return { ok: true, wireVersion: v };
|
|
110981
|
-
}
|
|
110982
|
-
async function netdStatus() {
|
|
110983
|
-
const r = await request(JSON.stringify({ op: "status" }));
|
|
110984
|
-
if (!r.ok)
|
|
110985
|
-
return r;
|
|
110986
|
-
const meshes = Array.isArray(r.body.meshes) ? r.body.meshes.filter((m) => typeof m === "string") : [];
|
|
110987
|
-
const iface = typeof r.body.interface === "string" ? r.body.interface : null;
|
|
110988
|
-
return { ok: true, meshes, interface: iface };
|
|
110989
|
-
}
|
|
110990
|
-
async function netdUp(args) {
|
|
110991
|
-
const r = await request(JSON.stringify({
|
|
110992
|
-
op: "up",
|
|
110993
|
-
meshId: args.meshId,
|
|
110994
|
-
genesis: hex4(args.genesis),
|
|
110995
|
-
cert: hex4(args.cert),
|
|
110996
|
-
fullTunnel: args.fullTunnel === true
|
|
110997
|
-
}));
|
|
110998
|
-
return r.ok ? { ok: true } : r;
|
|
110999
|
-
}
|
|
111000
|
-
async function netdDown(meshId) {
|
|
111001
|
-
const r = await request(JSON.stringify({ op: "down", meshId }));
|
|
111002
|
-
return r.ok ? { ok: true } : r;
|
|
111003
|
-
}
|
|
111004
|
-
async function netdAvailable() {
|
|
111005
|
-
return (await netdHello()).ok;
|
|
111006
|
-
}
|
|
111007
|
-
|
|
111008
111472
|
// ../../packages/host-core/dist/mesh/route-sync.js
|
|
111009
111473
|
async function syncRoutes(running2, log9 = () => {
|
|
111010
111474
|
}) {
|
|
@@ -111033,7 +111497,14 @@ async function syncRoutes(running2, log9 = () => {
|
|
|
111033
111497
|
// The per-machine choice, and `=== true` rather than a truthiness check: an absent or
|
|
111034
111498
|
// malformed field must read as NO. netd unions this across meshes itself — any mesh
|
|
111035
111499
|
// asking for a full tunnel makes it one for the machine — so this stays per mesh here.
|
|
111036
|
-
fullTunnel: readLocal(meshId)?.fullTunnel === true
|
|
111500
|
+
fullTunnel: readLocal(meshId)?.fullTunnel === true,
|
|
111501
|
+
// THE KEY, and the bundle. With these netd runs the node — the data plane — rather
|
|
111502
|
+
// than only pointing routes at a device nothing reads. The crate's own doc always
|
|
111503
|
+
// described the enrolment as "a genesis block, a certificate and a key"; this is the
|
|
111504
|
+
// first caller to send the third. Over the helper's authenticated socket, to the one
|
|
111505
|
+
// process on the machine that already holds the device the traffic goes into.
|
|
111506
|
+
staticPrivate: identity.staticPrivate,
|
|
111507
|
+
...identity.bundle ? { bundle: identity.bundle } : {}
|
|
111037
111508
|
});
|
|
111038
111509
|
if (r.ok) {
|
|
111039
111510
|
out.up.push(meshId);
|
|
@@ -111059,22 +111530,63 @@ async function syncRoutes(running2, log9 = () => {
|
|
|
111059
111530
|
}
|
|
111060
111531
|
|
|
111061
111532
|
// ../../packages/host-core/dist/mesh/start-meshes.js
|
|
111062
|
-
|
|
111533
|
+
var ROUTE_RETRY_MS = 6e4;
|
|
111534
|
+
function startMeshes(log9, opts = {}) {
|
|
111063
111535
|
const running2 = startNodes(log9);
|
|
111064
|
-
|
|
111536
|
+
const retryMs = opts.retryMs ?? ROUTE_RETRY_MS;
|
|
111537
|
+
let timer2 = null;
|
|
111538
|
+
let stopped2 = false;
|
|
111539
|
+
let lastSummary = "";
|
|
111540
|
+
const attempt = async () => {
|
|
111541
|
+
let r;
|
|
111542
|
+
try {
|
|
111543
|
+
r = await syncRoutes(running2, log9);
|
|
111544
|
+
} catch (e) {
|
|
111545
|
+
log9(`routes: sync threw \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
111546
|
+
return false;
|
|
111547
|
+
}
|
|
111065
111548
|
if (r.helperAbsent) {
|
|
111066
|
-
|
|
111067
|
-
|
|
111549
|
+
const msg = "routes: the network helper is not installed \u2014 the overlay carries no traffic yet";
|
|
111550
|
+
if (msg !== lastSummary)
|
|
111551
|
+
log9(msg);
|
|
111552
|
+
lastSummary = msg;
|
|
111553
|
+
return false;
|
|
111068
111554
|
}
|
|
111069
111555
|
if (r.up.length)
|
|
111070
111556
|
log9(`routes: installed for ${r.up.join(", ")}`);
|
|
111071
111557
|
if (r.down.length)
|
|
111072
111558
|
log9(`routes: removed for ${r.down.join(", ")}`);
|
|
111073
|
-
|
|
111074
|
-
|
|
111075
|
-
|
|
111076
|
-
|
|
111559
|
+
const failures2 = r.failed.map((f) => `routes: ${f.meshId} refused \u2014 ${f.reason}`).join("\n");
|
|
111560
|
+
if (failures2 && failures2 !== lastSummary)
|
|
111561
|
+
log9(failures2);
|
|
111562
|
+
lastSummary = failures2;
|
|
111563
|
+
return r.failed.length === 0;
|
|
111564
|
+
};
|
|
111565
|
+
const schedule = () => {
|
|
111566
|
+
if (stopped2 || running2.length === 0)
|
|
111567
|
+
return;
|
|
111568
|
+
timer2 = setTimeout(() => {
|
|
111569
|
+
timer2 = null;
|
|
111570
|
+
void attempt().then((done) => {
|
|
111571
|
+
if (!done)
|
|
111572
|
+
schedule();
|
|
111573
|
+
});
|
|
111574
|
+
}, retryMs);
|
|
111575
|
+
timer2.unref?.();
|
|
111576
|
+
};
|
|
111577
|
+
void attempt().then((done) => {
|
|
111578
|
+
if (!done)
|
|
111579
|
+
schedule();
|
|
111077
111580
|
});
|
|
111581
|
+
return {
|
|
111582
|
+
stop() {
|
|
111583
|
+
stopped2 = true;
|
|
111584
|
+
if (timer2) {
|
|
111585
|
+
clearTimeout(timer2);
|
|
111586
|
+
timer2 = null;
|
|
111587
|
+
}
|
|
111588
|
+
}
|
|
111589
|
+
};
|
|
111078
111590
|
}
|
|
111079
111591
|
function startNodes(log9) {
|
|
111080
111592
|
const ids = listMeshIds();
|
|
@@ -112404,7 +112916,7 @@ function appendRdeskAudit(e) {
|
|
|
112404
112916
|
|
|
112405
112917
|
// ../../packages/host-core/dist/fileprovider-bridge.js
|
|
112406
112918
|
init_managed_spawn();
|
|
112407
|
-
import { writeFileSync as writeFileSync36, mkdirSync as mkdirSync37, rmSync as rmSync8, existsSync as
|
|
112919
|
+
import { writeFileSync as writeFileSync36, mkdirSync as mkdirSync37, rmSync as rmSync8, existsSync as existsSync35 } from "node:fs";
|
|
112408
112920
|
import { homedir as homedir10 } from "node:os";
|
|
112409
112921
|
import { join as join73 } from "node:path";
|
|
112410
112922
|
init_safe_kill();
|
|
@@ -112575,7 +113087,7 @@ function stopFileProviderBridge() {
|
|
|
112575
113087
|
|
|
112576
113088
|
// ../../packages/host-core/dist/vault/autofill-bridge.js
|
|
112577
113089
|
import { randomBytes as randomBytes9 } from "node:crypto";
|
|
112578
|
-
import { existsSync as
|
|
113090
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync38, writeFileSync as writeFileSync37, rmSync as rmSync9 } from "node:fs";
|
|
112579
113091
|
import { homedir as homedir11 } from "node:os";
|
|
112580
113092
|
import { join as join74 } from "node:path";
|
|
112581
113093
|
|
|
@@ -112650,7 +113162,7 @@ function writeBridgeInfo(port, token3, log9) {
|
|
|
112650
113162
|
return;
|
|
112651
113163
|
const containerData = appexContainerData();
|
|
112652
113164
|
try {
|
|
112653
|
-
if (!
|
|
113165
|
+
if (!existsSync36(containerData)) {
|
|
112654
113166
|
if (!infoLoggedOnce) {
|
|
112655
113167
|
infoLoggedOnce = true;
|
|
112656
113168
|
log9("autofill bridge-info SKIP: no appex container yet at", containerData);
|
|
@@ -112899,7 +113411,7 @@ function createNodePowerController() {
|
|
|
112899
113411
|
|
|
112900
113412
|
// ../../packages/host-core/dist/native-host.js
|
|
112901
113413
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
112902
|
-
import { chmodSync as chmodSync6, existsSync as
|
|
113414
|
+
import { chmodSync as chmodSync6, existsSync as existsSync37, mkdirSync as mkdirSync39, writeFileSync as writeFileSync38 } from "node:fs";
|
|
112903
113415
|
import { homedir as homedir12, platform as platform5 } from "node:os";
|
|
112904
113416
|
import { dirname as dirname34, join as join75, resolve as resolve12 } from "node:path";
|
|
112905
113417
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
@@ -112930,7 +113442,7 @@ function resolveBridge(log9) {
|
|
|
112930
113442
|
} catch {
|
|
112931
113443
|
}
|
|
112932
113444
|
for (const c of candidates)
|
|
112933
|
-
if (c &&
|
|
113445
|
+
if (c && existsSync37(c))
|
|
112934
113446
|
return c;
|
|
112935
113447
|
log9("companion native-host: bridge not found; skipping registration (looked in", candidates.filter(Boolean).join(" , "), ")");
|
|
112936
113448
|
return null;
|
|
@@ -112972,7 +113484,7 @@ function registerNativeHost(opts) {
|
|
|
112972
113484
|
return registerWindows(opts.daemonDir, manifestJson, log9);
|
|
112973
113485
|
let wrote = 0;
|
|
112974
113486
|
for (const profile of browserProfileDirs()) {
|
|
112975
|
-
if (!
|
|
113487
|
+
if (!existsSync37(profile))
|
|
112976
113488
|
continue;
|
|
112977
113489
|
try {
|
|
112978
113490
|
const dir2 = join75(profile, "NativeMessagingHosts");
|
|
@@ -113008,7 +113520,7 @@ init_daemon_settings();
|
|
|
113008
113520
|
|
|
113009
113521
|
// ../../packages/host-core/dist/activity/activity-tracker.js
|
|
113010
113522
|
init_credentials();
|
|
113011
|
-
import { promises as fsp, writeFileSync as writeFileSync39, mkdirSync as mkdirSync40, existsSync as
|
|
113523
|
+
import { promises as fsp, writeFileSync as writeFileSync39, mkdirSync as mkdirSync40, existsSync as existsSync38 } from "node:fs";
|
|
113012
113524
|
import { join as join76 } from "node:path";
|
|
113013
113525
|
function activityProfilePath() {
|
|
113014
113526
|
return join76(daemonDir2(), "activity-profile.json");
|
|
@@ -113086,7 +113598,7 @@ function createActivityTracker(opts = {}) {
|
|
|
113086
113598
|
return;
|
|
113087
113599
|
const key2 = front.bundleId || front.name;
|
|
113088
113600
|
await ensureLoaded();
|
|
113089
|
-
if (profile && Object.keys(profile.apps).length && !
|
|
113601
|
+
if (profile && Object.keys(profile.apps).length && !existsSync38(activityProfilePath())) {
|
|
113090
113602
|
log9("activity profile cleared externally \u2014 resetting in-memory");
|
|
113091
113603
|
profile = { version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), apps: {} };
|
|
113092
113604
|
}
|
|
@@ -113319,7 +113831,7 @@ function backoff2() {
|
|
|
113319
113831
|
|
|
113320
113832
|
// ../../packages/host-core/dist/daemon-runtime.js
|
|
113321
113833
|
var daemonClipboardSync = null;
|
|
113322
|
-
var
|
|
113834
|
+
var REFRESH_INTERVAL_MS2 = 12 * 60 * 60 * 1e3;
|
|
113323
113835
|
async function runDaemon(opts = {}) {
|
|
113324
113836
|
const VERSION3 = opts.version ?? process.env.CCB_DAEMON_VERSION ?? "dev";
|
|
113325
113837
|
const TAG = opts.tag ?? "host-daemon";
|
|
@@ -113360,7 +113872,7 @@ async function runDaemon(opts = {}) {
|
|
|
113360
113872
|
function acquireLock2() {
|
|
113361
113873
|
const f = pidfile();
|
|
113362
113874
|
try {
|
|
113363
|
-
if (!
|
|
113875
|
+
if (!existsSync39(daemonDir2()))
|
|
113364
113876
|
mkdirSync41(daemonDir2(), { recursive: true, mode: 448 });
|
|
113365
113877
|
} catch {
|
|
113366
113878
|
}
|
|
@@ -113386,7 +113898,7 @@ async function runDaemon(opts = {}) {
|
|
|
113386
113898
|
function releaseLock() {
|
|
113387
113899
|
try {
|
|
113388
113900
|
const f = pidfile();
|
|
113389
|
-
if (
|
|
113901
|
+
if (existsSync39(f) && Number(readFileSync49(f, "utf-8").trim()) === process.pid)
|
|
113390
113902
|
unlinkSync6(f);
|
|
113391
113903
|
} catch {
|
|
113392
113904
|
}
|
|
@@ -113428,6 +113940,7 @@ async function runDaemon(opts = {}) {
|
|
|
113428
113940
|
}
|
|
113429
113941
|
}
|
|
113430
113942
|
let stopGatewayKeyRefresh = null;
|
|
113943
|
+
let meshRoutes = null;
|
|
113431
113944
|
let timer2 = null;
|
|
113432
113945
|
let identityWatch = null;
|
|
113433
113946
|
let refreshRetry = null;
|
|
@@ -113522,6 +114035,10 @@ async function runDaemon(opts = {}) {
|
|
|
113522
114035
|
stopGatewayKeyRefresh();
|
|
113523
114036
|
stopGatewayKeyRefresh = null;
|
|
113524
114037
|
}
|
|
114038
|
+
if (meshRoutes) {
|
|
114039
|
+
meshRoutes.stop();
|
|
114040
|
+
meshRoutes = null;
|
|
114041
|
+
}
|
|
113525
114042
|
try {
|
|
113526
114043
|
offRecovery?.();
|
|
113527
114044
|
offRecovery = null;
|
|
@@ -114213,7 +114730,7 @@ async function runDaemon(opts = {}) {
|
|
|
114213
114730
|
} catch {
|
|
114214
114731
|
}
|
|
114215
114732
|
try {
|
|
114216
|
-
startMeshes(log9);
|
|
114733
|
+
meshRoutes = startMeshes(log9);
|
|
114217
114734
|
} catch {
|
|
114218
114735
|
}
|
|
114219
114736
|
writeStatus2({ deviceId: c.deviceId, connected: true });
|
|
@@ -114492,7 +115009,7 @@ async function runDaemon(opts = {}) {
|
|
|
114492
115009
|
const daemonLogFile = join77(daemonDir2(), "daemon.log");
|
|
114493
115010
|
const capTimer = setInterval(() => {
|
|
114494
115011
|
try {
|
|
114495
|
-
if (
|
|
115012
|
+
if (existsSync39(daemonLogFile) && statSync14(daemonLogFile).size > DAEMON_LOG_CAP) {
|
|
114496
115013
|
const buf = readFileSync49(daemonLogFile, "utf-8");
|
|
114497
115014
|
const kept = `\u2026[daemon.log capped at ${(/* @__PURE__ */ new Date()).toISOString()}]\u2026
|
|
114498
115015
|
` + buf.slice(-DAEMON_LOG_CAP / 2);
|
|
@@ -114509,7 +115026,7 @@ async function runDaemon(opts = {}) {
|
|
|
114509
115026
|
startAutofillBridge(log9);
|
|
114510
115027
|
}
|
|
114511
115028
|
if (ownsMachine)
|
|
114512
|
-
startMeshes(log9);
|
|
115029
|
+
meshRoutes = startMeshes(log9);
|
|
114513
115030
|
if (ownsMachine) {
|
|
114514
115031
|
try {
|
|
114515
115032
|
stopGatewayKeyRefresh = startGatewayKeyRefresh();
|
|
@@ -114521,7 +115038,7 @@ async function runDaemon(opts = {}) {
|
|
|
114521
115038
|
await refresh();
|
|
114522
115039
|
timer2 = setInterval(() => {
|
|
114523
115040
|
void refresh();
|
|
114524
|
-
},
|
|
115041
|
+
}, REFRESH_INTERVAL_MS2);
|
|
114525
115042
|
}
|
|
114526
115043
|
|
|
114527
115044
|
// ../../packages/host-core/dist/host-daemon-entry.js
|
|
@@ -114631,7 +115148,7 @@ async function evictNode(args) {
|
|
|
114631
115148
|
const r = await call2(`/meshes/${encodeURIComponent(args.meshId)}/nodes/${encodeURIComponent(args.nodeId)}`, { method: "DELETE", body: {} });
|
|
114632
115149
|
return r.ok ? { ok: true, epoch: r.data.epoch } : r;
|
|
114633
115150
|
}
|
|
114634
|
-
async function enrolWith(requested, post) {
|
|
115151
|
+
async function enrolWith(requested, post, origin) {
|
|
114635
115152
|
const name = normaliseNodeName(requested);
|
|
114636
115153
|
if (!name) {
|
|
114637
115154
|
return fail3("bad-name", `"${requested}" cannot be a machine name \u2014 names are DNS labels, so letters, digits and hyphens only`);
|
|
@@ -114655,7 +115172,7 @@ async function enrolWith(requested, post) {
|
|
|
114655
115172
|
}
|
|
114656
115173
|
try {
|
|
114657
115174
|
saveMesh(meshId, { genesis, cert, staticPrivate, sigPrivate });
|
|
114658
|
-
writeLocal(meshId, { joinedAt: Date.now(), enabled: true });
|
|
115175
|
+
writeLocal(meshId, { joinedAt: Date.now(), enabled: true, ...origin ? { origin } : {} });
|
|
114659
115176
|
if (r.data.bundle) {
|
|
114660
115177
|
try {
|
|
114661
115178
|
cacheBundle(fromHexOrB64(r.data.bundle), meshId);
|
|
@@ -114677,16 +115194,24 @@ async function enrolWith(requested, post) {
|
|
|
114677
115194
|
};
|
|
114678
115195
|
}
|
|
114679
115196
|
async function enrol(args) {
|
|
114680
|
-
return enrolWith(
|
|
114681
|
-
|
|
114682
|
-
|
|
114683
|
-
|
|
115197
|
+
return enrolWith(
|
|
115198
|
+
args.name,
|
|
115199
|
+
(staticPub, sigPub, name) => call2(`/meshes/${encodeURIComponent(args.meshId)}/nodes`, {
|
|
115200
|
+
method: "POST",
|
|
115201
|
+
body: { name, staticPub, sigPub, ...args.exitNode === true ? { exitNode: true } : {} }
|
|
115202
|
+
}),
|
|
115203
|
+
// This path is authenticated, so the origin is necessarily the signed-in one.
|
|
115204
|
+
resolveOrigin(void 0)
|
|
115205
|
+
);
|
|
114684
115206
|
}
|
|
114685
115207
|
async function joinWithToken(args) {
|
|
114686
|
-
return enrolWith(args.name, async (staticPub, sigPub, name) => postJoin(args.origin, { token: args.token, name, staticPub, sigPub }));
|
|
115208
|
+
return enrolWith(args.name, async (staticPub, sigPub, name) => postJoin(args.origin, { token: args.token, name, staticPub, sigPub }), resolveOrigin(args.origin));
|
|
115209
|
+
}
|
|
115210
|
+
function resolveOrigin(origin) {
|
|
115211
|
+
return origin ?? readCredentials()?.apiOrigin ?? void 0;
|
|
114687
115212
|
}
|
|
114688
115213
|
async function postJoin(origin, body) {
|
|
114689
|
-
const base = origin
|
|
115214
|
+
const base = resolveOrigin(origin);
|
|
114690
115215
|
if (!base) {
|
|
114691
115216
|
return fail3("not-signed-in", "this machine has never signed in \u2014 pass --origin https://\u2026 so it knows which server to ask");
|
|
114692
115217
|
}
|
|
@@ -114714,7 +115239,7 @@ async function postJoin(origin, body) {
|
|
|
114714
115239
|
return { ok: true, data: JSON.parse(text12) };
|
|
114715
115240
|
}
|
|
114716
115241
|
async function enrolWithCode(args) {
|
|
114717
|
-
return enrolWith(args.name, async (staticPub, sigPub, name) => postJoin(args.origin, { code: args.code, name, staticPub, sigPub }));
|
|
115242
|
+
return enrolWith(args.name, async (staticPub, sigPub, name) => postJoin(args.origin, { code: args.code, name, staticPub, sigPub }), resolveOrigin(args.origin));
|
|
114718
115243
|
}
|
|
114719
115244
|
async function rawPrivate(key2) {
|
|
114720
115245
|
const pkcs8 = new Uint8Array(await crypto.subtle.exportKey("pkcs8", key2));
|
|
@@ -114798,13 +115323,13 @@ init_credentials();
|
|
|
114798
115323
|
|
|
114799
115324
|
// ../../packages/host-core/dist/net-service/ensure-installed.js
|
|
114800
115325
|
import { execFile as execFile14 } from "node:child_process";
|
|
114801
|
-
import { existsSync as
|
|
115326
|
+
import { existsSync as existsSync43 } from "node:fs";
|
|
114802
115327
|
import { dirname as dirname36, join as join81 } from "node:path";
|
|
114803
115328
|
import { promisify as promisify10 } from "node:util";
|
|
114804
115329
|
|
|
114805
115330
|
// ../../packages/host-core/dist/net-service/launchd-system.js
|
|
114806
115331
|
import { spawn as spawn9 } from "node:child_process";
|
|
114807
|
-
import { existsSync as
|
|
115332
|
+
import { existsSync as existsSync40, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync41, rmSync as rmSync10, readFileSync as readFileSync50 } from "node:fs";
|
|
114808
115333
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
114809
115334
|
import { join as join78 } from "node:path";
|
|
114810
115335
|
|
|
@@ -114975,7 +115500,7 @@ ${plist}TESSERACT_PLIST_EOF`,
|
|
|
114975
115500
|
};
|
|
114976
115501
|
},
|
|
114977
115502
|
async state() {
|
|
114978
|
-
if (!
|
|
115503
|
+
if (!existsSync40(PLIST))
|
|
114979
115504
|
return { installed: false, running: false };
|
|
114980
115505
|
const owner = parseOwnerUid(readFileSync50(PLIST, "utf8"));
|
|
114981
115506
|
const running2 = await netdAvailable();
|
|
@@ -114995,7 +115520,7 @@ ${plist}TESSERACT_PLIST_EOF`,
|
|
|
114995
115520
|
|
|
114996
115521
|
// ../../packages/host-core/dist/net-service/systemd-system.js
|
|
114997
115522
|
import { spawn as spawn11 } from "node:child_process";
|
|
114998
|
-
import { existsSync as
|
|
115523
|
+
import { existsSync as existsSync41, readFileSync as readFileSync51 } from "node:fs";
|
|
114999
115524
|
|
|
115000
115525
|
// ../../packages/host-core/dist/service/exec.js
|
|
115001
115526
|
import { execFile as execFile11, spawn as spawn10 } from "node:child_process";
|
|
@@ -115138,7 +115663,7 @@ function makeSystemdSystem() {
|
|
|
115138
115663
|
return code === 0 ? { ok: true } : { ok: false, detail: `removing the helper failed (exit ${code})` };
|
|
115139
115664
|
},
|
|
115140
115665
|
async state() {
|
|
115141
|
-
if (!
|
|
115666
|
+
if (!existsSync41(UNIT_PATH))
|
|
115142
115667
|
return { installed: false, running: false };
|
|
115143
115668
|
const owner = parseOwnerUid(readFileSync51(UNIT_PATH, "utf8"));
|
|
115144
115669
|
const active = await tryRun("systemctl", ["is-active", UNIT]);
|
|
@@ -115401,7 +115926,7 @@ function makeNetService() {
|
|
|
115401
115926
|
}
|
|
115402
115927
|
|
|
115403
115928
|
// ../../packages/host-core/dist/net-service/netd-binary.js
|
|
115404
|
-
import { existsSync as
|
|
115929
|
+
import { existsSync as existsSync42 } from "node:fs";
|
|
115405
115930
|
import { dirname as dirname35, join as join80 } from "node:path";
|
|
115406
115931
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
115407
115932
|
function netdBinaryName() {
|
|
@@ -115409,7 +115934,7 @@ function netdBinaryName() {
|
|
|
115409
115934
|
}
|
|
115410
115935
|
function override2() {
|
|
115411
115936
|
const p = process.env.CCB_NETD_BIN;
|
|
115412
|
-
return p &&
|
|
115937
|
+
return p && existsSync42(p) ? p : null;
|
|
115413
115938
|
}
|
|
115414
115939
|
function netdBinaryPath() {
|
|
115415
115940
|
const fromEnv = override2();
|
|
@@ -115436,7 +115961,7 @@ function netdBinaryPath() {
|
|
|
115436
115961
|
candidates.push(join80(process.cwd(), "crates", "target", "release", name));
|
|
115437
115962
|
for (const c of candidates) {
|
|
115438
115963
|
try {
|
|
115439
|
-
if (
|
|
115964
|
+
if (existsSync42(c))
|
|
115440
115965
|
return c;
|
|
115441
115966
|
} catch {
|
|
115442
115967
|
}
|
|
@@ -115478,7 +116003,7 @@ async function ensureNetHelperInstalled() {
|
|
|
115478
116003
|
if (st.installed && !mismatched)
|
|
115479
116004
|
return { ok: true, already: true };
|
|
115480
116005
|
const exec2 = netdBinaryPath();
|
|
115481
|
-
if (exec2 && process.platform === "win32" && !
|
|
116006
|
+
if (exec2 && process.platform === "win32" && !existsSync43(join81(dirname36(exec2), "wintun.dll"))) {
|
|
115482
116007
|
return {
|
|
115483
116008
|
ok: false,
|
|
115484
116009
|
reason: "no-binary",
|
|
@@ -115530,7 +116055,7 @@ import { platform as platform6 } from "node:os";
|
|
|
115530
116055
|
|
|
115531
116056
|
// ../../packages/host-core/dist/service/launchd.js
|
|
115532
116057
|
init_credentials();
|
|
115533
|
-
import { writeFileSync as writeFileSync43, existsSync as
|
|
116058
|
+
import { writeFileSync as writeFileSync43, existsSync as existsSync44, mkdirSync as mkdirSync42, unlinkSync as unlinkSync7 } from "node:fs";
|
|
115534
116059
|
import { dirname as dirname37, join as join82 } from "node:path";
|
|
115535
116060
|
import { homedir as homedir13, userInfo as userInfo2 } from "node:os";
|
|
115536
116061
|
function xml2(s) {
|
|
@@ -115585,6 +116110,36 @@ ${env}
|
|
|
115585
116110
|
</plist>
|
|
115586
116111
|
`;
|
|
115587
116112
|
}
|
|
116113
|
+
const loaded = async () => {
|
|
116114
|
+
try {
|
|
116115
|
+
await run5("launchctl", ["print", serviceTarget()]);
|
|
116116
|
+
return true;
|
|
116117
|
+
} catch {
|
|
116118
|
+
return false;
|
|
116119
|
+
}
|
|
116120
|
+
};
|
|
116121
|
+
const untilGone = async () => {
|
|
116122
|
+
for (let i = 0; i < 25; i++) {
|
|
116123
|
+
if (!await loaded())
|
|
116124
|
+
return;
|
|
116125
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
116126
|
+
}
|
|
116127
|
+
};
|
|
116128
|
+
const bootstrapWithRetry = async () => {
|
|
116129
|
+
let last;
|
|
116130
|
+
for (let i = 0; i < 5; i++) {
|
|
116131
|
+
try {
|
|
116132
|
+
await run5("launchctl", ["bootstrap", domainTarget(), plistPath()]);
|
|
116133
|
+
return;
|
|
116134
|
+
} catch (e) {
|
|
116135
|
+
last = e;
|
|
116136
|
+
if (await loaded())
|
|
116137
|
+
return;
|
|
116138
|
+
await new Promise((r) => setTimeout(r, 300 * (i + 1)));
|
|
116139
|
+
}
|
|
116140
|
+
}
|
|
116141
|
+
throw last instanceof Error ? last : new Error(String(last));
|
|
116142
|
+
};
|
|
115588
116143
|
return {
|
|
115589
116144
|
async install(spec) {
|
|
115590
116145
|
mkdirSync42(workDirOf(spec), { recursive: true });
|
|
@@ -115593,16 +116148,20 @@ ${env}
|
|
|
115593
116148
|
writeFileSync43(plistPath(), buildPlist2(spec));
|
|
115594
116149
|
await run5("launchctl", ["bootout", serviceTarget()]).catch(() => {
|
|
115595
116150
|
});
|
|
115596
|
-
await
|
|
116151
|
+
await untilGone();
|
|
116152
|
+
await bootstrapWithRetry();
|
|
115597
116153
|
await run5("launchctl", ["enable", serviceTarget()]).catch(() => {
|
|
115598
116154
|
});
|
|
115599
116155
|
await run5("launchctl", ["kickstart", "-k", serviceTarget()]).catch(() => {
|
|
115600
116156
|
});
|
|
116157
|
+
if (!await loaded()) {
|
|
116158
|
+
throw new Error(`the service ${label} is not loaded after install \u2014 check \`launchctl print ${serviceTarget()}\``);
|
|
116159
|
+
}
|
|
115601
116160
|
},
|
|
115602
116161
|
async uninstall() {
|
|
115603
116162
|
await run5("launchctl", ["bootout", serviceTarget()]).catch(() => {
|
|
115604
116163
|
});
|
|
115605
|
-
if (
|
|
116164
|
+
if (existsSync44(plistPath())) {
|
|
115606
116165
|
try {
|
|
115607
116166
|
unlinkSync7(plistPath());
|
|
115608
116167
|
} catch {
|
|
@@ -115613,7 +116172,7 @@ ${env}
|
|
|
115613
116172
|
await this.install(spec);
|
|
115614
116173
|
},
|
|
115615
116174
|
async isInstalled() {
|
|
115616
|
-
return
|
|
116175
|
+
return existsSync44(plistPath());
|
|
115617
116176
|
},
|
|
115618
116177
|
async start() {
|
|
115619
116178
|
await run5("launchctl", ["bootstrap", domainTarget(), plistPath()]).catch(() => {
|
|
@@ -115630,7 +116189,7 @@ ${env}
|
|
|
115630
116189
|
},
|
|
115631
116190
|
async logs(follow) {
|
|
115632
116191
|
const f = logFile();
|
|
115633
|
-
if (!
|
|
116192
|
+
if (!existsSync44(f)) {
|
|
115634
116193
|
console.log(`No log yet at ${f} \u2014 has the service started?`);
|
|
115635
116194
|
return;
|
|
115636
116195
|
}
|
|
@@ -115641,7 +116200,7 @@ ${env}
|
|
|
115641
116200
|
|
|
115642
116201
|
// ../../packages/host-core/dist/service/systemd.js
|
|
115643
116202
|
init_credentials();
|
|
115644
|
-
import { writeFileSync as writeFileSync44, existsSync as
|
|
116203
|
+
import { writeFileSync as writeFileSync44, existsSync as existsSync45, mkdirSync as mkdirSync43, unlinkSync as unlinkSync8 } from "node:fs";
|
|
115645
116204
|
import { dirname as dirname38, join as join83 } from "node:path";
|
|
115646
116205
|
import { homedir as homedir14, userInfo as userInfo3 } from "node:os";
|
|
115647
116206
|
function unitVal2(v) {
|
|
@@ -115697,7 +116256,7 @@ WantedBy=default.target
|
|
|
115697
116256
|
async uninstall() {
|
|
115698
116257
|
await run5("systemctl", ["--user", "disable", "--now", unit]).catch(() => {
|
|
115699
116258
|
});
|
|
115700
|
-
if (
|
|
116259
|
+
if (existsSync45(unitPath())) {
|
|
115701
116260
|
try {
|
|
115702
116261
|
unlinkSync8(unitPath());
|
|
115703
116262
|
} catch {
|
|
@@ -115710,7 +116269,7 @@ WantedBy=default.target
|
|
|
115710
116269
|
await this.install(spec);
|
|
115711
116270
|
},
|
|
115712
116271
|
async isInstalled() {
|
|
115713
|
-
return
|
|
116272
|
+
return existsSync45(unitPath());
|
|
115714
116273
|
},
|
|
115715
116274
|
async start() {
|
|
115716
116275
|
await run5("systemctl", ["--user", "start", unit]);
|
|
@@ -115723,7 +116282,7 @@ WantedBy=default.target
|
|
|
115723
116282
|
},
|
|
115724
116283
|
async logs(follow) {
|
|
115725
116284
|
const f = join83(daemonDir2(), "daemon.log");
|
|
115726
|
-
if (
|
|
116285
|
+
if (existsSync45(f)) {
|
|
115727
116286
|
await runInherit("tail", follow ? ["-f", f] : ["-n", "200", f]);
|
|
115728
116287
|
return;
|
|
115729
116288
|
}
|
|
@@ -115735,8 +116294,20 @@ WantedBy=default.target
|
|
|
115735
116294
|
|
|
115736
116295
|
// ../../packages/host-core/dist/service/windows.js
|
|
115737
116296
|
init_credentials();
|
|
115738
|
-
import { writeFileSync as writeFileSync45, existsSync as
|
|
116297
|
+
import { writeFileSync as writeFileSync45, existsSync as existsSync46, mkdirSync as mkdirSync44, unlinkSync as unlinkSync9, readFileSync as readFileSync52 } from "node:fs";
|
|
115739
116298
|
import { dirname as dirname39, join as join84 } from "node:path";
|
|
116299
|
+
function publishedDaemonPid(dir2) {
|
|
116300
|
+
for (const f of ["status.json", "daemon.lock"]) {
|
|
116301
|
+
try {
|
|
116302
|
+
const raw = readFileSync52(join84(dir2, f), "utf-8");
|
|
116303
|
+
const pid = f === "status.json" ? Number(JSON.parse(raw).pid) : Number(raw.trim());
|
|
116304
|
+
if (Number.isInteger(pid) && pid > 0)
|
|
116305
|
+
return pid;
|
|
116306
|
+
} catch {
|
|
116307
|
+
}
|
|
116308
|
+
}
|
|
116309
|
+
return null;
|
|
116310
|
+
}
|
|
115740
116311
|
function makeWindowsTask(instanceId) {
|
|
115741
116312
|
const suffix = instanceSuffix(instanceId);
|
|
115742
116313
|
const task = `Fineguide\\TesseractHost${suffix ? `-${suffix}` : ""}`;
|
|
@@ -115768,14 +116339,20 @@ start "" /b ${cmd} >> "${oneLine2(spec.logPath).replace(/"/g, "")}" 2>&1\r
|
|
|
115768
116339
|
async uninstall() {
|
|
115769
116340
|
await run5("schtasks", ["/Delete", "/TN", task, "/F"]).catch(() => {
|
|
115770
116341
|
});
|
|
115771
|
-
if (
|
|
116342
|
+
if (existsSync46(wrapperPath())) {
|
|
115772
116343
|
try {
|
|
115773
116344
|
unlinkSync9(wrapperPath());
|
|
115774
116345
|
} catch {
|
|
115775
116346
|
}
|
|
115776
116347
|
}
|
|
115777
116348
|
},
|
|
116349
|
+
// STOP FIRST. `install` alone re-creates the task and runs it, which on Windows spawns a
|
|
116350
|
+
// SECOND daemon beside the one that is still running (see stop()); the newcomer finds the
|
|
116351
|
+
// pid lock held and stands down, so "restart" restarted nothing — the old process kept
|
|
116352
|
+
// running the old code. That is how a machine upgraded to a fixed CLI kept exhibiting the
|
|
116353
|
+
// bug the upgrade fixed, and how `tesseract-agent restart` reported success while doing it.
|
|
115778
116354
|
async restart(spec) {
|
|
116355
|
+
await this.stop();
|
|
115779
116356
|
await this.install(spec);
|
|
115780
116357
|
},
|
|
115781
116358
|
async isInstalled() {
|
|
@@ -115789,16 +116366,54 @@ start "" /b ${cmd} >> "${oneLine2(spec.logPath).replace(/"/g, "")}" 2>&1\r
|
|
|
115789
116366
|
async start() {
|
|
115790
116367
|
await run5("schtasks", ["/Run", "/TN", task]);
|
|
115791
116368
|
},
|
|
116369
|
+
/**
|
|
116370
|
+
* Stop the daemon. Not the task — the daemon.
|
|
116371
|
+
*
|
|
116372
|
+
* `schtasks /End` ends the TASK's process, and the task's process is the .cmd wrapper, which
|
|
116373
|
+
* exited the instant it ran `start "" /b` to detach the daemon. So /End ended a task that had
|
|
116374
|
+
* already finished, reported success, and the daemon ran on. Every `stop`, `restart` and
|
|
116375
|
+
* self-update on Windows has been a no-op against a running daemon since the wrapper was
|
|
116376
|
+
* written; observed directly as two node processes surviving stop, restart, and the task's
|
|
116377
|
+
* own /End, with start times from the original launch.
|
|
116378
|
+
*
|
|
116379
|
+
* The daemon publishes its own pid (status.json / daemon.lock), and that is what gets ended.
|
|
116380
|
+
* Its PARENT is ended instead when the parent is the CLI supervisor (`cli.js run`), because
|
|
116381
|
+
* killing only the worker makes the supervisor respawn it; `/T` takes the whole tree either
|
|
116382
|
+
* way. The parent is checked by command line before being touched: the lock can be held by a
|
|
116383
|
+
* process that is not ours to kill — the desktop app owning the machine — and a supervisor
|
|
116384
|
+
* that is not `cli.js` is left alone, worker only.
|
|
116385
|
+
*
|
|
116386
|
+
* Best-effort throughout, like the other controllers' stop: a daemon that is already gone
|
|
116387
|
+
* is the desired state, not an error.
|
|
116388
|
+
*/
|
|
115792
116389
|
async stop() {
|
|
115793
116390
|
await run5("schtasks", ["/End", "/TN", task]).catch(() => {
|
|
115794
116391
|
});
|
|
116392
|
+
const pid = publishedDaemonPid(daemonDir2());
|
|
116393
|
+
if (pid === null)
|
|
116394
|
+
return;
|
|
116395
|
+
let target = pid;
|
|
116396
|
+
try {
|
|
116397
|
+
const { stdout } = await run5("powershell", [
|
|
116398
|
+
"-NoProfile",
|
|
116399
|
+
"-Command",
|
|
116400
|
+
`$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)" } }`
|
|
116401
|
+
]);
|
|
116402
|
+
const [ppidRaw, cmd = ""] = String(stdout).trim().split("|");
|
|
116403
|
+
const ppid = Number(ppidRaw);
|
|
116404
|
+
if (Number.isInteger(ppid) && ppid > 0 && /cli\.js["']?\s+run\b/.test(cmd))
|
|
116405
|
+
target = ppid;
|
|
116406
|
+
} catch {
|
|
116407
|
+
}
|
|
116408
|
+
await run5("taskkill", ["/pid", String(target), "/T", "/F"]).catch(() => {
|
|
116409
|
+
});
|
|
115795
116410
|
},
|
|
115796
116411
|
async status() {
|
|
115797
116412
|
await runInherit("schtasks", ["/Query", "/TN", task, "/V", "/FO", "LIST"]);
|
|
115798
116413
|
},
|
|
115799
116414
|
async logs(follow) {
|
|
115800
116415
|
const f = logFile();
|
|
115801
|
-
if (!
|
|
116416
|
+
if (!existsSync46(f)) {
|
|
115802
116417
|
console.log(`No log yet at ${f} \u2014 has the task run?`);
|
|
115803
116418
|
return;
|
|
115804
116419
|
}
|
|
@@ -115830,7 +116445,7 @@ init_safe_kill();
|
|
|
115830
116445
|
// src/auth.ts
|
|
115831
116446
|
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
115832
116447
|
import { randomUUID as randomUUID30 } from "node:crypto";
|
|
115833
|
-
import { existsSync as
|
|
116448
|
+
import { existsSync as existsSync47, rmSync as rmSync12 } from "node:fs";
|
|
115834
116449
|
import { join as join85 } from "node:path";
|
|
115835
116450
|
|
|
115836
116451
|
// src/config.ts
|
|
@@ -116033,7 +116648,7 @@ async function logout() {
|
|
|
116033
116648
|
for (const f of ["credentials.json", "status.json", "daemon.lock"]) {
|
|
116034
116649
|
try {
|
|
116035
116650
|
const p = join85(daemonDir2(), f);
|
|
116036
|
-
if (
|
|
116651
|
+
if (existsSync47(p)) rmSync12(p);
|
|
116037
116652
|
} catch {
|
|
116038
116653
|
}
|
|
116039
116654
|
}
|
|
@@ -116193,7 +116808,7 @@ function startSelfUpdater(current) {
|
|
|
116193
116808
|
// src/service.ts
|
|
116194
116809
|
import { join as join86 } from "node:path";
|
|
116195
116810
|
import { platform as platform7 } from "node:os";
|
|
116196
|
-
import { existsSync as
|
|
116811
|
+
import { existsSync as existsSync48, readFileSync as readFileSync53 } from "node:fs";
|
|
116197
116812
|
function controller(instanceId) {
|
|
116198
116813
|
const c = getServiceController(instanceId);
|
|
116199
116814
|
if (!c) {
|
|
@@ -116220,8 +116835,8 @@ var isAlive = (pid) => {
|
|
|
116220
116835
|
function runningDaemonInfo() {
|
|
116221
116836
|
const lock = join86(daemonDir2(), "daemon.lock");
|
|
116222
116837
|
try {
|
|
116223
|
-
if (!
|
|
116224
|
-
const pid = Number(
|
|
116838
|
+
if (!existsSync48(lock)) return null;
|
|
116839
|
+
const pid = Number(readFileSync53(lock, "utf-8").trim());
|
|
116225
116840
|
if (!pid || !isAlive(pid)) return null;
|
|
116226
116841
|
return { pid, device: readCredentials()?.deviceId };
|
|
116227
116842
|
} catch {
|
|
@@ -116255,7 +116870,7 @@ async function logs(instanceId, follow) {
|
|
|
116255
116870
|
}
|
|
116256
116871
|
|
|
116257
116872
|
// src/net.ts
|
|
116258
|
-
import { readFileSync as
|
|
116873
|
+
import { readFileSync as readFileSync55 } from "node:fs";
|
|
116259
116874
|
import { randomBytes as randomBytes11 } from "node:crypto";
|
|
116260
116875
|
import { hostname as hostname7 } from "node:os";
|
|
116261
116876
|
import { resolve as resolve14 } from "node:path";
|
|
@@ -116320,7 +116935,7 @@ function readOrDie(label, p) {
|
|
|
116320
116935
|
process.exit(1);
|
|
116321
116936
|
}
|
|
116322
116937
|
try {
|
|
116323
|
-
return new Uint8Array(
|
|
116938
|
+
return new Uint8Array(readFileSync55(resolve14(p)));
|
|
116324
116939
|
} catch (e) {
|
|
116325
116940
|
console.error(`net enroll: could not read --${label} ${p}: ${e instanceof Error ? e.message : e}`);
|
|
116326
116941
|
process.exit(1);
|
|
@@ -116392,6 +117007,9 @@ async function netCommand(rest, flags) {
|
|
|
116392
117007
|
const established = s.established ?? [];
|
|
116393
117008
|
const relayed = s.relayed ?? [];
|
|
116394
117009
|
console.log(`peers: ${s.peers ?? 0} known, ${established.length} with a live session`);
|
|
117010
|
+
if (typeof s.policyEpoch === "number") {
|
|
117011
|
+
console.log(`policy: epoch ${s.policyEpoch}${s.policyEpoch === 0 ? " \u2014 no policy yet, so NO traffic will pass" : ""}`);
|
|
117012
|
+
}
|
|
116395
117013
|
if (relayed.length) {
|
|
116396
117014
|
console.log(`relayed: ${relayed.length} (${relayed.join(", ")}) \u2014 these are NOT direct`);
|
|
116397
117015
|
} else if (established.length) {
|
|
@@ -116871,7 +117489,7 @@ async function netCommand(rest, flags) {
|
|
|
116871
117489
|
function pkgVersion() {
|
|
116872
117490
|
try {
|
|
116873
117491
|
const p = join88(dirname40(fileURLToPath8(import.meta.url)), "..", "package.json");
|
|
116874
|
-
return JSON.parse(
|
|
117492
|
+
return JSON.parse(readFileSync56(p, "utf8")).version ?? "0.0.0";
|
|
116875
117493
|
} catch {
|
|
116876
117494
|
return "0.0.0";
|
|
116877
117495
|
}
|
|
@@ -117117,7 +117735,7 @@ async function captureClaudeSubscription(flags) {
|
|
|
117117
117735
|
});
|
|
117118
117736
|
if (code !== 0) throw new Error(`Claude sign-in did not complete (exit ${code}).`);
|
|
117119
117737
|
const file2 = join88(home, ".credentials.json");
|
|
117120
|
-
if (
|
|
117738
|
+
if (existsSync50(file2)) return readFileSync56(file2, "utf8");
|
|
117121
117739
|
if (process.platform === "darwin") {
|
|
117122
117740
|
const value = execFileSync6("security", [
|
|
117123
117741
|
"find-generic-password",
|
|
@@ -117199,7 +117817,7 @@ async function runnerCommand(rest, flags) {
|
|
|
117199
117817
|
process.exit(1);
|
|
117200
117818
|
}
|
|
117201
117819
|
const oauthFilePath = typeof flags["oauth-file"] === "string" ? resolve15(flags["oauth-file"]) : void 0;
|
|
117202
|
-
const oauthAuthFile = oauthFilePath ?
|
|
117820
|
+
const oauthAuthFile = oauthFilePath ? readFileSync56(oauthFilePath, "utf8") : void 0;
|
|
117203
117821
|
if (oauthAuthFile) {
|
|
117204
117822
|
try {
|
|
117205
117823
|
JSON.parse(oauthAuthFile);
|