team-anya-cli 0.1.8 → 1.0.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.
Files changed (168) hide show
  1. package/README.md +1 -37
  2. package/package.json +6 -37
  3. package/anya/prompts/execution-guides/git-delivery.md +0 -38
  4. package/anya/prompts/execution-guides/testing-and-self-heal.md +0 -28
  5. package/anya/prompts/protocols/brief-assembly.md +0 -55
  6. package/anya/prompts/protocols/report.md +0 -175
  7. package/anya/prompts/protocols/review.md +0 -90
  8. package/anya/prompts/task-claude-md.template.md +0 -32
  9. package/apps/server/dist/broker/cc-broker.js +0 -261
  10. package/apps/server/dist/cli.js +0 -296
  11. package/apps/server/dist/config.js +0 -76
  12. package/apps/server/dist/daemon.js +0 -51
  13. package/apps/server/dist/franky/context-builder.js +0 -160
  14. package/apps/server/dist/franky/franky-mcp-server.js +0 -107
  15. package/apps/server/dist/franky/franky-orchestrator.js +0 -450
  16. package/apps/server/dist/franky/index.js +0 -5
  17. package/apps/server/dist/franky/topic-router.js +0 -16
  18. package/apps/server/dist/gateway/chat-sync.js +0 -135
  19. package/apps/server/dist/gateway/command-router.js +0 -114
  20. package/apps/server/dist/gateway/commands/cancel.js +0 -32
  21. package/apps/server/dist/gateway/commands/help.js +0 -16
  22. package/apps/server/dist/gateway/commands/index.js +0 -26
  23. package/apps/server/dist/gateway/commands/restart.js +0 -34
  24. package/apps/server/dist/gateway/commands/status.js +0 -34
  25. package/apps/server/dist/gateway/commands/tasks.js +0 -33
  26. package/apps/server/dist/gateway/feishu-sender.js +0 -410
  27. package/apps/server/dist/gateway/feishu-ws.js +0 -256
  28. package/apps/server/dist/gateway/http.js +0 -1019
  29. package/apps/server/dist/gateway/media-downloader.js +0 -149
  30. package/apps/server/dist/gateway/message-events.js +0 -10
  31. package/apps/server/dist/gateway/message-intake.js +0 -67
  32. package/apps/server/dist/gateway/message-queue.js +0 -118
  33. package/apps/server/dist/gateway/session-reader.js +0 -142
  34. package/apps/server/dist/gateway/ws-push.js +0 -115
  35. package/apps/server/dist/loid/brain.js +0 -105
  36. package/apps/server/dist/loid/clarifier.js +0 -162
  37. package/apps/server/dist/loid/context-builder.js +0 -413
  38. package/apps/server/dist/loid/mcp-server.js +0 -106
  39. package/apps/server/dist/loid/memory-settler.js +0 -189
  40. package/apps/server/dist/loid/opportunity-manager.js +0 -148
  41. package/apps/server/dist/loid/profile-updater.js +0 -179
  42. package/apps/server/dist/loid/reporter.js +0 -148
  43. package/apps/server/dist/loid/schemas.js +0 -117
  44. package/apps/server/dist/loid/self-calibrator.js +0 -314
  45. package/apps/server/dist/loid/session-manager.js +0 -301
  46. package/apps/server/dist/loid/session.js +0 -271
  47. package/apps/server/dist/loid/worktree-manager.js +0 -191
  48. package/apps/server/dist/main.js +0 -393
  49. package/apps/server/dist/tracing/index.js +0 -2
  50. package/apps/server/dist/tracing/trace-context.js +0 -92
  51. package/apps/server/dist/types/message.js +0 -2
  52. package/apps/server/dist/yor/yor-mcp-server.js +0 -104
  53. package/apps/server/dist/yor/yor-orchestrator.js +0 -233
  54. package/apps/web/dist/assets/index-BiiEB0qZ.css +0 -1
  55. package/apps/web/dist/assets/index-D1AK5ZEE.js +0 -798
  56. package/apps/web/dist/index.html +0 -13
  57. package/packages/cc-client/dist/claude-code-backend.js +0 -664
  58. package/packages/cc-client/dist/index.js +0 -2
  59. package/packages/cc-client/package.json +0 -11
  60. package/packages/core/dist/constants.js +0 -59
  61. package/packages/core/dist/errors.js +0 -35
  62. package/packages/core/dist/index.js +0 -7
  63. package/packages/core/dist/office-init.js +0 -101
  64. package/packages/core/dist/scope/checker.js +0 -114
  65. package/packages/core/dist/scope/defaults.js +0 -55
  66. package/packages/core/dist/scope/index.js +0 -3
  67. package/packages/core/dist/state-machine.js +0 -85
  68. package/packages/core/dist/types/audit.js +0 -12
  69. package/packages/core/dist/types/backend.js +0 -2
  70. package/packages/core/dist/types/commitment.js +0 -17
  71. package/packages/core/dist/types/communication.js +0 -18
  72. package/packages/core/dist/types/index.js +0 -8
  73. package/packages/core/dist/types/opportunity.js +0 -27
  74. package/packages/core/dist/types/org.js +0 -26
  75. package/packages/core/dist/types/task.js +0 -46
  76. package/packages/core/package.json +0 -10
  77. package/packages/db/dist/client.js +0 -69
  78. package/packages/db/dist/index.js +0 -691
  79. package/packages/db/dist/schema/audit-events.js +0 -13
  80. package/packages/db/dist/schema/cc-sessions.js +0 -14
  81. package/packages/db/dist/schema/chats.js +0 -33
  82. package/packages/db/dist/schema/commitments.js +0 -18
  83. package/packages/db/dist/schema/communication-events.js +0 -14
  84. package/packages/db/dist/schema/index.js +0 -13
  85. package/packages/db/dist/schema/message-log.js +0 -20
  86. package/packages/db/dist/schema/opportunities.js +0 -23
  87. package/packages/db/dist/schema/org.js +0 -36
  88. package/packages/db/dist/schema/projects.js +0 -23
  89. package/packages/db/dist/schema/tasks.js +0 -48
  90. package/packages/db/dist/schema/topics.js +0 -20
  91. package/packages/db/dist/schema/trace-spans.js +0 -19
  92. package/packages/db/package.json +0 -12
  93. package/packages/db/src/migrations/0000_baseline.sql +0 -251
  94. package/packages/db/src/migrations/meta/_journal.json +0 -13
  95. package/packages/mcp-tools/dist/index.js +0 -41
  96. package/packages/mcp-tools/dist/layer1/audit-append.js +0 -38
  97. package/packages/mcp-tools/dist/layer1/audit-query.js +0 -51
  98. package/packages/mcp-tools/dist/layer1/memory-brief.js +0 -168
  99. package/packages/mcp-tools/dist/layer1/memory-context.js +0 -124
  100. package/packages/mcp-tools/dist/layer1/memory-digest.js +0 -126
  101. package/packages/mcp-tools/dist/layer1/memory-forget.js +0 -108
  102. package/packages/mcp-tools/dist/layer1/memory-learn.js +0 -63
  103. package/packages/mcp-tools/dist/layer1/memory-recall.js +0 -287
  104. package/packages/mcp-tools/dist/layer1/memory-reflect.js +0 -80
  105. package/packages/mcp-tools/dist/layer1/memory-remember.js +0 -119
  106. package/packages/mcp-tools/dist/layer1/memory-search.js +0 -263
  107. package/packages/mcp-tools/dist/layer1/memory-write.js +0 -21
  108. package/packages/mcp-tools/dist/layer1/org-lookup.js +0 -47
  109. package/packages/mcp-tools/dist/layer1/project-get.js +0 -28
  110. package/packages/mcp-tools/dist/layer1/project-list.js +0 -20
  111. package/packages/mcp-tools/dist/layer1/report-daily.js +0 -68
  112. package/packages/mcp-tools/dist/layer1/task-get.js +0 -29
  113. package/packages/mcp-tools/dist/layer1/task-update.js +0 -34
  114. package/packages/mcp-tools/dist/layer2/franky/topic-checkpoint.js +0 -43
  115. package/packages/mcp-tools/dist/layer2/franky/topic-escalate.js +0 -19
  116. package/packages/mcp-tools/dist/layer2/loid/decision-log.js +0 -15
  117. package/packages/mcp-tools/dist/layer2/loid/decision-no-action.js +0 -15
  118. package/packages/mcp-tools/dist/layer2/loid/delivery-create-pr.js +0 -30
  119. package/packages/mcp-tools/dist/layer2/loid/delivery-share.js +0 -12
  120. package/packages/mcp-tools/dist/layer2/loid/delivery-submit.js +0 -77
  121. package/packages/mcp-tools/dist/layer2/loid/delivery-upload.js +0 -18
  122. package/packages/mcp-tools/dist/layer2/loid/project-remove.js +0 -16
  123. package/packages/mcp-tools/dist/layer2/loid/project-upsert.js +0 -33
  124. package/packages/mcp-tools/dist/layer2/loid/task-dispatch.js +0 -196
  125. package/packages/mcp-tools/dist/layer2/loid/task-lookup.js +0 -38
  126. package/packages/mcp-tools/dist/layer2/loid/topic-close.js +0 -22
  127. package/packages/mcp-tools/dist/layer2/loid/topic-create.js +0 -56
  128. package/packages/mcp-tools/dist/layer2/loid/yor-approve.js +0 -8
  129. package/packages/mcp-tools/dist/layer2/loid/yor-kill.js +0 -7
  130. package/packages/mcp-tools/dist/layer2/loid/yor-rework.js +0 -7
  131. package/packages/mcp-tools/dist/layer2/loid/yor-spawn.js +0 -15
  132. package/packages/mcp-tools/dist/layer2/loid/yor-status.js +0 -8
  133. package/packages/mcp-tools/dist/layer2/yor/task-block.js +0 -11
  134. package/packages/mcp-tools/dist/layer2/yor/task-deliver.js +0 -35
  135. package/packages/mcp-tools/dist/layer2/yor/task-progress.js +0 -21
  136. package/packages/mcp-tools/dist/layer3/adapters/feishu-adapter.js +0 -192
  137. package/packages/mcp-tools/dist/layer3/adapters/types.js +0 -28
  138. package/packages/mcp-tools/dist/layer3/channel-receive.js +0 -11
  139. package/packages/mcp-tools/dist/layer3/channel-send.js +0 -91
  140. package/packages/mcp-tools/dist/layer3/file-upload.js +0 -44
  141. package/packages/mcp-tools/dist/registry.js +0 -871
  142. package/packages/mcp-tools/package.json +0 -13
  143. package/workspace/.claude/settings.local.json +0 -9
  144. package/workspace/.mcp.json +0 -12
  145. package/workspace/CHARTER.md +0 -76
  146. package/workspace/CLAUDE.md +0 -58
  147. package/workspace/PROTOCOL.md +0 -160
  148. package/workspace/TOOLS.md +0 -470
  149. package/workspace/audit/.gitkeep +0 -0
  150. package/workspace/franky/CLAUDE.md +0 -37
  151. package/workspace/franky/PLAYBOOK.md +0 -215
  152. package/workspace/franky/PROFILE.md +0 -80
  153. package/workspace/loid/CLAUDE.md +0 -12
  154. package/workspace/loid/PLAYBOOK.md +0 -198
  155. package/workspace/loid/PROFILE.md +0 -78
  156. package/workspace/memory/commitments/.gitkeep +0 -0
  157. package/workspace/memory/execution/.gitkeep +0 -0
  158. package/workspace/memory/people/.gitkeep +0 -0
  159. package/workspace/memory/projects/.gitkeep +0 -0
  160. package/workspace/memory/self/.gitkeep +0 -0
  161. package/workspace/reference/identity/.gitkeep +0 -0
  162. package/workspace/reference/org/escalation.yaml +0 -24
  163. package/workspace/reference/org/ownership.yaml +0 -28
  164. package/workspace/reports/.gitkeep +0 -0
  165. package/workspace/yor/CLAUDE.md +0 -22
  166. package/workspace/yor/PLAYBOOK.md +0 -73
  167. package/workspace/yor/PROFILE.md +0 -52
  168. package/workspace/yor/SELF-HEAL.md +0 -39
