@gonzih/cc-discord 0.2.1 → 0.2.3

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/bot.d.ts CHANGED
@@ -64,6 +64,7 @@ export declare class CcDiscordBot {
64
64
  private sendToChannel;
65
65
  /** Send to a channel by ID — used by notifier callbacks. */
66
66
  sendToChannelById(channelId: string, text: string): Promise<void>;
67
+ sendAttachmentToChannelById(channelId: string, filePath: string): Promise<void>;
67
68
  private isAllowed;
68
69
  private handleMessage;
69
70
  private handleVoice;
package/dist/bot.js CHANGED
@@ -294,6 +294,15 @@ export class CcDiscordBot {
294
294
  }
295
295
  await this.sendToChannel(channel, text);
296
296
  }
297
+ async sendAttachmentToChannelById(channelId, filePath) {
298
+ const channel = await this.getChannel(channelId);
299
+ if (!channel) {
300
+ console.warn(`[bot] sendAttachmentToChannelById: channel ${channelId} not found`);
301
+ return;
302
+ }
303
+ const attachment = new AttachmentBuilder(filePath, { name: basename(filePath) });
304
+ await channel.send({ files: [attachment] });
305
+ }
297
306
  isAllowed(userId) {
298
307
  if (!this.opts.allowedUserIds?.length)
299
308
  return true;
@@ -59,25 +59,43 @@ export function injectMcp(ns, wsPath, token) {
59
59
  const npmCache = process.env.npm_config_cache ?? `${homedir()}/.npm`;
60
60
  const trustedOwners = process.env.CC_AGENT_TRUSTED_OWNERS ?? "";
61
61
  const systemPath = process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin";
62
- const config = {
63
- mcpServers: {
64
- "cc-agent": {
65
- command: "/opt/homebrew/bin/npx",
66
- args: ["-y", "--prefer-online", "@gonzih/cc-agent"],
67
- env: {
68
- CC_AGENT_NAMESPACE: ns,
69
- CWD: wsPath,
70
- CLAUDE_CODE_OAUTH_TOKEN: token,
71
- CLAUDE_TOKENS: token,
72
- CC_AGENT_TRUSTED_OWNERS: trustedOwners,
73
- PATH: systemPath,
74
- npm_config_cache: npmCache,
75
- },
62
+ const servers = {
63
+ "cc-agent": {
64
+ command: "/opt/homebrew/bin/npx",
65
+ args: ["-y", "--prefer-online", "@gonzih/cc-agent"],
66
+ env: {
67
+ CC_AGENT_NAMESPACE: ns,
68
+ CWD: wsPath,
69
+ CLAUDE_CODE_OAUTH_TOKEN: token,
70
+ CLAUDE_TOKENS: token,
71
+ CC_AGENT_TRUSTED_OWNERS: trustedOwners,
72
+ PATH: systemPath,
73
+ npm_config_cache: npmCache,
76
74
  },
77
75
  },
78
76
  };
77
+ // gmail MCP — enabled when GMAIL_EMAIL + GMAIL_APP_PASSWORD are in env
78
+ const gmailEmail = process.env.GMAIL_EMAIL;
79
+ const gmailPass = process.env.GMAIL_APP_PASSWORD;
80
+ if (gmailEmail && gmailPass) {
81
+ servers["gmail-personal"] = {
82
+ command: "/opt/homebrew/bin/npx",
83
+ args: ["-y", "gmail-mcp-imap"],
84
+ env: { GMAIL_EMAIL: gmailEmail, GMAIL_APP_PASSWORD: gmailPass, npm_config_cache: npmCache },
85
+ };
86
+ }
87
+ // github MCP — enabled when GITHUB_PERSONAL_ACCESS_TOKEN is in env
88
+ const ghToken = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
89
+ if (ghToken) {
90
+ servers["github"] = {
91
+ command: "docker",
92
+ args: ["run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server"],
93
+ env: { GITHUB_PERSONAL_ACCESS_TOKEN: ghToken },
94
+ };
95
+ }
96
+ const config = { mcpServers: servers };
79
97
  writeFileSync(mcpPath, JSON.stringify(config, null, 2), "utf8");
80
- console.log(`[meta-agent-manager] injected MCP config for namespace=${ns}`);
98
+ console.log(`[meta-agent-manager] injected MCP config for namespace=${ns} (servers: ${Object.keys(servers).join(", ")})`);
81
99
  }
82
100
  /**
83
101
  * Resolve claude binary — same logic as claude.ts resolveClaude.
package/dist/notifier.js CHANGED
@@ -10,8 +10,23 @@
10
10
  * cca:discord:chat:log:{ns} — LPUSH + LTRIM 0 499 (last 500 messages)
11
11
  * cca:discord:chat:outgoing:{ns} — PUBLISH for web UI to consume
12
12
  */
13
- import { discordChatLog, discordChatOutgoing, discordNotify, chatIncomingChannel, createCcWire, TIMING, } from "@gonzih/cc-wire";
13
+ import { discordChatLog, discordChatOutgoing, discordNotify, notifyChannel, chatIncomingChannel, createCcWire, TIMING, } from "@gonzih/cc-wire";
14
+ import { existsSync, statSync } from "fs";
14
15
  import { splitLongMessage, stripAnsi } from "./formatter.js";
16
+ const SAFE_DIRS = ["/tmp/", "/var/folders/"];
17
+ const MAX_DISCORD_BYTES = 25 * 1024 * 1024; // 25 MB Discord file limit
18
+ /** Extract /tmp/ or /var/folders/ file paths mentioned in text that actually exist on disk. */
19
+ function extractAttachablePaths(text) {
20
+ const pattern = /(?:^|[\s`'"(])(\/(?:tmp|var\/folders)\/[\w.\-/]+\.[\w]{1,10})(?:[\s`'")\n]|$)/gm;
21
+ const quoted = /"(\/(?:tmp|var\/folders)\/[^"]+\.[a-zA-Z0-9]{1,10})"|'(\/(?:tmp|var\/folders)\/[^']+\.[a-zA-Z0-9]{1,10})'/g;
22
+ const candidates = new Set();
23
+ let m;
24
+ while ((m = pattern.exec(text)) !== null)
25
+ candidates.add(m[1]);
26
+ while ((m = quoted.exec(text)) !== null)
27
+ candidates.add(m[1] ?? m[2]);
28
+ return [...candidates].filter(p => SAFE_DIRS.some(d => p.startsWith(d)) && existsSync(p));
29
+ }
15
30
  function log(level, ...args) {
16
31
  const fn = level === "error" ? console.error : level === "warn" ? console.warn : console.log;
17
32
  fn("[notifier]", ...args);
@@ -137,9 +152,11 @@ export function startNotifier(bot, notifyChannelId, namespace, redis, handleUser
137
152
  if (subscribedNamespaces.has(ns))
138
153
  return;
139
154
  subscribedNamespaces.add(ns);
140
- const notifyCh = discordNotify(ns);
155
+ const notifyCh = discordNotify(ns); // cca:discord:notify:{ns} — new cc-wire path
156
+ const legacyNotifyCh = notifyChannel(ns); // cca:notify:{ns} — cc-agent still publishes here
141
157
  const incomingCh = chatIncomingChannel(ns);
142
158
  channelToNamespace.set(notifyCh, ns);
159
+ channelToNamespace.set(legacyNotifyCh, ns);
143
160
  channelToNamespace.set(incomingCh, ns);
144
161
  sub.subscribe(notifyCh, (err) => {
145
162
  if (err) {
@@ -149,6 +166,15 @@ export function startNotifier(bot, notifyChannelId, namespace, redis, handleUser
149
166
  log("info", `subscribed to ${notifyCh}`);
150
167
  }
151
168
  });
169
+ // Also subscribe to legacy cc-agent notification channel
170
+ sub.subscribe(legacyNotifyCh, (err) => {
171
+ if (err) {
172
+ log("error", `subscribe ${legacyNotifyCh} failed:`, err.message);
173
+ }
174
+ else {
175
+ log("info", `subscribed to ${legacyNotifyCh} (legacy cc-agent)`);
176
+ }
177
+ });
152
178
  sub.subscribe(incomingCh, (err) => {
153
179
  if (err) {
154
180
  log("error", `subscribe ${incomingCh} failed:`, err.message);
@@ -182,7 +208,8 @@ export function startNotifier(bot, notifyChannelId, namespace, redis, handleUser
182
208
  const buf = metaAgentBuffers.get(ns);
183
209
  if (!buf || !buf.text.trim())
184
210
  return;
185
- const text = `← [${ns}] ` + stripAnsi(buf.text.trim());
211
+ const rawText = stripAnsi(buf.text.trim());
212
+ const text = `← [${ns}] ` + rawText;
186
213
  buf.text = "";
187
214
  buf.timer = null;
188
215
  const chunks = splitLongMessage(text);
@@ -191,6 +218,25 @@ export function startNotifier(bot, notifyChannelId, namespace, redis, handleUser
191
218
  log("warn", `meta-agent flush sendToChannelById failed (ns=${ns}):`, err.message);
192
219
  });
193
220
  }
221
+ // Attach any /tmp/ files mentioned in the response
222
+ const paths = extractAttachablePaths(rawText);
223
+ for (const filePath of paths) {
224
+ let size;
225
+ try {
226
+ size = statSync(filePath).size;
227
+ }
228
+ catch {
229
+ continue;
230
+ }
231
+ if (size > MAX_DISCORD_BYTES) {
232
+ bot.sendToChannelById(targetChannelId, `File too large for Discord (${(size / 1024 / 1024).toFixed(1)} MB): ${filePath}`).catch(() => { });
233
+ continue;
234
+ }
235
+ log("info", `attaching file to Discord (ns=${ns}): ${filePath}`);
236
+ bot.sendAttachmentToChannelById(targetChannelId, filePath).catch((err) => {
237
+ log("warn", `attachment send failed (ns=${ns}, path=${filePath}):`, err.message);
238
+ });
239
+ }
194
240
  }
195
241
  sub.on("pmessage", (pattern, channel, message) => {
196
242
  void pattern;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gonzih/cc-discord",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Claude Code Discord bot — chat with Claude Code via Discord",
5
5
  "type": "module",
6
6
  "bin": {