@damaall/ccx 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/commands/report.d.ts +8 -0
- package/dist/commands/report.d.ts.map +1 -0
- package/dist/commands/report.js +115 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/reuse.d.ts +7 -0
- package/dist/commands/reuse.d.ts.map +1 -0
- package/dist/commands/reuse.js +167 -0
- package/dist/commands/reuse.js.map +1 -0
- package/dist/commands/watch.d.ts +10 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +201 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/core/cursor-reader.d.ts +24 -0
- package/dist/core/cursor-reader.d.ts.map +1 -0
- package/dist/core/cursor-reader.js +128 -0
- package/dist/core/cursor-reader.js.map +1 -0
- package/dist/core/data-source-adapter.d.ts +239 -0
- package/dist/core/data-source-adapter.d.ts.map +1 -0
- package/dist/core/data-source-adapter.js +85 -0
- package/dist/core/data-source-adapter.js.map +1 -0
- package/dist/core/identity-resolver.d.ts +27 -0
- package/dist/core/identity-resolver.d.ts.map +1 -0
- package/dist/core/identity-resolver.js +55 -0
- package/dist/core/identity-resolver.js.map +1 -0
- package/dist/core/inbox-reader.d.ts +20 -0
- package/dist/core/inbox-reader.d.ts.map +1 -0
- package/dist/core/inbox-reader.js +105 -0
- package/dist/core/inbox-reader.js.map +1 -0
- package/dist/core/paths.d.ts +28 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +46 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/pricing.d.ts +25 -0
- package/dist/core/pricing.d.ts.map +1 -0
- package/dist/core/pricing.js +108 -0
- package/dist/core/pricing.js.map +1 -0
- package/dist/core/redact.d.ts +15 -0
- package/dist/core/redact.d.ts.map +1 -0
- package/dist/core/redact.js +60 -0
- package/dist/core/redact.js.map +1 -0
- package/dist/core/session-discovery.d.ts +26 -0
- package/dist/core/session-discovery.d.ts.map +1 -0
- package/dist/core/session-discovery.js +89 -0
- package/dist/core/session-discovery.js.map +1 -0
- package/dist/core/snapshot-manager.d.ts +29 -0
- package/dist/core/snapshot-manager.d.ts.map +1 -0
- package/dist/core/snapshot-manager.js +120 -0
- package/dist/core/snapshot-manager.js.map +1 -0
- package/dist/core/snapshot-reader.d.ts +23 -0
- package/dist/core/snapshot-reader.d.ts.map +1 -0
- package/dist/core/snapshot-reader.js +142 -0
- package/dist/core/snapshot-reader.js.map +1 -0
- package/dist/core/state-aggregator.d.ts +36 -0
- package/dist/core/state-aggregator.d.ts.map +1 -0
- package/dist/core/state-aggregator.js +218 -0
- package/dist/core/state-aggregator.js.map +1 -0
- package/dist/core/task-reader.d.ts +14 -0
- package/dist/core/task-reader.d.ts.map +1 -0
- package/dist/core/task-reader.js +55 -0
- package/dist/core/task-reader.js.map +1 -0
- package/dist/core/team-reader.d.ts +18 -0
- package/dist/core/team-reader.d.ts.map +1 -0
- package/dist/core/team-reader.js +68 -0
- package/dist/core/team-reader.js.map +1 -0
- package/dist/core/types.d.ts +105 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/watcher.d.ts +16 -0
- package/dist/core/watcher.d.ts.map +1 -0
- package/dist/core/watcher.js +194 -0
- package/dist/core/watcher.js.map +1 -0
- package/dist/guard/hard-limit.d.ts +13 -0
- package/dist/guard/hard-limit.d.ts.map +1 -0
- package/dist/guard/hard-limit.js +79 -0
- package/dist/guard/hard-limit.js.map +1 -0
- package/dist/guard/soft-limit.d.ts +5 -0
- package/dist/guard/soft-limit.d.ts.map +1 -0
- package/dist/guard/soft-limit.js +22 -0
- package/dist/guard/soft-limit.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/report/json.d.ts +6 -0
- package/dist/report/json.d.ts.map +1 -0
- package/dist/report/json.js +4 -0
- package/dist/report/json.js.map +1 -0
- package/dist/report/markdown.d.ts +6 -0
- package/dist/report/markdown.d.ts.map +1 -0
- package/dist/report/markdown.js +57 -0
- package/dist/report/markdown.js.map +1 -0
- package/dist/report/redact-snapshot.d.ts +3 -0
- package/dist/report/redact-snapshot.d.ts.map +1 -0
- package/dist/report/redact-snapshot.js +21 -0
- package/dist/report/redact-snapshot.js.map +1 -0
- package/dist/report/terminal.d.ts +3 -0
- package/dist/report/terminal.d.ts.map +1 -0
- package/dist/report/terminal.js +85 -0
- package/dist/report/terminal.js.map +1 -0
- package/dist/ui/AgentPanel.d.ts +13 -0
- package/dist/ui/AgentPanel.d.ts.map +1 -0
- package/dist/ui/AgentPanel.js +61 -0
- package/dist/ui/AgentPanel.js.map +1 -0
- package/dist/ui/AlertBanner.d.ts +11 -0
- package/dist/ui/AlertBanner.d.ts.map +1 -0
- package/dist/ui/AlertBanner.js +19 -0
- package/dist/ui/AlertBanner.js.map +1 -0
- package/dist/ui/CompletionBanner.d.ts +12 -0
- package/dist/ui/CompletionBanner.d.ts.map +1 -0
- package/dist/ui/CompletionBanner.js +11 -0
- package/dist/ui/CompletionBanner.js.map +1 -0
- package/dist/ui/CostBar.d.ts +12 -0
- package/dist/ui/CostBar.d.ts.map +1 -0
- package/dist/ui/CostBar.js +29 -0
- package/dist/ui/CostBar.js.map +1 -0
- package/dist/ui/Dashboard.d.ts +10 -0
- package/dist/ui/Dashboard.d.ts.map +1 -0
- package/dist/ui/Dashboard.js +70 -0
- package/dist/ui/Dashboard.js.map +1 -0
- package/dist/ui/TaskPanel.d.ts +11 -0
- package/dist/ui/TaskPanel.d.ts.map +1 -0
- package/dist/ui/TaskPanel.js +41 -0
- package/dist/ui/TaskPanel.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface SubagentDiscovery {
|
|
2
|
+
readonly sessionId: string;
|
|
3
|
+
readonly projectPath: string;
|
|
4
|
+
readonly subagentsDir: string;
|
|
5
|
+
readonly agentFiles: readonly AgentFileInfo[];
|
|
6
|
+
}
|
|
7
|
+
export interface AgentFileInfo {
|
|
8
|
+
readonly filePath: string;
|
|
9
|
+
readonly agentId: string;
|
|
10
|
+
readonly sizeBytes: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 根據 leadSessionId 找到 subagents 目錄
|
|
14
|
+
* 掃描所有 project 目錄下的 session 目錄
|
|
15
|
+
*/
|
|
16
|
+
export declare function discoverSubagents(leadSessionId: string): Promise<SubagentDiscovery | null>;
|
|
17
|
+
/**
|
|
18
|
+
* 列出 subagents 目錄下的所有 agent JSONL 檔案
|
|
19
|
+
*/
|
|
20
|
+
export declare function listAgentFiles(subagentsDir: string): Promise<AgentFileInfo[]>;
|
|
21
|
+
/**
|
|
22
|
+
* 監測新的 agent JSONL 出現(watch 模式用)
|
|
23
|
+
* 比較當前檔案列表和已知列表,回傳新增的
|
|
24
|
+
*/
|
|
25
|
+
export declare function findNewAgentFiles(current: readonly AgentFileInfo[], known: ReadonlySet<string>): AgentFileInfo[];
|
|
26
|
+
//# sourceMappingURL=session-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-discovery.d.ts","sourceRoot":"","sources":["../../src/core/session-discovery.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,CAAA;CAC9C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAqChG;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA2BnF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GACzB,aAAa,EAAE,CAEjB"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionDiscovery:掃描 projects/ 建立 sessionId → subagents path 索引
|
|
3
|
+
*
|
|
4
|
+
* Claude Code 的目錄結構:
|
|
5
|
+
* ~/.claude/projects/{encoded-project-path}/{sessionId}/subagents/agent-{id}.jsonl
|
|
6
|
+
*
|
|
7
|
+
* 目標:給定 leadSessionId,找到對應的 subagents 目錄
|
|
8
|
+
*/
|
|
9
|
+
import { readdir, access, stat } from 'node:fs/promises';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { claudePaths } from './paths.js';
|
|
12
|
+
/**
|
|
13
|
+
* 根據 leadSessionId 找到 subagents 目錄
|
|
14
|
+
* 掃描所有 project 目錄下的 session 目錄
|
|
15
|
+
*/
|
|
16
|
+
export async function discoverSubagents(leadSessionId) {
|
|
17
|
+
const projectsDir = claudePaths.projects;
|
|
18
|
+
try {
|
|
19
|
+
const projectEntries = await readdir(projectsDir, { withFileTypes: true });
|
|
20
|
+
for (const projectEntry of projectEntries) {
|
|
21
|
+
if (!projectEntry.isDirectory())
|
|
22
|
+
continue;
|
|
23
|
+
const projectPath = projectEntry.name;
|
|
24
|
+
const sessionDir = join(projectsDir, projectPath, leadSessionId);
|
|
25
|
+
try {
|
|
26
|
+
await access(sessionDir);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const subagentsDir = join(sessionDir, 'subagents');
|
|
32
|
+
try {
|
|
33
|
+
await access(subagentsDir);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const agentFiles = await listAgentFiles(subagentsDir);
|
|
39
|
+
return {
|
|
40
|
+
sessionId: leadSessionId,
|
|
41
|
+
projectPath,
|
|
42
|
+
subagentsDir,
|
|
43
|
+
agentFiles,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// projects 目錄不存在
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 列出 subagents 目錄下的所有 agent JSONL 檔案
|
|
54
|
+
*/
|
|
55
|
+
export async function listAgentFiles(subagentsDir) {
|
|
56
|
+
try {
|
|
57
|
+
const entries = await readdir(subagentsDir);
|
|
58
|
+
const agentFiles = [];
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
if (!entry.startsWith('agent-') || !entry.endsWith('.jsonl'))
|
|
61
|
+
continue;
|
|
62
|
+
const agentId = entry.replace('agent-', '').replace('.jsonl', '');
|
|
63
|
+
const filePath = join(subagentsDir, entry);
|
|
64
|
+
try {
|
|
65
|
+
const fileStat = await stat(filePath);
|
|
66
|
+
agentFiles.push({
|
|
67
|
+
filePath,
|
|
68
|
+
agentId,
|
|
69
|
+
sizeBytes: fileStat.size,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return agentFiles.sort((a, b) => a.agentId.localeCompare(b.agentId));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 監測新的 agent JSONL 出現(watch 模式用)
|
|
84
|
+
* 比較當前檔案列表和已知列表,回傳新增的
|
|
85
|
+
*/
|
|
86
|
+
export function findNewAgentFiles(current, known) {
|
|
87
|
+
return current.filter(f => !known.has(f.agentId));
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=session-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-discovery.js","sourceRoot":"","sources":["../../src/core/session-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { StateAggregator } from './state-aggregator.js';
|
|
2
|
+
export declare class SnapshotManager {
|
|
3
|
+
private readonly aggregator;
|
|
4
|
+
private readonly sessionName;
|
|
5
|
+
private readonly sessionDir;
|
|
6
|
+
private debounceTimer;
|
|
7
|
+
private initialized;
|
|
8
|
+
private finalized;
|
|
9
|
+
constructor(aggregator: StateAggregator, teamName: string);
|
|
10
|
+
get sessionPath(): string;
|
|
11
|
+
get name(): string;
|
|
12
|
+
/**
|
|
13
|
+
* 初始化:建立目錄 + 寫入 initial snapshot
|
|
14
|
+
*/
|
|
15
|
+
initialize(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Finalize:取消 debounce timer + 寫入最終 snapshot
|
|
18
|
+
*/
|
|
19
|
+
finalize(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Debounced write schedule
|
|
22
|
+
*/
|
|
23
|
+
private scheduleWrite;
|
|
24
|
+
/**
|
|
25
|
+
* 實際寫入 snapshot 到磁碟
|
|
26
|
+
*/
|
|
27
|
+
private writeSnapshot;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=snapshot-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-manager.d.ts","sourceRoot":"","sources":["../../src/core/snapshot-manager.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAK5D,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiB;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,SAAS,CAAQ;gBAEb,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM;IAazD,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAY/B;;OAEG;IACH,OAAO,CAAC,aAAa;IAUrB;;OAEG;YACW,aAAa;CAoC5B"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SnapshotManager:event-driven + debounced 快照寫入
|
|
3
|
+
*
|
|
4
|
+
* 設計:
|
|
5
|
+
* - trailing-edge debounce 500ms(多事件合併成一次寫入)
|
|
6
|
+
* - 寫入 ~/.ccx/sessions/{team}-{timestamp}/
|
|
7
|
+
* - Atomic write(寫 .tmp → rename)
|
|
8
|
+
* - SIGINT 時 finalize
|
|
9
|
+
*/
|
|
10
|
+
import { mkdir, writeFile, rename } from 'node:fs/promises';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { stringify } from 'yaml';
|
|
13
|
+
import { ccxPaths } from './paths.js';
|
|
14
|
+
const DEBOUNCE_MS = 500;
|
|
15
|
+
export class SnapshotManager {
|
|
16
|
+
aggregator;
|
|
17
|
+
sessionName;
|
|
18
|
+
sessionDir;
|
|
19
|
+
debounceTimer = null;
|
|
20
|
+
initialized = false;
|
|
21
|
+
finalized = false;
|
|
22
|
+
constructor(aggregator, teamName) {
|
|
23
|
+
this.aggregator = aggregator;
|
|
24
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
25
|
+
this.sessionName = `${teamName}-${ts}`;
|
|
26
|
+
this.sessionDir = ccxPaths.session(this.sessionName);
|
|
27
|
+
// 監聽 state events → debounced write
|
|
28
|
+
this.aggregator.on('event', (event) => {
|
|
29
|
+
if (this.finalized)
|
|
30
|
+
return;
|
|
31
|
+
this.scheduleWrite();
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
get sessionPath() {
|
|
35
|
+
return this.sessionDir;
|
|
36
|
+
}
|
|
37
|
+
get name() {
|
|
38
|
+
return this.sessionName;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 初始化:建立目錄 + 寫入 initial snapshot
|
|
42
|
+
*/
|
|
43
|
+
async initialize() {
|
|
44
|
+
if (this.initialized)
|
|
45
|
+
return;
|
|
46
|
+
await mkdir(this.sessionDir, { recursive: true, mode: 0o700 });
|
|
47
|
+
await this.writeSnapshot();
|
|
48
|
+
this.initialized = true;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Finalize:取消 debounce timer + 寫入最終 snapshot
|
|
52
|
+
*/
|
|
53
|
+
async finalize() {
|
|
54
|
+
if (this.finalized)
|
|
55
|
+
return;
|
|
56
|
+
this.finalized = true;
|
|
57
|
+
if (this.debounceTimer) {
|
|
58
|
+
clearTimeout(this.debounceTimer);
|
|
59
|
+
this.debounceTimer = null;
|
|
60
|
+
}
|
|
61
|
+
await this.writeSnapshot(true);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Debounced write schedule
|
|
65
|
+
*/
|
|
66
|
+
scheduleWrite() {
|
|
67
|
+
if (this.debounceTimer) {
|
|
68
|
+
clearTimeout(this.debounceTimer);
|
|
69
|
+
}
|
|
70
|
+
this.debounceTimer = setTimeout(async () => {
|
|
71
|
+
this.debounceTimer = null;
|
|
72
|
+
await this.writeSnapshot();
|
|
73
|
+
}, DEBOUNCE_MS);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 實際寫入 snapshot 到磁碟
|
|
77
|
+
*/
|
|
78
|
+
async writeSnapshot(isFinal = false) {
|
|
79
|
+
const state = this.aggregator.getState();
|
|
80
|
+
const snapshot = {
|
|
81
|
+
teamName: state.teamName,
|
|
82
|
+
sessionId: this.sessionName,
|
|
83
|
+
startedAt: state.startedAt,
|
|
84
|
+
lastUpdatedAt: Date.now(),
|
|
85
|
+
finalized: isFinal,
|
|
86
|
+
agents: state.agents.map(a => ({
|
|
87
|
+
agentId: a.agentId,
|
|
88
|
+
name: a.name,
|
|
89
|
+
model: a.model,
|
|
90
|
+
tokenUsage: a.tokenUsage,
|
|
91
|
+
cost: a.cost,
|
|
92
|
+
status: a.status,
|
|
93
|
+
})),
|
|
94
|
+
tasks: state.tasks,
|
|
95
|
+
totalCost: state.totalCost,
|
|
96
|
+
totalTokens: state.totalTokens,
|
|
97
|
+
alerts: state.alerts,
|
|
98
|
+
};
|
|
99
|
+
try {
|
|
100
|
+
await mkdir(this.sessionDir, { recursive: true, mode: 0o700 });
|
|
101
|
+
await atomicWriteYaml(join(this.sessionDir, 'snapshot.yaml'), snapshot);
|
|
102
|
+
this.aggregator.emit('event', {
|
|
103
|
+
type: 'snapshot_written',
|
|
104
|
+
path: this.sessionDir,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
// Snapshot 寫入失敗不應該 crash watch
|
|
109
|
+
// 靜默忽略,下次 debounce 再試
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// ─── Atomic write ───
|
|
114
|
+
async function atomicWriteYaml(targetPath, data) {
|
|
115
|
+
const tmpPath = `${targetPath}.tmp`;
|
|
116
|
+
const content = stringify(data, { indent: 2 });
|
|
117
|
+
await writeFile(tmpPath, content, { encoding: 'utf-8', mode: 0o600 });
|
|
118
|
+
await rename(tmpPath, targetPath);
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=snapshot-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-manager.js","sourceRoot":"","sources":["../../src/core/snapshot-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SessionSnapshot } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 列出所有 saved sessions
|
|
4
|
+
*/
|
|
5
|
+
export declare function listSessions(): Promise<SessionInfo[]>;
|
|
6
|
+
export interface SessionInfo {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly teamName: string;
|
|
9
|
+
readonly startedAt: number;
|
|
10
|
+
readonly lastUpdatedAt: number;
|
|
11
|
+
readonly finalized: boolean;
|
|
12
|
+
readonly totalCost: number;
|
|
13
|
+
readonly agentCount: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 讀取 snapshot YAML
|
|
17
|
+
*/
|
|
18
|
+
export declare function readSnapshot(sessionName: string): Promise<SessionSnapshot | null>;
|
|
19
|
+
/**
|
|
20
|
+
* 根據 session name 或 team name 查找 snapshot
|
|
21
|
+
*/
|
|
22
|
+
export declare function findSnapshot(nameOrTeam: string): Promise<SessionSnapshot | null>;
|
|
23
|
+
//# sourceMappingURL=snapshot-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-reader.d.ts","sourceRoot":"","sources":["../../src/core/snapshot-reader.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,eAAe,EAAmD,MAAM,YAAY,CAAA;AAGlG;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAyB3D;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CASvF;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAmBtF"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SnapshotReader:讀取 ~/.ccx/sessions/ 下的 snapshot
|
|
3
|
+
*
|
|
4
|
+
* 資料來源優先級:
|
|
5
|
+
* 1. ~/.claude/teams/* 仍存在 → 用 watcher 讀即時資料
|
|
6
|
+
* 2. ~/.claude/teams/* 已刪除 → 讀 ~/.ccx/sessions/* 副本
|
|
7
|
+
* 3. 都沒有 → 報錯
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { parse as parseYaml } from 'yaml';
|
|
12
|
+
import { ccxPaths } from './paths.js';
|
|
13
|
+
import { emptyUsage } from './pricing.js';
|
|
14
|
+
/**
|
|
15
|
+
* 列出所有 saved sessions
|
|
16
|
+
*/
|
|
17
|
+
export async function listSessions() {
|
|
18
|
+
try {
|
|
19
|
+
const entries = await readdir(ccxPaths.sessions, { withFileTypes: true });
|
|
20
|
+
const sessions = [];
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
if (!entry.isDirectory())
|
|
23
|
+
continue;
|
|
24
|
+
const snapshot = await readSnapshot(entry.name);
|
|
25
|
+
if (snapshot) {
|
|
26
|
+
sessions.push({
|
|
27
|
+
name: entry.name,
|
|
28
|
+
teamName: snapshot.teamName,
|
|
29
|
+
startedAt: snapshot.startedAt,
|
|
30
|
+
lastUpdatedAt: snapshot.lastUpdatedAt,
|
|
31
|
+
finalized: snapshot.finalized,
|
|
32
|
+
totalCost: snapshot.totalCost,
|
|
33
|
+
agentCount: snapshot.agents.length,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return sessions.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 讀取 snapshot YAML
|
|
45
|
+
*/
|
|
46
|
+
export async function readSnapshot(sessionName) {
|
|
47
|
+
const snapshotPath = join(ccxPaths.sessions, sessionName, 'snapshot.yaml');
|
|
48
|
+
try {
|
|
49
|
+
const raw = await readFile(snapshotPath, 'utf-8');
|
|
50
|
+
const data = parseYaml(raw);
|
|
51
|
+
return normalizeSnapshot(data);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 根據 session name 或 team name 查找 snapshot
|
|
59
|
+
*/
|
|
60
|
+
export async function findSnapshot(nameOrTeam) {
|
|
61
|
+
// 精確匹配 session name
|
|
62
|
+
const exact = await readSnapshot(nameOrTeam);
|
|
63
|
+
if (exact)
|
|
64
|
+
return exact;
|
|
65
|
+
// 模糊匹配 team name(取最新的)
|
|
66
|
+
const sessions = await listSessions();
|
|
67
|
+
const matching = sessions.filter(s => s.teamName === nameOrTeam);
|
|
68
|
+
if (matching.length > 0) {
|
|
69
|
+
return readSnapshot(matching[0].name);
|
|
70
|
+
}
|
|
71
|
+
// 前綴匹配
|
|
72
|
+
const prefixMatch = sessions.find(s => s.name.startsWith(nameOrTeam));
|
|
73
|
+
if (prefixMatch) {
|
|
74
|
+
return readSnapshot(prefixMatch.name);
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
// ─── YAML normalization ───
|
|
79
|
+
function normalizeSnapshot(data) {
|
|
80
|
+
if (!data || typeof data !== 'object')
|
|
81
|
+
return null;
|
|
82
|
+
const d = data;
|
|
83
|
+
return {
|
|
84
|
+
teamName: String(d.teamName ?? 'unknown'),
|
|
85
|
+
sessionId: String(d.sessionId ?? ''),
|
|
86
|
+
startedAt: Number(d.startedAt ?? 0),
|
|
87
|
+
lastUpdatedAt: Number(d.lastUpdatedAt ?? 0),
|
|
88
|
+
finalized: Boolean(d.finalized ?? false),
|
|
89
|
+
agents: normalizeAgents(d.agents),
|
|
90
|
+
tasks: normalizeTasks(d.tasks),
|
|
91
|
+
totalCost: Number(d.totalCost ?? 0),
|
|
92
|
+
totalTokens: normalizeUsage(d.totalTokens),
|
|
93
|
+
alerts: normalizeAlerts(d.alerts),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function normalizeAgents(raw) {
|
|
97
|
+
if (!Array.isArray(raw))
|
|
98
|
+
return [];
|
|
99
|
+
return raw.map(a => ({
|
|
100
|
+
agentId: String(a?.agentId ?? ''),
|
|
101
|
+
name: String(a?.name ?? ''),
|
|
102
|
+
model: String(a?.model ?? ''),
|
|
103
|
+
tokenUsage: normalizeUsage(a?.tokenUsage),
|
|
104
|
+
cost: Number(a?.cost ?? 0),
|
|
105
|
+
status: a?.status ?? 'unknown',
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
function normalizeTasks(raw) {
|
|
109
|
+
if (!Array.isArray(raw))
|
|
110
|
+
return [];
|
|
111
|
+
return raw.map(t => ({
|
|
112
|
+
id: String(t?.id ?? ''),
|
|
113
|
+
subject: String(t?.subject ?? ''),
|
|
114
|
+
description: String(t?.description ?? ''),
|
|
115
|
+
activeForm: String(t?.activeForm ?? ''),
|
|
116
|
+
owner: String(t?.owner ?? ''),
|
|
117
|
+
status: t?.status ?? 'pending',
|
|
118
|
+
blocks: Array.isArray(t?.blocks) ? t.blocks.map(String) : [],
|
|
119
|
+
blockedBy: Array.isArray(t?.blockedBy) ? t.blockedBy.map(String) : [],
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
function normalizeUsage(raw) {
|
|
123
|
+
if (!raw || typeof raw !== 'object')
|
|
124
|
+
return emptyUsage();
|
|
125
|
+
const u = raw;
|
|
126
|
+
return {
|
|
127
|
+
input_tokens: Number(u.input_tokens ?? 0),
|
|
128
|
+
output_tokens: Number(u.output_tokens ?? 0),
|
|
129
|
+
cache_creation_input_tokens: Number(u.cache_creation_input_tokens ?? 0),
|
|
130
|
+
cache_read_input_tokens: Number(u.cache_read_input_tokens ?? 0),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function normalizeAlerts(raw) {
|
|
134
|
+
if (!Array.isArray(raw))
|
|
135
|
+
return [];
|
|
136
|
+
return raw.map(a => ({
|
|
137
|
+
level: a?.level ?? 'info',
|
|
138
|
+
message: String(a?.message ?? ''),
|
|
139
|
+
timestamp: Number(a?.timestamp ?? 0),
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=snapshot-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-reader.js","sourceRoot":"","sources":["../../src/core/snapshot-reader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StateAggregator:純計算層,解耦 readers 和 consumers
|
|
3
|
+
*
|
|
4
|
+
* 接收所有 reader 的輸出 → 計算統一的 TeamState → 發射 events
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'node:events';
|
|
7
|
+
import type { TeamConfig, TaskData, TeamState, FileCursor } from './types.js';
|
|
8
|
+
import type { InboxData } from './inbox-reader.js';
|
|
9
|
+
export declare class StateAggregator extends EventEmitter {
|
|
10
|
+
private teamConfig;
|
|
11
|
+
private tasks;
|
|
12
|
+
private inboxes;
|
|
13
|
+
private cursors;
|
|
14
|
+
private alerts;
|
|
15
|
+
private startedAt;
|
|
16
|
+
private budget;
|
|
17
|
+
private stuckThresholdMs;
|
|
18
|
+
constructor(options?: {
|
|
19
|
+
budget?: number;
|
|
20
|
+
stuckThresholdMs?: number;
|
|
21
|
+
});
|
|
22
|
+
updateTeamConfig(config: TeamConfig): void;
|
|
23
|
+
updateTasks(tasks: TaskData[]): void;
|
|
24
|
+
updateInbox(inbox: InboxData): void;
|
|
25
|
+
updateCursor(cursor: FileCursor): void;
|
|
26
|
+
markTeamDeleted(): void;
|
|
27
|
+
getState(): TeamState;
|
|
28
|
+
private buildAgentState;
|
|
29
|
+
private inferAgentStatus;
|
|
30
|
+
private computeTotalTokens;
|
|
31
|
+
private computeTotalCost;
|
|
32
|
+
private lastBudgetLevel;
|
|
33
|
+
private checkBudgetThresholds;
|
|
34
|
+
private addAlert;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=state-aggregator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-aggregator.d.ts","sourceRoot":"","sources":["../../src/core/state-aggregator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EAGR,SAAS,EAGT,UAAU,EAEX,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAMlD,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,gBAAgB,CAA6B;gBAEzC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE;IAQpE,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAK1C,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI;IAOpC,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAInC,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAsBtC,eAAe,IAAI,IAAI;IAMvB,QAAQ,IAAI,SAAS;IAqBrB,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,gBAAgB;IA+BxB,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,eAAe,CAAI;IAE3B,OAAO,CAAC,qBAAqB;IA4B7B,OAAO,CAAC,QAAQ;CAGjB"}
|