@@ -1,191 +0,0 @@
1
- import { execFile as execFileCb } from 'node:child_process';
2
- import { mkdir, access, readFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import { promisify } from 'node:util';
5
- import { getAllProjects, upsertProject, getProjectRepos, syncProjectRepos } from '@team-anya/db';
6
- const execFile = promisify(execFileCb);
7
- export class WorktreeManager {
8
- workspacePath;
9
- projectsConfigPath;
10
- reposPath;
11
- db;
12
- logger;
13
- config = null;
14
- constructor(deps) {
15
- this.workspacePath = deps.workspacePath;
16
- this.projectsConfigPath = deps.projectsConfigPath;
17
- this.reposPath = deps.reposPath;
18
- this.db = deps.db;
19
- this.logger = deps.logger ?? { info: console.log, error: console.error };
20
- }
21
- /**
22
- * 加载项目配置
23
- *
24
- * 优先从 DB 读取。如果 DB 为空且 JSON 文件存在,则从 JSON 导入到 DB。
25
- */
26
- async loadConfig() {
27
- if (this.config !== null)
28
- return this.config;
29
- // 先从 DB 读取
30
- const dbProjects = getAllProjects(this.db);
31
- if (dbProjects.length > 0) {
32
- this.config = {
33
- projects: dbProjects.map(p => {
34
- const repos = getProjectRepos(this.db, p.project_id);
35
- return {
36
- projectId: p.project_id,
37
- name: p.name,
38
- description: p.description ?? undefined,
39
- repos: repos.map(r => ({
40
- name: r.name,
41
- gitUrl: r.git_url ?? undefined,
42
- repoPath: r.repo_path ?? undefined,
43
- defaultBranch: r.default_branch ?? undefined,
44
- })),
45
- claudeMd: p.claude_md ?? undefined,
46
- platform: p.platform ?? 'github',
47
- };
48
- }),
49
- };
50
- return this.config;
51
- }
52
- // DB 为空,尝试从 JSON 文件导入
53
- try {
54
- await access(this.projectsConfigPath);
55
- const content = await readFile(this.projectsConfigPath, 'utf-8');
56
- const parsed = JSON.parse(content);
57
- const jsonProjects = (parsed.projects ?? []);
58
- // 导入到 DB(项目 + repos 关联表)
59
- for (const p of jsonProjects) {
60
- upsertProject(this.db, {
61
- project_id: p.projectId,
62
- name: p.name,
63
- description: p.description,
64
- platform: p.platform ?? 'github',
65
- claude_md: p.claudeMd,
66
- });
67
- syncProjectRepos(this.db, p.projectId, p.repos.map(r => ({
68
- name: r.name,
69
- git_url: r.gitUrl,
70
- repo_path: r.repoPath,
71
- default_branch: r.defaultBranch,
72
- })));
73
- }
74
- this.logger.info(`[WorktreeManager] 从 JSON 导入 ${jsonProjects.length} 个项目到 DB`);
75
- this.config = { projects: jsonProjects };
76
- this.validateUniqueIds(this.config);
77
- }
78
- catch (err) {
79
- if (err instanceof Error && err.message.startsWith('ID 冲突')) {
80
- throw err;
81
- }
82
- this.logger.info('[WorktreeManager] 项目注册表为空,使用空列表');
83
- this.config = { projects: [] };
84
- }
85
- return this.config;
86
- }
87
- /**
88
- * 向后兼容:返回 projects 列表
89
- */
90
- async loadProjects() {
91
- const config = await this.loadConfig();
92
- return config.projects;
93
- }
94
- /**
95
- * 重新从 DB 加载(清除缓存)
96
- */
97
- invalidateCache() {
98
- this.config = null;
99
- }
100
- validateUniqueIds(config) {
101
- const ids = new Set();
102
- for (const p of config.projects) {
103
- if (ids.has(p.projectId)) {
104
- throw new Error(`ID 冲突: "${p.projectId}" 在配置中重复出现`);
105
- }
106
- ids.add(p.projectId);
107
- }
108
- }
109
- /**
110
- * 解析 repoPath:如果配置中未指定,使用 {reposPath}/{name} 作为默认值
111
- */
112
- resolveRepoPath(repoPath, name) {
113
- return repoPath ?? join(this.reposPath, name);
114
- }
115
- findProject(id) {
116
- return this.config?.projects.find(p => p.projectId === id);
117
- }
118
- /**
119
- * 自动初始化:遍历所有配置的仓库,缺失的自动 clone
120
- * 适合在服务启动时或手动初始化时调用
121
- */
122
- async ensureRepos() {
123
- await this.loadConfig();
124
- await mkdir(this.reposPath, { recursive: true });
125
- const result = { cloned: [], existed: [], failed: [] };
126
- // 收集所有需要检查的仓库
127
- for (const project of this.config.projects) {
128
- for (const repo of project.repos) {
129
- const repoPath = this.resolveRepoPath(repo.repoPath, repo.name);
130
- // 检查是否已存在
131
- try {
132
- await access(join(repoPath, '.git'));
133
- result.existed.push(repo.name);
134
- continue;
135
- }
136
- catch {
137
- // 不存在,需要 clone
138
- }
139
- if (!repo.gitUrl) {
140
- result.failed.push({
141
- name: repo.name,
142
- error: `仓库不存在且未配置 gitUrl: ${repoPath}`,
143
- });
144
- continue;
145
- }
146
- try {
147
- this.logger.info(`[WorktreeManager] 克隆 ${repo.name}: ${repo.gitUrl}`);
148
- const cloneArgs = ['clone'];
149
- if (repo.defaultBranch) {
150
- cloneArgs.push('--branch', repo.defaultBranch);
151
- }
152
- cloneArgs.push(repo.gitUrl, repoPath);
153
- await execFile('git', cloneArgs);
154
- result.cloned.push(repo.name);
155
- this.logger.info(`[WorktreeManager] 克隆完成: ${repo.name}`);
156
- }
157
- catch (err) {
158
- const msg = err instanceof Error ? err.message : String(err);
159
- result.failed.push({ name: repo.name, error: msg });
160
- this.logger.error(`[WorktreeManager] 克隆失败 (${repo.name}):`, err);
161
- }
162
- }
163
- }
164
- return result;
165
- }
166
- /**
167
- * 获取项目配置(不执行任何 git 命令)
168
- * 找到项目 → 返回 project 模式(repos 数组)
169
- * 未找到 → 返回 adhoc 模式
170
- */
171
- async getProjectConfig(projectId) {
172
- await this.loadConfig();
173
- if (projectId) {
174
- const project = this.findProject(projectId);
175
- if (project) {
176
- return {
177
- mode: 'project',
178
- platform: project.platform ?? 'github',
179
- repos: project.repos.map(repo => ({
180
- name: repo.name,
181
- repo_path: this.resolveRepoPath(repo.repoPath, repo.name),
182
- default_branch: repo.defaultBranch ?? 'main',
183
- })),
184
- claudeMd: project.claudeMd,
185
- };
186
- }
187
- }
188
- return { mode: 'adhoc', platform: 'local' };
189
- }
190
- }
191
- //# sourceMappingURL=worktree-manager.js.map
@@ -1,393 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { existsSync, mkdirSync } from 'node:fs';
3
- import { join, resolve } from 'node:path';
4
- import Fastify from 'fastify';
5
- import fastifyCors from '@fastify/cors';
6
- import fastifyStatic from '@fastify/static';
7
- import { createDB, getTask as dbGetTask, getTopic as dbGetTopic } from '@team-anya/db';
8
- import { memoryRemember, auditAppend, ChannelRegistry, FeishuAdapter } from '@team-anya/mcp-tools';
9
- import { ensureOffice, TaskStatus } from '@team-anya/core';
10
- import { loadConfig } from './config.js';
11
- import { registerRoutes } from './gateway/http.js';
12
- import { CCBroker } from './broker/cc-broker.js';
13
- import { YorOrchestrator } from './yor/yor-orchestrator.js';
14
- import { FeishuWSClient } from './gateway/feishu-ws.js';
15
- import { MediaDownloader } from './gateway/media-downloader.js';
16
- import { ChatSyncService } from './gateway/chat-sync.js';
17
- import { FeishuSender } from './gateway/feishu-sender.js';
18
- import { MessageIntake } from './gateway/message-intake.js';
19
- import { CommandRouter } from './gateway/command-router.js';
20
- import { registerBuiltinCommands } from './gateway/commands/index.js';
21
- import { MessageQueue } from './gateway/message-queue.js';
22
- import { MemorySettler } from './loid/memory-settler.js';
23
- import { LoidBrain } from './loid/brain.js';
24
- import { LoidContextBuilder } from './loid/context-builder.js';
25
- import { Clarifier } from './loid/clarifier.js';
26
- import { WorktreeManager } from './loid/worktree-manager.js';
27
- import { FrankyOrchestrator } from './franky/franky-orchestrator.js';
28
- import { FrankyTopicRouter } from './franky/topic-router.js';
29
- export async function buildServer() {
30
- const config = loadConfig();
31
- // 初始化 office 目录(从模板源复制缺失的文件)
32
- await ensureOffice(config.WORKSPACE_PATH, resolve(config.OFFICE_TEMPLATE_DIR), { info: (msg) => console.log(`[office-init] ${msg}`) });
33
- const db = createDB(config.SQLITE_PATH);
34
- // 日志配置:stdout + 文件双写(按日期+大小分割)
35
- let loggerConfig = false;
36
- if (config.NODE_ENV !== 'test') {
37
- const logDir = resolve(config.LOG_DIR);
38
- mkdirSync(logDir, { recursive: true });
39
- loggerConfig = {
40
- level: 'info',
41
- transport: {
42
- targets: [
43
- { target: 'pino-pretty', options: { destination: 1 }, level: 'info' },
44
- {
45
- target: 'pino-roll',
46
- options: {
47
- file: join(logDir, 'anya'),
48
- frequency: 'daily',
49
- limit: { count: 14 },
50
- mkdir: true,
51
- },
52
- level: 'info',
53
- },
54
- ],
55
- },
56
- };
57
- }
58
- const app = Fastify({
59
- logger: loggerConfig,
60
- disableRequestLogging: true,
61
- });
62
- // CORS
63
- if (config.NODE_ENV === 'development') {
64
- await app.register(fastifyCors, { origin: 'http://localhost:5173' });
65
- }
66
- // 静态文件(兼容 monorepo 开发 + npm 全局安装两种目录结构)
67
- const webDistCandidates = [
68
- resolve(import.meta.dirname, '../../web/dist'), // npm 包: apps/server/dist/ → apps/web/dist/
69
- resolve(import.meta.dirname, '../../../apps/web/dist'), // monorepo 开发: src/ 下通过 tsx 运行
70
- ];
71
- const webDistPath = webDistCandidates.find(p => existsSync(p)) ?? webDistCandidates[0];
72
- const serveStatic = config.NODE_ENV !== 'test' && existsSync(webDistPath);
73
- if (serveStatic) {
74
- await app.register(fastifyStatic, {
75
- root: webDistPath,
76
- prefix: '/',
77
- });
78
- }
79
- // FeishuSender + 共享 Lark Client
80
- let larkClient;
81
- const feishuSender = (config.FEISHU_APP_ID && config.FEISHU_APP_SECRET)
82
- ? new FeishuSender({ appId: config.FEISHU_APP_ID, appSecret: config.FEISHU_APP_SECRET, db })
83
- : null;
84
- if (config.FEISHU_APP_ID && config.FEISHU_APP_SECRET) {
85
- const Lark = await import('@larksuiteoapi/node-sdk');
86
- larkClient = new Lark.Client({
87
- appId: config.FEISHU_APP_ID,
88
- appSecret: config.FEISHU_APP_SECRET,
89
- });
90
- }
91
- // ChatSyncService(飞书群信息同步)
92
- const chatSyncService = larkClient ? new ChatSyncService(larkClient, db) : null;
93
- // ChannelRegistry(通道注册表)
94
- const channelRegistry = new ChannelRegistry();
95
- if (feishuSender) {
96
- channelRegistry.register(new FeishuAdapter(feishuSender));
97
- app.log.info('已注册飞书通道适配器');
98
- }
99
- // MemorySettler
100
- const memorySettler = new MemorySettler({
101
- readFile: async (path) => {
102
- try {
103
- return await readFile(path, 'utf-8');
104
- }
105
- catch {
106
- return null;
107
- }
108
- },
109
- memoryRemember: (input) => memoryRemember(db, config.WORKSPACE_PATH, input),
110
- auditAppend: (input) => auditAppend(db, config.WORKSPACE_PATH, input).then(() => { }),
111
- });
112
- // 任务完成后处理回调
113
- const onTaskComplete = async (taskId, exitCode) => {
114
- if (exitCode !== 0)
115
- return;
116
- try {
117
- const taskDir = join(config.WORKSPACE_PATH, 'yor', 'tasks', taskId);
118
- await memorySettler.settle(taskId, taskDir);
119
- }
120
- catch (err) {
121
- console.error('经验沉淀失败:', err);
122
- }
123
- };
124
- // Clarifier(保留用于 HTTP API)
125
- const clarifier = new Clarifier({ db });
126
- // WorktreeManager
127
- const worktreeManager = new WorktreeManager({
128
- workspacePath: resolve(config.WORKSPACE_PATH),
129
- projectsConfigPath: resolve(config.PROJECTS_CONFIG_PATH),
130
- reposPath: resolve(config.REPOS_PATH),
131
- db,
132
- });
133
- // 自动初始化仓库(首次使用时 clone 缺失的仓库)
134
- const ensureResult = await worktreeManager.ensureRepos();
135
- if (ensureResult.cloned.length > 0) {
136
- app.log.info(`自动 clone 仓库: ${ensureResult.cloned.join(', ')}`);
137
- }
138
- if (ensureResult.failed.length > 0) {
139
- for (const f of ensureResult.failed) {
140
- app.log.error(`仓库初始化失败 (${f.name}): ${f.error}`);
141
- }
142
- }
143
- // ContextBuilder
144
- const contextBuilder = new LoidContextBuilder({
145
- db,
146
- workspacePath: config.WORKSPACE_PATH,
147
- worktreeManager,
148
- });
149
- // CCBroker — 统一 CC 进程管理器
150
- const broker = new CCBroker({
151
- maxLoidInstances: 2,
152
- maxYorInstances: config.MAX_YOR_CONCURRENCY,
153
- maxFrankyInstances: 2,
154
- logger: app.log,
155
- });
156
- // YorOrchestrator — 替代 Yor 子进程,通过 CCBroker 管理 Yor CC
157
- const yorOrchestrator = new YorOrchestrator(broker, {
158
- binary: config.CLAUDE_CODE_BINARY,
159
- workspacePath: config.WORKSPACE_PATH,
160
- db,
161
- logger: app.log,
162
- });
163
- // ── Franky Orchestrator ──
164
- const frankyOrchestrator = new FrankyOrchestrator(broker, {
165
- binary: config.CLAUDE_CODE_BINARY,
166
- workspacePath: config.WORKSPACE_PATH,
167
- db,
168
- channelRegistry,
169
- onMessageSent: feishuSender
170
- ? (chatId) => { feishuSender.clearTypingReaction(chatId).catch(() => { }); }
171
- : undefined,
172
- onTopicEscalated: (topicId, reason, category) => {
173
- // 通过飞书通知让相关人感知升级事件(审计日志已由 topic.escalate 工具写入)
174
- if (feishuSender) {
175
- const topic = dbGetTopic(db, topicId);
176
- const chatId = topic?.chat_id;
177
- if (chatId) {
178
- feishuSender.sendText({
179
- receiveIdType: 'chat_id',
180
- receiveId: chatId,
181
- text: `[Franky 升级] 专项「${topic?.title ?? topicId}」需要介入(${category}):${reason}`,
182
- }).catch(err => {
183
- app.log.error({ err, topicId }, 'Franky 升级飞书通知发送失败');
184
- });
185
- }
186
- }
187
- app.log.info({ topicId, category, reason }, 'Franky 升级请求');
188
- },
189
- getProjectConfig: (projectId) => worktreeManager.getProjectConfig(projectId),
190
- logger: app.log,
191
- });
192
- const topicRouter = new FrankyTopicRouter(frankyOrchestrator, app.log);
193
- // LoidBrain(指挥模式)
194
- const loidBrain = new LoidBrain({
195
- binary: config.CLAUDE_CODE_BINARY,
196
- loidWorkDir: resolve(config.LOID_WORK_DIR),
197
- protocolsDir: resolve(config.LOID_PROTOCOLS_DIR),
198
- maxTurnsPerSession: 50,
199
- logger: app.log,
200
- }, {
201
- broker,
202
- feishuSender: feishuSender ?? undefined,
203
- mcpDeps: {
204
- db,
205
- channelRegistry,
206
- workspacePath: config.WORKSPACE_PATH,
207
- logger: app.log,
208
- yorOrchestrator,
209
- threadCreator: feishuSender ?? undefined,
210
- getProjectConfig: (projectId) => worktreeManager.getProjectConfig(projectId),
211
- onProjectChanged: () => worktreeManager.invalidateCache(),
212
- onMessageSent: feishuSender
213
- ? (chatId) => { feishuSender.clearTypingReaction(chatId).catch(() => { }); }
214
- : undefined,
215
- },
216
- });
217
- await loidBrain.init();
218
- app.log.info('LoidBrain (指挥模式) 已启动');
219
- // YorOrchestrator 回调 → 状态变更 + 通知 LoidBrain
220
- yorOrchestrator.setCallbacks({
221
- onDelivery: (taskId, _data) => {
222
- // IN_PROGRESS → DELIVERING
223
- yorOrchestrator.transitionTask(taskId, TaskStatus.DELIVERING, 'yor', 'Yor 提交交付');
224
- const ctx = contextBuilder.buildDeliveryContext(taskId, 0);
225
- loidBrain.handleDelivery(ctx).catch(err => {
226
- app.log.error({ err, taskId }, 'Yor 交付验收触发失败');
227
- });
228
- },
229
- onBlocker: (taskId, data) => {
230
- // IN_PROGRESS → BLOCKED
231
- yorOrchestrator.transitionTask(taskId, TaskStatus.BLOCKED, 'yor', `阻塞: ${data.category} - ${data.reason}`);
232
- app.log.info({ taskId, category: data.category }, 'Yor 报告阻塞,已更新状态');
233
- },
234
- onInstanceExited: (taskId) => {
235
- onTaskComplete(taskId, 0).catch(err => {
236
- app.log.error({ err, taskId }, '经验沉淀失败');
237
- });
238
- },
239
- onInstanceCrash: (taskId, error) => {
240
- app.log.error({ taskId, error }, 'Yor 实例崩溃');
241
- // 崩溃 → BLOCKED(非终态才变更)
242
- yorOrchestrator.transitionTask(taskId, TaskStatus.BLOCKED, 'system', `Yor 实例崩溃: ${error}`);
243
- // 通知用户:任务执行遇到问题
244
- if (feishuSender) {
245
- const task = dbGetTask(db, taskId);
246
- const chatId = task?.source_chat_id;
247
- if (chatId) {
248
- const title = task?.title ? `「${task.title}」` : taskId;
249
- feishuSender.sendText({
250
- receiveIdType: 'chat_id',
251
- receiveId: chatId,
252
- text: `${title} 执行过程中出了点问题,我先看看怎么回事`,
253
- }).catch(err => {
254
- app.log.error({ err, taskId }, 'Yor 崩溃通知发送失败');
255
- });
256
- }
257
- }
258
- const ctx = contextBuilder.buildDeliveryContext(taskId, 1);
259
- loidBrain.handleDelivery(ctx).catch(err => {
260
- app.log.error({ err, taskId }, 'Yor 崩溃后验收触发失败');
261
- });
262
- },
263
- });
264
- // 重启回调:释放 Loid 会话 + Broker 全部实例
265
- const onRestart = async () => {
266
- // 先让 SessionManager 清理 session map(会同时 dispose broker 中的 Loid 实例)
267
- if (loidBrain) {
268
- await loidBrain.disposeSessions();
269
- }
270
- // 再释放剩余实例(Yor 等)
271
- const ids = await broker.disposeAll();
272
- app.log.info(`[restart] 已释放 ${ids.length} 个 CC 实例`);
273
- return { disposed: ids.length };
274
- };
275
- // CommandRouter(斜杠命令拦截)
276
- let commandRouter;
277
- if (feishuSender) {
278
- commandRouter = new CommandRouter({ db, feishuSender, broker, onRestart, logger: app.log });
279
- registerBuiltinCommands(commandRouter);
280
- app.log.info('CommandRouter 已注册内置命令');
281
- }
282
- // MessageIntake(事件转发器)
283
- const messageIntake = new MessageIntake({
284
- db,
285
- loidBrain,
286
- contextBuilder,
287
- commandRouter,
288
- topicRouter,
289
- });
290
- // MessageQueue
291
- const messageQueue = new MessageQueue({
292
- handler: async (msg) => {
293
- try {
294
- await messageIntake.handle(msg);
295
- }
296
- catch (err) {
297
- app.log.error({ err, msg: msg.content.slice(0, 50) }, '消息处理失败');
298
- }
299
- },
300
- logger: {
301
- info: (...args) => app.log.info(args.join(' ')),
302
- warn: (...args) => app.log.warn(args.join(' ')),
303
- error: (...args) => app.log.error(args.join(' ')),
304
- },
305
- });
306
- // HTTP 路由
307
- await registerRoutes(app, { db, broker, clarifier, messageQueue, larkClient, chatSyncService, onRestart });
308
- // SPA fallback
309
- if (serveStatic) {
310
- app.setNotFoundHandler(async (request, reply) => {
311
- if (request.url.startsWith('/api/')) {
312
- return reply.status(404).send({ error: 'Not Found' });
313
- }
314
- return reply.sendFile('index.html', webDistPath);
315
- });
316
- }
317
- return { app, db, config, broker, yorOrchestrator, frankyOrchestrator, messageIntake, messageQueue, feishuSender, loidBrain, larkClient, chatSyncService };
318
- }
319
- /**
320
- * 完整启动流程:buildServer + listen + 飞书 WS + graceful shutdown
321
- * CLI 和直接运行都调用这个函数
322
- */
323
- export async function startServer() {
324
- const serverCtx = await buildServer();
325
- const { app, config, broker, messageQueue, feishuSender, loidBrain, larkClient, chatSyncService, db, frankyOrchestrator } = serverCtx;
326
- await app.listen({ port: config.PORT, host: '0.0.0.0' });
327
- app.log.info(`Anya server 已启动,端口 ${config.PORT}`);
328
- // 飞书 WebSocket 长连接
329
- let feishuWs = null;
330
- if (config.FEISHU_APP_ID && config.FEISHU_APP_SECRET && larkClient) {
331
- const mediaDownloader = new MediaDownloader(larkClient, {
332
- mediaDir: config.MEDIA_PATH,
333
- });
334
- await mediaDownloader.ensureDir();
335
- feishuWs = new FeishuWSClient({ appId: config.FEISHU_APP_ID, appSecret: config.FEISHU_APP_SECRET }, {
336
- onMessage: async (msg) => {
337
- messageQueue.enqueue(msg);
338
- },
339
- mediaDownloader,
340
- client: larkClient,
341
- db,
342
- chatSyncService: chatSyncService ?? undefined,
343
- feishuSender: feishuSender ?? undefined,
344
- logger: app.log,
345
- });
346
- feishuWs.connect().then(() => {
347
- app.log.info('飞书 WebSocket 连接已建立');
348
- }).catch((err) => {
349
- app.log.error({ err }, '飞书 WebSocket 连接失败');
350
- });
351
- }
352
- else {
353
- app.log.info('飞书凭证未配置,跳过 WebSocket 连接');
354
- }
355
- // ── Graceful shutdown ──
356
- let shuttingDown = false;
357
- const shutdown = async (signal) => {
358
- if (shuttingDown)
359
- return;
360
- shuttingDown = true;
361
- app.log.info(`收到 ${signal},开始优雅关闭...`);
362
- if (feishuWs)
363
- await feishuWs.close();
364
- messageQueue.dispose();
365
- await app.close();
366
- if (loidBrain) {
367
- try {
368
- await loidBrain.dispose();
369
- }
370
- catch (err) {
371
- app.log.error({ err }, 'LoidBrain 关闭失败');
372
- }
373
- }
374
- await frankyOrchestrator.shutdown();
375
- const result = await broker.shutdown(30_000);
376
- if (result.unfinished.length > 0) {
377
- app.log.info(`优雅关闭: ${result.unfinished.length} 个实例未完成`);
378
- }
379
- app.log.info('Anya server 已关闭');
380
- };
381
- process.on('SIGINT', () => { shutdown('SIGINT').then(() => process.exit(0)); });
382
- process.on('SIGTERM', () => { shutdown('SIGTERM').then(() => process.exit(0)); });
383
- return { app, config };
384
- }
385
- // 仅在直接运行时启动
386
- const isDirectRun = process.argv[1]?.endsWith('main.js') || process.argv[1]?.endsWith('main.ts');
387
- if (isDirectRun) {
388
- startServer().catch((err) => {
389
- console.error('启动失败:', err);
390
- process.exit(1);
391
- });
392
- }
393
- //# sourceMappingURL=main.js.map
@@ -1,2 +0,0 @@
1
- export { TraceContext } from './trace-context.js';
2
- //# sourceMappingURL=index.js.map
@@ -1,92 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { insertTraceSpan, updateTraceSpan } from '@team-anya/db';
3
- export class TraceContext {
4
- traceId;
5
- db;
6
- logger;
7
- spanStack = [];
8
- constructor(config, traceId) {
9
- this.traceId = traceId ?? randomUUID();
10
- this.db = config.db;
11
- this.logger = config.logger;
12
- }
13
- startSpan(operation, input) {
14
- const spanId = randomUUID();
15
- const parentSpanId = this.spanStack.length > 0
16
- ? this.spanStack[this.spanStack.length - 1].spanId
17
- : null;
18
- this.spanStack.push({ spanId, operation, startedAt: Date.now() });
19
- // Fire-and-forget DB write
20
- try {
21
- insertTraceSpan(this.db, {
22
- trace_id: this.traceId,
23
- span_id: spanId,
24
- parent_span_id: parentSpanId,
25
- operation,
26
- status: 'in_progress',
27
- started_at: new Date().toISOString(),
28
- input: input !== undefined ? JSON.stringify(input) : null,
29
- });
30
- }
31
- catch (err) {
32
- this.logger?.error('TraceContext: insertTraceSpan 失败', err);
33
- }
34
- return spanId;
35
- }
36
- endSpan(opts) {
37
- const targetSpanId = opts?.spanId;
38
- let entry;
39
- if (targetSpanId) {
40
- // Find and remove the specific span from the stack
41
- const idx = this.spanStack.findIndex(s => s.spanId === targetSpanId);
42
- if (idx >= 0) {
43
- entry = this.spanStack[idx];
44
- this.spanStack.splice(idx, 1);
45
- }
46
- }
47
- else {
48
- // Pop the top of the stack
49
- entry = this.spanStack.pop();
50
- }
51
- if (!entry) {
52
- this.logger?.error('TraceContext: endSpan 未找到对应 span');
53
- return;
54
- }
55
- const endedAt = Date.now();
56
- const durationMs = endedAt - entry.startedAt;
57
- try {
58
- updateTraceSpan(this.db, entry.spanId, {
59
- status: opts?.status ?? 'success',
60
- ended_at: new Date().toISOString(),
61
- duration_ms: durationMs,
62
- output: opts?.output !== undefined ? JSON.stringify(opts.output) : null,
63
- error: opts?.error ?? null,
64
- metadata: opts?.metadata ? JSON.stringify(opts.metadata) : null,
65
- });
66
- }
67
- catch (err) {
68
- this.logger?.error('TraceContext: updateTraceSpan 失败', err);
69
- }
70
- }
71
- async withSpan(operation, input, fn) {
72
- const spanId = this.startSpan(operation, input);
73
- try {
74
- const result = await fn();
75
- this.endSpan({
76
- spanId,
77
- status: 'success',
78
- output: result,
79
- });
80
- return result;
81
- }
82
- catch (err) {
83
- this.endSpan({
84
- spanId,
85
- status: 'error',
86
- error: err instanceof Error ? err.message : String(err),
87
- });
88
- throw err;
89
- }
90
- }
91
- }
92
- //# sourceMappingURL=trace-context.js.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=message.js.map