@wu529778790/open-im 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.
Files changed (58) hide show
  1. package/.env.example +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +51 -0
  4. package/dist/access/access-control.d.ts +5 -0
  5. package/dist/access/access-control.js +11 -0
  6. package/dist/adapters/claude-adapter.d.ts +7 -0
  7. package/dist/adapters/claude-adapter.js +17 -0
  8. package/dist/adapters/registry.d.ts +4 -0
  9. package/dist/adapters/registry.js +11 -0
  10. package/dist/adapters/tool-adapter.interface.d.ts +35 -0
  11. package/dist/adapters/tool-adapter.interface.js +4 -0
  12. package/dist/claude/cli-runner.d.ts +28 -0
  13. package/dist/claude/cli-runner.js +166 -0
  14. package/dist/claude/stream-parser.d.ts +17 -0
  15. package/dist/claude/stream-parser.js +50 -0
  16. package/dist/claude/types.d.ts +54 -0
  17. package/dist/claude/types.js +21 -0
  18. package/dist/cli.d.ts +2 -0
  19. package/dist/cli.js +6 -0
  20. package/dist/commands/handler.d.ts +30 -0
  21. package/dist/commands/handler.js +122 -0
  22. package/dist/config.d.ts +20 -0
  23. package/dist/config.js +88 -0
  24. package/dist/constants.d.ts +7 -0
  25. package/dist/constants.js +15 -0
  26. package/dist/hook/permission-server.d.ts +4 -0
  27. package/dist/hook/permission-server.js +12 -0
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.js +76 -0
  30. package/dist/logger.d.ts +16 -0
  31. package/dist/logger.js +65 -0
  32. package/dist/queue/request-queue.d.ts +6 -0
  33. package/dist/queue/request-queue.js +43 -0
  34. package/dist/sanitize.d.ts +1 -0
  35. package/dist/sanitize.js +11 -0
  36. package/dist/session/session-manager.d.ts +26 -0
  37. package/dist/session/session-manager.js +207 -0
  38. package/dist/setup.d.ts +5 -0
  39. package/dist/setup.js +97 -0
  40. package/dist/shared/active-chats.d.ts +4 -0
  41. package/dist/shared/active-chats.js +55 -0
  42. package/dist/shared/ai-task.d.ts +42 -0
  43. package/dist/shared/ai-task.js +167 -0
  44. package/dist/shared/message-dedup.d.ts +4 -0
  45. package/dist/shared/message-dedup.js +25 -0
  46. package/dist/shared/task-cleanup.d.ts +2 -0
  47. package/dist/shared/task-cleanup.js +19 -0
  48. package/dist/shared/types.d.ts +9 -0
  49. package/dist/shared/types.js +1 -0
  50. package/dist/shared/utils.d.ts +7 -0
  51. package/dist/shared/utils.js +72 -0
  52. package/dist/telegram/client.d.ts +6 -0
  53. package/dist/telegram/client.js +27 -0
  54. package/dist/telegram/event-handler.d.ts +8 -0
  55. package/dist/telegram/event-handler.js +174 -0
  56. package/dist/telegram/message-sender.d.ts +7 -0
  57. package/dist/telegram/message-sender.js +102 -0
  58. package/package.json +52 -0
