@frod.io/bridge 0.7.1 → 0.7.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.
- package/dist/cli.js +64 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// @frod.io/bridge 0.7.
|
|
2
|
+
// @frod.io/bridge 0.7.3. One file; do not edit: the source is apps/bridge/src in the platform repository.
|
|
3
3
|
import { createRequire as __createRequire } from 'node:module';
|
|
4
4
|
const require = __createRequire(import.meta.url);
|
|
5
5
|
var __create = Object.create;
|
|
@@ -6481,6 +6481,9 @@ function workdirRequestFor(spec) {
|
|
|
6481
6481
|
}
|
|
6482
6482
|
|
|
6483
6483
|
// src/connection.ts
|
|
6484
|
+
var RECONCILE_INTERVAL_MS = 6e4;
|
|
6485
|
+
var RECONCILE_WAIT_MS = 1500;
|
|
6486
|
+
var STOP_BOUND_MS = 1e4;
|
|
6484
6487
|
var HostConnection = class {
|
|
6485
6488
|
constructor(o) {
|
|
6486
6489
|
this.o = o;
|
|
@@ -6498,6 +6501,7 @@ var HostConnection = class {
|
|
|
6498
6501
|
/** Which runtime started each session — a turn must reach the one holding it. */
|
|
6499
6502
|
sessionAdapter = /* @__PURE__ */ new Map();
|
|
6500
6503
|
attempt = 0;
|
|
6504
|
+
reconcileTimer = null;
|
|
6501
6505
|
connectedAt = null;
|
|
6502
6506
|
/** Requests this computer sent to the pod (night 2: permissions), by id. */
|
|
6503
6507
|
nextRequestId = 1;
|
|
@@ -6593,6 +6597,7 @@ var HostConnection = class {
|
|
|
6593
6597
|
const reason = reasonBuf.toString();
|
|
6594
6598
|
this.connectedAt = null;
|
|
6595
6599
|
this.ws = null;
|
|
6600
|
+
this.stopReconciling();
|
|
6596
6601
|
this.failPending(`the connection closed (${code})`);
|
|
6597
6602
|
void this.endAllSessions(`connection closed (${code}${reason ? ` ${reason}` : ""})`);
|
|
6598
6603
|
if (final && (code === 4403 || code === 4404 || final.reason.startsWith("Another computer") || final.reason.startsWith("This computer left") || final.reason.startsWith("This computer was disconnected") || /access was revoked/.test(final.reason))) {
|
|
@@ -6634,6 +6639,8 @@ var HostConnection = class {
|
|
|
6634
6639
|
const hosted = hostedAgentsWords({ bindings, agentName: this.o.config.agentName });
|
|
6635
6640
|
log.event("host.welcome", { host: params.hostName ?? this.o.config.name, host_id: params.hostId ?? null, person: params.person?.name ?? null, workspace: params.workspaceId, agents: bindings.map((b) => `${b.agentName}: ${bindingWords(b)}`), ping_ms: params.pingIntervalMs });
|
|
6636
6641
|
log.info(`Connected \u2014 ${this.o.config.name} hosts ${hosted}`);
|
|
6642
|
+
this.reconcile("welcome");
|
|
6643
|
+
this.startReconciling();
|
|
6637
6644
|
if (typeof params.hostId === "string") {
|
|
6638
6645
|
this.o.onWelcome?.({ hostId: params.hostId, hostName: String(params.hostName ?? this.o.config.name), person: params.person ?? null, bindings, bridge: params.bridge });
|
|
6639
6646
|
}
|
|
@@ -6673,7 +6680,13 @@ var HostConnection = class {
|
|
|
6673
6680
|
const spec = params;
|
|
6674
6681
|
if (this.draining || this.stopping) return fail(`${this.o.config.name} is restarting${this.draining ? " to update itself" : ""} \u2014 try again in a moment`);
|
|
6675
6682
|
if (this.sessions.size >= this.o.config.maxSessions && !this.sessions.has(sid)) {
|
|
6676
|
-
|
|
6683
|
+
const before = this.sessions.size;
|
|
6684
|
+
this.reconcile("desk full");
|
|
6685
|
+
await sleep(RECONCILE_WAIT_MS);
|
|
6686
|
+
log.event("session.reconciled", { session: sid, before, after: this.sessions.size, max: this.o.config.maxSessions });
|
|
6687
|
+
if (this.sessions.size >= this.o.config.maxSessions) {
|
|
6688
|
+
return fail(`${this.o.config.name} is busy (${this.sessions.size} of ${this.o.config.maxSessions} sessions)`);
|
|
6689
|
+
}
|
|
6677
6690
|
}
|
|
6678
6691
|
this.sessions.add(sid);
|
|
6679
6692
|
this.sessionsChanged();
|
|
@@ -6774,7 +6787,10 @@ var HostConnection = class {
|
|
|
6774
6787
|
return;
|
|
6775
6788
|
}
|
|
6776
6789
|
if (method === "session/stop") {
|
|
6777
|
-
|
|
6790
|
+
const reason = typeof params.reason === "string" ? params.reason : "stopped";
|
|
6791
|
+
if (reason === "unknown_session") log.event("session.forgotten_by_pod", { session: sid });
|
|
6792
|
+
await this.adapterOf(sid).stop(sid, reason);
|
|
6793
|
+
this.forget(sid);
|
|
6778
6794
|
return;
|
|
6779
6795
|
}
|
|
6780
6796
|
if (method === "session/permission_decided") {
|
|
@@ -6869,15 +6885,50 @@ var HostConnection = class {
|
|
|
6869
6885
|
}
|
|
6870
6886
|
async endAllSessions(reason) {
|
|
6871
6887
|
if (this.sessions.size === 0) return;
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
this.
|
|
6877
|
-
this.checkouts.delete(sid);
|
|
6888
|
+
const sids = [...this.sessions];
|
|
6889
|
+
log.warn(`Stopping ${sids.length} session(s): ${reason}`);
|
|
6890
|
+
for (const sid of sids) this.forget(sid, { keepAdapter: true });
|
|
6891
|
+
await Promise.all(sids.map(async (sid) => {
|
|
6892
|
+
const adapter = this.sessionAdapter.get(sid) ?? this.adapterOf(sid);
|
|
6878
6893
|
this.sessionAdapter.delete(sid);
|
|
6894
|
+
await Promise.race([
|
|
6895
|
+
adapter.stop(sid, "host_disconnected").catch(() => {
|
|
6896
|
+
}),
|
|
6897
|
+
sleep(STOP_BOUND_MS).then(() => log.warn(`[${sid.slice(0, 8)}] did not stop within ${STOP_BOUND_MS / 1e3} s \u2014 slot freed anyway`))
|
|
6898
|
+
]);
|
|
6899
|
+
}));
|
|
6900
|
+
}
|
|
6901
|
+
/** Free a session's slot and everything this computer remembered about it. Idempotent. */
|
|
6902
|
+
forget(sid, opts = {}) {
|
|
6903
|
+
const had = this.sessions.delete(sid);
|
|
6904
|
+
this.checkouts.delete(sid);
|
|
6905
|
+
this.doorCheckouts.forget(sid);
|
|
6906
|
+
this.sessionDirs.delete(sid);
|
|
6907
|
+
if (!opts.keepAdapter) this.sessionAdapter.delete(sid);
|
|
6908
|
+
if (had) this.sessionsChanged();
|
|
6909
|
+
}
|
|
6910
|
+
/**
|
|
6911
|
+
* No tails: ask the pod about every lane this computer holds. A known
|
|
6912
|
+
* session gets silence; a forgotten one gets `session/stop` back, which
|
|
6913
|
+
* frees its slot through the ordinary path. Sent only while connected.
|
|
6914
|
+
*/
|
|
6915
|
+
reconcile(why) {
|
|
6916
|
+
if (this.sessions.size === 0 || !this.ws || this.ws.readyState !== wrapper_default.OPEN) return;
|
|
6917
|
+
log.event("sessions.reconcile", { why, held: this.sessions.size, max: this.o.config.maxSessions });
|
|
6918
|
+
for (const sid of this.sessions) {
|
|
6919
|
+
this.send({ sid, msg: { jsonrpc: "2.0", method: "session/heartbeat", params: { sessionId: sid } } });
|
|
6920
|
+
}
|
|
6921
|
+
}
|
|
6922
|
+
startReconciling() {
|
|
6923
|
+
this.stopReconciling();
|
|
6924
|
+
this.reconcileTimer = setInterval(() => this.reconcile("interval"), RECONCILE_INTERVAL_MS);
|
|
6925
|
+
this.reconcileTimer.unref?.();
|
|
6926
|
+
}
|
|
6927
|
+
stopReconciling() {
|
|
6928
|
+
if (this.reconcileTimer) {
|
|
6929
|
+
clearInterval(this.reconcileTimer);
|
|
6930
|
+
this.reconcileTimer = null;
|
|
6879
6931
|
}
|
|
6880
|
-
this.sessionsChanged();
|
|
6881
6932
|
}
|
|
6882
6933
|
send(frame) {
|
|
6883
6934
|
const ws = this.ws;
|
|
@@ -7779,7 +7830,7 @@ function samplesInWords(samples) {
|
|
|
7779
7830
|
}
|
|
7780
7831
|
|
|
7781
7832
|
// src/cli.ts
|
|
7782
|
-
var VERSION = "0.7.
|
|
7833
|
+
var VERSION = "0.7.3";
|
|
7783
7834
|
function arg(flag, argv) {
|
|
7784
7835
|
const i = argv.indexOf(flag);
|
|
7785
7836
|
return i >= 0 ? argv[i + 1] : void 0;
|
|
@@ -8129,7 +8180,8 @@ async function cmdLeave() {
|
|
|
8129
8180
|
const r = uninstallWindows();
|
|
8130
8181
|
if (r.removed) log.info("Removed the scheduled task.");
|
|
8131
8182
|
}
|
|
8132
|
-
|
|
8183
|
+
writeConfig({ ...cfg, dormant: { reason: "left", at: (/* @__PURE__ */ new Date()).toISOString() } });
|
|
8184
|
+
log.info(`Left. ${CONFIG_PATH} is kept for reference and marked as left; a new join code starts over.`);
|
|
8133
8185
|
}
|
|
8134
8186
|
async function cmdInstall() {
|
|
8135
8187
|
const cfg = readConfig();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frod.io/bridge",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Your computer is a desk: hosts a Frod.io agent's sessions with your own Claude Code, Codex or Grok Build. Installs and updates itself.",
|
|
6
6
|
"license": "UNLICENSED",
|