@gorlitzer-labs/apiary 1.13.7 → 1.13.8

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.
Files changed (28) hide show
  1. package/README.md +31 -6
  2. package/dist/agent/index.js +2 -2
  3. package/dist/{chunk-7JXPJO3R.js → chunk-3O76FESR.js} +4 -4
  4. package/dist/{chunk-4A7MREYX.js → chunk-63Q3KJ4W.js} +2 -2
  5. package/dist/{chunk-BCQQJR5E.js → chunk-FTN3D6WM.js} +3 -3
  6. package/dist/{chunk-MEWZT5KA.js → chunk-J3T2PS3M.js} +2 -2
  7. package/dist/{chunk-TISBXM4R.js → chunk-LN6ATWE3.js} +157 -157
  8. package/dist/chunk-LN6ATWE3.js.map +1 -0
  9. package/dist/{chunk-HTNCLNPX.js → chunk-XKFWEK7W.js} +4 -4
  10. package/dist/{chunk-TMXQDGMN.js → chunk-YP4K2A7G.js} +30 -30
  11. package/dist/chunk-YP4K2A7G.js.map +1 -0
  12. package/dist/cli/index.js +15 -15
  13. package/dist/{invites-GRROUSCE.js → invites-YZWCUC67.js} +5 -5
  14. package/dist/{join-H62VB4HU.js → join-FSKOS5BO.js} +5 -5
  15. package/dist/{room-ID4DQDPT.js → room-MAUAV2XD.js} +6 -6
  16. package/dist/{run-UJO7NO3C.js → run-4EJB2KC3.js} +5 -5
  17. package/package.json +1 -1
  18. package/dist/chunk-TISBXM4R.js.map +0 -1
  19. package/dist/chunk-TMXQDGMN.js.map +0 -1
  20. /package/dist/{chunk-7JXPJO3R.js.map → chunk-3O76FESR.js.map} +0 -0
  21. /package/dist/{chunk-4A7MREYX.js.map → chunk-63Q3KJ4W.js.map} +0 -0
  22. /package/dist/{chunk-BCQQJR5E.js.map → chunk-FTN3D6WM.js.map} +0 -0
  23. /package/dist/{chunk-MEWZT5KA.js.map → chunk-J3T2PS3M.js.map} +0 -0
  24. /package/dist/{chunk-HTNCLNPX.js.map → chunk-XKFWEK7W.js.map} +0 -0
  25. /package/dist/{invites-GRROUSCE.js.map → invites-YZWCUC67.js.map} +0 -0
  26. /package/dist/{join-H62VB4HU.js.map → join-FSKOS5BO.js.map} +0 -0
  27. /package/dist/{room-ID4DQDPT.js.map → room-MAUAV2XD.js.map} +0 -0
  28. /package/dist/{run-UJO7NO3C.js.map → run-4EJB2KC3.js.map} +0 -0
package/README.md CHANGED
@@ -55,7 +55,7 @@ Changed code? Just `make build` (or `npm run build`) — the `npm link` symlink
55
55
 
56
56
  ## Quick start
57
57
 
58
- **1. Add apiary to your MCP config** (`~/.claude/mcp.json` or project `.mcp.json`):
58
+ **1. Add apiary to your MCP config** (`~/.claude.json` for every Claude Code session, or project `.mcp.json`):
59
59
  ```json
60
60
  {
61
61
  "mcpServers": {
@@ -68,6 +68,8 @@ Changed code? Just `make build` (or `npm run build`) — the `npm link` symlink
68
68
  }
69
69
  ```
70
70
 
71
+ The name is optional — `["mcp"]` alone gives each session a random name, and the agent can pick its own with `join_room(url, name)`. Leave out `--admin` for agents that shouldn't kick, mute, or promote.
72
+
71
73
  **2. Start a room:**
