@coralai/sps-cli 0.57.15 → 0.58.1
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-CN.md +123 -2
- package/README.md +123 -2
- package/dist/commands/codexProxyCommand.d.ts +2 -0
- package/dist/commands/codexProxyCommand.d.ts.map +1 -0
- package/dist/commands/codexProxyCommand.js +81 -0
- package/dist/commands/codexProxyCommand.js.map +1 -0
- package/dist/commands/graphCommand.d.ts +2 -0
- package/dist/commands/graphCommand.d.ts.map +1 -0
- package/dist/commands/graphCommand.js +97 -0
- package/dist/commands/graphCommand.js.map +1 -0
- package/dist/commands/imCommand.d.ts +2 -0
- package/dist/commands/imCommand.d.ts.map +1 -0
- package/dist/commands/imCommand.js +34 -0
- package/dist/commands/imCommand.js.map +1 -0
- package/dist/commands/pipelineTick.d.ts.map +1 -1
- package/dist/commands/pipelineTick.js +9 -0
- package/dist/commands/pipelineTick.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +3 -0
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/tick.d.ts.map +1 -1
- package/dist/commands/tick.js +10 -0
- package/dist/commands/tick.js.map +1 -1
- package/dist/console/index.d.ts.map +1 -1
- package/dist/console/index.js +25 -13
- package/dist/console/index.js.map +1 -1
- package/dist/console/routes/channels.d.ts +3 -0
- package/dist/console/routes/channels.d.ts.map +1 -0
- package/dist/console/routes/channels.js +43 -0
- package/dist/console/routes/channels.js.map +1 -0
- package/dist/console/routes/chat.d.ts.map +1 -1
- package/dist/console/routes/chat.js +49 -7
- package/dist/console/routes/chat.js.map +1 -1
- package/dist/console/routes/codexProxy.d.ts +11 -0
- package/dist/console/routes/codexProxy.d.ts.map +1 -0
- package/dist/console/routes/codexProxy.js +58 -0
- package/dist/console/routes/codexProxy.js.map +1 -0
- package/dist/console/routes/graph.d.ts +13 -0
- package/dist/console/routes/graph.d.ts.map +1 -0
- package/dist/console/routes/graph.js +65 -0
- package/dist/console/routes/graph.js.map +1 -0
- package/dist/console/routes/providers.d.ts.map +1 -1
- package/dist/console/routes/providers.js +6 -2
- package/dist/console/routes/providers.js.map +1 -1
- package/dist/console-assets/assets/index-CKM0zA_z.js +666 -0
- package/dist/console-assets/assets/index-DVnv2TaM.css +10 -0
- package/dist/console-assets/index.html +2 -2
- package/dist/core/agents/sidecar/CodexProxyManager.d.ts +74 -0
- package/dist/core/agents/sidecar/CodexProxyManager.d.ts.map +1 -0
- package/dist/core/agents/sidecar/CodexProxyManager.js +292 -0
- package/dist/core/agents/sidecar/CodexProxyManager.js.map +1 -0
- package/dist/core/agents/workerAnthropicEnv.d.ts +23 -0
- package/dist/core/agents/workerAnthropicEnv.d.ts.map +1 -0
- package/dist/core/agents/workerAnthropicEnv.js +22 -0
- package/dist/core/agents/workerAnthropicEnv.js.map +1 -0
- package/dist/core/claudeJsonl.d.ts +2 -0
- package/dist/core/claudeJsonl.d.ts.map +1 -1
- package/dist/core/claudeJsonl.js +29 -0
- package/dist/core/claudeJsonl.js.map +1 -1
- package/dist/core/codegraph/CodeGraphService.d.ts +45 -0
- package/dist/core/codegraph/CodeGraphService.d.ts.map +1 -0
- package/dist/core/codegraph/CodeGraphService.js +141 -0
- package/dist/core/codegraph/CodeGraphService.js.map +1 -0
- package/dist/engines/StageEngine.d.ts.map +1 -1
- package/dist/engines/StageEngine.js +14 -0
- package/dist/engines/StageEngine.js.map +1 -1
- package/dist/engines/smartarrange/runner.d.ts.map +1 -1
- package/dist/engines/smartarrange/runner.js +2 -0
- package/dist/engines/smartarrange/runner.js.map +1 -1
- package/dist/im/IMAdapter.d.ts +40 -0
- package/dist/im/IMAdapter.d.ts.map +1 -0
- package/dist/im/IMAdapter.js +2 -0
- package/dist/im/IMAdapter.js.map +1 -0
- package/dist/im/IMGatewayManager.d.ts +13 -0
- package/dist/im/IMGatewayManager.d.ts.map +1 -0
- package/dist/im/IMGatewayManager.js +90 -0
- package/dist/im/IMGatewayManager.js.map +1 -0
- package/dist/im/adapters/dingtalkAdapter.d.ts +46 -0
- package/dist/im/adapters/dingtalkAdapter.d.ts.map +1 -0
- package/dist/im/adapters/dingtalkAdapter.js +157 -0
- package/dist/im/adapters/dingtalkAdapter.js.map +1 -0
- package/dist/im/adapters/discordAdapter.d.ts +43 -0
- package/dist/im/adapters/discordAdapter.d.ts.map +1 -0
- package/dist/im/adapters/discordAdapter.js +170 -0
- package/dist/im/adapters/discordAdapter.js.map +1 -0
- package/dist/im/adapters/feishuAdapter.d.ts +44 -0
- package/dist/im/adapters/feishuAdapter.d.ts.map +1 -0
- package/dist/im/adapters/feishuAdapter.js +154 -0
- package/dist/im/adapters/feishuAdapter.js.map +1 -0
- package/dist/im/adapters/matrixAdapter.d.ts +42 -0
- package/dist/im/adapters/matrixAdapter.d.ts.map +1 -0
- package/dist/im/adapters/matrixAdapter.js +106 -0
- package/dist/im/adapters/matrixAdapter.js.map +1 -0
- package/dist/im/adapters/slackAdapter.d.ts +41 -0
- package/dist/im/adapters/slackAdapter.d.ts.map +1 -0
- package/dist/im/adapters/slackAdapter.js +129 -0
- package/dist/im/adapters/slackAdapter.js.map +1 -0
- package/dist/im/adapters/telegramAdapter.d.ts +43 -0
- package/dist/im/adapters/telegramAdapter.d.ts.map +1 -0
- package/dist/im/adapters/telegramAdapter.js +147 -0
- package/dist/im/adapters/telegramAdapter.js.map +1 -0
- package/dist/im/binding.d.ts +24 -0
- package/dist/im/binding.d.ts.map +1 -0
- package/dist/im/binding.js +57 -0
- package/dist/im/binding.js.map +1 -0
- package/dist/im/chatBridge.d.ts +39 -0
- package/dist/im/chatBridge.d.ts.map +1 -0
- package/dist/im/chatBridge.js +215 -0
- package/dist/im/chatBridge.js.map +1 -0
- package/dist/im/router.d.ts +29 -0
- package/dist/im/router.d.ts.map +1 -0
- package/dist/im/router.js +120 -0
- package/dist/im/router.js.map +1 -0
- package/dist/im/types.d.ts +71 -0
- package/dist/im/types.d.ts.map +1 -0
- package/dist/im/types.js +25 -0
- package/dist/im/types.js.map +1 -0
- package/dist/main.js +29 -1
- package/dist/main.js.map +1 -1
- package/dist/manager/makeBackendFactory.d.ts.map +1 -1
- package/dist/manager/makeBackendFactory.js +4 -1
- package/dist/manager/makeBackendFactory.js.map +1 -1
- package/dist/shared/runtimeSchemas.d.ts +0 -2
- package/dist/shared/runtimeSchemas.d.ts.map +1 -1
- package/dist/shared/runtimeSchemas.js +3 -1
- package/dist/shared/runtimeSchemas.js.map +1 -1
- package/package.json +4 -1
- package/project-template/.claude/CLAUDE.md +81 -0
- package/project-template/.claude/hooks/start.sh +44 -0
- package/project-template/.claude/hooks/stop.sh +54 -0
- package/project-template/.claude/rules/.gitkeep +0 -0
- package/project-template/.claude/settings.json +32 -0
- package/project-template/.claude/settings.local.json.template +3 -0
- package/project-template/.claude/skills/.gitkeep +0 -0
- package/project-template/README.md +10 -0
- package/project-template/batch_scheduler.sh +8 -0
- package/project-template/conf.template +61 -0
- package/project-template/deploy.sh +3 -0
- package/project-template/pm_meta/.gitkeep +0 -0
- package/skills/sps-memory/SKILL.md +51 -0
- package/skills/sps-pipeline/SKILL.md +476 -0
- package/dist/console-assets/assets/index-BAA5k08F.js +0 -641
- package/dist/console-assets/assets/index-nY53xZWk.css +0 -10
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { chainToText } from '../types.js';
|
|
2
|
+
export class TelegramAdapter {
|
|
3
|
+
config;
|
|
4
|
+
kind = 'telegram';
|
|
5
|
+
api;
|
|
6
|
+
allow;
|
|
7
|
+
pollTimeout;
|
|
8
|
+
handler = null;
|
|
9
|
+
offset = 0;
|
|
10
|
+
stopped = false;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
if (!config.token)
|
|
14
|
+
throw new Error('telegram adapter 需要 token');
|
|
15
|
+
this.api = `https://api.telegram.org/bot${config.token}`;
|
|
16
|
+
this.allow = config.allowedChatIds?.length ? new Set(config.allowedChatIds.map(String)) : null;
|
|
17
|
+
this.pollTimeout = config.pollTimeoutSec ?? 30;
|
|
18
|
+
}
|
|
19
|
+
onMessage(handler) {
|
|
20
|
+
this.handler = handler;
|
|
21
|
+
}
|
|
22
|
+
async start() {
|
|
23
|
+
this.stopped = false;
|
|
24
|
+
// 长轮询循环(run_async):getUpdates 阻塞到有消息或超时,再取下一批。
|
|
25
|
+
while (!this.stopped) {
|
|
26
|
+
try {
|
|
27
|
+
const updates = await this.call('getUpdates', {
|
|
28
|
+
offset: this.offset,
|
|
29
|
+
timeout: this.pollTimeout,
|
|
30
|
+
allowed_updates: ['message'],
|
|
31
|
+
});
|
|
32
|
+
for (const u of updates ?? []) {
|
|
33
|
+
this.offset = Math.max(this.offset, (u.update_id ?? 0) + 1);
|
|
34
|
+
const evt = this.toEvent(u);
|
|
35
|
+
if (!evt)
|
|
36
|
+
continue;
|
|
37
|
+
if (!this.passAllow(evt.chatId)) {
|
|
38
|
+
this.log(`recv rejected (chatId ${evt.chatId} 不在白名单): ${chainToText(evt.chain).slice(0, 40)}`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
this.log(`recv from ${evt.chatId}: ${chainToText(evt.chain).slice(0, 60)}`);
|
|
42
|
+
if (this.handler) {
|
|
43
|
+
try {
|
|
44
|
+
await this.handler(evt);
|
|
45
|
+
this.log(`handled from ${evt.chatId}`);
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
this.log(`handler error: ${e instanceof Error ? e.message : String(e)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
if (this.stopped)
|
|
55
|
+
break;
|
|
56
|
+
this.log(`poll error: ${e instanceof Error ? e.message : String(e)}`);
|
|
57
|
+
await new Promise((r) => setTimeout(r, 3000)); // 退避,别打爆
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async stop() {
|
|
62
|
+
this.stopped = true;
|
|
63
|
+
}
|
|
64
|
+
async send(target, message) {
|
|
65
|
+
await this.call('sendMessage', { chat_id: target.chatId, text: chainToText(message) });
|
|
66
|
+
}
|
|
67
|
+
async reply(event, message) {
|
|
68
|
+
await this.call('sendMessage', {
|
|
69
|
+
chat_id: event.chatId,
|
|
70
|
+
text: chainToText(message),
|
|
71
|
+
...(event.messageId ? { reply_to_message_id: Number(event.messageId) } : {}),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
supportsStream() {
|
|
75
|
+
return true; // Telegram 靠 editMessageText 累积编辑实现"流式"
|
|
76
|
+
}
|
|
77
|
+
/** 流式回复:先发一条,再随增量 editMessageText;节流避免触发 Telegram 编辑频率限制。
|
|
78
|
+
* 期间用 sendChatAction('typing') 显示"正在回复…"状态(约 5s 有效,循环刷新)。 */
|
|
79
|
+
async replyChunk(event, chunks) {
|
|
80
|
+
// ── "正在回复…" 状态:Telegram 原生 typing 指示,循环刷新到收尾 ──
|
|
81
|
+
const typing = () => void this.call('sendChatAction', { chat_id: event.chatId, action: 'typing' }).catch(() => { });
|
|
82
|
+
typing();
|
|
83
|
+
const typingTimer = setInterval(typing, 4000);
|
|
84
|
+
let acc = '';
|
|
85
|
+
let messageId = null;
|
|
86
|
+
let lastEdit = 0;
|
|
87
|
+
const flush = async (force) => {
|
|
88
|
+
const now = Date.now();
|
|
89
|
+
if (!force && now - lastEdit < 1200)
|
|
90
|
+
return; // ~1 条编辑/1.2s
|
|
91
|
+
lastEdit = now;
|
|
92
|
+
const text = acc || '⏳ 正在回复…';
|
|
93
|
+
if (messageId == null) {
|
|
94
|
+
const sent = await this.call('sendMessage', { chat_id: event.chatId, text });
|
|
95
|
+
messageId = sent?.message_id ?? null;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
await this.call('editMessageText', { chat_id: event.chatId, message_id: messageId, text }).catch(() => { });
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
try {
|
|
102
|
+
for await (const c of chunks) {
|
|
103
|
+
acc += c;
|
|
104
|
+
await flush(false);
|
|
105
|
+
}
|
|
106
|
+
await flush(true); // 收尾整段
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
clearInterval(typingTimer);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// ─── 内部 ───
|
|
113
|
+
passAllow(chatId) {
|
|
114
|
+
return this.allow == null || this.allow.has(chatId);
|
|
115
|
+
}
|
|
116
|
+
toEvent(update) {
|
|
117
|
+
const m = update?.message;
|
|
118
|
+
if (!m || typeof m.text !== 'string')
|
|
119
|
+
return null; // P1 只处理文本
|
|
120
|
+
const t = m.chat?.type ?? 'private';
|
|
121
|
+
const chatType = t === 'private' ? 'private' : 'group';
|
|
122
|
+
return {
|
|
123
|
+
platform: this.kind,
|
|
124
|
+
chatType,
|
|
125
|
+
chatId: String(m.chat?.id),
|
|
126
|
+
sender: { id: String(m.from?.id), name: m.from?.username ?? m.from?.first_name },
|
|
127
|
+
chain: [{ type: 'text', text: m.text }],
|
|
128
|
+
messageId: m.message_id != null ? String(m.message_id) : undefined,
|
|
129
|
+
raw: update,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async call(method, body) {
|
|
133
|
+
const res = await fetch(`${this.api}/${method}`, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
headers: { 'content-type': 'application/json' },
|
|
136
|
+
body: JSON.stringify(body),
|
|
137
|
+
});
|
|
138
|
+
const json = (await res.json());
|
|
139
|
+
if (!json.ok)
|
|
140
|
+
throw new Error(`telegram ${method} failed: ${json.description ?? res.status}`);
|
|
141
|
+
return json.result;
|
|
142
|
+
}
|
|
143
|
+
log(msg) {
|
|
144
|
+
process.stderr.write(`[im/telegram] ${msg}\n`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=telegramAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegramAdapter.js","sourceRoot":"","sources":["../../../src/im/adapters/telegramAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ChatType } from './types.js';
|
|
2
|
+
export interface IMBinding {
|
|
3
|
+
platform: string;
|
|
4
|
+
chatType: ChatType;
|
|
5
|
+
chatId: string;
|
|
6
|
+
/** 当前项目(null = 尚未 /use 切换)。 */
|
|
7
|
+
project: string | null;
|
|
8
|
+
/** 对话会话 id(P2:ChatService 会话,cwd=项目 repoDir);切项目时重建。 */
|
|
9
|
+
chatSessionId?: string | null;
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
}
|
|
12
|
+
/** 读某 IM 会话的绑定(无 → null)。 */
|
|
13
|
+
export declare function getBinding(platform: string, chatType: ChatType, chatId: string): IMBinding | null;
|
|
14
|
+
/** 切换/更新某 IM 会话的绑定(部分字段;now 由调用方给,避免直接 new Date 以利测试)。 */
|
|
15
|
+
export declare function setBinding(b: {
|
|
16
|
+
platform: string;
|
|
17
|
+
chatType: ChatType;
|
|
18
|
+
chatId: string;
|
|
19
|
+
project?: string | null;
|
|
20
|
+
chatSessionId?: string | null;
|
|
21
|
+
}, nowIso: string): IMBinding;
|
|
22
|
+
/** 反向索引:切到某项目的所有 IM 会话(出站推送用)。 */
|
|
23
|
+
export declare function bindingsForProject(project: string): IMBinding[];
|
|
24
|
+
//# sourceMappingURL=binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../../src/im/binding.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB;AAyBD,6BAA6B;AAC7B,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAEjG;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CACxB,CAAC,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACnH,MAAM,EAAE,MAAM,GACb,SAAS,CAeX;AAED,kCAAkC;AAClC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAE/D"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module im/binding
|
|
3
|
+
* @description IM 会话 ↔ 项目 的绑定存储。每个 IM 会话(平台+群/私聊)记住自己"当前项目"
|
|
4
|
+
* (+ 对话用的 chatSessionId,P2 接 ChatService 用)。持久化到 ~/.coral/im/bindings.json。
|
|
5
|
+
* 反向索引(project→bindings)供出站:项目事件精准推给切到它的那些 IM 会话。
|
|
6
|
+
* @layer im
|
|
7
|
+
* @boundedContext im-gateway
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { dirname, resolve } from 'node:path';
|
|
11
|
+
import { bindingKey } from './types.js';
|
|
12
|
+
/** 惰性算路径(每次读 HOME,便于测试/多用户环境;不在模块加载期定死)。 */
|
|
13
|
+
function bindingsFile() {
|
|
14
|
+
return resolve(process.env.HOME || '/home/coral', '.coral', 'im', 'bindings.json');
|
|
15
|
+
}
|
|
16
|
+
function read() {
|
|
17
|
+
try {
|
|
18
|
+
const f = bindingsFile();
|
|
19
|
+
if (!existsSync(f))
|
|
20
|
+
return {};
|
|
21
|
+
return JSON.parse(readFileSync(f, 'utf-8'));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function write(store) {
|
|
28
|
+
const f = bindingsFile();
|
|
29
|
+
mkdirSync(dirname(f), { recursive: true });
|
|
30
|
+
writeFileSync(f, JSON.stringify(store, null, 2));
|
|
31
|
+
}
|
|
32
|
+
/** 读某 IM 会话的绑定(无 → null)。 */
|
|
33
|
+
export function getBinding(platform, chatType, chatId) {
|
|
34
|
+
return read()[bindingKey(platform, chatType, chatId)] ?? null;
|
|
35
|
+
}
|
|
36
|
+
/** 切换/更新某 IM 会话的绑定(部分字段;now 由调用方给,避免直接 new Date 以利测试)。 */
|
|
37
|
+
export function setBinding(b, nowIso) {
|
|
38
|
+
const store = read();
|
|
39
|
+
const key = bindingKey(b.platform, b.chatType, b.chatId);
|
|
40
|
+
const prev = store[key];
|
|
41
|
+
const next = {
|
|
42
|
+
platform: b.platform,
|
|
43
|
+
chatType: b.chatType,
|
|
44
|
+
chatId: b.chatId,
|
|
45
|
+
project: b.project !== undefined ? b.project : (prev?.project ?? null),
|
|
46
|
+
chatSessionId: b.chatSessionId !== undefined ? b.chatSessionId : (prev?.chatSessionId ?? null),
|
|
47
|
+
updatedAt: nowIso,
|
|
48
|
+
};
|
|
49
|
+
store[key] = next;
|
|
50
|
+
write(store);
|
|
51
|
+
return next;
|
|
52
|
+
}
|
|
53
|
+
/** 反向索引:切到某项目的所有 IM 会话(出站推送用)。 */
|
|
54
|
+
export function bindingsForProject(project) {
|
|
55
|
+
return Object.values(read()).filter((b) => b.project === project);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=binding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binding.js","sourceRoot":"","sources":["../../src/im/binding.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAaxC,4CAA4C;AAC5C,SAAS,YAAY;IACnB,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;AACrF,CAAC;AAID,SAAS,IAAI;IACX,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,YAAY,EAAE,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAU,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,KAAK,CAAC,KAAY;IACzB,MAAM,CAAC,GAAG,YAAY,EAAE,CAAC;IACzB,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,QAAkB,EAAE,MAAc;IAC7E,OAAO,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,IAAI,CAAC;AAChE,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,UAAU,CACxB,CAAmH,EACnH,MAAc;IAEd,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC;IACrB,MAAM,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IACxB,MAAM,IAAI,GAAc;QACtB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,OAAO,EAAE,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC;QACtE,aAAa,EAAE,CAAC,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,aAAa,IAAI,IAAI,CAAC;QAC9F,SAAS,EAAE,MAAM;KAClB,CAAC;IACF,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IAClB,KAAK,CAAC,KAAK,CAAC,CAAC;IACb,OAAO,IAAI,CAAC;AACd,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;AACpE,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IMAdapter } from './IMAdapter.js';
|
|
2
|
+
import type { IMBinding } from './binding.js';
|
|
3
|
+
import type { IMEvent } from './types.js';
|
|
4
|
+
export interface ConsoleConfig {
|
|
5
|
+
/** console 基址,如 http://localhost:4321。 */
|
|
6
|
+
url: string;
|
|
7
|
+
/** SPS_CONSOLE_TOKEN(console 设了鉴权时必填)。 */
|
|
8
|
+
token?: string;
|
|
9
|
+
/** chat 后端:claude(默认)或 openai。 */
|
|
10
|
+
agentKind?: 'claude' | 'openai';
|
|
11
|
+
}
|
|
12
|
+
export declare class ChatBridge {
|
|
13
|
+
private readonly cfg;
|
|
14
|
+
/** 每会话串行守卫:一个会话同一时刻只有一轮在途(防连发消息在 daemon 里堆出"active run"冲突/幽灵)。 */
|
|
15
|
+
private readonly busy;
|
|
16
|
+
constructor(cfg: ConsoleConfig);
|
|
17
|
+
/** 纯文本 → 当前项目 agent 对话 → 流式回灌 IM。 */
|
|
18
|
+
handle(binding: IMBinding, text: string, adapter: IMAdapter, event: IMEvent): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* 确保该 IM 会话有一个**存在且可用**的 chat 会话:先拉一次列表 →
|
|
21
|
+
* ① 绑定的会话仍在(未被删)→ 用;② 否则找项目现有可续会话复用;③ 都没有 → 新建。
|
|
22
|
+
* (关键:校验绑定会话是否还存在——用户可能在 console 删了它,不校验会 POST 到死会话一直空转。)
|
|
23
|
+
*/
|
|
24
|
+
private ensureSession;
|
|
25
|
+
private listSessions;
|
|
26
|
+
/**
|
|
27
|
+
* 从列表里挑该项目**已有的可续会话**(最近活动的);无则 null。
|
|
28
|
+
* 匹配:`project===名` **或** `cwd===项目repoDir`(捞 project 字段为 null、只有 cwd 的老会话);
|
|
29
|
+
* 排除:CLI 只读会话(source==='cli' / readonly / id 以 session- 开头)。
|
|
30
|
+
*/
|
|
31
|
+
private pickProjectSession;
|
|
32
|
+
private bindSession;
|
|
33
|
+
/** 开 SSE + 发消息,产出文本增量(async generator)。 */
|
|
34
|
+
private streamReply;
|
|
35
|
+
private headers;
|
|
36
|
+
private post;
|
|
37
|
+
private get;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=chatBridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatBridge.d.ts","sourceRoot":"","sources":["../../src/im/chatBridge.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,aAAa;IAC5B,0CAA0C;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CACjC;AAaD,qBAAa,UAAU;IAIT,OAAO,CAAC,QAAQ,CAAC,GAAG;IAHhC,kEAAkE;IAClE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;gBAEb,GAAG,EAAE,aAAa;IAE/C,qCAAqC;IAC/B,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BjG;;;;OAIG;YACW,aAAa;YA6Bb,YAAY;IAK1B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,WAAW;IAInB,2CAA2C;YAC5B,WAAW;IA0D1B,OAAO,CAAC,OAAO;YAOD,IAAI;YAMJ,GAAG;CAKlB"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module im/chatBridge
|
|
3
|
+
* @description P2:把 IM 纯文本桥到"当前项目的 agent 对话"。方案 A —— HTTP 桥接 console 的 chat API,
|
|
4
|
+
* 复用其成熟的发消息+流式(claude/openai/工具/durable resume 全白拿),不重造。
|
|
5
|
+
* 流程:确保该 IM 会话有一个 cwd=项目 repoDir 的 chat 会话(懒建)→ 开 SSE → 发消息 →
|
|
6
|
+
* 消费 chunk.text 增量 → 经 adapter.replyChunk 流式回灌 IM。
|
|
7
|
+
* @layer im
|
|
8
|
+
* @boundedContext im-gateway
|
|
9
|
+
*/
|
|
10
|
+
import { findProjectRepoDir } from '../shared/projectRepo.js';
|
|
11
|
+
import { setBinding } from './binding.js';
|
|
12
|
+
import { textChain } from './types.js';
|
|
13
|
+
export class ChatBridge {
|
|
14
|
+
cfg;
|
|
15
|
+
/** 每会话串行守卫:一个会话同一时刻只有一轮在途(防连发消息在 daemon 里堆出"active run"冲突/幽灵)。 */
|
|
16
|
+
busy = new Set();
|
|
17
|
+
constructor(cfg) {
|
|
18
|
+
this.cfg = cfg;
|
|
19
|
+
}
|
|
20
|
+
/** 纯文本 → 当前项目 agent 对话 → 流式回灌 IM。 */
|
|
21
|
+
async handle(binding, text, adapter, event) {
|
|
22
|
+
const project = binding.project;
|
|
23
|
+
if (!project)
|
|
24
|
+
return void (await adapter.reply(event, textChain('请先 /use <项目>')));
|
|
25
|
+
let sessionId;
|
|
26
|
+
try {
|
|
27
|
+
sessionId = await this.ensureSession(binding, project);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
return void (await adapter.reply(event, textChain(`建对话会话失败:${msg(e)}`)));
|
|
31
|
+
}
|
|
32
|
+
// 上一轮还在回复 → 别再发(否则 daemon slot 撞 active run)。提示稍候。
|
|
33
|
+
if (this.busy.has(sessionId)) {
|
|
34
|
+
return void (await adapter.reply(event, textChain('⏳ 上一条还在回复中,请等它回完再发~')));
|
|
35
|
+
}
|
|
36
|
+
this.busy.add(sessionId);
|
|
37
|
+
try {
|
|
38
|
+
const deltas = this.streamReply(sessionId, text);
|
|
39
|
+
if (adapter.supportsStream() && adapter.replyChunk) {
|
|
40
|
+
await adapter.replyChunk(event, deltas);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
let acc = '';
|
|
44
|
+
for await (const d of deltas)
|
|
45
|
+
acc += d;
|
|
46
|
+
await adapter.reply(event, textChain(acc || '(无回复)'));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
this.busy.delete(sessionId);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 确保该 IM 会话有一个**存在且可用**的 chat 会话:先拉一次列表 →
|
|
55
|
+
* ① 绑定的会话仍在(未被删)→ 用;② 否则找项目现有可续会话复用;③ 都没有 → 新建。
|
|
56
|
+
* (关键:校验绑定会话是否还存在——用户可能在 console 删了它,不校验会 POST 到死会话一直空转。)
|
|
57
|
+
*/
|
|
58
|
+
async ensureSession(binding, project) {
|
|
59
|
+
const list = await this.listSessions();
|
|
60
|
+
// ① 绑定的会话还在且可用 → 直接用。
|
|
61
|
+
if (binding.chatSessionId && list.some((s) => s.id === binding.chatSessionId && !s.readonly)) {
|
|
62
|
+
return binding.chatSessionId;
|
|
63
|
+
}
|
|
64
|
+
// ② 绑的没了(或没绑)→ 复用该项目现有可续会话(比如你原来那条)。
|
|
65
|
+
const existing = this.pickProjectSession(list, project);
|
|
66
|
+
if (existing) {
|
|
67
|
+
this.bindSession(binding, existing);
|
|
68
|
+
return existing;
|
|
69
|
+
}
|
|
70
|
+
// ③ 都没有 → 新建。
|
|
71
|
+
const cwd = findProjectRepoDir(project);
|
|
72
|
+
const body = await this.post('/api/chat/sessions', {
|
|
73
|
+
title: `IM:${binding.platform}:${project}`,
|
|
74
|
+
project,
|
|
75
|
+
...(cwd ? { cwd } : {}),
|
|
76
|
+
agentKind: this.cfg.agentKind ?? 'claude',
|
|
77
|
+
});
|
|
78
|
+
const id = body?.id;
|
|
79
|
+
if (!id)
|
|
80
|
+
throw new Error('console 未返回会话 id');
|
|
81
|
+
this.bindSession(binding, id);
|
|
82
|
+
return id;
|
|
83
|
+
}
|
|
84
|
+
async listSessions() {
|
|
85
|
+
const body = await this.get('/api/chat/sessions');
|
|
86
|
+
return (body?.data ?? []);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 从列表里挑该项目**已有的可续会话**(最近活动的);无则 null。
|
|
90
|
+
* 匹配:`project===名` **或** `cwd===项目repoDir`(捞 project 字段为 null、只有 cwd 的老会话);
|
|
91
|
+
* 排除:CLI 只读会话(source==='cli' / readonly / id 以 session- 开头)。
|
|
92
|
+
*/
|
|
93
|
+
pickProjectSession(list, project) {
|
|
94
|
+
const repoDir = findProjectRepoDir(project);
|
|
95
|
+
const mine = list.filter((s) => s.id &&
|
|
96
|
+
s.source !== 'cli' &&
|
|
97
|
+
!s.readonly &&
|
|
98
|
+
!String(s.id).startsWith('session-') &&
|
|
99
|
+
(s.project === project || (!!repoDir && s.cwd === repoDir)));
|
|
100
|
+
if (!mine.length)
|
|
101
|
+
return null;
|
|
102
|
+
const t = (s) => s.lastMessageAt ?? s.createdAt ?? '';
|
|
103
|
+
mine.sort((a, b) => t(b).localeCompare(t(a)));
|
|
104
|
+
return mine[0].id ?? null;
|
|
105
|
+
}
|
|
106
|
+
bindSession(binding, chatSessionId) {
|
|
107
|
+
setBinding({ platform: binding.platform, chatType: binding.chatType, chatId: binding.chatId, chatSessionId }, new Date().toISOString());
|
|
108
|
+
}
|
|
109
|
+
/** 开 SSE + 发消息,产出文本增量(async generator)。 */
|
|
110
|
+
async *streamReply(sessionId, text) {
|
|
111
|
+
// 开 SSE。注意:console 在**新连接时会补发上一轮(已完成)的 complete**(断线补偿),
|
|
112
|
+
// 不能见 complete 就结束——否则会截断本轮。用 sawChunk 门控:只有见过**本轮的 chunk** 后,
|
|
113
|
+
// 再来的 complete/error 才是本轮的终点(补偿的旧 complete 不带 chunk → 被跳过)。
|
|
114
|
+
const res = await fetch(`${this.cfg.url}/stream/chat/${sessionId}`, { headers: this.headers() });
|
|
115
|
+
if (!res.ok || !res.body)
|
|
116
|
+
throw new Error(`SSE 连接失败 HTTP ${res.status}`);
|
|
117
|
+
// 发消息触发本轮(不 await,避免它阻塞到回合结束才读流)。失败(如会话已删 404)记下来,读循环里止住空转。
|
|
118
|
+
let postError = null;
|
|
119
|
+
void this.post(`/api/chat/sessions/${sessionId}/messages`, { content: text }).catch((e) => {
|
|
120
|
+
postError = msg(e);
|
|
121
|
+
});
|
|
122
|
+
const reader = res.body.getReader();
|
|
123
|
+
const dec = new TextDecoder();
|
|
124
|
+
let buf = '';
|
|
125
|
+
let sawChunk = false;
|
|
126
|
+
const deadline = Date.now() + 8 * 60_000; // 兜底:单轮最长 8min(防卡死无限等)
|
|
127
|
+
try {
|
|
128
|
+
for (;;) {
|
|
129
|
+
if (postError && !sawChunk) {
|
|
130
|
+
yield `[发送失败:${postError}]`;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
if (Date.now() > deadline) {
|
|
134
|
+
if (!sawChunk)
|
|
135
|
+
yield '[对话超时,未收到回复]';
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
const { done, value } = await reader.read();
|
|
139
|
+
if (done)
|
|
140
|
+
break;
|
|
141
|
+
buf += dec.decode(value, { stream: true });
|
|
142
|
+
let idx;
|
|
143
|
+
while ((idx = buf.indexOf('\n\n')) >= 0) {
|
|
144
|
+
const frame = buf.slice(0, idx);
|
|
145
|
+
buf = buf.slice(idx + 2);
|
|
146
|
+
const parsed = parseFrame(frame);
|
|
147
|
+
if (!parsed)
|
|
148
|
+
continue;
|
|
149
|
+
if (parsed.event === 'chat.message.chunk.text') {
|
|
150
|
+
const t = parsed.data?.text;
|
|
151
|
+
if (t) {
|
|
152
|
+
sawChunk = true;
|
|
153
|
+
yield t;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else if (parsed.event === 'chat.message.complete') {
|
|
157
|
+
if (sawChunk)
|
|
158
|
+
return; // 本轮真结束
|
|
159
|
+
// 否则:补偿补发的上一轮 complete,忽略,继续等本轮 chunk。
|
|
160
|
+
}
|
|
161
|
+
else if (parsed.event === 'chat.message.error') {
|
|
162
|
+
if (!sawChunk)
|
|
163
|
+
continue; // 上一轮的死回合补偿,忽略
|
|
164
|
+
const m = parsed.data?.message?.content;
|
|
165
|
+
yield `\n[对话出错:${m ?? '未知'}]`;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
reader.cancel().catch(() => { });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
headers() {
|
|
176
|
+
return {
|
|
177
|
+
'content-type': 'application/json',
|
|
178
|
+
...(this.cfg.token ? { authorization: `Bearer ${this.cfg.token}` } : {}),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
async post(path, body) {
|
|
182
|
+
const res = await fetch(`${this.cfg.url}${path}`, { method: 'POST', headers: this.headers(), body: JSON.stringify(body) });
|
|
183
|
+
if (!res.ok)
|
|
184
|
+
throw new Error(`POST ${path} → HTTP ${res.status}`);
|
|
185
|
+
return res.json().catch(() => ({}));
|
|
186
|
+
}
|
|
187
|
+
async get(path) {
|
|
188
|
+
const res = await fetch(`${this.cfg.url}${path}`, { headers: this.headers() });
|
|
189
|
+
if (!res.ok)
|
|
190
|
+
throw new Error(`GET ${path} → HTTP ${res.status}`);
|
|
191
|
+
return res.json().catch(() => ({}));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function parseFrame(frame) {
|
|
195
|
+
let event = 'message';
|
|
196
|
+
const dataLines = [];
|
|
197
|
+
for (const line of frame.split('\n')) {
|
|
198
|
+
if (line.startsWith('event:'))
|
|
199
|
+
event = line.slice(6).trim();
|
|
200
|
+
else if (line.startsWith('data:'))
|
|
201
|
+
dataLines.push(line.slice(5).trim());
|
|
202
|
+
}
|
|
203
|
+
if (!dataLines.length)
|
|
204
|
+
return null;
|
|
205
|
+
try {
|
|
206
|
+
return { event, data: JSON.parse(dataLines.join('\n')) };
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function msg(e) {
|
|
213
|
+
return e instanceof Error ? e.message : String(e);
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=chatBridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatBridge.js","sourceRoot":"","sources":["../../src/im/chatBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAG9D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAsBvC,MAAM,OAAO,UAAU;IAIQ;IAH7B,kEAAkE;IACjD,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE1C,YAA6B,GAAkB;QAAlB,QAAG,GAAH,GAAG,CAAe;IAAG,CAAC;IAEnD,qCAAqC;IACrC,KAAK,CAAC,MAAM,CAAC,OAAkB,EAAE,IAAY,EAAE,OAAkB,EAAE,KAAc;QAC/E,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;QAClF,IAAI,SAAiB,CAAC;QACtB,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,KAAK,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3E,CAAC;QAED,mDAAmD;QACnD,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACjD,IAAI,OAAO,CAAC,cAAc,EAAE,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;gBACnD,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACN,IAAI,GAAG,GAAG,EAAE,CAAC;gBACb,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM;oBAAE,GAAG,IAAI,CAAC,CAAC;gBACvC,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,aAAa,CAAC,OAAkB,EAAE,OAAe;QAC7D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAEvC,sBAAsB;QACtB,IAAI,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7F,OAAO,OAAO,CAAC,aAAa,CAAC;QAC/B,CAAC;QAED,qCAAqC;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACxD,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACpC,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,cAAc;QACd,MAAM,GAAG,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE;YACjD,KAAK,EAAE,MAAM,OAAO,CAAC,QAAQ,IAAI,OAAO,EAAE;YAC1C,OAAO;YACP,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvB,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,QAAQ;SAC1C,CAAC,CAAC;QACH,MAAM,EAAE,GAAI,IAAwB,EAAE,EAAE,CAAC;QACzC,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAC7C,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC9B,OAAO,EAAE,CAAC;IACZ,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClD,OAAO,CAAE,IAA6B,EAAE,IAAI,IAAI,EAAE,CAAqB,CAAC;IAC1E,CAAC;IAED;;;;OAIG;IACK,kBAAkB,CAAC,IAAsB,EAAE,OAAe;QAChE,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CACtB,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,MAAM,KAAK,KAAK;YAClB,CAAC,CAAC,CAAC,QAAQ;YACX,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;YACpC,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,CAC9D,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,CAAC,GAAG,CAAC,CAAiB,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC;QACtE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,CAAC;IAC5B,CAAC;IAEO,WAAW,CAAC,OAAkB,EAAE,aAAqB;QAC3D,UAAU,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAC1I,CAAC;IAED,2CAA2C;IACnC,KAAK,CAAC,CAAC,WAAW,CAAC,SAAiB,EAAE,IAAY;QACxD,yDAAyD;QACzD,+DAA+D;QAC/D,4DAA4D;QAC5D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,gBAAgB,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACjG,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACzE,4DAA4D;QAC5D,IAAI,SAAS,GAAkB,IAAI,CAAC;QACpC,KAAK,IAAI,CAAC,IAAI,CAAC,sBAAsB,SAAS,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;YACxF,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,uBAAuB;QACjE,IAAI,CAAC;YACH,SAAS,CAAC;gBACR,IAAI,SAAS,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAC3B,MAAM,SAAS,SAAS,GAAG,CAAC;oBAC5B,MAAM;gBACR,CAAC;gBACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;oBAC1B,IAAI,CAAC,QAAQ;wBAAE,MAAM,cAAc,CAAC;oBACpC,MAAM;gBACR,CAAC;gBACD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC5C,IAAI,IAAI;oBAAE,MAAM;gBAChB,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC3C,IAAI,GAAW,CAAC;gBAChB,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;oBACxC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;oBAChC,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;oBACzB,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;oBACjC,IAAI,CAAC,MAAM;wBAAE,SAAS;oBACtB,IAAI,MAAM,CAAC,KAAK,KAAK,yBAAyB,EAAE,CAAC;wBAC/C,MAAM,CAAC,GAAI,MAAM,CAAC,IAA0B,EAAE,IAAI,CAAC;wBACnD,IAAI,CAAC,EAAE,CAAC;4BACN,QAAQ,GAAG,IAAI,CAAC;4BAChB,MAAM,CAAC,CAAC;wBACV,CAAC;oBACH,CAAC;yBAAM,IAAI,MAAM,CAAC,KAAK,KAAK,uBAAuB,EAAE,CAAC;wBACpD,IAAI,QAAQ;4BAAE,OAAO,CAAC,QAAQ;wBAC9B,uCAAuC;oBACzC,CAAC;yBAAM,IAAI,MAAM,CAAC,KAAK,KAAK,oBAAoB,EAAE,CAAC;wBACjD,IAAI,CAAC,QAAQ;4BAAE,SAAS,CAAC,eAAe;wBACxC,MAAM,CAAC,GAAI,MAAM,CAAC,IAA2C,EAAE,OAAO,EAAE,OAAO,CAAC;wBAChF,MAAM,WAAW,CAAC,IAAI,IAAI,GAAG,CAAC;wBAC9B,OAAO;oBACT,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAEO,OAAO;QACb,OAAO;YACL,cAAc,EAAE,kBAAkB;YAClC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzE,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,IAAa;QAC5C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3H,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,WAAW,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAClE,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC;IAEO,KAAK,CAAC,GAAG,CAAC,IAAY;QAC5B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,WAAW,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACjE,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC;CACF;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,IAAI,KAAK,GAAG,SAAS,CAAC;IACtB,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACvD,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,GAAG,CAAC,CAAU;IACrB,OAAO,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module im/router
|
|
3
|
+
* @description IM 命令路由(**平台无关**)。把 IMEvent 解析成命令(/projects /use /status …)或纯文本,
|
|
4
|
+
* 分派到服务容器 / 会话绑定 / (P2)ChatService。上层只有这一处懂"命令语义",各适配器不重复。
|
|
5
|
+
* @layer im
|
|
6
|
+
* @boundedContext im-gateway
|
|
7
|
+
*/
|
|
8
|
+
import type { ServiceContainer } from '../services/container.js';
|
|
9
|
+
import type { ChatBridge } from './chatBridge.js';
|
|
10
|
+
import type { IMAdapter } from './IMAdapter.js';
|
|
11
|
+
import type { IMEvent } from './types.js';
|
|
12
|
+
export declare class IMRouter {
|
|
13
|
+
private readonly container;
|
|
14
|
+
/** P2:纯文本 → 项目 agent 对话的桥(无则退回提示)。 */
|
|
15
|
+
private readonly bridge?;
|
|
16
|
+
constructor(container: ServiceContainer,
|
|
17
|
+
/** P2:纯文本 → 项目 agent 对话的桥(无则退回提示)。 */
|
|
18
|
+
bridge?: ChatBridge | undefined);
|
|
19
|
+
/** 处理一条入站事件(manager 在 adapter.onMessage 里调)。 */
|
|
20
|
+
handle(event: IMEvent, adapter: IMAdapter): Promise<void>;
|
|
21
|
+
private cmdProjects;
|
|
22
|
+
private cmdUse;
|
|
23
|
+
private cmdStatus;
|
|
24
|
+
/** 触发/停止流水线 或 批准编排(= gameforge 的按钮),都对"当前项目"。 */
|
|
25
|
+
private cmdProjectAction;
|
|
26
|
+
private handlePlainText;
|
|
27
|
+
private currentProject;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/im/router.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAiB1C,qBAAa,QAAQ;IAEjB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,sCAAsC;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAFP,SAAS,EAAE,gBAAgB;IAC5C,sCAAsC;IACrB,MAAM,CAAC,EAAE,UAAU,YAAA;IAGtC,gDAAgD;IAC1C,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;YAkCjD,WAAW;YAQX,MAAM;YAeN,SAAS;IAavB,iDAAiD;YACnC,gBAAgB;YAmBhB,eAAe;IAQ7B,OAAO,CAAC,cAAc;CAGvB"}
|