@chinchillaenterprises/mcp-claude-channel 0.1.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 ADDED
@@ -0,0 +1,117 @@
1
+ # @chinchillaenterprises/mcp-claude-channel
2
+
3
+ **Lane-to-lane Claude Code side-door channel server (v1).** Pushes a **nudge**
4
+ from a peer lane **into a live Claude Code session** via the official **Channels**
5
+ notification (`notifications/claude/channel`) and exposes a `send_to_lane` tool to
6
+ nudge the next lane. **v1 is session↔session only — no Slack.**
7
+
8
+ > The doorbell, not the payload. The **SQLite Lane Board** stays the source of truth.
9
+
10
+ ## What it's for
11
+
12
+ Echelon's "fused line": the PEP lanes (R&D · Arch · Dev · Deploy · Test) are
13
+ Claude Code sessions in separate tabs. This server automates the **baton handoff
14
+ between tabs** — replacing the human walking it lane-to-lane.
15
+
16
+ ## How it's different from every other ChillMCP server
17
+
18
+ The other servers are request/response stdio (`capabilities:{tools:{}}`). This one
19
+ adds two patterns net-new to the repo:
20
+
21
+ 1. **Server-initiated notifications** — emits `notifications/claude/channel`, a
22
+ server→client push Claude Code injects into the running session as a new
23
+ `<channel ...>` turn.
24
+ 2. **A resident file-inbox tailer** — watches this lane's inbox and turns each
25
+ appended line into a push. Peer lanes write to it via `send_to_lane`.
26
+
27
+ The process stays alive on the stdio transport + the inbox poll loop. No HTTP, no
28
+ Slack — this is a stdio child of Claude Code.
29
+
30
+ ## Wire model (pure files, no shared daemon)
31
+
32
+ - **Inbox:** `<LANE_INBOX_DIR>/chan-<LANE_NAME>-in.jsonl` — one JSON object per
33
+ line: `{"content": "...", "meta": {...}}`.
34
+ - This server **tails its own inbox** and pushes each new line.
35
+ - `send_to_lane(target, text, meta?)` **appends a line to the TARGET's inbox**,
36
+ stamping `meta.from_lane` with our own callsign.
37
+
38
+ Mirrors the proven `labs/sidedoor-ref/lane-channel.py`.
39
+
40
+ ## The principle — the push is a NUDGE, not the payload
41
+
42
+ Content is a short doorbell: *"you're next — round R7, baton at `<path>`, check the
43
+ Board."* The receiving lane reads the **real baton from the SQLite Lane Board**, not
44
+ from the push. This kills dedup / replay / double-process.
45
+
46
+ ## Protocol contract
47
+
48
+ - **Handshake:** `initialize` advertises `capabilities.experimental["claude/channel"] = {}`
49
+ (the badge that arms the session's notification listener).
50
+ - **Inbound (peer lane → session):** each allowed inbox line →
51
+ `notifications/claude/channel` with `params.content` (the nudge) and `params.meta`.
52
+ Every `meta` key becomes a `<channel ...>` attribute. We carry `from_lane`,
53
+ `to_lane`, `round_id`, `stage`, `baton_path`, plus `source` (this lane).
54
+ - **Outbound (session → peer lane):** the `send_to_lane` tool appends to the
55
+ target lane's inbox. (Replaces v1's would-be `reply_to_channel`-to-Slack.)
56
+
57
+ ## Security — allowlist on the sending lane
58
+
59
+ Access is gated by `LANE_ALLOWED_SENDERS` against **`meta.from_lane`** — the
60
+ **sending lane callsign**, not message content. Only the prev lane (forward) /
61
+ Test+Deploy (fail-back) / the human may inject into a given lane's door. A line
62
+ from a disallowed lane is **dropped silently** (logged to stderr). An empty
63
+ allowlist accepts any sender (dev only — set it in production).
64
+
65
+ Claude Code also treats injected `<channel>` content as untrusted **data, not
66
+ commands** (won't auto-obey), and gates outbound. For unattended `send_to_lane`,
67
+ add `mcp__claude-channel__send_to_lane` to the session's `allowedTools`.
68
+
69
+ ## Configuration (env-first, NO tokens)
70
+
71
+ | Env var | Default | Notes |
72
+ | --- | --- | --- |
73
+ | `LANE_NAME` | `sidedoor` | This lane's callsign. Also names its inbox file. |
74
+ | `LANE_INBOX_DIR` | `/tmp` | Directory holding the `chan-<lane>-in.jsonl` inboxes. |
75
+ | `LANE_ALLOWED_SENDERS` | _(empty = open)_ | Comma/space-separated callsigns allowed to inject (matched on `meta.from_lane`). |
76
+
77
+ ## Launch (consumer side, per tab)
78
+
79
+ Custom/dev channel servers require the dangerous flag (plain `--channels` is
80
+ marketplace-only), with `channelsEnabled: true` in `~/.claude/settings.json`:
81
+
82
+ ```bash
83
+ claude --dangerously-load-development-channels server:claude-channel
84
+ ```
85
+
86
+ `.mcp.json` (one per lane tab — note the per-lane `LANE_NAME`):
87
+
88
+ ```json
89
+ {
90
+ "mcpServers": {
91
+ "claude-channel": {
92
+ "command": "npx",
93
+ "args": ["-y", "@chinchillaenterprises/mcp-claude-channel"],
94
+ "env": {
95
+ "LANE_NAME": "bravo",
96
+ "LANE_ALLOWED_SENDERS": "alpha,human"
97
+ }
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ ## Build
104
+
105
+ ```bash
106
+ npm install
107
+ npm run build # tsc → dist/
108
+ ```
109
+
110
+ Published via the repo's OIDC `publish.yml`. Bump the version in **both**
111
+ `package.json` and `src/index.ts` (`VERSION`).
112
+
113
+ ## Roadmap
114
+
115
+ - **v1 (this):** lane-to-lane nudges, file-inbox transport, no Slack.
116
+ - **v2 (deferred, not deleted):** the Slack-bridge half — one app posting a remote
117
+ readout when the human walks away. See ChillMCP issues #8 / #13.
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * mcp-claude-channel — lane-to-lane Claude Code side-door channel server (v1).
4
+ *
5
+ * Echelon's "fused line": each PEP lane (R&D · Arch · Dev · Deploy · Test) is a
6
+ * Claude Code session in its own tab. This server automates the baton HANDOFF
7
+ * between tabs — replacing the human walking it lane-to-lane.
8
+ *
9
+ * Net-new to ChillMCP vs. the request/response servers:
10
+ * 1. Server-initiated notifications — emits `notifications/claude/channel`, a
11
+ * server→client push Claude Code injects into the live session as a new
12
+ * `<channel ...>` turn (the "doorbell").
13
+ * 2. A resident file-inbox tailer — watches this lane's inbox and turns each
14
+ * appended line into a push. Peer lanes write to it via `send_to_lane`.
15
+ *
16
+ * Wire model (no shared daemon, pure files — easy to reason about, mirrors the
17
+ * proven `labs/sidedoor-ref/lane-channel.py`):
18
+ * - INBOX = <LANE_INBOX_DIR>/chan-<LANE_NAME>-in.jsonl
19
+ * - this server tails its own INBOX and pushes each new line.
20
+ * - `send_to_lane(target, text, meta?)` appends a line to the TARGET's inbox.
21
+ *
22
+ * THE PRINCIPLE — the push is a NUDGE, not the payload. Content is a short
23
+ * doorbell ("you're next — round R<n>, baton at <path>, check the Board"). The
24
+ * SQLite Lane Board is the source of truth; the receiving lane reads the real
25
+ * baton from the Board, not from the push. (Kills dedup/replay/double-process.)
26
+ *
27
+ * Security: allowlist on the SENDING LANE callsign (`meta.from_lane`), via
28
+ * LANE_ALLOWED_SENDERS. Only the prev lane / fail-back lanes / the human may
29
+ * inject into a given lane's door. Provenance over content.
30
+ *
31
+ * v1 is Slack-free: NO Slack tokens are required to start.
32
+ */
33
+ declare class ClaudeChannelServer {
34
+ private server;
35
+ private transport;
36
+ private config;
37
+ private offset;
38
+ private buffer;
39
+ private pollTimer;
40
+ constructor();
41
+ /**
42
+ * Emit a `notifications/claude/channel` push. Each `meta` key becomes an
43
+ * attribute on the `<channel ...>` tag the session sees. Try the SDK's typed
44
+ * `server.notification()` first; because `notifications/claude/channel` is not
45
+ * a method the SDK knows, that may fail schema validation, so fall back to
46
+ * writing the raw JSON-RPC line straight to the transport (proven shape).
47
+ */
48
+ private pushToChannel;
49
+ /** Process one raw inbox line: parse, enforce the sender allowlist, push. */
50
+ private handleInboxLine;
51
+ /** Tail our own inbox file; push each newly-appended line. */
52
+ private startInboxTailer;
53
+ private sendToLane;
54
+ private setupHandlers;
55
+ run(): Promise<void>;
56
+ }
57
+ export { ClaudeChannelServer };
58
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAqEH,cAAM,mBAAmB;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAqC;IACtD,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,SAAS,CAA+C;;IAqBhE;;;;;;OAMG;YACW,aAAa;IAqB3B,6EAA6E;YAC/D,eAAe;IA8B7B,8DAA8D;IAC9D,OAAO,CAAC,gBAAgB;YAsDV,UAAU;IAwCxB,OAAO,CAAC,aAAa;IAgDf,GAAG;CAgBV;AAmBD,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,315 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * mcp-claude-channel — lane-to-lane Claude Code side-door channel server (v1).
4
+ *
5
+ * Echelon's "fused line": each PEP lane (R&D · Arch · Dev · Deploy · Test) is a
6
+ * Claude Code session in its own tab. This server automates the baton HANDOFF
7
+ * between tabs — replacing the human walking it lane-to-lane.
8
+ *
9
+ * Net-new to ChillMCP vs. the request/response servers:
10
+ * 1. Server-initiated notifications — emits `notifications/claude/channel`, a
11
+ * server→client push Claude Code injects into the live session as a new
12
+ * `<channel ...>` turn (the "doorbell").
13
+ * 2. A resident file-inbox tailer — watches this lane's inbox and turns each
14
+ * appended line into a push. Peer lanes write to it via `send_to_lane`.
15
+ *
16
+ * Wire model (no shared daemon, pure files — easy to reason about, mirrors the
17
+ * proven `labs/sidedoor-ref/lane-channel.py`):
18
+ * - INBOX = <LANE_INBOX_DIR>/chan-<LANE_NAME>-in.jsonl
19
+ * - this server tails its own INBOX and pushes each new line.
20
+ * - `send_to_lane(target, text, meta?)` appends a line to the TARGET's inbox.
21
+ *
22
+ * THE PRINCIPLE — the push is a NUDGE, not the payload. Content is a short
23
+ * doorbell ("you're next — round R<n>, baton at <path>, check the Board"). The
24
+ * SQLite Lane Board is the source of truth; the receiving lane reads the real
25
+ * baton from the Board, not from the push. (Kills dedup/replay/double-process.)
26
+ *
27
+ * Security: allowlist on the SENDING LANE callsign (`meta.from_lane`), via
28
+ * LANE_ALLOWED_SENDERS. Only the prev lane / fail-back lanes / the human may
29
+ * inject into a given lane's door. Provenance over content.
30
+ *
31
+ * v1 is Slack-free: NO Slack tokens are required to start.
32
+ */
33
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
34
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
35
+ import { ListToolsRequestSchema, CallToolRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
36
+ import { z } from "zod";
37
+ import { fileURLToPath } from "url";
38
+ import { realpathSync, existsSync, closeSync, openSync, statSync, readSync, writeSync } from "fs";
39
+ import { join } from "path";
40
+ const VERSION = "0.1.0";
41
+ function inboxPathFor(dir, lane) {
42
+ return join(dir, `chan-${lane}-in.jsonl`);
43
+ }
44
+ function loadConfigFromEnv() {
45
+ const lane = (process.env.LANE_NAME || "sidedoor").trim();
46
+ const inboxDir = process.env.LANE_INBOX_DIR || "/tmp";
47
+ // Comma/space separated sending-lane callsigns allowed to inject into THIS
48
+ // lane's door (matched against meta.from_lane). Empty = open (dev) + warning.
49
+ const allowedSenders = new Set((process.env.LANE_ALLOWED_SENDERS || "")
50
+ .split(/[,\s]+/)
51
+ .map((s) => s.trim())
52
+ .filter(Boolean));
53
+ return { lane, inboxDir, inboxPath: inboxPathFor(inboxDir, lane), allowedSenders };
54
+ }
55
+ // ---------------------------------------------------------------------------
56
+ // Tool schema
57
+ // ---------------------------------------------------------------------------
58
+ const SendToLaneArgsSchema = z.object({
59
+ target: z.string().min(1).describe("Callsign of the lane to nudge (e.g., bravo, arch, test)"),
60
+ text: z
61
+ .string()
62
+ .min(1)
63
+ .describe("The nudge — a SHORT doorbell, not the payload. e.g. 'you're next — round R7, baton at <path>, check the Board.'"),
64
+ meta: z
65
+ .record(z.any())
66
+ .optional()
67
+ .describe("Structured routing payload: round_id, stage, baton_path, to_lane, etc. from_lane is stamped automatically."),
68
+ });
69
+ // ---------------------------------------------------------------------------
70
+ // Server
71
+ // ---------------------------------------------------------------------------
72
+ class ClaudeChannelServer {
73
+ server;
74
+ transport = null;
75
+ config;
76
+ offset = 0;
77
+ buffer = "";
78
+ pollTimer = null;
79
+ constructor() {
80
+ this.server = new Server({ name: "mcp-claude-channel", version: VERSION }, {
81
+ capabilities: {
82
+ // The empty `claude/channel` object is the handshake badge that arms
83
+ // the receiving session's notification listener — THE thing that makes
84
+ // the side door work.
85
+ experimental: { "claude/channel": {} },
86
+ tools: {},
87
+ },
88
+ });
89
+ this.config = loadConfigFromEnv();
90
+ this.setupHandlers();
91
+ }
92
+ // --- inbound: push a peer-lane nudge into the live session ------------------
93
+ /**
94
+ * Emit a `notifications/claude/channel` push. Each `meta` key becomes an
95
+ * attribute on the `<channel ...>` tag the session sees. Try the SDK's typed
96
+ * `server.notification()` first; because `notifications/claude/channel` is not
97
+ * a method the SDK knows, that may fail schema validation, so fall back to
98
+ * writing the raw JSON-RPC line straight to the transport (proven shape).
99
+ */
100
+ async pushToChannel(content, meta) {
101
+ const merged = { ...meta, source: this.config.lane };
102
+ const params = { content, meta: merged };
103
+ try {
104
+ await this.server.notification({
105
+ method: "notifications/claude/channel",
106
+ params,
107
+ });
108
+ }
109
+ catch {
110
+ if (this.transport) {
111
+ await this.transport.send({
112
+ jsonrpc: "2.0",
113
+ method: "notifications/claude/channel",
114
+ params,
115
+ });
116
+ }
117
+ else {
118
+ console.error("[ClaudeChannel] No transport to push notification.");
119
+ }
120
+ }
121
+ }
122
+ /** Process one raw inbox line: parse, enforce the sender allowlist, push. */
123
+ async handleInboxLine(line) {
124
+ const trimmed = line.trim();
125
+ if (!trimmed)
126
+ return;
127
+ let content;
128
+ let meta;
129
+ try {
130
+ const obj = JSON.parse(trimmed);
131
+ content = typeof obj.content === "string" ? obj.content : trimmed;
132
+ meta = obj.meta && typeof obj.meta === "object" ? obj.meta : {};
133
+ }
134
+ catch {
135
+ // Tolerate a plain-text line (no JSON) — push as-is with empty meta.
136
+ content = trimmed;
137
+ meta = {};
138
+ }
139
+ // THE GATE: allowlist on the SENDING LANE callsign (meta.from_lane).
140
+ const fromLane = typeof meta.from_lane === "string" ? meta.from_lane : "";
141
+ if (this.config.allowedSenders.size > 0 && !this.config.allowedSenders.has(fromLane)) {
142
+ console.error(`[ClaudeChannel] dropped inbound from disallowed lane '${fromLane || "unknown"}' (allowed: ${[
143
+ ...this.config.allowedSenders,
144
+ ].join(",")})`);
145
+ return;
146
+ }
147
+ await this.pushToChannel(content, meta);
148
+ }
149
+ /** Tail our own inbox file; push each newly-appended line. */
150
+ startInboxTailer() {
151
+ const path = this.config.inboxPath;
152
+ // Ensure the inbox exists, then start reading only NEW lines from here on.
153
+ if (!existsSync(path))
154
+ closeSync(openSync(path, "a"));
155
+ try {
156
+ this.offset = statSync(path).size;
157
+ }
158
+ catch {
159
+ this.offset = 0;
160
+ }
161
+ const poll = () => {
162
+ let size;
163
+ try {
164
+ size = statSync(path).size;
165
+ }
166
+ catch {
167
+ return; // file vanished mid-flight; try again next tick
168
+ }
169
+ if (size < this.offset) {
170
+ // Truncated/rotated — start over from the top.
171
+ this.offset = 0;
172
+ this.buffer = "";
173
+ }
174
+ if (size === this.offset)
175
+ return;
176
+ const fd = openSync(path, "r");
177
+ try {
178
+ const len = size - this.offset;
179
+ const buf = Buffer.alloc(len);
180
+ const read = readSync(fd, buf, 0, len, this.offset);
181
+ this.offset += read;
182
+ this.buffer += buf.toString("utf8", 0, read);
183
+ }
184
+ finally {
185
+ closeSync(fd);
186
+ }
187
+ let nl;
188
+ while ((nl = this.buffer.indexOf("\n")) !== -1) {
189
+ const line = this.buffer.slice(0, nl);
190
+ this.buffer = this.buffer.slice(nl + 1);
191
+ void this.handleInboxLine(line);
192
+ }
193
+ };
194
+ this.pollTimer = setInterval(poll, 500);
195
+ console.error(`[ClaudeChannel] lane '${this.config.lane}' tailing inbox ${path}` +
196
+ (this.config.allowedSenders.size > 0
197
+ ? ` — allowed senders: ${[...this.config.allowedSenders].join(",")}`
198
+ : " — WARNING: no LANE_ALLOWED_SENDERS set, accepting any sender"));
199
+ }
200
+ // --- outbound: nudge another lane ------------------------------------------
201
+ async sendToLane(args) {
202
+ const validated = SendToLaneArgsSchema.parse(args);
203
+ const targetPath = inboxPathFor(this.config.inboxDir, validated.target);
204
+ // Stamp provenance: from_lane is OURS (the gate on the receiving side keys on
205
+ // this), to_lane is the target. Caller-supplied meta wins for everything else.
206
+ const meta = {
207
+ to_lane: validated.target,
208
+ ...(validated.meta || {}),
209
+ from_lane: this.config.lane,
210
+ };
211
+ const record = JSON.stringify({ content: validated.text, meta });
212
+ try {
213
+ const fd = openSync(targetPath, "a");
214
+ try {
215
+ writeSync(fd, record + "\n");
216
+ }
217
+ finally {
218
+ closeSync(fd);
219
+ }
220
+ return {
221
+ content: [
222
+ {
223
+ type: "text",
224
+ text: JSON.stringify({ ok: true, delivered_to: validated.target, inbox: targetPath, from_lane: this.config.lane }, null, 2),
225
+ },
226
+ ],
227
+ };
228
+ }
229
+ catch (err) {
230
+ return {
231
+ content: [{ type: "text", text: `Error delivering to lane '${validated.target}': ${err?.message || err}` }],
232
+ isError: true,
233
+ };
234
+ }
235
+ }
236
+ // --- handlers ---------------------------------------------------------------
237
+ setupHandlers() {
238
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
239
+ const tools = [
240
+ {
241
+ name: "send_to_lane",
242
+ description: "Nudge ANOTHER lane's Claude Code session via the side door — the session→session handoff. Send a SHORT doorbell ('you're next — round R<n>, baton at <path>, check the Board'), NOT the payload: the SQLite Lane Board is the source of truth, the receiving lane reads the real baton from there. Carry routing in meta (round_id, stage, baton_path, to_lane). from_lane is stamped automatically for the receiver's allowlist.",
243
+ inputSchema: {
244
+ type: "object",
245
+ properties: {
246
+ target: {
247
+ type: "string",
248
+ description: "Callsign of the lane to nudge (e.g., bravo, arch, test)",
249
+ },
250
+ text: {
251
+ type: "string",
252
+ description: "The nudge — a short doorbell pointing at the Board, not the baton payload",
253
+ },
254
+ meta: {
255
+ type: "object",
256
+ description: "Structured routing: round_id, stage, baton_path, to_lane. from_lane is added automatically.",
257
+ },
258
+ },
259
+ required: ["target", "text"],
260
+ },
261
+ },
262
+ ];
263
+ return { tools };
264
+ });
265
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
266
+ try {
267
+ const { name, arguments: args } = request.params;
268
+ switch (name) {
269
+ case "send_to_lane":
270
+ return await this.sendToLane(args);
271
+ default:
272
+ throw new Error(`Unknown tool: ${name}`);
273
+ }
274
+ }
275
+ catch (error) {
276
+ const msg = error instanceof Error ? error.message : String(error);
277
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
278
+ }
279
+ });
280
+ }
281
+ // --- boot -------------------------------------------------------------------
282
+ async run() {
283
+ this.transport = new StdioServerTransport();
284
+ await this.server.connect(this.transport);
285
+ console.error(`[ClaudeChannel] mcp-claude-channel v${VERSION} running on stdio — lane '${this.config.lane}'`);
286
+ // Start the tailer AFTER the transport is connected so the first push lands.
287
+ this.startInboxTailer();
288
+ const shutdown = () => {
289
+ if (this.pollTimer)
290
+ clearInterval(this.pollTimer);
291
+ process.exit(0);
292
+ };
293
+ process.once("SIGINT", shutdown);
294
+ process.once("SIGTERM", shutdown);
295
+ }
296
+ }
297
+ // Boot guard — only run when invoked directly (mirrors mcp-slack).
298
+ const isMain = (() => {
299
+ const entry = process.argv[1];
300
+ if (!entry)
301
+ return false;
302
+ const self = fileURLToPath(import.meta.url);
303
+ try {
304
+ return realpathSync(entry) === realpathSync(self);
305
+ }
306
+ catch {
307
+ return entry === self;
308
+ }
309
+ })();
310
+ if (isMain) {
311
+ const server = new ClaudeChannelServer();
312
+ server.run().catch(console.error);
313
+ }
314
+ export { ClaudeChannelServer };
315
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E4Qkc7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sMkNBQTJDLENBQUM7QUFDbkUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkNBQTJDLENBQUM7QUFDakYsT0FBTyxFQUNMLHNCQUFzQixFQUN0QixxQkFBcUIsR0FFdEIsTUFBTSxvQ0FBb0MsQ0FBQztBQUM1QyxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBQ3hCLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFDcEMsT0FBTyxFQUFFLFlBQVksRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxNQUFNLElBQUksQ0FBQztBQUNsRyxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBRTVCLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQztBQWF4QixTQUFTLFlBQVksQ0FBQyxHQUFXLEVBQUUsSUFBWTtJQUM3QyxPQUFPLElBQUksQ0FBQyxHQUFHLEVBQUUsUUFBUSxJQUFJLFdBQVcsQ0FBQyxDQUFDO0FBQzVDLENBQUM7QUFFRCxTQUFTLGlCQUFpQjtJQUN4QixNQUFNLElBQUksR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsU0FBUyxJQUFJLFVBQVUsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQzFELE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxJQUFJLE1BQU0sQ0FBQztJQUV0RCwyRUFBMkU7SUFDM0UsOEVBQThFO0lBQzlFLE1BQU0sY0FBYyxHQUFHLElBQUksR0FBRyxDQUM1QixDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsb0JBQW9CLElBQUksRUFBRSxDQUFDO1NBQ3JDLEtBQUssQ0FBQyxRQUFRLENBQUM7U0FDZixHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztTQUNwQixNQUFNLENBQUMsT0FBTyxDQUFDLENBQ25CLENBQUM7SUFFRixPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsWUFBWSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsRUFBRSxjQUFjLEVBQUUsQ0FBQztBQUNyRixDQUFDO0FBRUQsOEVBQThFO0FBQzlFLGNBQWM7QUFDZCw4RUFBOEU7QUFFOUUsTUFBTSxvQkFBb0IsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQ3BDLE1BQU0sRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyx5REFBeUQsQ0FBQztJQUM3RixJQUFJLEVBQUUsQ0FBQztTQUNKLE1BQU0sRUFBRTtTQUNSLEdBQUcsQ0FBQyxDQUFDLENBQUM7U0FDTixRQUFRLENBQ1AsaUhBQWlILENBQ2xIO0lBQ0gsSUFBSSxFQUFFLENBQUM7U0FDSixNQUFNLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDO1NBQ2YsUUFBUSxFQUFFO1NBQ1YsUUFBUSxDQUFDLDRHQUE0RyxDQUFDO0NBQzFILENBQUMsQ0FBQztBQUVILDhFQUE4RTtBQUM5RSxTQUFTO0FBQ1QsOEVBQThFO0FBRTlFLE1BQU0sbUJBQW1CO0lBQ2YsTUFBTSxDQUFTO0lBQ2YsU0FBUyxHQUFnQyxJQUFJLENBQUM7SUFDOUMsTUFBTSxDQUFhO0lBQ25CLE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDWCxNQUFNLEdBQUcsRUFBRSxDQUFDO0lBQ1osU0FBUyxHQUEwQyxJQUFJLENBQUM7SUFFaEU7UUFDRSxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksTUFBTSxDQUN0QixFQUFFLElBQUksRUFBRSxvQkFBb0IsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLEVBQ2hEO1lBQ0UsWUFBWSxFQUFFO2dCQUNaLHFFQUFxRTtnQkFDckUsdUVBQXVFO2dCQUN2RSxzQkFBc0I7Z0JBQ3RCLFlBQVksRUFBRSxFQUFFLGdCQUFnQixFQUFFLEVBQUUsRUFBRTtnQkFDdEMsS0FBSyxFQUFFLEVBQUU7YUFDVjtTQUNGLENBQ0YsQ0FBQztRQUNGLElBQUksQ0FBQyxNQUFNLEdBQUcsaUJBQWlCLEVBQUUsQ0FBQztRQUNsQyxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7SUFDdkIsQ0FBQztJQUVELCtFQUErRTtJQUUvRTs7Ozs7O09BTUc7SUFDSyxLQUFLLENBQUMsYUFBYSxDQUFDLE9BQWUsRUFBRSxJQUE2QjtRQUN4RSxNQUFNLE1BQU0sR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3JELE1BQU0sTUFBTSxHQUFHLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsQ0FBQztRQUN6QyxJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDO2dCQUM3QixNQUFNLEVBQUUsOEJBQThCO2dCQUN0QyxNQUFNO2FBQ0EsQ0FBQyxDQUFDO1FBQ1osQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO2dCQUNuQixNQUFNLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDO29CQUN4QixPQUFPLEVBQUUsS0FBSztvQkFDZCxNQUFNLEVBQUUsOEJBQThCO29CQUN0QyxNQUFNO2lCQUNBLENBQUMsQ0FBQztZQUNaLENBQUM7aUJBQU0sQ0FBQztnQkFDTixPQUFPLENBQUMsS0FBSyxDQUFDLG9EQUFvRCxDQUFDLENBQUM7WUFDdEUsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQsNkVBQTZFO0lBQ3JFLEtBQUssQ0FBQyxlQUFlLENBQUMsSUFBWTtRQUN4QyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDNUIsSUFBSSxDQUFDLE9BQU87WUFBRSxPQUFPO1FBRXJCLElBQUksT0FBZSxDQUFDO1FBQ3BCLElBQUksSUFBNkIsQ0FBQztRQUNsQyxJQUFJLENBQUM7WUFDSCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ2hDLE9BQU8sR0FBRyxPQUFPLEdBQUcsQ0FBQyxPQUFPLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUM7WUFDbEUsSUFBSSxHQUFHLEdBQUcsQ0FBQyxJQUFJLElBQUksT0FBTyxHQUFHLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ2xFLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxxRUFBcUU7WUFDckUsT0FBTyxHQUFHLE9BQU8sQ0FBQztZQUNsQixJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ1osQ0FBQztRQUVELHFFQUFxRTtRQUNyRSxNQUFNLFFBQVEsR0FBRyxPQUFPLElBQUksQ0FBQyxTQUFTLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBRSxJQUFJLENBQUMsU0FBb0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ3RGLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQ3JGLE9BQU8sQ0FBQyxLQUFLLENBQ1gseURBQXlELFFBQVEsSUFBSSxTQUFTLGVBQWU7Z0JBQzNGLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxjQUFjO2FBQzlCLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQ2YsQ0FBQztZQUNGLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxJQUFJLENBQUMsYUFBYSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztJQUMxQyxDQUFDO0lBRUQsOERBQThEO0lBQ3RELGdCQUFnQjtRQUN0QixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQztRQUNuQywyRUFBMkU7UUFDM0UsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUM7WUFBRSxTQUFTLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3RELElBQUksQ0FBQztZQUNILElBQUksQ0FBQyxNQUFNLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQztRQUNwQyxDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1AsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7UUFDbEIsQ0FBQztRQUVELE1BQU0sSUFBSSxHQUFHLEdBQUcsRUFBRTtZQUNoQixJQUFJLElBQVksQ0FBQztZQUNqQixJQUFJLENBQUM7Z0JBQ0gsSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFDN0IsQ0FBQztZQUFDLE1BQU0sQ0FBQztnQkFDUCxPQUFPLENBQUMsZ0RBQWdEO1lBQzFELENBQUM7WUFDRCxJQUFJLElBQUksR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7Z0JBQ3ZCLCtDQUErQztnQkFDL0MsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7Z0JBQ2hCLElBQUksQ0FBQyxNQUFNLEdBQUcsRUFBRSxDQUFDO1lBQ25CLENBQUM7WUFDRCxJQUFJLElBQUksS0FBSyxJQUFJLENBQUMsTUFBTTtnQkFBRSxPQUFPO1lBRWpDLE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDL0IsSUFBSSxDQUFDO2dCQUNILE1BQU0sR0FBRyxHQUFHLElBQUksR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDO2dCQUMvQixNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUM5QixNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDcEQsSUFBSSxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUM7Z0JBQ3BCLElBQUksQ0FBQyxNQUFNLElBQUksR0FBRyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQy9DLENBQUM7b0JBQVMsQ0FBQztnQkFDVCxTQUFTLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDaEIsQ0FBQztZQUVELElBQUksRUFBVSxDQUFDO1lBQ2YsT0FBTyxDQUFDLEVBQUUsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQy9DLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztnQkFDdEMsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ3hDLEtBQUssSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNsQyxDQUFDO1FBQ0gsQ0FBQyxDQUFDO1FBRUYsSUFBSSxDQUFDLFNBQVMsR0FBRyxXQUFXLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQ3hDLE9BQU8sQ0FBQyxLQUFLLENBQ1gseUJBQXlCLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxtQkFBbUIsSUFBSSxFQUFFO1lBQ2hFLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxHQUFHLENBQUM7Z0JBQ2xDLENBQUMsQ0FBQyx1QkFBdUIsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFO2dCQUNwRSxDQUFDLENBQUMsK0RBQStELENBQUMsQ0FDdkUsQ0FBQztJQUNKLENBQUM7SUFFRCw4RUFBOEU7SUFFdEUsS0FBSyxDQUFDLFVBQVUsQ0FBQyxJQUFhO1FBQ3BDLE1BQU0sU0FBUyxHQUFHLG9CQUFvQixDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNuRCxNQUFNLFVBQVUsR0FBRyxZQUFZLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUUsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3hFLDhFQUE4RTtRQUM5RSwrRUFBK0U7UUFDL0UsTUFBTSxJQUFJLEdBQUc7WUFDWCxPQUFPLEVBQUUsU0FBUyxDQUFDLE1BQU07WUFDekIsR0FBRyxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDO1lBQ3pCLFNBQVMsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUk7U0FDNUIsQ0FBQztRQUNGLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxPQUFPLEVBQUUsU0FBUyxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ2pFLElBQUksQ0FBQztZQUNILE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxVQUFVLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDckMsSUFBSSxDQUFDO2dCQUNILFNBQVMsQ0FBQyxFQUFFLEVBQUUsTUFBTSxHQUFHLElBQUksQ0FBQyxDQUFDO1lBQy9CLENBQUM7b0JBQVMsQ0FBQztnQkFDVCxTQUFTLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDaEIsQ0FBQztZQUNELE9BQU87Z0JBQ0wsT0FBTyxFQUFFO29CQUNQO3dCQUNFLElBQUksRUFBRSxNQUFNO3dCQUNaLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUNsQixFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFLFNBQVMsQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsRUFDNUYsSUFBSSxFQUNKLENBQUMsQ0FDRjtxQkFDRjtpQkFDRjthQUNGLENBQUM7UUFDSixDQUFDO1FBQUMsT0FBTyxHQUFRLEVBQUUsQ0FBQztZQUNsQixPQUFPO2dCQUNMLE9BQU8sRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsNkJBQTZCLFNBQVMsQ0FBQyxNQUFNLE1BQU0sR0FBRyxFQUFFLE9BQU8sSUFBSSxHQUFHLEVBQUUsRUFBRSxDQUFDO2dCQUMzRyxPQUFPLEVBQUUsSUFBSTthQUNkLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztJQUVELCtFQUErRTtJQUV2RSxhQUFhO1FBQ25CLElBQUksQ0FBQyxNQUFNLENBQUMsaUJBQWlCLENBQUMsc0JBQXNCLEVBQUUsS0FBSyxJQUFJLEVBQUU7WUFDL0QsTUFBTSxLQUFLLEdBQVc7Z0JBQ3BCO29CQUNFLElBQUksRUFBRSxjQUFjO29CQUNwQixXQUFXLEVBQ1QsbWFBQW1hO29CQUNyYSxXQUFXLEVBQUU7d0JBQ1gsSUFBSSxFQUFFLFFBQVE7d0JBQ2QsVUFBVSxFQUFFOzRCQUNWLE1BQU0sRUFBRTtnQ0FDTixJQUFJLEVBQUUsUUFBUTtnQ0FDZCxXQUFXLEVBQUUseURBQXlEOzZCQUN2RTs0QkFDRCxJQUFJLEVBQUU7Z0NBQ0osSUFBSSxFQUFFLFFBQVE7Z0NBQ2QsV0FBVyxFQUFFLDJFQUEyRTs2QkFDekY7NEJBQ0QsSUFBSSxFQUFFO2dDQUNKLElBQUksRUFBRSxRQUFRO2dDQUNkLFdBQVcsRUFBRSw2RkFBNkY7NkJBQzNHO3lCQUNGO3dCQUNELFFBQVEsRUFBRSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7cUJBQzdCO2lCQUNGO2FBQ0YsQ0FBQztZQUNGLE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQztRQUNuQixDQUFDLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxNQUFNLENBQUMsaUJBQWlCLENBQUMscUJBQXFCLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxFQUFFO1lBQ3JFLElBQUksQ0FBQztnQkFDSCxNQUFNLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDO2dCQUNqRCxRQUFRLElBQUksRUFBRSxDQUFDO29CQUNiLEtBQUssY0FBYzt3QkFDakIsT0FBTyxNQUFNLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7b0JBQ3JDO3dCQUNFLE1BQU0sSUFBSSxLQUFLLENBQUMsaUJBQWlCLElBQUksRUFBRSxDQUFDLENBQUM7Z0JBQzdDLENBQUM7WUFDSCxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixNQUFNLEdBQUcsR0FBRyxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQ25FLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFVBQVUsR0FBRyxFQUFFLEVBQUUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQztZQUMvRSxDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsK0VBQStFO0lBRS9FLEtBQUssQ0FBQyxHQUFHO1FBQ1AsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLG9CQUFvQixFQUFFLENBQUM7UUFDNUMsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDMUMsT0FBTyxDQUFDLEtBQUssQ0FDWCx1Q0FBdUMsT0FBTyw2QkFBNkIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEdBQUcsQ0FDL0YsQ0FBQztRQUNGLDZFQUE2RTtRQUM3RSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUV4QixNQUFNLFFBQVEsR0FBRyxHQUFHLEVBQUU7WUFDcEIsSUFBSSxJQUFJLENBQUMsU0FBUztnQkFBRSxhQUFhLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQ2xELE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbEIsQ0FBQyxDQUFDO1FBQ0YsT0FBTyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDakMsT0FBTyxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDcEMsQ0FBQztDQUNGO0FBRUQsbUVBQW1FO0FBQ25FLE1BQU0sTUFBTSxHQUFHLENBQUMsR0FBRyxFQUFFO0lBQ25CLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDOUIsSUFBSSxDQUFDLEtBQUs7UUFBRSxPQUFPLEtBQUssQ0FBQztJQUN6QixNQUFNLElBQUksR0FBRyxhQUFhLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM1QyxJQUFJLENBQUM7UUFDSCxPQUFPLFlBQVksQ0FBQyxLQUFLLENBQUMsS0FBSyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDcEQsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLE9BQU8sS0FBSyxLQUFLLElBQUksQ0FBQztJQUN4QixDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMLElBQUksTUFBTSxFQUFFLENBQUM7SUFDWCxNQUFNLE1BQU0sR0FBRyxJQUFJLG1CQUFtQixFQUFFLENBQUM7SUFDekMsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDcEMsQ0FBQztBQUVELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxDQUFDIn0=
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@chinchillaenterprises/mcp-claude-channel",
3
+ "version": "0.1.0",
4
+ "description": "Lane-to-lane Claude Code side-door channel server. Pushes a NUDGE from a peer lane INTO a live Claude Code session via the official Channels notification (notifications/claude/channel) and exposes a send_to_lane tool to nudge the next lane. v1: session↔session only, no Slack. The SQLite Lane Board stays the source of truth; the push is a doorbell, not the payload.",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "mcp-claude-channel": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc && chmod +x dist/index.js",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "prepare": "npm run build"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "keywords": [
20
+ "mcp",
21
+ "claude-code",
22
+ "channels",
23
+ "lane-to-lane",
24
+ "side-door",
25
+ "notifications"
26
+ ],
27
+ "author": "Chinchilla Enterprises",
28
+ "license": "MIT",
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "dependencies": {
33
+ "@modelcontextprotocol/sdk": "^1.12.3",
34
+ "zod": "^3.25.64"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^24.0.1",
38
+ "typescript": "^5.8.3"
39
+ }
40
+ }