opencastle 0.10.7 → 0.12.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/README.md +4 -0
- package/bin/cli.mjs +4 -0
- package/dist/cli/convoy/events.d.ts +10 -0
- package/dist/cli/convoy/events.d.ts.map +1 -0
- package/dist/cli/convoy/events.js +27 -0
- package/dist/cli/convoy/events.js.map +1 -0
- package/dist/cli/convoy/events.test.d.ts +2 -0
- package/dist/cli/convoy/events.test.d.ts.map +1 -0
- package/dist/cli/convoy/events.test.js +94 -0
- package/dist/cli/convoy/events.test.js.map +1 -0
- package/dist/cli/convoy/store.d.ts +23 -0
- package/dist/cli/convoy/store.d.ts.map +1 -0
- package/dist/cli/convoy/store.js +210 -0
- package/dist/cli/convoy/store.js.map +1 -0
- package/dist/cli/convoy/store.test.d.ts +2 -0
- package/dist/cli/convoy/store.test.d.ts.map +1 -0
- package/dist/cli/convoy/store.test.js +387 -0
- package/dist/cli/convoy/store.test.js.map +1 -0
- package/dist/cli/convoy/types.d.ts +56 -0
- package/dist/cli/convoy/types.d.ts.map +1 -0
- package/dist/cli/convoy/types.js +2 -0
- package/dist/cli/convoy/types.js.map +1 -0
- package/dist/cli/dashboard.d.ts.map +1 -1
- package/dist/cli/dashboard.js +5 -1
- package/dist/cli/dashboard.js.map +1 -1
- package/dist/cli/init.test.js +1 -1
- package/dist/cli/init.test.js.map +1 -1
- package/dist/cli/lesson.d.ts +17 -0
- package/dist/cli/lesson.d.ts.map +1 -0
- package/dist/cli/lesson.js +294 -0
- package/dist/cli/lesson.js.map +1 -0
- package/dist/cli/log.d.ts +7 -0
- package/dist/cli/log.d.ts.map +1 -0
- package/dist/cli/log.js +131 -0
- package/dist/cli/log.js.map +1 -0
- package/dist/cli/run/executor.js.map +1 -1
- package/dist/cli/run/executor.test.js +1 -0
- package/dist/cli/run/executor.test.js.map +1 -1
- package/dist/cli/run/loop-executor.d.ts +3 -0
- package/dist/cli/run/loop-executor.d.ts.map +1 -0
- package/dist/cli/run/loop-executor.js +155 -0
- package/dist/cli/run/loop-executor.js.map +1 -0
- package/dist/cli/run/loop-reporter.d.ts +6 -0
- package/dist/cli/run/loop-reporter.d.ts.map +1 -0
- package/dist/cli/run/loop-reporter.js +112 -0
- package/dist/cli/run/loop-reporter.js.map +1 -0
- package/dist/cli/run/reporter.d.ts.map +1 -1
- package/dist/cli/run/reporter.js +28 -1
- package/dist/cli/run/reporter.js.map +1 -1
- package/dist/cli/run/schema.d.ts +4 -0
- package/dist/cli/run/schema.d.ts.map +1 -1
- package/dist/cli/run/schema.js +178 -50
- package/dist/cli/run/schema.js.map +1 -1
- package/dist/cli/run/schema.test.js +598 -1
- package/dist/cli/run/schema.test.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +84 -3
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/types.d.ts +78 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +54 -1
- package/dist/cli/update.js.map +1 -1
- package/package.json +3 -2
- package/src/cli/convoy/events.test.ts +118 -0
- package/src/cli/convoy/events.ts +41 -0
- package/src/cli/convoy/store.test.ts +446 -0
- package/src/cli/convoy/store.ts +308 -0
- package/src/cli/convoy/types.ts +68 -0
- package/src/cli/dashboard.ts +5 -1
- package/src/cli/init.test.ts +1 -1
- package/src/cli/lesson.ts +312 -0
- package/src/cli/log.ts +133 -0
- package/src/cli/run/executor.test.ts +1 -0
- package/src/cli/run/executor.ts +8 -8
- package/src/cli/run/loop-executor.ts +199 -0
- package/src/cli/run/loop-reporter.ts +125 -0
- package/src/cli/run/reporter.ts +30 -1
- package/src/cli/run/schema.test.ts +704 -3
- package/src/cli/run/schema.ts +206 -56
- package/src/cli/run.ts +82 -5
- package/src/cli/types.ts +87 -1
- package/src/cli/update.ts +62 -1
- package/src/dashboard/dist/index.html +14 -15
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/scripts/generate-seed-data.ts +23 -43
- package/src/dashboard/seed-data/events.ndjson +104 -0
- package/src/dashboard/src/pages/index.astro +14 -15
- package/src/orchestrator/agents/api-designer.agent.md +1 -1
- package/src/orchestrator/agents/architect.agent.md +1 -1
- package/src/orchestrator/agents/content-engineer.agent.md +1 -1
- package/src/orchestrator/agents/copywriter.agent.md +1 -1
- package/src/orchestrator/agents/data-expert.agent.md +1 -1
- package/src/orchestrator/agents/database-engineer.agent.md +1 -1
- package/src/orchestrator/agents/developer.agent.md +1 -1
- package/src/orchestrator/agents/devops-expert.agent.md +1 -1
- package/src/orchestrator/agents/documentation-writer.agent.md +1 -1
- package/src/orchestrator/agents/performance-expert.agent.md +1 -1
- package/src/orchestrator/agents/release-manager.agent.md +1 -1
- package/src/orchestrator/agents/security-expert.agent.md +1 -1
- package/src/orchestrator/agents/seo-specialist.agent.md +1 -1
- package/src/orchestrator/agents/session-guard.agent.md +9 -21
- package/src/orchestrator/agents/team-lead.agent.md +8 -34
- package/src/orchestrator/agents/testing-expert.agent.md +1 -1
- package/src/orchestrator/agents/ui-ux-expert.agent.md +1 -1
- package/src/orchestrator/customizations/AGENT-PERFORMANCE.md +11 -12
- package/src/orchestrator/customizations/DISPUTES.md +2 -2
- package/src/orchestrator/customizations/README.md +1 -3
- package/src/orchestrator/customizations/logs/README.md +66 -14
- package/src/orchestrator/instructions/ai-optimization.instructions.md +21 -132
- package/src/orchestrator/instructions/general.instructions.md +35 -181
- package/src/orchestrator/plugins/nx/SKILL.md +1 -1
- package/src/orchestrator/prompts/bootstrap-customizations.prompt.md +4 -8
- package/src/orchestrator/prompts/bug-fix.prompt.md +4 -4
- package/src/orchestrator/prompts/implement-feature.prompt.md +3 -3
- package/src/orchestrator/prompts/quick-refinement.prompt.md +3 -3
- package/src/orchestrator/prompts/resolve-pr-comments.prompt.md +1 -1
- package/src/orchestrator/skills/agent-hooks/SKILL.md +11 -11
- package/src/orchestrator/skills/decomposition/SKILL.md +1 -1
- package/src/orchestrator/skills/fast-review/SKILL.md +4 -19
- package/src/orchestrator/skills/git-workflow/SKILL.md +72 -0
- package/src/orchestrator/skills/memory-merger/SKILL.md +1 -1
- package/src/orchestrator/skills/observability-logging/SKILL.md +129 -0
- package/src/orchestrator/skills/orchestration-protocols/SKILL.md +2 -2
- package/src/orchestrator/skills/panel-majority-vote/SKILL.md +4 -7
- package/src/orchestrator/skills/self-improvement/SKILL.md +13 -26
- package/src/orchestrator/skills/team-lead-reference/SKILL.md +2 -2
- package/src/orchestrator/customizations/logs/delegations.ndjson +0 -1
- package/src/orchestrator/customizations/logs/panels.ndjson +0 -1
- package/src/orchestrator/customizations/logs/reviews.ndjson +0 -0
- package/src/orchestrator/customizations/logs/sessions.ndjson +0 -1
- /package/src/orchestrator/customizations/logs/{disputes.ndjson → events.ndjson} +0 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite'
|
|
2
|
+
import type {
|
|
3
|
+
ConvoyRecord,
|
|
4
|
+
ConvoyStatus,
|
|
5
|
+
TaskRecord,
|
|
6
|
+
ConvoyTaskStatus,
|
|
7
|
+
WorkerRecord,
|
|
8
|
+
WorkerStatus,
|
|
9
|
+
EventRecord,
|
|
10
|
+
} from './types.js'
|
|
11
|
+
|
|
12
|
+
const SCHEMA_VERSION = 1
|
|
13
|
+
|
|
14
|
+
export interface ConvoyStore {
|
|
15
|
+
insertConvoy(record: Omit<ConvoyRecord, 'started_at' | 'finished_at'>): void
|
|
16
|
+
getConvoy(id: string): ConvoyRecord | undefined
|
|
17
|
+
updateConvoyStatus(
|
|
18
|
+
id: string,
|
|
19
|
+
status: ConvoyStatus,
|
|
20
|
+
extra?: { started_at?: string; finished_at?: string },
|
|
21
|
+
): void
|
|
22
|
+
insertTask(
|
|
23
|
+
record: Omit<
|
|
24
|
+
TaskRecord,
|
|
25
|
+
'worker_id' | 'worktree' | 'output' | 'exit_code' | 'started_at' | 'finished_at'
|
|
26
|
+
>,
|
|
27
|
+
): void
|
|
28
|
+
getTask(id: string, convoyId: string): TaskRecord | undefined
|
|
29
|
+
getTasksByConvoy(convoyId: string): TaskRecord[]
|
|
30
|
+
updateTaskStatus(
|
|
31
|
+
id: string,
|
|
32
|
+
convoyId: string,
|
|
33
|
+
status: ConvoyTaskStatus,
|
|
34
|
+
extra?: Partial<
|
|
35
|
+
Pick<TaskRecord, 'worker_id' | 'worktree' | 'output' | 'exit_code' | 'started_at' | 'finished_at' | 'retries'>
|
|
36
|
+
>,
|
|
37
|
+
): void
|
|
38
|
+
getReadyTasks(convoyId: string): TaskRecord[]
|
|
39
|
+
insertWorker(record: Omit<WorkerRecord, 'finished_at' | 'last_heartbeat'>): void
|
|
40
|
+
getWorker(id: string): WorkerRecord | undefined
|
|
41
|
+
updateWorkerStatus(
|
|
42
|
+
id: string,
|
|
43
|
+
status: WorkerStatus,
|
|
44
|
+
extra?: Partial<Pick<WorkerRecord, 'finished_at' | 'last_heartbeat' | 'pid'>>,
|
|
45
|
+
): void
|
|
46
|
+
insertEvent(record: Omit<EventRecord, 'id'>): void
|
|
47
|
+
getEvents(convoyId: string): EventRecord[]
|
|
48
|
+
withTransaction<T>(fn: () => T): T
|
|
49
|
+
close(): void
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class ConvoyStoreImpl implements ConvoyStore {
|
|
53
|
+
private db: DatabaseSync
|
|
54
|
+
|
|
55
|
+
constructor(dbPath: string) {
|
|
56
|
+
this.db = new DatabaseSync(dbPath)
|
|
57
|
+
this.db.exec('PRAGMA journal_mode = WAL')
|
|
58
|
+
this.db.exec('PRAGMA synchronous = NORMAL')
|
|
59
|
+
this.initSchema()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private initSchema(): void {
|
|
63
|
+
const row = this.db.prepare('PRAGMA user_version').get() as { user_version: number }
|
|
64
|
+
if (row.user_version === 0) {
|
|
65
|
+
this.db.exec(`
|
|
66
|
+
CREATE TABLE IF NOT EXISTS convoy (
|
|
67
|
+
id TEXT PRIMARY KEY,
|
|
68
|
+
name TEXT NOT NULL,
|
|
69
|
+
spec_hash TEXT NOT NULL,
|
|
70
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
71
|
+
branch TEXT,
|
|
72
|
+
created_at TEXT NOT NULL,
|
|
73
|
+
started_at TEXT,
|
|
74
|
+
finished_at TEXT,
|
|
75
|
+
spec_yaml TEXT NOT NULL
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
CREATE TABLE IF NOT EXISTS task (
|
|
79
|
+
id TEXT PRIMARY KEY,
|
|
80
|
+
convoy_id TEXT NOT NULL REFERENCES convoy(id),
|
|
81
|
+
phase INTEGER NOT NULL,
|
|
82
|
+
prompt TEXT NOT NULL,
|
|
83
|
+
agent TEXT NOT NULL DEFAULT 'developer',
|
|
84
|
+
model TEXT,
|
|
85
|
+
timeout_ms INTEGER NOT NULL DEFAULT 1800000,
|
|
86
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
87
|
+
worker_id TEXT,
|
|
88
|
+
worktree TEXT,
|
|
89
|
+
output TEXT,
|
|
90
|
+
exit_code INTEGER,
|
|
91
|
+
started_at TEXT,
|
|
92
|
+
finished_at TEXT,
|
|
93
|
+
retries INTEGER NOT NULL DEFAULT 0,
|
|
94
|
+
max_retries INTEGER NOT NULL DEFAULT 1,
|
|
95
|
+
files TEXT,
|
|
96
|
+
depends_on TEXT
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
CREATE TABLE IF NOT EXISTS worker (
|
|
100
|
+
id TEXT PRIMARY KEY,
|
|
101
|
+
task_id TEXT REFERENCES task(id),
|
|
102
|
+
adapter TEXT NOT NULL,
|
|
103
|
+
pid INTEGER,
|
|
104
|
+
session_id TEXT,
|
|
105
|
+
status TEXT NOT NULL DEFAULT 'spawned',
|
|
106
|
+
worktree TEXT,
|
|
107
|
+
created_at TEXT NOT NULL,
|
|
108
|
+
finished_at TEXT,
|
|
109
|
+
last_heartbeat TEXT
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
CREATE TABLE IF NOT EXISTS event (
|
|
113
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
114
|
+
convoy_id TEXT REFERENCES convoy(id),
|
|
115
|
+
task_id TEXT,
|
|
116
|
+
worker_id TEXT,
|
|
117
|
+
type TEXT NOT NULL,
|
|
118
|
+
data TEXT,
|
|
119
|
+
created_at TEXT NOT NULL
|
|
120
|
+
);
|
|
121
|
+
`)
|
|
122
|
+
this.db.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
insertConvoy(record: Omit<ConvoyRecord, 'started_at' | 'finished_at'>): void {
|
|
127
|
+
this.db
|
|
128
|
+
.prepare(
|
|
129
|
+
`INSERT INTO convoy (id, name, spec_hash, status, branch, created_at, started_at, finished_at, spec_yaml)
|
|
130
|
+
VALUES (:id, :name, :spec_hash, :status, :branch, :created_at, NULL, NULL, :spec_yaml)`,
|
|
131
|
+
)
|
|
132
|
+
.run(record)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getConvoy(id: string): ConvoyRecord | undefined {
|
|
136
|
+
return this.db
|
|
137
|
+
.prepare('SELECT * FROM convoy WHERE id = :id')
|
|
138
|
+
.get({ id }) as ConvoyRecord | undefined
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
updateConvoyStatus(
|
|
142
|
+
id: string,
|
|
143
|
+
status: ConvoyStatus,
|
|
144
|
+
extra?: { started_at?: string; finished_at?: string },
|
|
145
|
+
): void {
|
|
146
|
+
const sets = ['status = :status']
|
|
147
|
+
const params: Record<string, string | null> = { id, status }
|
|
148
|
+
|
|
149
|
+
if (extra?.started_at !== undefined) {
|
|
150
|
+
sets.push('started_at = :started_at')
|
|
151
|
+
params.started_at = extra.started_at
|
|
152
|
+
}
|
|
153
|
+
if (extra?.finished_at !== undefined) {
|
|
154
|
+
sets.push('finished_at = :finished_at')
|
|
155
|
+
params.finished_at = extra.finished_at
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this.db.prepare(`UPDATE convoy SET ${sets.join(', ')} WHERE id = :id`).run(params)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
insertTask(
|
|
162
|
+
record: Omit<
|
|
163
|
+
TaskRecord,
|
|
164
|
+
'worker_id' | 'worktree' | 'output' | 'exit_code' | 'started_at' | 'finished_at'
|
|
165
|
+
>,
|
|
166
|
+
): void {
|
|
167
|
+
this.db
|
|
168
|
+
.prepare(
|
|
169
|
+
`INSERT INTO task
|
|
170
|
+
(id, convoy_id, phase, prompt, agent, model, timeout_ms, status,
|
|
171
|
+
worker_id, worktree, output, exit_code, started_at, finished_at,
|
|
172
|
+
retries, max_retries, files, depends_on)
|
|
173
|
+
VALUES
|
|
174
|
+
(:id, :convoy_id, :phase, :prompt, :agent, :model, :timeout_ms, :status,
|
|
175
|
+
NULL, NULL, NULL, NULL, NULL, NULL,
|
|
176
|
+
:retries, :max_retries, :files, :depends_on)`,
|
|
177
|
+
)
|
|
178
|
+
.run(record)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
getTask(id: string, convoyId: string): TaskRecord | undefined {
|
|
182
|
+
return this.db
|
|
183
|
+
.prepare('SELECT * FROM task WHERE id = :id AND convoy_id = :convoy_id')
|
|
184
|
+
.get({ id, convoy_id: convoyId }) as TaskRecord | undefined
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
getTasksByConvoy(convoyId: string): TaskRecord[] {
|
|
188
|
+
return this.db
|
|
189
|
+
.prepare('SELECT * FROM task WHERE convoy_id = :convoy_id ORDER BY phase, id')
|
|
190
|
+
.all({ convoy_id: convoyId }) as unknown as TaskRecord[]
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
updateTaskStatus(
|
|
194
|
+
id: string,
|
|
195
|
+
convoyId: string,
|
|
196
|
+
status: ConvoyTaskStatus,
|
|
197
|
+
extra?: Partial<
|
|
198
|
+
Pick<TaskRecord, 'worker_id' | 'worktree' | 'output' | 'exit_code' | 'started_at' | 'finished_at' | 'retries'>
|
|
199
|
+
>,
|
|
200
|
+
): void {
|
|
201
|
+
const sets = ['status = :status']
|
|
202
|
+
const params: Record<string, string | number | null> = { id, convoy_id: convoyId, status }
|
|
203
|
+
const extraFields = ['worker_id', 'worktree', 'output', 'exit_code', 'started_at', 'finished_at', 'retries'] as const
|
|
204
|
+
|
|
205
|
+
if (extra) {
|
|
206
|
+
for (const field of extraFields) {
|
|
207
|
+
if (field in extra && extra[field] !== undefined) {
|
|
208
|
+
sets.push(`${field} = :${field}`)
|
|
209
|
+
params[field] = extra[field] as string | number | null
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this.db
|
|
215
|
+
.prepare(`UPDATE task SET ${sets.join(', ')} WHERE id = :id AND convoy_id = :convoy_id`)
|
|
216
|
+
.run(params)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
getReadyTasks(convoyId: string): TaskRecord[] {
|
|
220
|
+
const allTasks = this.getTasksByConvoy(convoyId)
|
|
221
|
+
const doneTaskIds = new Set(allTasks.filter(t => t.status === 'done').map(t => t.id))
|
|
222
|
+
|
|
223
|
+
return allTasks.filter(task => {
|
|
224
|
+
if (task.status !== 'pending') return false
|
|
225
|
+
if (!task.depends_on) return true
|
|
226
|
+
const deps = JSON.parse(task.depends_on) as string[]
|
|
227
|
+
return deps.length === 0 || deps.every(depId => doneTaskIds.has(depId))
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
insertWorker(record: Omit<WorkerRecord, 'finished_at' | 'last_heartbeat'>): void {
|
|
232
|
+
this.db
|
|
233
|
+
.prepare(
|
|
234
|
+
`INSERT INTO worker
|
|
235
|
+
(id, task_id, adapter, pid, session_id, status, worktree, created_at,
|
|
236
|
+
finished_at, last_heartbeat)
|
|
237
|
+
VALUES
|
|
238
|
+
(:id, :task_id, :adapter, :pid, :session_id, :status, :worktree, :created_at,
|
|
239
|
+
NULL, NULL)`,
|
|
240
|
+
)
|
|
241
|
+
.run(record)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
getWorker(id: string): WorkerRecord | undefined {
|
|
245
|
+
return this.db
|
|
246
|
+
.prepare('SELECT * FROM worker WHERE id = :id')
|
|
247
|
+
.get({ id }) as WorkerRecord | undefined
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
updateWorkerStatus(
|
|
251
|
+
id: string,
|
|
252
|
+
status: WorkerStatus,
|
|
253
|
+
extra?: Partial<Pick<WorkerRecord, 'finished_at' | 'last_heartbeat' | 'pid'>>,
|
|
254
|
+
): void {
|
|
255
|
+
const sets = ['status = :status']
|
|
256
|
+
const params: Record<string, string | number | null> = { id, status }
|
|
257
|
+
|
|
258
|
+
if (extra?.finished_at !== undefined) {
|
|
259
|
+
sets.push('finished_at = :finished_at')
|
|
260
|
+
params.finished_at = extra.finished_at
|
|
261
|
+
}
|
|
262
|
+
if (extra?.last_heartbeat !== undefined) {
|
|
263
|
+
sets.push('last_heartbeat = :last_heartbeat')
|
|
264
|
+
params.last_heartbeat = extra.last_heartbeat
|
|
265
|
+
}
|
|
266
|
+
if (extra?.pid !== undefined) {
|
|
267
|
+
sets.push('pid = :pid')
|
|
268
|
+
params.pid = extra.pid
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
this.db.prepare(`UPDATE worker SET ${sets.join(', ')} WHERE id = :id`).run(params)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
insertEvent(record: Omit<EventRecord, 'id'>): void {
|
|
275
|
+
this.db
|
|
276
|
+
.prepare(
|
|
277
|
+
`INSERT INTO event (convoy_id, task_id, worker_id, type, data, created_at)
|
|
278
|
+
VALUES (:convoy_id, :task_id, :worker_id, :type, :data, :created_at)`,
|
|
279
|
+
)
|
|
280
|
+
.run(record)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
getEvents(convoyId: string): EventRecord[] {
|
|
284
|
+
return this.db
|
|
285
|
+
.prepare('SELECT * FROM event WHERE convoy_id = :convoy_id ORDER BY id')
|
|
286
|
+
.all({ convoy_id: convoyId }) as unknown as EventRecord[]
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
withTransaction<T>(fn: () => T): T {
|
|
290
|
+
this.db.exec('BEGIN')
|
|
291
|
+
try {
|
|
292
|
+
const result = fn()
|
|
293
|
+
this.db.exec('COMMIT')
|
|
294
|
+
return result
|
|
295
|
+
} catch (err) {
|
|
296
|
+
this.db.exec('ROLLBACK')
|
|
297
|
+
throw err
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
close(): void {
|
|
302
|
+
this.db.close()
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function createConvoyStore(dbPath: string): ConvoyStore {
|
|
307
|
+
return new ConvoyStoreImpl(dbPath)
|
|
308
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export type ConvoyStatus = 'pending' | 'running' | 'done' | 'failed' | 'gate-failed'
|
|
2
|
+
|
|
3
|
+
export type ConvoyTaskStatus =
|
|
4
|
+
| 'pending'
|
|
5
|
+
| 'assigned'
|
|
6
|
+
| 'running'
|
|
7
|
+
| 'done'
|
|
8
|
+
| 'failed'
|
|
9
|
+
| 'timed-out'
|
|
10
|
+
| 'skipped'
|
|
11
|
+
|
|
12
|
+
export type WorkerStatus = 'spawned' | 'running' | 'done' | 'failed' | 'killed'
|
|
13
|
+
|
|
14
|
+
export interface ConvoyRecord {
|
|
15
|
+
id: string
|
|
16
|
+
name: string
|
|
17
|
+
spec_hash: string
|
|
18
|
+
status: ConvoyStatus
|
|
19
|
+
branch: string | null
|
|
20
|
+
created_at: string
|
|
21
|
+
started_at: string | null
|
|
22
|
+
finished_at: string | null
|
|
23
|
+
spec_yaml: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface TaskRecord {
|
|
27
|
+
id: string
|
|
28
|
+
convoy_id: string
|
|
29
|
+
phase: number
|
|
30
|
+
prompt: string
|
|
31
|
+
agent: string
|
|
32
|
+
model: string | null
|
|
33
|
+
timeout_ms: number
|
|
34
|
+
status: ConvoyTaskStatus
|
|
35
|
+
worker_id: string | null
|
|
36
|
+
worktree: string | null
|
|
37
|
+
output: string | null
|
|
38
|
+
exit_code: number | null
|
|
39
|
+
started_at: string | null
|
|
40
|
+
finished_at: string | null
|
|
41
|
+
retries: number
|
|
42
|
+
max_retries: number
|
|
43
|
+
files: string | null
|
|
44
|
+
depends_on: string | null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface WorkerRecord {
|
|
48
|
+
id: string
|
|
49
|
+
task_id: string | null
|
|
50
|
+
adapter: string
|
|
51
|
+
pid: number | null
|
|
52
|
+
session_id: string | null
|
|
53
|
+
status: WorkerStatus
|
|
54
|
+
worktree: string | null
|
|
55
|
+
created_at: string
|
|
56
|
+
finished_at: string | null
|
|
57
|
+
last_heartbeat: string | null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface EventRecord {
|
|
61
|
+
id?: number
|
|
62
|
+
convoy_id: string | null
|
|
63
|
+
task_id: string | null
|
|
64
|
+
worker_id: string | null
|
|
65
|
+
type: string
|
|
66
|
+
data: string | null
|
|
67
|
+
created_at: string
|
|
68
|
+
}
|
package/src/cli/dashboard.ts
CHANGED
|
@@ -19,10 +19,13 @@ const MIME_TYPES: Record<string, string> = {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const DATA_FILES = [
|
|
22
|
+
'events.ndjson',
|
|
23
|
+
// Legacy individual files — kept for backwards compatibility
|
|
22
24
|
'sessions.ndjson',
|
|
23
25
|
'delegations.ndjson',
|
|
24
26
|
'panels.ndjson',
|
|
25
27
|
'reviews.ndjson',
|
|
28
|
+
'disputes.ndjson',
|
|
26
29
|
]
|
|
27
30
|
|
|
28
31
|
interface DashboardArgs {
|
|
@@ -122,7 +125,8 @@ export default async function dashboard({
|
|
|
122
125
|
// Check if any log files exist (for messaging)
|
|
123
126
|
let hasLogs = false
|
|
124
127
|
if (!seed) {
|
|
125
|
-
|
|
128
|
+
const checkFiles = ['events.ndjson', ...DATA_FILES]
|
|
129
|
+
for (const f of checkFiles) {
|
|
126
130
|
if (await fileExists(join(logsDir, f))) {
|
|
127
131
|
hasLogs = true
|
|
128
132
|
break
|
package/src/cli/init.test.ts
CHANGED
|
@@ -366,7 +366,7 @@ describe('VS Code adapter install', () => {
|
|
|
366
366
|
|
|
367
367
|
const logsDir = join(tempDir, '.github', 'customizations', 'logs')
|
|
368
368
|
expect(existsSync(logsDir)).toBe(true)
|
|
369
|
-
for (const file of ['
|
|
369
|
+
for (const file of ['events.ndjson']) {
|
|
370
370
|
expect(existsSync(join(logsDir, file))).toBe(true)
|
|
371
371
|
}
|
|
372
372
|
})
|