72
74
  ```bash
73
75
  apiary room brood-box
@@ -75,6 +77,21 @@ apiary room brood-box
75
77
 
76
78
  **3. Paste the room URL to any agent.** It calls `join_room()` and participates. No tmux, no wrapper — just MCP tools.
77
79
 
80
+ The room server still has to be running — `apiary mcp` is only a client. One `apiary room` / `apiary serve` per room, left up, and any number of MCP clients can drop in and out with the URL.
81
+
82
+ ### MCP client or tmux wrapper?
83
+
84
+ Both put an agent in a room. They differ in who wakes it up:
85
+
86
+ | | MCP client (`apiary mcp`) | tmux wrapper (`apiary claude` / `codex`) |
87
+ |---|---|---|
88
+ | 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** — seen only when the agent calls `catch_up()` | **Pushed** into the session, even while it's idle |
90
+ | Idle agent | Sleeps at the prompt until you type | Woken by a mention or a message |
91
+ | `apiary stop`, factory, supervisor | Not managed — just a participant | Tracked by pid; factory can staff it |
92
+
93
+ 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
+
78
95
  **Remote?** If you use Tailscale (or any VPN), bind the room to it instead of opening a public tunnel:
79
96
 
80
97
  ```bash
@@ -155,21 +172,29 @@ The primary way to connect any MCP client (Claude Code, Cursor, Windsurf, etc.)
155
172
 
156
173
  **Per-project setup** — add to `.mcp.json` in the repo root instead if you only want it for specific projects.
157
174
 
158
- **Other MCP clients** (Cursor, Windsurf, etc.) — use `npx` if `apiary` isn't globally linked:
175
+ **Other MCP clients** (Cursor, Windsurf, etc.) — Cursor reads `~/.cursor/mcp.json`. GUI apps launched from the Dock don't inherit your shell's PATH, so the `apiary` launcher can't find `node` — give it the absolute path and a PATH:
159
176
 
160
177
  ```json
161
178
  {
162
179
  "mcpServers": {
163
180
  "apiary": {
164
- "type": "stdio",
165
- "command": "npx",
166
- "args": ["apiary", "mcp", "WorkerBee", "--admin"]
181
+ "command": "/Users/you/Library/pnpm/apiary",
182
+ "args": ["mcp"],
183
+ "env": { "PATH": "/Users/you/Library/pnpm:/opt/homebrew/bin:/usr/bin:/bin" }
167
184
  }
168
185
  }
169
186
  }
170
187
  ```
171
188
 
172
- Then tell the agent a room URL — it calls `join_room()` and participates. Events are pull-based via `catch_up()`. The EventProcessor runs in the background classifying and buffering events, the agent pulls them when ready.
189
+ (`which apiary` and `which node` give you the two paths.) Without a global install, use `npx` — with the **scoped** name:
190
+
191
+ ```json
192
+ "args": ["-y", "@gorlitzer-labs/apiary", "mcp"]
193
+ ```
194
+
195
+ Never `npx apiary`: the unscoped `apiary` on npm is an unrelated third-party package, so that line downloads and runs someone else's code.
196
+
197
+ Then tell the agent a room URL — it calls `join_room()` and participates. Events are pull-based via `catch_up()` (see [MCP client or tmux wrapper?](#mcp-client-or-tmux-wrapper)). The EventProcessor runs in the background classifying and buffering events, the agent pulls them when ready.
173
198
 
174
199
  #### A note on `--dangerously-skip-permissions`
175
200
 
@@ -8,13 +8,13 @@ import {
8
8
  StoopsEngagement,
9
9
  classifyEvent,
10
10
  createRuntimeMcpServer
11
- } from "../chunk-TISBXM4R.js";
11
+ } from "../chunk-LN6ATWE3.js";
12
12
  import {
13
13
  contentPartsToString,
14
14
  formatEvent,
15
15
  messageRef,
16
16
  participantLabel
17
- } from "../chunk-TMXQDGMN.js";
17
+ } from "../chunk-YP4K2A7G.js";
18
18
  import "../chunk-BZ4VDWC2.js";
19
19
  export {
20
20
  EventMultiplexer,
@@ -10,16 +10,16 @@ import {
10
10
  tmuxInjectText,
11
11
  tmuxSendEnter,
12
12
  tmuxSessionExists
13
- } from "./chunk-BCQQJR5E.js";
13
+ } from "./chunk-FTN3D6WM.js";
14
14
  import {
15
15
  extractToken
16
- } from "./chunk-4A7MREYX.js";
16
+ } from "./chunk-63Q3KJ4W.js";
17
17
  import {
18
18
  randomName
19
19
  } from "./chunk-D7DXZQWT.js";
