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.
- package/README.md +1 -37
- package/package.json +6 -37
- package/anya/prompts/execution-guides/git-delivery.md +0 -38
- package/anya/prompts/execution-guides/testing-and-self-heal.md +0 -28
- package/anya/prompts/protocols/brief-assembly.md +0 -55
- package/anya/prompts/protocols/report.md +0 -175
- package/anya/prompts/protocols/review.md +0 -90
- package/anya/prompts/task-claude-md.template.md +0 -32
- package/apps/server/dist/broker/cc-broker.js +0 -261
- package/apps/server/dist/cli.js +0 -296
- package/apps/server/dist/config.js +0 -76
- package/apps/server/dist/daemon.js +0 -51
- package/apps/server/dist/franky/context-builder.js +0 -160
- package/apps/server/dist/franky/franky-mcp-server.js +0 -107
- package/apps/server/dist/franky/franky-orchestrator.js +0 -450
- package/apps/server/dist/franky/index.js +0 -5
- package/apps/server/dist/franky/topic-router.js +0 -16
- package/apps/server/dist/gateway/chat-sync.js +0 -135
- package/apps/server/dist/gateway/command-router.js +0 -114
- package/apps/server/dist/gateway/commands/cancel.js +0 -32
- package/apps/server/dist/gateway/commands/help.js +0 -16
- package/apps/server/dist/gateway/commands/index.js +0 -26
- package/apps/server/dist/gateway/commands/restart.js +0 -34
- package/apps/server/dist/gateway/commands/status.js +0 -34
- package/apps/server/dist/gateway/commands/tasks.js +0 -33
- package/apps/server/dist/gateway/feishu-sender.js +0 -410
- package/apps/server/dist/gateway/feishu-ws.js +0 -256
- package/apps/server/dist/gateway/http.js +0 -1019
- package/apps/server/dist/gateway/media-downloader.js +0 -149
- package/apps/server/dist/gateway/message-events.js +0 -10
- package/apps/server/dist/gateway/message-intake.js +0 -67
- package/apps/server/dist/gateway/message-queue.js +0 -118
- package/apps/server/dist/gateway/session-reader.js +0 -142
- package/apps/server/dist/gateway/ws-push.js +0 -115
- package/apps/server/dist/loid/brain.js +0 -105
- package/apps/server/dist/loid/clarifier.js +0 -162
- package/apps/server/dist/loid/context-builder.js +0 -413
- package/apps/server/dist/loid/mcp-server.js +0 -106
- package/apps/server/dist/loid/memory-settler.js +0 -189
- package/apps/server/dist/loid/opportunity-manager.js +0 -148
- package/apps/server/dist/loid/profile-updater.js +0 -179
- package/apps/server/dist/loid/reporter.js +0 -148
- package/apps/server/dist/loid/schemas.js +0 -117
- package/apps/server/dist/loid/self-calibrator.js +0 -314
- package/apps/server/dist/loid/session-manager.js +0 -301
- package/apps/server/dist/loid/session.js +0 -271
- package/apps/server/dist/loid/worktree-manager.js +0 -191
- package/apps/server/dist/main.js +0 -393
- package/apps/server/dist/tracing/index.js +0 -2
- package/apps/server/dist/tracing/trace-context.js +0 -92
- package/apps/server/dist/types/message.js +0 -2
- package/apps/server/dist/yor/yor-mcp-server.js +0 -104
- package/apps/server/dist/yor/yor-orchestrator.js +0 -233
- package/apps/web/dist/assets/index-BiiEB0qZ.css +0 -1
- package/apps/web/dist/assets/index-D1AK5ZEE.js +0 -798
- package/apps/web/dist/index.html +0 -13
- package/packages/cc-client/dist/claude-code-backend.js +0 -664
- package/packages/cc-client/dist/index.js +0 -2
- package/packages/cc-client/package.json +0 -11
- package/packages/core/dist/constants.js +0 -59
- package/packages/core/dist/errors.js +0 -35
- package/packages/core/dist/index.js +0 -7
- package/packages/core/dist/office-init.js +0 -101
- package/packages/core/dist/scope/checker.js +0 -114
- package/packages/core/dist/scope/defaults.js +0 -55
- package/packages/core/dist/scope/index.js +0 -3
- package/packages/core/dist/state-machine.js +0 -85
- package/packages/core/dist/types/audit.js +0 -12
- package/packages/core/dist/types/backend.js +0 -2
- package/packages/core/dist/types/commitment.js +0 -17
- package/packages/core/dist/types/communication.js +0 -18
- package/packages/core/dist/types/index.js +0 -8
- package/packages/core/dist/types/opportunity.js +0 -27
- package/packages/core/dist/types/org.js +0 -26
- package/packages/core/dist/types/task.js +0 -46
- package/packages/core/package.json +0 -10
- package/packages/db/dist/client.js +0 -69
- package/packages/db/dist/index.js +0 -691
- package/packages/db/dist/schema/audit-events.js +0 -13
- package/packages/db/dist/schema/cc-sessions.js +0 -14
- package/packages/db/dist/schema/chats.js +0 -33
- package/packages/db/dist/schema/commitments.js +0 -18
- package/packages/db/dist/schema/communication-events.js +0 -14
- package/packages/db/dist/schema/index.js +0 -13
- package/packages/db/dist/schema/message-log.js +0 -20
- package/packages/db/dist/schema/opportunities.js +0 -23
- package/packages/db/dist/schema/org.js +0 -36
- package/packages/db/dist/schema/projects.js +0 -23
- package/packages/db/dist/schema/tasks.js +0 -48
- package/packages/db/dist/schema/topics.js +0 -20
- package/packages/db/dist/schema/trace-spans.js +0 -19
- package/packages/db/package.json +0 -12
- package/packages/db/src/migrations/0000_baseline.sql +0 -251
- package/packages/db/src/migrations/meta/_journal.json +0 -13
- package/packages/mcp-tools/dist/index.js +0 -41
- package/packages/mcp-tools/dist/layer1/audit-append.js +0 -38
- package/packages/mcp-tools/dist/layer1/audit-query.js +0 -51
- package/packages/mcp-tools/dist/layer1/memory-brief.js +0 -168
- package/packages/mcp-tools/dist/layer1/memory-context.js +0 -124
- package/packages/mcp-tools/dist/layer1/memory-digest.js +0 -126
- package/packages/mcp-tools/dist/layer1/memory-forget.js +0 -108
- package/packages/mcp-tools/dist/layer1/memory-learn.js +0 -63
- package/packages/mcp-tools/dist/layer1/memory-recall.js +0 -287
- package/packages/mcp-tools/dist/layer1/memory-reflect.js +0 -80
- package/packages/mcp-tools/dist/layer1/memory-remember.js +0 -119
- package/packages/mcp-tools/dist/layer1/memory-search.js +0 -263
- package/packages/mcp-tools/dist/layer1/memory-write.js +0 -21
- package/packages/mcp-tools/dist/layer1/org-lookup.js +0 -47
- package/packages/mcp-tools/dist/layer1/project-get.js +0 -28
- package/packages/mcp-tools/dist/layer1/project-list.js +0 -20
- package/packages/mcp-tools/dist/layer1/report-daily.js +0 -68
- package/packages/mcp-tools/dist/layer1/task-get.js +0 -29
- package/packages/mcp-tools/dist/layer1/task-update.js +0 -34
- package/packages/mcp-tools/dist/layer2/franky/topic-checkpoint.js +0 -43
- package/packages/mcp-tools/dist/layer2/franky/topic-escalate.js +0 -19
- package/packages/mcp-tools/dist/layer2/loid/decision-log.js +0 -15
- package/packages/mcp-tools/dist/layer2/loid/decision-no-action.js +0 -15
- package/packages/mcp-tools/dist/layer2/loid/delivery-create-pr.js +0 -30
- package/packages/mcp-tools/dist/layer2/loid/delivery-share.js +0 -12
- package/packages/mcp-tools/dist/layer2/loid/delivery-submit.js +0 -77
- package/packages/mcp-tools/dist/layer2/loid/delivery-upload.js +0 -18
- package/packages/mcp-tools/dist/layer2/loid/project-remove.js +0 -16
- package/packages/mcp-tools/dist/layer2/loid/project-upsert.js +0 -33
- package/packages/mcp-tools/dist/layer2/loid/task-dispatch.js +0 -196
- package/packages/mcp-tools/dist/layer2/loid/task-lookup.js +0 -38
- package/packages/mcp-tools/dist/layer2/loid/topic-close.js +0 -22
- package/packages/mcp-tools/dist/layer2/loid/topic-create.js +0 -56
- package/packages/mcp-tools/dist/layer2/loid/yor-approve.js +0 -8
- package/packages/mcp-tools/dist/layer2/loid/yor-kill.js +0 -7
- package/packages/mcp-tools/dist/layer2/loid/yor-rework.js +0 -7
- package/packages/mcp-tools/dist/layer2/loid/yor-spawn.js +0 -15
- package/packages/mcp-tools/dist/layer2/loid/yor-status.js +0 -8
- package/packages/mcp-tools/dist/layer2/yor/task-block.js +0 -11
- package/packages/mcp-tools/dist/layer2/yor/task-deliver.js +0 -35
- package/packages/mcp-tools/dist/layer2/yor/task-progress.js +0 -21
- package/packages/mcp-tools/dist/layer3/adapters/feishu-adapter.js +0 -192
- package/packages/mcp-tools/dist/layer3/adapters/types.js +0 -28
- package/packages/mcp-tools/dist/layer3/channel-receive.js +0 -11
- package/packages/mcp-tools/dist/layer3/channel-send.js +0 -91
- package/packages/mcp-tools/dist/layer3/file-upload.js +0 -44
- package/packages/mcp-tools/dist/registry.js +0 -871
- package/packages/mcp-tools/package.json +0 -13
- package/workspace/.claude/settings.local.json +0 -9
- package/workspace/.mcp.json +0 -12
- package/workspace/CHARTER.md +0 -76
- package/workspace/CLAUDE.md +0 -58
- package/workspace/PROTOCOL.md +0 -160
- package/workspace/TOOLS.md +0 -470
- package/workspace/audit/.gitkeep +0 -0
- package/workspace/franky/CLAUDE.md +0 -37
- package/workspace/franky/PLAYBOOK.md +0 -215
- package/workspace/franky/PROFILE.md +0 -80
- package/workspace/loid/CLAUDE.md +0 -12
- package/workspace/loid/PLAYBOOK.md +0 -198
- package/workspace/loid/PROFILE.md +0 -78
- package/workspace/memory/commitments/.gitkeep +0 -0
- package/workspace/memory/execution/.gitkeep +0 -0
- package/workspace/memory/people/.gitkeep +0 -0
- package/workspace/memory/projects/.gitkeep +0 -0
- package/workspace/memory/self/.gitkeep +0 -0
- package/workspace/reference/identity/.gitkeep +0 -0
- package/workspace/reference/org/escalation.yaml +0 -24
- package/workspace/reference/org/ownership.yaml +0 -28
- package/workspace/reports/.gitkeep +0 -0
- package/workspace/yor/CLAUDE.md +0 -22
- package/workspace/yor/PLAYBOOK.md +0 -73
- package/workspace/yor/PROFILE.md +0 -52
- 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
|
package/apps/server/dist/main.js
DELETED
|
@@ -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,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
|