@sudocode-ai/local-server 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +6 -104
- package/dist/cli.js.map +1 -7
- package/dist/execution/engine/engine.js +10 -0
- package/dist/execution/engine/engine.js.map +1 -0
- package/dist/execution/engine/simple-engine.js +611 -0
- package/dist/execution/engine/simple-engine.js.map +1 -0
- package/dist/execution/engine/types.js +10 -0
- package/dist/execution/engine/types.js.map +1 -0
- package/dist/execution/output/ag-ui-adapter.js +438 -0
- package/dist/execution/output/ag-ui-adapter.js.map +1 -0
- package/dist/execution/output/ag-ui-integration.js +96 -0
- package/dist/execution/output/ag-ui-integration.js.map +1 -0
- package/dist/execution/output/claude-code-output-processor.js +769 -0
- package/dist/execution/output/claude-code-output-processor.js.map +1 -0
- package/dist/execution/output/index.js +15 -0
- package/dist/execution/output/index.js.map +1 -0
- package/dist/execution/output/types.js +22 -0
- package/dist/execution/output/types.js.map +1 -0
- package/dist/execution/process/builders/claude.js +59 -0
- package/dist/execution/process/builders/claude.js.map +1 -0
- package/dist/execution/process/index.js +15 -0
- package/dist/execution/process/index.js.map +1 -0
- package/dist/execution/process/manager.js +10 -0
- package/dist/execution/process/manager.js.map +1 -0
- package/dist/execution/process/simple-manager.js +336 -0
- package/dist/execution/process/simple-manager.js.map +1 -0
- package/dist/execution/process/types.js +10 -0
- package/dist/execution/process/types.js.map +1 -0
- package/dist/execution/process/utils.js +97 -0
- package/dist/execution/process/utils.js.map +1 -0
- package/dist/execution/resilience/circuit-breaker.js +291 -0
- package/dist/execution/resilience/circuit-breaker.js.map +1 -0
- package/dist/execution/resilience/executor.js +10 -0
- package/dist/execution/resilience/executor.js.map +1 -0
- package/dist/execution/resilience/index.js +15 -0
- package/dist/execution/resilience/index.js.map +1 -0
- package/dist/execution/resilience/resilient-executor.js +261 -0
- package/dist/execution/resilience/resilient-executor.js.map +1 -0
- package/dist/execution/resilience/retry.js +234 -0
- package/dist/execution/resilience/retry.js.map +1 -0
- package/dist/execution/resilience/types.js +30 -0
- package/dist/execution/resilience/types.js.map +1 -0
- package/dist/execution/transport/event-buffer.js +208 -0
- package/dist/execution/transport/event-buffer.js.map +1 -0
- package/dist/execution/transport/index.js +10 -0
- package/dist/execution/transport/index.js.map +1 -0
- package/dist/execution/transport/sse-transport.js +282 -0
- package/dist/execution/transport/sse-transport.js.map +1 -0
- package/dist/execution/transport/transport-manager.js +231 -0
- package/dist/execution/transport/transport-manager.js.map +1 -0
- package/dist/execution/workflow/index.js +13 -0
- package/dist/execution/workflow/index.js.map +1 -0
- package/dist/execution/workflow/linear-orchestrator.js +683 -0
- package/dist/execution/workflow/linear-orchestrator.js.map +1 -0
- package/dist/execution/workflow/memory-storage.js +68 -0
- package/dist/execution/workflow/memory-storage.js.map +1 -0
- package/dist/execution/workflow/orchestrator.js +9 -0
- package/dist/execution/workflow/orchestrator.js.map +1 -0
- package/dist/execution/workflow/types.js +9 -0
- package/dist/execution/workflow/types.js.map +1 -0
- package/dist/execution/workflow/utils.js +152 -0
- package/dist/execution/workflow/utils.js.map +1 -0
- package/dist/execution/worktree/config.js +280 -0
- package/dist/execution/worktree/config.js.map +1 -0
- package/dist/execution/worktree/git-cli.js +189 -0
- package/dist/execution/worktree/git-cli.js.map +1 -0
- package/dist/execution/worktree/index.js +15 -0
- package/dist/execution/worktree/index.js.map +1 -0
- package/dist/execution/worktree/manager.js +452 -0
- package/dist/execution/worktree/manager.js.map +1 -0
- package/dist/execution/worktree/types.js +42 -0
- package/dist/execution/worktree/types.js.map +1 -0
- package/dist/index.js +356 -104
- package/dist/index.js.map +1 -7
- package/dist/routes/executions-stream.js +55 -0
- package/dist/routes/executions-stream.js.map +1 -0
- package/dist/routes/executions.js +267 -0
- package/dist/routes/executions.js.map +1 -0
- package/dist/routes/feedback.js +329 -0
- package/dist/routes/feedback.js.map +1 -0
- package/dist/routes/issues.js +280 -0
- package/dist/routes/issues.js.map +1 -0
- package/dist/routes/relationships.js +308 -0
- package/dist/routes/relationships.js.map +1 -0
- package/dist/routes/specs.js +270 -0
- package/dist/routes/specs.js.map +1 -0
- package/dist/services/db.js +85 -0
- package/dist/services/db.js.map +1 -0
- package/dist/services/execution-lifecycle.js +286 -0
- package/dist/services/execution-lifecycle.js.map +1 -0
- package/dist/services/execution-service.js +676 -0
- package/dist/services/execution-service.js.map +1 -0
- package/dist/services/executions.js +164 -0
- package/dist/services/executions.js.map +1 -0
- package/dist/services/export.js +106 -0
- package/dist/services/export.js.map +1 -0
- package/dist/services/feedback.js +54 -0
- package/dist/services/feedback.js.map +1 -0
- package/dist/services/issues.js +35 -0
- package/dist/services/issues.js.map +1 -0
- package/dist/services/prompt-template-engine.js +212 -0
- package/dist/services/prompt-template-engine.js.map +1 -0
- package/dist/services/prompt-templates.js +236 -0
- package/dist/services/prompt-templates.js.map +1 -0
- package/dist/services/relationships.js +42 -0
- package/dist/services/relationships.js.map +1 -0
- package/dist/services/specs.js +35 -0
- package/dist/services/specs.js.map +1 -0
- package/dist/services/watcher.js +69 -0
- package/dist/services/watcher.js.map +1 -0
- package/dist/services/websocket.js +389 -0
- package/dist/services/websocket.js.map +1 -0
- package/dist/utils/sudocode-dir.js +9 -0
- package/dist/utils/sudocode-dir.js.map +1 -0
- package/package.json +4 -6
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Specs API routes (mapped to /api/specs)
|
|
3
|
+
*/
|
|
4
|
+
import { Router } from "express";
|
|
5
|
+
import { getAllSpecs, getSpecById, createNewSpec, updateExistingSpec, deleteExistingSpec, } from "../services/specs.js";
|
|
6
|
+
import { generateSpecId } from "@sudocode-ai/cli/dist/id-generator.js";
|
|
7
|
+
import { broadcastSpecUpdate } from "../services/websocket.js";
|
|
8
|
+
import { getSudocodeDir } from "../utils/sudocode-dir.js";
|
|
9
|
+
import { triggerExport, syncEntityToMarkdown } from "../services/export.js";
|
|
10
|
+
import * as path from "path";
|
|
11
|
+
export function createSpecsRouter(db) {
|
|
12
|
+
const router = Router();
|
|
13
|
+
/**
|
|
14
|
+
* GET /api/specs - List all specs
|
|
15
|
+
*/
|
|
16
|
+
router.get("/", (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
// Parse query parameters for filtering
|
|
19
|
+
const options = {};
|
|
20
|
+
if (req.query.priority) {
|
|
21
|
+
options.priority = parseInt(req.query.priority, 10);
|
|
22
|
+
}
|
|
23
|
+
// Default to excluding archived unless explicitly specified
|
|
24
|
+
options.archived =
|
|
25
|
+
req.query.archived !== undefined
|
|
26
|
+
? req.query.archived === "true"
|
|
27
|
+
: false;
|
|
28
|
+
if (req.query.limit) {
|
|
29
|
+
options.limit = parseInt(req.query.limit, 10);
|
|
30
|
+
}
|
|
31
|
+
if (req.query.offset) {
|
|
32
|
+
options.offset = parseInt(req.query.offset, 10);
|
|
33
|
+
}
|
|
34
|
+
const specs = getAllSpecs(db, options);
|
|
35
|
+
res.json({
|
|
36
|
+
success: true,
|
|
37
|
+
data: specs,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error("Error listing specs:", error);
|
|
42
|
+
res.status(500).json({
|
|
43
|
+
success: false,
|
|
44
|
+
data: null,
|
|
45
|
+
error_data: error instanceof Error ? error.message : String(error),
|
|
46
|
+
message: "Failed to list specs",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
/**
|
|
51
|
+
* GET /api/specs/:id - Get a specific spec
|
|
52
|
+
*/
|
|
53
|
+
router.get("/:id", (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
const { id } = req.params;
|
|
56
|
+
const spec = getSpecById(db, id);
|
|
57
|
+
if (!spec) {
|
|
58
|
+
res.status(404).json({
|
|
59
|
+
success: false,
|
|
60
|
+
data: null,
|
|
61
|
+
message: `Spec not found: ${id}`,
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
res.json({
|
|
66
|
+
success: true,
|
|
67
|
+
data: spec,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error("Error getting spec:", error);
|
|
72
|
+
res.status(500).json({
|
|
73
|
+
success: false,
|
|
74
|
+
data: null,
|
|
75
|
+
error_data: error instanceof Error ? error.message : String(error),
|
|
76
|
+
message: "Failed to get spec",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
/**
|
|
81
|
+
* POST /api/specs - Create a new spec
|
|
82
|
+
*/
|
|
83
|
+
router.post("/", (req, res) => {
|
|
84
|
+
try {
|
|
85
|
+
const { title, content, priority, parent_id } = req.body;
|
|
86
|
+
// Validate required fields
|
|
87
|
+
if (!title || typeof title !== "string") {
|
|
88
|
+
res.status(400).json({
|
|
89
|
+
success: false,
|
|
90
|
+
data: null,
|
|
91
|
+
message: "Title is required and must be a string",
|
|
92
|
+
});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (title.length > 500) {
|
|
96
|
+
res.status(400).json({
|
|
97
|
+
success: false,
|
|
98
|
+
data: null,
|
|
99
|
+
message: "Title must be 500 characters or less",
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// Generate new spec ID
|
|
104
|
+
const outputDir = getSudocodeDir();
|
|
105
|
+
const id = generateSpecId(db, outputDir);
|
|
106
|
+
// Generate file path for the spec
|
|
107
|
+
const file_path = path.join(outputDir, "specs", `${id}.md`);
|
|
108
|
+
// Create spec using CLI operation
|
|
109
|
+
const spec = createNewSpec(db, {
|
|
110
|
+
id,
|
|
111
|
+
title,
|
|
112
|
+
file_path,
|
|
113
|
+
content: content || "",
|
|
114
|
+
priority: priority !== undefined ? priority : 2,
|
|
115
|
+
parent_id: parent_id || undefined,
|
|
116
|
+
});
|
|
117
|
+
// Trigger export to JSONL files
|
|
118
|
+
triggerExport(db);
|
|
119
|
+
// Sync this specific spec to its markdown file (don't wait for it)
|
|
120
|
+
syncEntityToMarkdown(db, spec.id, "spec").catch((error) => {
|
|
121
|
+
console.error(`Failed to sync spec ${spec.id} to markdown:`, error);
|
|
122
|
+
});
|
|
123
|
+
// Broadcast spec creation to WebSocket clients
|
|
124
|
+
broadcastSpecUpdate(spec.id, "created", spec);
|
|
125
|
+
res.status(201).json({
|
|
126
|
+
success: true,
|
|
127
|
+
data: spec,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error("Error creating spec:", error);
|
|
132
|
+
res.status(500).json({
|
|
133
|
+
success: false,
|
|
134
|
+
data: null,
|
|
135
|
+
error_data: error instanceof Error ? error.message : String(error),
|
|
136
|
+
message: "Failed to create spec",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
/**
|
|
141
|
+
* PUT /api/specs/:id - Update an existing spec
|
|
142
|
+
*/
|
|
143
|
+
router.put("/:id", (req, res) => {
|
|
144
|
+
try {
|
|
145
|
+
const { id } = req.params;
|
|
146
|
+
const { title, content, priority, parent_id, archived } = req.body;
|
|
147
|
+
// Validate that at least one field is provided
|
|
148
|
+
if (title === undefined &&
|
|
149
|
+
content === undefined &&
|
|
150
|
+
priority === undefined &&
|
|
151
|
+
parent_id === undefined &&
|
|
152
|
+
archived === undefined) {
|
|
153
|
+
res.status(400).json({
|
|
154
|
+
success: false,
|
|
155
|
+
data: null,
|
|
156
|
+
message: "At least one field must be provided for update",
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// Validate title length if provided
|
|
161
|
+
if (title !== undefined &&
|
|
162
|
+
typeof title === "string" &&
|
|
163
|
+
title.length > 500) {
|
|
164
|
+
res.status(400).json({
|
|
165
|
+
success: false,
|
|
166
|
+
data: null,
|
|
167
|
+
message: "Title must be 500 characters or less",
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
// Build update input
|
|
172
|
+
const updateInput = {};
|
|
173
|
+
if (title !== undefined)
|
|
174
|
+
updateInput.title = title;
|
|
175
|
+
if (content !== undefined)
|
|
176
|
+
updateInput.content = content;
|
|
177
|
+
if (priority !== undefined)
|
|
178
|
+
updateInput.priority = priority;
|
|
179
|
+
if (parent_id !== undefined)
|
|
180
|
+
updateInput.parent_id = parent_id;
|
|
181
|
+
if (archived !== undefined) {
|
|
182
|
+
updateInput.archived = archived;
|
|
183
|
+
updateInput.archived_at = archived ? new Date().toISOString() : null;
|
|
184
|
+
}
|
|
185
|
+
// Update spec using CLI operation
|
|
186
|
+
const spec = updateExistingSpec(db, id, updateInput);
|
|
187
|
+
// Trigger export to JSONL files
|
|
188
|
+
triggerExport(db);
|
|
189
|
+
// Sync this specific spec to its markdown file (don't wait for it)
|
|
190
|
+
syncEntityToMarkdown(db, spec.id, "spec").catch((error) => {
|
|
191
|
+
console.error(`Failed to sync spec ${spec.id} to markdown:`, error);
|
|
192
|
+
});
|
|
193
|
+
// Broadcast spec update to WebSocket clients
|
|
194
|
+
broadcastSpecUpdate(spec.id, "updated", spec);
|
|
195
|
+
res.json({
|
|
196
|
+
success: true,
|
|
197
|
+
data: spec,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
console.error("Error updating spec:", error);
|
|
202
|
+
// Handle "not found" errors
|
|
203
|
+
if (error instanceof Error && error.message.includes("not found")) {
|
|
204
|
+
res.status(404).json({
|
|
205
|
+
success: false,
|
|
206
|
+
data: null,
|
|
207
|
+
message: error.message,
|
|
208
|
+
});
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
res.status(500).json({
|
|
212
|
+
success: false,
|
|
213
|
+
data: null,
|
|
214
|
+
error_data: error instanceof Error ? error.message : String(error),
|
|
215
|
+
message: "Failed to update spec",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
/**
|
|
220
|
+
* DELETE /api/specs/:id - Delete a spec
|
|
221
|
+
*/
|
|
222
|
+
router.delete("/:id", (req, res) => {
|
|
223
|
+
try {
|
|
224
|
+
const { id } = req.params;
|
|
225
|
+
// Check if spec exists first
|
|
226
|
+
const existingSpec = getSpecById(db, id);
|
|
227
|
+
if (!existingSpec) {
|
|
228
|
+
res.status(404).json({
|
|
229
|
+
success: false,
|
|
230
|
+
data: null,
|
|
231
|
+
message: `Spec not found: ${id}`,
|
|
232
|
+
});
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
// Delete spec using CLI operation
|
|
236
|
+
const deleted = deleteExistingSpec(db, id);
|
|
237
|
+
if (deleted) {
|
|
238
|
+
// Trigger export to JSONL files
|
|
239
|
+
triggerExport(db);
|
|
240
|
+
// Broadcast spec deletion to WebSocket clients
|
|
241
|
+
broadcastSpecUpdate(id, "deleted", { id });
|
|
242
|
+
res.json({
|
|
243
|
+
success: true,
|
|
244
|
+
data: {
|
|
245
|
+
id,
|
|
246
|
+
deleted: true,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
res.status(500).json({
|
|
252
|
+
success: false,
|
|
253
|
+
data: null,
|
|
254
|
+
message: "Failed to delete spec",
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
console.error("Error deleting spec:", error);
|
|
260
|
+
res.status(500).json({
|
|
261
|
+
success: false,
|
|
262
|
+
data: null,
|
|
263
|
+
error_data: error instanceof Error ? error.message : String(error),
|
|
264
|
+
message: "Failed to delete spec",
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
return router;
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=specs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"specs.js","sourceRoot":"","sources":["../../src/routes/specs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAEpD,OAAO,EACL,WAAW,EACX,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,MAAM,UAAU,iBAAiB,CAAC,EAAqB;IACrD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;QAC9C,IAAI,CAAC;YACH,uCAAuC;YACvC,MAAM,OAAO,GAAQ,EAAE,CAAC;YAExB,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACvB,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,QAAkB,EAAE,EAAE,CAAC,CAAC;YAChE,CAAC;YACD,4DAA4D;YAC5D,OAAO,CAAC,QAAQ;gBACd,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS;oBAC9B,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,MAAM;oBAC/B,CAAC,CAAC,KAAK,CAAC;YACZ,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACpB,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,KAAe,EAAE,EAAE,CAAC,CAAC;YAC1D,CAAC;YACD,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;gBACrB,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,MAAgB,EAAE,EAAE,CAAC,CAAC;YAC5D,CAAC;YAED,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;YAEvC,GAAG,CAAC,IAAI,CAAC;gBACP,OAAO,EAAE,IAAI;gBACb,IAAI,EAAE,KAAK;aACZ,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAC7C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,IAAI;gBACV,UAAU,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;gBAClE,OAAO,EAAE,sBAAsB;aAChC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;QACjD,IAAI,CAAC;YACH,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;YAC1B,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAEjC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,mBAAmB,EAAE,EAAE;iBACjC,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,GAAG,CAAC,IAAI,CAAC;gBACP,OAAO,EAAE,IAAI;gBACb,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;YAC5C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,IAAI;gBACV,UAAU,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;gBAClE,OAAO,EAAE,oBAAoB;aAC9B,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;QAC/C,IAAI,CAAC;YACH,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;YAEzD,2BAA2B;YAC3B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACxC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,wCAAwC;iBAClD,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBACvB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,sCAAsC;iBAChD,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,uBAAuB;YACvB,MAAM,SAAS,GAAG,cAAc,EAAE,CAAC;YACnC,MAAM,EAAE,GAAG,cAAc,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;YAEzC,kCAAkC;YAClC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YAE5D,kCAAkC;YAClC,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,EAAE;gBAC7B,EAAE;gBACF,KAAK;gBACL,SAAS;gBACT,OAAO,EAAE,OAAO,IAAI,EAAE;gBACtB,QAAQ,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC/C,SAAS,EAAE,SAAS,IAAI,SAAS;aAClC,CAAC,CAAC;YAEH,gCAAgC;YAChC,aAAa,CAAC,EAAE,CAAC,CAAC;YAElB,mEAAmE;YACnE,oBAAoB,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACxD,OAAO,CAAC,KAAK,CAAC,uBAAuB,IAAI,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC;YACtE,CAAC,CAAC,CAAC;YAEH,+CAA+C;YAC/C,mBAAmB,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;YAE9C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,IAAI;gBACb,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAC7C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,IAAI;gBACV,UAAU,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;gBAClE,OAAO,EAAE,uBAAuB;aACjC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;QACjD,IAAI,CAAC;YACH,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;YAC1B,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;YAEnE,+CAA+C;YAC/C,IACE,KAAK,KAAK,SAAS;gBACnB,OAAO,KAAK,SAAS;gBACrB,QAAQ,KAAK,SAAS;gBACtB,SAAS,KAAK,SAAS;gBACvB,QAAQ,KAAK,SAAS,EACtB,CAAC;gBACD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,gDAAgD;iBAC1D,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,oCAAoC;YACpC,IACE,KAAK,KAAK,SAAS;gBACnB,OAAO,KAAK,KAAK,QAAQ;gBACzB,KAAK,CAAC,MAAM,GAAG,GAAG,EAClB,CAAC;gBACD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,sCAAsC;iBAChD,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,qBAAqB;YACrB,MAAM,WAAW,GAAQ,EAAE,CAAC;YAC5B,IAAI,KAAK,KAAK,SAAS;gBAAE,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC;YACnD,IAAI,OAAO,KAAK,SAAS;gBAAE,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC;YACzD,IAAI,QAAQ,KAAK,SAAS;gBAAE,WAAW,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAC5D,IAAI,SAAS,KAAK,SAAS;gBAAE,WAAW,CAAC,SAAS,GAAG,SAAS,CAAC;YAC/D,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,WAAW,CAAC,QAAQ,GAAG,QAAQ,CAAC;gBAChC,WAAW,CAAC,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACvE,CAAC;YAED,kCAAkC;YAClC,MAAM,IAAI,GAAG,kBAAkB,CAAC,EAAE,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC;YAErD,gCAAgC;YAChC,aAAa,CAAC,EAAE,CAAC,CAAC;YAElB,mEAAmE;YACnE,oBAAoB,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACxD,OAAO,CAAC,KAAK,CAAC,uBAAuB,IAAI,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC;YACtE,CAAC,CAAC,CAAC;YAEH,6CAA6C;YAC7C,mBAAmB,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;YAE9C,GAAG,CAAC,IAAI,CAAC;gBACP,OAAO,EAAE,IAAI;gBACb,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAE7C,4BAA4B;YAC5B,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBAClE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,KAAK,CAAC,OAAO;iBACvB,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,IAAI;gBACV,UAAU,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;gBAClE,OAAO,EAAE,uBAAuB;aACjC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;QACpD,IAAI,CAAC;YACH,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;YAE1B,6BAA6B;YAC7B,MAAM,YAAY,GAAG,WAAW,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACzC,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,mBAAmB,EAAE,EAAE;iBACjC,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,kCAAkC;YAClC,MAAM,OAAO,GAAG,kBAAkB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAE3C,IAAI,OAAO,EAAE,CAAC;gBACZ,gCAAgC;gBAChC,aAAa,CAAC,EAAE,CAAC,CAAC;gBAElB,+CAA+C;gBAC/C,mBAAmB,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;gBAE3C,GAAG,CAAC,IAAI,CAAC;oBACP,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE;wBACJ,EAAE;wBACF,OAAO,EAAE,IAAI;qBACd;iBACF,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,KAAK;oBACd,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,uBAAuB;iBACjC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAC7C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,IAAI;gBACV,UAAU,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;gBAClE,OAAO,EAAE,uBAAuB;aACjC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database service for sudocode server
|
|
3
|
+
* Uses shared schema from @sudocode-ai/types
|
|
4
|
+
*/
|
|
5
|
+
import Database from "better-sqlite3";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import { EXECUTIONS_TABLE, EXECUTIONS_INDEXES, PROMPT_TEMPLATES_TABLE, PROMPT_TEMPLATES_INDEXES, EXECUTION_LOGS_TABLE, EXECUTION_LOGS_INDEXES, } from "@sudocode-ai/types/schema";
|
|
9
|
+
import { initializeDefaultTemplates } from "./prompt-templates.js";
|
|
10
|
+
/**
|
|
11
|
+
* Initialize database with CLI schema + server extensions
|
|
12
|
+
*/
|
|
13
|
+
export function initDatabase(config) {
|
|
14
|
+
const { path: dbPath, readOnly = false } = config;
|
|
15
|
+
// Ensure directory exists
|
|
16
|
+
const dir = path.dirname(dbPath);
|
|
17
|
+
if (!fs.existsSync(dir)) {
|
|
18
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
// Open database
|
|
21
|
+
const db = new Database(dbPath, {
|
|
22
|
+
readonly: readOnly,
|
|
23
|
+
fileMustExist: false,
|
|
24
|
+
});
|
|
25
|
+
// Don't modify schema if read-only
|
|
26
|
+
if (readOnly) {
|
|
27
|
+
return db;
|
|
28
|
+
}
|
|
29
|
+
// Configure database
|
|
30
|
+
db.pragma("journal_mode = WAL");
|
|
31
|
+
db.pragma("foreign_keys = ON");
|
|
32
|
+
db.pragma("synchronous = NORMAL");
|
|
33
|
+
db.pragma("temp_store = MEMORY");
|
|
34
|
+
// Create server-specific tables
|
|
35
|
+
db.exec(EXECUTIONS_TABLE);
|
|
36
|
+
db.exec(PROMPT_TEMPLATES_TABLE);
|
|
37
|
+
db.exec(EXECUTION_LOGS_TABLE);
|
|
38
|
+
// Create indexes
|
|
39
|
+
db.exec(EXECUTIONS_INDEXES);
|
|
40
|
+
db.exec(PROMPT_TEMPLATES_INDEXES);
|
|
41
|
+
db.exec(EXECUTION_LOGS_INDEXES);
|
|
42
|
+
// Initialize default prompt templates
|
|
43
|
+
initializeDefaultTemplates(db);
|
|
44
|
+
return db;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if database has CLI tables
|
|
48
|
+
*/
|
|
49
|
+
export function hasCliTables(db) {
|
|
50
|
+
const result = db
|
|
51
|
+
.prepare(`
|
|
52
|
+
SELECT COUNT(*) as count
|
|
53
|
+
FROM sqlite_master
|
|
54
|
+
WHERE type='table'
|
|
55
|
+
AND name IN ('specs', 'issues', 'relationships', 'tags')
|
|
56
|
+
`)
|
|
57
|
+
.get();
|
|
58
|
+
return result.count === 4;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get database info
|
|
62
|
+
*/
|
|
63
|
+
export function getDatabaseInfo(db) {
|
|
64
|
+
const tables = db
|
|
65
|
+
.prepare(`
|
|
66
|
+
SELECT name
|
|
67
|
+
FROM sqlite_master
|
|
68
|
+
WHERE type='table'
|
|
69
|
+
ORDER BY name
|
|
70
|
+
`)
|
|
71
|
+
.all();
|
|
72
|
+
const version = db.prepare("PRAGMA user_version").get();
|
|
73
|
+
return {
|
|
74
|
+
tables: tables.map((t) => t.name),
|
|
75
|
+
version: version.user_version,
|
|
76
|
+
hasCliTables: hasCliTables(db),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Close database connection
|
|
81
|
+
*/
|
|
82
|
+
export function closeDatabase(db) {
|
|
83
|
+
db.close();
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/services/db.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAUnE;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAAsB;IACjD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAG,KAAK,EAAE,GAAG,MAAM,CAAC;IAElD,0BAA0B;IAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,gBAAgB;IAChB,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE;QAC9B,QAAQ,EAAE,QAAQ;QAClB,aAAa,EAAE,KAAK;KACrB,CAAC,CAAC;IAEH,mCAAmC;IACnC,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,qBAAqB;IACrB,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAClC,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAEjC,gCAAgC;IAChC,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC1B,EAAE,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAChC,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAE9B,iBAAiB;IACjB,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC5B,EAAE,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAClC,EAAE,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAEhC,sCAAsC;IACtC,0BAA0B,CAAC,EAAE,CAAC,CAAC;IAE/B,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,EAAqB;IAChD,MAAM,MAAM,GAAG,EAAE;SACd,OAAO,CACN;;;;;GAKH,CACE;SACA,GAAG,EAAuB,CAAC;IAE9B,OAAO,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,EAAqB;IACnD,MAAM,MAAM,GAAG,EAAE;SACd,OAAO,CACN;;;;;GAKH,CACE;SACA,GAAG,EAAwB,CAAC;IAE/B,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,GAAG,EAEpD,CAAC;IAEF,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACjC,OAAO,EAAE,OAAO,CAAC,YAAY;QAC7B,YAAY,EAAE,YAAY,CAAC,EAAE,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,EAAqB;IACjD,EAAE,CAAC,KAAK,EAAE,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution Lifecycle Service
|
|
3
|
+
*
|
|
4
|
+
* Centralized service for managing execution lifecycle with worktree integration.
|
|
5
|
+
* Coordinates between WorktreeManager and execution database services.
|
|
6
|
+
*
|
|
7
|
+
* @module services/execution-lifecycle
|
|
8
|
+
*/
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { WorktreeManager, } from "../execution/worktree/manager.js";
|
|
11
|
+
import { getWorktreeConfig } from "../execution/worktree/config.js";
|
|
12
|
+
import { createExecution, getExecution } from "./executions.js";
|
|
13
|
+
import { randomUUID } from "crypto";
|
|
14
|
+
/**
|
|
15
|
+
* ExecutionLifecycleService
|
|
16
|
+
*
|
|
17
|
+
* Manages the full lifecycle of executions with worktree support:
|
|
18
|
+
* - Creating executions with isolated worktrees
|
|
19
|
+
* - Cleaning up executions and associated worktrees
|
|
20
|
+
* - Handling orphaned worktrees
|
|
21
|
+
*/
|
|
22
|
+
export class ExecutionLifecycleService {
|
|
23
|
+
worktreeManager;
|
|
24
|
+
db;
|
|
25
|
+
repoPath;
|
|
26
|
+
/**
|
|
27
|
+
* Create a new ExecutionLifecycleService
|
|
28
|
+
*
|
|
29
|
+
* @param db - Database instance
|
|
30
|
+
* @param repoPath - Path to the git repository
|
|
31
|
+
* @param worktreeManager - Optional worktree manager (defaults to new instance)
|
|
32
|
+
*/
|
|
33
|
+
constructor(db, repoPath, worktreeManager) {
|
|
34
|
+
this.db = db;
|
|
35
|
+
this.repoPath = repoPath;
|
|
36
|
+
// Load config and create worktree manager if not provided
|
|
37
|
+
if (worktreeManager) {
|
|
38
|
+
this.worktreeManager = worktreeManager;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const config = getWorktreeConfig(repoPath);
|
|
42
|
+
this.worktreeManager = new WorktreeManager(config);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create an execution with an isolated worktree
|
|
47
|
+
*
|
|
48
|
+
* Creates a worktree first, then creates the execution record.
|
|
49
|
+
* If worktree creation fails, no execution is created.
|
|
50
|
+
* If execution creation fails, the worktree is cleaned up.
|
|
51
|
+
*
|
|
52
|
+
* @param params - Execution creation parameters
|
|
53
|
+
* @returns Execution with worktree information
|
|
54
|
+
* @throws Error if creation fails
|
|
55
|
+
*/
|
|
56
|
+
async createExecutionWithWorktree(params) {
|
|
57
|
+
const { issueId, issueTitle, agentType, targetBranch, repoPath } = params;
|
|
58
|
+
// Validation 1: Check for existing active execution for this issue
|
|
59
|
+
const existingExecution = this.db
|
|
60
|
+
.prepare(`SELECT id FROM executions
|
|
61
|
+
WHERE issue_id = ?
|
|
62
|
+
AND status = 'running'
|
|
63
|
+
AND worktree_path IS NOT NULL`)
|
|
64
|
+
.get(issueId);
|
|
65
|
+
if (existingExecution) {
|
|
66
|
+
throw new Error(`Active execution already exists for issue ${issueId}: ${existingExecution.id}`);
|
|
67
|
+
}
|
|
68
|
+
// Validation 2: Validate git repository
|
|
69
|
+
const isValidRepo = await this.worktreeManager.isValidRepo(repoPath);
|
|
70
|
+
if (!isValidRepo) {
|
|
71
|
+
throw new Error(`Not a git repository: ${repoPath}`);
|
|
72
|
+
}
|
|
73
|
+
// Validation 3: Validate target branch exists
|
|
74
|
+
const branches = await this.worktreeManager.listBranches(repoPath);
|
|
75
|
+
if (!branches.includes(targetBranch)) {
|
|
76
|
+
throw new Error(`Target branch does not exist: ${targetBranch}`);
|
|
77
|
+
}
|
|
78
|
+
const config = this.worktreeManager.getConfig();
|
|
79
|
+
// Generate execution ID
|
|
80
|
+
const executionId = randomUUID();
|
|
81
|
+
// Determine branch name based on autoCreateBranches setting
|
|
82
|
+
let branchName;
|
|
83
|
+
if (config.autoCreateBranches) {
|
|
84
|
+
// Generate branch name: {branchPrefix}/{execution-id}/{sanitized-issue-title}
|
|
85
|
+
const sanitizedTitle = sanitizeForBranchName(issueTitle);
|
|
86
|
+
branchName = `${config.branchPrefix}/${executionId.substring(0, 8)}/${sanitizedTitle}`;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Use target branch directly when not auto-creating branches
|
|
90
|
+
branchName = targetBranch;
|
|
91
|
+
}
|
|
92
|
+
// Generate worktree path: {repoPath}/{worktreeStoragePath}/{execution-id}
|
|
93
|
+
const worktreePath = path.join(repoPath, config.worktreeStoragePath, executionId);
|
|
94
|
+
let worktreeCreated = false;
|
|
95
|
+
try {
|
|
96
|
+
// Step 1: Create worktree
|
|
97
|
+
await this.worktreeManager.createWorktree({
|
|
98
|
+
repoPath,
|
|
99
|
+
branchName,
|
|
100
|
+
worktreePath,
|
|
101
|
+
baseBranch: targetBranch,
|
|
102
|
+
createBranch: config.autoCreateBranches,
|
|
103
|
+
});
|
|
104
|
+
worktreeCreated = true;
|
|
105
|
+
// Step 2: Create execution record in database
|
|
106
|
+
const execution = createExecution(this.db, {
|
|
107
|
+
id: executionId,
|
|
108
|
+
issue_id: issueId,
|
|
109
|
+
agent_type: agentType,
|
|
110
|
+
mode: params.mode,
|
|
111
|
+
prompt: params.prompt,
|
|
112
|
+
config: params.config,
|
|
113
|
+
target_branch: targetBranch,
|
|
114
|
+
branch_name: branchName,
|
|
115
|
+
worktree_path: worktreePath,
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
execution,
|
|
119
|
+
worktreePath,
|
|
120
|
+
branchName,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
// If worktree was created but execution creation failed, cleanup worktree
|
|
125
|
+
if (worktreeCreated) {
|
|
126
|
+
try {
|
|
127
|
+
await this.worktreeManager.cleanupWorktree(worktreePath, repoPath);
|
|
128
|
+
}
|
|
129
|
+
catch (cleanupError) {
|
|
130
|
+
// Log cleanup error but throw original error
|
|
131
|
+
console.error(`Failed to cleanup worktree after execution creation failure:`, cleanupError);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Re-throw the original error
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Check if an execution should be cleaned up based on its config
|
|
140
|
+
*
|
|
141
|
+
* @param executionId - ID of execution to check
|
|
142
|
+
* @returns true if should cleanup, false otherwise
|
|
143
|
+
*/
|
|
144
|
+
shouldCleanupExecution(executionId) {
|
|
145
|
+
const execution = getExecution(this.db, executionId);
|
|
146
|
+
if (!execution) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
// Check if cleanupMode is set to 'manual'
|
|
150
|
+
if (execution.config) {
|
|
151
|
+
try {
|
|
152
|
+
const config = JSON.parse(execution.config);
|
|
153
|
+
if (config.cleanupMode === "manual" || config.cleanupMode === "never") {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
console.error(`Failed to parse execution config for ${executionId}:`, error);
|
|
159
|
+
// Default to cleanup on parse error
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Clean up an execution and its associated worktree
|
|
166
|
+
*
|
|
167
|
+
* Removes the worktree from filesystem and git metadata.
|
|
168
|
+
* Branch deletion is controlled by autoDeleteBranches config.
|
|
169
|
+
* Respects the cleanupMode configuration from execution config.
|
|
170
|
+
*
|
|
171
|
+
* IMPORTANT: The worktree_path is NEVER cleared from the database.
|
|
172
|
+
* This allows follow-up executions to find and reuse the same worktree path.
|
|
173
|
+
* The filesystem worktree is deleted, but the path remains in the DB as a historical record.
|
|
174
|
+
*
|
|
175
|
+
* @param executionId - ID of execution to cleanup
|
|
176
|
+
* @throws Error if cleanup fails
|
|
177
|
+
*/
|
|
178
|
+
async cleanupExecution(executionId) {
|
|
179
|
+
// Check if we should cleanup based on config
|
|
180
|
+
if (!this.shouldCleanupExecution(executionId)) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Get execution from database
|
|
184
|
+
const execution = getExecution(this.db, executionId);
|
|
185
|
+
if (!execution) {
|
|
186
|
+
// Execution doesn't exist, nothing to cleanup
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
// If execution has a worktree path, clean up the filesystem worktree
|
|
190
|
+
// but KEEP the worktree_path in the database for follow-up executions
|
|
191
|
+
if (execution.worktree_path) {
|
|
192
|
+
try {
|
|
193
|
+
await this.worktreeManager.cleanupWorktree(execution.worktree_path, this.repoPath);
|
|
194
|
+
// NOTE: We do NOT set worktree_path to null in the database
|
|
195
|
+
// Follow-up executions need this path to recreate the worktree
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
// Log error but don't fail - cleanup is best-effort
|
|
199
|
+
console.error(`Failed to cleanup worktree for execution ${executionId}:`, error);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Clean up orphaned worktrees
|
|
205
|
+
*
|
|
206
|
+
* Finds worktrees that are registered in git but don't have
|
|
207
|
+
* corresponding execution records, or vice versa.
|
|
208
|
+
* Also cleans up worktrees for finished executions (completed/failed/stopped).
|
|
209
|
+
*/
|
|
210
|
+
async cleanupOrphanedWorktrees() {
|
|
211
|
+
const repoPath = this.repoPath;
|
|
212
|
+
const config = this.worktreeManager.getConfig();
|
|
213
|
+
try {
|
|
214
|
+
// List all worktrees from git
|
|
215
|
+
const worktrees = await this.worktreeManager.listWorktrees(repoPath);
|
|
216
|
+
// Filter to worktrees in our storage path
|
|
217
|
+
const managedWorktrees = worktrees.filter((w) => w.path.includes(config.worktreeStoragePath));
|
|
218
|
+
// For each managed worktree, check if it has a corresponding execution
|
|
219
|
+
for (const worktree of managedWorktrees) {
|
|
220
|
+
const worktreePath = worktree.path;
|
|
221
|
+
// Try to extract execution ID from path
|
|
222
|
+
const executionId = path.basename(worktreePath);
|
|
223
|
+
// Check if execution exists in database
|
|
224
|
+
const execution = getExecution(this.db, executionId);
|
|
225
|
+
if (!execution) {
|
|
226
|
+
// Orphaned worktree - cleanup
|
|
227
|
+
console.log(`Cleaning up orphaned worktree: ${worktreePath} (no execution found)`);
|
|
228
|
+
try {
|
|
229
|
+
await this.worktreeManager.cleanupWorktree(worktreePath, repoPath);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
console.error(`Failed to cleanup orphaned worktree ${worktreePath}:`, error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else if (execution.status === "completed" ||
|
|
236
|
+
execution.status === "failed" ||
|
|
237
|
+
execution.status === "stopped") {
|
|
238
|
+
// Execution is finished but worktree still exists
|
|
239
|
+
// Check if we should cleanup based on execution config
|
|
240
|
+
if (!this.shouldCleanupExecution(executionId)) {
|
|
241
|
+
console.log(`Skipping cleanup for finished execution ${executionId} (manual cleanup mode)`);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
console.log(`Cleaning up worktree for finished execution ${executionId} (status: ${execution.status})`);
|
|
245
|
+
try {
|
|
246
|
+
await this.worktreeManager.cleanupWorktree(worktreePath, repoPath);
|
|
247
|
+
// NOTE: We do NOT set worktree_path to null in the database
|
|
248
|
+
// Follow-up executions need this path to recreate the worktree
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
console.error(`Failed to cleanup worktree for finished execution ${executionId}:`, error);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
console.error(`Failed to cleanup orphaned worktrees in ${repoPath}:`, error);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Sanitize a string to be safe for use in git branch names
|
|
263
|
+
*
|
|
264
|
+
* - Converts to lowercase
|
|
265
|
+
* - Replaces spaces and slashes with hyphens
|
|
266
|
+
* - Removes special characters
|
|
267
|
+
* - Limits length to 50 characters
|
|
268
|
+
*
|
|
269
|
+
* @param str - String to sanitize
|
|
270
|
+
* @returns Sanitized string safe for branch names
|
|
271
|
+
*/
|
|
272
|
+
export function sanitizeForBranchName(str) {
|
|
273
|
+
return (str
|
|
274
|
+
.toLowerCase()
|
|
275
|
+
// Replace spaces and slashes with hyphens
|
|
276
|
+
.replace(/[\s/]+/g, "-")
|
|
277
|
+
// Remove special characters (keep alphanumeric, hyphens, underscores)
|
|
278
|
+
.replace(/[^a-z0-9\-_]/g, "")
|
|
279
|
+
// Remove consecutive hyphens
|
|
280
|
+
.replace(/-+/g, "-")
|
|
281
|
+
// Remove leading/trailing hyphens
|
|
282
|
+
.replace(/^-+|-+$/g, "")
|
|
283
|
+
// Limit length
|
|
284
|
+
.substring(0, 50));
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=execution-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-lifecycle.js","sourceRoot":"","sources":["../../src/services/execution-lifecycle.ts"],"names":[],"mappings":"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"}
|