20
20
  import {
21
21
  can
22
- } from "./chunk-TMXQDGMN.js";
22
+ } from "./chunk-YP4K2A7G.js";
23
23
 
24
24
  // src/cli/join.ts
25
25
  import { randomUUID } from "crypto";
@@ -2018,4 +2018,4 @@ export {
2018
2018
  HEADLESS_READY,
2019
2019
  join
2020
2020
  };
2021
- //# sourceMappingURL=chunk-7JXPJO3R.js.map
2021
+ //# sourceMappingURL=chunk-3O76FESR.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TIER_ORDER
3
- } from "./chunk-TMXQDGMN.js";
3
+ } from "./chunk-YP4K2A7G.js";
4
4
 
5
5
  // src/cli/auth.ts
6
6
  import { randomBytes } from "crypto";
@@ -149,4 +149,4 @@ export {
149
149
  buildShareUrl,
150
150
  extractToken
151
151
  };
152
- //# sourceMappingURL=chunk-4A7MREYX.js.map
152
+ //# sourceMappingURL=chunk-63Q3KJ4W.js.map
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  TokenManager,
7
7
  buildShareUrl
8
- } from "./chunk-4A7MREYX.js";
8
+ } from "./chunk-63Q3KJ4W.js";
9
9
  import {
10
10
  randomName,
11
11
  randomRoomName
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  can,
15
15
  formatTimestamp
16
- } from "./chunk-TMXQDGMN.js";
16
+ } from "./chunk-YP4K2A7G.js";
17
17
  import {
18
18
  AttachmentSchema,
19
19
  createEvent
@@ -1716,4 +1716,4 @@ export {
1716
1716
  removeRoomSession,
1717
1717
  serve
1718
1718
  };
1719
- //# sourceMappingURL=chunk-BCQQJR5E.js.map
1719
+ //# sourceMappingURL=chunk-FTN3D6WM.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  INVITES_DIR
3
- } from "./chunk-BCQQJR5E.js";
3
+ } from "./chunk-FTN3D6WM.js";
4
4
 
5
5
  // src/cli/invites.ts
6
6
  import { existsSync, readFileSync, unlinkSync } from "fs";
@@ -92,4 +92,4 @@ export {
92
92
  invitePrompt,
93
93
  deliverInvite
94
94
  };
95
- //# sourceMappingURL=chunk-MEWZT5KA.js.map
95
+ //# sourceMappingURL=chunk-J3T2PS3M.js.map
@@ -4,55 +4,125 @@ import {
4
4
  formatEvent,
5
5
  formatTimestamp,
6
6
  messageRef
7
- } from "./chunk-TMXQDGMN.js";
7
+ } from "./chunk-YP4K2A7G.js";
8
8
  import {
9
9
  EVENT_ROLE,
10
10
  EventCategory,
11
11
  createEvent
12
12
  } from "./chunk-BZ4VDWC2.js";
13
13
 
