agenthub-multiagent-mcp 1.47.0 → 1.49.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/dist/channel.js +65 -41
- package/dist/channel.js.map +1 -1
- package/dist/client.d.ts +80 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +67 -0
- package/dist/client.js.map +1 -1
- package/dist/commands/setup-shell.js +11 -11
- package/dist/setup.js +1 -77
- package/dist/setup.js.map +1 -1
- package/dist/skillInstaller.d.ts.map +1 -1
- package/dist/skillInstaller.gate.test.js +6 -0
- package/dist/skillInstaller.gate.test.js.map +1 -1
- package/dist/skillInstaller.js +9 -0
- package/dist/skillInstaller.js.map +1 -1
- package/dist/skillInstaller.test.js +39 -1
- package/dist/skillInstaller.test.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +229 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/tools.test.js +71 -10
- package/dist/tools/tools.test.js.map +1 -1
- package/dist/worker.js +0 -4
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/skills/catalog.json +8 -0
- package/skills/commands/start-session.md +77 -77
- package/skills/manifest.json +5 -0
- package/skills/skills/deploy-staging/SKILL.md +164 -164
- package/skills/skills/deploy-vps-openclaw/SKILL.md +97 -97
- package/skills/skills/karpathy-guidelines/SKILL.md +67 -67
- package/skills/skills/multiagent-brainstorm/SKILL.md +142 -0
- package/dist/.agenthub/activity +0 -1
- package/dist/channelLib.d.ts +0 -42
- package/dist/channelLib.d.ts.map +0 -1
- package/dist/channelLib.js +0 -74
- package/dist/channelLib.js.map +0 -1
- package/dist/channelLib.test.d.ts +0 -2
- package/dist/channelLib.test.d.ts.map +0 -1
- package/dist/channelLib.test.js +0 -67
- package/dist/channelLib.test.js.map +0 -1
- package/dist/channelSetup.d.ts +0 -44
- package/dist/channelSetup.d.ts.map +0 -1
- package/dist/channelSetup.js +0 -61
- package/dist/channelSetup.js.map +0 -1
- package/dist/channelSetup.test.d.ts +0 -2
- package/dist/channelSetup.test.d.ts.map +0 -1
- package/dist/channelSetup.test.js +0 -75
- package/dist/channelSetup.test.js.map +0 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: multiagent-brainstorm
|
|
3
|
+
description: Brainstorm a design with another AgentHub agent, mostly autonomously. Use when you want two agents (e.g. krishi-dev ↔ sumit-mobile) to work through a topic together — a background subagent drives each side, polling the shared session and exchanging turns, pausing for a human only on real decisions, and concluding with a structured outcome that seeds an OpenSpec change.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Multi-Agent Brainstorm
|
|
8
|
+
|
|
9
|
+
Two AgentHub agents brainstorm a topic through a **server-tracked session**. Each
|
|
10
|
+
side is driven by a **fully autonomous background subagent** that polls the
|
|
11
|
+
session, reasons, and posts turns — surfacing to a human only when a decision
|
|
12
|
+
genuinely needs one. A concluded brainstorm produces a structured outcome that
|
|
13
|
+
seeds an OpenSpec change.
|
|
14
|
+
|
|
15
|
+
Requires the server kill switch `AGENTHUB_BRAINSTORM_ENABLED=true` (tools return
|
|
16
|
+
`brainstorm_disabled` otherwise) and `agent_register` first (the tools bind to
|
|
17
|
+
your project). Runs on the latest Sonnet fine — no special model needed.
|
|
18
|
+
|
|
19
|
+
## Two roles
|
|
20
|
+
|
|
21
|
+
You are one of:
|
|
22
|
+
|
|
23
|
+
- **initiator** — your human said "brainstorm X with `<peer>`". You call
|
|
24
|
+
`brainstorm_open`, which delivers an invite to the peer.
|
|
25
|
+
- **responder** — you received a `[[BRAINSTORM INVITE]]` message (via your
|
|
26
|
+
auto-inbox at session start, or mid-session). Call `brainstorm_accept` and
|
|
27
|
+
mirror the initiator.
|
|
28
|
+
|
|
29
|
+
If you got a brainstorm invite in your inbox, run the **responder** flow below.
|
|
30
|
+
Otherwise, if the user asked you to brainstorm with someone, run **initiator**.
|
|
31
|
+
|
|
32
|
+
## Initiator flow
|
|
33
|
+
|
|
34
|
+
1. `brainstorm_open({ peer, topic, escalation })` — `escalation` is `local`
|
|
35
|
+
(default; you'll ask your local human via a decision prompt) or `slack` (the
|
|
36
|
+
subagent DMs the operator and polls for the reply). Note the returned
|
|
37
|
+
`session_id`.
|
|
38
|
+
2. **Spawn the autonomous subagent** (see below) with role `initiator`,
|
|
39
|
+
the `session_id`, `after_seq=0`, the topic, and your human's framing.
|
|
40
|
+
3. Wait. Handle any `NEED_HUMAN` return (see *Human escalation*).
|
|
41
|
+
4. When the subagent reports `concluded`, run the **Conclusion → OpenSpec** step.
|
|
42
|
+
|
|
43
|
+
## Responder flow
|
|
44
|
+
|
|
45
|
+
1. `brainstorm_accept({ session_id })` (idempotent — safe if the invite was
|
|
46
|
+
redelivered).
|
|
47
|
+
2. Spawn the autonomous subagent with role `responder`, the `session_id`,
|
|
48
|
+
`after_seq=0`, and the topic from the invite.
|
|
49
|
+
3. Handle `NEED_HUMAN` the same way. When the subagent reports `concluded`, you're
|
|
50
|
+
done — **only the initiator authors the OpenSpec** (avoids double-proposing).
|
|
51
|
+
|
|
52
|
+
## The autonomous subagent
|
|
53
|
+
|
|
54
|
+
Spawn it with the `Agent` tool (background). Give it this loop, verbatim in
|
|
55
|
+
spirit — it holds the whole transcript in its own context so your main session
|
|
56
|
+
stays clean:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
after_seq = 0
|
|
60
|
+
loop:
|
|
61
|
+
{ turns, state, turn_count } = brainstorm_poll(session_id, after_seq)
|
|
62
|
+
after_seq = max(seq of turns, after_seq)
|
|
63
|
+
|
|
64
|
+
if state != "active":
|
|
65
|
+
return { done: state } # concluded | aborted | exhausted | abandoned
|
|
66
|
+
|
|
67
|
+
peer_turns = turns authored by the OTHER agent
|
|
68
|
+
if peer's latest turn.kind == "propose_conclude":
|
|
69
|
+
if you agree with their outcome:
|
|
70
|
+
brainstorm_conclude(session_id, outcome) # mutual consensus
|
|
71
|
+
else:
|
|
72
|
+
brainstorm_turn(session_id, "<why not yet, what's open>")
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
if it's your turn (peer has posted since your last, or you're the initiator on seq 0):
|
|
76
|
+
reason about the dialogue and form your next contribution
|
|
77
|
+
if a decision here really needs a human (scope, tradeoff, commitment):
|
|
78
|
+
return { need_human: { question, digest, after_seq } }
|
|
79
|
+
else:
|
|
80
|
+
brainstorm_turn(session_id, "<your turn>")
|
|
81
|
+
# when you believe you're done, post kind="propose_conclude" with a draft outcome instead
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
# nothing new and not your move → wait for the peer
|
|
85
|
+
backoff sleep (start ~5s, cap ~30s), then re-poll
|
|
86
|
+
# if last_turn_at is older than the silence deadline, brainstorm_close(state="abandoned")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Discipline:** one contribution per turn; stay on the topic; don't rubber-stamp
|
|
90
|
+
— push back when the design isn't right yet; propose_conclude only when the
|
|
91
|
+
problem, approach, and key decisions are actually settled.
|
|
92
|
+
|
|
93
|
+
## Human escalation (return-and-resume)
|
|
94
|
+
|
|
95
|
+
A background subagent can't ask a human directly. When it returns
|
|
96
|
+
`{ need_human: { question, digest, after_seq } }`:
|
|
97
|
+
|
|
98
|
+
- **`escalation=local`** — ask your human with `AskUserQuestion` (include the
|
|
99
|
+
`digest` for context).
|
|
100
|
+
- **`escalation=slack`** — `slack_dm` the operator and poll your inbox for the
|
|
101
|
+
reply.
|
|
102
|
+
|
|
103
|
+
Then **resume the same subagent** (SendMessage to its agent id, context intact)
|
|
104
|
+
with the human's answer and the `after_seq` it gave you. It posts the
|
|
105
|
+
human-informed turn and continues. Do **not** spawn a fresh subagent — you'd lose
|
|
106
|
+
the transcript.
|
|
107
|
+
|
|
108
|
+
## Peer is a headless daemon?
|
|
109
|
+
|
|
110
|
+
Employee daemons (eng-ic, expert, …) can't run a live loop — one inbound message
|
|
111
|
+
is one shift. If the peer never `brainstorm_accept`s but replies via ordinary
|
|
112
|
+
A2A, the subagent should **fall back to async round-trips**: post a turn, then
|
|
113
|
+
`check_inbox`/`brainstorm_poll` on a longer cadence, one shift per turn. The
|
|
114
|
+
session still tracks turns; you just can't expect a mirror subagent on their end.
|
|
115
|
+
|
|
116
|
+
## Conclusion → OpenSpec (initiator only)
|
|
117
|
+
|
|
118
|
+
On `concluded`, `brainstorm_get(session_id)` returns the structured `outcome`
|
|
119
|
+
(`problem`, `agreed_approach`, `key_decisions`, `open_questions`,
|
|
120
|
+
`suggested_change_id`, `summary`). Then:
|
|
121
|
+
|
|
122
|
+
1. **Render the outcome to your human** and ask (via `AskUserQuestion`): author
|
|
123
|
+
the OpenSpec now? — matches the repo's plan-first mandate; a human verifies the
|
|
124
|
+
agreed design before any spec is written.
|
|
125
|
+
2. On approval, invoke the **`openspec:proposal`** skill seeded from the outcome
|
|
126
|
+
(use `suggested_change_id` as the change id), then `openspec_publish` to sync
|
|
127
|
+
to AgentHub (the mandate: a change on disk only isn't done).
|
|
128
|
+
3. `brainstorm_link_change(session_id, change_id)` to stamp the change onto the
|
|
129
|
+
session — closes the traceability loop (brainstorm → change).
|
|
130
|
+
|
|
131
|
+
## Stopping early
|
|
132
|
+
|
|
133
|
+
Either operator can halt: `brainstorm_close({ session_id, reason })` →
|
|
134
|
+
`aborted`. The safety backstops (`exhausted` at `max_turns`, `abandoned` on peer
|
|
135
|
+
silence) fire automatically so a runaway loop or an offline peer can't hang.
|
|
136
|
+
|
|
137
|
+
## Tool reference
|
|
138
|
+
|
|
139
|
+
`brainstorm_open` · `brainstorm_accept` · `brainstorm_turn` · `brainstorm_poll` ·
|
|
140
|
+
`brainstorm_conclude` · `brainstorm_close` · `brainstorm_get` ·
|
|
141
|
+
`brainstorm_link_change`. Full design: the `multiagent-brainstorm-design` doc;
|
|
142
|
+
guide: `multiagent-brainstorm-guide`.
|
package/dist/.agenthub/activity
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"active_at":"2026-06-20T12:20:44.058Z","category":"other"}
|
package/dist/channelLib.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure helpers for the AgentHub channel plugin (add-inbox-push-bridge).
|
|
3
|
-
*
|
|
4
|
-
* Extracted from channel.ts so the WS→notification mapping, kill switch, and
|
|
5
|
-
* reply-body construction are unit-testable without opening a socket or running
|
|
6
|
-
* the MCP server. channel.ts wires these into the live plugin.
|
|
7
|
-
*/
|
|
8
|
-
export interface ChannelNotification {
|
|
9
|
-
content: string;
|
|
10
|
-
meta: Record<string, unknown>;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Phase B kill switch (§2.1). The plugin is OFF unless
|
|
14
|
-
* AGENTHUB_INBOX_CHANNEL_ENABLED is explicitly truthy. Independent of
|
|
15
|
-
* AGENTHUB_AUTO_INBOX (the Phase A SessionStart poll), which stays the floor.
|
|
16
|
-
*/
|
|
17
|
-
export declare function channelEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
18
|
-
/**
|
|
19
|
-
* Map a raw AgentHub WS event to a channel notification, or null when nothing
|
|
20
|
-
* should be surfaced (self-message, or an event type we don't push). The caller
|
|
21
|
-
* still ACKs message events even when this returns null (self-filter case).
|
|
22
|
-
*/
|
|
23
|
-
export declare function buildChannelNotification(msg: unknown, agentId: string): ChannelNotification | null;
|
|
24
|
-
/**
|
|
25
|
-
* Build the reply POST body (§2.2). Mirrors mcp__agenthub__reply semantics: the
|
|
26
|
-
* recipient resolves to the ORIGINAL sender (never empty) and the reply carries
|
|
27
|
-
* reply_to, which is what transitions the original message to `responded`
|
|
28
|
-
* server-side. The old plugin posted to_agent:"" then PATCHed status to `read`,
|
|
29
|
-
* which left the original un-responded — fixed here.
|
|
30
|
-
*/
|
|
31
|
-
export declare function buildReplyBody(original: {
|
|
32
|
-
from_agent: string;
|
|
33
|
-
subject: string;
|
|
34
|
-
}, agentId: string, body: string, messageId: string): {
|
|
35
|
-
from_agent: string;
|
|
36
|
-
to_agent: string;
|
|
37
|
-
type: "response";
|
|
38
|
-
subject: string;
|
|
39
|
-
body: string;
|
|
40
|
-
reply_to: string;
|
|
41
|
-
};
|
|
42
|
-
//# sourceMappingURL=channelLib.d.ts.map
|
package/dist/channelLib.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"channelLib.d.ts","sourceRoot":"","sources":["../src/channelLib.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,MAAM,GACd,mBAAmB,GAAG,IAAI,CAuC5B;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACjD,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CASA"}
|
package/dist/channelLib.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure helpers for the AgentHub channel plugin (add-inbox-push-bridge).
|
|
3
|
-
*
|
|
4
|
-
* Extracted from channel.ts so the WS→notification mapping, kill switch, and
|
|
5
|
-
* reply-body construction are unit-testable without opening a socket or running
|
|
6
|
-
* the MCP server. channel.ts wires these into the live plugin.
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Phase B kill switch (§2.1). The plugin is OFF unless
|
|
10
|
-
* AGENTHUB_INBOX_CHANNEL_ENABLED is explicitly truthy. Independent of
|
|
11
|
-
* AGENTHUB_AUTO_INBOX (the Phase A SessionStart poll), which stays the floor.
|
|
12
|
-
*/
|
|
13
|
-
export function channelEnabled(env = process.env) {
|
|
14
|
-
return /^(1|true|yes|on)$/i.test((env.AGENTHUB_INBOX_CHANNEL_ENABLED || "").trim());
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Map a raw AgentHub WS event to a channel notification, or null when nothing
|
|
18
|
-
* should be surfaced (self-message, or an event type we don't push). The caller
|
|
19
|
-
* still ACKs message events even when this returns null (self-filter case).
|
|
20
|
-
*/
|
|
21
|
-
export function buildChannelNotification(msg, agentId) {
|
|
22
|
-
if (!msg || typeof msg !== "object")
|
|
23
|
-
return null;
|
|
24
|
-
const m = msg;
|
|
25
|
-
if (m.type === "message" && m.message) {
|
|
26
|
-
const msgObj = m.message;
|
|
27
|
-
// Never echo a message the agent itself sent.
|
|
28
|
-
if (msgObj.from_agent === agentId)
|
|
29
|
-
return null;
|
|
30
|
-
const priority = msgObj.priority === "urgent" ? " [URGENT]" : msgObj.priority === "high" ? " [HIGH]" : "";
|
|
31
|
-
const content = `📨 Message from ${msgObj.from_agent}${priority}\n` +
|
|
32
|
-
`Subject: ${msgObj.subject}\n\n${msgObj.body}\n\n` +
|
|
33
|
-
`Message ID: ${msgObj.id}\n` +
|
|
34
|
-
`Use agenthub_reply with this message_id to respond.`;
|
|
35
|
-
return {
|
|
36
|
-
content,
|
|
37
|
-
meta: {
|
|
38
|
-
source: "agenthub",
|
|
39
|
-
from: msgObj.from_agent,
|
|
40
|
-
subject: msgObj.subject,
|
|
41
|
-
message_id: msgObj.id,
|
|
42
|
-
type: msgObj.type,
|
|
43
|
-
priority: msgObj.priority,
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
if (m.type === "task" && m.task) {
|
|
48
|
-
const t = m.task;
|
|
49
|
-
return {
|
|
50
|
-
content: `📋 New task assigned by ${t.assigned_by}\n\n${t.task}\n\n` +
|
|
51
|
-
`Task ID: ${t.id}\nPriority: ${t.priority}`,
|
|
52
|
-
meta: { source: "agenthub", from: t.assigned_by, task_id: t.id, type: "task", priority: t.priority },
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Build the reply POST body (§2.2). Mirrors mcp__agenthub__reply semantics: the
|
|
59
|
-
* recipient resolves to the ORIGINAL sender (never empty) and the reply carries
|
|
60
|
-
* reply_to, which is what transitions the original message to `responded`
|
|
61
|
-
* server-side. The old plugin posted to_agent:"" then PATCHed status to `read`,
|
|
62
|
-
* which left the original un-responded — fixed here.
|
|
63
|
-
*/
|
|
64
|
-
export function buildReplyBody(original, agentId, body, messageId) {
|
|
65
|
-
return {
|
|
66
|
-
from_agent: agentId,
|
|
67
|
-
to_agent: original.from_agent,
|
|
68
|
-
type: "response",
|
|
69
|
-
subject: `Re: ${original.subject}`,
|
|
70
|
-
body: body.slice(0, 5000),
|
|
71
|
-
reply_to: messageId,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
//# sourceMappingURL=channelLib.js.map
|
package/dist/channelLib.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"channelLib.js","sourceRoot":"","sources":["../src/channelLib.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG;IACjE,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,8BAA8B,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACtF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,GAAY,EACZ,OAAe;IAEf,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,CAAC,GAAG,GAAmD,CAAC;IAE9D,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC;QACzB,8CAA8C;QAC9C,IAAI,MAAM,CAAC,UAAU,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QAC/C,MAAM,QAAQ,GACZ,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3F,MAAM,OAAO,GACX,mBAAmB,MAAM,CAAC,UAAU,GAAG,QAAQ,IAAI;YACnD,YAAY,MAAM,CAAC,OAAO,OAAO,MAAM,CAAC,IAAI,MAAM;YAClD,eAAe,MAAM,CAAC,EAAE,IAAI;YAC5B,qDAAqD,CAAC;QACxD,OAAO;YACL,OAAO;YACP,IAAI,EAAE;gBACJ,MAAM,EAAE,UAAU;gBAClB,IAAI,EAAE,MAAM,CAAC,UAAU;gBACvB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,UAAU,EAAE,MAAM,CAAC,EAAE;gBACrB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;aAC1B;SACF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QACjB,OAAO;YACL,OAAO,EACL,2BAA2B,CAAC,CAAC,WAAW,OAAO,CAAC,CAAC,IAAI,MAAM;gBAC3D,YAAY,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,QAAQ,EAAE;YAC7C,IAAI,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE;SACrG,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAiD,EACjD,OAAe,EACf,IAAY,EACZ,SAAiB;IASjB,OAAO;QACL,UAAU,EAAE,OAAO;QACnB,QAAQ,EAAE,QAAQ,CAAC,UAAU;QAC7B,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,OAAO,QAAQ,CAAC,OAAO,EAAE;QAClC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC;QACzB,QAAQ,EAAE,SAAS;KACpB,CAAC;AACJ,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"channelLib.test.d.ts","sourceRoot":"","sources":["../src/channelLib.test.ts"],"names":[],"mappings":""}
|
package/dist/channelLib.test.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { buildChannelNotification, buildReplyBody, channelEnabled } from "./channelLib.js";
|
|
3
|
-
// add-inbox-push-bridge §4 — hardening tests for the channel plugin's pure logic.
|
|
4
|
-
describe("channelEnabled (kill switch §2.1)", () => {
|
|
5
|
-
it("is off when unset", () => {
|
|
6
|
-
expect(channelEnabled({})).toBe(false);
|
|
7
|
-
});
|
|
8
|
-
it("is off for falsey strings", () => {
|
|
9
|
-
for (const v of ["", "false", "0", "no", "off", " "]) {
|
|
10
|
-
expect(channelEnabled({ AGENTHUB_INBOX_CHANNEL_ENABLED: v })).toBe(false);
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
it("is on only for explicit truthy values (case-insensitive, trimmed)", () => {
|
|
14
|
-
for (const v of ["true", "TRUE", "1", "yes", "on", " true "]) {
|
|
15
|
-
expect(channelEnabled({ AGENTHUB_INBOX_CHANNEL_ENABLED: v })).toBe(true);
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
describe("buildChannelNotification (§4.1)", () => {
|
|
20
|
-
const agentId = "me";
|
|
21
|
-
it("maps an inbound message to a channel notification", () => {
|
|
22
|
-
const note = buildChannelNotification({
|
|
23
|
-
type: "message",
|
|
24
|
-
message: { id: "m1", from_agent: "alice", subject: "Hi", body: "ping", priority: "normal", type: "question" },
|
|
25
|
-
}, agentId);
|
|
26
|
-
expect(note).not.toBeNull();
|
|
27
|
-
expect(note.content).toContain("Message from alice");
|
|
28
|
-
expect(note.content).toContain("ping");
|
|
29
|
-
expect(note.content).toContain("m1");
|
|
30
|
-
expect(note.meta).toMatchObject({ source: "agenthub", from: "alice", message_id: "m1" });
|
|
31
|
-
});
|
|
32
|
-
it("surfaces priority for urgent/high", () => {
|
|
33
|
-
const urgent = buildChannelNotification({ type: "message", message: { id: "m2", from_agent: "alice", subject: "x", body: "y", priority: "urgent" } }, agentId);
|
|
34
|
-
expect(urgent.content).toContain("[URGENT]");
|
|
35
|
-
});
|
|
36
|
-
it("returns null for a self-message (no echo)", () => {
|
|
37
|
-
const note = buildChannelNotification({ type: "message", message: { id: "m3", from_agent: agentId, subject: "x", body: "y" } }, agentId);
|
|
38
|
-
expect(note).toBeNull();
|
|
39
|
-
});
|
|
40
|
-
it("maps a task event", () => {
|
|
41
|
-
const note = buildChannelNotification({ type: "task", task: { id: "t1", assigned_by: "boss", task: "do it", priority: "high" } }, agentId);
|
|
42
|
-
expect(note).not.toBeNull();
|
|
43
|
-
expect(note.content).toContain("New task assigned by boss");
|
|
44
|
-
expect(note.meta).toMatchObject({ task_id: "t1", type: "task" });
|
|
45
|
-
});
|
|
46
|
-
it("returns null for unknown / malformed events", () => {
|
|
47
|
-
expect(buildChannelNotification({ type: "pong" }, agentId)).toBeNull();
|
|
48
|
-
expect(buildChannelNotification({ type: "message" }, agentId)).toBeNull(); // no message body
|
|
49
|
-
expect(buildChannelNotification(null, agentId)).toBeNull();
|
|
50
|
-
expect(buildChannelNotification("nope", agentId)).toBeNull();
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
describe("buildReplyBody (reply correctness §2.2)", () => {
|
|
54
|
-
it("addresses the reply to the original sender and carries reply_to", () => {
|
|
55
|
-
const body = buildReplyBody({ from_agent: "alice", subject: "Hi" }, "me", "thanks", "m1");
|
|
56
|
-
expect(body.to_agent).toBe("alice"); // NOT "" — the old bug
|
|
57
|
-
expect(body.from_agent).toBe("me");
|
|
58
|
-
expect(body.type).toBe("response");
|
|
59
|
-
expect(body.subject).toBe("Re: Hi");
|
|
60
|
-
expect(body.reply_to).toBe("m1"); // reply_to is what marks the original `responded`
|
|
61
|
-
});
|
|
62
|
-
it("truncates the body to 5000 chars", () => {
|
|
63
|
-
const body = buildReplyBody({ from_agent: "a", subject: "s" }, "me", "x".repeat(6000), "m1");
|
|
64
|
-
expect(body.body.length).toBe(5000);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
//# sourceMappingURL=channelLib.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"channelLib.test.js","sourceRoot":"","sources":["../src/channelLib.test.ts"],"names":[],"mappings":"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"}
|
package/dist/channelSetup.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interactive-session wiring for the AgentHub channel plugin
|
|
3
|
-
* (add-inbox-push-bridge §3). Builds the `agenthub-channel` MCP-server entry and
|
|
4
|
-
* merges it idempotently into a project's MCP config, so a normal `claude`
|
|
5
|
-
* session can opt in via `--channels server:agenthub-channel`.
|
|
6
|
-
*
|
|
7
|
-
* Pure + filesystem helpers are split so the merge logic is unit-testable.
|
|
8
|
-
*/
|
|
9
|
-
export declare const CHANNEL_SERVER_NAME = "agenthub-channel";
|
|
10
|
-
export interface McpServerEntry {
|
|
11
|
-
type: "stdio";
|
|
12
|
-
command: string;
|
|
13
|
-
args: string[];
|
|
14
|
-
env: Record<string, string>;
|
|
15
|
-
}
|
|
16
|
-
export interface ChannelEntryOptions {
|
|
17
|
-
url: string;
|
|
18
|
-
apiKey: string;
|
|
19
|
-
agentId: string;
|
|
20
|
-
/** Defaults to true (explicit opt-in). false writes the entry but keeps the
|
|
21
|
-
* plugin a no-op via the §2.1 kill switch — safe to install while disabled. */
|
|
22
|
-
enabled?: boolean;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* The MCP-server entry that makes `server:agenthub-channel` resolvable. Runs the
|
|
26
|
-
* published `agenthub-channel` bin via npx (same distribution as the main MCP),
|
|
27
|
-
* carrying the agent's existing AgentHub creds + the kill switch.
|
|
28
|
-
*/
|
|
29
|
-
export declare function channelMcpServerEntry(opts: ChannelEntryOptions): McpServerEntry;
|
|
30
|
-
/**
|
|
31
|
-
* Idempotently set the channel server entry under `mcpServers`, preserving every
|
|
32
|
-
* other server. Re-running with the same opts produces the same config.
|
|
33
|
-
*/
|
|
34
|
-
export declare function mergeChannelServer(config: Record<string, unknown>, entry: McpServerEntry, name?: string): Record<string, unknown>;
|
|
35
|
-
/**
|
|
36
|
-
* Read a JSON MCP-config file (or start from {} if absent/empty), merge the
|
|
37
|
-
* channel entry, and write it back with 2-space indent. Returns whether the file
|
|
38
|
-
* was newly created and whether the channel entry already matched (no-op).
|
|
39
|
-
*/
|
|
40
|
-
export declare function applyChannelToConfigFile(filePath: string, opts: ChannelEntryOptions, name?: string): {
|
|
41
|
-
created: boolean;
|
|
42
|
-
unchanged: boolean;
|
|
43
|
-
};
|
|
44
|
-
//# sourceMappingURL=channelSetup.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"channelSetup.d.ts","sourceRoot":"","sources":["../src/channelSetup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB;oFACgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,GAAG,cAAc,CAc/E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,EAAE,cAAc,EACrB,IAAI,GAAE,MAA4B,GACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGzB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,mBAAmB,EACzB,IAAI,GAAE,MAA4B,GACjC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAgB1C"}
|
package/dist/channelSetup.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interactive-session wiring for the AgentHub channel plugin
|
|
3
|
-
* (add-inbox-push-bridge §3). Builds the `agenthub-channel` MCP-server entry and
|
|
4
|
-
* merges it idempotently into a project's MCP config, so a normal `claude`
|
|
5
|
-
* session can opt in via `--channels server:agenthub-channel`.
|
|
6
|
-
*
|
|
7
|
-
* Pure + filesystem helpers are split so the merge logic is unit-testable.
|
|
8
|
-
*/
|
|
9
|
-
import * as fs from "fs";
|
|
10
|
-
export const CHANNEL_SERVER_NAME = "agenthub-channel";
|
|
11
|
-
/**
|
|
12
|
-
* The MCP-server entry that makes `server:agenthub-channel` resolvable. Runs the
|
|
13
|
-
* published `agenthub-channel` bin via npx (same distribution as the main MCP),
|
|
14
|
-
* carrying the agent's existing AgentHub creds + the kill switch.
|
|
15
|
-
*/
|
|
16
|
-
export function channelMcpServerEntry(opts) {
|
|
17
|
-
return {
|
|
18
|
-
type: "stdio",
|
|
19
|
-
command: "npx",
|
|
20
|
-
args: ["-y", "-p", "agenthub-multiagent-mcp@latest", CHANNEL_SERVER_NAME],
|
|
21
|
-
env: {
|
|
22
|
-
AGENTHUB_URL: opts.url,
|
|
23
|
-
AGENTHUB_API_KEY: opts.apiKey,
|
|
24
|
-
AGENTHUB_AGENT_ID: opts.agentId,
|
|
25
|
-
// §3.2 — the opt-in is safe even when disabled: the plugin no-ops unless
|
|
26
|
-
// this is truthy (see channelEnabled / §2.1).
|
|
27
|
-
AGENTHUB_INBOX_CHANNEL_ENABLED: opts.enabled === false ? "false" : "true",
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Idempotently set the channel server entry under `mcpServers`, preserving every
|
|
33
|
-
* other server. Re-running with the same opts produces the same config.
|
|
34
|
-
*/
|
|
35
|
-
export function mergeChannelServer(config, entry, name = CHANNEL_SERVER_NAME) {
|
|
36
|
-
const existing = config.mcpServers || {};
|
|
37
|
-
return { ...config, mcpServers: { ...existing, [name]: entry } };
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Read a JSON MCP-config file (or start from {} if absent/empty), merge the
|
|
41
|
-
* channel entry, and write it back with 2-space indent. Returns whether the file
|
|
42
|
-
* was newly created and whether the channel entry already matched (no-op).
|
|
43
|
-
*/
|
|
44
|
-
export function applyChannelToConfigFile(filePath, opts, name = CHANNEL_SERVER_NAME) {
|
|
45
|
-
let config = {};
|
|
46
|
-
let created = true;
|
|
47
|
-
if (fs.existsSync(filePath)) {
|
|
48
|
-
created = false;
|
|
49
|
-
const raw = fs.readFileSync(filePath, "utf-8").trim();
|
|
50
|
-
if (raw) {
|
|
51
|
-
config = JSON.parse(raw);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const entry = channelMcpServerEntry(opts);
|
|
55
|
-
const before = JSON.stringify(config.mcpServers?.[name]);
|
|
56
|
-
const merged = mergeChannelServer(config, entry, name);
|
|
57
|
-
const after = JSON.stringify(merged.mcpServers[name]);
|
|
58
|
-
fs.writeFileSync(filePath, JSON.stringify(merged, null, 2));
|
|
59
|
-
return { created, unchanged: before === after };
|
|
60
|
-
}
|
|
61
|
-
//# sourceMappingURL=channelSetup.js.map
|
package/dist/channelSetup.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"channelSetup.js","sourceRoot":"","sources":["../src/channelSetup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB,MAAM,CAAC,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAkBtD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAyB;IAC7D,OAAO;QACL,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,gCAAgC,EAAE,mBAAmB,CAAC;QACzE,GAAG,EAAE;YACH,YAAY,EAAE,IAAI,CAAC,GAAG;YACtB,gBAAgB,EAAE,IAAI,CAAC,MAAM;YAC7B,iBAAiB,EAAE,IAAI,CAAC,OAAO;YAC/B,yEAAyE;YACzE,8CAA8C;YAC9C,8BAA8B,EAAE,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;SAC1E;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAA+B,EAC/B,KAAqB,EACrB,OAAe,mBAAmB;IAElC,MAAM,QAAQ,GAAI,MAAM,CAAC,UAAkD,IAAI,EAAE,CAAC;IAClF,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,EAAE,GAAG,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;AACnE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,QAAgB,EAChB,IAAyB,EACzB,OAAe,mBAAmB;IAElC,IAAI,MAAM,GAA4B,EAAE,CAAC;IACzC,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,OAAO,GAAG,KAAK,CAAC;QAChB,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QACtD,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QACtD,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAE,MAAM,CAAC,UAAsC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACtF,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAE,MAAM,CAAC,UAAsC,CAAC,IAAI,CAAC,CAAC,CAAC;IACnF,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,KAAK,EAAE,CAAC;AAClD,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"channelSetup.test.d.ts","sourceRoot":"","sources":["../src/channelSetup.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import * as os from "os";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import { channelMcpServerEntry, mergeChannelServer, applyChannelToConfigFile, CHANNEL_SERVER_NAME, } from "./channelSetup.js";
|
|
6
|
-
// add-inbox-push-bridge §3 — interactive-session wiring helpers.
|
|
7
|
-
const OPTS = { url: "https://h.example.com", apiKey: "k123", agentId: "agent-x" };
|
|
8
|
-
describe("channelMcpServerEntry", () => {
|
|
9
|
-
it("builds a stdio npx entry carrying creds + opt-in", () => {
|
|
10
|
-
const e = channelMcpServerEntry(OPTS);
|
|
11
|
-
expect(e.type).toBe("stdio");
|
|
12
|
-
expect(e.command).toBe("npx");
|
|
13
|
-
expect(e.args).toContain(CHANNEL_SERVER_NAME);
|
|
14
|
-
expect(e.env.AGENTHUB_URL).toBe("https://h.example.com");
|
|
15
|
-
expect(e.env.AGENTHUB_API_KEY).toBe("k123");
|
|
16
|
-
expect(e.env.AGENTHUB_AGENT_ID).toBe("agent-x");
|
|
17
|
-
expect(e.env.AGENTHUB_INBOX_CHANNEL_ENABLED).toBe("true");
|
|
18
|
-
});
|
|
19
|
-
it("respects the kill switch when installed disabled (§3.2)", () => {
|
|
20
|
-
const e = channelMcpServerEntry({ ...OPTS, enabled: false });
|
|
21
|
-
expect(e.env.AGENTHUB_INBOX_CHANNEL_ENABLED).toBe("false");
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
describe("mergeChannelServer", () => {
|
|
25
|
-
it("adds the channel server while preserving existing servers", () => {
|
|
26
|
-
const before = { mcpServers: { agenthub: { command: "npx" } }, other: 1 };
|
|
27
|
-
const merged = mergeChannelServer(before, channelMcpServerEntry(OPTS));
|
|
28
|
-
expect(merged.mcpServers.agenthub).toEqual({ command: "npx" }); // preserved
|
|
29
|
-
expect(merged.mcpServers[CHANNEL_SERVER_NAME]).toBeDefined();
|
|
30
|
-
expect(merged.other).toBe(1);
|
|
31
|
-
});
|
|
32
|
-
it("is idempotent — re-merging the same entry yields the same config", () => {
|
|
33
|
-
const entry = channelMcpServerEntry(OPTS);
|
|
34
|
-
const once = mergeChannelServer({}, entry);
|
|
35
|
-
const twice = mergeChannelServer(once, entry);
|
|
36
|
-
expect(JSON.stringify(twice)).toBe(JSON.stringify(once));
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
describe("applyChannelToConfigFile", () => {
|
|
40
|
-
let dir;
|
|
41
|
-
let file;
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
dir = fs.mkdtempSync(path.join(os.tmpdir(), "channel-setup-"));
|
|
44
|
-
file = path.join(dir, ".mcp.json");
|
|
45
|
-
});
|
|
46
|
-
afterEach(() => {
|
|
47
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
48
|
-
});
|
|
49
|
-
it("creates a config file when none exists", () => {
|
|
50
|
-
const r = applyChannelToConfigFile(file, OPTS);
|
|
51
|
-
expect(r.created).toBe(true);
|
|
52
|
-
const written = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
53
|
-
expect(written.mcpServers[CHANNEL_SERVER_NAME].env.AGENTHUB_AGENT_ID).toBe("agent-x");
|
|
54
|
-
});
|
|
55
|
-
it("merges into an existing config without clobbering other servers", () => {
|
|
56
|
-
fs.writeFileSync(file, JSON.stringify({ mcpServers: { agenthub: { command: "npx", args: [] } } }));
|
|
57
|
-
const r = applyChannelToConfigFile(file, OPTS);
|
|
58
|
-
expect(r.created).toBe(false);
|
|
59
|
-
const written = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
60
|
-
expect(written.mcpServers.agenthub).toBeDefined(); // preserved
|
|
61
|
-
expect(written.mcpServers[CHANNEL_SERVER_NAME]).toBeDefined();
|
|
62
|
-
});
|
|
63
|
-
it("is idempotent — second apply reports unchanged", () => {
|
|
64
|
-
applyChannelToConfigFile(file, OPTS);
|
|
65
|
-
const second = applyChannelToConfigFile(file, OPTS);
|
|
66
|
-
expect(second.unchanged).toBe(true);
|
|
67
|
-
});
|
|
68
|
-
it("tolerates an empty file", () => {
|
|
69
|
-
fs.writeFileSync(file, "");
|
|
70
|
-
const r = applyChannelToConfigFile(file, OPTS);
|
|
71
|
-
expect(r.created).toBe(false);
|
|
72
|
-
expect(JSON.parse(fs.readFileSync(file, "utf-8")).mcpServers[CHANNEL_SERVER_NAME]).toBeDefined();
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
//# sourceMappingURL=channelSetup.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"channelSetup.test.js","sourceRoot":"","sources":["../src/channelSetup.test.ts"],"names":[],"mappings":"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"}
|