agent-coord-mcp 0.2.1 → 0.3.1
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 +83 -8
- package/dist/server.js +7 -2
- package/dist/server.js.map +1 -1
- package/dist/store.js +19 -1
- package/dist/store.js.map +1 -1
- package/dist/tools.js +345 -42
- package/dist/tools.js.map +1 -1
- package/hooks/tmux-pusher.mjs +273 -0
- package/package.json +2 -1
- package/scripts/spawn-agent.sh +91 -0
- package/scripts/stop-agent.sh +43 -0
- package/src/server.ts +46 -1
- package/src/store.ts +22 -1
- package/src/tools.ts +398 -41
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ State lives in `~/agent-coord/` as JSONL/JSON files, so you can `tail -f` the co
|
|
|
6
6
|
|
|
7
7
|
> **Local-only** — coordination happens through the local filesystem. Agents need to share the same `~/agent-coord/` directory (i.e. same machine, same user). You *can* point `AGENT_COORD_DIR` at a synced/network folder for multi-machine coord, but lockfile semantics over NFS/Dropbox aren't reliable, so it isn't promised.
|
|
8
8
|
>
|
|
9
|
-
> **Works with any MCP client.** The server speaks plain MCP over stdio: Claude Code, Cursor, Cline, Continue, Zed AI, custom SDK apps. Anywhere two or more agents can connect to the same stdio MCP server, they can talk.
|
|
9
|
+
> **Works with any MCP client — and across client types.** The server speaks plain MCP over stdio: Claude Code, Cursor, Cline, Continue, Zed AI, custom SDK apps. Anywhere two or more agents can connect to the same stdio MCP server, they can talk. A Claude Code session, a Cursor agent, and a custom Python SDK worker can all share the same room and DM each other — they're just rows in the same JSONL files.
|
|
10
10
|
>
|
|
11
11
|
> No auth, no encryption. Anything that can read your home directory can read the messages.
|
|
12
12
|
|
|
@@ -55,20 +55,33 @@ If you're building an agent with the official MCP SDKs (`@modelcontextprotocol/s
|
|
|
55
55
|
|
|
56
56
|
| Tool | Purpose |
|
|
57
57
|
| --- | --- |
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
58
|
+
| `join({agentId, project?, role?, attach?, readInbox?})` | **Recommended session-start call.** register + auto-attach (if `$TMUX_PANE` is set) + drain inbox in one round-trip. Pass `attach:false` to skip the transport, `attach:{...}` to override defaults, or omit to let the server auto-detect. |
|
|
59
|
+
| `register({agentId, project?, role?})` | Lower-level: just the registry entry. Use `join` unless you need explicit control. |
|
|
60
|
+
| `unregister({agentId})` | Clean shutdown: detaches any transport and drops the registry entry. |
|
|
61
|
+
| `status({agentId})` | Introspect: registration, attached transport, inbox depth/unread, whether the MCP server is in tmux. Debug "why isn't my DM landing." |
|
|
62
|
+
| `heartbeat({agentId})` | Manual heartbeat. Usually unnecessary — agents with a live transport get heartbeats auto-bumped on every `list_agents`. |
|
|
63
|
+
| `list_agents()` | See all known agents, who looks online, and which `transport` (if any) they have attached. Validates transport pid liveness on every call. |
|
|
61
64
|
| `send_message({from, to?, room?, text})` | If `to` set → that agent's inbox. Else → shared room. |
|
|
62
65
|
| `read_messages({agentId, source, limit?, peek?, sinceTs?})` | Read new messages. `source` is `inbox`/`room`/`status`. Advances cursor unless `peek:true`. |
|
|
63
66
|
| `post_status({agentId, status, detail?})` | Append to the shared status stream (separate from chat). |
|
|
64
67
|
| `wait_for_message({agentId, source, timeoutMs?})` | Block (max 60s) until a new entry appears, then return it. |
|
|
68
|
+
| `attach_agent({agentId, tmuxTarget?, includeRoom?, allowlist?, debounceMs?})` | Start the **tmux-push transport** for this agent — spawns `hooks/tmux-pusher.mjs` so peer DMs get typed into the agent's tmux pane in real time. `tmuxTarget` defaults to the MCP server's own `$TMUX_PANE` if it's running inside tmux, so the most common call is just `attach_agent({agentId:"me"})`. Updates `list_agents` to show `transport: "tmux-push"`. See [tmux push](#active-push-via-tmux-any-cli-agent). |
|
|
69
|
+
| `detach_agent({agentId})` | Stop the tmux-push transport: kill the pusher and clear the transport marker. |
|
|
65
70
|
| `prune({olderThanDays?, removeOrphanInboxes?, dryRun?})` | Trim room/status/inbox JSONL to entries newer than N days (default 7). Removes inbox files for agents no longer in the registry. Pass `dryRun:true` to preview. |
|
|
66
71
|
|
|
67
|
-
##
|
|
72
|
+
## First session checklist
|
|
68
73
|
|
|
69
|
-
|
|
74
|
+
The ergonomic path is the `join` tool. Put this in each agent's `CLAUDE.md` (or equivalent persistent instruction):
|
|
70
75
|
|
|
71
|
-
> Your coord agentId is `frontend`. On session start, call `
|
|
76
|
+
> Your coord agentId is `frontend`. On session start, call `join({agentId:"frontend", project:"...", role:"..."})`. That registers you, drains any unread DMs, and — if you're running inside tmux — attaches the real-time `tmux-push` transport automatically so peers can wake you. On session end, call `unregister({agentId:"frontend"})`.
|
|
77
|
+
|
|
78
|
+
That single call replaces the older three-step ritual (`register` + `read_messages` + `attach_agent`) and Just Works whether you're in tmux or not.
|
|
79
|
+
|
|
80
|
+
If you need to override defaults (custom tmux target, peer allowlist, room delivery, etc.) pass an object: `join({agentId:"frontend", attach:{allowlist:["backend","worker"], includeRoom:true}})`. Pass `attach:false` to opt out entirely.
|
|
81
|
+
|
|
82
|
+
### Convention for agent IDs
|
|
83
|
+
|
|
84
|
+
Use the project's directory name or a short stable slug (e.g. `frontend`, `api`, `worker`).
|
|
72
85
|
|
|
73
86
|
## Tail it from a terminal
|
|
74
87
|
|
|
@@ -111,7 +124,17 @@ Set `AGENT_COORD_DIR=/some/other/path` in the MCP server's env to relocate state
|
|
|
111
124
|
|
|
112
125
|
`wait_for_message` is the cheap path: one tool call, server-side `fs.watch` + 500ms poll, capped at 60s. The model only pays for one round-trip per wait.
|
|
113
126
|
|
|
114
|
-
But the model is fundamentally turn-based — there's no async push that wakes
|
|
127
|
+
But the model is fundamentally turn-based — there's no async push that wakes a fully idle agent. For *passive* presence (react when pinged without being told to poll) wire a client-side hook that drains unread messages into the next turn.
|
|
128
|
+
|
|
129
|
+
### Why this is a feature, not a bug
|
|
130
|
+
|
|
131
|
+
Delivery is **always tied to a turn the agent is already taking** — a user prompt, a tool result, a Stop-hook continuation. That means:
|
|
132
|
+
|
|
133
|
+
- **The human stays in control.** Peer agents can't silently kick off work in your session while you're away from the keyboard. Messages land the next time *you* (or your agent's own lifecycle) drive a turn.
|
|
134
|
+
- **Billing stays predictable.** No background daemon spinning up extra completions on your subscription.
|
|
135
|
+
- **Mixed-client coordination works naturally.** A Claude Code session driven by a human, a headless Claude Agent SDK worker running on a cron, and a Cursor agent in another repo can all participate in the same room — each on its own cadence, each respecting its own client's turn model. The MCP doesn't care who's on the other end of the socket.
|
|
136
|
+
|
|
137
|
+
If you genuinely need an always-on responder (e.g. a worker that should react within seconds of any DM), build *that specific agent* on the Claude Agent SDK or a similar library where you own the loop, and let it talk to your interactive Claude Code sessions through this same MCP.
|
|
115
138
|
|
|
116
139
|
### Claude Code hook
|
|
117
140
|
|
|
@@ -155,6 +178,58 @@ Set `AGENT_COORD_ID` to whatever you passed to `register({agentId})`. Set `AGENT
|
|
|
155
178
|
|
|
156
179
|
Caveat: the hook writes the cursor file directly (atomic tmp+rename) without taking the MCP server's lockfile, so if the agent calls `read_messages` at the exact instant the hook runs, one of them may double-deliver a message. In practice hooks fire between turns and tool calls fire during them, so this is rare. The hook also banners injected messages with *"do not call read_messages for them again"* to keep the agent from re-fetching.
|
|
157
180
|
|
|
181
|
+
## Active push via tmux (any CLI agent)
|
|
182
|
+
|
|
183
|
+
Hooks are reactive — they only fire when the agent is already taking a turn. If you need peer messages to *wake* an idle agent (no human typing, agent already stopped), the working option is to run the agent inside a tmux pane and have a tiny daemon type incoming messages into that pane.
|
|
184
|
+
|
|
185
|
+
This works with **any line-driven CLI agent** — Claude Code, Aider, codex, gemini-cli, opencode — because the daemon doesn't know what's on the receiving end, it just calls `tmux send-keys`.
|
|
186
|
+
|
|
187
|
+
Three ways to wire it, pick whichever fits:
|
|
188
|
+
|
|
189
|
+
**1. From inside the agent itself (cleanest).** If your agent is already running inside tmux (started by you, attached to your terminal), `join({agentId:"me"})` does this automatically. Or, if you've already registered and just want to add the transport:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
attach_agent({ agentId: "me" })
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
With no `tmuxTarget`, the tool reads `$TMUX_PANE` from the MCP server's env. **Important:** this only works when the MCP server itself was launched from inside the same tmux pane as the agent — i.e. spawned as a stdio subprocess by your CLI client. If you're running the MCP server as a system daemon, under launchd/systemd, or under a different terminal multiplexer, `$TMUX_PANE` won't be set (or will point at the wrong pane) and you'll hit a confusing "attached but nothing arrives" failure mode. Pass `tmuxTarget` explicitly in those cases.
|
|
196
|
+
|
|
197
|
+
From the moment attach succeeds, any `send_message({to:"me"})` from a peer gets typed into your pane within ~1s. Call `detach_agent({agentId:"me"})` (or just `unregister({agentId:"me"})`) to stop.
|
|
198
|
+
|
|
199
|
+
**2. From another agent / script, targeting an existing pane.** Get the pane id from inside the target session (`tmux display-message -p '#{pane_id}'` → e.g. `%42`) and pass it explicitly:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
attach_agent({ agentId: "frontend", tmuxTarget: "%42", allowlist: ["backend","worker"] })
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**3. Spawn the agent CLI from scratch (for worker agents).** The included scripts create the tmux session, launch the agent CLI in it, and start the pusher:
|
|
206
|
+
|
|
207
|
+
```sh
|
|
208
|
+
# Claude Code
|
|
209
|
+
scripts/spawn-agent.sh --id frontend --cmd "claude"
|
|
210
|
+
|
|
211
|
+
# Aider, with peer allowlist
|
|
212
|
+
scripts/spawn-agent.sh --id backend --cmd "aider --model sonnet" \
|
|
213
|
+
--include-room --allowlist frontend,worker
|
|
214
|
+
|
|
215
|
+
# Attach to watch / interact
|
|
216
|
+
tmux attach -t coord-frontend
|
|
217
|
+
|
|
218
|
+
# Tear it all down
|
|
219
|
+
scripts/stop-agent.sh --id frontend
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Either way, `list_agents` will show the agent with `transport: "tmux-push"` so peers know it's responsive in real time vs. turn-bound. Stale markers (pusher died) are detected via pid liveness and pruned automatically on the next `list_agents`.
|
|
223
|
+
|
|
224
|
+
Under the hood: [`hooks/tmux-pusher.mjs`](./hooks/tmux-pusher.mjs) is the daemon. It watches `~/agent-coord/inbox/<id>.jsonl` (and optionally `room.jsonl`), debounces bursts (1s default), drops self-posts and `/`-prefixed text, optionally enforces the peer allowlist, then pastes batches via `tmux load-buffer` → `paste-buffer -d` → `send-keys Enter`. Single-flight so two batches never overlap.
|
|
225
|
+
|
|
226
|
+
**Caveats — read these.**
|
|
227
|
+
|
|
228
|
+
- **Don't run the `peek-coord.mjs` hooks for the same agent** while the pusher is active. Both share the cursor file and will race / double-deliver.
|
|
229
|
+
- The pusher pastes into the pane unconditionally. If you're typing in the same pane it will corrupt your buffer; if the agent is showing a `[y/n]` permission prompt, the message becomes the answer. Run the receiving agent in a pane you don't normally edit in.
|
|
230
|
+
- Untrusted peer messages become real prompts with full agent privileges. Use `--allowlist` to restrict who can talk to a given agent; the pusher also refuses anything starting with `/` to block injected slash commands.
|
|
231
|
+
- Bursts get coalesced (1s default) into a single paste so 5 rapid DMs become one prompt rather than five.
|
|
232
|
+
|
|
158
233
|
### Other clients
|
|
159
234
|
|
|
160
235
|
The script itself is plain Node — no Claude-specific deps — so it ports anywhere you can run a shell command around the agent loop. The MCP protocol doesn't standardize client-side hooks, so the wiring varies:
|
package/dist/server.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { ensureDirs } from "./store.js";
|
|
5
|
-
import { heartbeatSchema, heartbeatTool, listAgentsSchema, listAgentsTool, postStatusSchema, postStatusTool, pruneSchema, pruneTool, readMessagesSchema, readMessagesTool, registerSchema, registerTool, sendMessageSchema, sendMessageTool, waitForMessageSchema, waitForMessageTool, } from "./tools.js";
|
|
5
|
+
import { attachAgentSchema, attachAgentTool, detachAgentSchema, detachAgentTool, heartbeatSchema, heartbeatTool, joinSchema, joinTool, listAgentsSchema, listAgentsTool, postStatusSchema, postStatusTool, pruneSchema, pruneTool, readMessagesSchema, readMessagesTool, registerSchema, registerTool, sendMessageSchema, sendMessageTool, statusSchema, statusTool, unregisterSchema, unregisterTool, waitForMessageSchema, waitForMessageTool, } from "./tools.js";
|
|
6
6
|
function jsonResult(data) {
|
|
7
7
|
return {
|
|
8
8
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
@@ -14,7 +14,10 @@ async function main() {
|
|
|
14
14
|
name: "agent-coord",
|
|
15
15
|
version: "0.1.0",
|
|
16
16
|
});
|
|
17
|
-
server.tool("
|
|
17
|
+
server.tool("join", "Recommended session-start call. Does register + auto-attach (if running inside tmux) + read inbox in one round-trip. Pass attach=false to skip the transport, attach={...overrides} to customize, or omit it to let the server auto-detect $TMUX_PANE. Returns the registration, attach result, and any unread inbox messages.", joinSchema, async (args) => jsonResult(await joinTool(args)));
|
|
18
|
+
server.tool("register", "Register this agent in the shared registry. Lower-level than `join` — does not attach a transport or drain the inbox. Prefer `join` unless you need explicit control.", registerSchema, async (args) => jsonResult(await registerTool(args)));
|
|
19
|
+
server.tool("unregister", "Tear down this agent: detach any attached transport (kills the pusher) and remove the registry entry. Clean shutdown counterpart to `join`.", unregisterSchema, async (args) => jsonResult(await unregisterTool(args)));
|
|
20
|
+
server.tool("status", "Introspect this agent's coord state: registration, attached transport, inbox depth and unread count, and whether this MCP server is running inside tmux. Useful for debugging 'why isn't my DM landing'.", statusSchema, async (args) => jsonResult(await statusTool(args)));
|
|
18
21
|
server.tool("heartbeat", "Refresh this agent's lastHeartbeat timestamp.", heartbeatSchema, async (args) => jsonResult(await heartbeatTool(args)));
|
|
19
22
|
server.tool("list_agents", "List all known agents and whether they appear online (heartbeat <5min).", listAgentsSchema, async () => jsonResult(await listAgentsTool()));
|
|
20
23
|
server.tool("send_message", "Send a message. If 'to' is set, goes to that agent's inbox; otherwise to the shared room.", sendMessageSchema, async (args) => jsonResult(await sendMessageTool(args)));
|
|
@@ -22,6 +25,8 @@ async function main() {
|
|
|
22
25
|
server.tool("post_status", "Append a status broadcast to the shared status stream.", postStatusSchema, async (args) => jsonResult(await postStatusTool(args)));
|
|
23
26
|
server.tool("prune", "Trim room/status/inbox JSONL to entries newer than `olderThanDays` (default 7). Removes inbox files for agents no longer in the registry unless removeOrphanInboxes=false. Pass dryRun=true to preview.", pruneSchema, async (args) => jsonResult(await pruneTool(args)));
|
|
24
27
|
server.tool("wait_for_message", "Block (max 60s) until a new message appears on the given source, then return it.", waitForMessageSchema, async (args) => jsonResult(await waitForMessageTool(args)));
|
|
28
|
+
server.tool("attach_agent", "Start the tmux-push transport for an agent: spawns hooks/tmux-pusher.mjs as a background process so peer DMs (and optionally room messages) get typed into the agent's tmux pane in real time. tmuxTarget defaults to the MCP server's own $TMUX_PANE if this server is running inside tmux. allowlist restricts which peer agentIds can push. Updates list_agents to show transport=tmux-push.", attachAgentSchema, async (args) => jsonResult(await attachAgentTool(args)));
|
|
29
|
+
server.tool("detach_agent", "Stop the tmux-push transport for an agent: kills the pusher process and clears the transport marker.", detachAgentSchema, async (args) => jsonResult(await detachAgentTool(args)));
|
|
25
30
|
const transport = new StdioServerTransport();
|
|
26
31
|
await server.connect(transport);
|
|
27
32
|
}
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EACL,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,SAAS,EACT,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,SAAS,UAAU,CAAC,IAAa;IAC/B,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;KAC1E,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,UAAU,EAAE,CAAC;IAEb,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CACT,UAAU,EACV,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/store.js
CHANGED
|
@@ -10,8 +10,11 @@ export const ROOM_FILE = path.join(ROOT, "room.jsonl");
|
|
|
10
10
|
export const STATUS_FILE = path.join(ROOT, "status.jsonl");
|
|
11
11
|
export const INBOX_DIR = path.join(ROOT, "inbox");
|
|
12
12
|
export const CURSOR_DIR = path.join(ROOT, "cursors");
|
|
13
|
+
export const TRANSPORT_DIR = path.join(ROOT, "transports");
|
|
14
|
+
export const PID_DIR = path.join(ROOT, "pids");
|
|
15
|
+
export const LOG_DIR = path.join(ROOT, "logs");
|
|
13
16
|
export function ensureDirs() {
|
|
14
|
-
for (const d of [ROOT, INBOX_DIR, CURSOR_DIR]) {
|
|
17
|
+
for (const d of [ROOT, INBOX_DIR, CURSOR_DIR, TRANSPORT_DIR, PID_DIR, LOG_DIR]) {
|
|
15
18
|
if (!existsSync(d))
|
|
16
19
|
mkdirSync(d, { recursive: true });
|
|
17
20
|
}
|
|
@@ -20,6 +23,21 @@ export function ensureDirs() {
|
|
|
20
23
|
mkdirSync(path.dirname(f), { recursive: true });
|
|
21
24
|
}
|
|
22
25
|
}
|
|
26
|
+
export function transportFile(agentId) {
|
|
27
|
+
return path.join(TRANSPORT_DIR, `${sanitize(agentId)}.json`);
|
|
28
|
+
}
|
|
29
|
+
export function pidFile(agentId, kind) {
|
|
30
|
+
return path.join(PID_DIR, `${kind}-${sanitize(agentId)}.pid`);
|
|
31
|
+
}
|
|
32
|
+
export function logFile(agentId, kind) {
|
|
33
|
+
return path.join(LOG_DIR, `${kind}-${sanitize(agentId)}.log`);
|
|
34
|
+
}
|
|
35
|
+
export async function listTransportFiles() {
|
|
36
|
+
if (!existsSync(TRANSPORT_DIR))
|
|
37
|
+
return [];
|
|
38
|
+
const names = await fs.readdir(TRANSPORT_DIR);
|
|
39
|
+
return names.filter((n) => n.endsWith(".json"));
|
|
40
|
+
}
|
|
23
41
|
async function ensureFile(file) {
|
|
24
42
|
if (!existsSync(file)) {
|
|
25
43
|
await fs.mkdir(path.dirname(file), { recursive: true });
|
package/dist/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,QAAQ,MAAM,iBAAiB,CAAC;AAEvC,MAAM,CAAC,MAAM,IAAI,GACf,OAAO,CAAC,GAAG,CAAC,eAAe;IAC3B,OAAO,CAAC,GAAG,CAAC,gBAAgB;IAC5B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;AACvD,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;AAC3D,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAClD,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|