agent-comms 3.8.0 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +5 -1
- package/dist/bridges/cc-peer/default-front.d.ts +20 -0
- package/dist/bridges/cc-peer/default-front.d.ts.map +1 -0
- package/dist/bridges/cc-peer/default-front.js +28 -0
- package/dist/bridges/cc-peer/default-front.js.map +1 -0
- package/dist/bridges/cc-peer/front-controller.d.ts +50 -0
- package/dist/bridges/cc-peer/front-controller.d.ts.map +1 -0
- package/dist/bridges/cc-peer/front-controller.js +84 -0
- package/dist/bridges/cc-peer/front-controller.js.map +1 -0
- package/dist/bridges/cc-peer/front-relay.d.ts +42 -0
- package/dist/bridges/cc-peer/front-relay.d.ts.map +1 -0
- package/dist/bridges/cc-peer/front-relay.js +43 -0
- package/dist/bridges/cc-peer/front-relay.js.map +1 -0
- package/dist/bridges/cc-peer/front-runtime.d.ts +18 -0
- package/dist/bridges/cc-peer/front-runtime.d.ts.map +1 -0
- package/dist/bridges/cc-peer/front-runtime.js +76 -0
- package/dist/bridges/cc-peer/front-runtime.js.map +1 -0
- package/dist/bridges/cc-peer/front.d.ts +32 -0
- package/dist/bridges/cc-peer/front.d.ts.map +1 -0
- package/dist/bridges/cc-peer/front.js +37 -0
- package/dist/bridges/cc-peer/front.js.map +1 -0
- package/dist/bridges/cc-peer/run.d.ts.map +1 -1
- package/dist/bridges/cc-peer/run.js +2 -0
- package/dist/bridges/cc-peer/run.js.map +1 -1
- package/dist/bridges/claude-code/channel.d.ts.map +1 -1
- package/dist/bridges/claude-code/channel.js +2 -0
- package/dist/bridges/claude-code/channel.js.map +1 -1
- package/dist/bridges/codex/tool.d.ts.map +1 -1
- package/dist/bridges/codex/tool.js +2 -0
- package/dist/bridges/codex/tool.js.map +1 -1
- package/dist/bridges/mcp/server.d.ts.map +1 -1
- package/dist/bridges/mcp/server.js +2 -0
- package/dist/bridges/mcp/server.js.map +1 -1
- package/dist/bridges/opencode/plugin.d.ts.map +1 -1
- package/dist/bridges/opencode/plugin.js +2 -0
- package/dist/bridges/opencode/plugin.js.map +1 -1
- package/dist/bridges/pi/extension.d.ts.map +1 -1
- package/dist/bridges/pi/extension.js +2 -0
- package/dist/bridges/pi/extension.js.map +1 -1
- package/dist/bridges/user/controller.d.ts.map +1 -1
- package/dist/bridges/user/controller.js +4 -0
- package/dist/bridges/user/controller.js.map +1 -1
- package/dist/core/bridge-mesh.d.ts +9 -0
- package/dist/core/bridge-mesh.d.ts.map +1 -1
- package/dist/core/bridge-mesh.js +14 -1
- package/dist/core/bridge-mesh.js.map +1 -1
- package/dist/core/identity-store.d.ts +8 -0
- package/dist/core/identity-store.d.ts.map +1 -1
- package/dist/core/identity-store.js +18 -0
- package/dist/core/identity-store.js.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/mesh-store.d.ts +4 -0
- package/dist/core/mesh-store.d.ts.map +1 -1
- package/dist/core/mesh-store.js +9 -0
- package/dist/core/mesh-store.js.map +1 -1
- package/dist/core/peer-lifecycle.d.ts +2 -0
- package/dist/core/peer-lifecycle.d.ts.map +1 -1
- package/dist/core/peer-lifecycle.js +1 -0
- package/dist/core/peer-lifecycle.js.map +1 -1
- package/dist/test/bridge-mesh.test.js +16 -2
- package/dist/test/bridge-mesh.test.js.map +1 -1
- package/dist/test/cc-peer-default-front.test.d.ts +2 -0
- package/dist/test/cc-peer-default-front.test.d.ts.map +1 -0
- package/dist/test/cc-peer-default-front.test.js +71 -0
- package/dist/test/cc-peer-default-front.test.js.map +1 -0
- package/dist/test/cc-peer-front-controller.test.d.ts +2 -0
- package/dist/test/cc-peer-front-controller.test.d.ts.map +1 -0
- package/dist/test/cc-peer-front-controller.test.js +206 -0
- package/dist/test/cc-peer-front-controller.test.js.map +1 -0
- package/dist/test/cc-peer-front-relay.test.d.ts +2 -0
- package/dist/test/cc-peer-front-relay.test.d.ts.map +1 -0
- package/dist/test/cc-peer-front-relay.test.js +177 -0
- package/dist/test/cc-peer-front-relay.test.js.map +1 -0
- package/dist/test/cc-peer-front.test.d.ts +2 -0
- package/dist/test/cc-peer-front.test.d.ts.map +1 -0
- package/dist/test/cc-peer-front.test.js +109 -0
- package/dist/test/cc-peer-front.test.js.map +1 -0
- package/dist/test/identity-store.test.js +45 -1
- package/dist/test/identity-store.test.js.map +1 -1
- package/dist/test/mesh-store-orchestration.test.js +27 -0
- package/dist/test/mesh-store-orchestration.test.js.map +1 -1
- package/dist/test/peer-lifecycle.test.js +12 -0
- package/dist/test/peer-lifecycle.test.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/ExaDev/agent-comms)
|
|
4
4
|
[](https://www.npmjs.com/package/agent-comms)
|
|
5
|
-
[](https://github.com/ExaDev/agent-comms/releases/tag/v3.9.0)
|
|
6
6
|
[](https://github.com/ExaDev/agent-comms/actions)
|
|
7
7
|
|
|
8
8
|
Cross-harness communication mesh for LLM agents: rooms, DMs, presence, and visibility over TCP with zero filesystem dependencies.
|
|
@@ -153,6 +153,10 @@ npx agent-comms bridge cc-peer --pid=12345
|
|
|
153
153
|
|
|
154
154
|
One bridge process relays for exactly one local Claude Code session, the same "one bridge process is one agent is one device" model every other bridge here follows. Inbound messages from that session are posted into this bridge's own project room; mesh deliveries addressed to this bridge's agent are relayed back to that same session via `cc-peer`'s own `send()`.
|
|
155
155
|
|
|
156
|
+
### Default cc-peer front
|
|
157
|
+
|
|
158
|
+
A Claude Code session with no agent-comms bridge of its own is still reachable from the mesh: whichever bridge on the machine currently holds the mesh coordinator role fronts every local session it discovers via `cc-peer`'s own roster, using the same `(harness, cwd)` identity slot that session's own `claude-code` bridge would use if it started. Identity belongs to the slot, not to whichever process is currently serving it — the session's own bridge holds the slot when it's live; the front holds it otherwise, and yields the moment a real bridge for that slot appears, so addressing, room membership, and queued deliveries all carry over unchanged across the transition. No configuration is needed: every bridge in this repo wires the front to its own coordinator-role transitions automatically, and a machine with no local Claude Code sessions (or no `cc-peer` sockets at all) runs it as a clean no-op.
|
|
159
|
+
|
|
156
160
|
## Adding a new harness
|
|
157
161
|
|
|
158
162
|
A bridge is two things:
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires the default cc-peer front (agent-comms#157) onto a real bridge's own store, right after construction -- the piece that actually makes "a machine's coordinator bridge fronts local Claude Code sessions by default" true, rather than a capability every bridge builds but nothing ever switches on. Every real bridge entry point (pi, claude-code, mcp, codex, opencode, user, cc-peer's own one-shot command) calls this once, immediately after createBridgeMesh/createBridgeMeshSync.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately the only file outside bridges/cc-peer/ that ever needs to know cc-peer exists: core/mesh-store.ts's onCoordinatorRoleChanged hook is generic (a bare boolean callback), and core/bridge-mesh.ts's factories return a plain MeshStore with no cc-peer awareness at all -- keeping this Node/filesystem/cc-peer-specific capability out of the transport-agnostic core, per this repo's own portable-runtime-boundary convention. A bridge that never becomes this machine's coordinator never starts a front at all; one that does gets it started and stopped automatically as that role comes and goes.
|
|
5
|
+
*/
|
|
6
|
+
import type { MeshStore } from "../../core/mesh-store.js";
|
|
7
|
+
import { type CreateDefaultCcPeerFrontOptions } from "./front-runtime.js";
|
|
8
|
+
import type { CcPeerFront } from "./front-controller.js";
|
|
9
|
+
import type { FrontedSessionRecord } from "./front-controller.js";
|
|
10
|
+
export interface WireDefaultCcPeerFrontOptions {
|
|
11
|
+
coordinatorPort?: number | undefined;
|
|
12
|
+
hubUrl?: string | undefined;
|
|
13
|
+
/** Builds the front itself -- defaults to the real createDefaultCcPeerFront. Overridable so this function's own start/stop wiring against store.onCoordinatorRoleChanged is testable without a real CcPeer/local Claude Code session. */
|
|
14
|
+
createFront?: ((options: Readonly<CreateDefaultCcPeerFrontOptions>) => Pick<CcPeerFront<FrontedSessionRecord>, "start" | "stop">) | undefined;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Builds the store.onCoordinatorRoleChanged callback for a real bridge's own store. Returns the callback rather than assigning it directly, so the caller does the actual store.onCoordinatorRoleChanged = ... assignment itself -- this keeps the store parameter here read-only, matching every other pure-construction function in this file's neighbourhood, instead of this function reaching into the store to mutate it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function wireDefaultCcPeerFront(store: Readonly<Pick<MeshStore, "onError">>, options?: Readonly<WireDefaultCcPeerFrontOptions>): (isCoordinator: boolean) => Promise<void>;
|
|
20
|
+
//# sourceMappingURL=default-front.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-front.d.ts","sourceRoot":"","sources":["../../../src/bridges/cc-peer/default-front.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,MAAM,WAAW,6BAA6B;IAC5C,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,yOAAyO;IACzO,WAAW,CAAC,EACR,CAAC,CACC,OAAO,EAAE,QAAQ,CAAC,+BAA+B,CAAC,KAC/C,IAAI,CAAC,WAAW,CAAC,oBAAoB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC,GAC/D,SAAS,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,EAC3C,OAAO,GAAE,QAAQ,CAAC,6BAA6B,CAAM,GACpD,CAAC,aAAa,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAiB3C"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires the default cc-peer front (agent-comms#157) onto a real bridge's own store, right after construction -- the piece that actually makes "a machine's coordinator bridge fronts local Claude Code sessions by default" true, rather than a capability every bridge builds but nothing ever switches on. Every real bridge entry point (pi, claude-code, mcp, codex, opencode, user, cc-peer's own one-shot command) calls this once, immediately after createBridgeMesh/createBridgeMeshSync.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately the only file outside bridges/cc-peer/ that ever needs to know cc-peer exists: core/mesh-store.ts's onCoordinatorRoleChanged hook is generic (a bare boolean callback), and core/bridge-mesh.ts's factories return a plain MeshStore with no cc-peer awareness at all -- keeping this Node/filesystem/cc-peer-specific capability out of the transport-agnostic core, per this repo's own portable-runtime-boundary convention. A bridge that never becomes this machine's coordinator never starts a front at all; one that does gets it started and stopped automatically as that role comes and goes.
|
|
5
|
+
*/
|
|
6
|
+
import { createDefaultCcPeerFront, } from "./front-runtime.js";
|
|
7
|
+
/**
|
|
8
|
+
* Builds the store.onCoordinatorRoleChanged callback for a real bridge's own store. Returns the callback rather than assigning it directly, so the caller does the actual store.onCoordinatorRoleChanged = ... assignment itself -- this keeps the store parameter here read-only, matching every other pure-construction function in this file's neighbourhood, instead of this function reaching into the store to mutate it.
|
|
9
|
+
*/
|
|
10
|
+
export function wireDefaultCcPeerFront(store, options = {}) {
|
|
11
|
+
const createFront = options.createFront ?? createDefaultCcPeerFront;
|
|
12
|
+
const front = createFront({
|
|
13
|
+
coordinatorPort: options.coordinatorPort,
|
|
14
|
+
hubUrl: options.hubUrl,
|
|
15
|
+
onError: (error) => {
|
|
16
|
+
store.onError?.(error);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
return async (isCoordinator) => {
|
|
20
|
+
if (isCoordinator) {
|
|
21
|
+
front.start();
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
await front.stop();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=default-front.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-front.js","sourceRoot":"","sources":["../../../src/bridges/cc-peer/default-front.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,wBAAwB,GAEzB,MAAM,oBAAoB,CAAC;AAe5B;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAA2C,EAC3C,UAAmD,EAAE;IAErD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,wBAAwB,CAAC;IACpE,MAAM,KAAK,GAAG,WAAW,CAAC;QACxB,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACjB,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,KAAK,EAAE,aAAsB,EAAE,EAAE;QACtC,IAAI,aAAa,EAAE,CAAC;YAClB,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACrB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CcPeerFront — coordinator-only periodic controller for the default cc-peer front (agent-comms#157), attaching and detaching fronted sessions on each poll tick. Mirrors StaleAgentChecker's own "coordinator-only periodic probe" shape (owns its own interval timer exclusively, start/stop, no immediate first tick) since both are the same kind of thing: a background job that only ever runs on this machine's current mesh coordinator.
|
|
3
|
+
*
|
|
4
|
+
* Kept free of any real cc-peer/MeshStore construction, exactly like front.ts's pure decision logic this class is built on -- every real I/O (listRoster, probeSlotOwner, attach, detach) is injected, so the attach/detach/yield diffing across ticks is testable with fake timers and no real local Claude Code session. front-runtime.ts supplies the real dependencies.
|
|
5
|
+
*/
|
|
6
|
+
import { type CcPeerRosterEntryLike } from "./front.js";
|
|
7
|
+
import type { IdentitySlot } from "../../core/identity-store.js";
|
|
8
|
+
/** The minimum shape a fronted-session record must carry so CcPeerFront can track and route to it, regardless of whatever real MeshStore/CommsTool state a concrete implementation (front-runtime.ts) attaches alongside these fields. */
|
|
9
|
+
export interface FrontedSessionRecord {
|
|
10
|
+
readonly pid: number;
|
|
11
|
+
readonly cwd: string;
|
|
12
|
+
readonly messagingSocketPath: string;
|
|
13
|
+
/** Delivers an inbound cc-peer message addressed to this fronted session into the mesh (the session-to-mesh direction) -- wired by attach() itself, exactly as wireCcPeerBridge's own peer.on("message") handler does for the one-shot bridge command. */
|
|
14
|
+
readonly handleInbound: (message: Readonly<{
|
|
15
|
+
from?: string;
|
|
16
|
+
fromName?: string;
|
|
17
|
+
body: string;
|
|
18
|
+
}>) => void;
|
|
19
|
+
}
|
|
20
|
+
export interface CcPeerFrontDeps<TRecord extends FrontedSessionRecord> {
|
|
21
|
+
/** Enumerates the current local cc-peer roster. Rejects propagate to onError rather than throwing out of the poll timer. */
|
|
22
|
+
listRoster: () => Promise<readonly CcPeerRosterEntryLike[]>;
|
|
23
|
+
/** Read-only probe of a slot's current lock holder -- identity-store.ts's probeSlotOwner in production. */
|
|
24
|
+
probeSlotOwner: (slot: Readonly<IdentitySlot>) => number | undefined;
|
|
25
|
+
/** Builds a fronted-session record for a newly-selected roster entry (mints/loads its identity, wires the mesh store and the inbound relay). Rejects propagate to onError; the entry is retried on the next tick. */
|
|
26
|
+
attach: (entry: Readonly<CcPeerRosterEntryLike>) => Promise<TRecord>;
|
|
27
|
+
/** Tears a fronted-session record down (marks its agent offline, shuts its mesh store down). Rejects propagate to onError. */
|
|
28
|
+
detach: (record: TRecord) => Promise<void>;
|
|
29
|
+
pollIntervalMs?: number | undefined;
|
|
30
|
+
onError?: ((error: Error) => void) | undefined;
|
|
31
|
+
}
|
|
32
|
+
export declare class CcPeerFront<TRecord extends FrontedSessionRecord> {
|
|
33
|
+
private readonly deps;
|
|
34
|
+
private readonly fronted;
|
|
35
|
+
private timer;
|
|
36
|
+
constructor(deps: Readonly<CcPeerFrontDeps<TRecord>>);
|
|
37
|
+
/** Starts the periodic poll (coordinator-only). No-op if already running. Matches StaleAgentChecker's own convention of no immediate first tick -- the first attach happens on the first elapsed interval, not synchronously on start(). */
|
|
38
|
+
start(): void;
|
|
39
|
+
/** Stops the periodic poll and detaches every currently-fronted session -- this side is no longer the coordinator, so it has no business still relaying on any session's behalf. */
|
|
40
|
+
stop(): Promise<void>;
|
|
41
|
+
/** Routes an inbound cc-peer message to whichever fronted session it came from, if any -- the shared front-wide CcPeer instance's own "message" listener calls this directly (front-runtime.ts), since only this class knows the current fronted set. A message matching no fronted session (already detached, or genuinely foreign) is silently dropped -- there's nowhere for it to go. */
|
|
42
|
+
handleInboundMessage(message: Readonly<{
|
|
43
|
+
from?: string;
|
|
44
|
+
fromName?: string;
|
|
45
|
+
body: string;
|
|
46
|
+
}>): void;
|
|
47
|
+
private tick;
|
|
48
|
+
private detachOne;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=front-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"front-controller.d.ts","sourceRoot":"","sources":["../../../src/bridges/cc-peer/front-controller.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAKjE,0OAA0O;AAC1O,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,0PAA0P;IAC1P,QAAQ,CAAC,aAAa,EAAE,CACtB,OAAO,EAAE,QAAQ,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,KAClE,IAAI,CAAC;CACX;AAED,MAAM,WAAW,eAAe,CAAC,OAAO,SAAS,oBAAoB;IACnE,4HAA4H;IAC5H,UAAU,EAAE,MAAM,OAAO,CAAC,SAAS,qBAAqB,EAAE,CAAC,CAAC;IAC5D,2GAA2G;IAC3G,cAAc,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;IACrE,qNAAqN;IACrN,MAAM,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,qBAAqB,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrE,8HAA8H;IAC9H,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAChD;AAMD,qBAAa,WAAW,CAAC,OAAO,SAAS,oBAAoB;IAI/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,KAAK,CAA6C;gBAE7B,IAAI,EAAE,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAErE,4OAA4O;IAC5O,KAAK,IAAI,IAAI;IAOb,oLAAoL;IAC9K,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,6XAA6X;IAC7X,oBAAoB,CAClB,OAAO,EAAE,QAAQ,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GACpE,IAAI;YAKO,IAAI;YA+BJ,SAAS;CAOxB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CcPeerFront — coordinator-only periodic controller for the default cc-peer front (agent-comms#157), attaching and detaching fronted sessions on each poll tick. Mirrors StaleAgentChecker's own "coordinator-only periodic probe" shape (owns its own interval timer exclusively, start/stop, no immediate first tick) since both are the same kind of thing: a background job that only ever runs on this machine's current mesh coordinator.
|
|
3
|
+
*
|
|
4
|
+
* Kept free of any real cc-peer/MeshStore construction, exactly like front.ts's pure decision logic this class is built on -- every real I/O (listRoster, probeSlotOwner, attach, detach) is injected, so the attach/detach/yield diffing across ticks is testable with fake timers and no real local Claude Code session. front-runtime.ts supplies the real dependencies.
|
|
5
|
+
*/
|
|
6
|
+
import { selectSessionsToFront, matchInboundMessageSession, } from "./front.js";
|
|
7
|
+
/** How often the front re-enumerates the local cc-peer roster and re-probes every currently-fronted session's own slot. */
|
|
8
|
+
const DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
9
|
+
function toError(value) {
|
|
10
|
+
return value instanceof Error ? value : new Error(String(value));
|
|
11
|
+
}
|
|
12
|
+
export class CcPeerFront {
|
|
13
|
+
deps;
|
|
14
|
+
fronted = new Map();
|
|
15
|
+
timer;
|
|
16
|
+
constructor(deps) {
|
|
17
|
+
this.deps = deps;
|
|
18
|
+
}
|
|
19
|
+
/** Starts the periodic poll (coordinator-only). No-op if already running. Matches StaleAgentChecker's own convention of no immediate first tick -- the first attach happens on the first elapsed interval, not synchronously on start(). */
|
|
20
|
+
start() {
|
|
21
|
+
if (this.timer)
|
|
22
|
+
return;
|
|
23
|
+
this.timer = setInterval(() => {
|
|
24
|
+
void this.tick();
|
|
25
|
+
}, this.deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
|
|
26
|
+
}
|
|
27
|
+
/** Stops the periodic poll and detaches every currently-fronted session -- this side is no longer the coordinator, so it has no business still relaying on any session's behalf. */
|
|
28
|
+
async stop() {
|
|
29
|
+
if (this.timer) {
|
|
30
|
+
clearInterval(this.timer);
|
|
31
|
+
this.timer = undefined;
|
|
32
|
+
}
|
|
33
|
+
const records = [...this.fronted.values()];
|
|
34
|
+
this.fronted.clear();
|
|
35
|
+
for (const record of records) {
|
|
36
|
+
await this.detachOne(record);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Routes an inbound cc-peer message to whichever fronted session it came from, if any -- the shared front-wide CcPeer instance's own "message" listener calls this directly (front-runtime.ts), since only this class knows the current fronted set. A message matching no fronted session (already detached, or genuinely foreign) is silently dropped -- there's nowhere for it to go. */
|
|
40
|
+
handleInboundMessage(message) {
|
|
41
|
+
const record = matchInboundMessageSession(this.fronted.values(), message);
|
|
42
|
+
record?.handleInbound(message);
|
|
43
|
+
}
|
|
44
|
+
async tick() {
|
|
45
|
+
let roster;
|
|
46
|
+
try {
|
|
47
|
+
roster = await this.deps.listRoster();
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
this.deps.onError?.(toError(err));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const selected = selectSessionsToFront(roster, this.deps.probeSlotOwner);
|
|
54
|
+
const selectedPids = new Set(selected.map((entry) => entry.pid));
|
|
55
|
+
const rosterPids = new Set(roster.map((entry) => entry.pid));
|
|
56
|
+
// Detach anything that either exited (gone from the roster entirely) or yielded (its own slot is now held by a live bridge, so it's no longer in the selected set even though the session process itself is still running).
|
|
57
|
+
for (const [pid, record] of [...this.fronted]) {
|
|
58
|
+
if (rosterPids.has(pid) && selectedPids.has(pid))
|
|
59
|
+
continue;
|
|
60
|
+
this.fronted.delete(pid);
|
|
61
|
+
await this.detachOne(record);
|
|
62
|
+
}
|
|
63
|
+
for (const entry of selected) {
|
|
64
|
+
if (this.fronted.has(entry.pid))
|
|
65
|
+
continue;
|
|
66
|
+
try {
|
|
67
|
+
const record = await this.deps.attach(entry);
|
|
68
|
+
this.fronted.set(entry.pid, record);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
this.deps.onError?.(toError(err));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async detachOne(record) {
|
|
76
|
+
try {
|
|
77
|
+
await this.deps.detach(record);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
this.deps.onError?.(toError(err));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=front-controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"front-controller.js","sourceRoot":"","sources":["../../../src/bridges/cc-peer/front-controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, DI-testable wiring for one fronted session's own cc-peer-to-mesh relay (agent-comms#157) -- the "attach"/"detach" half of the default front, building a FrontedSessionRecord (front-controller.ts) from an already-constructed mesh and the front's shared cc-peer peer. Mirrors bridges/cc-peer/bridge.ts's own wireCcPeerBridge split: kept free of any real CcPeer.create()/createBridgeMeshFromIdentity construction so it's testable against fakes, with front-runtime.ts supplying the real ones.
|
|
3
|
+
*
|
|
4
|
+
* Reuses CcPeerRef/CcPeerInboundMessage from bridge.ts rather than redeclaring them -- the wire shape a shared front-wide peer speaks is identical to the one-shot bridge command's own peer, just addressed by pid instead of a fixed target.
|
|
5
|
+
*/
|
|
6
|
+
import type { CommsTool } from "../../core/tool.js";
|
|
7
|
+
import type { DeliveryEvent } from "../../core/types.js";
|
|
8
|
+
import type { FrontedSessionRecord } from "./front-controller.js";
|
|
9
|
+
import type { CcPeerRosterEntryLike } from "./front.js";
|
|
10
|
+
import type { CcPeerRef } from "./bridge.js";
|
|
11
|
+
/** The narrow slice of MeshStore a fronted session's own record needs -- onDelivery to wire the mesh-to-session direction, setAgentOffline/shutdown for detachFrontedSession's own teardown. */
|
|
12
|
+
export interface FrontRelayStore {
|
|
13
|
+
onDelivery: ((agentId: string, event: DeliveryEvent) => void | Promise<void>) | undefined;
|
|
14
|
+
setAgentOffline: (id: string) => Promise<void>;
|
|
15
|
+
shutdown: () => Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/** The narrow slice of the front's shared CcPeer instance this relay needs -- just send(), since the front-wide "message" listener (front-runtime.ts) is what routes an inbound message to this record's own handleInbound in the first place, not this module. */
|
|
18
|
+
export interface FrontRelayPeer {
|
|
19
|
+
send: (target: Readonly<CcPeerRef>, body: string) => Promise<{
|
|
20
|
+
msgId: string;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export interface FrontedRelayRecord extends FrontedSessionRecord {
|
|
24
|
+
agentId: string;
|
|
25
|
+
roomId: string;
|
|
26
|
+
store: FrontRelayStore;
|
|
27
|
+
}
|
|
28
|
+
export interface BuildFrontedSessionRecordDeps {
|
|
29
|
+
entry: Readonly<CcPeerRosterEntryLike>;
|
|
30
|
+
agentId: string;
|
|
31
|
+
roomId: string;
|
|
32
|
+
store: FrontRelayStore;
|
|
33
|
+
tool: Pick<CommsTool, "handle">;
|
|
34
|
+
peer: FrontRelayPeer;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Wires both relay directions for one fronted session and returns the record CcPeerFront tracks it under. Mesh-to-session: store.onDelivery sends the formatted event to this session's own pid via the shared peer. Session-to-mesh: the returned handleInbound (called by the front's shared "message" listener once it's matched this record by socket path) posts the message into this session's own project room, exactly as wireCcPeerBridge's own peer.on("message") handler does for the one-shot bridge command.
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildFrontedSessionRecord(deps: Readonly<BuildFrontedSessionRecordDeps>): FrontedRelayRecord;
|
|
40
|
+
/** Tears a fronted session's record down: marks its agent offline (a courtesy to peers watching its presence), then shuts its mesh store down. There is no identity lock to release -- attach() never took one (loadIdentityForFront's whole point), so a real bridge for this slot can already have claimed it by the time this runs. */
|
|
41
|
+
export declare function detachFrontedSession(record: Readonly<Pick<FrontedRelayRecord, "agentId" | "store">>): Promise<void>;
|
|
42
|
+
//# sourceMappingURL=front-relay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"front-relay.d.ts","sourceRoot":"","sources":["../../../src/bridges/cc-peer/front-relay.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAwB,SAAS,EAAE,MAAM,aAAa,CAAC;AAEnE,gMAAgM;AAChM,MAAM,WAAW,eAAe;IAC9B,UAAU,EACN,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GACjE,SAAS,CAAC;IACd,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,mQAAmQ;AACnQ,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,CACJ,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,EAC3B,IAAI,EAAE,MAAM,KACT,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,kBAAmB,SAAQ,oBAAoB;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,MAAM,WAAW,6BAA6B;IAC5C,KAAK,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,eAAe,CAAC;IACvB,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAChC,IAAI,EAAE,cAAc,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,QAAQ,CAAC,6BAA6B,CAAC,GAC5C,kBAAkB,CAgCpB;AAED,0UAA0U;AAC1U,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,GAC9D,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, DI-testable wiring for one fronted session's own cc-peer-to-mesh relay (agent-comms#157) -- the "attach"/"detach" half of the default front, building a FrontedSessionRecord (front-controller.ts) from an already-constructed mesh and the front's shared cc-peer peer. Mirrors bridges/cc-peer/bridge.ts's own wireCcPeerBridge split: kept free of any real CcPeer.create()/createBridgeMeshFromIdentity construction so it's testable against fakes, with front-runtime.ts supplying the real ones.
|
|
3
|
+
*
|
|
4
|
+
* Reuses CcPeerRef/CcPeerInboundMessage from bridge.ts rather than redeclaring them -- the wire shape a shared front-wide peer speaks is identical to the one-shot bridge command's own peer, just addressed by pid instead of a fixed target.
|
|
5
|
+
*/
|
|
6
|
+
import { buildAction, formatDeliveryEvent } from "../../core/bridge.js";
|
|
7
|
+
/**
|
|
8
|
+
* Wires both relay directions for one fronted session and returns the record CcPeerFront tracks it under. Mesh-to-session: store.onDelivery sends the formatted event to this session's own pid via the shared peer. Session-to-mesh: the returned handleInbound (called by the front's shared "message" listener once it's matched this record by socket path) posts the message into this session's own project room, exactly as wireCcPeerBridge's own peer.on("message") handler does for the one-shot bridge command.
|
|
9
|
+
*/
|
|
10
|
+
export function buildFrontedSessionRecord(deps) {
|
|
11
|
+
const { entry, agentId, roomId, store, tool, peer } = deps;
|
|
12
|
+
store.onDelivery = (_targetId, event) => {
|
|
13
|
+
void peer.send({ pid: entry.pid }, formatDeliveryEvent(event));
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
pid: entry.pid,
|
|
17
|
+
cwd: entry.cwd,
|
|
18
|
+
messagingSocketPath: entry.messagingSocketPath,
|
|
19
|
+
agentId,
|
|
20
|
+
roomId,
|
|
21
|
+
store,
|
|
22
|
+
handleInbound: (message) => {
|
|
23
|
+
const sender = message.fromName ?? message.from ?? "unknown";
|
|
24
|
+
const action = buildAction({
|
|
25
|
+
action: "send",
|
|
26
|
+
room: roomId,
|
|
27
|
+
content: `${sender}: ${message.body}`,
|
|
28
|
+
});
|
|
29
|
+
void tool.handle({
|
|
30
|
+
agentId,
|
|
31
|
+
harness: "claude-code",
|
|
32
|
+
cwd: entry.cwd,
|
|
33
|
+
pid: process.pid,
|
|
34
|
+
}, action);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Tears a fronted session's record down: marks its agent offline (a courtesy to peers watching its presence), then shuts its mesh store down. There is no identity lock to release -- attach() never took one (loadIdentityForFront's whole point), so a real bridge for this slot can already have claimed it by the time this runs. */
|
|
39
|
+
export async function detachFrontedSession(record) {
|
|
40
|
+
await record.store.setAgentOffline(record.agentId);
|
|
41
|
+
await record.store.shutdown();
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=front-relay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"front-relay.js","sourceRoot":"","sources":["../../../src/bridges/cc-peer/front-relay.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAuCxE;;GAEG;AACH,MAAM,UAAU,yBAAyB,CACvC,IAA6C;IAE7C,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAE3D,KAAK,CAAC,UAAU,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE;QACtC,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC;IAEF,OAAO;QACL,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;QAC9C,OAAO;QACP,MAAM;QACN,KAAK;QACL,aAAa,EAAE,CAAC,OAAuC,EAAE,EAAE;YACzD,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;YAC7D,MAAM,MAAM,GAAG,WAAW,CAAC;gBACzB,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,EAAE;aACtC,CAAC,CAAC;YACH,KAAK,IAAI,CAAC,MAAM,CACd;gBACE,OAAO;gBACP,OAAO,EAAE,aAAa;gBACtB,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,GAAG,EAAE,OAAO,CAAC,GAAG;aACjB,EACD,MAAM,CACP,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED,0UAA0U;AAC1U,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAA+D;IAE/D,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real cc-peer/MeshStore construction for the default coordinator-run front (agent-comms#157) -- the counterpart to bridges/cc-peer/run.ts's own real CcPeer.create()/createBridgeMesh construction for the one-shot `bridge cc-peer` command. Wires front-controller.ts's CcPeerFront (attach/detach diffing across ticks) to front-relay.ts's buildFrontedSessionRecord/detachFrontedSession (one session's own relay) against one shared, front-wide CcPeer instance and a fresh per-session MeshStore built from loadIdentityForFront's lock-free identity.
|
|
3
|
+
*
|
|
4
|
+
* Untested directly, exactly like run.ts's own real construction (see cc-peer-bridge.test.ts's header comment): front.ts, front-controller.ts, and front-relay.ts already carry the front's entire decision/diffing/relay logic under direct DI-based unit tests, so this file's only remaining job -- calling the real cc-peer/core APIs in the right order -- is exercised by actually running a bridge as this machine's coordinator.
|
|
5
|
+
*/
|
|
6
|
+
import { CcPeerFront } from "./front-controller.js";
|
|
7
|
+
import { type FrontedRelayRecord } from "./front-relay.js";
|
|
8
|
+
export interface CreateDefaultCcPeerFrontOptions {
|
|
9
|
+
coordinatorPort?: number | undefined;
|
|
10
|
+
hubUrl?: string | undefined;
|
|
11
|
+
pollIntervalMs?: number | undefined;
|
|
12
|
+
onError?: ((error: Error) => void) | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Builds the default cc-peer front. start()/stop() are safe to call from bridge-mesh.ts's onCoordinatorRoleChanged hook regardless of whether cc-peer itself is usable on this machine: the shared CcPeer instance is created lazily, on the first poll tick that actually needs it (listRoster), not at start() itself, and any construction or attach failure is reported via onError rather than thrown -- "absent sockets" (no local Claude Code sessions at all) is then a clean no-op front rather than a crash of the coordinator that owns it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createDefaultCcPeerFront(options?: Readonly<CreateDefaultCcPeerFrontOptions>): Pick<CcPeerFront<FrontedRelayRecord>, "start" | "stop">;
|
|
18
|
+
//# sourceMappingURL=front-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"front-runtime.d.ts","sourceRoot":"","sources":["../../../src/bridges/cc-peer/front-runtime.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,kBAAkB,CAAC;AAK1B,MAAM,WAAW,+BAA+B;IAC9C,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAChD;AAOD;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,QAAQ,CAAC,+BAA+B,CAAM,GACtD,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,CAsEzD"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real cc-peer/MeshStore construction for the default coordinator-run front (agent-comms#157) -- the counterpart to bridges/cc-peer/run.ts's own real CcPeer.create()/createBridgeMesh construction for the one-shot `bridge cc-peer` command. Wires front-controller.ts's CcPeerFront (attach/detach diffing across ticks) to front-relay.ts's buildFrontedSessionRecord/detachFrontedSession (one session's own relay) against one shared, front-wide CcPeer instance and a fresh per-session MeshStore built from loadIdentityForFront's lock-free identity.
|
|
3
|
+
*
|
|
4
|
+
* Untested directly, exactly like run.ts's own real construction (see cc-peer-bridge.test.ts's header comment): front.ts, front-controller.ts, and front-relay.ts already carry the front's entire decision/diffing/relay logic under direct DI-based unit tests, so this file's only remaining job -- calling the real cc-peer/core APIs in the right order -- is exercised by actually running a bridge as this machine's coordinator.
|
|
5
|
+
*/
|
|
6
|
+
import { CcPeer } from "cc-peer";
|
|
7
|
+
import { createBridgeMeshFromIdentity, ensureRegistered, ensureProjectRoom, } from "../../core/index.js";
|
|
8
|
+
import { loadIdentityForFront, probeSlotOwner, } from "../../core/identity-store.js";
|
|
9
|
+
import { CcPeerFront } from "./front-controller.js";
|
|
10
|
+
import { computeFrontSlot } from "./front.js";
|
|
11
|
+
import { buildFrontedSessionRecord, detachFrontedSession, } from "./front-relay.js";
|
|
12
|
+
/** cc-peer's own registered display name for the front's shared peer -- distinct from any individual fronted session's own agent-comms display name (front-relay.ts's ensureRegistered call uses the session's own cc-peer name/pid for that). */
|
|
13
|
+
const FRONT_PEER_NAME = "agent-comms-front";
|
|
14
|
+
/** Falls back to "claude-code-<pid>" when a session has never picked its own cc-peer display name -- ensureRegistered requires a defaultName, and an unnamed session is still worth fronting under something stable and identifiable. */
|
|
15
|
+
function defaultSessionName(entry) {
|
|
16
|
+
return entry.name ?? `claude-code-${String(entry.pid)}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Builds the default cc-peer front. start()/stop() are safe to call from bridge-mesh.ts's onCoordinatorRoleChanged hook regardless of whether cc-peer itself is usable on this machine: the shared CcPeer instance is created lazily, on the first poll tick that actually needs it (listRoster), not at start() itself, and any construction or attach failure is reported via onError rather than thrown -- "absent sockets" (no local Claude Code sessions at all) is then a clean no-op front rather than a crash of the coordinator that owns it.
|
|
20
|
+
*/
|
|
21
|
+
export function createDefaultCcPeerFront(options = {}) {
|
|
22
|
+
let sharedPeerPromise;
|
|
23
|
+
const front = new CcPeerFront({
|
|
24
|
+
listRoster: async () => {
|
|
25
|
+
const peer = await ensureSharedPeer();
|
|
26
|
+
return peer.roster();
|
|
27
|
+
},
|
|
28
|
+
probeSlotOwner,
|
|
29
|
+
attach: async (entry) => attachSession(entry, await ensureSharedPeer()),
|
|
30
|
+
detach: detachFrontedSession,
|
|
31
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
32
|
+
onError: options.onError,
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
start: () => {
|
|
36
|
+
front.start();
|
|
37
|
+
},
|
|
38
|
+
stop: async () => {
|
|
39
|
+
await front.stop();
|
|
40
|
+
const peer = await sharedPeerPromise?.catch(() => undefined);
|
|
41
|
+
sharedPeerPromise = undefined;
|
|
42
|
+
await peer?.stop();
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
async function ensureSharedPeer() {
|
|
46
|
+
sharedPeerPromise ??= CcPeer.create({ name: FRONT_PEER_NAME }).then((peer) => {
|
|
47
|
+
peer.on("message", (message) => {
|
|
48
|
+
front.handleInboundMessage(message);
|
|
49
|
+
});
|
|
50
|
+
return peer;
|
|
51
|
+
});
|
|
52
|
+
return sharedPeerPromise;
|
|
53
|
+
}
|
|
54
|
+
async function attachSession(entry, peer) {
|
|
55
|
+
const slot = computeFrontSlot(entry.cwd);
|
|
56
|
+
const identity = loadIdentityForFront(slot);
|
|
57
|
+
const { store, tool } = await createBridgeMeshFromIdentity(identity, slot, options.coordinatorPort, options.hubUrl);
|
|
58
|
+
const reg = await ensureRegistered({
|
|
59
|
+
store,
|
|
60
|
+
harness: "claude-code",
|
|
61
|
+
cwd: entry.cwd,
|
|
62
|
+
defaultName: defaultSessionName(entry),
|
|
63
|
+
visibility: "visible",
|
|
64
|
+
});
|
|
65
|
+
const roomId = await ensureProjectRoom(store, reg.agentId, entry.cwd);
|
|
66
|
+
return buildFrontedSessionRecord({
|
|
67
|
+
entry,
|
|
68
|
+
agentId: reg.agentId,
|
|
69
|
+
roomId,
|
|
70
|
+
store,
|
|
71
|
+
tool,
|
|
72
|
+
peer,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=front-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"front-runtime.js","sourceRoot":"","sources":["../../../src/bridges/cc-peer/front-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure decision logic for the default cc-peer front (agent-comms#157): a machine's coordinator bridge relays every local Claude Code session that doesn't already front itself, using the same (harness, cwd) identity slot that session's own agent-comms bridge would use, so addressing carries over unchanged the moment that session's own bridge appears.
|
|
3
|
+
*
|
|
4
|
+
* Kept free of any real cc-peer/MeshStore construction so it's testable without a real local Claude Code session, a real filesystem, or real sockets -- see front-runtime.ts for the stateful controller and the real wiring built on top of these functions.
|
|
5
|
+
*/
|
|
6
|
+
import type { IdentitySlot } from "../../core/identity-store.js";
|
|
7
|
+
/** The slice of cc-peer's own RegistryEntry this module needs. Narrowed rather than importing cc-peer's type directly so this file has no compile-time dependency on the cc-peer package (only front-runtime.ts, which does the real construction, needs that). */
|
|
8
|
+
export interface CcPeerRosterEntryLike {
|
|
9
|
+
pid: number;
|
|
10
|
+
cwd: string;
|
|
11
|
+
name?: string | undefined;
|
|
12
|
+
/** cc-peer's own registry entries for library-backed peers (this front itself, the one-shot `bridge cc-peer` command, an alias-pool worker) always carry the literal string "cc-peer" here -- see buildRegistryEntry in cc-peer's own source. A real interactive Claude Code session, which registers itself natively rather than through the cc-peer library, reports its own Claude Code version instead. */
|
|
13
|
+
version: string;
|
|
14
|
+
messagingSocketPath: string;
|
|
15
|
+
}
|
|
16
|
+
/** True for a roster entry that is itself a cc-peer-library-backed peer (this front's own shared identity, the one-shot bridge command, an alias-pool worker) rather than a real interactive Claude Code session -- see CcPeerRosterEntryLike.version's own doc comment for the mechanism. The front must never try to front one of these: it would compute a spurious claude-code identity slot for a process that isn't a Claude Code session at all, and relay cc-peer traffic into it that nothing there ever reads. */
|
|
17
|
+
export declare function isCcPeerLibraryPeer(entry: Readonly<CcPeerRosterEntryLike>): boolean;
|
|
18
|
+
/** The identity slot a session's own agent-comms bridge would hold if it started right now -- the same (harness, cwd) pair every real claude-code bridge entry point (bridges/claude-code/channel.ts) already constructs. */
|
|
19
|
+
export declare function computeFrontSlot(cwd: string): IdentitySlot;
|
|
20
|
+
/**
|
|
21
|
+
* Filters a cc-peer roster down to the real Claude Code sessions this front should attach to right now: excludes other cc-peer-library peers, and excludes any session whose own slot is already held by a live PID (that session already fronts itself, via its own agent-comms bridge). probeSlotOwner is injected rather than imported directly so this stays a pure function over its inputs -- the real probe (identity-store.ts's probeSlotOwner) reads the filesystem, which has no place in a decision function tested purely over data.
|
|
22
|
+
*/
|
|
23
|
+
export declare function selectSessionsToFront(roster: readonly CcPeerRosterEntryLike[], probeSlotOwner: (slot: Readonly<IdentitySlot>) => number | undefined): CcPeerRosterEntryLike[];
|
|
24
|
+
/**
|
|
25
|
+
* Finds which currently-fronted session an inbound cc-peer message came from, by matching the envelope's own `from` field (always the literal string "uds:" followed by the sender's own listening socket path, per cc-peer's own send() -- see CcPeer.send in cc-peer's source) against each fronted session's messagingSocketPath. Generic over the fronted-session record type so front-runtime.ts's real records (which carry a live MeshStore/CommsTool alongside the roster entry) can be matched directly without this module needing to know their shape.
|
|
26
|
+
*/
|
|
27
|
+
export declare function matchInboundMessageSession<T extends {
|
|
28
|
+
readonly messagingSocketPath: string;
|
|
29
|
+
}>(fronted: Readonly<Iterable<T>>, message: Readonly<{
|
|
30
|
+
from?: string;
|
|
31
|
+
}>): T | undefined;
|
|
32
|
+
//# sourceMappingURL=front.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"front.d.ts","sourceRoot":"","sources":["../../../src/bridges/cc-peer/front.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,mQAAmQ;AACnQ,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,+YAA+Y;IAC/Y,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,4fAA4f;AAC5f,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GACrC,OAAO,CAET;AAED,6NAA6N;AAC7N,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAE1D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,qBAAqB,EAAE,EACxC,cAAc,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,KAAK,MAAM,GAAG,SAAS,GACnE,qBAAqB,EAAE,CAMzB;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,CAAC,SAAS;IAAE,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;CAAE,EAElD,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAC9B,OAAO,EAAE,QAAQ,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACnC,CAAC,GAAG,SAAS,CAMf"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure decision logic for the default cc-peer front (agent-comms#157): a machine's coordinator bridge relays every local Claude Code session that doesn't already front itself, using the same (harness, cwd) identity slot that session's own agent-comms bridge would use, so addressing carries over unchanged the moment that session's own bridge appears.
|
|
3
|
+
*
|
|
4
|
+
* Kept free of any real cc-peer/MeshStore construction so it's testable without a real local Claude Code session, a real filesystem, or real sockets -- see front-runtime.ts for the stateful controller and the real wiring built on top of these functions.
|
|
5
|
+
*/
|
|
6
|
+
/** True for a roster entry that is itself a cc-peer-library-backed peer (this front's own shared identity, the one-shot bridge command, an alias-pool worker) rather than a real interactive Claude Code session -- see CcPeerRosterEntryLike.version's own doc comment for the mechanism. The front must never try to front one of these: it would compute a spurious claude-code identity slot for a process that isn't a Claude Code session at all, and relay cc-peer traffic into it that nothing there ever reads. */
|
|
7
|
+
export function isCcPeerLibraryPeer(entry) {
|
|
8
|
+
return entry.version === "cc-peer";
|
|
9
|
+
}
|
|
10
|
+
/** The identity slot a session's own agent-comms bridge would hold if it started right now -- the same (harness, cwd) pair every real claude-code bridge entry point (bridges/claude-code/channel.ts) already constructs. */
|
|
11
|
+
export function computeFrontSlot(cwd) {
|
|
12
|
+
return { harness: "claude-code", cwd };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Filters a cc-peer roster down to the real Claude Code sessions this front should attach to right now: excludes other cc-peer-library peers, and excludes any session whose own slot is already held by a live PID (that session already fronts itself, via its own agent-comms bridge). probeSlotOwner is injected rather than imported directly so this stays a pure function over its inputs -- the real probe (identity-store.ts's probeSlotOwner) reads the filesystem, which has no place in a decision function tested purely over data.
|
|
16
|
+
*/
|
|
17
|
+
export function selectSessionsToFront(roster, probeSlotOwner) {
|
|
18
|
+
return roster.filter((entry) => {
|
|
19
|
+
if (isCcPeerLibraryPeer(entry))
|
|
20
|
+
return false;
|
|
21
|
+
const slot = computeFrontSlot(entry.cwd);
|
|
22
|
+
return probeSlotOwner(slot) === undefined;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Finds which currently-fronted session an inbound cc-peer message came from, by matching the envelope's own `from` field (always the literal string "uds:" followed by the sender's own listening socket path, per cc-peer's own send() -- see CcPeer.send in cc-peer's source) against each fronted session's messagingSocketPath. Generic over the fronted-session record type so front-runtime.ts's real records (which carry a live MeshStore/CommsTool alongside the roster entry) can be matched directly without this module needing to know their shape.
|
|
27
|
+
*/
|
|
28
|
+
export function matchInboundMessageSession(fronted, message) {
|
|
29
|
+
if (message.from === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
for (const record of fronted) {
|
|
32
|
+
if (message.from === `uds:${record.messagingSocketPath}`)
|
|
33
|
+
return record;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=front.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"front.js","sourceRoot":"","sources":["../../../src/bridges/cc-peer/front.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH,4fAA4f;AAC5f,MAAM,UAAU,mBAAmB,CACjC,KAAsC;IAEtC,OAAO,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC;AACrC,CAAC;AAED,6NAA6N;AAC7N,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAwC,EACxC,cAAoE;IAEpE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QAC7B,IAAI,mBAAmB,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC7C,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACzC,OAAO,cAAc,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,0BAA0B,CAGxC,OAA8B,EAC9B,OAAoC;IAEpC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACjD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,MAAM,CAAC,mBAAmB,EAAE;YAAE,OAAO,MAAM,CAAC;IAC1E,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/bridges/cc-peer/run.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/bridges/cc-peer/run.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoCH,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CA2CzC"}
|
|
@@ -9,6 +9,7 @@ import { CcPeer } from "cc-peer";
|
|
|
9
9
|
import { createBridgeMesh, ensureRegistered, ensureProjectRoom, } from "../../core/index.js";
|
|
10
10
|
import { releaseIdentityLock } from "../../core/identity-store.js";
|
|
11
11
|
import { wireCcPeerBridge } from "./bridge.js";
|
|
12
|
+
import { wireDefaultCcPeerFront } from "./default-front.js";
|
|
12
13
|
const PID_FLAG_PREFIX = "--pid=";
|
|
13
14
|
/** argv layout for `node cli.js bridge cc-peer <target-arg>`: index 0/1 are the node binary and script path, 2 is "bridge", 3 is the bridge id ("cc-peer") itself -- this bridge's own args start one past that. */
|
|
14
15
|
const BRIDGE_ARGS_START_INDEX = 4;
|
|
@@ -32,6 +33,7 @@ export async function run() {
|
|
|
32
33
|
const target = parseTarget(process.argv.slice(BRIDGE_ARGS_START_INDEX));
|
|
33
34
|
const identitySlot = { harness: "cc-peer", cwd: process.cwd() };
|
|
34
35
|
const { store, tool } = await createBridgeMesh(identitySlot);
|
|
36
|
+
store.onCoordinatorRoleChanged = wireDefaultCcPeerFront(store);
|
|
35
37
|
const reg = await ensureRegistered({
|
|
36
38
|
store,
|
|
37
39
|
cwd: process.cwd(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/bridges/cc-peer/run.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAkB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/bridges/cc-peer/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/bridges/claude-code/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/bridges/claude-code/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoJH,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAyJzC"}
|
|
@@ -19,6 +19,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
19
19
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
20
20
|
import { createBridgeMesh, buildAction, ensureRegistered, extractStreamingBehavior, formatDeliveryEvent, isActionableEvent, MCP_TOOL_PARAMS, } from "../../core/index.js";
|
|
21
21
|
import { releaseIdentityLock, } from "../../core/identity-store.js";
|
|
22
|
+
import { wireDefaultCcPeerFront } from "../cc-peer/default-front.js";
|
|
22
23
|
import { tryStartWebServer } from "../user/web/server.js";
|
|
23
24
|
import { nanoid } from "../../core/nanoid.js";
|
|
24
25
|
function isRecord(value) {
|
|
@@ -149,6 +150,7 @@ export async function run() {
|
|
|
149
150
|
cwd: process.cwd(),
|
|
150
151
|
};
|
|
151
152
|
const { store, tool } = await createBridgeMesh(identitySlot);
|
|
153
|
+
store.onCoordinatorRoleChanged = wireDefaultCcPeerFront(store);
|
|
152
154
|
let agentId;
|
|
153
155
|
const claudeCodePid = findClaudeCodePid();
|
|
154
156
|
if (claudeCodePid === undefined) {
|