@gonzih/cc-discord 0.1.1 → 0.1.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.js +1 -51
- package/dist/router.d.ts +2 -27
- package/dist/router.js +1 -46
- package/package.json +1 -1
package/dist/bot.js
CHANGED
|
@@ -13,8 +13,7 @@ import { formatForDiscord, splitLongMessage, stripAnsi } from "./formatter.js";
|
|
|
13
13
|
import { getCurrentToken } from "./tokens.js";
|
|
14
14
|
import { writeChatLog } from "./notifier.js";
|
|
15
15
|
import { CronManager } from "./cron.js";
|
|
16
|
-
import {
|
|
17
|
-
import { metaAgentStatusKey } from "@gonzih/cc-wire";
|
|
16
|
+
import { parseChannelCreateIntent, ensureMetaAgent, routeToMetaAgent } from "./router.js";
|
|
18
17
|
/** Convert a Discord snowflake string to a safe 53-bit integer for CronManager compatibility. */
|
|
19
18
|
function snowflakeToInt(id) {
|
|
20
19
|
// Discord snowflakes are up to 2^63, beyond Number.MAX_SAFE_INTEGER.
|
|
@@ -278,55 +277,6 @@ export class CcDiscordBot {
|
|
|
278
277
|
}
|
|
279
278
|
return;
|
|
280
279
|
}
|
|
281
|
-
// #tag / #org/repo routing — delegate to meta-agent
|
|
282
|
-
if (this.redis) {
|
|
283
|
-
const routing = parseRoutingTag(text);
|
|
284
|
-
if (routing) {
|
|
285
|
-
const channel = msg.channel;
|
|
286
|
-
await channel.send(`→ #${routing.namespace}`).catch(() => { });
|
|
287
|
-
this.writeChatMessage("user", "discord", text, effectiveChannelId);
|
|
288
|
-
this.opts.registerRoutedChannelId?.(routing.namespace, effectiveChannelId);
|
|
289
|
-
try {
|
|
290
|
-
await ensureMetaAgent(routing.namespace, routing.repoUrl, (toolName, args) => this.callCcAgentTool(toolName, args ?? {}), this.redis);
|
|
291
|
-
await routeToMetaAgent(routing.namespace, routing.strippedMessage, this.redis);
|
|
292
|
-
}
|
|
293
|
-
catch (err) {
|
|
294
|
-
await channel.send(`Failed to route to #${routing.namespace}: ${err.message}`).catch(() => { });
|
|
295
|
-
}
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
// Channel name → meta-agent namespace routing
|
|
300
|
-
if (this.redis) {
|
|
301
|
-
const channelName = msg.channel.name ?? "";
|
|
302
|
-
if (channelName && channelName !== "general") {
|
|
303
|
-
// Check if a meta-agent is running for this channel name as namespace
|
|
304
|
-
const ns = channelName.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
305
|
-
try {
|
|
306
|
-
const statusRaw = await this.redis.get(metaAgentStatusKey(ns));
|
|
307
|
-
if (statusRaw) {
|
|
308
|
-
const status = JSON.parse(statusRaw);
|
|
309
|
-
if (status.status === "running" || status.status === "idle") {
|
|
310
|
-
// Route to meta-agent
|
|
311
|
-
const channel = msg.channel;
|
|
312
|
-
await channel.send(`→ #${ns} (meta-agent)`).catch(() => { });
|
|
313
|
-
this.writeChatMessage("user", "discord", text, effectiveChannelId);
|
|
314
|
-
this.opts.registerRoutedChannelId?.(ns, effectiveChannelId);
|
|
315
|
-
try {
|
|
316
|
-
await routeToMetaAgent(ns, text, this.redis);
|
|
317
|
-
}
|
|
318
|
-
catch (err) {
|
|
319
|
-
await channel.send(`Failed to route to #${ns}: ${err.message}`).catch(() => { });
|
|
320
|
-
}
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
catch {
|
|
326
|
-
// Redis error — fall through to local session
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
280
|
// Local Claude session
|
|
331
281
|
const session = this.getOrCreateSession(effectiveChannelId, msg.channel);
|
|
332
282
|
try {
|
package/dist/router.d.ts
CHANGED
|
@@ -1,34 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Routing helpers:
|
|
3
|
-
*
|
|
4
|
-
* Parses #tag or #org/repo tokens from Telegram messages and routes them to
|
|
5
|
-
* the appropriate cc-agent meta-agent instead of the local Claude session.
|
|
6
|
-
*
|
|
7
|
-
* Tag formats:
|
|
8
|
-
* #repo-name → namespace=repo-name, repo=https://github.com/{DEFAULT_GITHUB_ORG}/repo-name (null if DEFAULT_GITHUB_ORG unset)
|
|
9
|
-
* #org/repo → namespace=repo, repo=https://github.com/org/repo
|
|
2
|
+
* Routing helpers: channel-creation intent detection and meta-agent routing.
|
|
10
3
|
*/
|
|
11
4
|
import { Redis } from "ioredis";
|
|
12
|
-
/** Callback type matching
|
|
5
|
+
/** Callback type matching CcDiscordBot.callCcAgentTool */
|
|
13
6
|
export type CallToolFn = (toolName: string, args?: Record<string, unknown>) => Promise<string | null>;
|
|
14
|
-
export interface RoutingTag {
|
|
15
|
-
namespace: string;
|
|
16
|
-
repoUrl: string;
|
|
17
|
-
/** Original message with the tag token stripped and whitespace collapsed */
|
|
18
|
-
strippedMessage: string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Parse the first #tag or #org/repo token from a message.
|
|
22
|
-
* Returns null when no routing tag is present, or when the short #repo format is used
|
|
23
|
-
* without DEFAULT_GITHUB_ORG being set (operators must configure this explicitly).
|
|
24
|
-
*
|
|
25
|
-
* Examples:
|
|
26
|
-
* "#cc-agent fix the bug" → null if DEFAULT_GITHUB_ORG unset; { namespace: "cc-agent", repoUrl: "…/{org}/cc-agent", … } if set
|
|
27
|
-
* "#gonzih/of-stack deploy it" → { namespace: "of-stack", repoUrl: "…/gonzih/of-stack", … }
|
|
28
|
-
* "#org/repo do something" → { namespace: "repo", repoUrl: "…/org/repo", … }
|
|
29
|
-
* "please help #of-stack with this" → null if DEFAULT_GITHUB_ORG unset
|
|
30
|
-
*/
|
|
31
|
-
export declare function parseRoutingTag(text: string): RoutingTag | null;
|
|
32
7
|
/**
|
|
33
8
|
* Ensure a meta-agent for the given namespace is running.
|
|
34
9
|
*
|
package/dist/router.js
CHANGED
|
@@ -1,53 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Routing helpers:
|
|
3
|
-
*
|
|
4
|
-
* Parses #tag or #org/repo tokens from Telegram messages and routes them to
|
|
5
|
-
* the appropriate cc-agent meta-agent instead of the local Claude session.
|
|
6
|
-
*
|
|
7
|
-
* Tag formats:
|
|
8
|
-
* #repo-name → namespace=repo-name, repo=https://github.com/{DEFAULT_GITHUB_ORG}/repo-name (null if DEFAULT_GITHUB_ORG unset)
|
|
9
|
-
* #org/repo → namespace=repo, repo=https://github.com/org/repo
|
|
2
|
+
* Routing helpers: channel-creation intent detection and meta-agent routing.
|
|
10
3
|
*/
|
|
11
4
|
import { execSync } from "child_process";
|
|
12
5
|
import { metaAgentStatusKey, metaKey, metaInputKey } from "@gonzih/cc-wire";
|
|
13
|
-
/**
|
|
14
|
-
* Parse the first #tag or #org/repo token from a message.
|
|
15
|
-
* Returns null when no routing tag is present, or when the short #repo format is used
|
|
16
|
-
* without DEFAULT_GITHUB_ORG being set (operators must configure this explicitly).
|
|
17
|
-
*
|
|
18
|
-
* Examples:
|
|
19
|
-
* "#cc-agent fix the bug" → null if DEFAULT_GITHUB_ORG unset; { namespace: "cc-agent", repoUrl: "…/{org}/cc-agent", … } if set
|
|
20
|
-
* "#gonzih/of-stack deploy it" → { namespace: "of-stack", repoUrl: "…/gonzih/of-stack", … }
|
|
21
|
-
* "#org/repo do something" → { namespace: "repo", repoUrl: "…/org/repo", … }
|
|
22
|
-
* "please help #of-stack with this" → null if DEFAULT_GITHUB_ORG unset
|
|
23
|
-
*/
|
|
24
|
-
export function parseRoutingTag(text) {
|
|
25
|
-
const defaultOrg = process.env.DEFAULT_GITHUB_ORG;
|
|
26
|
-
// Match #word or #org/repo — each segment: starts with alphanumeric, allows ._- inside
|
|
27
|
-
const match = text.match(/#([a-zA-Z0-9][a-zA-Z0-9._-]*)(?:\/([a-zA-Z0-9][a-zA-Z0-9._-]*))?/);
|
|
28
|
-
if (!match)
|
|
29
|
-
return null;
|
|
30
|
-
const fullMatch = match[0]; // e.g. "#gonzih/of-stack"
|
|
31
|
-
const part1 = match[1]; // org-or-repo
|
|
32
|
-
const part2 = match[2]; // repo (only present in #org/repo format)
|
|
33
|
-
let namespace;
|
|
34
|
-
let repoUrl;
|
|
35
|
-
if (part2) {
|
|
36
|
-
// #org/repo format
|
|
37
|
-
namespace = part2;
|
|
38
|
-
repoUrl = `https://github.com/${part1}/${part2}`;
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
// #repo format — requires DEFAULT_GITHUB_ORG; return null if unset
|
|
42
|
-
if (!defaultOrg)
|
|
43
|
-
return null;
|
|
44
|
-
namespace = part1;
|
|
45
|
-
repoUrl = `https://github.com/${defaultOrg}/${part1}`;
|
|
46
|
-
}
|
|
47
|
-
// Strip the matched tag token and collapse whitespace
|
|
48
|
-
const strippedMessage = text.replace(fullMatch, "").replace(/\s+/g, " ").trim();
|
|
49
|
-
return { namespace, repoUrl, strippedMessage };
|
|
50
|
-
}
|
|
51
6
|
/**
|
|
52
7
|
* Ensure a meta-agent for the given namespace is running.
|
|
53
8
|
*
|