pi-forge 1.3.5 → 1.3.6
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 +1 -1
- package/dist/client/assets/{CodeMirrorEditor-iLMm0PBW.js → CodeMirrorEditor-BH6J6WhM.js} +2 -2
- package/dist/client/assets/{CodeMirrorEditor-iLMm0PBW.js.map → CodeMirrorEditor-BH6J6WhM.js.map} +1 -1
- package/dist/client/assets/{index-D311Lak3.css → index-BszHzUYE.css} +1 -1
- package/dist/client/assets/{index-ChVHn55j.js → index-bJJrpg1R.js} +84 -84
- package/dist/client/assets/index-bJJrpg1R.js.map +1 -0
- package/dist/client/index.html +2 -2
- package/dist/client/sw.js +1 -1
- package/dist/client/sw.js.map +1 -1
- package/dist/server/git-runner.js +57 -0
- package/dist/server/git-runner.js.map +1 -1
- package/dist/server/mcp/manager.js +40 -5
- package/dist/server/mcp/manager.js.map +1 -1
- package/dist/server/orchestration/event-bridge.js +11 -0
- package/dist/server/orchestration/event-bridge.js.map +1 -1
- package/dist/server/orchestration/tools.js +16 -14
- package/dist/server/orchestration/tools.js.map +1 -1
- package/dist/server/orchestration/worker-lifecycle.js +68 -0
- package/dist/server/orchestration/worker-lifecycle.js.map +1 -0
- package/dist/server/routes/git.js +227 -216
- package/dist/server/routes/git.js.map +1 -1
- package/dist/server/routes/orchestration.js +10 -8
- package/dist/server/routes/orchestration.js.map +1 -1
- package/dist/server/routes/sessions.js +32 -13
- package/dist/server/routes/sessions.js.map +1 -1
- package/dist/server/routes/terminal.js +18 -7
- package/dist/server/routes/terminal.js.map +1 -1
- package/dist/server/session-registry.js +34 -4
- package/dist/server/session-registry.js.map +1 -1
- package/dist/server/sse-bridge.js +19 -0
- package/dist/server/sse-bridge.js.map +1 -1
- package/package.json +4 -4
- package/dist/client/assets/index-ChVHn55j.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { GitCommandError, GitNotInstalledError, InvalidBranchNameError, checkoutBranch, commit, createBranch, deleteBranch, fetch, addRemote, getBranches, getRemotes, removeRemote, getDiff, getFileDiff, getLog, getStagedDiff, getStatus, initRepo, isGitRepo, pull, push, revertPaths, stagePaths, unstagePaths, } from "../git-runner.js";
|
|
1
|
+
import { GitCommandError, GitNotInstalledError, InvalidBranchNameError, checkoutBranch, commit, createBranch, deleteBranch, fetch, addRemote, getBranches, getRemotes, removeRemote, getDiff, getFileDiff, getLog, getStagedDiff, getStatus, getWorktrees, initRepo, isGitRepo, pull, push, revertPaths, stagePaths, unstagePaths, } from "../git-runner.js";
|
|
2
2
|
import { applyHunks, HunkStagingError } from "../git-hunk-stager.js";
|
|
3
3
|
import { config } from "../config.js";
|
|
4
|
+
import { PathOutsideRootError } from "../file-manager.js";
|
|
4
5
|
import { getProject } from "../project-manager.js";
|
|
5
6
|
import { errorSchema } from "./_schemas.js";
|
|
6
7
|
/* ----------------------------- schemas ----------------------------- */
|
|
@@ -107,6 +108,34 @@ const remotesSchema = {
|
|
|
107
108
|
},
|
|
108
109
|
},
|
|
109
110
|
};
|
|
111
|
+
const worktreesSchema = {
|
|
112
|
+
type: "object",
|
|
113
|
+
required: ["isGitRepo", "worktrees"],
|
|
114
|
+
properties: {
|
|
115
|
+
isGitRepo: { type: "boolean" },
|
|
116
|
+
worktrees: {
|
|
117
|
+
type: "array",
|
|
118
|
+
items: {
|
|
119
|
+
type: "object",
|
|
120
|
+
required: ["path", "bare", "detached", "current"],
|
|
121
|
+
properties: {
|
|
122
|
+
path: { type: "string" },
|
|
123
|
+
head: { type: "string" },
|
|
124
|
+
branch: { type: "string" },
|
|
125
|
+
bare: { type: "boolean" },
|
|
126
|
+
detached: { type: "boolean" },
|
|
127
|
+
current: { type: "boolean" },
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
class InvalidWorktreePathError extends Error {
|
|
134
|
+
constructor(path) {
|
|
135
|
+
super(`invalid worktree path: ${path}`);
|
|
136
|
+
this.name = "InvalidWorktreePathError";
|
|
137
|
+
}
|
|
138
|
+
}
|
|
110
139
|
/* ----------------------------- error mapping ----------------------------- */
|
|
111
140
|
function mapError(reply, err) {
|
|
112
141
|
if (err instanceof GitNotInstalledError) {
|
|
@@ -118,6 +147,12 @@ function mapError(reply, err) {
|
|
|
118
147
|
if (err instanceof InvalidBranchNameError) {
|
|
119
148
|
return reply.code(400).send({ error: "invalid_branch_name", message: err.message });
|
|
120
149
|
}
|
|
150
|
+
if (err instanceof InvalidWorktreePathError) {
|
|
151
|
+
return reply.code(400).send({ error: "invalid_worktree_path", message: err.message });
|
|
152
|
+
}
|
|
153
|
+
if (err instanceof PathOutsideRootError) {
|
|
154
|
+
return reply.code(403).send({ error: "path_not_allowed", message: "path outside workspace" });
|
|
155
|
+
}
|
|
121
156
|
if (err instanceof GitCommandError) {
|
|
122
157
|
// Git "rejected" / "non-fast-forward" / commit hook failures /
|
|
123
158
|
// missing upstream are user-actionable, not server bugs. 400
|
|
@@ -173,6 +208,36 @@ async function withProject(projectId, reply, fn) {
|
|
|
173
208
|
return mapError(reply, err);
|
|
174
209
|
}
|
|
175
210
|
}
|
|
211
|
+
async function resolveGitCwd(project, worktreePath) {
|
|
212
|
+
if (worktreePath === undefined)
|
|
213
|
+
return project.path;
|
|
214
|
+
const listed = await getWorktrees(project.path);
|
|
215
|
+
const match = listed.worktrees.find((w) => w.path === worktreePath);
|
|
216
|
+
if (match === undefined)
|
|
217
|
+
throw new InvalidWorktreePathError(worktreePath);
|
|
218
|
+
return match.path;
|
|
219
|
+
}
|
|
220
|
+
async function withGitCwd(projectId, worktreePath, reply, fn) {
|
|
221
|
+
const project = await resolveProject(projectId, reply);
|
|
222
|
+
if (project === undefined)
|
|
223
|
+
return reply;
|
|
224
|
+
try {
|
|
225
|
+
const cwd = await resolveGitCwd(project, worktreePath);
|
|
226
|
+
return await fn(cwd);
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
return mapError(reply, err);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const projectWorktreeQuerySchema = {
|
|
233
|
+
type: "object",
|
|
234
|
+
required: ["projectId"],
|
|
235
|
+
properties: {
|
|
236
|
+
projectId: { type: "string", minLength: 1 },
|
|
237
|
+
worktreePath: { type: "string", minLength: 1 },
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
const worktreeBodyProperty = { worktreePath: { type: "string", minLength: 1 } };
|
|
176
241
|
/* ----------------------------- routes ----------------------------- */
|
|
177
242
|
export const gitRoutes = async (fastify) => {
|
|
178
243
|
fastify.post("/git/init", {
|
|
@@ -230,48 +295,65 @@ export const gitRoutes = async (fastify) => {
|
|
|
230
295
|
"`{ isGitRepo: false, files: [] }` (NOT 500) so the panel can sit " +
|
|
231
296
|
"quiet on plain folders.",
|
|
232
297
|
tags: ["git"],
|
|
298
|
+
querystring: projectWorktreeQuerySchema,
|
|
299
|
+
response: {
|
|
300
|
+
200: statusSchema,
|
|
301
|
+
400: errorSchema,
|
|
302
|
+
403: errorSchema,
|
|
303
|
+
404: errorSchema,
|
|
304
|
+
500: errorSchema,
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
}, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getStatus(cwd)));
|
|
308
|
+
fastify.get("/git/worktrees", {
|
|
309
|
+
schema: {
|
|
310
|
+
description: "List registered git worktrees for the project's repository. Returned absolute paths " +
|
|
311
|
+
"can be passed back as `worktreePath` to git routes and are accepted only if still registered.",
|
|
312
|
+
tags: ["git"],
|
|
233
313
|
querystring: {
|
|
234
314
|
type: "object",
|
|
235
315
|
required: ["projectId"],
|
|
236
316
|
properties: { projectId: { type: "string", minLength: 1 } },
|
|
237
317
|
},
|
|
238
|
-
response: {
|
|
318
|
+
response: {
|
|
319
|
+
200: worktreesSchema,
|
|
320
|
+
400: errorSchema,
|
|
321
|
+
403: errorSchema,
|
|
322
|
+
404: errorSchema,
|
|
323
|
+
500: errorSchema,
|
|
324
|
+
},
|
|
239
325
|
},
|
|
240
|
-
}, async (req, reply) => {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
return reply;
|
|
244
|
-
try {
|
|
245
|
-
return await getStatus(project.path);
|
|
246
|
-
}
|
|
247
|
-
catch (err) {
|
|
248
|
-
return mapError(reply, err);
|
|
249
|
-
}
|
|
250
|
-
});
|
|
326
|
+
}, async (req, reply) => withProject(req.query.projectId, reply, async (p) => {
|
|
327
|
+
return getWorktrees(p.path);
|
|
328
|
+
}));
|
|
251
329
|
fastify.get("/git/diff", {
|
|
252
330
|
schema: {
|
|
253
331
|
description: "Unstaged unified diff for the project (working tree vs index).",
|
|
254
332
|
tags: ["git"],
|
|
255
|
-
querystring:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
333
|
+
querystring: projectWorktreeQuerySchema,
|
|
334
|
+
response: {
|
|
335
|
+
200: diffSchema,
|
|
336
|
+
400: errorSchema,
|
|
337
|
+
403: errorSchema,
|
|
338
|
+
404: errorSchema,
|
|
339
|
+
500: errorSchema,
|
|
259
340
|
},
|
|
260
|
-
response: { 200: diffSchema, 400: errorSchema, 404: errorSchema, 500: errorSchema },
|
|
261
341
|
},
|
|
262
|
-
}, async (req, reply) =>
|
|
342
|
+
}, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getDiff(cwd)));
|
|
263
343
|
fastify.get("/git/diff/staged", {
|
|
264
344
|
schema: {
|
|
265
345
|
description: "Staged unified diff (index vs HEAD).",
|
|
266
346
|
tags: ["git"],
|
|
267
|
-
querystring:
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
347
|
+
querystring: projectWorktreeQuerySchema,
|
|
348
|
+
response: {
|
|
349
|
+
200: diffSchema,
|
|
350
|
+
400: errorSchema,
|
|
351
|
+
403: errorSchema,
|
|
352
|
+
404: errorSchema,
|
|
353
|
+
500: errorSchema,
|
|
271
354
|
},
|
|
272
|
-
response: { 200: diffSchema, 400: errorSchema, 404: errorSchema, 500: errorSchema },
|
|
273
355
|
},
|
|
274
|
-
}, async (req, reply) =>
|
|
356
|
+
}, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getStagedDiff(cwd)));
|
|
275
357
|
fastify.get("/git/diff/file", {
|
|
276
358
|
schema: {
|
|
277
359
|
description: "Unified diff for a single file. `?staged=1` for the index↔HEAD diff; " +
|
|
@@ -284,13 +366,20 @@ export const gitRoutes = async (fastify) => {
|
|
|
284
366
|
projectId: { type: "string", minLength: 1 },
|
|
285
367
|
path: { type: "string", minLength: 1 },
|
|
286
368
|
staged: { type: "string", enum: ["0", "1", "true", "false"] },
|
|
369
|
+
worktreePath: { type: "string", minLength: 1 },
|
|
287
370
|
},
|
|
288
371
|
},
|
|
289
|
-
response: {
|
|
372
|
+
response: {
|
|
373
|
+
200: diffSchema,
|
|
374
|
+
400: errorSchema,
|
|
375
|
+
403: errorSchema,
|
|
376
|
+
404: errorSchema,
|
|
377
|
+
500: errorSchema,
|
|
378
|
+
},
|
|
290
379
|
},
|
|
291
380
|
}, async (req, reply) => {
|
|
292
381
|
const staged = req.query.staged === "1" || req.query.staged === "true";
|
|
293
|
-
return
|
|
382
|
+
return withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getFileDiff(cwd, req.query.path, staged));
|
|
294
383
|
});
|
|
295
384
|
fastify.get("/git/log", {
|
|
296
385
|
schema: {
|
|
@@ -302,41 +391,52 @@ export const gitRoutes = async (fastify) => {
|
|
|
302
391
|
properties: {
|
|
303
392
|
projectId: { type: "string", minLength: 1 },
|
|
304
393
|
limit: { type: "string", pattern: "^[0-9]+$" },
|
|
394
|
+
worktreePath: { type: "string", minLength: 1 },
|
|
305
395
|
},
|
|
306
396
|
},
|
|
307
|
-
response: {
|
|
397
|
+
response: {
|
|
398
|
+
200: logSchema,
|
|
399
|
+
400: errorSchema,
|
|
400
|
+
403: errorSchema,
|
|
401
|
+
404: errorSchema,
|
|
402
|
+
500: errorSchema,
|
|
403
|
+
},
|
|
308
404
|
},
|
|
309
405
|
}, async (req, reply) => {
|
|
310
406
|
const limit = req.query.limit !== undefined
|
|
311
407
|
? Math.min(1000, Math.max(1, Number.parseInt(req.query.limit, 10)))
|
|
312
408
|
: 30;
|
|
313
|
-
return
|
|
409
|
+
return withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getLog(cwd, limit));
|
|
314
410
|
});
|
|
315
411
|
fastify.get("/git/branches", {
|
|
316
412
|
schema: {
|
|
317
413
|
description: "Local + remote branch list with `current` flag.",
|
|
318
414
|
tags: ["git"],
|
|
319
|
-
querystring:
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
415
|
+
querystring: projectWorktreeQuerySchema,
|
|
416
|
+
response: {
|
|
417
|
+
200: branchesSchema,
|
|
418
|
+
400: errorSchema,
|
|
419
|
+
403: errorSchema,
|
|
420
|
+
404: errorSchema,
|
|
421
|
+
500: errorSchema,
|
|
323
422
|
},
|
|
324
|
-
response: { 200: branchesSchema, 400: errorSchema, 404: errorSchema, 500: errorSchema },
|
|
325
423
|
},
|
|
326
|
-
}, async (req, reply) =>
|
|
424
|
+
}, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getBranches(cwd)));
|
|
327
425
|
fastify.get("/git/remotes", {
|
|
328
426
|
schema: {
|
|
329
427
|
description: "Configured git remotes with their fetch + push URLs. " +
|
|
330
428
|
"Empty array for non-git projects or repos with no remotes.",
|
|
331
429
|
tags: ["git"],
|
|
332
|
-
querystring:
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
430
|
+
querystring: projectWorktreeQuerySchema,
|
|
431
|
+
response: {
|
|
432
|
+
200: remotesSchema,
|
|
433
|
+
400: errorSchema,
|
|
434
|
+
403: errorSchema,
|
|
435
|
+
404: errorSchema,
|
|
436
|
+
500: errorSchema,
|
|
336
437
|
},
|
|
337
|
-
response: { 200: remotesSchema, 400: errorSchema, 404: errorSchema, 500: errorSchema },
|
|
338
438
|
},
|
|
339
|
-
}, async (req, reply) =>
|
|
439
|
+
}, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getRemotes(cwd)));
|
|
340
440
|
fastify.post("/git/remote/add", {
|
|
341
441
|
schema: {
|
|
342
442
|
description: "Add a git remote (`git remote add <name> <url>`). Name is " +
|
|
@@ -352,6 +452,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
352
452
|
projectId: { type: "string", minLength: 1 },
|
|
353
453
|
name: { type: "string", minLength: 1 },
|
|
354
454
|
url: { type: "string", minLength: 1, maxLength: 1024 },
|
|
455
|
+
...worktreeBodyProperty,
|
|
355
456
|
},
|
|
356
457
|
},
|
|
357
458
|
response: {
|
|
@@ -361,18 +462,10 @@ export const gitRoutes = async (fastify) => {
|
|
|
361
462
|
500: errorSchema,
|
|
362
463
|
},
|
|
363
464
|
},
|
|
364
|
-
}, async (req, reply) => {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
try {
|
|
369
|
-
await addRemote(project.path, req.body.name, req.body.url);
|
|
370
|
-
return { ok: true };
|
|
371
|
-
}
|
|
372
|
-
catch (err) {
|
|
373
|
-
return mapError(reply, err);
|
|
374
|
-
}
|
|
375
|
-
});
|
|
465
|
+
}, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
|
|
466
|
+
await addRemote(cwd, req.body.name, req.body.url);
|
|
467
|
+
return { ok: true };
|
|
468
|
+
}));
|
|
376
469
|
fastify.delete("/git/remote/:name", {
|
|
377
470
|
schema: {
|
|
378
471
|
description: "Remove a git remote (`git remote remove <name>`). 400 " +
|
|
@@ -383,11 +476,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
383
476
|
required: ["name"],
|
|
384
477
|
properties: { name: { type: "string", minLength: 1 } },
|
|
385
478
|
},
|
|
386
|
-
querystring:
|
|
387
|
-
type: "object",
|
|
388
|
-
required: ["projectId"],
|
|
389
|
-
properties: { projectId: { type: "string", minLength: 1 } },
|
|
390
|
-
},
|
|
479
|
+
querystring: projectWorktreeQuerySchema,
|
|
391
480
|
response: {
|
|
392
481
|
200: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
|
|
393
482
|
400: errorSchema,
|
|
@@ -395,18 +484,10 @@ export const gitRoutes = async (fastify) => {
|
|
|
395
484
|
500: errorSchema,
|
|
396
485
|
},
|
|
397
486
|
},
|
|
398
|
-
}, async (req, reply) => {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
try {
|
|
403
|
-
await removeRemote(project.path, req.params.name);
|
|
404
|
-
return { ok: true };
|
|
405
|
-
}
|
|
406
|
-
catch (err) {
|
|
407
|
-
return mapError(reply, err);
|
|
408
|
-
}
|
|
409
|
-
});
|
|
487
|
+
}, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, async (cwd) => {
|
|
488
|
+
await removeRemote(cwd, req.params.name);
|
|
489
|
+
return { ok: true };
|
|
490
|
+
}));
|
|
410
491
|
fastify.post("/git/checkout", {
|
|
411
492
|
schema: {
|
|
412
493
|
description: "Switch the working tree to `branch`. Refuses on a dirty tree (git's " +
|
|
@@ -421,6 +502,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
421
502
|
properties: {
|
|
422
503
|
projectId: { type: "string", minLength: 1 },
|
|
423
504
|
branch: { type: "string", minLength: 1 },
|
|
505
|
+
...worktreeBodyProperty,
|
|
424
506
|
},
|
|
425
507
|
},
|
|
426
508
|
response: {
|
|
@@ -430,18 +512,10 @@ export const gitRoutes = async (fastify) => {
|
|
|
430
512
|
500: errorSchema,
|
|
431
513
|
},
|
|
432
514
|
},
|
|
433
|
-
}, async (req, reply) => {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
try {
|
|
438
|
-
await checkoutBranch(project.path, req.body.branch);
|
|
439
|
-
return { ok: true };
|
|
440
|
-
}
|
|
441
|
-
catch (err) {
|
|
442
|
-
return mapError(reply, err);
|
|
443
|
-
}
|
|
444
|
-
});
|
|
515
|
+
}, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
|
|
516
|
+
await checkoutBranch(cwd, req.body.branch);
|
|
517
|
+
return { ok: true };
|
|
518
|
+
}));
|
|
445
519
|
fastify.post("/git/branch/create", {
|
|
446
520
|
schema: {
|
|
447
521
|
description: "Create a local branch. `startPoint` (defaults to HEAD) accepts any ref " +
|
|
@@ -457,6 +531,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
457
531
|
name: { type: "string", minLength: 1 },
|
|
458
532
|
startPoint: { type: "string", minLength: 1 },
|
|
459
533
|
checkout: { type: "boolean" },
|
|
534
|
+
...worktreeBodyProperty,
|
|
460
535
|
},
|
|
461
536
|
},
|
|
462
537
|
response: {
|
|
@@ -466,23 +541,15 @@ export const gitRoutes = async (fastify) => {
|
|
|
466
541
|
500: errorSchema,
|
|
467
542
|
},
|
|
468
543
|
},
|
|
469
|
-
}, async (req, reply) => {
|
|
470
|
-
const
|
|
471
|
-
if (
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
opts.checkout = req.body.checkout;
|
|
479
|
-
await createBranch(project.path, req.body.name, opts);
|
|
480
|
-
return { ok: true };
|
|
481
|
-
}
|
|
482
|
-
catch (err) {
|
|
483
|
-
return mapError(reply, err);
|
|
484
|
-
}
|
|
485
|
-
});
|
|
544
|
+
}, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
|
|
545
|
+
const opts = {};
|
|
546
|
+
if (req.body.startPoint !== undefined)
|
|
547
|
+
opts.startPoint = req.body.startPoint;
|
|
548
|
+
if (req.body.checkout !== undefined)
|
|
549
|
+
opts.checkout = req.body.checkout;
|
|
550
|
+
await createBranch(cwd, req.body.name, opts);
|
|
551
|
+
return { ok: true };
|
|
552
|
+
}));
|
|
486
553
|
fastify.delete("/git/branch/:name", {
|
|
487
554
|
schema: {
|
|
488
555
|
description: "Delete a local branch via `git branch -d <name>`. `?force=1` switches " +
|
|
@@ -500,6 +567,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
500
567
|
properties: {
|
|
501
568
|
projectId: { type: "string", minLength: 1 },
|
|
502
569
|
force: { type: "string", enum: ["0", "1", "true", "false"] },
|
|
570
|
+
worktreePath: { type: "string", minLength: 1 },
|
|
503
571
|
},
|
|
504
572
|
},
|
|
505
573
|
response: {
|
|
@@ -510,17 +578,11 @@ export const gitRoutes = async (fastify) => {
|
|
|
510
578
|
},
|
|
511
579
|
},
|
|
512
580
|
}, async (req, reply) => {
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
try {
|
|
517
|
-
const force = req.query.force === "1" || req.query.force === "true";
|
|
518
|
-
await deleteBranch(project.path, req.params.name, { force });
|
|
581
|
+
const force = req.query.force === "1" || req.query.force === "true";
|
|
582
|
+
return withGitCwd(req.query.projectId, req.query.worktreePath, reply, async (cwd) => {
|
|
583
|
+
await deleteBranch(cwd, req.params.name, { force });
|
|
519
584
|
return { ok: true };
|
|
520
|
-
}
|
|
521
|
-
catch (err) {
|
|
522
|
-
return mapError(reply, err);
|
|
523
|
-
}
|
|
585
|
+
});
|
|
524
586
|
});
|
|
525
587
|
fastify.post("/git/stage", {
|
|
526
588
|
schema: {
|
|
@@ -543,6 +605,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
543
605
|
// even a very wide repo land well under 1000 paths.
|
|
544
606
|
maxItems: 1000,
|
|
545
607
|
},
|
|
608
|
+
...worktreeBodyProperty,
|
|
546
609
|
},
|
|
547
610
|
},
|
|
548
611
|
response: {
|
|
@@ -552,18 +615,10 @@ export const gitRoutes = async (fastify) => {
|
|
|
552
615
|
500: errorSchema,
|
|
553
616
|
},
|
|
554
617
|
},
|
|
555
|
-
}, async (req, reply) => {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
try {
|
|
560
|
-
await stagePaths(project.path, req.body.paths);
|
|
561
|
-
return { ok: true };
|
|
562
|
-
}
|
|
563
|
-
catch (err) {
|
|
564
|
-
return mapError(reply, err);
|
|
565
|
-
}
|
|
566
|
-
});
|
|
618
|
+
}, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
|
|
619
|
+
await stagePaths(cwd, req.body.paths);
|
|
620
|
+
return { ok: true };
|
|
621
|
+
}));
|
|
567
622
|
fastify.post("/git/unstage", {
|
|
568
623
|
schema: {
|
|
569
624
|
description: "Unstage one or more files (`git restore --staged -- <paths>`).",
|
|
@@ -585,6 +640,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
585
640
|
// even a very wide repo land well under 1000 paths.
|
|
586
641
|
maxItems: 1000,
|
|
587
642
|
},
|
|
643
|
+
...worktreeBodyProperty,
|
|
588
644
|
},
|
|
589
645
|
},
|
|
590
646
|
response: {
|
|
@@ -594,18 +650,10 @@ export const gitRoutes = async (fastify) => {
|
|
|
594
650
|
500: errorSchema,
|
|
595
651
|
},
|
|
596
652
|
},
|
|
597
|
-
}, async (req, reply) => {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
try {
|
|
602
|
-
await unstagePaths(project.path, req.body.paths);
|
|
603
|
-
return { ok: true };
|
|
604
|
-
}
|
|
605
|
-
catch (err) {
|
|
606
|
-
return mapError(reply, err);
|
|
607
|
-
}
|
|
608
|
-
});
|
|
653
|
+
}, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
|
|
654
|
+
await unstagePaths(cwd, req.body.paths);
|
|
655
|
+
return { ok: true };
|
|
656
|
+
}));
|
|
609
657
|
fastify.post("/git/apply-hunks", {
|
|
610
658
|
schema: {
|
|
611
659
|
description: "Stage or unstage selected hunks of a single file. Builds a " +
|
|
@@ -630,6 +678,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
630
678
|
minItems: 1,
|
|
631
679
|
maxItems: 1_000,
|
|
632
680
|
},
|
|
681
|
+
...worktreeBodyProperty,
|
|
633
682
|
},
|
|
634
683
|
},
|
|
635
684
|
response: {
|
|
@@ -647,12 +696,9 @@ export const gitRoutes = async (fastify) => {
|
|
|
647
696
|
500: errorSchema,
|
|
648
697
|
},
|
|
649
698
|
},
|
|
650
|
-
}, async (req, reply) => {
|
|
651
|
-
const project = await resolveProject(req.body.projectId, reply);
|
|
652
|
-
if (project === undefined)
|
|
653
|
-
return reply;
|
|
699
|
+
}, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
|
|
654
700
|
try {
|
|
655
|
-
const { totalHunks } = await applyHunks(
|
|
701
|
+
const { totalHunks } = await applyHunks(cwd, req.body.path, req.body.hunkIndices, req.body.mode);
|
|
656
702
|
return { ok: true, totalHunks };
|
|
657
703
|
}
|
|
658
704
|
catch (err) {
|
|
@@ -663,9 +709,9 @@ export const gitRoutes = async (fastify) => {
|
|
|
663
709
|
if (err instanceof HunkStagingError) {
|
|
664
710
|
return { ok: false, error: err.code, totalHunks: 0 };
|
|
665
711
|
}
|
|
666
|
-
|
|
712
|
+
throw err;
|
|
667
713
|
}
|
|
668
|
-
});
|
|
714
|
+
}));
|
|
669
715
|
fastify.post("/git/revert", {
|
|
670
716
|
schema: {
|
|
671
717
|
description: "Discard local changes for the given files via `git restore " +
|
|
@@ -692,6 +738,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
692
738
|
// even a very wide repo land well under 1000 paths.
|
|
693
739
|
maxItems: 1000,
|
|
694
740
|
},
|
|
741
|
+
...worktreeBodyProperty,
|
|
695
742
|
},
|
|
696
743
|
},
|
|
697
744
|
response: {
|
|
@@ -701,18 +748,10 @@ export const gitRoutes = async (fastify) => {
|
|
|
701
748
|
500: errorSchema,
|
|
702
749
|
},
|
|
703
750
|
},
|
|
704
|
-
}, async (req, reply) => {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
try {
|
|
709
|
-
await revertPaths(project.path, req.body.paths);
|
|
710
|
-
return { ok: true };
|
|
711
|
-
}
|
|
712
|
-
catch (err) {
|
|
713
|
-
return mapError(reply, err);
|
|
714
|
-
}
|
|
715
|
-
});
|
|
751
|
+
}, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
|
|
752
|
+
await revertPaths(cwd, req.body.paths);
|
|
753
|
+
return { ok: true };
|
|
754
|
+
}));
|
|
716
755
|
fastify.post("/git/commit", {
|
|
717
756
|
schema: {
|
|
718
757
|
description: "Commit the currently-staged changes. Pre-commit hooks fire as " +
|
|
@@ -726,6 +765,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
726
765
|
properties: {
|
|
727
766
|
projectId: { type: "string", minLength: 1 },
|
|
728
767
|
message: { type: "string", minLength: 1 },
|
|
768
|
+
...worktreeBodyProperty,
|
|
729
769
|
},
|
|
730
770
|
},
|
|
731
771
|
response: {
|
|
@@ -740,19 +780,11 @@ export const gitRoutes = async (fastify) => {
|
|
|
740
780
|
},
|
|
741
781
|
},
|
|
742
782
|
}, async (req, reply) => {
|
|
743
|
-
const project = await resolveProject(req.body.projectId, reply);
|
|
744
|
-
if (project === undefined)
|
|
745
|
-
return reply;
|
|
746
783
|
const message = req.body.message.trim();
|
|
747
784
|
if (message.length === 0) {
|
|
748
785
|
return reply.code(400).send({ error: "empty_message" });
|
|
749
786
|
}
|
|
750
|
-
|
|
751
|
-
return await commit(project.path, message);
|
|
752
|
-
}
|
|
753
|
-
catch (err) {
|
|
754
|
-
return mapError(reply, err);
|
|
755
|
-
}
|
|
787
|
+
return withGitCwd(req.body.projectId, req.body.worktreePath, reply, (cwd) => commit(cwd, message));
|
|
756
788
|
});
|
|
757
789
|
fastify.post("/git/fetch", {
|
|
758
790
|
schema: {
|
|
@@ -768,6 +800,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
768
800
|
projectId: { type: "string", minLength: 1 },
|
|
769
801
|
remote: { type: "string", minLength: 1 },
|
|
770
802
|
prune: { type: "boolean" },
|
|
803
|
+
...worktreeBodyProperty,
|
|
771
804
|
},
|
|
772
805
|
},
|
|
773
806
|
response: {
|
|
@@ -777,23 +810,15 @@ export const gitRoutes = async (fastify) => {
|
|
|
777
810
|
500: errorSchema,
|
|
778
811
|
},
|
|
779
812
|
},
|
|
780
|
-
}, async (req, reply) => {
|
|
781
|
-
const
|
|
782
|
-
if (
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
opts.prune = req.body.prune;
|
|
790
|
-
const { stdout } = await fetch(project.path, opts);
|
|
791
|
-
return { output: stdout };
|
|
792
|
-
}
|
|
793
|
-
catch (err) {
|
|
794
|
-
return mapError(reply, err);
|
|
795
|
-
}
|
|
796
|
-
});
|
|
813
|
+
}, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
|
|
814
|
+
const opts = {};
|
|
815
|
+
if (req.body.remote !== undefined)
|
|
816
|
+
opts.remote = req.body.remote;
|
|
817
|
+
if (req.body.prune !== undefined)
|
|
818
|
+
opts.prune = req.body.prune;
|
|
819
|
+
const { stdout } = await fetch(cwd, opts);
|
|
820
|
+
return { output: stdout };
|
|
821
|
+
}));
|
|
797
822
|
fastify.post("/git/pull", {
|
|
798
823
|
schema: {
|
|
799
824
|
description: "git pull — fetches AND merges (or rebases with `rebase: true`). " +
|
|
@@ -810,6 +835,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
810
835
|
remote: { type: "string", minLength: 1 },
|
|
811
836
|
branch: { type: "string", minLength: 1 },
|
|
812
837
|
rebase: { type: "boolean" },
|
|
838
|
+
...worktreeBodyProperty,
|
|
813
839
|
},
|
|
814
840
|
},
|
|
815
841
|
response: {
|
|
@@ -819,25 +845,17 @@ export const gitRoutes = async (fastify) => {
|
|
|
819
845
|
500: errorSchema,
|
|
820
846
|
},
|
|
821
847
|
},
|
|
822
|
-
}, async (req, reply) => {
|
|
823
|
-
const
|
|
824
|
-
if (
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
opts.rebase = req.body.rebase;
|
|
834
|
-
const { stdout } = await pull(project.path, opts);
|
|
835
|
-
return { output: stdout };
|
|
836
|
-
}
|
|
837
|
-
catch (err) {
|
|
838
|
-
return mapError(reply, err);
|
|
839
|
-
}
|
|
840
|
-
});
|
|
848
|
+
}, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
|
|
849
|
+
const opts = {};
|
|
850
|
+
if (req.body.remote !== undefined)
|
|
851
|
+
opts.remote = req.body.remote;
|
|
852
|
+
if (req.body.branch !== undefined)
|
|
853
|
+
opts.branch = req.body.branch;
|
|
854
|
+
if (req.body.rebase !== undefined)
|
|
855
|
+
opts.rebase = req.body.rebase;
|
|
856
|
+
const { stdout } = await pull(cwd, opts);
|
|
857
|
+
return { output: stdout };
|
|
858
|
+
}));
|
|
841
859
|
fastify.post("/git/push", {
|
|
842
860
|
config: {
|
|
843
861
|
rateLimit: {
|
|
@@ -862,6 +880,7 @@ export const gitRoutes = async (fastify) => {
|
|
|
862
880
|
remote: { type: "string", minLength: 1 },
|
|
863
881
|
branch: { type: "string", minLength: 1 },
|
|
864
882
|
setUpstream: { type: "boolean" },
|
|
883
|
+
...worktreeBodyProperty,
|
|
865
884
|
},
|
|
866
885
|
},
|
|
867
886
|
response: {
|
|
@@ -875,24 +894,16 @@ export const gitRoutes = async (fastify) => {
|
|
|
875
894
|
500: errorSchema,
|
|
876
895
|
},
|
|
877
896
|
},
|
|
878
|
-
}, async (req, reply) => {
|
|
879
|
-
const
|
|
880
|
-
if (
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
opts.setUpstream = req.body.setUpstream;
|
|
890
|
-
const { stdout } = await push(project.path, opts);
|
|
891
|
-
return { output: stdout };
|
|
892
|
-
}
|
|
893
|
-
catch (err) {
|
|
894
|
-
return mapError(reply, err);
|
|
895
|
-
}
|
|
896
|
-
});
|
|
897
|
+
}, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
|
|
898
|
+
const opts = {};
|
|
899
|
+
if (req.body.remote !== undefined)
|
|
900
|
+
opts.remote = req.body.remote;
|
|
901
|
+
if (req.body.branch !== undefined)
|
|
902
|
+
opts.branch = req.body.branch;
|
|
903
|
+
if (req.body.setUpstream !== undefined)
|
|
904
|
+
opts.setUpstream = req.body.setUpstream;
|
|
905
|
+
const { stdout } = await push(cwd, opts);
|
|
906
|
+
return { output: stdout };
|
|
907
|
+
}));
|
|
897
908
|
};
|
|
898
909
|
//# sourceMappingURL=git.js.map
|