@gorlitzer-labs/apiary 1.14.0 → 1.15.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/README.md +5 -3
- package/dist/agent/index.d.ts +73 -0
- package/dist/agent/index.js +1 -1
- package/dist/{chunk-LOONM4G3.js → chunk-3UQBSELW.js} +1 -3
- package/dist/{chunk-LOONM4G3.js.map → chunk-3UQBSELW.js.map} +1 -1
- package/dist/{chunk-LNSDH2V5.js → chunk-74XU7HHN.js} +2 -2
- package/dist/{chunk-36WX2UAF.js → chunk-OEAHTZCA.js} +369 -38
- package/dist/chunk-OEAHTZCA.js.map +1 -0
- package/dist/{chunk-XP5F5NCQ.js → chunk-W4KWQKPP.js} +2 -2
- package/dist/{chunk-WJD5EKO6.js → chunk-Z4J6TRJY.js} +154 -41
- package/dist/chunk-Z4J6TRJY.js.map +1 -0
- package/dist/cli/index.js +16 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/{invites-ID5SRM3D.js → invites-SJ4ZVKVX.js} +3 -3
- package/dist/{join-W53OPEHR.js → join-5UFFMSLS.js} +3 -3
- package/dist/{room-XC546NTX.js → room-S6ULVAEH.js} +4 -4
- package/dist/run-5PSUQJF4.js +270 -0
- package/dist/run-5PSUQJF4.js.map +1 -0
- package/package.json +3 -2
- package/dist/chunk-36WX2UAF.js.map +0 -1
- package/dist/chunk-WJD5EKO6.js.map +0 -1
- package/dist/run-6JRFWMAQ.js +0 -164
- package/dist/run-6JRFWMAQ.js.map +0 -1
- /package/dist/{chunk-LNSDH2V5.js.map → chunk-74XU7HHN.js.map} +0 -0
- /package/dist/{chunk-XP5F5NCQ.js.map → chunk-W4KWQKPP.js.map} +0 -0
- /package/dist/{invites-ID5SRM3D.js.map → invites-SJ4ZVKVX.js.map} +0 -0
- /package/dist/{join-W53OPEHR.js.map → join-5UFFMSLS.js.map} +0 -0
- /package/dist/{room-XC546NTX.js.map → room-S6ULVAEH.js.map} +0 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
roomEmoji,
|
|
7
7
|
saveConfig,
|
|
8
8
|
saveRoomSession
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3UQBSELW.js";
|
|
10
10
|
import {
|
|
11
11
|
extractToken,
|
|
12
12
|
tmuxCapturePane,
|
|
@@ -2018,4 +2018,4 @@ export {
|
|
|
2018
2018
|
HEADLESS_READY,
|
|
2019
2019
|
join
|
|
2020
2020
|
};
|
|
2021
|
-
//# sourceMappingURL=chunk-
|
|
2021
|
+
//# sourceMappingURL=chunk-74XU7HHN.js.map
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RemoteRoomDataSource,
|
|
3
|
+
RoomConnectionError,
|
|
3
4
|
buildCatchUpLines
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-Z4J6TRJY.js";
|
|
5
6
|
import {
|
|
6
7
|
extractToken,
|
|
7
8
|
tmuxCapturePane,
|
|
@@ -437,28 +438,349 @@ function describeOutcome(participant, until, outcome) {
|
|
|
437
438
|
}
|
|
438
439
|
}
|
|
439
440
|
|
|
441
|
+
// src/cli/rejoin.ts
|
|
442
|
+
import { existsSync, readFileSync } from "fs";
|
|
443
|
+
import { homedir } from "os";
|
|
444
|
+
import { join } from "path";
|
|
445
|
+
function readLocalRoomSession(roomName) {
|
|
446
|
+
if (!/^[A-Za-z0-9._-]+$/.test(roomName) || roomName.includes("..")) return null;
|
|
447
|
+
const path = join(homedir(), ".apiary", "sessions", `room_${roomName}.json`);
|
|
448
|
+
if (!existsSync(path)) return null;
|
|
449
|
+
try {
|
|
450
|
+
const data = JSON.parse(readFileSync(path, "utf-8"));
|
|
451
|
+
const str = (v) => typeof v === "string" && v ? v : void 0;
|
|
452
|
+
return {
|
|
453
|
+
serverUrl: str(data.serverUrl),
|
|
454
|
+
publicUrl: str(data.publicUrl),
|
|
455
|
+
adminToken: str(data.adminToken),
|
|
456
|
+
memberToken: str(data.memberToken)
|
|
457
|
+
};
|
|
458
|
+
} catch {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
function isLoopbackUrl(url) {
|
|
463
|
+
try {
|
|
464
|
+
const host = new URL(url).hostname;
|
|
465
|
+
return host === "127.0.0.1" || host === "localhost";
|
|
466
|
+
} catch {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function sameOrigin(a, b) {
|
|
471
|
+
try {
|
|
472
|
+
return new URL(a).origin === new URL(b).origin;
|
|
473
|
+
} catch {
|
|
474
|
+
return a === b;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
function localTierOf(shareToken, session) {
|
|
478
|
+
if (!shareToken || !session) return void 0;
|
|
479
|
+
if (session.adminToken === shareToken) return "admin";
|
|
480
|
+
if (session.memberToken === shareToken) return "member";
|
|
481
|
+
return void 0;
|
|
482
|
+
}
|
|
483
|
+
var RoomRejoiner = class {
|
|
484
|
+
constructor(room, deps) {
|
|
485
|
+
this.room = room;
|
|
486
|
+
this.deps = deps;
|
|
487
|
+
this.fetchFn = deps.fetch ?? ((...args) => fetch(...args));
|
|
488
|
+
this.now = deps.now ?? Date.now;
|
|
489
|
+
this.sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
490
|
+
this.readSession = deps.readRoomSession ?? readLocalRoomSession;
|
|
491
|
+
}
|
|
492
|
+
inflight = null;
|
|
493
|
+
lost = null;
|
|
494
|
+
failures = 0;
|
|
495
|
+
cooldownUntil = 0;
|
|
496
|
+
lastRejoinAt = Number.NEGATIVE_INFINITY;
|
|
497
|
+
lastProbeAt = Number.NEGATIVE_INFINITY;
|
|
498
|
+
fetchFn;
|
|
499
|
+
now;
|
|
500
|
+
sleep;
|
|
501
|
+
readSession;
|
|
502
|
+
/** Why the room is lost for good, if it is. */
|
|
503
|
+
get lostError() {
|
|
504
|
+
return this.lost;
|
|
505
|
+
}
|
|
506
|
+
// ── SessionRecovery (RemoteRoomDataSource) ─────────────────────────────
|
|
507
|
+
async sessionInvalid(usedToken) {
|
|
508
|
+
if (usedToken !== void 0 && usedToken !== this.room.sessionToken) return;
|
|
509
|
+
await this.rejoin("session token rejected (401)");
|
|
510
|
+
}
|
|
511
|
+
async unreachable() {
|
|
512
|
+
if (this.lost || this.room.removedBy) return false;
|
|
513
|
+
const now = this.now();
|
|
514
|
+
if (now - this.lastProbeAt < (this.deps.probeIntervalMs ?? 5e3)) return false;
|
|
515
|
+
this.lastProbeAt = now;
|
|
516
|
+
const local = this.readSession(this.room.rejoin.serverRoomName)?.serverUrl;
|
|
517
|
+
if (!local || !isLoopbackUrl(local) || sameOrigin(local, this.room.serverUrl)) return false;
|
|
518
|
+
const base = local.replace(/\/$/, "");
|
|
519
|
+
let res;
|
|
520
|
+
try {
|
|
521
|
+
res = await this.fetchFn(`${base}/participants`, {
|
|
522
|
+
headers: { Authorization: `Bearer ${this.room.sessionToken}` },
|
|
523
|
+
signal: AbortSignal.timeout(5e3)
|
|
524
|
+
});
|
|
525
|
+
} catch {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
await res.body?.cancel().catch(() => {
|
|
529
|
+
});
|
|
530
|
+
if (res.ok) {
|
|
531
|
+
const from = this.room.serverUrl;
|
|
532
|
+
this.swapSession(base, this.room.sessionToken);
|
|
533
|
+
this.log(`room "${this.room.roomName}": ${from} unreachable, moved to ${base} (same session)`);
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
if (res.status === 401) {
|
|
537
|
+
try {
|
|
538
|
+
await this.rejoin(`${this.room.serverUrl} unreachable`);
|
|
539
|
+
return true;
|
|
540
|
+
} catch {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
// ── SseRecoveryHandler (per room) ──────────────────────────────────────
|
|
547
|
+
async authRejected(status, usedToken) {
|
|
548
|
+
if (usedToken !== void 0 && usedToken !== this.room.sessionToken) return true;
|
|
549
|
+
try {
|
|
550
|
+
await this.rejoin(`event stream rejected (${status})`);
|
|
551
|
+
return true;
|
|
552
|
+
} catch (err) {
|
|
553
|
+
if (err instanceof RoomConnectionError && err.permanent) throw err;
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
// ── Re-join ────────────────────────────────────────────────────────────
|
|
558
|
+
/**
|
|
559
|
+
* Join the room again and swap the new session in everywhere. Concurrent
|
|
560
|
+
* callers share one attempt. Throws RoomConnectionError: `permanent` when
|
|
561
|
+
* retrying cannot help, otherwise after bounded retries.
|
|
562
|
+
*/
|
|
563
|
+
rejoin(reason) {
|
|
564
|
+
if (this.lost) return Promise.reject(this.lost);
|
|
565
|
+
const roomLabel = `"${this.room.roomName}"`;
|
|
566
|
+
if (this.room.removedBy) return Promise.reject(this.removed());
|
|
567
|
+
if (this.inflight) return this.inflight;
|
|
568
|
+
const now = this.now();
|
|
569
|
+
if (now - this.lastRejoinAt < (this.deps.replacedWindowMs ?? 3e4)) {
|
|
570
|
+
return Promise.reject(this.loseFor(
|
|
571
|
+
`Your seat in room ${roomLabel} was taken over again right after apiary re-joined it \u2014 another session is probably using the name "${this.room.rejoin.name}". Not re-joining automatically, so the two do not keep evicting each other. Call join_room to take the seat back.`
|
|
572
|
+
));
|
|
573
|
+
}
|
|
574
|
+
if (now < this.cooldownUntil) {
|
|
575
|
+
const secs = Math.ceil((this.cooldownUntil - now) / 1e3);
|
|
576
|
+
return Promise.reject(new RoomConnectionError(
|
|
577
|
+
`Lost the session in room ${roomLabel} and the last re-join failed; apiary retries in ${secs}s. If this persists, call join_room again with the room's share URL.`,
|
|
578
|
+
false
|
|
579
|
+
));
|
|
580
|
+
}
|
|
581
|
+
this.inflight = this.runRejoin(reason).finally(() => {
|
|
582
|
+
this.inflight = null;
|
|
583
|
+
});
|
|
584
|
+
return this.inflight;
|
|
585
|
+
}
|
|
586
|
+
async runRejoin(reason) {
|
|
587
|
+
const roomLabel = `"${this.room.roomName}"`;
|
|
588
|
+
this.log(`room ${roomLabel}: ${reason}; re-joining as "${this.room.rejoin.name}"`);
|
|
589
|
+
const delays = [0, ...this.deps.retryDelaysMs ?? [500, 1500]];
|
|
590
|
+
let lastError = "";
|
|
591
|
+
for (const delay of delays) {
|
|
592
|
+
if (delay > 0) await this.sleep(delay);
|
|
593
|
+
if (this.room.removedBy) break;
|
|
594
|
+
const outcome = await this.attemptOnce();
|
|
595
|
+
if (outcome.permanent) {
|
|
596
|
+
throw this.loseFor(
|
|
597
|
+
`Room ${roomLabel} no longer accepts the share link this agent joined with (${outcome.error}). The room was recreated or the link revoked \u2014 ask for a new share URL and call join_room with it.`
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
if (outcome.done) {
|
|
601
|
+
this.failures = 0;
|
|
602
|
+
this.cooldownUntil = 0;
|
|
603
|
+
this.lastRejoinAt = this.now();
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
lastError = outcome.error;
|
|
607
|
+
}
|
|
608
|
+
if (this.room.removedBy) throw this.removed();
|
|
609
|
+
this.failures += 1;
|
|
610
|
+
const cooldown = Math.min(1e3 * 2 ** (this.failures - 1), this.deps.maxCooldownMs ?? 3e4);
|
|
611
|
+
this.cooldownUntil = this.now() + cooldown;
|
|
612
|
+
this.log(`room ${roomLabel}: re-join failed (${lastError}); next try in ${Math.ceil(cooldown / 1e3)}s`);
|
|
613
|
+
throw new RoomConnectionError(
|
|
614
|
+
`Lost the session in room ${roomLabel} (${reason}) and could not re-join yet: ${lastError}. apiary keeps retrying; if this persists, call join_room again with the room's share URL.`,
|
|
615
|
+
false
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
/** One pass over the candidate addresses and tokens. */
|
|
619
|
+
async attemptOnce() {
|
|
620
|
+
const info = this.room.rejoin;
|
|
621
|
+
const candidates = [
|
|
622
|
+
{ url: this.room.serverUrl, shareToken: info.shareToken }
|
|
623
|
+
];
|
|
624
|
+
const session = this.readSession(info.serverRoomName);
|
|
625
|
+
const local = session?.serverUrl && isLoopbackUrl(session.serverUrl) ? session.serverUrl.replace(/\/$/, "") : void 0;
|
|
626
|
+
if (local && !sameOrigin(local, this.room.serverUrl)) {
|
|
627
|
+
candidates.push({ url: local, shareToken: info.shareToken });
|
|
628
|
+
}
|
|
629
|
+
const fresh = info.localTier === "admin" ? session?.adminToken : info.localTier === "member" ? session?.memberToken : void 0;
|
|
630
|
+
if (local && fresh && fresh !== info.shareToken) {
|
|
631
|
+
candidates.push({ url: local, shareToken: fresh });
|
|
632
|
+
}
|
|
633
|
+
let shareRejected = "";
|
|
634
|
+
let transient = "";
|
|
635
|
+
let unreachable = "";
|
|
636
|
+
for (const c of candidates) {
|
|
637
|
+
const r = await this.joinAt(c.url, c.shareToken);
|
|
638
|
+
if (r.ok) {
|
|
639
|
+
this.apply(r.url, r.shareToken, r.data);
|
|
640
|
+
return { done: true, permanent: false, error: "" };
|
|
641
|
+
}
|
|
642
|
+
if (r.kind === "share-rejected") shareRejected = r.error;
|
|
643
|
+
else if (r.kind === "unreachable") unreachable ||= r.error;
|
|
644
|
+
else transient ||= r.error;
|
|
645
|
+
}
|
|
646
|
+
if (shareRejected && !transient) return { done: false, permanent: true, error: shareRejected };
|
|
647
|
+
return { done: false, permanent: false, error: transient || unreachable || shareRejected || "no address answered" };
|
|
648
|
+
}
|
|
649
|
+
async joinAt(url, shareToken) {
|
|
650
|
+
const body = { type: this.room.rejoin.type, name: this.room.rejoin.name };
|
|
651
|
+
if (shareToken) body.token = shareToken;
|
|
652
|
+
let res;
|
|
653
|
+
try {
|
|
654
|
+
res = await this.fetchFn(`${url}/join`, {
|
|
655
|
+
method: "POST",
|
|
656
|
+
headers: { "Content-Type": "application/json" },
|
|
657
|
+
body: JSON.stringify(body),
|
|
658
|
+
signal: AbortSignal.timeout(1e4)
|
|
659
|
+
});
|
|
660
|
+
} catch (err) {
|
|
661
|
+
return { ok: false, kind: "unreachable", error: `${url} unreachable (${err instanceof Error ? err.message : String(err)})` };
|
|
662
|
+
}
|
|
663
|
+
if (!res.ok) {
|
|
664
|
+
const text = (await res.text().catch(() => "")).slice(0, 200);
|
|
665
|
+
if (res.status === 403 && /invalid share token/i.test(text)) {
|
|
666
|
+
return { ok: false, kind: "share-rejected", error: `403 Invalid share token at ${url}` };
|
|
667
|
+
}
|
|
668
|
+
return { ok: false, kind: "transient", error: `${res.status} from ${url}/join${text ? `: ${text}` : ""}` };
|
|
669
|
+
}
|
|
670
|
+
const data = await res.json().catch(() => ({}));
|
|
671
|
+
if (String(data.roomName ?? "") !== this.room.rejoin.serverRoomName || String(data.roomId ?? "") !== this.room.roomId) {
|
|
672
|
+
await this.fetchFn(`${url}/disconnect`, {
|
|
673
|
+
method: "POST",
|
|
674
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${String(data.sessionToken ?? "")}` },
|
|
675
|
+
body: "{}"
|
|
676
|
+
}).catch(() => {
|
|
677
|
+
});
|
|
678
|
+
return { ok: false, kind: "wrong-room", error: `${url} serves a different room (${String(data.roomName ?? "?")})` };
|
|
679
|
+
}
|
|
680
|
+
return { ok: true, url, shareToken, data };
|
|
681
|
+
}
|
|
682
|
+
/** Swap the new session into the JoinResult, data source, processor and SSE stream. */
|
|
683
|
+
apply(url, shareToken, data) {
|
|
684
|
+
const room = this.room;
|
|
685
|
+
const oldId = room.participantId;
|
|
686
|
+
const token = String(data.sessionToken ?? "");
|
|
687
|
+
const newId = String(data.participantId ?? "");
|
|
688
|
+
const participants = data.participants ?? [];
|
|
689
|
+
room.participantId = newId;
|
|
690
|
+
room.authority = String(data.authority ?? room.authority);
|
|
691
|
+
room.participants = participants;
|
|
692
|
+
if (shareToken !== void 0) room.rejoin.shareToken = shareToken;
|
|
693
|
+
room.dataSource.setParticipants(participants);
|
|
694
|
+
room.dataSource.setSelf(newId, room.rejoin.name);
|
|
695
|
+
const { processor } = this.deps;
|
|
696
|
+
processor.setRoomParticipantId(room.roomId, newId);
|
|
697
|
+
if (processor.participantId === oldId) processor.participantId = newId;
|
|
698
|
+
this.swapSession(url, token);
|
|
699
|
+
this.log(`room "${room.roomName}": re-joined as "${room.rejoin.name}" at ${url}`);
|
|
700
|
+
const mode = processor.getModeForRoom(room.roomId);
|
|
701
|
+
if (mode !== "everyone") {
|
|
702
|
+
this.fetchFn(`${url}/set-mode`, {
|
|
703
|
+
method: "POST",
|
|
704
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
705
|
+
body: JSON.stringify({ mode })
|
|
706
|
+
}).catch(() => {
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
swapSession(url, token) {
|
|
711
|
+
this.room.serverUrl = url;
|
|
712
|
+
this.room.sessionToken = token;
|
|
713
|
+
this.room.dataSource.setSession(url, token);
|
|
714
|
+
this.deps.sseMux.updateConnection(this.room.roomId, url, token);
|
|
715
|
+
}
|
|
716
|
+
removed() {
|
|
717
|
+
return this.loseFor(
|
|
718
|
+
`You were removed from room "${this.room.roomName}" by ${this.room.removedBy}, so apiary will not re-join it. Call join_room again only if you are invited back.`
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
loseFor(message) {
|
|
722
|
+
if (!this.lost) {
|
|
723
|
+
this.lost = new RoomConnectionError(message, true);
|
|
724
|
+
this.log(`room "${this.room.roomName}": giving up \u2014 ${message}`);
|
|
725
|
+
try {
|
|
726
|
+
this.deps.onLost?.(this.room, this.lost);
|
|
727
|
+
} catch {
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return this.lost;
|
|
731
|
+
}
|
|
732
|
+
log(line) {
|
|
733
|
+
this.deps.log?.(line);
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
|
|
440
737
|
// src/cli/runtime-handlers.ts
|
|
738
|
+
function trackRoomEvent(jr, event) {
|
|
739
|
+
if (event.type === "ParticipantJoined") {
|
|
740
|
+
jr.dataSource.addParticipant(event.participant);
|
|
741
|
+
} else if (event.type === "ParticipantLeft") {
|
|
742
|
+
jr.dataSource.removeParticipant(event.participant_id);
|
|
743
|
+
} else if (event.type === "ParticipantKicked") {
|
|
744
|
+
if (event.participant_id === jr.participantId) jr.removedBy = safeName(event.kicked_by);
|
|
745
|
+
jr.dataSource.removeParticipant(event.participant_id);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
function safeName(name) {
|
|
749
|
+
return String(name ?? "").replace(/[^A-Za-z0-9 _.-]/g, "").trim().slice(0, 40) || "an admin";
|
|
750
|
+
}
|
|
441
751
|
function buildRuntimeMcpOptions(ctx) {
|
|
442
752
|
const { processor, sseMux, joinResults, agentName, authority, onRoomJoined } = ctx;
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
753
|
+
const rejoinerFor = (roomId) => joinResults.find((j) => j.roomId === roomId)?.rejoiner;
|
|
754
|
+
sseMux.setRecoveryHandler({
|
|
755
|
+
authRejected: async (roomId, status, usedToken) => {
|
|
756
|
+
const r = rejoinerFor(roomId);
|
|
757
|
+
return r ? r.authRejected(status, usedToken) : false;
|
|
758
|
+
},
|
|
759
|
+
unreachable: async (roomId) => await rejoinerFor(roomId)?.unreachable() ?? false
|
|
760
|
+
});
|
|
761
|
+
const post = async (ds, path, body) => {
|
|
450
762
|
try {
|
|
451
|
-
const res = await
|
|
763
|
+
const res = await ds.request(path, {
|
|
452
764
|
method: "POST",
|
|
453
|
-
headers: { "Content-Type": "application/json"
|
|
765
|
+
headers: { "Content-Type": "application/json" },
|
|
454
766
|
body: JSON.stringify(body)
|
|
455
767
|
});
|
|
456
768
|
if (!res.ok) return { success: false, error: await res.text() };
|
|
457
769
|
return { success: true };
|
|
458
|
-
} catch {
|
|
459
|
-
|
|
770
|
+
} catch (err) {
|
|
771
|
+
const msg = err instanceof Error && err.name === "RoomConnectionError" ? err.message : "Server unreachable.";
|
|
772
|
+
return { success: false, error: msg };
|
|
460
773
|
}
|
|
461
774
|
};
|
|
775
|
+
const adminAction = (build) => async (room, participant) => {
|
|
776
|
+
const conn = processor.resolve(room);
|
|
777
|
+
if (!conn) return { success: false, error: `Unknown room "${room}".` };
|
|
778
|
+
const ds = conn.dataSource;
|
|
779
|
+
const p = conn.dataSource.listParticipants().find((pp) => pp.name === participant);
|
|
780
|
+
if (!p) return { success: false, error: `Unknown participant "${participant}".` };
|
|
781
|
+
const { path, body } = build(p.id);
|
|
782
|
+
return post(ds, path, body);
|
|
783
|
+
};
|
|
462
784
|
return {
|
|
463
785
|
resolver: processor,
|
|
464
786
|
toolOptions: {
|
|
@@ -472,15 +794,9 @@ function buildRuntimeMcpOptions(ctx) {
|
|
|
472
794
|
const conn = processor.resolve(room);
|
|
473
795
|
if (!conn) return { success: false, error: `Unknown room "${room}".` };
|
|
474
796
|
processor.setModeForRoom(conn.dataSource.roomId, mode, false);
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
method: "POST",
|
|
479
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${ds.sessionToken}` },
|
|
480
|
-
body: JSON.stringify({ mode })
|
|
481
|
-
});
|
|
482
|
-
if (!res.ok) return { success: false, error: `Server rejected: ${await res.text()}` };
|
|
483
|
-
} catch {
|
|
797
|
+
const result = await post(conn.dataSource, "/set-mode", { mode });
|
|
798
|
+
if (!result.success && result.error !== "Server unreachable.") {
|
|
799
|
+
return { success: false, error: `Server rejected: ${result.error}` };
|
|
484
800
|
}
|
|
485
801
|
return { success: true };
|
|
486
802
|
},
|
|
@@ -500,10 +816,7 @@ function buildRuntimeMcpOptions(ctx) {
|
|
|
500
816
|
}
|
|
501
817
|
const readState = async () => {
|
|
502
818
|
try {
|
|
503
|
-
const res = await
|
|
504
|
-
headers: { Authorization: `Bearer ${ds.sessionToken}` },
|
|
505
|
-
signal: AbortSignal.timeout(1e4)
|
|
506
|
-
});
|
|
819
|
+
const res = await ds.request("/participants", { signal: AbortSignal.timeout(1e4) });
|
|
507
820
|
if (!res.ok) return "unknown";
|
|
508
821
|
const data = await res.json();
|
|
509
822
|
const found = data.participants?.find((pp) => pp.id === target.id);
|
|
@@ -526,17 +839,7 @@ function buildRuntimeMcpOptions(ctx) {
|
|
|
526
839
|
const ds = conn.dataSource;
|
|
527
840
|
const p = conn.dataSource.listParticipants().find((pp) => pp.name === participant);
|
|
528
841
|
if (!p) return { success: false, error: `Unknown participant "${participant}".` };
|
|
529
|
-
|
|
530
|
-
const res = await fetch(`${ds.serverUrl}/ping`, {
|
|
531
|
-
method: "POST",
|
|
532
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${ds.sessionToken}` },
|
|
533
|
-
body: JSON.stringify({ participantId: p.id })
|
|
534
|
-
});
|
|
535
|
-
if (!res.ok) return { success: false, error: await res.text() };
|
|
536
|
-
return { success: true };
|
|
537
|
-
} catch {
|
|
538
|
-
return { success: false, error: "Server unreachable." };
|
|
539
|
-
}
|
|
842
|
+
return post(ds, "/ping", { participantId: p.id });
|
|
540
843
|
},
|
|
541
844
|
onJoinRoom: async (url, alias, nameOverride) => {
|
|
542
845
|
const token = extractToken(url);
|
|
@@ -566,6 +869,12 @@ function buildRuntimeMcpOptions(ctx) {
|
|
|
566
869
|
const roomAuthority = String(data.authority ?? "member");
|
|
567
870
|
const participants = data.participants ?? [];
|
|
568
871
|
const newParticipantId = String(data.participantId ?? "");
|
|
872
|
+
const existingIdx = joinResults.findIndex((j) => j.roomId === roomId);
|
|
873
|
+
if (existingIdx >= 0) {
|
|
874
|
+
sseMux.removeConnection(roomId);
|
|
875
|
+
processor.disconnectRemoteRoom(roomId);
|
|
876
|
+
joinResults.splice(existingIdx, 1);
|
|
877
|
+
}
|
|
569
878
|
const dataSource = new RemoteRoomDataSource(serverUrl, sessionToken, roomId);
|
|
570
879
|
dataSource.setParticipants(participants);
|
|
571
880
|
dataSource.setSelf(newParticipantId, joinName);
|
|
@@ -576,6 +885,7 @@ function buildRuntimeMcpOptions(ctx) {
|
|
|
576
885
|
const mode = processor.getModeForRoom(roomId) ?? "everyone";
|
|
577
886
|
processor.connectRemoteRoom(dataSource, roomName);
|
|
578
887
|
sseMux.addConnection(serverUrl, sessionToken, roomName, roomId);
|
|
888
|
+
const serverRoomName = String(data.roomName ?? "");
|
|
579
889
|
const jr = {
|
|
580
890
|
serverUrl,
|
|
581
891
|
sessionToken,
|
|
@@ -584,8 +894,27 @@ function buildRuntimeMcpOptions(ctx) {
|
|
|
584
894
|
roomId,
|
|
585
895
|
authority: roomAuthority,
|
|
586
896
|
participants,
|
|
587
|
-
dataSource
|
|
897
|
+
dataSource,
|
|
898
|
+
rejoin: {
|
|
899
|
+
joinUrl: url,
|
|
900
|
+
shareToken: token ?? void 0,
|
|
901
|
+
name: joinName,
|
|
902
|
+
type: "agent",
|
|
903
|
+
serverRoomName,
|
|
904
|
+
localTier: localTierOf(
|
|
905
|
+
token ?? void 0,
|
|
906
|
+
(ctx.rejoinOverrides?.readRoomSession ?? readLocalRoomSession)(serverRoomName)
|
|
907
|
+
)
|
|
908
|
+
}
|
|
588
909
|
};
|
|
910
|
+
jr.rejoiner = new RoomRejoiner(jr, {
|
|
911
|
+
...ctx.rejoinOverrides,
|
|
912
|
+
sseMux,
|
|
913
|
+
processor,
|
|
914
|
+
log: ctx.log,
|
|
915
|
+
onLost: ctx.onRoomLost ? (_room, err) => ctx.onRoomLost(jr, err) : void 0
|
|
916
|
+
});
|
|
917
|
+
dataSource.setRecovery(jr.rejoiner);
|
|
589
918
|
joinResults.push(jr);
|
|
590
919
|
const conn = processor.resolve(roomName);
|
|
591
920
|
let recentLines = [];
|
|
@@ -631,6 +960,7 @@ function buildRuntimeMcpOptions(ctx) {
|
|
|
631
960
|
const idx = joinResults.findIndex((jr) => jr.roomId === roomId);
|
|
632
961
|
if (idx >= 0) {
|
|
633
962
|
const jr = joinResults[idx];
|
|
963
|
+
jr.dataSource.setRecovery(null);
|
|
634
964
|
sseMux.removeConnection(roomId);
|
|
635
965
|
processor.disconnectRemoteRoom(roomId);
|
|
636
966
|
try {
|
|
@@ -658,6 +988,7 @@ function buildRuntimeMcpOptions(ctx) {
|
|
|
658
988
|
export {
|
|
659
989
|
composerStillHolds,
|
|
660
990
|
TmuxBridge,
|
|
991
|
+
trackRoomEvent,
|
|
661
992
|
buildRuntimeMcpOptions
|
|
662
993
|
};
|
|
663
|
-
//# sourceMappingURL=chunk-
|
|
994
|
+
//# sourceMappingURL=chunk-OEAHTZCA.js.map
|