@dreb/telegram 1.16.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 +91 -0
- package/dist/agent-bridge.d.ts +146 -0
- package/dist/agent-bridge.d.ts.map +1 -0
- package/dist/agent-bridge.js +466 -0
- package/dist/agent-bridge.js.map +1 -0
- package/dist/bot.d.ts +11 -0
- package/dist/bot.d.ts.map +1 -0
- package/dist/bot.js +112 -0
- package/dist/bot.js.map +1 -0
- package/dist/bridge-lifecycle.d.ts +17 -0
- package/dist/bridge-lifecycle.d.ts.map +1 -0
- package/dist/bridge-lifecycle.js +71 -0
- package/dist/bridge-lifecycle.js.map +1 -0
- package/dist/commands/agent.d.ts +11 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +171 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/buddy.d.ts +20 -0
- package/dist/commands/buddy.d.ts.map +1 -0
- package/dist/commands/buddy.js +84 -0
- package/dist/commands/buddy.js.map +1 -0
- package/dist/commands/core.d.ts +13 -0
- package/dist/commands/core.d.ts.map +1 -0
- package/dist/commands/core.js +107 -0
- package/dist/commands/core.js.map +1 -0
- package/dist/commands/index.d.ts +16 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +132 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/refresh.d.ts +18 -0
- package/dist/commands/refresh.d.ts.map +1 -0
- package/dist/commands/refresh.js +55 -0
- package/dist/commands/refresh.js.map +1 -0
- package/dist/commands/sessions.d.ts +10 -0
- package/dist/commands/sessions.d.ts.map +1 -0
- package/dist/commands/sessions.js +125 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/skills.d.ts +10 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +48 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +77 -0
- package/dist/config.js.map +1 -0
- package/dist/handlers/buddy.d.ts +31 -0
- package/dist/handlers/buddy.d.ts.map +1 -0
- package/dist/handlers/buddy.js +126 -0
- package/dist/handlers/buddy.js.map +1 -0
- package/dist/handlers/events.d.ts +65 -0
- package/dist/handlers/events.d.ts.map +1 -0
- package/dist/handlers/events.js +381 -0
- package/dist/handlers/events.js.map +1 -0
- package/dist/handlers/file.d.ts +11 -0
- package/dist/handlers/file.d.ts.map +1 -0
- package/dist/handlers/file.js +138 -0
- package/dist/handlers/file.js.map +1 -0
- package/dist/handlers/message.d.ts +34 -0
- package/dist/handlers/message.d.ts.map +1 -0
- package/dist/handlers/message.js +262 -0
- package/dist/handlers/message.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -0
- package/dist/state.d.ts +11 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +47 -0
- package/dist/state.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/util/files.d.ts +27 -0
- package/dist/util/files.d.ts.map +1 -0
- package/dist/util/files.js +75 -0
- package/dist/util/files.js.map +1 -0
- package/dist/util/telegram.d.ts +60 -0
- package/dist/util/telegram.d.ts.map +1 -0
- package/dist/util/telegram.js +192 -0
- package/dist/util/telegram.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge lifecycle helpers — extracted to avoid circular imports between bot.ts and commands.
|
|
3
|
+
*/
|
|
4
|
+
import { AgentBridge } from "./agent-bridge.js";
|
|
5
|
+
import type { Config } from "./config.js";
|
|
6
|
+
import type { UserState } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Ensure the user has an active agent bridge, starting one if needed.
|
|
9
|
+
* Does NOT handle session selection — that's up to the caller.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ensureBridge(config: Config, userState: UserState): Promise<AgentBridge>;
|
|
12
|
+
/**
|
|
13
|
+
* Ensure bridge is alive AND a session is selected.
|
|
14
|
+
* Used by message/file handlers and skill commands before prompting.
|
|
15
|
+
*/
|
|
16
|
+
export declare function ensureBridgeWithSession(config: Config, userState: UserState): Promise<AgentBridge>;
|
|
17
|
+
//# sourceMappingURL=bridge-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-lifecycle.d.ts","sourceRoot":"","sources":["../src/bridge-lifecycle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CA2B7F;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAqCxG","sourcesContent":["/**\n * Bridge lifecycle helpers — extracted to avoid circular imports between bot.ts and commands.\n */\n\nimport { AgentBridge } from \"./agent-bridge.js\";\nimport type { Config } from \"./config.js\";\nimport type { UserState } from \"./types.js\";\n\n/**\n * Ensure the user has an active agent bridge, starting one if needed.\n * Does NOT handle session selection — that's up to the caller.\n */\nexport async function ensureBridge(config: Config, userState: UserState): Promise<AgentBridge> {\n\tif (!userState.bridge || !userState.bridge.isAlive) {\n\t\t// Use effectiveCwd if set — preserves custom cwd across bridge crashes\n\t\tconst effectiveConfig =\n\t\t\tuserState.effectiveCwd && userState.effectiveCwd !== config.workingDir\n\t\t\t\t? { ...config, workingDir: userState.effectiveCwd }\n\t\t\t\t: config;\n\t\tconst bridge = new AgentBridge(effectiveConfig);\n\t\tawait bridge.start();\n\t\tuserState.bridge = bridge;\n\n\t\t// Wire up background agent tracking\n\t\tbridge.onEvent((event: any) => {\n\t\t\tif (event.type === \"background_agent_start\") {\n\t\t\t\tuserState.backgroundAgents.set(event.agentId, {\n\t\t\t\t\tagentId: event.agentId,\n\t\t\t\t\tagentType: event.agentType,\n\t\t\t\t\ttaskSummary: event.taskSummary,\n\t\t\t\t\tstartTime: Date.now(),\n\t\t\t\t});\n\t\t\t} else if (event.type === \"background_agent_end\") {\n\t\t\t\tuserState.backgroundAgents.delete(event.agentId);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn userState.bridge;\n}\n\n/**\n * Ensure bridge is alive AND a session is selected.\n * Used by message/file handlers and skill commands before prompting.\n */\nexport async function ensureBridgeWithSession(config: Config, userState: UserState): Promise<AgentBridge> {\n\t// Handle new session — always kill and recreate the bridge for clean state.\n\t// For /new <path>: uses the user-specified directory.\n\t// For /new (bare): preserves the current effectiveCwd.\n\tif (userState.newSessionFlag) {\n\t\tconst cwd = userState.newSessionCwd ?? userState.effectiveCwd ?? config.workingDir;\n\t\tuserState.newSessionFlag = false;\n\t\tuserState.newSessionCwd = null;\n\n\t\t// Kill existing bridge and start a new one with the resolved cwd\n\t\tif (userState.bridge?.isAlive) {\n\t\t\tawait userState.bridge.stop();\n\t\t}\n\t\tuserState.bridge = null;\n\n\t\tconst customConfig = { ...config, workingDir: cwd };\n\t\t// Set effectiveCwd BEFORE ensureBridge so the stale-cwd override\n\t\t// in ensureBridge doesn't clobber the resolved directory\n\t\tuserState.effectiveCwd = cwd;\n\t\tconst bridge = await ensureBridge(customConfig, userState);\n\t\tawait bridge.newSession();\n\t\treturn bridge;\n\t}\n\n\tconst bridge = await ensureBridge(config, userState);\n\n\t// Track effective cwd (default from config on first bridge creation)\n\tif (!userState.effectiveCwd) {\n\t\tuserState.effectiveCwd = config.workingDir;\n\t}\n\n\t// No session yet — try to resume latest\n\tif (!bridge.sessionId) {\n\t\tawait bridge.resumeLatest();\n\t}\n\n\treturn bridge;\n}\n"]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge lifecycle helpers — extracted to avoid circular imports between bot.ts and commands.
|
|
3
|
+
*/
|
|
4
|
+
import { AgentBridge } from "./agent-bridge.js";
|
|
5
|
+
/**
|
|
6
|
+
* Ensure the user has an active agent bridge, starting one if needed.
|
|
7
|
+
* Does NOT handle session selection — that's up to the caller.
|
|
8
|
+
*/
|
|
9
|
+
export async function ensureBridge(config, userState) {
|
|
10
|
+
if (!userState.bridge || !userState.bridge.isAlive) {
|
|
11
|
+
// Use effectiveCwd if set — preserves custom cwd across bridge crashes
|
|
12
|
+
const effectiveConfig = userState.effectiveCwd && userState.effectiveCwd !== config.workingDir
|
|
13
|
+
? { ...config, workingDir: userState.effectiveCwd }
|
|
14
|
+
: config;
|
|
15
|
+
const bridge = new AgentBridge(effectiveConfig);
|
|
16
|
+
await bridge.start();
|
|
17
|
+
userState.bridge = bridge;
|
|
18
|
+
// Wire up background agent tracking
|
|
19
|
+
bridge.onEvent((event) => {
|
|
20
|
+
if (event.type === "background_agent_start") {
|
|
21
|
+
userState.backgroundAgents.set(event.agentId, {
|
|
22
|
+
agentId: event.agentId,
|
|
23
|
+
agentType: event.agentType,
|
|
24
|
+
taskSummary: event.taskSummary,
|
|
25
|
+
startTime: Date.now(),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
else if (event.type === "background_agent_end") {
|
|
29
|
+
userState.backgroundAgents.delete(event.agentId);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return userState.bridge;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Ensure bridge is alive AND a session is selected.
|
|
37
|
+
* Used by message/file handlers and skill commands before prompting.
|
|
38
|
+
*/
|
|
39
|
+
export async function ensureBridgeWithSession(config, userState) {
|
|
40
|
+
// Handle new session — always kill and recreate the bridge for clean state.
|
|
41
|
+
// For /new <path>: uses the user-specified directory.
|
|
42
|
+
// For /new (bare): preserves the current effectiveCwd.
|
|
43
|
+
if (userState.newSessionFlag) {
|
|
44
|
+
const cwd = userState.newSessionCwd ?? userState.effectiveCwd ?? config.workingDir;
|
|
45
|
+
userState.newSessionFlag = false;
|
|
46
|
+
userState.newSessionCwd = null;
|
|
47
|
+
// Kill existing bridge and start a new one with the resolved cwd
|
|
48
|
+
if (userState.bridge?.isAlive) {
|
|
49
|
+
await userState.bridge.stop();
|
|
50
|
+
}
|
|
51
|
+
userState.bridge = null;
|
|
52
|
+
const customConfig = { ...config, workingDir: cwd };
|
|
53
|
+
// Set effectiveCwd BEFORE ensureBridge so the stale-cwd override
|
|
54
|
+
// in ensureBridge doesn't clobber the resolved directory
|
|
55
|
+
userState.effectiveCwd = cwd;
|
|
56
|
+
const bridge = await ensureBridge(customConfig, userState);
|
|
57
|
+
await bridge.newSession();
|
|
58
|
+
return bridge;
|
|
59
|
+
}
|
|
60
|
+
const bridge = await ensureBridge(config, userState);
|
|
61
|
+
// Track effective cwd (default from config on first bridge creation)
|
|
62
|
+
if (!userState.effectiveCwd) {
|
|
63
|
+
userState.effectiveCwd = config.workingDir;
|
|
64
|
+
}
|
|
65
|
+
// No session yet — try to resume latest
|
|
66
|
+
if (!bridge.sessionId) {
|
|
67
|
+
await bridge.resumeLatest();
|
|
68
|
+
}
|
|
69
|
+
return bridge;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=bridge-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-lifecycle.js","sourceRoot":"","sources":["../src/bridge-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bridge lifecycle helpers — extracted to avoid circular imports between bot.ts and commands.\n */\n\nimport { AgentBridge } from \"./agent-bridge.js\";\nimport type { Config } from \"./config.js\";\nimport type { UserState } from \"./types.js\";\n\n/**\n * Ensure the user has an active agent bridge, starting one if needed.\n * Does NOT handle session selection — that's up to the caller.\n */\nexport async function ensureBridge(config: Config, userState: UserState): Promise<AgentBridge> {\n\tif (!userState.bridge || !userState.bridge.isAlive) {\n\t\t// Use effectiveCwd if set — preserves custom cwd across bridge crashes\n\t\tconst effectiveConfig =\n\t\t\tuserState.effectiveCwd && userState.effectiveCwd !== config.workingDir\n\t\t\t\t? { ...config, workingDir: userState.effectiveCwd }\n\t\t\t\t: config;\n\t\tconst bridge = new AgentBridge(effectiveConfig);\n\t\tawait bridge.start();\n\t\tuserState.bridge = bridge;\n\n\t\t// Wire up background agent tracking\n\t\tbridge.onEvent((event: any) => {\n\t\t\tif (event.type === \"background_agent_start\") {\n\t\t\t\tuserState.backgroundAgents.set(event.agentId, {\n\t\t\t\t\tagentId: event.agentId,\n\t\t\t\t\tagentType: event.agentType,\n\t\t\t\t\ttaskSummary: event.taskSummary,\n\t\t\t\t\tstartTime: Date.now(),\n\t\t\t\t});\n\t\t\t} else if (event.type === \"background_agent_end\") {\n\t\t\t\tuserState.backgroundAgents.delete(event.agentId);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn userState.bridge;\n}\n\n/**\n * Ensure bridge is alive AND a session is selected.\n * Used by message/file handlers and skill commands before prompting.\n */\nexport async function ensureBridgeWithSession(config: Config, userState: UserState): Promise<AgentBridge> {\n\t// Handle new session — always kill and recreate the bridge for clean state.\n\t// For /new <path>: uses the user-specified directory.\n\t// For /new (bare): preserves the current effectiveCwd.\n\tif (userState.newSessionFlag) {\n\t\tconst cwd = userState.newSessionCwd ?? userState.effectiveCwd ?? config.workingDir;\n\t\tuserState.newSessionFlag = false;\n\t\tuserState.newSessionCwd = null;\n\n\t\t// Kill existing bridge and start a new one with the resolved cwd\n\t\tif (userState.bridge?.isAlive) {\n\t\t\tawait userState.bridge.stop();\n\t\t}\n\t\tuserState.bridge = null;\n\n\t\tconst customConfig = { ...config, workingDir: cwd };\n\t\t// Set effectiveCwd BEFORE ensureBridge so the stale-cwd override\n\t\t// in ensureBridge doesn't clobber the resolved directory\n\t\tuserState.effectiveCwd = cwd;\n\t\tconst bridge = await ensureBridge(customConfig, userState);\n\t\tawait bridge.newSession();\n\t\treturn bridge;\n\t}\n\n\tconst bridge = await ensureBridge(config, userState);\n\n\t// Track effective cwd (default from config on first bridge creation)\n\tif (!userState.effectiveCwd) {\n\t\tuserState.effectiveCwd = config.workingDir;\n\t}\n\n\t// No session yet — try to resume latest\n\tif (!bridge.sessionId) {\n\t\tawait bridge.resumeLatest();\n\t}\n\n\treturn bridge;\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent slash commands: /compact, /agents, /stats, /model, /thinking
|
|
3
|
+
*/
|
|
4
|
+
import type { Context } from "grammy";
|
|
5
|
+
import type { UserState } from "../types.js";
|
|
6
|
+
export declare function cmdCompact(ctx: Context, userState: UserState): Promise<void>;
|
|
7
|
+
export declare function cmdAgents(ctx: Context, userState: UserState): Promise<void>;
|
|
8
|
+
export declare function cmdStats(ctx: Context, userState: UserState): Promise<void>;
|
|
9
|
+
export declare function cmdModel(ctx: Context, userState: UserState, args: string): Promise<void>;
|
|
10
|
+
export declare function cmdThinking(ctx: Context, userState: UserState, args: string): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBlF;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAcjF;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ChF;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgE9F;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BjG","sourcesContent":["/**\n * Agent slash commands: /compact, /agents, /stats, /model, /thinking\n */\n\nimport type { Context } from \"grammy\";\nimport type { UserState } from \"../types.js\";\nimport { log, safeSend } from \"../util/telegram.js\";\n\nexport async function cmdCompact(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\tawait safeSend(ctx.api, chatId, \"🗜 _Compacting context..._\");\n\ttry {\n\t\tconst result = await bridge.compact();\n\t\tif (result) {\n\t\t\tconst before = (result as any).tokensBefore || 0;\n\t\t\tawait safeSend(ctx.api, chatId, `✅ Compacted (was ${Math.round(before / 1000)}k tokens)`);\n\t\t} else {\n\t\t\tawait safeSend(ctx.api, chatId, \"✅ Compacted.\");\n\t\t}\n\t} catch (e) {\n\t\tlog(`[CMD] /compact error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ Compaction failed: ${e}`);\n\t}\n}\n\nexport async function cmdAgents(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\n\tif (userState.backgroundAgents.size === 0) {\n\t\tawait safeSend(ctx.api, chatId, \"No background agents running.\");\n\t\treturn;\n\t}\n\n\tconst lines = [\"🤖 *Background Agents*:\\n\"];\n\tfor (const agent of userState.backgroundAgents.values()) {\n\t\tconst elapsed = Math.round((Date.now() - agent.startTime) / 1000);\n\t\tlines.push(`• *${agent.agentType}* (${elapsed}s)\\n ${agent.taskSummary.slice(0, 200)}`);\n\t}\n\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n}\n\nexport async function cmdStats(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst stats = await bridge.getSessionStats();\n\t\tif (!stats) {\n\t\t\tawait safeSend(ctx.api, chatId, \"No stats available.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = [\"📊 *Session Stats*:\\n\"];\n\t\tlines.push(`Messages: ${stats.userMessages || 0} user, ${stats.assistantMessages || 0} assistant`);\n\t\tlines.push(`Tool calls: ${stats.toolCalls || 0}`);\n\n\t\tif (stats.tokens) {\n\t\t\tconst t = stats.tokens;\n\t\t\tlines.push(`\\nTokens: ${Math.round((t.total || 0) / 1000)}k total`);\n\t\t\tlines.push(` Input: ${Math.round((t.input || 0) / 1000)}k`);\n\t\t\tlines.push(` Output: ${Math.round((t.output || 0) / 1000)}k`);\n\t\t\tif (t.cacheRead) lines.push(` Cache read: ${Math.round(t.cacheRead / 1000)}k`);\n\t\t}\n\n\t\tif (stats.cost != null) {\n\t\t\tlines.push(`\\n💰 Cost: $${stats.cost.toFixed(4)}`);\n\t\t}\n\n\t\tif (stats.contextUsage) {\n\t\t\tconst cu = stats.contextUsage;\n\t\t\tif (cu.percent != null) {\n\t\t\t\tlines.push(\n\t\t\t\t\t`\\n📏 Context: ${cu.percent}% (${Math.round((cu.tokens || 0) / 1000)}k / ${Math.round((cu.contextWindow || 0) / 1000)}k)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n\t} catch (e) {\n\t\tlog(`[CMD] /stats error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ Failed to get stats: ${e}`);\n\t}\n}\n\nexport async function cmdModel(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tif (!args.trim()) {\n\t\t\t// Show current model\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.model) {\n\t\t\t\tawait safeSend(ctx.api, chatId, `🧠 Current model: \\`${state.model.provider}/${state.model.id}\\``);\n\t\t\t} else {\n\t\t\t\tawait safeSend(ctx.api, chatId, \"🧠 No model set.\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Try to match and switch\n\t\tconst pattern = args.trim().toLowerCase();\n\t\tconst models = await bridge.getAvailableModels();\n\n\t\t// Score matches: exact id > exact provider/id > substring\n\t\tconst scored = models\n\t\t\t.map((m: any) => {\n\t\t\t\tconst id = m.id.toLowerCase();\n\t\t\t\tconst full = `${m.provider}/${m.id}`.toLowerCase();\n\t\t\t\tif (id === pattern || full === pattern) return { model: m, score: 0 };\n\t\t\t\tif (id.includes(pattern) || full.includes(pattern)) return { model: m, score: 1 };\n\t\t\t\treturn { model: m, score: -1 };\n\t\t\t})\n\t\t\t.filter((s) => s.score >= 0)\n\t\t\t.sort((a, b) => a.score - b.score);\n\n\t\tif (scored.length === 0) {\n\t\t\t// Group models by provider for readable display\n\t\t\tconst byProvider = new Map<string, string[]>();\n\t\t\tfor (const m of models as any[]) {\n\t\t\t\tconst list = byProvider.get(m.provider) || [];\n\t\t\t\tlist.push(m.id);\n\t\t\t\tbyProvider.set(m.provider, list);\n\t\t\t}\n\t\t\tconst lines = [`No model matching \"${pattern}\". Available:`];\n\t\t\tfor (const [provider, ids] of byProvider) {\n\t\t\t\tlines.push(`\\n*${provider}*:`);\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tlines.push(` \\`${id}\\``);\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\").slice(0, 4000));\n\t\t\treturn;\n\t\t}\n\n\t\t// If multiple matches, prefer exact over substring\n\t\tconst match = scored[0].model;\n\t\tawait bridge.setModel((match as any).provider, (match as any).id);\n\t\tawait safeSend(ctx.api, chatId, `🧠 Switched to \\`${(match as any).provider}/${(match as any).id}\\``);\n\t} catch (e) {\n\t\tlog(`[CMD] /model error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ ${e}`);\n\t}\n}\n\nexport async function cmdThinking(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tif (!args.trim()) {\n\t\t\tconst state = await bridge.getState();\n\t\t\tawait safeSend(ctx.api, chatId, `💭 Thinking level: \\`${state?.thinkingLevel || \"unknown\"}\\``);\n\t\t\treturn;\n\t\t}\n\n\t\tconst level = args.trim().toLowerCase();\n\t\tconst valid = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\t\tif (!valid.includes(level)) {\n\t\t\tawait safeSend(ctx.api, chatId, `Invalid level. Options: ${valid.join(\", \")}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait bridge.setThinkingLevel(level);\n\t\tawait safeSend(ctx.api, chatId, `💭 Thinking level set to \\`${level}\\``);\n\t} catch (e) {\n\t\tlog(`[CMD] /thinking error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ ${e}`);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent slash commands: /compact, /agents, /stats, /model, /thinking
|
|
3
|
+
*/
|
|
4
|
+
import { log, safeSend } from "../util/telegram.js";
|
|
5
|
+
export async function cmdCompact(ctx, userState) {
|
|
6
|
+
const chatId = ctx.chat.id;
|
|
7
|
+
const bridge = userState.bridge;
|
|
8
|
+
if (!bridge?.isAlive) {
|
|
9
|
+
await safeSend(ctx.api, chatId, "No active session.");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
await safeSend(ctx.api, chatId, "🗜 _Compacting context..._");
|
|
13
|
+
try {
|
|
14
|
+
const result = await bridge.compact();
|
|
15
|
+
if (result) {
|
|
16
|
+
const before = result.tokensBefore || 0;
|
|
17
|
+
await safeSend(ctx.api, chatId, `✅ Compacted (was ${Math.round(before / 1000)}k tokens)`);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await safeSend(ctx.api, chatId, "✅ Compacted.");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
log(`[CMD] /compact error: ${e}`);
|
|
25
|
+
await safeSend(ctx.api, chatId, `❌ Compaction failed: ${e}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function cmdAgents(ctx, userState) {
|
|
29
|
+
const chatId = ctx.chat.id;
|
|
30
|
+
if (userState.backgroundAgents.size === 0) {
|
|
31
|
+
await safeSend(ctx.api, chatId, "No background agents running.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const lines = ["🤖 *Background Agents*:\n"];
|
|
35
|
+
for (const agent of userState.backgroundAgents.values()) {
|
|
36
|
+
const elapsed = Math.round((Date.now() - agent.startTime) / 1000);
|
|
37
|
+
lines.push(`• *${agent.agentType}* (${elapsed}s)\n ${agent.taskSummary.slice(0, 200)}`);
|
|
38
|
+
}
|
|
39
|
+
await safeSend(ctx.api, chatId, lines.join("\n"));
|
|
40
|
+
}
|
|
41
|
+
export async function cmdStats(ctx, userState) {
|
|
42
|
+
const chatId = ctx.chat.id;
|
|
43
|
+
const bridge = userState.bridge;
|
|
44
|
+
if (!bridge?.isAlive) {
|
|
45
|
+
await safeSend(ctx.api, chatId, "No active session.");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const stats = await bridge.getSessionStats();
|
|
50
|
+
if (!stats) {
|
|
51
|
+
await safeSend(ctx.api, chatId, "No stats available.");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const lines = ["📊 *Session Stats*:\n"];
|
|
55
|
+
lines.push(`Messages: ${stats.userMessages || 0} user, ${stats.assistantMessages || 0} assistant`);
|
|
56
|
+
lines.push(`Tool calls: ${stats.toolCalls || 0}`);
|
|
57
|
+
if (stats.tokens) {
|
|
58
|
+
const t = stats.tokens;
|
|
59
|
+
lines.push(`\nTokens: ${Math.round((t.total || 0) / 1000)}k total`);
|
|
60
|
+
lines.push(` Input: ${Math.round((t.input || 0) / 1000)}k`);
|
|
61
|
+
lines.push(` Output: ${Math.round((t.output || 0) / 1000)}k`);
|
|
62
|
+
if (t.cacheRead)
|
|
63
|
+
lines.push(` Cache read: ${Math.round(t.cacheRead / 1000)}k`);
|
|
64
|
+
}
|
|
65
|
+
if (stats.cost != null) {
|
|
66
|
+
lines.push(`\n💰 Cost: $${stats.cost.toFixed(4)}`);
|
|
67
|
+
}
|
|
68
|
+
if (stats.contextUsage) {
|
|
69
|
+
const cu = stats.contextUsage;
|
|
70
|
+
if (cu.percent != null) {
|
|
71
|
+
lines.push(`\n📏 Context: ${cu.percent}% (${Math.round((cu.tokens || 0) / 1000)}k / ${Math.round((cu.contextWindow || 0) / 1000)}k)`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
await safeSend(ctx.api, chatId, lines.join("\n"));
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
log(`[CMD] /stats error: ${e}`);
|
|
78
|
+
await safeSend(ctx.api, chatId, `❌ Failed to get stats: ${e}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export async function cmdModel(ctx, userState, args) {
|
|
82
|
+
const chatId = ctx.chat.id;
|
|
83
|
+
const bridge = userState.bridge;
|
|
84
|
+
if (!bridge?.isAlive) {
|
|
85
|
+
await safeSend(ctx.api, chatId, "No active session.");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
if (!args.trim()) {
|
|
90
|
+
// Show current model
|
|
91
|
+
const state = await bridge.getState();
|
|
92
|
+
if (state?.model) {
|
|
93
|
+
await safeSend(ctx.api, chatId, `🧠 Current model: \`${state.model.provider}/${state.model.id}\``);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
await safeSend(ctx.api, chatId, "🧠 No model set.");
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Try to match and switch
|
|
101
|
+
const pattern = args.trim().toLowerCase();
|
|
102
|
+
const models = await bridge.getAvailableModels();
|
|
103
|
+
// Score matches: exact id > exact provider/id > substring
|
|
104
|
+
const scored = models
|
|
105
|
+
.map((m) => {
|
|
106
|
+
const id = m.id.toLowerCase();
|
|
107
|
+
const full = `${m.provider}/${m.id}`.toLowerCase();
|
|
108
|
+
if (id === pattern || full === pattern)
|
|
109
|
+
return { model: m, score: 0 };
|
|
110
|
+
if (id.includes(pattern) || full.includes(pattern))
|
|
111
|
+
return { model: m, score: 1 };
|
|
112
|
+
return { model: m, score: -1 };
|
|
113
|
+
})
|
|
114
|
+
.filter((s) => s.score >= 0)
|
|
115
|
+
.sort((a, b) => a.score - b.score);
|
|
116
|
+
if (scored.length === 0) {
|
|
117
|
+
// Group models by provider for readable display
|
|
118
|
+
const byProvider = new Map();
|
|
119
|
+
for (const m of models) {
|
|
120
|
+
const list = byProvider.get(m.provider) || [];
|
|
121
|
+
list.push(m.id);
|
|
122
|
+
byProvider.set(m.provider, list);
|
|
123
|
+
}
|
|
124
|
+
const lines = [`No model matching "${pattern}". Available:`];
|
|
125
|
+
for (const [provider, ids] of byProvider) {
|
|
126
|
+
lines.push(`\n*${provider}*:`);
|
|
127
|
+
for (const id of ids) {
|
|
128
|
+
lines.push(` \`${id}\``);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
await safeSend(ctx.api, chatId, lines.join("\n").slice(0, 4000));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// If multiple matches, prefer exact over substring
|
|
135
|
+
const match = scored[0].model;
|
|
136
|
+
await bridge.setModel(match.provider, match.id);
|
|
137
|
+
await safeSend(ctx.api, chatId, `🧠 Switched to \`${match.provider}/${match.id}\``);
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
log(`[CMD] /model error: ${e}`);
|
|
141
|
+
await safeSend(ctx.api, chatId, `❌ ${e}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export async function cmdThinking(ctx, userState, args) {
|
|
145
|
+
const chatId = ctx.chat.id;
|
|
146
|
+
const bridge = userState.bridge;
|
|
147
|
+
if (!bridge?.isAlive) {
|
|
148
|
+
await safeSend(ctx.api, chatId, "No active session.");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
if (!args.trim()) {
|
|
153
|
+
const state = await bridge.getState();
|
|
154
|
+
await safeSend(ctx.api, chatId, `💭 Thinking level: \`${state?.thinkingLevel || "unknown"}\``);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const level = args.trim().toLowerCase();
|
|
158
|
+
const valid = ["off", "minimal", "low", "medium", "high"];
|
|
159
|
+
if (!valid.includes(level)) {
|
|
160
|
+
await safeSend(ctx.api, chatId, `Invalid level. Options: ${valid.join(", ")}`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
await bridge.setThinkingLevel(level);
|
|
164
|
+
await safeSend(ctx.api, chatId, `💭 Thinking level set to \`${level}\``);
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
log(`[CMD] /thinking error: ${e}`);
|
|
168
|
+
await safeSend(ctx.api, chatId, `❌ ${e}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent slash commands: /compact, /agents, /stats, /model, /thinking\n */\n\nimport type { Context } from \"grammy\";\nimport type { UserState } from \"../types.js\";\nimport { log, safeSend } from \"../util/telegram.js\";\n\nexport async function cmdCompact(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\tawait safeSend(ctx.api, chatId, \"🗜 _Compacting context..._\");\n\ttry {\n\t\tconst result = await bridge.compact();\n\t\tif (result) {\n\t\t\tconst before = (result as any).tokensBefore || 0;\n\t\t\tawait safeSend(ctx.api, chatId, `✅ Compacted (was ${Math.round(before / 1000)}k tokens)`);\n\t\t} else {\n\t\t\tawait safeSend(ctx.api, chatId, \"✅ Compacted.\");\n\t\t}\n\t} catch (e) {\n\t\tlog(`[CMD] /compact error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ Compaction failed: ${e}`);\n\t}\n}\n\nexport async function cmdAgents(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\n\tif (userState.backgroundAgents.size === 0) {\n\t\tawait safeSend(ctx.api, chatId, \"No background agents running.\");\n\t\treturn;\n\t}\n\n\tconst lines = [\"🤖 *Background Agents*:\\n\"];\n\tfor (const agent of userState.backgroundAgents.values()) {\n\t\tconst elapsed = Math.round((Date.now() - agent.startTime) / 1000);\n\t\tlines.push(`• *${agent.agentType}* (${elapsed}s)\\n ${agent.taskSummary.slice(0, 200)}`);\n\t}\n\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n}\n\nexport async function cmdStats(ctx: Context, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst stats = await bridge.getSessionStats();\n\t\tif (!stats) {\n\t\t\tawait safeSend(ctx.api, chatId, \"No stats available.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = [\"📊 *Session Stats*:\\n\"];\n\t\tlines.push(`Messages: ${stats.userMessages || 0} user, ${stats.assistantMessages || 0} assistant`);\n\t\tlines.push(`Tool calls: ${stats.toolCalls || 0}`);\n\n\t\tif (stats.tokens) {\n\t\t\tconst t = stats.tokens;\n\t\t\tlines.push(`\\nTokens: ${Math.round((t.total || 0) / 1000)}k total`);\n\t\t\tlines.push(` Input: ${Math.round((t.input || 0) / 1000)}k`);\n\t\t\tlines.push(` Output: ${Math.round((t.output || 0) / 1000)}k`);\n\t\t\tif (t.cacheRead) lines.push(` Cache read: ${Math.round(t.cacheRead / 1000)}k`);\n\t\t}\n\n\t\tif (stats.cost != null) {\n\t\t\tlines.push(`\\n💰 Cost: $${stats.cost.toFixed(4)}`);\n\t\t}\n\n\t\tif (stats.contextUsage) {\n\t\t\tconst cu = stats.contextUsage;\n\t\t\tif (cu.percent != null) {\n\t\t\t\tlines.push(\n\t\t\t\t\t`\\n📏 Context: ${cu.percent}% (${Math.round((cu.tokens || 0) / 1000)}k / ${Math.round((cu.contextWindow || 0) / 1000)}k)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n\t} catch (e) {\n\t\tlog(`[CMD] /stats error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ Failed to get stats: ${e}`);\n\t}\n}\n\nexport async function cmdModel(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tif (!args.trim()) {\n\t\t\t// Show current model\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.model) {\n\t\t\t\tawait safeSend(ctx.api, chatId, `🧠 Current model: \\`${state.model.provider}/${state.model.id}\\``);\n\t\t\t} else {\n\t\t\t\tawait safeSend(ctx.api, chatId, \"🧠 No model set.\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Try to match and switch\n\t\tconst pattern = args.trim().toLowerCase();\n\t\tconst models = await bridge.getAvailableModels();\n\n\t\t// Score matches: exact id > exact provider/id > substring\n\t\tconst scored = models\n\t\t\t.map((m: any) => {\n\t\t\t\tconst id = m.id.toLowerCase();\n\t\t\t\tconst full = `${m.provider}/${m.id}`.toLowerCase();\n\t\t\t\tif (id === pattern || full === pattern) return { model: m, score: 0 };\n\t\t\t\tif (id.includes(pattern) || full.includes(pattern)) return { model: m, score: 1 };\n\t\t\t\treturn { model: m, score: -1 };\n\t\t\t})\n\t\t\t.filter((s) => s.score >= 0)\n\t\t\t.sort((a, b) => a.score - b.score);\n\n\t\tif (scored.length === 0) {\n\t\t\t// Group models by provider for readable display\n\t\t\tconst byProvider = new Map<string, string[]>();\n\t\t\tfor (const m of models as any[]) {\n\t\t\t\tconst list = byProvider.get(m.provider) || [];\n\t\t\t\tlist.push(m.id);\n\t\t\t\tbyProvider.set(m.provider, list);\n\t\t\t}\n\t\t\tconst lines = [`No model matching \"${pattern}\". Available:`];\n\t\t\tfor (const [provider, ids] of byProvider) {\n\t\t\t\tlines.push(`\\n*${provider}*:`);\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tlines.push(` \\`${id}\\``);\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\").slice(0, 4000));\n\t\t\treturn;\n\t\t}\n\n\t\t// If multiple matches, prefer exact over substring\n\t\tconst match = scored[0].model;\n\t\tawait bridge.setModel((match as any).provider, (match as any).id);\n\t\tawait safeSend(ctx.api, chatId, `🧠 Switched to \\`${(match as any).provider}/${(match as any).id}\\``);\n\t} catch (e) {\n\t\tlog(`[CMD] /model error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ ${e}`);\n\t}\n}\n\nexport async function cmdThinking(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tif (!bridge?.isAlive) {\n\t\tawait safeSend(ctx.api, chatId, \"No active session.\");\n\t\treturn;\n\t}\n\n\ttry {\n\t\tif (!args.trim()) {\n\t\t\tconst state = await bridge.getState();\n\t\t\tawait safeSend(ctx.api, chatId, `💭 Thinking level: \\`${state?.thinkingLevel || \"unknown\"}\\``);\n\t\t\treturn;\n\t\t}\n\n\t\tconst level = args.trim().toLowerCase();\n\t\tconst valid = [\"off\", \"minimal\", \"low\", \"medium\", \"high\"];\n\t\tif (!valid.includes(level)) {\n\t\t\tawait safeSend(ctx.api, chatId, `Invalid level. Options: ${valid.join(\", \")}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait bridge.setThinkingLevel(level);\n\t\tawait safeSend(ctx.api, chatId, `💭 Thinking level set to \\`${level}\\``);\n\t} catch (e) {\n\t\tlog(`[CMD] /thinking error: ${e}`);\n\t\tawait safeSend(ctx.api, chatId, `❌ ${e}`);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /buddy command — manage the buddy companion in Telegram.
|
|
3
|
+
*/
|
|
4
|
+
import type { Context } from "grammy";
|
|
5
|
+
import type { Config } from "../config.js";
|
|
6
|
+
import type { UserState } from "../types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Ensure the user has a buddy controller, creating one if needed.
|
|
9
|
+
*
|
|
10
|
+
* The controller auto-loads from the shared buddy.json on creation,
|
|
11
|
+
* so it picks up any buddy hatched in the TUI (and vice versa).
|
|
12
|
+
*
|
|
13
|
+
* @param api — grammy Api for chat actions
|
|
14
|
+
* @param userState — per-user state (controller stored here)
|
|
15
|
+
* @param chatId — Telegram chat ID (private chat = user ID)
|
|
16
|
+
* @param config — bot config (for bridge resolution in hatch/reroll)
|
|
17
|
+
*/
|
|
18
|
+
export declare function ensureBuddyController(api: any, userState: UserState, chatId: number, config: Config): void;
|
|
19
|
+
export declare function cmdBuddy(ctx: Context, config: Config, userState: UserState): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=buddy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buddy.d.ts","sourceRoot":"","sources":["../../src/commands/buddy.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAO1G;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DhG","sourcesContent":["/**\n * /buddy command — manage the buddy companion in Telegram.\n */\n\nimport type { Context } from \"grammy\";\nimport type { Config } from \"../config.js\";\nimport { createTelegramBuddyController, formatBuddyStats } from \"../handlers/buddy.js\";\nimport { enqueueSend } from \"../handlers/message.js\";\nimport type { UserState } from \"../types.js\";\nimport { safeSend } from \"../util/telegram.js\";\n\n/**\n * Ensure the user has a buddy controller, creating one if needed.\n *\n * The controller auto-loads from the shared buddy.json on creation,\n * so it picks up any buddy hatched in the TUI (and vice versa).\n *\n * @param api — grammy Api for chat actions\n * @param userState — per-user state (controller stored here)\n * @param chatId — Telegram chat ID (private chat = user ID)\n * @param config — bot config (for bridge resolution in hatch/reroll)\n */\nexport function ensureBuddyController(api: any, userState: UserState, chatId: number, config: Config): void {\n\tif (userState.buddyController) return;\n\n\tconst send = (text: string, long?: boolean) => {\n\t\tenqueueSend(api, userState, chatId, text, long);\n\t};\n\tuserState.buddyController = createTelegramBuddyController(send, api, chatId, config, userState);\n}\n\nexport async function cmdBuddy(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst args = (ctx.match as string)?.trim() ?? \"\";\n\tconst subcommand = args.split(/\\s+/)[0]?.toLowerCase() ?? \"\";\n\n\ttry {\n\t\t// Ensure buddy controller exists\n\t\tensureBuddyController(ctx.api, userState, chatId, config);\n\n\t\tconst controller = userState.buddyController;\n\t\tconst result = await controller.handleCommand(subcommand);\n\n\t\tswitch (result.type) {\n\t\t\tcase \"hatch\":\n\t\t\tcase \"reroll\": {\n\t\t\t\t// Reload manager state after RPC hatch/reroll changed buddy.json\n\t\t\t\tcontroller.manager.load();\n\t\t\t\tawait safeSend(ctx.api, chatId, formatBuddyStats(result.state));\n\t\t\t\ttry {\n\t\t\t\t\tawait ctx.api.setMessageReaction(chatId, ctx.message!.message_id, [{ type: \"emoji\", emoji: \"❤\" }]);\n\t\t\t\t} catch {\n\t\t\t\t\t/* Reactions not available in all chats */\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"show\": {\n\t\t\t\tawait safeSend(ctx.api, chatId, formatBuddyStats(result.state));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"pet\": {\n\t\t\t\ttry {\n\t\t\t\t\tawait ctx.api.setMessageReaction(chatId, ctx.message!.message_id, [{ type: \"emoji\", emoji: \"❤\" }]);\n\t\t\t\t} catch {\n\t\t\t\t\tawait safeSend(ctx.api, chatId, \"❤️\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"stats\": {\n\t\t\t\tawait safeSend(ctx.api, chatId, formatBuddyStats(result.state));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"off\": {\n\t\t\t\tawait safeSend(ctx.api, chatId, \"🐣 Buddy hidden. Use /buddy to bring them back.\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"warning\": {\n\t\t\t\tawait safeSend(ctx.api, chatId, `⚠️ ${result.message}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"error\": {\n\t\t\t\tawait safeSend(ctx.api, chatId, `❌ ${result.message}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} catch (err) {\n\t\tawait safeSend(\n\t\t\tctx.api,\n\t\t\tchatId,\n\t\t\t`❌ Failed to initialize buddy: ${err instanceof Error ? err.message : String(err)}`,\n\t\t);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /buddy command — manage the buddy companion in Telegram.
|
|
3
|
+
*/
|
|
4
|
+
import { createTelegramBuddyController, formatBuddyStats } from "../handlers/buddy.js";
|
|
5
|
+
import { enqueueSend } from "../handlers/message.js";
|
|
6
|
+
import { safeSend } from "../util/telegram.js";
|
|
7
|
+
/**
|
|
8
|
+
* Ensure the user has a buddy controller, creating one if needed.
|
|
9
|
+
*
|
|
10
|
+
* The controller auto-loads from the shared buddy.json on creation,
|
|
11
|
+
* so it picks up any buddy hatched in the TUI (and vice versa).
|
|
12
|
+
*
|
|
13
|
+
* @param api — grammy Api for chat actions
|
|
14
|
+
* @param userState — per-user state (controller stored here)
|
|
15
|
+
* @param chatId — Telegram chat ID (private chat = user ID)
|
|
16
|
+
* @param config — bot config (for bridge resolution in hatch/reroll)
|
|
17
|
+
*/
|
|
18
|
+
export function ensureBuddyController(api, userState, chatId, config) {
|
|
19
|
+
if (userState.buddyController)
|
|
20
|
+
return;
|
|
21
|
+
const send = (text, long) => {
|
|
22
|
+
enqueueSend(api, userState, chatId, text, long);
|
|
23
|
+
};
|
|
24
|
+
userState.buddyController = createTelegramBuddyController(send, api, chatId, config, userState);
|
|
25
|
+
}
|
|
26
|
+
export async function cmdBuddy(ctx, config, userState) {
|
|
27
|
+
const chatId = ctx.chat.id;
|
|
28
|
+
const args = ctx.match?.trim() ?? "";
|
|
29
|
+
const subcommand = args.split(/\s+/)[0]?.toLowerCase() ?? "";
|
|
30
|
+
try {
|
|
31
|
+
// Ensure buddy controller exists
|
|
32
|
+
ensureBuddyController(ctx.api, userState, chatId, config);
|
|
33
|
+
const controller = userState.buddyController;
|
|
34
|
+
const result = await controller.handleCommand(subcommand);
|
|
35
|
+
switch (result.type) {
|
|
36
|
+
case "hatch":
|
|
37
|
+
case "reroll": {
|
|
38
|
+
// Reload manager state after RPC hatch/reroll changed buddy.json
|
|
39
|
+
controller.manager.load();
|
|
40
|
+
await safeSend(ctx.api, chatId, formatBuddyStats(result.state));
|
|
41
|
+
try {
|
|
42
|
+
await ctx.api.setMessageReaction(chatId, ctx.message.message_id, [{ type: "emoji", emoji: "❤" }]);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
/* Reactions not available in all chats */
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case "show": {
|
|
50
|
+
await safeSend(ctx.api, chatId, formatBuddyStats(result.state));
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case "pet": {
|
|
54
|
+
try {
|
|
55
|
+
await ctx.api.setMessageReaction(chatId, ctx.message.message_id, [{ type: "emoji", emoji: "❤" }]);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
await safeSend(ctx.api, chatId, "❤️");
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case "stats": {
|
|
63
|
+
await safeSend(ctx.api, chatId, formatBuddyStats(result.state));
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
case "off": {
|
|
67
|
+
await safeSend(ctx.api, chatId, "🐣 Buddy hidden. Use /buddy to bring them back.");
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case "warning": {
|
|
71
|
+
await safeSend(ctx.api, chatId, `⚠️ ${result.message}`);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case "error": {
|
|
75
|
+
await safeSend(ctx.api, chatId, `❌ ${result.message}`);
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
await safeSend(ctx.api, chatId, `❌ Failed to initialize buddy: ${err instanceof Error ? err.message : String(err)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=buddy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buddy.js","sourceRoot":"","sources":["../../src/commands/buddy.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * /buddy command — manage the buddy companion in Telegram.\n */\n\nimport type { Context } from \"grammy\";\nimport type { Config } from \"../config.js\";\nimport { createTelegramBuddyController, formatBuddyStats } from \"../handlers/buddy.js\";\nimport { enqueueSend } from \"../handlers/message.js\";\nimport type { UserState } from \"../types.js\";\nimport { safeSend } from \"../util/telegram.js\";\n\n/**\n * Ensure the user has a buddy controller, creating one if needed.\n *\n * The controller auto-loads from the shared buddy.json on creation,\n * so it picks up any buddy hatched in the TUI (and vice versa).\n *\n * @param api — grammy Api for chat actions\n * @param userState — per-user state (controller stored here)\n * @param chatId — Telegram chat ID (private chat = user ID)\n * @param config — bot config (for bridge resolution in hatch/reroll)\n */\nexport function ensureBuddyController(api: any, userState: UserState, chatId: number, config: Config): void {\n\tif (userState.buddyController) return;\n\n\tconst send = (text: string, long?: boolean) => {\n\t\tenqueueSend(api, userState, chatId, text, long);\n\t};\n\tuserState.buddyController = createTelegramBuddyController(send, api, chatId, config, userState);\n}\n\nexport async function cmdBuddy(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst args = (ctx.match as string)?.trim() ?? \"\";\n\tconst subcommand = args.split(/\\s+/)[0]?.toLowerCase() ?? \"\";\n\n\ttry {\n\t\t// Ensure buddy controller exists\n\t\tensureBuddyController(ctx.api, userState, chatId, config);\n\n\t\tconst controller = userState.buddyController;\n\t\tconst result = await controller.handleCommand(subcommand);\n\n\t\tswitch (result.type) {\n\t\t\tcase \"hatch\":\n\t\t\tcase \"reroll\": {\n\t\t\t\t// Reload manager state after RPC hatch/reroll changed buddy.json\n\t\t\t\tcontroller.manager.load();\n\t\t\t\tawait safeSend(ctx.api, chatId, formatBuddyStats(result.state));\n\t\t\t\ttry {\n\t\t\t\t\tawait ctx.api.setMessageReaction(chatId, ctx.message!.message_id, [{ type: \"emoji\", emoji: \"❤\" }]);\n\t\t\t\t} catch {\n\t\t\t\t\t/* Reactions not available in all chats */\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"show\": {\n\t\t\t\tawait safeSend(ctx.api, chatId, formatBuddyStats(result.state));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"pet\": {\n\t\t\t\ttry {\n\t\t\t\t\tawait ctx.api.setMessageReaction(chatId, ctx.message!.message_id, [{ type: \"emoji\", emoji: \"❤\" }]);\n\t\t\t\t} catch {\n\t\t\t\t\tawait safeSend(ctx.api, chatId, \"❤️\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"stats\": {\n\t\t\t\tawait safeSend(ctx.api, chatId, formatBuddyStats(result.state));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"off\": {\n\t\t\t\tawait safeSend(ctx.api, chatId, \"🐣 Buddy hidden. Use /buddy to bring them back.\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"warning\": {\n\t\t\t\tawait safeSend(ctx.api, chatId, `⚠️ ${result.message}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"error\": {\n\t\t\t\tawait safeSend(ctx.api, chatId, `❌ ${result.message}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} catch (err) {\n\t\tawait safeSend(\n\t\t\tctx.api,\n\t\t\tchatId,\n\t\t\t`❌ Failed to initialize buddy: ${err instanceof Error ? err.message : String(err)}`,\n\t\t);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core slash commands: /start, /status, /cwd, /new, /stop, /restart
|
|
3
|
+
*/
|
|
4
|
+
import type { Api, Context } from "grammy";
|
|
5
|
+
import type { Config } from "../config.js";
|
|
6
|
+
import type { UserState } from "../types.js";
|
|
7
|
+
export declare function cmdStart(ctx: Context): Promise<void>;
|
|
8
|
+
export declare function cmdStatus(ctx: Context, config: Config, userState: UserState): Promise<void>;
|
|
9
|
+
export declare function cmdCwd(ctx: Context, config: Config, userState: UserState): Promise<void>;
|
|
10
|
+
export declare function cmdNew(ctx: Context, userState: UserState, args: string): Promise<void>;
|
|
11
|
+
export declare function cmdStop(ctx: Context, _api: Api, userState: UserState): Promise<void>;
|
|
12
|
+
export declare function cmdRestart(ctx: Context, config: Config): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/commands/core.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB1D;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BjG;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9F;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB5F;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAY1F;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5E","sourcesContent":["/**\n * Core slash commands: /start, /status, /cwd, /new, /stop, /restart\n */\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { resolve } from \"node:path\";\nimport type { Api, Context } from \"grammy\";\nimport type { Config } from \"../config.js\";\nimport type { UserState } from \"../types.js\";\nimport { log, safeSend } from \"../util/telegram.js\";\n\nexport async function cmdStart(ctx: Context): Promise<void> {\n\tawait ctx.reply(\n\t\t\"🤖 *dreb Telegram*\\n\\n\" +\n\t\t\t\"Send me a message and I'll forward it to dreb.\\n\\n\" +\n\t\t\t\"*Session:*\\n\" +\n\t\t\t\"/new \\\\[path\\\\] — Start a fresh session (optionally in a different directory)\\n\" +\n\t\t\t\"/sessions — List recent sessions\\n\" +\n\t\t\t\"/resume <id> — Resume a session\\n\" +\n\t\t\t\"/recent \\\\[N\\\\] — Resend last N messages\\n\\n\" +\n\t\t\t\"*Agent:*\\n\" +\n\t\t\t\"/status — Connection info\\n\" +\n\t\t\t\"/cwd — Working directory\\n\" +\n\t\t\t\"/stats — Token usage & cost\\n\" +\n\t\t\t\"/compact — Compact context\\n\" +\n\t\t\t\"/agents — Background subagents\\n\" +\n\t\t\t\"/model \\\\[pattern\\\\] — View/switch model\\n\" +\n\t\t\t\"/thinking \\\\[level\\\\] — View/set thinking\\n\" +\n\t\t\t\"/skills — List available skills\\n\\n\" +\n\t\t\t\"*Control:*\\n\" +\n\t\t\t\"/stop — Interrupt current task\\n\" +\n\t\t\t\"/restart — Restart the bot\",\n\t\t{ parse_mode: \"Markdown\" },\n\t);\n}\n\nexport async function cmdStatus(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst chatId = ctx.chat!.id;\n\tconst bridge = userState.bridge;\n\n\tlet version = \"unknown\";\n\tlet model = \"none\";\n\ttry {\n\t\tif (bridge?.isAlive) {\n\t\t\tversion = await bridge.getVersion();\n\t\t\tconst state = await bridge.getState();\n\t\t\tif (state?.model) model = `${state.model.provider}/${state.model.id}`;\n\t\t}\n\t} catch (e) {\n\t\tlog(`[CMD] /status error: ${e}`);\n\t}\n\n\tconst lines = [\n\t\tbridge?.isAlive ? \"✅ Connected\" : \"⚠️ Not connected (will start on next message)\",\n\t\t`📁 Working dir: \\`${config.workingDir}\\``,\n\t\t`🔧 dreb ${version}`,\n\t\t`🧠 Model: ${model}`,\n\t];\n\n\tif (bridge?.sessionId) {\n\t\tlines.push(`📂 Session: \\`${bridge.sessionId.slice(0, 8)}...\\``);\n\t}\n\n\tawait safeSend(ctx.api, chatId, lines.join(\"\\n\"));\n}\n\nexport async function cmdCwd(ctx: Context, config: Config, userState: UserState): Promise<void> {\n\tconst cwd = userState.effectiveCwd ?? config.workingDir;\n\tawait safeSend(ctx.api, ctx.chat!.id, `📁 Working directory: \\`${cwd}\\``);\n}\n\nexport async function cmdNew(ctx: Context, userState: UserState, args: string): Promise<void> {\n\tconst pathArg = args.trim();\n\n\tif (pathArg) {\n\t\t// Resolve path (expand ~ and make absolute)\n\t\tconst expanded = pathArg.startsWith(\"~\") ? pathArg.replace(\"~\", homedir()) : pathArg;\n\t\tconst resolved = resolve(expanded);\n\n\t\tif (!existsSync(resolved)) {\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Directory not found: \\`${resolved}\\``);\n\t\t\treturn;\n\t\t}\n\t\tif (!statSync(resolved).isDirectory()) {\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Not a directory: \\`${resolved}\\``);\n\t\t\treturn;\n\t\t}\n\n\t\tuserState.newSessionFlag = true;\n\t\tuserState.newSessionCwd = resolved;\n\t\tawait ctx.reply(`🆕 Next message will start a fresh session in \\`${resolved}\\``);\n\t} else {\n\t\tuserState.newSessionFlag = true;\n\t\tuserState.newSessionCwd = null;\n\t\tawait ctx.reply(\"🆕 Next message will start a fresh session.\");\n\t}\n}\n\nexport async function cmdStop(ctx: Context, _api: Api, userState: UserState): Promise<void> {\n\tuserState.stopRequested = true;\n\n\t// Abort current agent activity — like pressing Esc in the TUI.\n\t// This stops the agent, not the bridge. Session stays connected.\n\tif (userState.bridge?.isAlive) {\n\t\tawait userState.bridge.abort();\n\t}\n\n\tconst parts: string[] = [];\n\tif (userState.bridge?.isStreaming || userState.promptInFlight) parts.push(\"interrupted current task\");\n\tawait ctx.reply(parts.length > 0 ? `🛑 Stopped — ${parts.join(\", \")}.` : \"🛑 Stopped.\");\n}\n\nexport async function cmdRestart(ctx: Context, config: Config): Promise<void> {\n\tawait ctx.reply(\"🔄 Restarting...\");\n\tlog(\"[CMD] /restart — triggering systemctl restart\");\n\ttry {\n\t\texecSync(`systemctl --user restart ${config.serviceName}`, { timeout: 5000 });\n\t} catch {\n\t\t// Process will be killed by systemd restart\n\t}\n}\n"]}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core slash commands: /start, /status, /cwd, /new, /stop, /restart
|
|
3
|
+
*/
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
5
|
+
import { existsSync, statSync } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
import { log, safeSend } from "../util/telegram.js";
|
|
9
|
+
export async function cmdStart(ctx) {
|
|
10
|
+
await ctx.reply("🤖 *dreb Telegram*\n\n" +
|
|
11
|
+
"Send me a message and I'll forward it to dreb.\n\n" +
|
|
12
|
+
"*Session:*\n" +
|
|
13
|
+
"/new \\[path\\] — Start a fresh session (optionally in a different directory)\n" +
|
|
14
|
+
"/sessions — List recent sessions\n" +
|
|
15
|
+
"/resume <id> — Resume a session\n" +
|
|
16
|
+
"/recent \\[N\\] — Resend last N messages\n\n" +
|
|
17
|
+
"*Agent:*\n" +
|
|
18
|
+
"/status — Connection info\n" +
|
|
19
|
+
"/cwd — Working directory\n" +
|
|
20
|
+
"/stats — Token usage & cost\n" +
|
|
21
|
+
"/compact — Compact context\n" +
|
|
22
|
+
"/agents — Background subagents\n" +
|
|
23
|
+
"/model \\[pattern\\] — View/switch model\n" +
|
|
24
|
+
"/thinking \\[level\\] — View/set thinking\n" +
|
|
25
|
+
"/skills — List available skills\n\n" +
|
|
26
|
+
"*Control:*\n" +
|
|
27
|
+
"/stop — Interrupt current task\n" +
|
|
28
|
+
"/restart — Restart the bot", { parse_mode: "Markdown" });
|
|
29
|
+
}
|
|
30
|
+
export async function cmdStatus(ctx, config, userState) {
|
|
31
|
+
const chatId = ctx.chat.id;
|
|
32
|
+
const bridge = userState.bridge;
|
|
33
|
+
let version = "unknown";
|
|
34
|
+
let model = "none";
|
|
35
|
+
try {
|
|
36
|
+
if (bridge?.isAlive) {
|
|
37
|
+
version = await bridge.getVersion();
|
|
38
|
+
const state = await bridge.getState();
|
|
39
|
+
if (state?.model)
|
|
40
|
+
model = `${state.model.provider}/${state.model.id}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
log(`[CMD] /status error: ${e}`);
|
|
45
|
+
}
|
|
46
|
+
const lines = [
|
|
47
|
+
bridge?.isAlive ? "✅ Connected" : "⚠️ Not connected (will start on next message)",
|
|
48
|
+
`📁 Working dir: \`${config.workingDir}\``,
|
|
49
|
+
`🔧 dreb ${version}`,
|
|
50
|
+
`🧠 Model: ${model}`,
|
|
51
|
+
];
|
|
52
|
+
if (bridge?.sessionId) {
|
|
53
|
+
lines.push(`📂 Session: \`${bridge.sessionId.slice(0, 8)}...\``);
|
|
54
|
+
}
|
|
55
|
+
await safeSend(ctx.api, chatId, lines.join("\n"));
|
|
56
|
+
}
|
|
57
|
+
export async function cmdCwd(ctx, config, userState) {
|
|
58
|
+
const cwd = userState.effectiveCwd ?? config.workingDir;
|
|
59
|
+
await safeSend(ctx.api, ctx.chat.id, `📁 Working directory: \`${cwd}\``);
|
|
60
|
+
}
|
|
61
|
+
export async function cmdNew(ctx, userState, args) {
|
|
62
|
+
const pathArg = args.trim();
|
|
63
|
+
if (pathArg) {
|
|
64
|
+
// Resolve path (expand ~ and make absolute)
|
|
65
|
+
const expanded = pathArg.startsWith("~") ? pathArg.replace("~", homedir()) : pathArg;
|
|
66
|
+
const resolved = resolve(expanded);
|
|
67
|
+
if (!existsSync(resolved)) {
|
|
68
|
+
await safeSend(ctx.api, ctx.chat.id, `❌ Directory not found: \`${resolved}\``);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (!statSync(resolved).isDirectory()) {
|
|
72
|
+
await safeSend(ctx.api, ctx.chat.id, `❌ Not a directory: \`${resolved}\``);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
userState.newSessionFlag = true;
|
|
76
|
+
userState.newSessionCwd = resolved;
|
|
77
|
+
await ctx.reply(`🆕 Next message will start a fresh session in \`${resolved}\``);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
userState.newSessionFlag = true;
|
|
81
|
+
userState.newSessionCwd = null;
|
|
82
|
+
await ctx.reply("🆕 Next message will start a fresh session.");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export async function cmdStop(ctx, _api, userState) {
|
|
86
|
+
userState.stopRequested = true;
|
|
87
|
+
// Abort current agent activity — like pressing Esc in the TUI.
|
|
88
|
+
// This stops the agent, not the bridge. Session stays connected.
|
|
89
|
+
if (userState.bridge?.isAlive) {
|
|
90
|
+
await userState.bridge.abort();
|
|
91
|
+
}
|
|
92
|
+
const parts = [];
|
|
93
|
+
if (userState.bridge?.isStreaming || userState.promptInFlight)
|
|
94
|
+
parts.push("interrupted current task");
|
|
95
|
+
await ctx.reply(parts.length > 0 ? `🛑 Stopped — ${parts.join(", ")}.` : "🛑 Stopped.");
|
|
96
|
+
}
|
|
97
|
+
export async function cmdRestart(ctx, config) {
|
|
98
|
+
await ctx.reply("🔄 Restarting...");
|
|
99
|
+
log("[CMD] /restart — triggering systemctl restart");
|
|
100
|
+
try {
|
|
101
|
+
execSync(`systemctl --user restart ${config.serviceName}`, { timeout: 5000 });
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Process will be killed by systemd restart
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=core.js.map
|