@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,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StateAggregator:純計算層,解耦 readers 和 consumers
|
|
3
|
+
*
|
|
4
|
+
* 接收所有 reader 的輸出 → 計算統一的 TeamState → 發射 events
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'node:events';
|
|
7
|
+
import { calculateCost, addUsage, emptyUsage, getModelShortName } from './pricing.js';
|
|
8
|
+
import { getDisplayName } from './identity-resolver.js';
|
|
9
|
+
const STUCK_THRESHOLD_MS = 180_000; // 3 分鐘
|
|
10
|
+
export class StateAggregator extends EventEmitter {
|
|
11
|
+
teamConfig = null;
|
|
12
|
+
tasks = [];
|
|
13
|
+
inboxes = new Map();
|
|
14
|
+
cursors = new Map();
|
|
15
|
+
alerts = [];
|
|
16
|
+
startedAt = Date.now();
|
|
17
|
+
budget = null;
|
|
18
|
+
stuckThresholdMs = STUCK_THRESHOLD_MS;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
super();
|
|
21
|
+
if (options?.budget)
|
|
22
|
+
this.budget = options.budget;
|
|
23
|
+
if (options?.stuckThresholdMs)
|
|
24
|
+
this.stuckThresholdMs = options.stuckThresholdMs;
|
|
25
|
+
}
|
|
26
|
+
// ─── Input methods(由 watcher 呼叫)───
|
|
27
|
+
updateTeamConfig(config) {
|
|
28
|
+
this.teamConfig = config;
|
|
29
|
+
if (config.createdAt)
|
|
30
|
+
this.startedAt = config.createdAt;
|
|
31
|
+
}
|
|
32
|
+
updateTasks(tasks) {
|
|
33
|
+
this.tasks = tasks;
|
|
34
|
+
for (const task of tasks) {
|
|
35
|
+
this.emit('event', { type: 'task_updated', task });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
updateInbox(inbox) {
|
|
39
|
+
this.inboxes.set(inbox.agentName, inbox);
|
|
40
|
+
}
|
|
41
|
+
updateCursor(cursor) {
|
|
42
|
+
const prev = this.cursors.get(cursor.agentId);
|
|
43
|
+
this.cursors.set(cursor.agentId, cursor);
|
|
44
|
+
const agent = this.buildAgentState(cursor);
|
|
45
|
+
this.emit('event', { type: 'agent_updated', agent });
|
|
46
|
+
// Budget check
|
|
47
|
+
if (this.budget) {
|
|
48
|
+
const totalCost = this.computeTotalCost();
|
|
49
|
+
this.checkBudgetThresholds(totalCost);
|
|
50
|
+
}
|
|
51
|
+
// Stuck detection
|
|
52
|
+
if (prev && agent.status === 'stuck') {
|
|
53
|
+
const prevAgent = this.buildAgentState(prev);
|
|
54
|
+
if (prevAgent.status !== 'stuck') {
|
|
55
|
+
this.addAlert('warning', `${agent.name}: no activity for ${Math.round(this.stuckThresholdMs / 60000)}m (possibly stuck)`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
markTeamDeleted() {
|
|
60
|
+
this.emit('event', { type: 'team_deleted' });
|
|
61
|
+
}
|
|
62
|
+
// ─── Output:取得當前完整狀態 ───
|
|
63
|
+
getState() {
|
|
64
|
+
const rawAgents = [...this.cursors.values()].map(c => this.buildAgentState(c));
|
|
65
|
+
const agents = deduplicateAgents(rawAgents);
|
|
66
|
+
const totalTokens = this.computeTotalTokens();
|
|
67
|
+
const totalCost = this.computeTotalCost();
|
|
68
|
+
return {
|
|
69
|
+
teamName: this.teamConfig?.name ?? 'unknown',
|
|
70
|
+
startedAt: this.startedAt,
|
|
71
|
+
elapsedMs: Date.now() - this.startedAt,
|
|
72
|
+
agents,
|
|
73
|
+
tasks: this.tasks,
|
|
74
|
+
totalCost,
|
|
75
|
+
totalTokens,
|
|
76
|
+
alerts: this.alerts.slice(-10),
|
|
77
|
+
isActive: this.teamConfig !== null,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// ─── Private ───
|
|
81
|
+
buildAgentState(cursor) {
|
|
82
|
+
const name = getDisplayName(cursor);
|
|
83
|
+
const model = cursor.model ?? 'unknown';
|
|
84
|
+
const cost = calculateCost(model, cursor.accumulated);
|
|
85
|
+
const status = this.inferAgentStatus(cursor);
|
|
86
|
+
// 從 ring buffer 取最後一條的 timestamp 和內容摘要
|
|
87
|
+
const lastEntry = cursor.recentEntries.length > 0
|
|
88
|
+
? cursor.recentEntries[cursor.recentEntries.length - 1]
|
|
89
|
+
: null;
|
|
90
|
+
const lastActivityAt = lastEntry ? new Date(lastEntry.timestamp).getTime() : 0;
|
|
91
|
+
return {
|
|
92
|
+
agentId: cursor.agentId,
|
|
93
|
+
name,
|
|
94
|
+
model: getModelShortName(model),
|
|
95
|
+
status,
|
|
96
|
+
tokenUsage: cursor.accumulated,
|
|
97
|
+
cost,
|
|
98
|
+
lastActivityAt,
|
|
99
|
+
lastActivity: '',
|
|
100
|
+
identityConfidence: cursor.resolvedName ? 'high' : 'low',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
inferAgentStatus(cursor) {
|
|
104
|
+
const name = cursor.resolvedName ?? cursor.agentId;
|
|
105
|
+
// 優先用 inbox 推斷
|
|
106
|
+
const inbox = this.inboxes.get(name);
|
|
107
|
+
if (inbox) {
|
|
108
|
+
if (inbox.inferredStatus === 'shutdown')
|
|
109
|
+
return 'shutdown';
|
|
110
|
+
if (inbox.inferredStatus === 'done')
|
|
111
|
+
return 'done';
|
|
112
|
+
}
|
|
113
|
+
// 用 task owner 推斷
|
|
114
|
+
const ownedTasks = this.tasks.filter(t => t.owner === name);
|
|
115
|
+
const hasActiveTasks = ownedTasks.some(t => t.status === 'in_progress');
|
|
116
|
+
const allDone = ownedTasks.length > 0 && ownedTasks.every(t => t.status === 'completed');
|
|
117
|
+
if (allDone && !hasActiveTasks)
|
|
118
|
+
return 'done';
|
|
119
|
+
// 用 JSONL activity 推斷
|
|
120
|
+
const lastEntry = cursor.recentEntries.length > 0
|
|
121
|
+
? cursor.recentEntries[cursor.recentEntries.length - 1]
|
|
122
|
+
: null;
|
|
123
|
+
if (!lastEntry)
|
|
124
|
+
return 'unknown';
|
|
125
|
+
const lastActivityMs = Date.now() - new Date(lastEntry.timestamp).getTime();
|
|
126
|
+
if (lastActivityMs > this.stuckThresholdMs && hasActiveTasks)
|
|
127
|
+
return 'stuck';
|
|
128
|
+
if (lastActivityMs > 30_000)
|
|
129
|
+
return 'idle';
|
|
130
|
+
return 'working';
|
|
131
|
+
}
|
|
132
|
+
computeTotalTokens() {
|
|
133
|
+
let total = emptyUsage();
|
|
134
|
+
for (const cursor of this.cursors.values()) {
|
|
135
|
+
total = addUsage(total, cursor.accumulated);
|
|
136
|
+
}
|
|
137
|
+
return total;
|
|
138
|
+
}
|
|
139
|
+
computeTotalCost() {
|
|
140
|
+
let total = 0;
|
|
141
|
+
for (const cursor of this.cursors.values()) {
|
|
142
|
+
const model = cursor.model ?? 'claude-opus-4-6';
|
|
143
|
+
total += calculateCost(model, cursor.accumulated);
|
|
144
|
+
}
|
|
145
|
+
return total;
|
|
146
|
+
}
|
|
147
|
+
lastBudgetLevel = 0;
|
|
148
|
+
checkBudgetThresholds(currentCost) {
|
|
149
|
+
if (!this.budget)
|
|
150
|
+
return;
|
|
151
|
+
const pct = currentCost / this.budget;
|
|
152
|
+
let level = 0;
|
|
153
|
+
if (pct >= 1.0)
|
|
154
|
+
level = 4;
|
|
155
|
+
else if (pct >= 0.9)
|
|
156
|
+
level = 3;
|
|
157
|
+
else if (pct >= 0.8)
|
|
158
|
+
level = 2;
|
|
159
|
+
else if (pct >= 0.6)
|
|
160
|
+
level = 1;
|
|
161
|
+
if (level > this.lastBudgetLevel) {
|
|
162
|
+
this.lastBudgetLevel = level;
|
|
163
|
+
if (level >= 3) {
|
|
164
|
+
this.addAlert('critical', `Budget: ${Math.round(pct * 100)}% consumed ($${currentCost.toFixed(2)} / $${this.budget.toFixed(2)})`);
|
|
165
|
+
}
|
|
166
|
+
else if (level >= 1) {
|
|
167
|
+
this.addAlert('warning', `Budget: ${Math.round(pct * 100)}% consumed ($${currentCost.toFixed(2)} / $${this.budget.toFixed(2)})`);
|
|
168
|
+
}
|
|
169
|
+
this.emit('event', {
|
|
170
|
+
type: 'cost_threshold',
|
|
171
|
+
current: currentCost,
|
|
172
|
+
budget: this.budget,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
addAlert(level, message) {
|
|
177
|
+
this.alerts.push({ level, message, timestamp: Date.now() });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// ─── Agent deduplication ───
|
|
181
|
+
// 多輪 session 可能產生多個同名 agent(例如 team-lead 出現 6 次)
|
|
182
|
+
// 策略:合併同名 agents 的 token usage 和 cost
|
|
183
|
+
function deduplicateAgents(agents) {
|
|
184
|
+
const byName = new Map();
|
|
185
|
+
for (const agent of agents) {
|
|
186
|
+
const existing = byName.get(agent.name) ?? [];
|
|
187
|
+
existing.push(agent);
|
|
188
|
+
byName.set(agent.name, existing);
|
|
189
|
+
}
|
|
190
|
+
const result = [];
|
|
191
|
+
for (const [, group] of byName) {
|
|
192
|
+
if (group.length === 1) {
|
|
193
|
+
result.push(group[0]);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
// 合併:加總 tokens/cost,取最新的 status 和 activity
|
|
197
|
+
let mergedUsage = emptyUsage();
|
|
198
|
+
let mergedCost = 0;
|
|
199
|
+
let latestActivity = 0;
|
|
200
|
+
let latestAgent = group[0];
|
|
201
|
+
for (const agent of group) {
|
|
202
|
+
mergedUsage = addUsage(mergedUsage, agent.tokenUsage);
|
|
203
|
+
mergedCost += agent.cost;
|
|
204
|
+
if (agent.lastActivityAt > latestActivity) {
|
|
205
|
+
latestActivity = agent.lastActivityAt;
|
|
206
|
+
latestAgent = agent;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
result.push({
|
|
210
|
+
...latestAgent,
|
|
211
|
+
tokenUsage: mergedUsage,
|
|
212
|
+
cost: mergedCost,
|
|
213
|
+
lastActivityAt: latestActivity,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=state-aggregator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-aggregator.js","sourceRoot":"","sources":["../../src/core/state-aggregator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TaskData } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 讀取單一 task
|
|
4
|
+
*/
|
|
5
|
+
export declare function readTask(teamName: string, taskId: string): Promise<TaskData | null>;
|
|
6
|
+
/**
|
|
7
|
+
* 讀取 team 的所有 tasks
|
|
8
|
+
*/
|
|
9
|
+
export declare function readAllTasks(teamName: string): Promise<TaskData[]>;
|
|
10
|
+
/**
|
|
11
|
+
* 從 tasks 中推斷 agent 名稱(owner 欄位)
|
|
12
|
+
*/
|
|
13
|
+
export declare function extractAgentNames(tasks: readonly TaskData[]): string[];
|
|
14
|
+
//# sourceMappingURL=task-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-reader.d.ts","sourceRoot":"","sources":["../../src/core/task-reader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C;;GAEG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAUzF;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAiBxE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,MAAM,EAAE,CAQtE"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TaskReader:讀取 ~/.claude/tasks/{team}/*.json
|
|
3
|
+
*/
|
|
4
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
5
|
+
import { claudePaths } from './paths.js';
|
|
6
|
+
import { TaskDataSchema, safeParseJson } from './data-source-adapter.js';
|
|
7
|
+
/**
|
|
8
|
+
* 讀取單一 task
|
|
9
|
+
*/
|
|
10
|
+
export async function readTask(teamName, taskId) {
|
|
11
|
+
const taskPath = claudePaths.teamTask(teamName, taskId);
|
|
12
|
+
try {
|
|
13
|
+
const raw = await readFile(taskPath, 'utf-8');
|
|
14
|
+
const result = safeParseJson(raw, TaskDataSchema);
|
|
15
|
+
if (result.ok)
|
|
16
|
+
return result.data;
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 讀取 team 的所有 tasks
|
|
25
|
+
*/
|
|
26
|
+
export async function readAllTasks(teamName) {
|
|
27
|
+
const dir = claudePaths.teamTasks(teamName);
|
|
28
|
+
try {
|
|
29
|
+
const entries = await readdir(dir);
|
|
30
|
+
const jsonFiles = entries.filter(f => f.endsWith('.json'));
|
|
31
|
+
const tasks = await Promise.all(jsonFiles.map(async (filename) => {
|
|
32
|
+
const taskId = filename.replace('.json', '');
|
|
33
|
+
return readTask(teamName, taskId);
|
|
34
|
+
}));
|
|
35
|
+
return tasks
|
|
36
|
+
.filter((t) => t !== null)
|
|
37
|
+
.sort((a, b) => Number(a.id) - Number(b.id));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 從 tasks 中推斷 agent 名稱(owner 欄位)
|
|
45
|
+
*/
|
|
46
|
+
export function extractAgentNames(tasks) {
|
|
47
|
+
const names = new Set();
|
|
48
|
+
for (const task of tasks) {
|
|
49
|
+
if (task.owner && task.owner.length > 0) {
|
|
50
|
+
names.add(task.owner);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [...names].sort();
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=task-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-reader.js","sourceRoot":"","sources":["../../src/core/task-reader.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAGxE;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAgB,EAAE,MAAc;IAC7D,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IACvD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAC7C,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,EAAE,cAAc,CAAC,CAAA;QACjD,IAAI,MAAM,CAAC,EAAE;YAAE,OAAO,MAAM,CAAC,IAAI,CAAA;QACjC,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,MAAM,GAAG,GAAG,WAAW,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;IAC3C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAA;QAClC,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAA;QAC1D,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAC7B,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;YAC/B,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;YAC5C,OAAO,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACnC,CAAC,CAAC,CACH,CAAA;QACD,OAAO,KAAK;aACT,MAAM,CAAC,CAAC,CAAC,EAAiB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;aACxC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAA0B;IAC1D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAA;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAA;AAC1B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { TeamConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* 讀取單一 team 的 config
|
|
4
|
+
*/
|
|
5
|
+
export declare function readTeamConfig(teamName: string): Promise<TeamConfig | null>;
|
|
6
|
+
/**
|
|
7
|
+
* 列出所有現存 team 名稱
|
|
8
|
+
*/
|
|
9
|
+
export declare function listTeamNames(): Promise<string[]>;
|
|
10
|
+
/**
|
|
11
|
+
* 列出所有「活躍」的 team(有 config.json)
|
|
12
|
+
*/
|
|
13
|
+
export declare function listActiveTeams(): Promise<TeamConfig[]>;
|
|
14
|
+
/**
|
|
15
|
+
* 檢查 team 是否存在
|
|
16
|
+
*/
|
|
17
|
+
export declare function teamExists(teamName: string): Promise<boolean>;
|
|
18
|
+
//# sourceMappingURL=team-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team-reader.d.ts","sourceRoot":"","sources":["../../src/core/team-reader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;GAEG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAUjF;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAkBvD;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAI7D;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TeamReader:讀取 ~/.claude/teams/{team}/config.json
|
|
3
|
+
*/
|
|
4
|
+
import { readFile, readdir, access } from 'node:fs/promises';
|
|
5
|
+
import { claudePaths } from './paths.js';
|
|
6
|
+
import { TeamConfigSchema, safeParseJson } from './data-source-adapter.js';
|
|
7
|
+
/**
|
|
8
|
+
* 讀取單一 team 的 config
|
|
9
|
+
*/
|
|
10
|
+
export async function readTeamConfig(teamName) {
|
|
11
|
+
const configPath = claudePaths.teamConfig(teamName);
|
|
12
|
+
try {
|
|
13
|
+
const raw = await readFile(configPath, 'utf-8');
|
|
14
|
+
const result = safeParseJson(raw, TeamConfigSchema);
|
|
15
|
+
if (result.ok)
|
|
16
|
+
return result.data;
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 列出所有現存 team 名稱
|
|
25
|
+
*/
|
|
26
|
+
export async function listTeamNames() {
|
|
27
|
+
try {
|
|
28
|
+
const entries = await readdir(claudePaths.teams, { withFileTypes: true });
|
|
29
|
+
const teams = [];
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
if (!entry.isDirectory())
|
|
32
|
+
continue;
|
|
33
|
+
const configPath = claudePaths.teamConfig(entry.name);
|
|
34
|
+
try {
|
|
35
|
+
await access(configPath);
|
|
36
|
+
teams.push(entry.name);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// config.json 不存在,跳過
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return teams.sort();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 列出所有「活躍」的 team(有 config.json)
|
|
50
|
+
*/
|
|
51
|
+
export async function listActiveTeams() {
|
|
52
|
+
const names = await listTeamNames();
|
|
53
|
+
const configs = await Promise.all(names.map(readTeamConfig));
|
|
54
|
+
return configs.filter((c) => c !== null);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 檢查 team 是否存在
|
|
58
|
+
*/
|
|
59
|
+
export async function teamExists(teamName) {
|
|
60
|
+
try {
|
|
61
|
+
await access(claudePaths.teamConfig(teamName));
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=team-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team-reader.js","sourceRoot":"","sources":["../../src/core/team-reader.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAG1E;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAgB;IACnD,MAAM,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;IACnD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;QAC/C,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAA;QACnD,IAAI,MAAM,CAAC,EAAE;YAAE,OAAO,MAAM,CAAC,IAAI,CAAA;QACjC,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QACzE,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;gBAAE,SAAQ;YAClC,MAAM,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACrD,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,UAAU,CAAC,CAAA;gBACxB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,qBAAqB;YACvB,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAA;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,KAAK,GAAG,MAAM,aAAa,EAAE,CAAA;IACnC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAA;IAC5D,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAmB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAA;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAA;QAC9C,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ccx 核心型別定義
|
|
3
|
+
*
|
|
4
|
+
* Claude Code 原始資料型別直接從 zod schema 推導(見 data-source-adapter.ts)
|
|
5
|
+
* 這裡只定義 ccx 內部狀態型別
|
|
6
|
+
*/
|
|
7
|
+
import type { z } from 'zod';
|
|
8
|
+
import type { TeamConfigSchema, TeamMemberSchema, TaskDataSchema, InboxMessageSchema, JournalEntrySchema } from './data-source-adapter.js';
|
|
9
|
+
export type TeamConfig = z.output<typeof TeamConfigSchema>;
|
|
10
|
+
export type TeamMember = z.output<typeof TeamMemberSchema>;
|
|
11
|
+
export type TaskData = z.output<typeof TaskDataSchema>;
|
|
12
|
+
export type InboxMessage = z.output<typeof InboxMessageSchema>;
|
|
13
|
+
export type JournalEntry = z.output<typeof JournalEntrySchema>;
|
|
14
|
+
export type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'deleted';
|
|
15
|
+
export interface TokenUsage {
|
|
16
|
+
readonly input_tokens: number;
|
|
17
|
+
readonly output_tokens: number;
|
|
18
|
+
readonly cache_creation_input_tokens: number;
|
|
19
|
+
readonly cache_read_input_tokens: number;
|
|
20
|
+
}
|
|
21
|
+
export type AgentStatus = 'idle' | 'thinking' | 'working' | 'stuck' | 'done' | 'shutdown' | 'unknown';
|
|
22
|
+
export interface AgentState {
|
|
23
|
+
readonly agentId: string;
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly model: string;
|
|
26
|
+
readonly status: AgentStatus;
|
|
27
|
+
readonly tokenUsage: TokenUsage;
|
|
28
|
+
readonly cost: number;
|
|
29
|
+
readonly lastActivityAt: number;
|
|
30
|
+
readonly lastActivity: string;
|
|
31
|
+
readonly identityConfidence: IdentityConfidence;
|
|
32
|
+
}
|
|
33
|
+
export type IdentityConfidence = 'high' | 'medium' | 'low';
|
|
34
|
+
export interface TeamState {
|
|
35
|
+
readonly teamName: string;
|
|
36
|
+
readonly startedAt: number;
|
|
37
|
+
readonly elapsedMs: number;
|
|
38
|
+
readonly agents: readonly AgentState[];
|
|
39
|
+
readonly tasks: readonly TaskData[];
|
|
40
|
+
readonly totalCost: number;
|
|
41
|
+
readonly totalTokens: TokenUsage;
|
|
42
|
+
readonly alerts: readonly Alert[];
|
|
43
|
+
readonly isActive: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface Alert {
|
|
46
|
+
readonly level: 'info' | 'warning' | 'critical';
|
|
47
|
+
readonly message: string;
|
|
48
|
+
readonly timestamp: number;
|
|
49
|
+
}
|
|
50
|
+
export type CursorState = 'UNRESOLVED' | 'RESOLVED';
|
|
51
|
+
export interface FileCursor {
|
|
52
|
+
readonly path: string;
|
|
53
|
+
readonly agentId: string;
|
|
54
|
+
readonly lastByteOffset: number;
|
|
55
|
+
readonly lastInode: number;
|
|
56
|
+
readonly state: CursorState;
|
|
57
|
+
readonly resolvedName: string | null;
|
|
58
|
+
readonly accumulated: TokenUsage;
|
|
59
|
+
readonly model: string | null;
|
|
60
|
+
readonly recentEntries: readonly JournalEntry[];
|
|
61
|
+
}
|
|
62
|
+
export interface SessionSnapshot {
|
|
63
|
+
readonly teamName: string;
|
|
64
|
+
readonly sessionId: string;
|
|
65
|
+
readonly startedAt: number;
|
|
66
|
+
readonly lastUpdatedAt: number;
|
|
67
|
+
readonly finalized: boolean;
|
|
68
|
+
readonly agents: readonly AgentSnapshotEntry[];
|
|
69
|
+
readonly tasks: readonly TaskData[];
|
|
70
|
+
readonly totalCost: number;
|
|
71
|
+
readonly totalTokens: TokenUsage;
|
|
72
|
+
readonly alerts: readonly Alert[];
|
|
73
|
+
}
|
|
74
|
+
export interface AgentSnapshotEntry {
|
|
75
|
+
readonly agentId: string;
|
|
76
|
+
readonly name: string;
|
|
77
|
+
readonly model: string;
|
|
78
|
+
readonly tokenUsage: TokenUsage;
|
|
79
|
+
readonly cost: number;
|
|
80
|
+
readonly status: AgentStatus;
|
|
81
|
+
}
|
|
82
|
+
export type StateEvent = {
|
|
83
|
+
readonly type: 'agent_updated';
|
|
84
|
+
readonly agent: AgentState;
|
|
85
|
+
} | {
|
|
86
|
+
readonly type: 'task_updated';
|
|
87
|
+
readonly task: TaskData;
|
|
88
|
+
} | {
|
|
89
|
+
readonly type: 'alert';
|
|
90
|
+
readonly alert: Alert;
|
|
91
|
+
} | {
|
|
92
|
+
readonly type: 'team_deleted';
|
|
93
|
+
} | {
|
|
94
|
+
readonly type: 'cost_threshold';
|
|
95
|
+
readonly current: number;
|
|
96
|
+
readonly budget: number;
|
|
97
|
+
} | {
|
|
98
|
+
readonly type: 'snapshot_written';
|
|
99
|
+
readonly path: string;
|
|
100
|
+
} | {
|
|
101
|
+
readonly type: 'watcher_error';
|
|
102
|
+
readonly source: string;
|
|
103
|
+
readonly message: string;
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { StateAggregator } from './state-aggregator.js';
|
|
2
|
+
import { SnapshotManager } from './snapshot-manager.js';
|
|
3
|
+
export interface WatchOptions {
|
|
4
|
+
readonly teamName: string;
|
|
5
|
+
readonly budget?: number;
|
|
6
|
+
readonly stuckTimeoutMs?: number;
|
|
7
|
+
readonly onStateChange?: () => void;
|
|
8
|
+
readonly onError?: (err: Error) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface WatchHandle {
|
|
11
|
+
readonly aggregator: StateAggregator;
|
|
12
|
+
readonly snapshotManager: SnapshotManager;
|
|
13
|
+
stop: () => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare function startWatch(options: WatchOptions): Promise<WatchHandle>;
|
|
16
|
+
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/core/watcher.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAGvD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;CACxC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAA;IACpC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAA;IACzC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA6L5E"}
|