package/.env.example ADDED
@@ -0,0 +1,16 @@
1
+ # Telegram Bot Token (from @BotFather)
2
+ TELEGRAM_BOT_TOKEN=
3
+
4
+ # Allowed user IDs (comma-separated, empty = allow all in dev)
5
+ ALLOWED_USER_IDS=
6
+
7
+ # AI tool: claude | codex | cursor
8
+ AI_COMMAND=claude
9
+
10
+ # Claude CLI (when AI_COMMAND=claude)
11
+ CLAUDE_CLI_PATH=claude
12
+ CLAUDE_WORK_DIR=.
13
+ CLAUDE_SKIP_PERMISSIONS=true
14
+ CLAUDE_TIMEOUT_MS=600000
15
+ CLAUDE_MODEL=
16
+ ALLOWED_BASE_DIRS=
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wu529778790
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # open-im
2
+
3
+ 多平台 IM 桥接,支持多种 AI CLI 工具(Claude Code、Codex、Cursor 等)。
4
+
5
+ ## 功能
6
+
7
+ - **多 AI 工具**:通过 `AI_COMMAND` 配置切换 Claude / Codex / Cursor
8
+ - **流式输出**:节流更新,Telegram editMessage 实时展示
9
+ - **会话管理**:每用户独立 session,`/new` 重置
10
+ - **命令**:`/help` `/new` `/cd` `/pwd` `/status`
11
+
12
+ ## 快速开始
13
+
14
+ ### 快速开始
15
+
16
+ ```bash
17
+ npm run build
18
+ npm run dev # 或 npm start
19
+ ```
20
+
21
+ 首次运行会进入交互式配置,按提示输入后自动启动服务。配置保存到 `~/.open-im/config.json`。
22
+
23
+ ## 配置
24
+
25
+ | 变量 | 说明 |
26
+ |------|------|
27
+ | `TELEGRAM_BOT_TOKEN` | Telegram Bot Token |
28
+ | `ALLOWED_USER_IDS` | 白名单用户 ID(逗号分隔,空=所有人) |
29
+ | `AI_COMMAND` | `claude` \| `codex` \| `cursor`,默认 `claude` |
30
+ | `CLAUDE_CLI_PATH` | Claude CLI 路径,默认 `claude` |
31
+ | `CLAUDE_WORK_DIR` | 工作目录 |
32
+ | `CLAUDE_SKIP_PERMISSIONS` | 跳过权限确认,默认 `true` |
33
+
34
+ ## 项目结构
35
+
36
+ ```
37
+ src/
38
+ ├── adapters/ # ToolAdapter 抽象与实现
39
+ │ ├── tool-adapter.interface.ts
40
+ │ ├── claude-adapter.ts
41
+ │ └── registry.ts
42
+ ├── claude/ # Claude CLI 运行与解析
43
+ ├── shared/ # ai-task、utils、types
44
+ ├── telegram/ # Telegram 事件与消息
45
+ ├── session/ # 会话管理
46
+ └── commands/ # 命令分发
47
+ ```
48
+
49
+ ## License
50
+
51
+ MIT
@@ -0,0 +1,5 @@
1
+ export declare class AccessControl {
2
+ private allowedUserIds;
3
+ constructor(allowedUserIds: string[]);
4
+ isAllowed(userId: string): boolean;
5
+ }
@@ -0,0 +1,11 @@
1
+ export class AccessControl {
2
+ allowedUserIds;
3
+ constructor(allowedUserIds) {
4
+ this.allowedUserIds = new Set(allowedUserIds);
5
+ }
6
+ isAllowed(userId) {
7
+ if (this.allowedUserIds.size === 0)
8
+ return true;
9
+ return this.allowedUserIds.has(userId);
10
+ }
11
+ }
@@ -0,0 +1,7 @@
1
+ import type { ToolAdapter, RunCallbacks, RunOptions, RunHandle } from './tool-adapter.interface.js';
2
+ export declare class ClaudeAdapter implements ToolAdapter {
3
+ private cliPath;
4
+ readonly toolId = "claude";
5
+ constructor(cliPath: string);
6
+ run(prompt: string, sessionId: string | undefined, workDir: string, callbacks: RunCallbacks, options?: RunOptions): RunHandle;
7
+ }
@@ -0,0 +1,17 @@
1
+ import { runClaude } from '../claude/cli-runner.js';
2
+ export class ClaudeAdapter {
3
+ cliPath;
4
+ toolId = 'claude';
5
+ constructor(cliPath) {
6
+ this.cliPath = cliPath;
7
+ }
8
+ run(prompt, sessionId, workDir, callbacks, options) {
9
+ return runClaude(this.cliPath, prompt, sessionId, workDir, callbacks, {
10
+ skipPermissions: options?.skipPermissions,
11
+ timeoutMs: options?.timeoutMs,
12
+ model: options?.model,
13
+ chatId: options?.chatId,
14
+ hookPort: options?.hookPort,
15
+ });
16
+ }
17
+ }
@@ -0,0 +1,4 @@
1
+ import type { Config } from '../config.js';
2
+ import type { ToolAdapter } from './tool-adapter.interface.js';
3
+ export declare function initAdapters(config: Config): void;
4
+ export declare function getAdapter(aiCommand: string): ToolAdapter | undefined;
@@ -0,0 +1,11 @@
1
+ import { ClaudeAdapter } from './claude-adapter.js';
2
+ const adapters = new Map();
3
+ export function initAdapters(config) {
4
+ adapters.clear();
5
+ if (config.aiCommand === 'claude') {
6
+ adapters.set('claude', new ClaudeAdapter(config.claudeCliPath));
7
+ }
8
+ }
9
+ export function getAdapter(aiCommand) {
10
+ return adapters.get(aiCommand);
11
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * ToolAdapter 接口 - 多 AI CLI 统一抽象
3
+ */
4
+ export interface ParsedResult {
5
+ success: boolean;
6
+ result: string;
7
+ accumulated: string;
8
+ cost: number;
9
+ durationMs: number;
10
+ model?: string;
11
+ numTurns: number;
12
+ toolStats: Record<string, number>;
13
+ }
14
+ export interface RunCallbacks {
15
+ onText: (accumulated: string) => void;
16
+ onThinking?: (accumulated: string) => void;
17
+ onToolUse?: (toolName: string, toolInput?: Record<string, unknown>) => void;
18
+ onComplete: (result: ParsedResult) => void;
19
+ onError: (error: string) => void;
20
+ onSessionId?: (sessionId: string) => void;
21
+ }
22
+ export interface RunOptions {
23
+ skipPermissions?: boolean;
24
+ timeoutMs?: number;
25
+ model?: string;
26
+ chatId?: string;
27
+ hookPort?: number;
28
+ }
29
+ export interface RunHandle {
30
+ abort: () => void;
31
+ }
32
+ export interface ToolAdapter {
33
+ readonly toolId: string;
34
+ run(prompt: string, sessionId: string | undefined, workDir: string, callbacks: RunCallbacks, options?: RunOptions): RunHandle;
35
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * ToolAdapter 接口 - 多 AI CLI 统一抽象
3
+ */
4
+ export {};
@@ -0,0 +1,28 @@
1
+ export interface ClaudeRunCallbacks {
2
+ onText: (accumulated: string) => void;
3
+ onThinking?: (accumulated: string) => void;
4
+ onToolUse?: (toolName: string, toolInput?: Record<string, unknown>) => void;
5
+ onComplete: (result: {
6
+ success: boolean;
7
+ result: string;
8
+ accumulated: string;
9
+ cost: number;
10
+ durationMs: number;
11
+ model?: string;
12
+ numTurns: number;
13
+ toolStats: Record<string, number>;
14
+ }) => void;
15
+ onError: (error: string) => void;
16
+ onSessionId?: (sessionId: string) => void;
17
+ }
18
+ export interface ClaudeRunOptions {
19
+ skipPermissions?: boolean;
20
+ timeoutMs?: number;
21
+ model?: string;
22
+ chatId?: string;
23
+ hookPort?: number;
24
+ }
25
+ export interface ClaudeRunHandle {
26
+ abort: () => void;
27
+ }
28
+ export declare function runClaude(cliPath: string, prompt: string, sessionId: string | undefined, workDir: string, callbacks: ClaudeRunCallbacks, options?: ClaudeRunOptions): ClaudeRunHandle;
@@ -0,0 +1,166 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { createInterface } from 'node:readline';
3
+ import { parseStreamLine, extractTextDelta, extractThinkingDelta, extractResult } from './stream-parser.js';
4
+ import { isStreamInit, isContentBlockStart, isContentBlockDelta, isContentBlockStop } from './types.js';
5
+ import { createLogger } from '../logger.js';
6
+ const log = createLogger('CliRunner');
7
+ export function runClaude(cliPath, prompt, sessionId, workDir, callbacks, options) {
8
+ const args = ['-p', '--output-format', 'stream-json', '--verbose', '--include-partial-messages'];
9
+ if (options?.skipPermissions)
10
+ args.push('--dangerously-skip-permissions');
11
+ if (options?.model)
12
+ args.push('--model', options.model);
13
+ if (sessionId)
14
+ args.push('--resume', sessionId);
15
+ args.push('--', prompt);
16
+ const env = {};
17
+ for (const [k, v] of Object.entries(process.env)) {
18
+ if (v !== undefined)
19
+ env[k] = v;
20
+ }
21
+ if (options?.chatId)
22
+ env.CC_IM_CHAT_ID = options.chatId;
23
+ if (options?.hookPort)
24
+ env.CC_IM_HOOK_PORT = String(options.hookPort);
25
+ const child = spawn(cliPath, args, { cwd: workDir, stdio: ['ignore', 'pipe', 'pipe'], env });
26
+ log.debug(`Claude CLI spawned: pid=${child.pid}, cwd=${workDir}, session=${sessionId ?? 'new'}`);
27
+ let accumulated = '';
28
+ let accumulatedThinking = '';
29
+ let completed = false;
30
+ let model = '';
31
+ const toolStats = {};
32
+ const pendingToolInputs = new Map();
33
+ const MAX_TIMEOUT = 2_147_483_647;
34
+ const timeoutMs = options?.timeoutMs && options.timeoutMs > 0 ? Math.min(options.timeoutMs, MAX_TIMEOUT) : 0;
35
+ let timeoutHandle = null;
36
+ if (timeoutMs > 0) {
37
+ timeoutHandle = setTimeout(() => {
38
+ if (!completed && !child.killed) {
39
+ completed = true;
40
+ log.warn(`Claude CLI timeout after ${timeoutMs}ms, killing pid=${child.pid}`);
41
+ child.kill('SIGTERM');
42
+ callbacks.onError(`执行超时(${timeoutMs}ms),已终止进程`);
43
+ }
44
+ }, timeoutMs);
45
+ }
46
+ const rl = createInterface({ input: child.stdout });
47
+ rl.on('line', (line) => {
48
+ const event = parseStreamLine(line);
49
+ if (!event)
50
+ return;
51
+ if (isStreamInit(event)) {
52
+ model = event.model;
53
+ callbacks.onSessionId?.(event.session_id);
54
+ }
55
+ const delta = extractTextDelta(event);
56
+ if (delta) {
57
+ accumulated += delta.text;
58
+ callbacks.onText(accumulated);
59
+ return;
60
+ }
61
+ const thinking = extractThinkingDelta(event);
62
+ if (thinking) {
63
+ accumulatedThinking += thinking.text;
64
+ callbacks.onThinking?.(accumulatedThinking);
65
+ return;
66
+ }
67
+ if (isContentBlockStart(event) && event.event.content_block?.type === 'tool_use') {
68
+ const name = event.event.content_block.name;
69
+ if (name)
70
+ pendingToolInputs.set(event.event.index, { name, json: '' });
71
+ return;
72
+ }
73
+ if (isContentBlockDelta(event) && event.event.delta?.type === 'input_json_delta') {
74
+ const pending = pendingToolInputs.get(event.event.index);
75
+ if (pending)
76
+ pending.json += event.event.delta.partial_json ?? '';
77
+ return;
78
+ }
79
+ if (isContentBlockStop(event)) {
80
+ const pending = pendingToolInputs.get(event.event.index);
81
+ if (pending) {
82
+ toolStats[pending.name] = (toolStats[pending.name] || 0) + 1;
83
+ let input;
84
+ try {
85
+ input = JSON.parse(pending.json);
86
+ }
87
+ catch {
88
+ /* empty */
89
+ }
90
+ callbacks.onToolUse?.(pending.name, input);
91
+ pendingToolInputs.delete(event.event.index);
92
+ }
93
+ return;
94
+ }
95
+ const result = extractResult(event);
96
+ if (result) {
97
+ completed = true;
98
+ if (timeoutHandle)
99
+ clearTimeout(timeoutHandle);
100
+ const fullResult = {
101
+ ...result,
102
+ accumulated,
103
+ model,
104
+ toolStats,
105
+ };
106
+ if (!accumulated && fullResult.result)
107
+ accumulated = fullResult.result;
108
+ callbacks.onComplete(fullResult);
109
+ }
110
+ });
111
+ let exitCode = null;
112
+ let rlClosed = false;
113
+ let childClosed = false;
114
+ const finalize = () => {
115
+ if (!rlClosed || !childClosed)
116
+ return;
117
+ if (timeoutHandle)
118
+ clearTimeout(timeoutHandle);
119
+ if (!completed) {
120
+ if (exitCode !== null && exitCode !== 0) {
121
+ callbacks.onError(`Claude CLI exited with code ${exitCode}`);
122
+ }
123
+ else {
124
+ callbacks.onComplete({
125
+ success: true,
126
+ result: accumulated,
127
+ accumulated,
128
+ cost: 0,
129
+ durationMs: 0,
130
+ model,
131
+ numTurns: 0,
132
+ toolStats,
133
+ });
134
+ }
135
+ }
136
+ };
137
+ child.on('close', (code) => {
138
+ exitCode = code;
139
+ childClosed = true;
140
+ finalize();
141
+ });
142
+ rl.on('close', () => {
143
+ rlClosed = true;
144
+ finalize();
145
+ });
146
+ child.on('error', (err) => {
147
+ log.error(`Claude CLI error: ${err.message}`);
148
+ if (timeoutHandle)
149
+ clearTimeout(timeoutHandle);
150
+ if (!completed) {
151
+ completed = true;
152
+ callbacks.onError(`Failed to start Claude CLI: ${err.message}`);
153
+ }
154
+ childClosed = true;
155
+ finalize();
156
+ });
157
+ return {
158
+ abort: () => {
159
+ if (timeoutHandle)
160
+ clearTimeout(timeoutHandle);
161
+ rl.close();
162
+ if (!child.killed)
163
+ child.kill('SIGTERM');
164
+ },
165
+ };
166
+ }
@@ -0,0 +1,17 @@
1
+ import type { StreamEvent } from './types.js';
2
+ export declare function parseStreamLine(line: string): StreamEvent | null;
3
+ export declare function extractTextDelta(event: StreamEvent): {
4
+ text: string;
5
+ } | null;
6
+ export declare function extractThinkingDelta(event: StreamEvent): {
7
+ text: string;
8
+ } | null;
9
+ export declare function extractResult(event: StreamEvent): {
10
+ success: boolean;
11
+ result: string;
12
+ accumulated: string;
13
+ cost: number;
14
+ durationMs: number;
15
+ numTurns: number;
16
+ toolStats: Record<string, number>;
17
+ } | null;
@@ -0,0 +1,50 @@
1
+ export function parseStreamLine(line) {
2
+ const trimmed = line.trim();
3
+ if (!trimmed)
4
+ return null;
5
+ try {
6
+ const parsed = JSON.parse(trimmed);
7
+ if (typeof parsed === 'object' && parsed !== null && 'type' in parsed) {
8
+ return parsed;
9
+ }
10
+ }
11
+ catch {
12
+ /* ignore */
13
+ }
14
+ return null;
15
+ }
16
+ export function extractTextDelta(event) {
17
+ const e = event;
18
+ if (e.type === 'stream_event' &&
19
+ e.event?.type === 'content_block_delta' &&
20
+ e.event.delta?.type === 'text_delta' &&
21
+ e.event.delta.text) {
22
+ return { text: e.event.delta.text };
23
+ }
24
+ return null;
25
+ }
26
+ export function extractThinkingDelta(event) {
27
+ const e = event;
28
+ if (e.type === 'stream_event' &&
29
+ e.event?.type === 'content_block_delta' &&
30
+ e.event.delta?.type === 'thinking_delta' &&
31
+ e.event.delta.thinking) {
32
+ return { text: e.event.delta.thinking };
33
+ }
34
+ return null;
35
+ }
36
+ export function extractResult(event) {
37
+ if (event.type === 'result' && 'subtype' in event) {
38
+ const e = event;
39
+ return {
40
+ success: e.subtype === 'success',
41
+ result: e.result,
42
+ accumulated: '',
43
+ cost: e.total_cost_usd ?? 0,
44
+ durationMs: e.duration_ms ?? 0,
45
+ numTurns: e.num_turns ?? 0,
46
+ toolStats: {},
47
+ };
48
+ }
49
+ return null;
50
+ }
@@ -0,0 +1,54 @@
1
+ export interface StreamInit {
2
+ type: 'system';
3
+ subtype: 'init';
4
+ session_id: string;
5
+ model: string;
6
+ }
7
+ export interface StreamContentBlockDelta {
8
+ type: 'stream_event';
9
+ event: {
10
+ type: 'content_block_delta';
11
+ index: number;
12
+ delta: {
13
+ type: string;
14
+ text?: string;
15
+ thinking?: string;
16
+ partial_json?: string;
17
+ };
18
+ };
19
+ }
20
+ export interface StreamContentBlockStop {
21
+ type: 'stream_event';
22
+ event: {
23
+ type: 'content_block_stop';
24
+ index: number;
25
+ };
26
+ }
27
+ export interface StreamContentBlockStart {
28
+ type: 'stream_event';
29
+ event: {
30
+ type: 'content_block_start';
31
+ index: number;
32
+ content_block: {
33
+ type: string;
34
+ name?: string;
35
+ };
36
+ };
37
+ }
38
+ export interface StreamResult {
39
+ type: 'result';
40
+ subtype: 'success' | 'error';
41
+ result: string;
42
+ total_cost_usd: number;
43
+ duration_ms: number;
44
+ num_turns: number;
45
+ }
46
+ export type StreamEvent = StreamInit | StreamContentBlockDelta | StreamContentBlockStart | StreamContentBlockStop | StreamResult | {
47
+ type: string;
48
+ [key: string]: unknown;
49
+ };
50
+ export declare function isStreamInit(e: StreamEvent): e is StreamInit;
51
+ export declare function isContentBlockDelta(e: StreamEvent): e is StreamContentBlockDelta;
52
+ export declare function isContentBlockStart(e: StreamEvent): e is StreamContentBlockStart;
53
+ export declare function isContentBlockStop(e: StreamEvent): e is StreamContentBlockStop;
54
+ export declare function isStreamResult(e: StreamEvent): e is StreamResult;
@@ -0,0 +1,21 @@
1
+ export function isStreamInit(e) {
2
+ return e.type === 'system' && 'subtype' in e && e.subtype === 'init';
3
+ }
4
+ export function isContentBlockDelta(e) {
5
+ return (e.type === 'stream_event' &&
6
+ typeof e.event === 'object' &&
7
+ e.event.type === 'content_block_delta');
8
+ }
9
+ export function isContentBlockStart(e) {
10
+ return (e.type === 'stream_event' &&
11
+ typeof e.event === 'object' &&
12
+ e.event.type === 'content_block_start');
13
+ }
14
+ export function isContentBlockStop(e) {
15
+ return (e.type === 'stream_event' &&
16
+ typeof e.event === 'object' &&
17
+ e.event.type === 'content_block_stop');
18
+ }
19
+ export function isStreamResult(e) {
20
+ return e.type === 'result' && 'subtype' in e;
21
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { main } from './index.js';
3
+ main().catch((err) => {
4
+ console.error(err);
5
+ process.exit(1);
6
+ });
@@ -0,0 +1,30 @@
1
+ import type { Config } from '../config.js';
2
+ import type { SessionManager } from '../session/session-manager.js';
3
+ import type { RequestQueue } from '../queue/request-queue.js';
4
+ import type { ThreadContext, CostRecord } from '../shared/types.js';
5
+ export type { ThreadContext, CostRecord };
6
+ export interface MessageSender {
7
+ sendTextReply(chatId: string, text: string, threadCtx?: ThreadContext): Promise<void>;
8
+ }
9
+ export interface CommandHandlerDeps {
10
+ config: Config;
11
+ sessionManager: SessionManager;
12
+ requestQueue: RequestQueue;
13
+ sender: MessageSender;
14
+ userCosts: Map<string, CostRecord>;
15
+ getRunningTasksSize: () => number;
16
+ }
17
+ export type ClaudeRequestHandler = (userId: string, chatId: string, prompt: string, workDir: string, convId?: string, threadCtx?: ThreadContext, replyToMessageId?: string) => Promise<void>;
18
+ export declare class CommandHandler {
19
+ private deps;
20
+ constructor(deps: CommandHandlerDeps);
21
+ dispatch(text: string, chatId: string, userId: string, platform: 'feishu' | 'telegram', handleClaudeRequest: ClaudeRequestHandler): Promise<boolean>;
22
+ private handleHelp;
23
+ private handleNew;
24
+ private handlePwd;
25
+ private handleStatus;
26
+ private handleCd;
27
+ private handleAllow;
28
+ private handleDeny;
29
+ private getClaudeVersion;
30
+ }