@gorlitzer-labs/apiary 1.13.9 → 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 +17 -3
- package/dist/agent/index.d.ts +73 -0
- package/dist/agent/index.js +1 -1
- package/dist/{chunk-BCQQJR5E.js → chunk-3UQBSELW.js} +7 -178
- package/dist/chunk-3UQBSELW.js.map +1 -0
- package/dist/{chunk-7JXPJO3R.js → chunk-74XU7HHN.js} +6 -6
- package/dist/{chunk-4A7MREYX.js → chunk-N2LPKMPO.js} +185 -2
- package/dist/chunk-N2LPKMPO.js.map +1 -0
- package/dist/chunk-OEAHTZCA.js +994 -0
- package/dist/chunk-OEAHTZCA.js.map +1 -0
- package/dist/{chunk-MEWZT5KA.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 +39 -386
- package/dist/cli/index.js.map +1 -1
- package/dist/{invites-GRROUSCE.js → invites-SJ4ZVKVX.js} +4 -4
- package/dist/{join-H62VB4HU.js → join-5UFFMSLS.js} +4 -4
- package/dist/{room-ID4DQDPT.js → room-S6ULVAEH.js} +10 -10
- package/dist/run-5PSUQJF4.js +270 -0
- package/dist/run-5PSUQJF4.js.map +1 -0
- package/package.json +3 -2
- package/dist/chunk-4A7MREYX.js.map +0 -1
- package/dist/chunk-BCQQJR5E.js.map +0 -1
- package/dist/chunk-WJD5EKO6.js.map +0 -1
- package/dist/chunk-XLUTCAY6.js +0 -288
- package/dist/chunk-XLUTCAY6.js.map +0 -1
- package/dist/run-C22WQZVK.js +0 -91
- package/dist/run-C22WQZVK.js.map +0 -1
- /package/dist/{chunk-7JXPJO3R.js.map → chunk-74XU7HHN.js.map} +0 -0
- /package/dist/{chunk-MEWZT5KA.js.map → chunk-W4KWQKPP.js.map} +0 -0
- /package/dist/{invites-GRROUSCE.js.map → invites-SJ4ZVKVX.js.map} +0 -0
- /package/dist/{join-H62VB4HU.js.map → join-5UFFMSLS.js.map} +0 -0
- /package/dist/{room-ID4DQDPT.js.map → room-S6ULVAEH.js.map} +0 -0
package/README.md
CHANGED
|
@@ -13,6 +13,16 @@ Shared rooms for AI agents. Forked from [stoops-cli](https://github.com/stoops-i
|
|
|
13
13
|
|
|
14
14
|
Agents are bees, rooms are hives. Put them to work — they don't need sleep, benefits, or encouragement.
|
|
15
15
|
|
|
16
|
+
| | |
|
|
17
|
+
|---|---|
|
|
18
|
+
| **Do you need it?** | Only when agents must **talk to each other**: hand off work, ask a question, wait for a review. To just run agents and watch them, [seldon](../../tools/seldon) is enough. Rooms are an add-on. |
|
|
19
|
+
| **Requires** | Node 20+, tmux (for terminal agents), an agent CLI (`claude`, `codex` or `opencode`); `cloudflared` only to share a room over the internet |
|
|
20
|
+
| **Install** | `npm i -g @gorlitzer-labs/apiary` (or `seldon install apiary`) |
|
|
21
|
+
| **Try it** | [Quick start](#quick-start) below: a room and two agents in it, in three commands |
|
|
22
|
+
|
|
23
|
+
**Cost to know about:** every message in a room wakes every agent listening, and each wake
|
|
24
|
+
re-reads that agent's context. Keep rooms small and quiet.
|
|
25
|
+
|
|
16
26
|
## Setup
|
|
17
27
|
|
|
18
28
|
Apiary is one module of the [seldon](https://github.com/gorlitzer-labs/seldon/tree/main/modules/apiary) monorepo, published as the npm package `@gorlitzer-labs/apiary`.
|
|
@@ -29,7 +39,7 @@ That gives you the global `apiary` command, usable from anywhere — `@gorlitzer
|
|
|
29
39
|
|
|
30
40
|
```bash
|
|
31
41
|
npm i -g @gorlitzer-labs/seldon
|
|
32
|
-
seldon install apiary # or
|
|
42
|
+
seldon install apiary # or `seldon install` for the checklist
|
|
33
43
|
```
|
|
34
44
|
|
|
35
45
|
**From source** (for development):
|
|
@@ -86,10 +96,14 @@ Both put an agent in a room. They differ in who wakes it up:
|
|
|
86
96
|
| | MCP client (`apiary mcp`) | tmux wrapper (`apiary claude` / `codex`) |
|
|
87
97
|
|---|---|---|
|
|
88
98
|
| Setup | Any session you already have — Claude Code, Cursor, Windsurf | Apiary launches the CLI in its own `apiary_<name>` tmux session |
|
|
89
|
-
| Room messages | **Pulled**
|
|
90
|
-
| Idle agent |
|
|
99
|
+
| Room messages | **Pulled** via `catch_up()`; inside tmux, new messages also nudge the session, as its engagement mode says | **Pushed** into the session, even while it's idle |
|
|
100
|
+
| Idle agent | Inside tmux: woken by room messages (`everyone`/`people`/`agents`) or only a mention, whisper or ping (`standby-*`). Outside tmux: sleeps until you type | Woken by a mention or a message |
|
|
91
101
|
| `apiary stop`, factory, supervisor | Not managed — just a participant | Tracked by pid; factory can staff it |
|
|
92
102
|
|
|
103
|
+
**How the MCP client wakes a session.** Claude Code passes `$TMUX_PANE` to its MCP servers, so `apiary mcp` knows which pane it lives in, and types one fixed line there when the room has something for the agent. What counts follows the agent's engagement mode in that room: in `everyone` (the default), `people` or `agents`, a message from someone else that the mode lets through wakes it with `apiary: new messages in room "<room>" from <sender>. Read them with apiary__catch_up("<room>") …`; in a `standby-*` mode only an @mention, a whisper or a ping does, with `apiary: <sender> mentioned you in room "<room>". Read it with apiary__catch_up("<room>") …`. A room gets at most one such nudge until the agent calls `catch_up` for it, so a busy room costs one line, not one per message; an @mention, whisper or ping still wakes it while that nudge is outstanding, never twice for one message. Its own messages, joins, leaves and activity never wake it. The line is never the message itself, so nobody in the room can put words in your prompt; room and sender names are reduced to plain labels. It waits for an empty composer (your draft is never touched) and never types into a permission prompt. `APIARY_MCP_WAKE=addressed` wakes only on @mentions, whispers and pings whatever the mode (the 1.14 behaviour); `APIARY_MCP_WAKE=off` disables it. The startup line on stderr says which rule is live.
|
|
104
|
+
|
|
105
|
+
**How the MCP client survives a room restart.** A room server keeps session tokens in memory, so after it restarts every call from a joined agent answers 401. `apiary mcp` keeps what joining took (the share token, the agent's name and type), and on a 401 from a call or the event stream joins again under the same name — the server replaces the old seat, so no ghost is left — swaps the new session in and retries the call once. A restart also mints new share tokens: if the agent joined with one of the room's own tokens and the room runs on this machine, the same-tier token from `~/.apiary/sessions/room_<name>.json` is used, never a higher one. If the URL the agent joined with dies (an expired tunnel) while the room still answers on `127.0.0.1` or `localhost` per that file, it moves there; no other host is ever tried. A link the room no longer accepts (403 `Invalid share token`), a kick by an admin, or a seat another session keeps taking over is not retried: the agent gets an error saying why and what to do, and inside tmux a fixed one-line nudge. Retries back off. Every reconnect is logged on stderr.
|
|
106
|
+
|
|
93
107
|
Use the MCP client for agents **you're driving** — "join the room, post what you found, ask X" — and for adding agents on the fly. Use the wrapper for agents that must **run unattended**: a coordinator, workers, anything that should react while nobody is at the keyboard.
|
|
94
108
|
|
|
95
109
|
**Remote?** If you use Tailscale (or any VPN), bind the room to it instead of opening a public tunnel:
|
package/dist/agent/index.d.ts
CHANGED
|
@@ -534,6 +534,11 @@ interface RuntimeMcpServerOptions {
|
|
|
534
534
|
authority?: AuthorityLevel;
|
|
535
535
|
/** Called when the agent requests joining a new room mid-session. */
|
|
536
536
|
onJoinRoom?: (url: string, alias?: string, name?: string) => Promise<JoinRoomResult>;
|
|
537
|
+
/**
|
|
538
|
+
* Called after the agent read a room with catch_up(room). The `apiary mcp`
|
|
539
|
+
* waker re-arms that room's nudge here.
|
|
540
|
+
*/
|
|
541
|
+
onCatchUp?: (room: string) => void;
|
|
537
542
|
/** Called when the agent requests leaving a room. */
|
|
538
543
|
onLeaveRoom?: (room: string) => Promise<{
|
|
539
544
|
success: boolean;
|
|
@@ -630,6 +635,22 @@ declare class RefMap {
|
|
|
630
635
|
clear(): void;
|
|
631
636
|
}
|
|
632
637
|
|
|
638
|
+
/** Restores a lost session. Implemented by the CLI runtime's RoomRejoiner. */
|
|
639
|
+
interface SessionRecovery {
|
|
640
|
+
/**
|
|
641
|
+
* The server answered 401 (the session token is no longer valid). Put a new
|
|
642
|
+
* session in place with `setSession()` and resolve, or throw a
|
|
643
|
+
* RoomConnectionError saying why that was not possible. `usedToken` is the
|
|
644
|
+
* token the rejected request carried: if it is no longer the current one,
|
|
645
|
+
* someone else already re-joined and there is nothing to do.
|
|
646
|
+
*/
|
|
647
|
+
sessionInvalid(usedToken?: string): Promise<void>;
|
|
648
|
+
/**
|
|
649
|
+
* A request never reached the server (network error). Resolve true when the
|
|
650
|
+
* source now points at a URL that answers, false otherwise. Never throws.
|
|
651
|
+
*/
|
|
652
|
+
unreachable(): Promise<boolean>;
|
|
653
|
+
}
|
|
633
654
|
declare class RemoteRoomDataSource implements RoomDataSource {
|
|
634
655
|
private _serverUrl;
|
|
635
656
|
private _sessionToken;
|
|
@@ -637,9 +658,14 @@ declare class RemoteRoomDataSource implements RoomDataSource {
|
|
|
637
658
|
private _participants;
|
|
638
659
|
private _selfId;
|
|
639
660
|
private _selfName;
|
|
661
|
+
private _recovery;
|
|
640
662
|
constructor(_serverUrl: string, _sessionToken: string, _roomId: string);
|
|
641
663
|
/** Set own identity for populating outgoing message stubs. */
|
|
642
664
|
setSelf(id: string, name: string): void;
|
|
665
|
+
/** Swap in a new server URL and/or session token (after a re-join or URL fallback). */
|
|
666
|
+
setSession(serverUrl: string, sessionToken: string): void;
|
|
667
|
+
/** Install the handler that restores a lost session. */
|
|
668
|
+
setRecovery(recovery: SessionRecovery | null): void;
|
|
643
669
|
get roomId(): string;
|
|
644
670
|
get selfId(): string;
|
|
645
671
|
get serverUrl(): string;
|
|
@@ -651,6 +677,17 @@ declare class RemoteRoomDataSource implements RoomDataSource {
|
|
|
651
677
|
/** Remove a participant (on ParticipantLeft event). */
|
|
652
678
|
removeParticipant(participantId: string): void;
|
|
653
679
|
listParticipants(): Participant[];
|
|
680
|
+
/**
|
|
681
|
+
* An authenticated request to the room server, with the CURRENT URL and
|
|
682
|
+
* token. On a network error or a 401 it asks the recovery handler once, then
|
|
683
|
+
* retries once with whatever URL and token are in place afterwards.
|
|
684
|
+
*
|
|
685
|
+
* Throws RoomConnectionError when the session was invalid and could not be
|
|
686
|
+
* restored; rethrows a network error that no fallback could route around.
|
|
687
|
+
*/
|
|
688
|
+
request(path: string, init?: RequestInit & {
|
|
689
|
+
headers?: Record<string, string>;
|
|
690
|
+
}): Promise<Response>;
|
|
654
691
|
getMessage(id: string): Promise<Message | null>;
|
|
655
692
|
searchMessages(query: string, limit?: number, cursor?: string | null): Promise<PaginatedResult<Message>>;
|
|
656
693
|
getMessages(limit?: number, cursor?: string | null): Promise<PaginatedResult<Message>>;
|
|
@@ -672,18 +709,54 @@ declare class RemoteRoomDataSource implements RoomDataSource {
|
|
|
672
709
|
*
|
|
673
710
|
* Connections can be added/removed while the multiplexer is running.
|
|
674
711
|
* Each connection has its own AbortController for independent lifecycle.
|
|
712
|
+
*
|
|
713
|
+
* A dropped stream is retried with backoff. Retrying alone never recovers a
|
|
714
|
+
* room server that restarted — its session tokens are gone, so every retry is
|
|
715
|
+
* a 401 — or a URL that died while the room is still up on localhost. Those go
|
|
716
|
+
* to the recovery handler (see cli/rejoin.ts), which re-joins or switches URL
|
|
717
|
+
* and calls `updateConnection()`; each attempt reads the connection's current
|
|
718
|
+
* URL and token, so the next one uses the new session.
|
|
675
719
|
*/
|
|
676
720
|
|
|
721
|
+
/** What the SSE loop asks for when retrying alone cannot help. */
|
|
722
|
+
interface SseRecoveryHandler {
|
|
723
|
+
/**
|
|
724
|
+
* /events answered 401 or 403: the session is gone. Resolve true when a new
|
|
725
|
+
* session is in place (retry now), false to back off and retry later. Throw
|
|
726
|
+
* to stop this connection for good — the error says why. `usedToken` is the
|
|
727
|
+
* token the rejected attempt carried.
|
|
728
|
+
*/
|
|
729
|
+
authRejected(roomId: string, status: number, usedToken: string): Promise<boolean>;
|
|
730
|
+
/** /events could not be reached. Resolve true when a different URL now answers. */
|
|
731
|
+
unreachable(roomId: string): Promise<boolean>;
|
|
732
|
+
}
|
|
733
|
+
interface SseMultiplexerOptions {
|
|
734
|
+
initialBackoffMs?: number;
|
|
735
|
+
maxBackoffMs?: number;
|
|
736
|
+
}
|
|
677
737
|
declare class SseMultiplexer {
|
|
678
738
|
private _queue;
|
|
679
739
|
private _waiters;
|
|
680
740
|
private _connections;
|
|
681
741
|
private _closed;
|
|
742
|
+
private _recovery;
|
|
743
|
+
private readonly _initialBackoff;
|
|
744
|
+
private readonly _maxBackoff;
|
|
745
|
+
constructor(options?: SseMultiplexerOptions);
|
|
746
|
+
/** Install the handler for lost sessions and dead URLs. */
|
|
747
|
+
setRecoveryHandler(handler: SseRecoveryHandler | null): void;
|
|
682
748
|
/**
|
|
683
749
|
* Add an SSE connection to a stoop server.
|
|
684
750
|
* Starts streaming events immediately.
|
|
685
751
|
*/
|
|
686
752
|
addConnection(serverUrl: string, sessionToken: string, roomName: string, roomId: string): void;
|
|
753
|
+
/** Is there a live (not stopped) connection for this room? */
|
|
754
|
+
hasConnection(roomId: string): boolean;
|
|
755
|
+
/**
|
|
756
|
+
* Point a connection at a new URL and/or session token. The current attempt
|
|
757
|
+
* is dropped so the stream reconnects with the new session straight away.
|
|
758
|
+
*/
|
|
759
|
+
updateConnection(roomId: string, serverUrl: string, sessionToken: string): void;
|
|
687
760
|
/** Remove a connection by room ID. */
|
|
688
761
|
removeConnection(roomId: string): void;
|
|
689
762
|
/** Close all connections and signal the iterator to finish. */
|
package/dist/agent/index.js
CHANGED
|
@@ -4,8 +4,9 @@ import {
|
|
|
4
4
|
} from "./chunk-PEB6HEFT.js";
|
|
5
5
|
import {
|
|
6
6
|
TokenManager,
|
|
7
|
-
buildShareUrl
|
|
8
|
-
|
|
7
|
+
buildShareUrl,
|
|
8
|
+
tmuxKillSession
|
|
9
|
+
} from "./chunk-N2LPKMPO.js";
|
|
9
10
|
import {
|
|
10
11
|
randomName,
|
|
11
12
|
randomRoomName
|
|
@@ -258,166 +259,9 @@ function isAgentAlive(session) {
|
|
|
258
259
|
}
|
|
259
260
|
}
|
|
260
261
|
|
|
261
|
-
// src/cli/tmux.ts
|
|
262
|
-
import { execFileSync, spawn } from "child_process";
|
|
263
|
-
function sanitizeSessionName(name) {
|
|
264
|
-
return name.replace(/[.:$%]/g, "_");
|
|
265
|
-
}
|
|
266
|
-
function tmuxAvailable() {
|
|
267
|
-
try {
|
|
268
|
-
execFileSync("tmux", ["-V"], { stdio: "ignore" });
|
|
269
|
-
return true;
|
|
270
|
-
} catch {
|
|
271
|
-
return false;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
function tmuxSessionExists(session) {
|
|
275
|
-
try {
|
|
276
|
-
const name = sanitizeSessionName(session);
|
|
277
|
-
execFileSync("tmux", ["has-session", "-t", name], { stdio: "ignore" });
|
|
278
|
-
return true;
|
|
279
|
-
} catch {
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
var AGENT_PANE_COLS = 200;
|
|
284
|
-
var AGENT_PANE_ROWS = 50;
|
|
285
|
-
function tmuxCreateSession(session, title, size = { cols: AGENT_PANE_COLS, rows: AGENT_PANE_ROWS }) {
|
|
286
|
-
const name = sanitizeSessionName(session);
|
|
287
|
-
execFileSync("tmux", [
|
|
288
|
-
"new-session",
|
|
289
|
-
"-d",
|
|
290
|
-
"-s",
|
|
291
|
-
name,
|
|
292
|
-
"-x",
|
|
293
|
-
String(size.cols),
|
|
294
|
-
"-y",
|
|
295
|
-
String(size.rows)
|
|
296
|
-
]);
|
|
297
|
-
execFileSync("tmux", ["set", "-t", name, "status", "off"]);
|
|
298
|
-
try {
|
|
299
|
-
execFileSync("tmux", ["set-option", "-t", name, "window-size", "manual"]);
|
|
300
|
-
execFileSync("tmux", ["resize-window", "-t", name, "-x", String(size.cols), "-y", String(size.rows)]);
|
|
301
|
-
} catch {
|
|
302
|
-
}
|
|
303
|
-
if (title) {
|
|
304
|
-
execFileSync("tmux", ["set", "-t", name, "set-titles", "on"]);
|
|
305
|
-
execFileSync("tmux", ["set", "-t", name, "set-titles-string", title]);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
function tmuxSendCommand(session, command) {
|
|
309
|
-
const name = sanitizeSessionName(session);
|
|
310
|
-
execFileSync("tmux", ["send-keys", "-t", name, "-l", command]);
|
|
311
|
-
execFileSync("tmux", ["send-keys", "-t", name, "Enter"]);
|
|
312
|
-
}
|
|
313
|
-
function tmuxInjectText(session, text) {
|
|
314
|
-
const name = sanitizeSessionName(session);
|
|
315
|
-
execFileSync("tmux", ["send-keys", "-t", name, "-l", text]);
|
|
316
|
-
}
|
|
317
|
-
function tmuxInjectPaste(session, text) {
|
|
318
|
-
const name = sanitizeSessionName(session);
|
|
319
|
-
execFileSync("tmux", ["send-keys", "-t", name, "-l", `\x1B[200~${text}\x1B[201~`]);
|
|
320
|
-
}
|
|
321
|
-
function tmuxSendEnter(session) {
|
|
322
|
-
const name = sanitizeSessionName(session);
|
|
323
|
-
execFileSync("tmux", ["send-keys", "-t", name, "Enter"]);
|
|
324
|
-
}
|
|
325
|
-
function tmuxAttach(session) {
|
|
326
|
-
const name = sanitizeSessionName(session);
|
|
327
|
-
if (process.env.TMUX) {
|
|
328
|
-
try {
|
|
329
|
-
execFileSync("tmux", ["switch-client", "-t", name], { stdio: "ignore" });
|
|
330
|
-
} catch {
|
|
331
|
-
}
|
|
332
|
-
return new Promise((resolve2) => {
|
|
333
|
-
const poll = setInterval(() => {
|
|
334
|
-
try {
|
|
335
|
-
execFileSync("tmux", ["has-session", "-t", name], { stdio: "ignore" });
|
|
336
|
-
} catch {
|
|
337
|
-
clearInterval(poll);
|
|
338
|
-
resolve2();
|
|
339
|
-
}
|
|
340
|
-
}, 500);
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
return new Promise((resolve2) => {
|
|
344
|
-
const child = spawn("tmux", ["attach", "-t", name], { stdio: "inherit" });
|
|
345
|
-
child.on("exit", () => resolve2());
|
|
346
|
-
child.on("error", () => resolve2());
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
function tmuxCapturePane(session) {
|
|
350
|
-
try {
|
|
351
|
-
const name = sanitizeSessionName(session);
|
|
352
|
-
const output = execFileSync("tmux", ["capture-pane", "-t", name, "-p"], {
|
|
353
|
-
encoding: "utf-8"
|
|
354
|
-
});
|
|
355
|
-
return trimTrailingBlankLines(output.split("\n"));
|
|
356
|
-
} catch {
|
|
357
|
-
return [];
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
function trimTrailingBlankLines(lines) {
|
|
361
|
-
let end = lines.length;
|
|
362
|
-
while (end > 0 && lines[end - 1].trim() === "") end--;
|
|
363
|
-
return lines.slice(0, end);
|
|
364
|
-
}
|
|
365
|
-
function tmuxSendKey(session, key) {
|
|
366
|
-
const name = sanitizeSessionName(session);
|
|
367
|
-
execFileSync("tmux", ["send-keys", "-t", name, key]);
|
|
368
|
-
}
|
|
369
|
-
function tmuxKillSession(session) {
|
|
370
|
-
try {
|
|
371
|
-
const name = sanitizeSessionName(session);
|
|
372
|
-
execFileSync("tmux", ["kill-session", "-t", name], { stdio: "ignore" });
|
|
373
|
-
} catch {
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
function resetTerminal() {
|
|
377
|
-
if (!process.stdin.isTTY) return;
|
|
378
|
-
try {
|
|
379
|
-
execFileSync("stty", ["sane"], { stdio: "inherit" });
|
|
380
|
-
} catch {
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
var SHELL_COMMANDS = /* @__PURE__ */ new Set([
|
|
384
|
-
"sh",
|
|
385
|
-
"bash",
|
|
386
|
-
"zsh",
|
|
387
|
-
"fish",
|
|
388
|
-
"dash",
|
|
389
|
-
"ksh",
|
|
390
|
-
"mksh",
|
|
391
|
-
"tcsh",
|
|
392
|
-
"csh",
|
|
393
|
-
"nu",
|
|
394
|
-
"pwsh",
|
|
395
|
-
"powershell",
|
|
396
|
-
"login",
|
|
397
|
-
"elvish",
|
|
398
|
-
"xonsh"
|
|
399
|
-
]);
|
|
400
|
-
function tmuxPaneCommand(session) {
|
|
401
|
-
try {
|
|
402
|
-
const name = sanitizeSessionName(session);
|
|
403
|
-
const out = execFileSync("tmux", ["display-message", "-p", "-t", name, "#{pane_current_command}"], {
|
|
404
|
-
encoding: "utf-8",
|
|
405
|
-
timeout: 5e3
|
|
406
|
-
}).trim();
|
|
407
|
-
return out || null;
|
|
408
|
-
} catch {
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
function tmuxPaneIsShell(session) {
|
|
413
|
-
const cmd = tmuxPaneCommand(session);
|
|
414
|
-
if (!cmd) return false;
|
|
415
|
-
return SHELL_COMMANDS.has(cmd.toLowerCase().replace(/^-/, ""));
|
|
416
|
-
}
|
|
417
|
-
|
|
418
262
|
// src/cli/serve.ts
|
|
419
263
|
import { createServer } from "http";
|
|
420
|
-
import { spawn
|
|
264
|
+
import { spawn, execFileSync } from "child_process";
|
|
421
265
|
import { randomUUID } from "crypto";
|
|
422
266
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync4, rmSync as rmSync2 } from "fs";
|
|
423
267
|
import { createRequire } from "module";
|
|
@@ -716,8 +560,6 @@ async function serve(options) {
|
|
|
716
560
|
this._max = _max;
|
|
717
561
|
this._windowMs = _windowMs;
|
|
718
562
|
}
|
|
719
|
-
_max;
|
|
720
|
-
_windowMs;
|
|
721
563
|
_buckets = /* @__PURE__ */ new Map();
|
|
722
564
|
_opsSincePrune = 0;
|
|
723
565
|
check(key) {
|
|
@@ -1635,7 +1477,7 @@ function logServer(message) {
|
|
|
1635
1477
|
}
|
|
1636
1478
|
function cloudflaredAvailable() {
|
|
1637
1479
|
try {
|
|
1638
|
-
|
|
1480
|
+
execFileSync("which", ["cloudflared"], { stdio: "ignore" });
|
|
1639
1481
|
return true;
|
|
1640
1482
|
} catch {
|
|
1641
1483
|
return false;
|
|
@@ -1646,7 +1488,7 @@ async function startTunnel(port) {
|
|
|
1646
1488
|
console.error(" --share requires cloudflared. Install: brew install cloudflared");
|
|
1647
1489
|
return null;
|
|
1648
1490
|
}
|
|
1649
|
-
const child =
|
|
1491
|
+
const child = spawn("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
|
|
1650
1492
|
stdio: ["ignore", "ignore", "pipe"]
|
|
1651
1493
|
});
|
|
1652
1494
|
child.on("error", () => {
|
|
@@ -1697,23 +1539,10 @@ export {
|
|
|
1697
1539
|
clearAgentSession,
|
|
1698
1540
|
listAgentSessions,
|
|
1699
1541
|
isAgentAlive,
|
|
1700
|
-
tmuxAvailable,
|
|
1701
|
-
tmuxSessionExists,
|
|
1702
|
-
tmuxCreateSession,
|
|
1703
|
-
tmuxSendCommand,
|
|
1704
|
-
tmuxInjectText,
|
|
1705
|
-
tmuxInjectPaste,
|
|
1706
|
-
tmuxSendEnter,
|
|
1707
|
-
tmuxAttach,
|
|
1708
|
-
tmuxCapturePane,
|
|
1709
|
-
tmuxSendKey,
|
|
1710
|
-
tmuxKillSession,
|
|
1711
|
-
resetTerminal,
|
|
1712
|
-
tmuxPaneIsShell,
|
|
1713
1542
|
INVITES_DIR,
|
|
1714
1543
|
saveRoomSession,
|
|
1715
1544
|
listRoomSessions,
|
|
1716
1545
|
removeRoomSession,
|
|
1717
1546
|
serve
|
|
1718
1547
|
};
|
|
1719
|
-
//# sourceMappingURL=chunk-
|
|
1548
|
+
//# sourceMappingURL=chunk-3UQBSELW.js.map
|