@jawkx1999/opencr 0.1.0-alpha.2 → 0.1.0-alpha.4
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/dist/client/assets/index-C9jeaZcr.css +1 -0
- package/dist/client/assets/index-DF6LDO7P.js +1978 -0
- package/dist/client/index.html +3 -3
- package/dist/server/cli/index.js +32 -1
- package/dist/server/cli/index.js.map +1 -1
- package/dist/server/core/agents/opencode.js +685 -0
- package/dist/server/core/agents/opencode.js.map +1 -0
- package/dist/server/core/reviewDiagnostics.js +116 -0
- package/dist/server/core/reviewDiagnostics.js.map +1 -0
- package/dist/server/core/reviewPayload.js +7 -0
- package/dist/server/core/reviewPayload.js.map +1 -1
- package/dist/server/core/reviewRequest.js +4 -1
- package/dist/server/core/reviewRequest.js.map +1 -1
- package/dist/server/core/reviewSource/commitish.js +1 -1
- package/dist/server/core/reviewSource/commitish.js.map +1 -1
- package/dist/server/core/reviewSource/githubPr.js +31 -0
- package/dist/server/core/reviewSource/githubPr.js.map +1 -1
- package/dist/server/core/reviewSource/index.js +2 -2
- package/dist/server/core/reviewSource/index.js.map +1 -1
- package/dist/server/core/server.js +27 -6
- package/dist/server/core/server.js.map +1 -1
- package/dist/server/core/workspace/identity.js +47 -0
- package/dist/server/core/workspace/identity.js.map +1 -0
- package/dist/server/core/workspace/repository.js +399 -0
- package/dist/server/core/workspace/repository.js.map +1 -0
- package/dist/server/server/app.js +204 -2
- package/dist/server/server/app.js.map +1 -1
- package/dist/server/shared/types.js +1 -1
- package/dist/server/shared/types.js.map +1 -1
- package/package.json +5 -4
- package/dist/client/assets/index-CMvi92Od.js +0 -1974
- package/dist/client/assets/index-dwKNSbA7.css +0 -1
- package/dist/server/cli/utils.js +0 -2
- package/dist/server/cli/utils.js.map +0 -1
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { mkdirSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
5
|
+
import { DEFAULT_REVIEW_PANEL_WIDTH_PX } from '../../shared/types.js';
|
|
6
|
+
const DEFAULT_DB_PATH = resolve(homedir(), '.opencr', 'state.db');
|
|
7
|
+
const isObject = (value) => {
|
|
8
|
+
return typeof value === 'object' && value !== null;
|
|
9
|
+
};
|
|
10
|
+
const isSelectionSide = (value) => {
|
|
11
|
+
return value === 'additions' || value === 'deletions';
|
|
12
|
+
};
|
|
13
|
+
const isAgentBackendId = (value) => {
|
|
14
|
+
return value === 'opencode' || value === 'codex';
|
|
15
|
+
};
|
|
16
|
+
const isPersistedRange = (value) => {
|
|
17
|
+
if (!isObject(value)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (typeof value.start !== 'number' || typeof value.end !== 'number') {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (value.side !== undefined && !isSelectionSide(value.side)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (value.endSide !== undefined && !isSelectionSide(value.endSide)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
};
|
|
31
|
+
const isPersistedSelection = (value) => {
|
|
32
|
+
if (!isObject(value)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return (typeof value.fileId === 'string' &&
|
|
36
|
+
typeof value.fileLabel === 'string' &&
|
|
37
|
+
isPersistedRange(value.range));
|
|
38
|
+
};
|
|
39
|
+
const isPersistedNote = (value) => {
|
|
40
|
+
if (!isObject(value)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return (typeof value.id === 'string' &&
|
|
44
|
+
typeof value.body === 'string' &&
|
|
45
|
+
typeof value.fileId === 'string' &&
|
|
46
|
+
typeof value.fileLabel === 'string' &&
|
|
47
|
+
isPersistedRange(value.range));
|
|
48
|
+
};
|
|
49
|
+
const parseAgentSessionTarget = (value) => {
|
|
50
|
+
if (!isObject(value)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
if (!isAgentBackendId(value.backendId)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (value.modelId !== undefined && value.modelId !== null && typeof value.modelId !== 'string') {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
if (value.variant !== undefined && value.variant !== null && typeof value.variant !== 'string') {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const modelId = value.modelId === null || typeof value.modelId === 'string' ? (value.modelId ?? null) : null;
|
|
63
|
+
const variant = value.variant === null || typeof value.variant === 'string' ? (value.variant ?? null) : null;
|
|
64
|
+
return {
|
|
65
|
+
backendId: value.backendId,
|
|
66
|
+
modelId,
|
|
67
|
+
variant,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
const parsePersistedAgentSession = (value) => {
|
|
71
|
+
if (!isObject(value)) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
if (typeof value.id !== 'string' ||
|
|
75
|
+
typeof value.title !== 'string' ||
|
|
76
|
+
typeof value.prompt !== 'string' ||
|
|
77
|
+
typeof value.createdAt !== 'string' ||
|
|
78
|
+
typeof value.updatedAt !== 'string' ||
|
|
79
|
+
typeof value.fileId !== 'string' ||
|
|
80
|
+
typeof value.fileLabel !== 'string' ||
|
|
81
|
+
!isPersistedRange(value.range)) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const legacyOpencodeSessionId = typeof value.opencodeSessionId === 'string' ? value.opencodeSessionId : null;
|
|
85
|
+
const backendId = isAgentBackendId(value.backendId)
|
|
86
|
+
? value.backendId
|
|
87
|
+
: legacyOpencodeSessionId != null
|
|
88
|
+
? 'opencode'
|
|
89
|
+
: null;
|
|
90
|
+
const backendSessionId = typeof value.backendSessionId === 'string' ? value.backendSessionId : legacyOpencodeSessionId;
|
|
91
|
+
if (backendId == null || backendSessionId == null) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
if (value.modelId !== undefined && value.modelId !== null && typeof value.modelId !== 'string') {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
if (value.variant !== undefined && value.variant !== null && typeof value.variant !== 'string') {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const modelId = value.modelId === null || typeof value.modelId === 'string' ? (value.modelId ?? null) : null;
|
|
101
|
+
const variant = value.variant === null || typeof value.variant === 'string' ? (value.variant ?? null) : null;
|
|
102
|
+
return {
|
|
103
|
+
id: value.id,
|
|
104
|
+
backendId,
|
|
105
|
+
backendSessionId,
|
|
106
|
+
modelId,
|
|
107
|
+
variant,
|
|
108
|
+
title: value.title,
|
|
109
|
+
prompt: value.prompt,
|
|
110
|
+
createdAt: value.createdAt,
|
|
111
|
+
updatedAt: value.updatedAt,
|
|
112
|
+
fileId: value.fileId,
|
|
113
|
+
fileLabel: value.fileLabel,
|
|
114
|
+
range: value.range,
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
const parseWorkspace = (json) => {
|
|
118
|
+
try {
|
|
119
|
+
const parsed = JSON.parse(json);
|
|
120
|
+
if (!isObject(parsed)) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
if (!Array.isArray(parsed.notes) || !parsed.notes.every(isPersistedNote)) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
if (parsed.agentSessions !== undefined && !Array.isArray(parsed.agentSessions)) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const agentSessions = [];
|
|
130
|
+
for (const agentSession of parsed.agentSessions ?? []) {
|
|
131
|
+
const parsedSession = parsePersistedAgentSession(agentSession);
|
|
132
|
+
if (parsedSession == null) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
agentSessions.push(parsedSession);
|
|
136
|
+
}
|
|
137
|
+
let agentDraftTarget = null;
|
|
138
|
+
if (parsed.agentDraftTarget !== undefined && parsed.agentDraftTarget !== null) {
|
|
139
|
+
agentDraftTarget = parseAgentSessionTarget(parsed.agentDraftTarget);
|
|
140
|
+
if (agentDraftTarget == null) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (parsed.draftSelection !== null && !isPersistedSelection(parsed.draftSelection)) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
if (typeof parsed.draftNoteBody !== 'string') {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
if (parsed.activeNoteId !== null && typeof parsed.activeNoteId !== 'string') {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
if (parsed.activeAgentSessionId !== undefined &&
|
|
154
|
+
parsed.activeAgentSessionId !== null &&
|
|
155
|
+
typeof parsed.activeAgentSessionId !== 'string') {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
if (parsed.activeSidebarPanel !== undefined &&
|
|
159
|
+
parsed.activeSidebarPanel !== 'notes' &&
|
|
160
|
+
parsed.activeSidebarPanel !== 'agents') {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
if (typeof parsed.isPanelOpen !== 'boolean') {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
if (parsed.panelWidthPx !== undefined &&
|
|
167
|
+
(typeof parsed.panelWidthPx !== 'number' ||
|
|
168
|
+
!Number.isFinite(parsed.panelWidthPx) ||
|
|
169
|
+
parsed.panelWidthPx <= 0)) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
if (parsed.diffViewMode !== 'split' && parsed.diffViewMode !== 'unified') {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
notes: parsed.notes,
|
|
177
|
+
agentSessions,
|
|
178
|
+
agentDraftTarget,
|
|
179
|
+
draftSelection: parsed.draftSelection,
|
|
180
|
+
draftNoteBody: parsed.draftNoteBody,
|
|
181
|
+
activeNoteId: parsed.activeNoteId,
|
|
182
|
+
activeAgentSessionId: parsed.activeAgentSessionId ?? null,
|
|
183
|
+
activeSidebarPanel: parsed.activeSidebarPanel ?? 'notes',
|
|
184
|
+
isPanelOpen: parsed.isPanelOpen,
|
|
185
|
+
panelWidthPx: parsed.panelWidthPx ?? DEFAULT_REVIEW_PANEL_WIDTH_PX,
|
|
186
|
+
diffViewMode: parsed.diffViewMode,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const WORKSPACES_TABLE_SQL = `
|
|
194
|
+
CREATE TABLE IF NOT EXISTS workspaces (
|
|
195
|
+
target_id TEXT PRIMARY KEY,
|
|
196
|
+
last_saved_snapshot_id TEXT NOT NULL,
|
|
197
|
+
revision INTEGER NOT NULL,
|
|
198
|
+
state_json TEXT NOT NULL,
|
|
199
|
+
created_at TEXT NOT NULL,
|
|
200
|
+
updated_at TEXT NOT NULL
|
|
201
|
+
) STRICT;
|
|
202
|
+
`;
|
|
203
|
+
export class ReviewWorkspaceRepository {
|
|
204
|
+
db;
|
|
205
|
+
constructor(databasePath = DEFAULT_DB_PATH) {
|
|
206
|
+
mkdirSync(dirname(databasePath), { recursive: true });
|
|
207
|
+
this.db = new DatabaseSync(databasePath, {
|
|
208
|
+
timeout: 2000,
|
|
209
|
+
enableForeignKeyConstraints: true,
|
|
210
|
+
});
|
|
211
|
+
this.db.exec('PRAGMA journal_mode = WAL;');
|
|
212
|
+
this.db.exec('PRAGMA foreign_keys = ON;');
|
|
213
|
+
this.migrateSchema();
|
|
214
|
+
this.db.exec(WORKSPACES_TABLE_SQL);
|
|
215
|
+
}
|
|
216
|
+
close() {
|
|
217
|
+
this.db.close();
|
|
218
|
+
}
|
|
219
|
+
loadWorkspace(targetId) {
|
|
220
|
+
const row = this.getWorkspaceRow(targetId);
|
|
221
|
+
if (row == null) {
|
|
222
|
+
return {
|
|
223
|
+
revision: null,
|
|
224
|
+
savedSnapshotId: null,
|
|
225
|
+
workspace: null,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const workspace = parseWorkspace(row.state_json);
|
|
229
|
+
if (workspace == null) {
|
|
230
|
+
return {
|
|
231
|
+
revision: null,
|
|
232
|
+
savedSnapshotId: null,
|
|
233
|
+
workspace: null,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
revision: row.revision,
|
|
238
|
+
savedSnapshotId: row.last_saved_snapshot_id,
|
|
239
|
+
workspace,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
clearWorkspace(targetId) {
|
|
243
|
+
this.db.prepare('DELETE FROM workspaces WHERE target_id = ?').run(targetId);
|
|
244
|
+
}
|
|
245
|
+
saveWorkspace(input) {
|
|
246
|
+
const existingWorkspace = this.getWorkspaceRow(input.targetId);
|
|
247
|
+
if (existingWorkspace == null) {
|
|
248
|
+
if (input.baseRevision !== 0) {
|
|
249
|
+
return {
|
|
250
|
+
status: 'conflict',
|
|
251
|
+
revision: 0,
|
|
252
|
+
workspace: input.workspace,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const now = new Date().toISOString();
|
|
256
|
+
this.db
|
|
257
|
+
.prepare(`
|
|
258
|
+
INSERT INTO workspaces (
|
|
259
|
+
target_id,
|
|
260
|
+
last_saved_snapshot_id,
|
|
261
|
+
revision,
|
|
262
|
+
state_json,
|
|
263
|
+
created_at,
|
|
264
|
+
updated_at
|
|
265
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
266
|
+
`)
|
|
267
|
+
.run(input.targetId, input.snapshotId, 1, JSON.stringify(input.workspace), now, now);
|
|
268
|
+
return {
|
|
269
|
+
status: 'saved',
|
|
270
|
+
revision: 1,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
if (existingWorkspace.revision !== input.baseRevision) {
|
|
274
|
+
const latestWorkspace = parseWorkspace(existingWorkspace.state_json) ?? input.workspace;
|
|
275
|
+
return {
|
|
276
|
+
status: 'conflict',
|
|
277
|
+
revision: existingWorkspace.revision,
|
|
278
|
+
workspace: latestWorkspace,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const nextRevision = existingWorkspace.revision + 1;
|
|
282
|
+
const now = new Date().toISOString();
|
|
283
|
+
this.db
|
|
284
|
+
.prepare(`
|
|
285
|
+
UPDATE workspaces
|
|
286
|
+
SET last_saved_snapshot_id = ?,
|
|
287
|
+
revision = ?,
|
|
288
|
+
state_json = ?,
|
|
289
|
+
updated_at = ?
|
|
290
|
+
WHERE target_id = ?
|
|
291
|
+
`)
|
|
292
|
+
.run(input.snapshotId, nextRevision, JSON.stringify(input.workspace), now, input.targetId);
|
|
293
|
+
return {
|
|
294
|
+
status: 'saved',
|
|
295
|
+
revision: nextRevision,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
migrateSchema() {
|
|
299
|
+
const workspaceColumns = this.getTableColumns('workspaces');
|
|
300
|
+
if (workspaceColumns.length === 0) {
|
|
301
|
+
this.dropLegacyTables();
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const isLegacySchema = workspaceColumns.includes('snapshot_id') || !workspaceColumns.includes('last_saved_snapshot_id');
|
|
305
|
+
if (isLegacySchema) {
|
|
306
|
+
this.migrateLegacyWorkspaceTable();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
this.dropLegacyTables();
|
|
310
|
+
}
|
|
311
|
+
migrateLegacyWorkspaceTable() {
|
|
312
|
+
const legacyRows = this.db
|
|
313
|
+
.prepare(`
|
|
314
|
+
SELECT target_id, snapshot_id, revision, state_json, created_at, updated_at
|
|
315
|
+
FROM workspaces
|
|
316
|
+
ORDER BY updated_at DESC
|
|
317
|
+
`)
|
|
318
|
+
.all() ?? [];
|
|
319
|
+
this.db.exec('BEGIN TRANSACTION');
|
|
320
|
+
try {
|
|
321
|
+
this.db.exec('DROP TABLE IF EXISTS workspaces_v2');
|
|
322
|
+
this.db.exec(`
|
|
323
|
+
CREATE TABLE workspaces_v2 (
|
|
324
|
+
target_id TEXT PRIMARY KEY,
|
|
325
|
+
last_saved_snapshot_id TEXT NOT NULL,
|
|
326
|
+
revision INTEGER NOT NULL,
|
|
327
|
+
state_json TEXT NOT NULL,
|
|
328
|
+
created_at TEXT NOT NULL,
|
|
329
|
+
updated_at TEXT NOT NULL
|
|
330
|
+
) STRICT;
|
|
331
|
+
`);
|
|
332
|
+
const insertWorkspace = this.db.prepare(`
|
|
333
|
+
INSERT INTO workspaces_v2 (
|
|
334
|
+
target_id,
|
|
335
|
+
last_saved_snapshot_id,
|
|
336
|
+
revision,
|
|
337
|
+
state_json,
|
|
338
|
+
created_at,
|
|
339
|
+
updated_at
|
|
340
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
341
|
+
`);
|
|
342
|
+
const migratedTargetIds = new Set();
|
|
343
|
+
for (const row of legacyRows) {
|
|
344
|
+
if (migratedTargetIds.has(row.target_id)) {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (parseWorkspace(row.state_json) == null) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
insertWorkspace.run(row.target_id, row.snapshot_id, row.revision, row.state_json, row.created_at, row.updated_at);
|
|
351
|
+
migratedTargetIds.add(row.target_id);
|
|
352
|
+
}
|
|
353
|
+
this.db.exec('DROP TABLE workspaces');
|
|
354
|
+
this.dropLegacyTables();
|
|
355
|
+
this.db.exec('ALTER TABLE workspaces_v2 RENAME TO workspaces');
|
|
356
|
+
this.db.exec('COMMIT');
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
this.db.exec('ROLLBACK');
|
|
360
|
+
throw error;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
dropLegacyTables() {
|
|
364
|
+
if (this.tableExists('review_snapshots')) {
|
|
365
|
+
this.db.exec('DROP TABLE review_snapshots');
|
|
366
|
+
}
|
|
367
|
+
if (this.tableExists('review_targets')) {
|
|
368
|
+
this.db.exec('DROP TABLE review_targets');
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
tableExists(tableName) {
|
|
372
|
+
const row = this.db
|
|
373
|
+
.prepare(`
|
|
374
|
+
SELECT name
|
|
375
|
+
FROM sqlite_master
|
|
376
|
+
WHERE type = 'table'
|
|
377
|
+
AND name = ?
|
|
378
|
+
`)
|
|
379
|
+
.get(tableName);
|
|
380
|
+
return row != null;
|
|
381
|
+
}
|
|
382
|
+
getTableColumns(tableName) {
|
|
383
|
+
if (!this.tableExists(tableName)) {
|
|
384
|
+
return [];
|
|
385
|
+
}
|
|
386
|
+
const rows = this.db.prepare(`PRAGMA table_info(${tableName})`).all();
|
|
387
|
+
return rows.map((row) => row.name);
|
|
388
|
+
}
|
|
389
|
+
getWorkspaceRow(targetId) {
|
|
390
|
+
return this.db
|
|
391
|
+
.prepare(`
|
|
392
|
+
SELECT revision, last_saved_snapshot_id, state_json
|
|
393
|
+
FROM workspaces
|
|
394
|
+
WHERE target_id = ?
|
|
395
|
+
`)
|
|
396
|
+
.get(targetId) ?? null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
//# sourceMappingURL=repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.js","sourceRoot":"","sources":["../../../../src/core/workspace/repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,10 +10,58 @@ const clientRoot = resolve(__dirname, '../../client');
|
|
|
10
10
|
const clientIndexPath = resolve(clientRoot, 'index.html');
|
|
11
11
|
const clientAssetsPath = resolve(clientRoot, 'assets');
|
|
12
12
|
const devUiUrl = process.env.OPENCR_DEV_UI_URL;
|
|
13
|
+
const DEV_API_BASE_QUERY_KEY = 'opencrApiBase';
|
|
14
|
+
const getRequestApiBase = (request) => {
|
|
15
|
+
const host = request.headers.host;
|
|
16
|
+
if (!host) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return `${request.protocol}://${host}`;
|
|
20
|
+
};
|
|
21
|
+
const isObject = (value) => {
|
|
22
|
+
return typeof value === 'object' && value !== null;
|
|
23
|
+
};
|
|
24
|
+
const isSelectionSide = (value) => {
|
|
25
|
+
return value === 'additions' || value === 'deletions';
|
|
26
|
+
};
|
|
27
|
+
const isAgentBackendId = (value) => {
|
|
28
|
+
return value === 'opencode' || value === 'codex';
|
|
29
|
+
};
|
|
30
|
+
const isAgentSessionTarget = (value) => {
|
|
31
|
+
if (!isObject(value)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const hasValidModel = value.modelId === null || typeof value.modelId === 'string';
|
|
35
|
+
const hasValidVariant = value.variant === null || typeof value.variant === 'string';
|
|
36
|
+
return isAgentBackendId(value.backendId) && hasValidModel && hasValidVariant;
|
|
37
|
+
};
|
|
38
|
+
const isPersistedSelection = (value) => {
|
|
39
|
+
if (!isObject(value)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (typeof value.fileId !== 'string' || typeof value.fileLabel !== 'string' || !isObject(value.range)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
if (typeof value.range.start !== 'number' || typeof value.range.end !== 'number') {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (value.range.side !== undefined && !isSelectionSide(value.range.side)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (value.range.endSide !== undefined && !isSelectionSide(value.range.endSide)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return true;
|
|
55
|
+
};
|
|
13
56
|
const sendClient = async (request, reply) => {
|
|
14
57
|
if (!existsSync(clientIndexPath)) {
|
|
15
58
|
if (devUiUrl) {
|
|
16
|
-
|
|
59
|
+
const redirectUrl = new URL(request.url, devUiUrl);
|
|
60
|
+
const apiBase = getRequestApiBase(request);
|
|
61
|
+
if (apiBase) {
|
|
62
|
+
redirectUrl.searchParams.set(DEV_API_BASE_QUERY_KEY, apiBase);
|
|
63
|
+
}
|
|
64
|
+
return reply.redirect(redirectUrl.toString());
|
|
17
65
|
}
|
|
18
66
|
return reply
|
|
19
67
|
.code(503)
|
|
@@ -25,16 +73,170 @@ const sendClient = async (request, reply) => {
|
|
|
25
73
|
};
|
|
26
74
|
export async function createApp(options) {
|
|
27
75
|
const app = Fastify({ logger: false });
|
|
76
|
+
const getAgentSessionParams = (request) => {
|
|
77
|
+
const params = request.params;
|
|
78
|
+
const backendId = params.backendId?.trim() ?? '';
|
|
79
|
+
const sessionId = params.sessionId?.trim() ?? '';
|
|
80
|
+
if (!isAgentBackendId(backendId) || sessionId.length === 0) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
backendId,
|
|
85
|
+
sessionId,
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
app.addHook('onRequest', (request, reply, done) => {
|
|
89
|
+
if (request.url.startsWith('/api/')) {
|
|
90
|
+
const origin = request.headers.origin;
|
|
91
|
+
reply.header('Access-Control-Allow-Origin', origin ?? '*');
|
|
92
|
+
reply.header('Vary', 'Origin');
|
|
93
|
+
reply.header('Access-Control-Allow-Methods', 'GET, POST, PUT, OPTIONS');
|
|
94
|
+
reply.header('Access-Control-Allow-Headers', 'Content-Type');
|
|
95
|
+
}
|
|
96
|
+
done();
|
|
97
|
+
});
|
|
98
|
+
app.options('/api/*', async (_request, reply) => {
|
|
99
|
+
return reply.code(204).send();
|
|
100
|
+
});
|
|
28
101
|
app.get('/api/debug-state', async () => options.debugState);
|
|
29
|
-
app.get('/api/review', async () => options.
|
|
102
|
+
app.get('/api/review', async () => options.getReviewPayload());
|
|
103
|
+
app.get('/api/agents/status', async () => options.agentService.getStatus());
|
|
104
|
+
app.get('/api/agents/catalog', async (_request, reply) => {
|
|
105
|
+
try {
|
|
106
|
+
return await options.agentService.getCatalog();
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
return reply.code(502).send({ error: message });
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
app.get('/api/agents/events', async (request, reply) => {
|
|
114
|
+
reply.hijack();
|
|
115
|
+
reply.raw.writeHead(200, {
|
|
116
|
+
'Content-Type': 'text/event-stream',
|
|
117
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
118
|
+
Connection: 'keep-alive',
|
|
119
|
+
'Access-Control-Allow-Origin': request.headers.origin ?? '*',
|
|
120
|
+
Vary: 'Origin',
|
|
121
|
+
});
|
|
122
|
+
const unsubscribe = options.agentService.subscribeEvents((event) => {
|
|
123
|
+
if (!reply.raw.writableEnded) {
|
|
124
|
+
reply.raw.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const heartbeat = setInterval(() => {
|
|
128
|
+
if (!reply.raw.writableEnded) {
|
|
129
|
+
reply.raw.write('data: {"kind":"connected"}\n\n');
|
|
130
|
+
}
|
|
131
|
+
}, 10_000);
|
|
132
|
+
request.raw.on('close', () => {
|
|
133
|
+
clearInterval(heartbeat);
|
|
134
|
+
unsubscribe();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
app.get('/api/agents/session/:backendId/:sessionId/status', async (request, reply) => {
|
|
138
|
+
const session = getAgentSessionParams(request);
|
|
139
|
+
if (session == null) {
|
|
140
|
+
return reply.code(400).send({ error: 'Invalid session id.' });
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
return {
|
|
144
|
+
status: await options.agentService.getSessionStatus({
|
|
145
|
+
backendId: session.backendId,
|
|
146
|
+
sessionId: session.sessionId,
|
|
147
|
+
}),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
152
|
+
return reply.code(502).send({ error: message });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
app.put('/api/workspace', async (request, reply) => {
|
|
156
|
+
const body = request.body;
|
|
157
|
+
const baseRevision = body?.baseRevision;
|
|
158
|
+
const workspace = body?.workspace;
|
|
159
|
+
if (body == null ||
|
|
160
|
+
typeof baseRevision !== 'number' ||
|
|
161
|
+
!Number.isInteger(baseRevision) ||
|
|
162
|
+
baseRevision < 0 ||
|
|
163
|
+
workspace == null) {
|
|
164
|
+
return reply.code(400).send({ error: 'Invalid workspace payload.' });
|
|
165
|
+
}
|
|
166
|
+
return options.saveWorkspace({
|
|
167
|
+
baseRevision,
|
|
168
|
+
workspace,
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
app.post('/api/agents/session', async (request, reply) => {
|
|
172
|
+
const body = request.body;
|
|
173
|
+
const prompt = typeof body?.prompt === 'string' ? body.prompt.trim() : '';
|
|
174
|
+
const selection = body?.selection;
|
|
175
|
+
const target = body?.target;
|
|
176
|
+
if (prompt.length === 0 || !isPersistedSelection(selection) || (target !== undefined && !isAgentSessionTarget(target))) {
|
|
177
|
+
return reply.code(400).send({ error: 'Invalid agent session payload.' });
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
return await options.agentService.createSession({
|
|
181
|
+
prompt,
|
|
182
|
+
selection,
|
|
183
|
+
...(target === undefined ? {} : { target }),
|
|
184
|
+
diffPatch: options.getReviewPayload().diff.patch,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
189
|
+
return reply.code(502).send({ error: message });
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
app.get('/api/agents/session/:backendId/:sessionId/messages', async (request, reply) => {
|
|
193
|
+
const session = getAgentSessionParams(request);
|
|
194
|
+
if (session == null) {
|
|
195
|
+
return reply.code(400).send({ error: 'Invalid session id.' });
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
return await options.agentService.listMessages({
|
|
199
|
+
backendId: session.backendId,
|
|
200
|
+
sessionId: session.sessionId,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
205
|
+
return reply.code(502).send({ error: message });
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
app.post('/api/agents/session/:backendId/:sessionId/message', async (request, reply) => {
|
|
209
|
+
const body = request.body;
|
|
210
|
+
const prompt = typeof body?.prompt === 'string' ? body.prompt.trim() : '';
|
|
211
|
+
const session = getAgentSessionParams(request);
|
|
212
|
+
if (session == null || prompt.length === 0) {
|
|
213
|
+
return reply.code(400).send({ error: 'Invalid agent prompt payload.' });
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
return await options.agentService.sendMessage({
|
|
217
|
+
backendId: session.backendId,
|
|
218
|
+
sessionId: session.sessionId,
|
|
219
|
+
request: {
|
|
220
|
+
prompt,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
226
|
+
return reply.code(502).send({ error: message });
|
|
227
|
+
}
|
|
228
|
+
});
|
|
30
229
|
app.get('/api/heartbeat', async (request, reply) => {
|
|
31
230
|
reply.hijack();
|
|
32
231
|
options.debugState.server.activeHeartbeatClients += 1;
|
|
33
232
|
options.debugState.server.hasSeenHeartbeatClient = true;
|
|
233
|
+
options.onHeartbeatClientConnected();
|
|
34
234
|
reply.raw.writeHead(200, {
|
|
35
235
|
'Content-Type': 'text/event-stream',
|
|
36
236
|
'Cache-Control': 'no-cache, no-transform',
|
|
37
237
|
Connection: 'keep-alive',
|
|
238
|
+
'Access-Control-Allow-Origin': request.headers.origin ?? '*',
|
|
239
|
+
Vary: 'Origin',
|
|
38
240
|
});
|
|
39
241
|
reply.raw.write('data: connected\n\n');
|
|
40
242
|
const interval = setInterval(() => {
|