autosnippet 3.2.8 → 3.2.10
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/bin/cli.js +6 -5
- package/dashboard/dist/assets/index-BTAsOZv2.js +128 -0
- package/dashboard/dist/assets/index-C_72Ct98.css +1 -0
- package/dashboard/dist/index.html +2 -2
- package/lib/cli/AiScanService.js +23 -26
- package/lib/cli/SetupService.js +1 -1
- package/lib/cli/deploy/FileManifest.js +1 -1
- package/lib/core/AstAnalyzer.js +1 -1
- package/lib/core/discovery/index.js +2 -2
- package/lib/external/ai/AiProvider.js +66 -172
- package/lib/external/ai/providers/GoogleGeminiProvider.js +29 -5
- package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +3 -3
- package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +8 -8
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +291 -204
- package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +7 -6
- package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
- package/lib/external/mcp/handlers/bootstrap-internal.js +2 -2
- package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
- package/lib/http/HttpServer.js +1 -1
- package/lib/http/middleware/requestLogger.js +1 -0
- package/lib/http/routes/ai.js +240 -35
- package/lib/http/routes/candidates.js +2 -3
- package/lib/http/routes/extract.js +13 -11
- package/lib/http/routes/modules.js +2 -2
- package/lib/http/routes/recipes.js +5 -5
- package/lib/http/routes/remote.js +134 -255
- package/lib/http/routes/violations.js +0 -54
- package/lib/http/utils/sse-sessions.js +1 -1
- package/lib/infrastructure/logging/Logger.js +5 -4
- package/lib/infrastructure/monitoring/PerformanceMonitor.js +3 -2
- package/lib/injection/ServiceContainer.js +64 -17
- package/lib/platform/ScreenCaptureService.js +177 -0
- package/lib/platform/ios/routes/spm.js +2 -2
- package/lib/service/agent/AgentEventBus.js +207 -0
- package/lib/service/agent/AgentFactory.js +535 -0
- package/lib/service/agent/AgentMessage.js +240 -0
- package/lib/service/agent/AgentRouter.js +228 -0
- package/lib/service/agent/AgentRuntime.js +1056 -0
- package/lib/service/agent/AgentState.js +217 -0
- package/lib/service/agent/IntentClassifier.js +331 -0
- package/lib/service/agent/LarkTransport.js +389 -0
- package/lib/service/agent/capabilities.js +409 -0
- package/lib/service/{chat → agent/context}/ContextWindow.js +37 -12
- package/lib/service/{chat → agent/context}/ExplorationTracker.js +112 -33
- package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +5 -3
- package/lib/service/agent/core/LoopContext.js +170 -0
- package/lib/service/agent/core/MessageAdapter.js +223 -0
- package/lib/service/agent/core/ToolExecutionPipeline.js +376 -0
- package/lib/service/{chat → agent/domain}/ChatAgentTasks.js +15 -98
- package/lib/service/{chat → agent/domain}/EpisodicConsolidator.js +7 -7
- package/lib/service/{chat → agent/domain}/EvidenceCollector.js +4 -2
- package/lib/service/{chat/AnalystAgent.js → agent/domain/insight-analyst.js} +37 -172
- package/lib/service/{chat/HandoffProtocol.js → agent/domain/insight-gate.js} +85 -135
- package/lib/service/agent/domain/insight-producer.js +270 -0
- package/lib/service/agent/domain/scan-prompts.js +444 -0
- package/lib/service/agent/forced-summary.js +266 -0
- package/lib/service/agent/index.js +91 -0
- package/lib/service/{chat → agent}/memory/ActiveContext.js +29 -1
- package/lib/service/{chat → agent}/memory/MemoryCoordinator.js +7 -7
- package/lib/service/{chat/ProjectSemanticMemory.js → agent/memory/PersistentMemory.js} +359 -89
- package/lib/service/{chat → agent}/memory/SessionStore.js +1 -1
- package/lib/service/{chat → agent}/memory/index.js +1 -1
- package/lib/service/agent/policies.js +442 -0
- package/lib/service/agent/presets.js +305 -0
- package/lib/service/agent/strategies.js +756 -0
- package/lib/service/{chat → agent/tools}/ToolRegistry.js +3 -3
- package/lib/service/agent/tools/ai-analysis.js +75 -0
- package/lib/service/{chat → agent}/tools/composite.js +2 -1
- package/lib/service/{chat → agent}/tools/guard.js +1 -121
- package/lib/service/{chat → agent}/tools/index.js +27 -21
- package/lib/service/{chat → agent}/tools/infrastructure.js +1 -1
- package/lib/service/agent/tools/knowledge-graph.js +112 -0
- package/lib/service/agent/tools/scan-recipe.js +189 -0
- package/lib/service/agent/tools/system-interaction.js +476 -0
- package/lib/service/automation/DirectiveDetector.js +0 -1
- package/lib/service/automation/FileWatcher.js +0 -8
- package/lib/service/automation/handlers/CreateHandler.js +7 -3
- package/lib/service/automation/handlers/DraftHandler.js +7 -6
- package/lib/service/module/ModuleService.js +40 -73
- package/lib/service/skills/SignalCollector.js +26 -19
- package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
- package/lib/shared/FieldSpec.js +1 -1
- package/lib/shared/StyleGuide.js +1 -1
- package/package.json +4 -1
- package/resources/native-ui/screenshot.swift +228 -0
- package/dashboard/dist/assets/index-D5jiDBQG.css +0 -1
- package/dashboard/dist/assets/index-e5OKj-Ni.js +0 -128
- package/lib/core/discovery/SpmDiscoverer.js +0 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -750
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +0 -277
- package/lib/http/routes/spm.js +0 -5
- package/lib/infrastructure/external/XcodeAutomation.js +0 -15
- package/lib/service/chat/ChatAgent.js +0 -1602
- package/lib/service/chat/Memory.js +0 -161
- package/lib/service/chat/ProducerAgent.js +0 -431
- package/lib/service/chat/ReasoningTrace.js +0 -523
- package/lib/service/chat/TaskPipeline.js +0 -357
- package/lib/service/chat/WorkingMemory.js +0 -359
- package/lib/service/chat/memory/PersistentMemory.js +0 -450
- package/lib/service/chat/tools/ai-analysis.js +0 -267
- package/lib/service/chat/tools/knowledge-graph.js +0 -234
- package/lib/service/chat/tools.js +0 -18
- package/lib/service/snippet/PlaceholderConverter.js +0 -5
- package/lib/service/snippet/codecs/XcodeCodec.js +0 -5
- /package/lib/service/{chat → agent}/ConversationStore.js +0 -0
- /package/lib/service/{chat → agent}/tools/_shared.js +0 -0
- /package/lib/service/{chat → agent}/tools/ast-graph.js +0 -0
- /package/lib/service/{chat → agent}/tools/lifecycle.js +0 -0
- /package/lib/service/{chat → agent}/tools/project-access.js +0 -0
- /package/lib/service/{chat → agent}/tools/query.js +0 -0
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Remote Command Router — 飞书 Bot
|
|
2
|
+
* Remote Command Router — 飞书 Bot ↔ IDE 编程桥接 + AI Agent 知识管理
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* 架构(自然语言路由模式 v2):
|
|
5
|
+
* 飞书消息 → LarkTransport → IntentClassifier 意图分类
|
|
6
|
+
* → bot_agent: 知识管理任务 → AgentRuntime 直接处理 → 飞书回复
|
|
7
|
+
* → ide_agent: 编程任务 → remote_commands 队列 → VSCode 扩展 → Copilot Chat
|
|
8
|
+
* → system: 状态/截图 → 本地直接处理
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* 设计原则:
|
|
11
|
+
* ✓ 零命令交互 — 全部使用自然语言,AI 自动判断意图
|
|
12
|
+
* ✓ 双 Agent 分流 — 知识任务服务端处理,编程任务转发 IDE
|
|
11
13
|
* ✓ 飞书 WS 随路由加载自动启动
|
|
12
|
-
* ✓ 系统命令: /help /status /queue /cancel /clear /ping /screen
|
|
13
14
|
* ✓ 超时自动清理(pending 120s / running 600s)
|
|
14
15
|
* ✓ 消息去重 + 非文本提示
|
|
15
16
|
* ✓ SDK Client 回复 + REST 回退
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
import crypto from 'node:crypto';
|
|
19
|
-
import {
|
|
20
|
-
import { readFileSync, unlinkSync, existsSync, statSync } from 'node:fs';
|
|
21
|
-
import { tmpdir } from 'node:os';
|
|
22
|
-
import { join } from 'node:path';
|
|
20
|
+
import { readFileSync, unlinkSync, existsSync } from 'node:fs';
|
|
23
21
|
import express from 'express';
|
|
24
22
|
import Logger from '../../infrastructure/logging/Logger.js';
|
|
25
23
|
import { getServiceContainer } from '../../injection/ServiceContainer.js';
|
|
26
24
|
import { asyncHandler } from '../middleware/errorHandler.js';
|
|
25
|
+
import { LarkTransport } from '../../service/agent/LarkTransport.js';
|
|
27
26
|
|
|
28
27
|
const router = express.Router();
|
|
29
28
|
const logger = Logger.getInstance();
|
|
@@ -286,60 +285,60 @@ setInterval(() => {
|
|
|
286
285
|
}, CLEANUP_INTERVAL_MS);
|
|
287
286
|
|
|
288
287
|
// ═══════════════════════════════════════════════════════
|
|
289
|
-
//
|
|
288
|
+
// LarkTransport — 自然语言意图路由
|
|
290
289
|
// ═══════════════════════════════════════════════════════
|
|
291
290
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
291
|
+
/** @type {LarkTransport|null} */
|
|
292
|
+
let _larkTransport = null;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* 获取或创建 LarkTransport 实例
|
|
296
|
+
* 延迟初始化,等待 ServiceContainer 就绪
|
|
297
|
+
*/
|
|
298
|
+
function getLarkTransport() {
|
|
299
|
+
if (_larkTransport) return _larkTransport;
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
const container = getServiceContainer();
|
|
303
|
+
const agentFactory = container.get('agentFactory');
|
|
304
|
+
const aiProvider = container.get('aiProvider');
|
|
305
|
+
|
|
306
|
+
if (!agentFactory) {
|
|
307
|
+
logger.warn('[Remote/Lark] AgentFactory not available, transport not ready');
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
_larkTransport = new LarkTransport({
|
|
312
|
+
agentFactory,
|
|
313
|
+
aiProvider,
|
|
314
|
+
replyFn: replyLark,
|
|
315
|
+
sendFn: sendLarkNotification,
|
|
316
|
+
sendImageFn: sendLarkScreenshot,
|
|
317
|
+
getStatusFn: getStatusText,
|
|
318
|
+
enqueueIdeFn: enqueueIdeCommand,
|
|
319
|
+
isUserAllowed,
|
|
320
|
+
});
|
|
307
321
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
'系统命令:',
|
|
315
|
-
' /status — 连接诊断 + 队列状态',
|
|
316
|
-
' /queue — 查看待执行队列',
|
|
317
|
-
' /cancel — 取消所有 pending 指令',
|
|
318
|
-
' /clear — 清空历史记录',
|
|
319
|
-
' /ping — 测试连通性',
|
|
320
|
-
' /screen — 截取 IDE 画面发到飞书',
|
|
321
|
-
' /help — 显示此帮助',
|
|
322
|
-
'',
|
|
323
|
-
'💡 远程模式自动开启全局 Auto-Approve,',
|
|
324
|
-
' Copilot 将自动执行工具调用/编辑/终端操作。',
|
|
325
|
-
].join('\n'));
|
|
322
|
+
logger.info('[Remote/Lark] LarkTransport initialized');
|
|
323
|
+
return _larkTransport;
|
|
324
|
+
} catch (err) {
|
|
325
|
+
logger.warn(`[Remote/Lark] LarkTransport init failed: ${err.message}`);
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
326
328
|
}
|
|
327
329
|
|
|
328
|
-
|
|
330
|
+
/**
|
|
331
|
+
* 生成系统状态文本 (给 LarkTransport 系统操作使用)
|
|
332
|
+
*/
|
|
333
|
+
async function getStatusText() {
|
|
329
334
|
const lines = ['📊 状态面板', ''];
|
|
330
335
|
const now = Math.floor(Date.now() / 1000);
|
|
331
336
|
let ideOk = false;
|
|
332
337
|
|
|
333
|
-
// 1. 飞书 WebSocket
|
|
334
338
|
lines.push(`① 飞书 WebSocket: ${_wsConnected ? '✅ 已连接' : '❌ 断开'}`);
|
|
335
|
-
|
|
336
|
-
// 2. API 服务器
|
|
337
339
|
lines.push('② API 服务器: ✅ 运行中 (port ' + (process.env.PORT || 3000) + ')');
|
|
338
|
-
|
|
339
|
-
// 3. 活跃会话
|
|
340
340
|
lines.push(`③ 活跃会话: ${_activeChatId ? '✅ ' + _activeChatId.slice(0, 16) + '...' : '⚠️ 无活跃会话'}`);
|
|
341
341
|
|
|
342
|
-
// 4. IDE 扩展
|
|
343
342
|
try {
|
|
344
343
|
const db = getDb();
|
|
345
344
|
ensureTable(db);
|
|
@@ -365,7 +364,6 @@ async function handleStatus(_args, messageId) {
|
|
|
365
364
|
}
|
|
366
365
|
}
|
|
367
366
|
|
|
368
|
-
// 5. 队列
|
|
369
367
|
const counts = {};
|
|
370
368
|
for (const s of ['pending', 'running', 'completed', 'timeout']) {
|
|
371
369
|
counts[s] = db.prepare('SELECT COUNT(*) as c FROM remote_commands WHERE status = ?').get(s)?.c || 0;
|
|
@@ -376,165 +374,87 @@ async function handleStatus(_args, messageId) {
|
|
|
376
374
|
lines.push('⑤ 队列: ❓ 查询失败');
|
|
377
375
|
}
|
|
378
376
|
|
|
379
|
-
// 6. 通知通道
|
|
380
377
|
lines.push(`⑥ 通知通道: ${isLarkNotificationReady() ? '✅ 就绪' : '❌ 未就绪'}`);
|
|
381
378
|
|
|
382
|
-
// 总结
|
|
383
379
|
const allGood = _wsConnected && _activeChatId && ideOk && isLarkNotificationReady();
|
|
384
380
|
lines.push('');
|
|
385
381
|
lines.push(allGood ? '🟢 全链路正常,可以远程编程!' : '🟡 部分链路异常,请检查上方标记。');
|
|
386
382
|
|
|
387
|
-
|
|
383
|
+
return lines.join('\n');
|
|
388
384
|
}
|
|
389
385
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const lines = rows.map((r, i) => {
|
|
404
|
-
const icon = r.status === 'running' ? '🔄' : '⏳';
|
|
405
|
-
const cmd = r.command.length > 40 ? r.command.slice(0, 40) + '...' : r.command;
|
|
406
|
-
return `${i + 1}. ${icon} ${cmd} (${r.id.slice(-8)})`;
|
|
407
|
-
});
|
|
386
|
+
/**
|
|
387
|
+
* 写入 IDE 编程指令队列 (供 LarkTransport 的 ide_agent 路由使用)
|
|
388
|
+
*
|
|
389
|
+
* @param {string} command — 自然语言编程指令
|
|
390
|
+
* @param {Object} meta — { chatId, messageId, senderId, senderName }
|
|
391
|
+
* @returns {Promise<{id: string}>}
|
|
392
|
+
*/
|
|
393
|
+
async function enqueueIdeCommand(command, meta = {}) {
|
|
394
|
+
const db = getDb();
|
|
395
|
+
ensureTable(db);
|
|
396
|
+
const id = genId();
|
|
397
|
+
const now = Math.floor(Date.now() / 1000);
|
|
408
398
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
399
|
+
if (meta.chatId) {
|
|
400
|
+
_activeChatId = meta.chatId;
|
|
401
|
+
_persistActiveChatId(meta.chatId);
|
|
412
402
|
}
|
|
413
|
-
}
|
|
414
403
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const now = Math.floor(Date.now() / 1000);
|
|
420
|
-
const result = db.prepare(
|
|
421
|
-
'UPDATE remote_commands SET status = ?, completed_at = ? WHERE status = ?'
|
|
422
|
-
).run('cancelled', now, 'pending');
|
|
423
|
-
await replyLark(messageId, `🗑 已取消 ${result.changes} 条待执行指令。`);
|
|
424
|
-
} catch (err) {
|
|
425
|
-
await replyLark(messageId, `❌ 取消失败: ${err.message}`);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
404
|
+
db.prepare(`
|
|
405
|
+
INSERT INTO remote_commands (id, source, chat_id, message_id, user_id, user_name, command, status, created_at)
|
|
406
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?)
|
|
407
|
+
`).run(id, 'lark', meta.chatId || '', meta.messageId || '', meta.senderId || '', meta.senderName || 'lark_user', command, now);
|
|
428
408
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const db = getDb();
|
|
432
|
-
ensureTable(db);
|
|
433
|
-
const result = db.prepare(
|
|
434
|
-
"DELETE FROM remote_commands WHERE status IN ('completed', 'timeout', 'cancelled')"
|
|
435
|
-
).run();
|
|
436
|
-
await replyLark(messageId, `🧹 已清理 ${result.changes} 条历史记录。`);
|
|
437
|
-
} catch (err) {
|
|
438
|
-
await replyLark(messageId, `❌ 清理失败: ${err.message}`);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
409
|
+
logger.info(`[Remote/Lark] IDE command queued: ${id} — "${command.slice(0, 50)}"`);
|
|
410
|
+
wakeWaiters();
|
|
441
411
|
|
|
442
|
-
|
|
443
|
-
await replyLark(messageId, `🏓 pong! (${new Date().toLocaleTimeString('zh-CN')})`);
|
|
412
|
+
return { id };
|
|
444
413
|
}
|
|
445
414
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
async function handleScreen(_args, messageId) {
|
|
450
|
-
await replyLark(messageId, '📸 正在截取 IDE 画面...');
|
|
451
|
-
try {
|
|
452
|
-
const result = await sendLarkScreenshot('');
|
|
453
|
-
if (!result.success) {
|
|
454
|
-
await replyLark(messageId, `❌ 截图失败: ${result.message}`);
|
|
455
|
-
}
|
|
456
|
-
// 成功时 sendLarkScreenshot 已自动发送图片消息
|
|
457
|
-
} catch (err) {
|
|
458
|
-
await replyLark(messageId, `❌ 截图异常: ${err.message}`);
|
|
459
|
-
}
|
|
415
|
+
function getProjectRoot() {
|
|
416
|
+
const container = getServiceContainer();
|
|
417
|
+
return container.singletons?._projectRoot || process.env.ASD_PROJECT_DIR || process.cwd();
|
|
460
418
|
}
|
|
461
419
|
|
|
462
420
|
// ═══════════════════════════════════════════════════════
|
|
463
|
-
// 飞书消息处理
|
|
421
|
+
// 飞书消息处理 — 通过 LarkTransport 路由
|
|
464
422
|
// ═══════════════════════════════════════════════════════
|
|
465
423
|
|
|
466
424
|
async function handleLarkMessage(data) {
|
|
467
425
|
const message = data?.message || data?.event?.message || {};
|
|
468
|
-
const sender = data?.sender || data?.event?.sender || {};
|
|
469
426
|
const messageId = message.message_id;
|
|
470
427
|
const chatId = message.chat_id;
|
|
471
|
-
const msgType = message.message_type;
|
|
472
428
|
|
|
473
429
|
if (isDuplicate(messageId)) return;
|
|
474
430
|
|
|
475
|
-
//
|
|
476
|
-
const senderId = sender.sender_id?.user_id || sender.sender_id?.open_id || '';
|
|
477
|
-
if (!isUserAllowed(senderId)) {
|
|
478
|
-
logger.warn(`[Remote/Lark] Blocked unauthorized user: ${senderId}`);
|
|
479
|
-
await replyLark(messageId, '🔒 权限不足,你不在授权用户列表中。');
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (msgType !== 'text') {
|
|
484
|
-
await replyLark(messageId, '🤖 目前只支持文本指令。\n发 /help 查看帮助。');
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
let textContent = '';
|
|
489
|
-
try {
|
|
490
|
-
const content = JSON.parse(message.content || '{}');
|
|
491
|
-
textContent = (content.text || '').trim();
|
|
492
|
-
} catch {
|
|
493
|
-
textContent = '';
|
|
494
|
-
}
|
|
495
|
-
if (!textContent) return;
|
|
496
|
-
|
|
497
|
-
textContent = textContent.replace(/@_user_\d+/g, '').trim();
|
|
498
|
-
if (!textContent) return;
|
|
499
|
-
|
|
500
|
-
// ── 系统命令拦截 ──
|
|
501
|
-
const sysHandler = isSystemCommand(textContent);
|
|
502
|
-
if (sysHandler) {
|
|
503
|
-
const args = textContent.split(/\s+/).slice(1).join(' ');
|
|
504
|
-
await sysHandler(args, messageId);
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// ── 写入编程指令队列 ──
|
|
509
|
-
const db = getDb();
|
|
510
|
-
ensureTable(db);
|
|
511
|
-
const id = genId();
|
|
512
|
-
const now = Math.floor(Date.now() / 1000);
|
|
513
|
-
|
|
514
|
-
const userId = sender.sender_id?.user_id || sender.sender_id?.open_id || '';
|
|
515
|
-
const userName = sender.sender_id?.user_id || 'lark_user';
|
|
516
|
-
|
|
517
|
-
// 记录活跃会话(供主动通知使用)
|
|
431
|
+
// 更新活跃会话
|
|
518
432
|
if (chatId) {
|
|
519
433
|
_activeChatId = chatId;
|
|
520
434
|
_persistActiveChatId(chatId);
|
|
521
435
|
}
|
|
522
436
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
437
|
+
// 通过 LarkTransport 路由 (自然语言意图分类)
|
|
438
|
+
const transport = getLarkTransport();
|
|
439
|
+
if (transport) {
|
|
440
|
+
await transport.receive(data);
|
|
441
|
+
} else {
|
|
442
|
+
// Transport 未就绪 → 降级为旧队列模式
|
|
443
|
+
logger.warn('[Remote/Lark] Transport not ready, falling back to queue mode');
|
|
444
|
+
const sender = data?.sender || data?.event?.sender || {};
|
|
445
|
+
let text = '';
|
|
446
|
+
try {
|
|
447
|
+
const content = JSON.parse(message.content || '{}');
|
|
448
|
+
text = (content.text || '').trim().replace(/@_user_\d+/g, '').trim();
|
|
449
|
+
} catch { text = ''; }
|
|
450
|
+
|
|
451
|
+
if (text) {
|
|
452
|
+
const senderId = sender.sender_id?.user_id || sender.sender_id?.open_id || '';
|
|
453
|
+
const senderName = sender.sender_id?.user_id || 'lark_user';
|
|
454
|
+
await enqueueIdeCommand(text, { chatId, messageId, senderId, senderName });
|
|
455
|
+
await replyLark(messageId, '📝 收到,已加入执行队列。(Agent 模式未就绪)');
|
|
456
|
+
}
|
|
457
|
+
}
|
|
538
458
|
}
|
|
539
459
|
|
|
540
460
|
// ═══════════════════════════════════════════════════════
|
|
@@ -833,90 +753,49 @@ async function replyLark(messageId, text) {
|
|
|
833
753
|
}
|
|
834
754
|
|
|
835
755
|
// ═══════════════════════════════════════════════════════
|
|
836
|
-
// IDE 窗口截图 +
|
|
756
|
+
// IDE 窗口截图 + 飞书发送(ScreenCaptureKit,息屏可用)
|
|
837
757
|
// ═══════════════════════════════════════════════════════
|
|
838
758
|
|
|
839
759
|
/**
|
|
840
|
-
*
|
|
841
|
-
*
|
|
760
|
+
* 截取 IDE 窗口截图(通过 ScreenCaptureKit 原生 API,息屏时可用)
|
|
761
|
+
* @param {object} [opts]
|
|
762
|
+
* @param {string} [opts.windowTitle] — 窗口标题关键词(默认 "Code")
|
|
763
|
+
* @returns {Promise<{path: string|null, error: string|null}>}
|
|
842
764
|
*/
|
|
843
|
-
function
|
|
765
|
+
async function captureIDEScreenshot(opts = {}) {
|
|
844
766
|
try {
|
|
845
|
-
const
|
|
846
|
-
import CoreGraphics
|
|
847
|
-
let list = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) as! [[String: Any]]
|
|
848
|
-
var best = 0, bestArea = 0
|
|
849
|
-
for w in list {
|
|
850
|
-
guard let o = w["kCGWindowOwnerName"] as? String, o.contains("Code"),
|
|
851
|
-
let b = w["kCGWindowBounds"] as? [String: Double],
|
|
852
|
-
let width = b["Width"], let height = b["Height"],
|
|
853
|
-
let id = w["kCGWindowNumber"] as? Int,
|
|
854
|
-
width > 100, height > 100 else { continue }
|
|
855
|
-
let area = Int(width * height)
|
|
856
|
-
if area > bestArea { bestArea = area; best = id }
|
|
857
|
-
}
|
|
858
|
-
print(best)
|
|
859
|
-
`;
|
|
860
|
-
const out = execSync(`swift -e '${script.replace(/'/g, "'\\''")}'`, {
|
|
861
|
-
timeout: 10000,
|
|
862
|
-
encoding: 'utf-8',
|
|
863
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
864
|
-
}).trim();
|
|
865
|
-
const wid = parseInt(out, 10);
|
|
866
|
-
logger.info(`[Remote/Screenshot] VSCode windowId: ${wid}`);
|
|
867
|
-
return wid > 0 ? wid : 0;
|
|
868
|
-
} catch (err) {
|
|
869
|
-
const stderr = err.stderr ? err.stderr.toString().slice(0, 200) : '';
|
|
870
|
-
logger.warn(`[Remote/Screenshot] Failed to get VSCode windowId: ${err.message}${stderr ? ' | stderr: ' + stderr : ''}`);
|
|
871
|
-
return 0;
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
/**
|
|
876
|
-
* 截取 IDE 窗口截图
|
|
877
|
-
* @returns {{ path: string|null, error: string|null }} 临时图片文件路径,或错误信息
|
|
878
|
-
*/
|
|
879
|
-
function captureIDEScreenshot() {
|
|
880
|
-
const tmpFile = join(tmpdir(), `asd-screenshot-${Date.now()}.jpg`);
|
|
767
|
+
const { screenshot } = await import('../../platform/ScreenCaptureService.js');
|
|
881
768
|
|
|
882
|
-
|
|
883
|
-
|
|
769
|
+
// 统一使用窗口截取(desktopIndependentWindow,亮屏/息屏均可用)
|
|
770
|
+
// 优先截取 IDE 窗口
|
|
771
|
+
const windowTitle = opts.windowTitle || 'Code';
|
|
772
|
+
let result = await screenshot({ windowTitle, format: 'png' });
|
|
884
773
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
774
|
+
// IDE 窗口未找到 → 尝试常见 IDE 名称
|
|
775
|
+
if (!result.success) {
|
|
776
|
+
for (const alt of ['Visual Studio', 'Cursor', 'Xcode', 'IntelliJ', 'WebStorm']) {
|
|
777
|
+
if (alt.toLowerCase() === windowTitle.toLowerCase()) continue;
|
|
778
|
+
result = await screenshot({ windowTitle: alt, format: 'png' });
|
|
779
|
+
if (result.success) break;
|
|
780
|
+
}
|
|
889
781
|
}
|
|
890
|
-
} catch { /* swift failed, skip window capture */ }
|
|
891
782
|
|
|
892
|
-
|
|
893
|
-
|
|
783
|
+
// 仍未找到 → 不指定窗口名,Swift 工具会自动选最大窗口
|
|
784
|
+
if (!result.success) {
|
|
785
|
+
logger.info(`[Remote/Screenshot] IDE window not found, capturing largest available window`);
|
|
786
|
+
result = await screenshot({ format: 'png' });
|
|
787
|
+
}
|
|
894
788
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
execSync(cmd, { timeout: 8000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
899
|
-
if (existsSync(tmpFile)) {
|
|
900
|
-
const { size } = statSync(tmpFile);
|
|
901
|
-
if (size > 0) {
|
|
902
|
-
logger.info(`[Remote/Screenshot] Captured via ${label}: ${tmpFile} (${size} bytes)`);
|
|
903
|
-
return { path: tmpFile, error: null };
|
|
904
|
-
}
|
|
905
|
-
// 文件存在但为空
|
|
906
|
-
logger.warn(`[Remote/Screenshot] ${label}: file created but empty`);
|
|
907
|
-
try { unlinkSync(tmpFile); } catch { /* ignore */ }
|
|
908
|
-
} else {
|
|
909
|
-
logger.warn(`[Remote/Screenshot] ${label}: file not created`);
|
|
910
|
-
}
|
|
911
|
-
} catch (err) {
|
|
912
|
-
const stderr = err.stderr ? err.stderr.toString().slice(0, 300) : '';
|
|
913
|
-
const detail = `${err.message}${stderr ? ' | stderr: ' + stderr : ''}`;
|
|
914
|
-
logger.warn(`[Remote/Screenshot] ${label} failed: ${detail}`);
|
|
915
|
-
// 继续尝试下一种方案
|
|
789
|
+
if (result.success) {
|
|
790
|
+
logger.info(`[Remote/Screenshot] Captured: ${result.path} (${result.width}x${result.height})`);
|
|
791
|
+
return { path: result.path, error: null };
|
|
916
792
|
}
|
|
917
|
-
}
|
|
918
793
|
|
|
919
|
-
|
|
794
|
+
return { path: null, error: result.error || 'Screenshot failed' };
|
|
795
|
+
} catch (err) {
|
|
796
|
+
logger.warn(`[Remote/Screenshot] ScreenCaptureKit error: ${err.message}`);
|
|
797
|
+
return { path: null, error: err.message };
|
|
798
|
+
}
|
|
920
799
|
}
|
|
921
800
|
|
|
922
801
|
/**
|
|
@@ -1007,8 +886,8 @@ export async function sendLarkScreenshot(caption = '') {
|
|
|
1007
886
|
return { success: false, message: 'Lark not connected or no active chat' };
|
|
1008
887
|
}
|
|
1009
888
|
|
|
1010
|
-
// 1.
|
|
1011
|
-
const capture = captureIDEScreenshot();
|
|
889
|
+
// 1. 截图(ScreenCaptureKit,息屏可用)
|
|
890
|
+
const capture = await captureIDEScreenshot();
|
|
1012
891
|
if (!capture.path) {
|
|
1013
892
|
return { success: false, message: capture.error || 'Screenshot capture failed' };
|
|
1014
893
|
}
|
|
@@ -91,58 +91,4 @@ router.post(
|
|
|
91
91
|
})
|
|
92
92
|
);
|
|
93
93
|
|
|
94
|
-
/**
|
|
95
|
-
* POST /api/v1/violations/rules/generate
|
|
96
|
-
* AI 根据语义描述生成 Guard 规则
|
|
97
|
-
*/
|
|
98
|
-
router.post(
|
|
99
|
-
'/rules/generate',
|
|
100
|
-
asyncHandler(async (req, res) => {
|
|
101
|
-
const { description } = req.body;
|
|
102
|
-
|
|
103
|
-
if (!description || typeof description !== 'string' || !description.trim()) {
|
|
104
|
-
throw new ValidationError('description is required');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const container = getServiceContainer();
|
|
108
|
-
const chatAgent = container.get('chatAgent');
|
|
109
|
-
const result = await chatAgent.executeTool('generate_guard_rule', {
|
|
110
|
-
description: description.trim(),
|
|
111
|
-
language: 'objc',
|
|
112
|
-
severity: 'warning',
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
if (result?.error) {
|
|
116
|
-
throw new ValidationError(result.error);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// 从 generate_guard_rule 工具返回的 rule 中提取并规范化
|
|
120
|
-
const rule = result.rule || result;
|
|
121
|
-
|
|
122
|
-
const normalized = {
|
|
123
|
-
ruleId: String(rule.name || rule.ruleId || '')
|
|
124
|
-
.trim()
|
|
125
|
-
.replace(/\s+/g, '-'),
|
|
126
|
-
message: String(rule.description || rule.message || '').trim(),
|
|
127
|
-
severity: rule.severity === 'error' ? 'error' : 'warning',
|
|
128
|
-
pattern: String(rule.pattern || '').trim(),
|
|
129
|
-
languages:
|
|
130
|
-
Array.isArray(rule.languages) && rule.languages.length > 0
|
|
131
|
-
? rule.languages
|
|
132
|
-
: ['objc', 'swift'],
|
|
133
|
-
note: rule.note != null ? String(rule.note).trim() : rule.description_cn || '',
|
|
134
|
-
dimension: ['file', 'target', 'project'].includes(rule.dimension) ? rule.dimension : '',
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
if (!normalized.ruleId || !normalized.message || !normalized.pattern) {
|
|
138
|
-
throw new ValidationError('AI 返回的规则缺少 ruleId、message 或 pattern');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
res.json({
|
|
142
|
-
success: true,
|
|
143
|
-
data: normalized,
|
|
144
|
-
});
|
|
145
|
-
})
|
|
146
|
-
);
|
|
147
|
-
|
|
148
94
|
export default router;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* SSE Session Manager — 基于 EventSource 的流式会话管理
|
|
3
3
|
*
|
|
4
4
|
* 架构:
|
|
5
|
-
* POST /chat/stream → 创建 session + 后台执行
|
|
5
|
+
* POST /chat/stream → 创建 session + 后台执行 AgentRuntime → 返回 { sessionId }
|
|
6
6
|
* GET /chat/events/:sessionId → EventSource 端点, 回放缓冲事件 + 实时推送
|
|
7
7
|
*
|
|
8
8
|
* 为什么不用 fetch + ReadableStream:
|
|
@@ -2,9 +2,10 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import winston from 'winston';
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// Agent 系统相关标签 — 终端高亮显示
|
|
6
6
|
const AGENT_TAGS = [
|
|
7
|
-
'
|
|
7
|
+
'AgentRuntime',
|
|
8
|
+
'AgentFactory',
|
|
8
9
|
'ToolRegistry',
|
|
9
10
|
'SignalCollector',
|
|
10
11
|
'SkillAdvisor',
|
|
@@ -38,7 +39,7 @@ const LEVEL_COLORS = {
|
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* 精简 Console 格式
|
|
41
|
-
* -
|
|
42
|
+
* - Agent 相关日志: 高亮 cyan/magenta,显示完整信息
|
|
42
43
|
* - warn/error: 醒目颜色完整显示
|
|
43
44
|
* - HTTP 日志: 精简并降低视觉权重
|
|
44
45
|
* - 其他 info/debug: 一行精简格式
|
|
@@ -65,7 +66,7 @@ const compactConsoleFormat = winston.format.printf(({ level, message, timestamp,
|
|
|
65
66
|
);
|
|
66
67
|
|
|
67
68
|
if (isAgentLog) {
|
|
68
|
-
//
|
|
69
|
+
// Agent 日志 — 高亮显示
|
|
69
70
|
const metaStr =
|
|
70
71
|
Object.keys(meta).length > 0
|
|
71
72
|
? ` ${JSON.stringify(meta, null, 0).replace(/"/g, '').replace(/,/g, ', ')}`
|
|
@@ -120,8 +120,9 @@ export class PerformanceMonitor {
|
|
|
120
120
|
this.metrics.responseTimes.shift();
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
//
|
|
124
|
-
|
|
123
|
+
// 慢请求记录(排除设计上就是长耗时的 long-poll 端点)
|
|
124
|
+
const isLongPoll = route.includes('/remote/wait');
|
|
125
|
+
if (duration > this.config.slowRequestThreshold && !isLongPoll) {
|
|
125
126
|
this.metrics.slowRequests.push({
|
|
126
127
|
...requestData,
|
|
127
128
|
duration,
|