14
- // src/agent/room-data-source.ts
15
- var LocalRoomDataSource = class {
16
- constructor(_room, _channel) {
17
- this._room = _room;
18
- this._channel = _channel;
19
- }
20
- _room;
21
- _channel;
22
- get roomId() {
23
- return this._room.roomId;
24
- }
25
- get selfId() {
26
- return this._channel.participantId;
27
- }
28
- /** Direct access to the underlying Room (for backward compat / internal use). */
29
- get room() {
30
- return this._room;
31
- }
32
- /** Direct access to the underlying Channel (for backward compat / internal use). */
33
- get channel() {
34
- return this._channel;
35
- }
36
- listParticipants() {
37
- return this._room.listParticipants();
38
- }
39
- async getMessage(id) {
40
- return this._room.getMessage(id);
14
+ // src/agent/sse-multiplexer.ts
15
+ var SseMultiplexer = class {
16
+ _queue = [];
17
+ _waiters = [];
18
+ _connections = /* @__PURE__ */ new Map();
19
+ _closed = false;
20
+ /**
21
+ * Add an SSE connection to a stoop server.
22
+ * Starts streaming events immediately.
23
+ */
24
+ addConnection(serverUrl, sessionToken, roomName, roomId) {
25
+ if (this._connections.has(roomId) || this._closed) return;
26
+ const abortController = new AbortController();
27
+ const loopPromise = this._sseLoop(serverUrl, sessionToken, roomName, roomId, abortController.signal);
28
+ this._connections.set(roomId, { serverUrl, sessionToken, roomName, roomId, abortController, loopPromise });
41
29
  }
42
- async searchMessages(query, limit = 10, cursor = null) {
43
- return this._room.searchMessages(query, limit, cursor);
30
+ /** Remove a connection by room ID. */
31
+ removeConnection(roomId) {
32
+ const entry = this._connections.get(roomId);
33
+ if (!entry) return;
34
+ entry.abortController.abort();
35
+ this._connections.delete(roomId);
44
36
  }
45
- async getMessages(limit = 30, cursor = null) {
46
- return this._room.listMessages(limit, cursor);
37
+ /** Close all connections and signal the iterator to finish. */
38
+ close() {
39
+ this._closed = true;
40
+ for (const [, entry] of this._connections) {
41
+ entry.abortController.abort();
42
+ }
43
+ this._connections.clear();
44
+ for (const waiter of this._waiters) {
45
+ waiter.resolve(null);
46
+ }
47
+ this._waiters = [];
47
48
  }
48
- async getEvents(category = null, limit = 50, cursor = null) {
49
- return this._room.listEvents(category, limit, cursor);
49
+ async _sseLoop(serverUrl, sessionToken, roomName, roomId, signal) {
50
+ const INITIAL_BACKOFF = 1e3;
51
+ const MAX_BACKOFF = 3e4;
52
+ let backoff = INITIAL_BACKOFF;
53
+ while (!signal.aborted && !this._closed) {
54
+ try {
55
+ const res = await fetch(`${serverUrl}/events`, {
56
+ method: "POST",
57
+ headers: {
58
+ Accept: "text/event-stream",
59
+ Authorization: `Bearer ${sessionToken}`
60
+ },
61
+ signal
62
+ });
63
+ if (!res.ok || !res.body) {
64
+ throw new Error(`SSE connect failed: ${res.status}`);
65
+ }
66
+ backoff = INITIAL_BACKOFF;
67
+ const reader = res.body.getReader();
68
+ const decoder = new TextDecoder();
69
+ let buffer = "";
70
+ while (!signal.aborted) {
71
+ const { done, value } = await reader.read();
72
+ if (done) break;
73
+ buffer += decoder.decode(value, { stream: true });
74
+ const parts = buffer.split("\n\n");
75
+ buffer = parts.pop();
76
+ for (const part of parts) {
77
+ const dataLine = part.split("\n").find((l) => l.startsWith("data: "));
78
+ if (!dataLine) continue;
79
+ try {
80
+ const event = JSON.parse(dataLine.slice(6));
81
+ this._push({ roomId, roomName, event });
82
+ } catch {
83
+ }
84
+ }
85
+ }
86
+ } catch (err) {
87
+ if (signal.aborted) break;
88
+ }
89
+ if (!signal.aborted && !this._closed) {
90
+ await new Promise((r) => setTimeout(r, backoff));
91
+ backoff = Math.min(backoff * 2, MAX_BACKOFF);
92
+ }
93
+ }
50
94
  }
51
- async sendMessage(content, replyToId, image, attachments, recipients) {
52
- return this._channel.sendMessage(content, replyToId, image ?? void 0, attachments, recipients);
95
+ _push(labeled) {
96
+ if (this._closed) return;
97
+ if (this._waiters.length > 0) {
98
+ const waiter = this._waiters.shift();
99
+ waiter.resolve(labeled);
100
+ } else {
101
+ this._queue.push(labeled);
102
+ }
53
103
  }
54
- async emitEvent(event) {
55
- await this._channel.emit(event);
104
+ [Symbol.asyncIterator]() {
105
+ return {
106
+ next: () => {
107
+ if (this._queue.length > 0) {
108
+ return Promise.resolve({ value: this._queue.shift(), done: false });
109
+ }
110
+ if (this._closed) {
111
+ return Promise.resolve({ value: void 0, done: true });
112
+ }
113
+ return new Promise((resolve) => {
114
+ this._waiters.push({
115
+ resolve: (value) => {
116
+ if (this._closed || value === null) {
117
+ resolve({ value: void 0, done: true });
118
+ } else {
119
+ resolve({ value, done: false });
120
+ }
121
+ }
122
+ });
123
+ });
124
+ }
125
+ };
56
126
  }
57
127
  };
58
128
 
@@ -138,6 +208,51 @@ function classifyEvent(event, mode, selfId, senderType, senderId) {
138
208
  return classify(event, mode, selfId, senderType, senderId);
139
209
  }
140
210
 
211
+ // src/agent/room-data-source.ts
212
+ var LocalRoomDataSource = class {
213
+ constructor(_room, _channel) {
214
+ this._room = _room;
215
+ this._channel = _channel;
216
+ }
217
+ _room;
218
+ _channel;
219
+ get roomId() {
220
+ return this._room.roomId;
221
+ }
222
+ get selfId() {
223
+ return this._channel.participantId;
224
+ }
225
+ /** Direct access to the underlying Room (for backward compat / internal use). */
226
+ get room() {
227
+ return this._room;
228
+ }
229
+ /** Direct access to the underlying Channel (for backward compat / internal use). */
230
+ get channel() {
231
+ return this._channel;
232
+ }
233
+ listParticipants() {
234
+ return this._room.listParticipants();
235
+ }
236
+ async getMessage(id) {
237
+ return this._room.getMessage(id);
238
+ }
239
+ async searchMessages(query, limit = 10, cursor = null) {
240
+ return this._room.searchMessages(query, limit, cursor);
241
+ }
242
+ async getMessages(limit = 30, cursor = null) {
243
+ return this._room.listMessages(limit, cursor);
244
+ }
245
+ async getEvents(category = null, limit = 50, cursor = null) {
246
+ return this._room.listEvents(category, limit, cursor);
247
+ }
248
+ async sendMessage(content, replyToId, image, attachments, recipients) {
249
+ return this._channel.sendMessage(content, replyToId, image ?? void 0, attachments, recipients);
250
+ }
251
+ async emitEvent(event) {
252
+ await this._channel.emit(event);
253
+ }
254
+ };
255
+
141
256
  // src/agent/remote-room-data-source.ts
142
257
  var RemoteRoomDataSource = class {
143
258
  constructor(_serverUrl, _sessionToken, _roomId) {
@@ -1637,126 +1752,12 @@ async function createStdioRuntimeMcpServer(opts) {
1637
1752
  return { stop, closed };
1638
1753
  }
1639
1754
 
1640
- // src/agent/sse-multiplexer.ts
1641
- var SseMultiplexer = class {
1642
- _queue = [];
1643
- _waiters = [];
1644
- _connections = /* @__PURE__ */ new Map();
1645
- _closed = false;
1646
- /**
1647
- * Add an SSE connection to a stoop server.
1648
- * Starts streaming events immediately.
1649
- */
1650
- addConnection(serverUrl, sessionToken, roomName, roomId) {
1651
- if (this._connections.has(roomId) || this._closed) return;
1652
- const abortController = new AbortController();
1653
- const loopPromise = this._sseLoop(serverUrl, sessionToken, roomName, roomId, abortController.signal);
1654
- this._connections.set(roomId, { serverUrl, sessionToken, roomName, roomId, abortController, loopPromise });
1655
- }
1656
- /** Remove a connection by room ID. */
1657
- removeConnection(roomId) {
1658
- const entry = this._connections.get(roomId);
1659
- if (!entry) return;
1660
- entry.abortController.abort();
1661
- this._connections.delete(roomId);
1662
- }
1663
- /** Close all connections and signal the iterator to finish. */
1664
- close() {
1665
- this._closed = true;
1666
- for (const [, entry] of this._connections) {
1667
- entry.abortController.abort();
1668
- }
1669
- this._connections.clear();
1670
- for (const waiter of this._waiters) {
1671
- waiter.resolve(null);
1672
- }
1673
- this._waiters = [];
1674
- }
1675
- async _sseLoop(serverUrl, sessionToken, roomName, roomId, signal) {
1676
- const INITIAL_BACKOFF = 1e3;
1677
- const MAX_BACKOFF = 3e4;
1678
- let backoff = INITIAL_BACKOFF;
1679
- while (!signal.aborted && !this._closed) {
1680
- try {
1681
- const res = await fetch(`${serverUrl}/events`, {
1682
- method: "POST",
1683
- headers: {
1684
- Accept: "text/event-stream",
1685
- Authorization: `Bearer ${sessionToken}`
1686
- },
1687
- signal
1688
- });
1689
- if (!res.ok || !res.body) {
1690
- throw new Error(`SSE connect failed: ${res.status}`);
1691
- }
1692
- backoff = INITIAL_BACKOFF;
1693
- const reader = res.body.getReader();
1694
- const decoder = new TextDecoder();
1695
- let buffer = "";
1696
- while (!signal.aborted) {
1697
- const { done, value } = await reader.read();
1698
- if (done) break;
1699
- buffer += decoder.decode(value, { stream: true });
1700
- const parts = buffer.split("\n\n");
1701
- buffer = parts.pop();
1702
- for (const part of parts) {
1703
- const dataLine = part.split("\n").find((l) => l.startsWith("data: "));
1704
- if (!dataLine) continue;
1705
- try {
1706
- const event = JSON.parse(dataLine.slice(6));
1707
- this._push({ roomId, roomName, event });
1708
- } catch {
1709
- }
1710
- }
1711
- }
1712
- } catch (err) {
1713
- if (signal.aborted) break;
1714
- }
1715
- if (!signal.aborted && !this._closed) {
1716
- await new Promise((r) => setTimeout(r, backoff));
1717
- backoff = Math.min(backoff * 2, MAX_BACKOFF);
1718
- }
1719
- }
1720
- }
1721
- _push(labeled) {
1722
- if (this._closed) return;
1723
- if (this._waiters.length > 0) {
1724
- const waiter = this._waiters.shift();
1725
- waiter.resolve(labeled);
1726
- } else {
1727
- this._queue.push(labeled);
1728
- }
1729
- }
1730
- [Symbol.asyncIterator]() {
1731
- return {
1732
- next: () => {
1733
- if (this._queue.length > 0) {
1734
- return Promise.resolve({ value: this._queue.shift(), done: false });
1735
- }
1736
- if (this._closed) {
1737
- return Promise.resolve({ value: void 0, done: true });
1738
- }
1739
- return new Promise((resolve) => {
1740
- this._waiters.push({
1741
- resolve: (value) => {
1742
- if (this._closed || value === null) {
1743
- resolve({ value: void 0, done: true });
1744
- } else {
1745
- resolve({ value, done: false });
1746
- }
1747
- }
1748
- });
1749
- });
1750
- }
1751
- };
1752
- }
1753
- };
1754
-
1755
1755
  export {
1756
- LocalRoomDataSource,
1756
+ SseMultiplexer,
1757
1757
  parseEngagementMode,
1758
1758
  StoopsEngagement,
1759
1759
  classifyEvent,
1760
+ LocalRoomDataSource,
1760
1761
  RemoteRoomDataSource,
1761
1762
  buildCatchUpLines,
1762
1763
  EventMultiplexer,
@@ -1765,7 +1766,6 @@ export {
1765
1766
  EventProcessor,
1766
1767
  RUNTIME_TOOL_NAMES,
1767
1768
  createRuntimeMcpServer,
1768
- createStdioRuntimeMcpServer,
1769
- SseMultiplexer
1769
+ createStdioRuntimeMcpServer
1770
1770
  };
1771
- //# sourceMappingURL=chunk-TISBXM4R.js.map
1771
+ //# sourceMappingURL=chunk-LN6ATWE3.js.map