@agimon-ai/po-git 0.0.0-alpha.9 → 0.0.0-alpha.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +149 -21
- package/README.md +16 -4
- package/dist/apiContracts.cjs +34 -1
- package/dist/apiContracts.cjs.map +1 -1
- package/dist/apiContracts.mjs +34 -1
- package/dist/apiContracts.mjs.map +1 -1
- package/dist/extensions/native.mjs +2159 -520
- package/dist/extensions/native.mjs.map +1 -1
- package/dist/extensions/server.cjs +512 -275
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts +2 -2
- package/dist/extensions/server.d.mts.map +1 -1
- package/dist/extensions/server.mjs +509 -272
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +1549 -1294
- package/dist/extensions/web.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.mts +2 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{worktreeOperations-BSeyxUai.mjs → worktreeOperations-B3E4JY4H.mjs} +258 -85
- package/dist/worktreeOperations-B3E4JY4H.mjs.map +1 -0
- package/dist/{worktreeOperations-DGUbUnYU.cjs → worktreeOperations-CD7s0YnO.cjs} +288 -85
- package/dist/worktreeOperations-CD7s0YnO.cjs.map +1 -0
- package/llms.txt +1 -1
- package/package.json +21 -19
- package/src/prompts/{po-use-git → codemode-how-to-git}/SKILL.md +35 -32
- package/dist/worktreeOperations-BSeyxUai.mjs.map +0 -1
- package/dist/worktreeOperations-DGUbUnYU.cjs.map +0 -1
|
@@ -2,17 +2,20 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_worktreeOperations = require("../worktreeOperations-
|
|
6
|
-
let
|
|
7
|
-
|
|
8
|
-
let node_os = require("node:os");
|
|
9
|
-
node_os = require_worktreeOperations.__toESM(node_os, 1);
|
|
5
|
+
const require_worktreeOperations = require("../worktreeOperations-CD7s0YnO.cjs");
|
|
6
|
+
let node_fs_promises = require("node:fs/promises");
|
|
7
|
+
node_fs_promises = require_worktreeOperations.__toESM(node_fs_promises, 1);
|
|
10
8
|
let node_path = require("node:path");
|
|
11
9
|
node_path = require_worktreeOperations.__toESM(node_path, 1);
|
|
12
|
-
let
|
|
10
|
+
let node_os = require("node:os");
|
|
11
|
+
node_os = require_worktreeOperations.__toESM(node_os, 1);
|
|
12
|
+
let _agimon_ai_po_core_serverPlugin = require("@agimon-ai/po-core/serverPlugin");
|
|
13
13
|
let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile");
|
|
14
|
+
let _agimon_ai_po_core_codemode = require("@agimon-ai/po-core/codemode");
|
|
14
15
|
let _agimon_ai_po_session = require("@agimon-ai/po-session");
|
|
15
16
|
let typebox = require("typebox");
|
|
17
|
+
let _agimon_ai_po_core_platform = require("@agimon-ai/po-core/platform");
|
|
18
|
+
let _agimon_ai_po_core_packageApi = require("@agimon-ai/po-core/packageApi");
|
|
16
19
|
//#region src/schemas/runWorktreeTool.ts
|
|
17
20
|
/**
|
|
18
21
|
* The tool's action vocabulary.
|
|
@@ -254,7 +257,8 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
254
257
|
content: text(describe(record, context.sessionId), `Session: ${record.sessionId}`, dirtyFiles.length === 0 ? "Clean." : `${String(dirtyFiles.length)} uncommitted file(s): ${dirtyFiles.slice(0, 20).join(", ")}`),
|
|
255
258
|
details: {
|
|
256
259
|
action: params.action,
|
|
257
|
-
records: [record]
|
|
260
|
+
records: [record],
|
|
261
|
+
dirtyFiles
|
|
258
262
|
}
|
|
259
263
|
};
|
|
260
264
|
}
|
|
@@ -278,7 +282,10 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
278
282
|
const waiting = await operations.messages(context, params.id);
|
|
279
283
|
return {
|
|
280
284
|
content: waiting.length === 0 ? text("No messages.") : text(`${String(waiting.length)} message(s):`, ...waiting.map((entry) => `[${entry.sentAt}] ${entry.from}: ${entry.text}`)),
|
|
281
|
-
details: {
|
|
285
|
+
details: {
|
|
286
|
+
action: params.action,
|
|
287
|
+
messages: waiting
|
|
288
|
+
}
|
|
282
289
|
};
|
|
283
290
|
}
|
|
284
291
|
case WORKTREE_ACTIONS.prune: {
|
|
@@ -293,12 +300,172 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
293
300
|
`Untracked directories, left alone: ${plan.untracked.length === 0 ? "none" : plan.untracked.join(", ")}`,
|
|
294
301
|
...plan.keptBranches.length === 0 ? [] : [`Branches kept, they hold commits: ${plan.keptBranches.join(", ")}`]
|
|
295
302
|
]),
|
|
296
|
-
details: {
|
|
303
|
+
details: {
|
|
304
|
+
action: params.action,
|
|
305
|
+
plan
|
|
306
|
+
}
|
|
297
307
|
};
|
|
298
308
|
}
|
|
299
309
|
}
|
|
300
310
|
}
|
|
301
311
|
//#endregion
|
|
312
|
+
//#region src/services/worktreeNamespace/index.ts
|
|
313
|
+
const DECLARATIONS = `type WorktreeStatus = 'spawning' | 'running' | 'closing' | 'orphaned';
|
|
314
|
+
|
|
315
|
+
interface Worktree {
|
|
316
|
+
id: string;
|
|
317
|
+
branch: string;
|
|
318
|
+
baseRef: string;
|
|
319
|
+
/** Absolute path of the checkout, under ~/.po/git/worktrees. */
|
|
320
|
+
path: string;
|
|
321
|
+
repositoryRoot: string;
|
|
322
|
+
/** The session running inside the worktree. */
|
|
323
|
+
sessionId: string;
|
|
324
|
+
parentSessionId?: string;
|
|
325
|
+
status: WorktreeStatus;
|
|
326
|
+
createdAt: string;
|
|
327
|
+
/** How your session relates to it: 'parent' spawned it, 'child' runs inside it, 'other' is neither. */
|
|
328
|
+
relationship: 'parent' | 'child' | 'other';
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
interface WorktreeMessage {
|
|
332
|
+
worktreeId: string;
|
|
333
|
+
fromSessionId: string;
|
|
334
|
+
from: 'parent' | 'child';
|
|
335
|
+
text: string;
|
|
336
|
+
sentAt: string;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
interface PrunePlan {
|
|
340
|
+
remove: Worktree[];
|
|
341
|
+
forget: Worktree[];
|
|
342
|
+
/** Kept because they hold uncommitted work. */
|
|
343
|
+
keptDirty: Worktree[];
|
|
344
|
+
untracked: string[];
|
|
345
|
+
/** Branches kept because they hold commits. */
|
|
346
|
+
keptBranches: string[];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
declare const worktrees: {
|
|
350
|
+
/** Every worktree for this repository. */
|
|
351
|
+
list(): Promise<Worktree[]>;
|
|
352
|
+
/** One worktree and its uncommitted files. */
|
|
353
|
+
status(id: string): Promise<{ worktree: Worktree; dirtyFiles: string[] }>;
|
|
354
|
+
/**
|
|
355
|
+
* Create a worktree on a new branch and start a Po session in it, nested under this one. baseRef defaults to
|
|
356
|
+
* the current branch's remote-tracking ref, then the remote default branch; name labels it in the rail; task is
|
|
357
|
+
* the new session's first message. Setup can be slow, so give exec a larger timeoutMs.
|
|
358
|
+
*/
|
|
359
|
+
spawn(request: { branch: string; baseRef?: string; name?: string; task?: string }): Promise<Worktree>;
|
|
360
|
+
/** Stop the worktree's session and delete its checkout. Refused on a dirty tree unless force is true. */
|
|
361
|
+
close(id: string, options?: { force?: boolean }): Promise<Worktree>;
|
|
362
|
+
/** Merge the worktree's branch into this checkout. Refused when this checkout is dirty. */
|
|
363
|
+
merge(id: string, options?: { message?: string }): Promise<Worktree>;
|
|
364
|
+
/** Remove orphaned worktrees. With dryRun, only return the plan. */
|
|
365
|
+
prune(options?: { dryRun?: boolean }): Promise<PrunePlan>;
|
|
366
|
+
/**
|
|
367
|
+
* Message the other side of a worktree. Use the record with relationship 'child' to report to your parent,
|
|
368
|
+
* and 'parent' to reach your own children. 'other' records are not communication edges.
|
|
369
|
+
*/
|
|
370
|
+
send(id: string, message: string): Promise<void>;
|
|
371
|
+
/** Messages waiting for you from a worktree. */
|
|
372
|
+
messages(id: string): Promise<WorktreeMessage[]>;
|
|
373
|
+
};`;
|
|
374
|
+
const json = (value) => JSON.parse(JSON.stringify(value));
|
|
375
|
+
/** Git worktrees for code mode: the actions of `run_worktree` as methods, returning records instead of text. */
|
|
376
|
+
function createWorktreeNamespace(options) {
|
|
377
|
+
return {
|
|
378
|
+
name: "worktrees",
|
|
379
|
+
summary: "Git worktrees, each with its own Po session nested under this one: worktrees.list, status, spawn, close, merge, prune, send and messages.",
|
|
380
|
+
declarations: DECLARATIONS,
|
|
381
|
+
bind(run) {
|
|
382
|
+
if (run.sessionId !== options.execution.sessionId) throw new Error("Worktrees namespace belongs to another session.");
|
|
383
|
+
let disposed = false;
|
|
384
|
+
run.onDispose(() => {
|
|
385
|
+
disposed = true;
|
|
386
|
+
});
|
|
387
|
+
const view = (record) => ({
|
|
388
|
+
...record,
|
|
389
|
+
relationship: record.parentSessionId === options.execution.sessionId ? "parent" : record.sessionId === options.execution.sessionId ? "child" : "other"
|
|
390
|
+
});
|
|
391
|
+
/** One `run_worktree` action through the tool's own code, so the tool and the namespace cannot drift. */
|
|
392
|
+
const act = async (name, action, input) => {
|
|
393
|
+
run.signal.throwIfAborted();
|
|
394
|
+
if (disposed) throw new Error("Worktrees namespace run is disposed.");
|
|
395
|
+
options.assertAvailable();
|
|
396
|
+
const params = validateParams({
|
|
397
|
+
action,
|
|
398
|
+
...typeof input === "object" && input !== null ? input : {}
|
|
399
|
+
});
|
|
400
|
+
const context = {
|
|
401
|
+
cwd: options.execution.cwd,
|
|
402
|
+
sessionId: options.execution.sessionId
|
|
403
|
+
};
|
|
404
|
+
const started = Date.now();
|
|
405
|
+
try {
|
|
406
|
+
const result = await executeRunWorktreeTool(params, options.operations, context, { signal: run.signal });
|
|
407
|
+
options.assertAvailable();
|
|
408
|
+
run.record?.({
|
|
409
|
+
name: `worktrees.${name}`,
|
|
410
|
+
tool: RUN_WORKTREE_TOOL_NAME,
|
|
411
|
+
args: params,
|
|
412
|
+
ok: true,
|
|
413
|
+
ms: Date.now() - started,
|
|
414
|
+
result
|
|
415
|
+
});
|
|
416
|
+
return result;
|
|
417
|
+
} catch (error) {
|
|
418
|
+
run.record?.({
|
|
419
|
+
name: `worktrees.${name}`,
|
|
420
|
+
tool: RUN_WORKTREE_TOOL_NAME,
|
|
421
|
+
args: params,
|
|
422
|
+
ok: false,
|
|
423
|
+
ms: Date.now() - started,
|
|
424
|
+
error: error instanceof Error ? error.message : String(error)
|
|
425
|
+
});
|
|
426
|
+
throw error;
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
const record = (result) => view(result.details.records[0]);
|
|
430
|
+
const members = Object.create(null);
|
|
431
|
+
members.list = async () => json((await act("list", "list", {})).details.records.map(view));
|
|
432
|
+
members.status = async (id) => {
|
|
433
|
+
const { details } = await act("status", "status", { id });
|
|
434
|
+
return json({
|
|
435
|
+
worktree: view(details.records[0]),
|
|
436
|
+
dirtyFiles: details.dirtyFiles ?? []
|
|
437
|
+
});
|
|
438
|
+
};
|
|
439
|
+
members.spawn = async (request) => json(record(await act("spawn", "spawn_worktree", request)));
|
|
440
|
+
members.close = async (id, settings) => json(record(await act("close", "close_worktree", {
|
|
441
|
+
...settings,
|
|
442
|
+
id
|
|
443
|
+
})));
|
|
444
|
+
members.merge = async (id, settings) => json(record(await act("merge", "merge", {
|
|
445
|
+
...settings,
|
|
446
|
+
id
|
|
447
|
+
})));
|
|
448
|
+
members.prune = async (settings) => {
|
|
449
|
+
const { plan } = (await act("prune", "prune", settings)).details;
|
|
450
|
+
return json({
|
|
451
|
+
...plan,
|
|
452
|
+
remove: plan.remove.map(view),
|
|
453
|
+
forget: plan.forget.map(view),
|
|
454
|
+
keptDirty: plan.keptDirty.map(view)
|
|
455
|
+
});
|
|
456
|
+
};
|
|
457
|
+
members.send = async (id, message) => {
|
|
458
|
+
await act("send", "send", {
|
|
459
|
+
id,
|
|
460
|
+
message
|
|
461
|
+
});
|
|
462
|
+
};
|
|
463
|
+
members.messages = async (id) => json((await act("messages", "messages", { id })).details.messages);
|
|
464
|
+
return members;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
//#endregion
|
|
302
469
|
//#region src/extensions/workspaces/sessions/(backend)/_lib/root.server.ts
|
|
303
470
|
function progressResult(action, label) {
|
|
304
471
|
return {
|
|
@@ -309,9 +476,9 @@ function progressResult(action, label) {
|
|
|
309
476
|
details: { action }
|
|
310
477
|
};
|
|
311
478
|
}
|
|
312
|
-
function createGitSession({ context, host: serverHost }) {
|
|
313
|
-
if (serverHost.context.directEvents === void 0) throw new Error("Git
|
|
314
|
-
if (serverHost.context.sessionId === void 0) throw new Error("Git
|
|
479
|
+
function createGitSession({ context, host: serverHost, agent, signal }) {
|
|
480
|
+
if (serverHost.context.directEvents === void 0) throw new Error("Git server plugin requires the session direct event bus.");
|
|
481
|
+
if (serverHost.context.sessionId === void 0) throw new Error("Git server plugin requires a session identity.");
|
|
315
482
|
const sessionId = serverHost.context.sessionId;
|
|
316
483
|
const directEvents = serverHost.context.directEvents;
|
|
317
484
|
const messageInbox = require_worktreeOperations.createWorktreeMessageInbox(directEvents, sessionId);
|
|
@@ -322,20 +489,12 @@ function createGitSession({ context, host: serverHost }) {
|
|
|
322
489
|
sessionDelivery: () => (0, _agimon_ai_po_session.readPoSessionDelivery)(context)
|
|
323
490
|
});
|
|
324
491
|
const resources = [{
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
attribution: {
|
|
328
|
-
kind: "minor",
|
|
329
|
-
mode: "help"
|
|
330
|
-
}
|
|
331
|
-
},
|
|
332
|
-
name: "po-git",
|
|
333
|
-
kind: "context",
|
|
334
|
-
read: () => (0, _agimon_ai_po_core_serverFacet.readPackageResource)(require("url").pathToFileURL(__filename).href, "llms.txt")
|
|
335
|
-
}, {
|
|
336
|
-
name: "po-use-git",
|
|
492
|
+
name: "codemode-how-to-git",
|
|
493
|
+
description: "Hand a branch of work to a separate session in its own checkout, keeping the current checkout untouched. Use for a long refactor, a risky migration, or parallel features that touch the same files; not for parallel work on one branch.",
|
|
337
494
|
kind: "skill",
|
|
338
|
-
|
|
495
|
+
pinned: true,
|
|
496
|
+
path: (0, _agimon_ai_po_core_serverPlugin.packageResourcePath)(require("url").pathToFileURL(__filename).href, "src/prompts/codemode-how-to-git/SKILL.md"),
|
|
497
|
+
read: () => (0, _agimon_ai_po_core_serverPlugin.readPackageResource)(require("url").pathToFileURL(__filename).href, "src/prompts/codemode-how-to-git/SKILL.md")
|
|
339
498
|
}];
|
|
340
499
|
const tool = {
|
|
341
500
|
name: RUN_WORKTREE_TOOL_NAME,
|
|
@@ -355,17 +514,37 @@ function createGitSession({ context, host: serverHost }) {
|
|
|
355
514
|
const changed = (_event, execution) => {
|
|
356
515
|
directEvents.publish(require_worktreeOperations.GIT_SESSION_CHANGED_EVENT, execution.sessionId, { version: 1 });
|
|
357
516
|
};
|
|
517
|
+
const hooks = [{
|
|
518
|
+
event: "tool_execution_end",
|
|
519
|
+
handle: changed
|
|
520
|
+
}, {
|
|
521
|
+
event: "agent_settled",
|
|
522
|
+
handle: changed
|
|
523
|
+
}];
|
|
524
|
+
if (agent) {
|
|
525
|
+
const namespace = createWorktreeNamespace({
|
|
526
|
+
operations,
|
|
527
|
+
execution: agent.context,
|
|
528
|
+
assertAvailable: () => {
|
|
529
|
+
if (signal?.aborted) throw new Error("Git session is unavailable.");
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
context.inject([_agimon_ai_po_core_codemode.PO_CODEMODE_SERVICE], (scoped) => {
|
|
533
|
+
const codemode = (0, _agimon_ai_po_core_codemode.readPoCodemode)(scoped);
|
|
534
|
+
if (!codemode) return void 0;
|
|
535
|
+
const registration = codemode.register("@agimon-ai/po-git", namespace);
|
|
536
|
+
const restriction = agent.registerToolRestriction({ excludedTools: [RUN_WORKTREE_TOOL_NAME] });
|
|
537
|
+
return () => {
|
|
538
|
+
restriction.dispose();
|
|
539
|
+
registration.dispose();
|
|
540
|
+
};
|
|
541
|
+
});
|
|
542
|
+
}
|
|
358
543
|
return {
|
|
359
544
|
value: {
|
|
360
545
|
resources,
|
|
361
546
|
tool,
|
|
362
|
-
hooks
|
|
363
|
-
event: "tool_execution_end",
|
|
364
|
-
handle: changed
|
|
365
|
-
}, {
|
|
366
|
-
event: "agent_settled",
|
|
367
|
-
handle: changed
|
|
368
|
-
}]
|
|
547
|
+
hooks
|
|
369
548
|
},
|
|
370
549
|
onDispose: () => messageInbox.close()
|
|
371
550
|
};
|
|
@@ -476,11 +655,11 @@ const NAME_STATUS = {
|
|
|
476
655
|
T: "modified"
|
|
477
656
|
};
|
|
478
657
|
/** Line count of a small regular file, or undefined when it is binary, large, or not a regular file. */
|
|
479
|
-
function untrackedLines(root, relPath) {
|
|
658
|
+
async function untrackedLines(root, relPath) {
|
|
480
659
|
const absolute = node_path.default.join(root, relPath);
|
|
481
660
|
let stat;
|
|
482
661
|
try {
|
|
483
|
-
stat =
|
|
662
|
+
stat = await node_fs_promises.default.lstat(absolute);
|
|
484
663
|
} catch {
|
|
485
664
|
return;
|
|
486
665
|
}
|
|
@@ -489,7 +668,7 @@ function untrackedLines(root, relPath) {
|
|
|
489
668
|
binary: false
|
|
490
669
|
};
|
|
491
670
|
if (!stat.isFile() || stat.size > MAX_FILE_BYTES) return void 0;
|
|
492
|
-
const content =
|
|
671
|
+
const content = await node_fs_promises.default.readFile(absolute);
|
|
493
672
|
if (content.subarray(0, BINARY_PROBE_BYTES).includes(0)) return {
|
|
494
673
|
lines: 0,
|
|
495
674
|
binary: true
|
|
@@ -612,8 +791,8 @@ function createBranchDiff() {
|
|
|
612
791
|
"--exclude-standard",
|
|
613
792
|
"-z"
|
|
614
793
|
]));
|
|
615
|
-
untracked.
|
|
616
|
-
const counted = index < MAX_UNTRACKED_COUNTED ? untrackedLines(root, relPath) : void 0;
|
|
794
|
+
for (const [index, relPath] of untracked.entries()) {
|
|
795
|
+
const counted = index < MAX_UNTRACKED_COUNTED ? await untrackedLines(root, relPath) : void 0;
|
|
617
796
|
entries.push({
|
|
618
797
|
path: relPath,
|
|
619
798
|
status: "untracked",
|
|
@@ -621,18 +800,20 @@ function createBranchDiff() {
|
|
|
621
800
|
removed: 0,
|
|
622
801
|
...counted?.binary ? { binary: true } : {}
|
|
623
802
|
});
|
|
624
|
-
}
|
|
803
|
+
}
|
|
625
804
|
entries.sort((left, right) => left.path.localeCompare(right.path));
|
|
626
805
|
const conflicts = [...unmerged];
|
|
627
|
-
const
|
|
806
|
+
const rebaseMerge = await readLine$1(root, [
|
|
628
807
|
"rev-parse",
|
|
629
808
|
"--git-path",
|
|
630
809
|
"rebase-merge"
|
|
631
|
-
])
|
|
810
|
+
]);
|
|
811
|
+
const rebaseApply = await readLine$1(root, [
|
|
632
812
|
"rev-parse",
|
|
633
813
|
"--git-path",
|
|
634
814
|
"rebase-apply"
|
|
635
|
-
])
|
|
815
|
+
]);
|
|
816
|
+
const rebasing = (await Promise.all([rebaseMerge, rebaseApply].map(async (gitPath) => gitPath === void 0 ? false : node_fs_promises.default.access(node_path.default.resolve(root, gitPath)).then(() => true, () => false)))).some(Boolean);
|
|
636
817
|
const upstreamRef = branch === void 0 ? void 0 : await upstreamOf(root, branch);
|
|
637
818
|
let upstream;
|
|
638
819
|
if (upstreamRef !== void 0) {
|
|
@@ -677,7 +858,7 @@ function createBranchDiff() {
|
|
|
677
858
|
if (entry === void 0) throw new Error("That path is not in this review.");
|
|
678
859
|
if (entry.status === "untracked") {
|
|
679
860
|
const absolute = node_path.default.join(review.root, entry.path);
|
|
680
|
-
const stat =
|
|
861
|
+
const stat = await node_fs_promises.default.lstat(absolute);
|
|
681
862
|
if (stat.isSymbolicLink()) return {
|
|
682
863
|
path: entry.path,
|
|
683
864
|
hunks: [{
|
|
@@ -685,7 +866,7 @@ function createBranchDiff() {
|
|
|
685
866
|
rows: [{
|
|
686
867
|
marker: "+",
|
|
687
868
|
line: 1,
|
|
688
|
-
content:
|
|
869
|
+
content: await node_fs_promises.default.readlink(absolute)
|
|
689
870
|
}]
|
|
690
871
|
}]
|
|
691
872
|
};
|
|
@@ -694,7 +875,7 @@ function createBranchDiff() {
|
|
|
694
875
|
hunks: [],
|
|
695
876
|
tooLarge: true
|
|
696
877
|
};
|
|
697
|
-
const content =
|
|
878
|
+
const content = await node_fs_promises.default.readFile(absolute);
|
|
698
879
|
if (content.subarray(0, BINARY_PROBE_BYTES).includes(0)) return {
|
|
699
880
|
path: entry.path,
|
|
700
881
|
hunks: [],
|
|
@@ -811,9 +992,9 @@ function isRecord(value) {
|
|
|
811
992
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
812
993
|
}
|
|
813
994
|
/** The workspace key: the canonical root, so a symlinked path and its target share one entry. */
|
|
814
|
-
function workspaceKey(workspaceRoot) {
|
|
995
|
+
async function workspaceKey(workspaceRoot) {
|
|
815
996
|
try {
|
|
816
|
-
return
|
|
997
|
+
return await node_fs_promises.default.realpath(workspaceRoot);
|
|
817
998
|
} catch {
|
|
818
999
|
return node_path.default.resolve(workspaceRoot);
|
|
819
1000
|
}
|
|
@@ -862,7 +1043,7 @@ function publicView(config) {
|
|
|
862
1043
|
* the saved one, and only for the same host: a token never follows a host it
|
|
863
1044
|
* was not entered for.
|
|
864
1045
|
*/
|
|
865
|
-
function parseGitAuthSave(request, current, homeDir) {
|
|
1046
|
+
async function parseGitAuthSave(request, current, homeDir) {
|
|
866
1047
|
if (!isRecord(request)) throw require_worktreeOperations.invalidRequest("The git auth request is not an object.", "Send method none, ssh or https.");
|
|
867
1048
|
if (request.method === "none") return { method: "none" };
|
|
868
1049
|
if (request.method === "ssh") {
|
|
@@ -872,7 +1053,7 @@ function parseGitAuthSave(request, current, homeDir) {
|
|
|
872
1053
|
if (!keyPath.startsWith("/") && !keyPath.startsWith("~/")) throw require_worktreeOperations.invalidRequest("The key path is not absolute.", "Give the key as an absolute path or one under ~/.");
|
|
873
1054
|
let isFile = false;
|
|
874
1055
|
try {
|
|
875
|
-
isFile =
|
|
1056
|
+
isFile = (await node_fs_promises.default.stat(expandHome(keyPath, homeDir))).isFile();
|
|
876
1057
|
} catch {
|
|
877
1058
|
isFile = false;
|
|
878
1059
|
}
|
|
@@ -907,16 +1088,16 @@ function parseGitAuthSave(request, current, homeDir) {
|
|
|
907
1088
|
throw require_worktreeOperations.invalidRequest("The git auth method is unknown.", "Send method none, ssh or https.");
|
|
908
1089
|
}
|
|
909
1090
|
/** Tightens a file or folder something left readable by group or others. */
|
|
910
|
-
function tighten(target, mode) {
|
|
1091
|
+
async function tighten(target, mode) {
|
|
911
1092
|
try {
|
|
912
|
-
if ((
|
|
1093
|
+
if (((await node_fs_promises.default.stat(target)).mode & LOOSE_BITS) !== 0) await node_fs_promises.default.chmod(target, mode);
|
|
913
1094
|
} catch {}
|
|
914
1095
|
}
|
|
915
1096
|
function createGitAuthStore(homeDir = node_os.default.homedir()) {
|
|
916
1097
|
const file = require_worktreeOperations.credentialsFile(homeDir);
|
|
917
|
-
const readFile = () => {
|
|
918
|
-
tighten(file, OWNER_ONLY_FILE);
|
|
919
|
-
const raw = require_worktreeOperations.readJson(file);
|
|
1098
|
+
const readFile = async () => {
|
|
1099
|
+
await tighten(file, OWNER_ONLY_FILE);
|
|
1100
|
+
const raw = await require_worktreeOperations.readJson(file);
|
|
920
1101
|
const workspaces = {};
|
|
921
1102
|
if (isRecord(raw) && raw.version === 1 && isRecord(raw.workspaces)) for (const [key, value] of Object.entries(raw.workspaces)) {
|
|
922
1103
|
const config = parseConfig(value);
|
|
@@ -927,28 +1108,28 @@ function createGitAuthStore(homeDir = node_os.default.homedir()) {
|
|
|
927
1108
|
workspaces
|
|
928
1109
|
};
|
|
929
1110
|
};
|
|
930
|
-
const read = (workspaceRoot) => readFile().workspaces[workspaceKey(workspaceRoot)] ?? { method: "none" };
|
|
1111
|
+
const read = async (workspaceRoot) => (await readFile()).workspaces[await workspaceKey(workspaceRoot)] ?? { method: "none" };
|
|
931
1112
|
return {
|
|
932
1113
|
read,
|
|
933
|
-
view: (workspaceRoot) => publicView(read(workspaceRoot)),
|
|
934
|
-
save(workspaceRoot, request) {
|
|
935
|
-
const key = workspaceKey(workspaceRoot);
|
|
936
|
-
const current = readFile();
|
|
937
|
-
const next = parseGitAuthSave(request, current.workspaces[key] ?? { method: "none" }, homeDir);
|
|
1114
|
+
view: async (workspaceRoot) => publicView(await read(workspaceRoot)),
|
|
1115
|
+
async save(workspaceRoot, request) {
|
|
1116
|
+
const key = await workspaceKey(workspaceRoot);
|
|
1117
|
+
const current = await readFile();
|
|
1118
|
+
const next = await parseGitAuthSave(request, current.workspaces[key] ?? { method: "none" }, homeDir);
|
|
938
1119
|
const root = require_worktreeOperations.poGitRoot(homeDir);
|
|
939
|
-
|
|
1120
|
+
await node_fs_promises.default.mkdir(root, {
|
|
940
1121
|
recursive: true,
|
|
941
1122
|
mode: OWNER_ONLY_DIRECTORY
|
|
942
1123
|
});
|
|
943
|
-
|
|
1124
|
+
await node_fs_promises.default.chmod(root, OWNER_ONLY_DIRECTORY);
|
|
944
1125
|
const workspaces = { ...current.workspaces };
|
|
945
1126
|
if (next.method === "none") delete workspaces[key];
|
|
946
1127
|
else workspaces[key] = next;
|
|
947
|
-
require_worktreeOperations.writeJsonAtomic(file, {
|
|
1128
|
+
await require_worktreeOperations.writeJsonAtomic(file, {
|
|
948
1129
|
version: 1,
|
|
949
1130
|
workspaces
|
|
950
1131
|
});
|
|
951
|
-
|
|
1132
|
+
await node_fs_promises.default.chmod(file, OWNER_ONLY_FILE);
|
|
952
1133
|
return publicView(next);
|
|
953
1134
|
}
|
|
954
1135
|
};
|
|
@@ -1113,177 +1294,236 @@ function failed(error) {
|
|
|
1113
1294
|
}, { status: error.retryable ? 503 : 409 });
|
|
1114
1295
|
return Response.json({ error: "The worktree operation failed." }, { status: 500 });
|
|
1115
1296
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1297
|
+
function createGitApi(openDirectory) {
|
|
1298
|
+
return {
|
|
1299
|
+
basePath: "git",
|
|
1300
|
+
start(context) {
|
|
1301
|
+
if (context.directEvents === void 0) throw new Error("Git hub API requires the direct event bus.");
|
|
1302
|
+
const directEvents = context.directEvents;
|
|
1303
|
+
const publishLifecycle = (sessionId, repositoryRoot) => {
|
|
1304
|
+
directEvents.publish(require_worktreeOperations.GIT_WORKTREE_LIFECYCLE_EVENT, sessionId, {
|
|
1305
|
+
version: 1,
|
|
1306
|
+
repositoryRoot
|
|
1307
|
+
});
|
|
1308
|
+
};
|
|
1309
|
+
const git = require_worktreeOperations.createWorktreeGit();
|
|
1310
|
+
const operations = require_worktreeOperations.createWorktreeOperations({
|
|
1311
|
+
git,
|
|
1312
|
+
sessionService: context.sessionService,
|
|
1313
|
+
...context.homeDirectory === void 0 ? {} : { homeDir: context.homeDirectory }
|
|
1127
1314
|
});
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1315
|
+
/**
|
|
1316
|
+
* The new-session dialog's routes: the workspace's branches, and a top-level
|
|
1317
|
+
* worktree session on one. Workspace-mounted only, and always rooted at the
|
|
1318
|
+
* mount's own workspace, so a request can never name another directory.
|
|
1319
|
+
*/
|
|
1320
|
+
const newSession = async (request, url) => {
|
|
1321
|
+
const isBranches = request.method === "GET" && url.pathname === "/branches";
|
|
1322
|
+
const isCreate = request.method === "POST" && url.pathname === "/sessions";
|
|
1323
|
+
const isDelete = request.method === "DELETE" && url.pathname === "/branches";
|
|
1324
|
+
const isOpen = request.method === "POST" && url.pathname === `/branches/open`;
|
|
1325
|
+
if (!isBranches && !isCreate && !isDelete && !isOpen) return void 0;
|
|
1326
|
+
const workspaceRoot = context.scope === "workspace" ? context.workspaceRoot : void 0;
|
|
1327
|
+
if (workspaceRoot === void 0) return Response.json({ error: "This route belongs to a workspace." }, { status: 404 });
|
|
1328
|
+
if (isBranches) {
|
|
1329
|
+
const repositoryRoot = await git.repositoryRoot(workspaceRoot);
|
|
1330
|
+
const branches = repositoryRoot === void 0 ? {
|
|
1331
|
+
repository: false,
|
|
1332
|
+
local: [],
|
|
1333
|
+
remote: []
|
|
1334
|
+
} : {
|
|
1335
|
+
repository: true,
|
|
1336
|
+
...await git.listBranches(repositoryRoot)
|
|
1337
|
+
};
|
|
1338
|
+
return Response.json(branches);
|
|
1339
|
+
}
|
|
1340
|
+
if (isDelete || isOpen) {
|
|
1341
|
+
if (isOpen && (0, _agimon_ai_po_core_packageApi.poApiCallerFrom)(request.headers)?.locality !== "local") return Response.json({ error: "Opening Finder is only available on the host." }, { status: 403 });
|
|
1342
|
+
const body = await request.json().catch(() => void 0);
|
|
1343
|
+
const branch = typeof body === "object" && body !== null ? body.branch : void 0;
|
|
1344
|
+
if (typeof branch !== "string" || !require_worktreeOperations.validRefName(branch)) return badRequest("A valid branch name is required.");
|
|
1345
|
+
const repositoryRoot = await git.repositoryRoot(workspaceRoot);
|
|
1346
|
+
if (repositoryRoot === void 0) return badRequest("This workspace is not a git checkout.");
|
|
1347
|
+
const branches = await git.listBranches(repositoryRoot);
|
|
1348
|
+
const local = branches.local.find((item) => item.name === branch);
|
|
1349
|
+
if (local === void 0) return Response.json({ error: "That local branch no longer exists." }, { status: 404 });
|
|
1350
|
+
if (isOpen) {
|
|
1351
|
+
if (local.checkedOutAt === void 0) return Response.json({ error: "This branch has no checked-out folder." }, { status: 409 });
|
|
1352
|
+
if (openDirectory === void 0) return Response.json({ error: "Finder is not available on this host." }, { status: 409 });
|
|
1353
|
+
try {
|
|
1354
|
+
await openDirectory(local.checkedOutAt);
|
|
1355
|
+
return Response.json({ opened: true });
|
|
1356
|
+
} catch {
|
|
1357
|
+
return Response.json({ error: "The branch folder could not be opened in Finder." }, { status: 409 });
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
if (local.checkedOutAt !== void 0 || branches.current === branch) return Response.json({ error: "Close this branch’s worktree before deleting it." }, { status: 409 });
|
|
1361
|
+
if (!await git.deleteBranch({
|
|
1362
|
+
repositoryRoot,
|
|
1363
|
+
branch
|
|
1364
|
+
})) return Response.json({ error: "Git refused to delete this branch. It may contain unmerged commits or be checked out." }, { status: 409 });
|
|
1365
|
+
return Response.json({ deleted: true });
|
|
1366
|
+
}
|
|
1367
|
+
const parsed = newSessionRequest(await request.json().catch(() => void 0));
|
|
1368
|
+
if ("error" in parsed) return badRequest(parsed.error);
|
|
1369
|
+
const record = await operations.spawn({ cwd: workspaceRoot }, {
|
|
1370
|
+
branch: parsed.branch,
|
|
1371
|
+
checkout: parsed.mode === "existing-branch" ? "existing" : "new",
|
|
1372
|
+
...parsed.mode === "existing-branch" && parsed.remote !== void 0 ? { remote: parsed.remote } : {},
|
|
1373
|
+
...parsed.mode === "new-branch" && parsed.baseRef !== void 0 ? { baseRef: parsed.baseRef } : {},
|
|
1374
|
+
...parsed.name === void 0 ? {} : { name: parsed.name }
|
|
1375
|
+
});
|
|
1376
|
+
publishLifecycle(record.sessionId, record.repositoryRoot);
|
|
1377
|
+
const created = {
|
|
1378
|
+
sessionId: record.sessionId,
|
|
1379
|
+
worktreeId: record.id
|
|
1155
1380
|
};
|
|
1156
|
-
return Response.json(
|
|
1157
|
-
}
|
|
1158
|
-
const parsed = newSessionRequest(await request.json().catch(() => void 0));
|
|
1159
|
-
if ("error" in parsed) return badRequest(parsed.error);
|
|
1160
|
-
const record = await operations.spawn({ cwd: workspaceRoot }, {
|
|
1161
|
-
branch: parsed.branch,
|
|
1162
|
-
checkout: parsed.mode === "existing-branch" ? "existing" : "new",
|
|
1163
|
-
...parsed.mode === "existing-branch" && parsed.remote !== void 0 ? { remote: parsed.remote } : {},
|
|
1164
|
-
...parsed.mode === "new-branch" && parsed.baseRef !== void 0 ? { baseRef: parsed.baseRef } : {},
|
|
1165
|
-
...parsed.name === void 0 ? {} : { name: parsed.name }
|
|
1166
|
-
});
|
|
1167
|
-
publishLifecycle(record.sessionId, record.repositoryRoot);
|
|
1168
|
-
const created = {
|
|
1169
|
-
sessionId: record.sessionId,
|
|
1170
|
-
worktreeId: record.id
|
|
1381
|
+
return Response.json(created, { status: 201 });
|
|
1171
1382
|
};
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
if (request.method === "GET") return Response.json(gitAuth.view(root));
|
|
1198
|
-
try {
|
|
1199
|
-
return Response.json(gitAuth.save(root, await request.json().catch(() => void 0)));
|
|
1200
|
-
} catch (error) {
|
|
1201
|
-
if (error instanceof require_worktreeOperations.PoGitExpectedError && error.code === "invalid_request") {
|
|
1202
|
-
const text = error.message.replace(/^\[[a-z_]+\] /u, "").replace("\nRecovery: ", " ");
|
|
1203
|
-
return Response.json({
|
|
1204
|
-
error: text,
|
|
1205
|
-
code: error.code
|
|
1206
|
-
}, { status: 400 });
|
|
1207
|
-
}
|
|
1208
|
-
throw error;
|
|
1209
|
-
}
|
|
1210
|
-
};
|
|
1211
|
-
/**
|
|
1212
|
-
* The session's change against its base. Session mounts only, rooted at the
|
|
1213
|
-
* session's own checkout; a file is served only when it is in the change set
|
|
1214
|
-
* computed for the request. Overlapping requests share an in-flight snapshot.
|
|
1215
|
-
*/
|
|
1216
|
-
const review = async (request, url) => {
|
|
1217
|
-
if (url.pathname !== "/review" && url.pathname !== "/review/file") return void 0;
|
|
1218
|
-
const cwd = context.scope === "session" ? context.cwd : void 0;
|
|
1219
|
-
if (cwd === void 0) return Response.json({ error: "This route belongs to a session." }, { status: 404 });
|
|
1220
|
-
if (request.method !== "GET") return Response.json({ error: "Use GET." }, { status: 405 });
|
|
1221
|
-
let recordedBaseRef;
|
|
1222
|
-
try {
|
|
1223
|
-
recordedBaseRef = require_worktreeOperations.createWorktreeRegistry(require_worktreeOperations.registryFile(cwd, homeDir)).list().find((record) => record.sessionId === context.sessionId)?.baseRef;
|
|
1224
|
-
} catch {
|
|
1225
|
-
recordedBaseRef = void 0;
|
|
1226
|
-
}
|
|
1227
|
-
const key = JSON.stringify([cwd, recordedBaseRef ?? null]);
|
|
1228
|
-
let pending = pendingReviews.get(key);
|
|
1229
|
-
if (pending === void 0) {
|
|
1230
|
-
pending = branchDiff.review(cwd, recordedBaseRef === void 0 ? {} : { recordedBaseRef }).finally(() => pendingReviews.delete(key));
|
|
1231
|
-
pendingReviews.set(key, pending);
|
|
1232
|
-
}
|
|
1233
|
-
const result = await pending;
|
|
1234
|
-
if (url.pathname === "/review") return Response.json(result?.summary ?? {
|
|
1235
|
-
repository: false,
|
|
1236
|
-
files: []
|
|
1237
|
-
});
|
|
1238
|
-
const filePath = url.searchParams.get("path") ?? "";
|
|
1239
|
-
if (result === void 0 || !result.summary.files.some((file) => file.path === filePath)) return Response.json({ error: "That file is not part of this review." }, { status: 404 });
|
|
1240
|
-
return Response.json(await branchDiff.fileDiff(result, filePath));
|
|
1241
|
-
};
|
|
1242
|
-
return {
|
|
1243
|
-
async fetch(request) {
|
|
1244
|
-
const url = new URL(request.url);
|
|
1383
|
+
/** The admitted root for a request, or a response explaining why not. */
|
|
1384
|
+
const rootOf = (url) => {
|
|
1385
|
+
const repositoryId = url.searchParams.get("repositoryId");
|
|
1386
|
+
if (repositoryId === null || repositoryId === "") return badRequest("A repositoryId is required.");
|
|
1387
|
+
const root = context.resolveRepository?.(repositoryId);
|
|
1388
|
+
if (root === void 0) return badRequest("That repository is not admitted to this cockpit.");
|
|
1389
|
+
return root;
|
|
1390
|
+
};
|
|
1391
|
+
const homeDir = context.homeDirectory ?? context.environment?.HOME;
|
|
1392
|
+
const gitAuth = homeDir === void 0 ? void 0 : createGitAuthStore(homeDir);
|
|
1393
|
+
const branchDiff = createBranchDiff();
|
|
1394
|
+
const pendingReviews = /* @__PURE__ */ new Map();
|
|
1395
|
+
/**
|
|
1396
|
+
* A workspace's remote auth. Workspace mounts only, so the step-up gate on
|
|
1397
|
+
* `PUT /api/workspaces/<id>/plugins/git/auth` is the one way to write it.
|
|
1398
|
+
* The view never carries the token.
|
|
1399
|
+
*/
|
|
1400
|
+
const auth = async (request, url) => {
|
|
1401
|
+
if (url.pathname !== "/auth") return void 0;
|
|
1402
|
+
if (context.scope !== "workspace") return Response.json({ error: "This route belongs to a workspace." }, { status: 404 });
|
|
1403
|
+
if (request.method !== "GET" && request.method !== "PUT") return Response.json({ error: "Use GET or PUT." }, { status: 405 });
|
|
1404
|
+
if (gitAuth === void 0) throw new Error("Git auth requires the admitted host home directory.");
|
|
1405
|
+
const root = rootOf(url);
|
|
1406
|
+
if (root instanceof Response) return root;
|
|
1407
|
+
if (request.method === "GET") return Response.json(await gitAuth.view(root));
|
|
1245
1408
|
try {
|
|
1246
|
-
|
|
1247
|
-
if (answered !== void 0) return answered;
|
|
1409
|
+
return Response.json(await gitAuth.save(root, await request.json().catch(() => void 0)));
|
|
1248
1410
|
} catch (error) {
|
|
1249
|
-
|
|
1250
|
-
|
|
1411
|
+
if (error instanceof require_worktreeOperations.PoGitExpectedError && error.code === "invalid_request") {
|
|
1412
|
+
const text = error.message.replace(/^\[[a-z_]+\] /u, "").replace("\nRecovery: ", " ");
|
|
1413
|
+
return Response.json({
|
|
1414
|
+
error: text,
|
|
1415
|
+
code: error.code
|
|
1416
|
+
}, { status: 400 });
|
|
1417
|
+
}
|
|
1418
|
+
throw error;
|
|
1251
1419
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1420
|
+
};
|
|
1421
|
+
/**
|
|
1422
|
+
* The session's change against its base. Session mounts only, rooted at the
|
|
1423
|
+
* session's own checkout; a file is served only when it is in the change set
|
|
1424
|
+
* computed for the request. Overlapping requests share an in-flight snapshot.
|
|
1425
|
+
*/
|
|
1426
|
+
const review = async (request, url) => {
|
|
1427
|
+
if (url.pathname !== "/review" && url.pathname !== "/review/file") return void 0;
|
|
1428
|
+
const cwd = context.scope === "session" ? context.cwd : void 0;
|
|
1429
|
+
if (cwd === void 0) return Response.json({ error: "This route belongs to a session." }, { status: 404 });
|
|
1430
|
+
if (request.method !== "GET") return Response.json({ error: "Use GET." }, { status: 405 });
|
|
1431
|
+
if (homeDir === void 0) throw new Error("Git review requires the admitted host home directory.");
|
|
1432
|
+
let recordedBaseRef;
|
|
1259
1433
|
try {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1434
|
+
recordedBaseRef = (await require_worktreeOperations.createWorktreeRegistry(await require_worktreeOperations.registryFile(cwd, homeDir)).list()).find((record) => record.sessionId === context.sessionId)?.baseRef;
|
|
1435
|
+
} catch {
|
|
1436
|
+
recordedBaseRef = void 0;
|
|
1437
|
+
}
|
|
1438
|
+
const key = JSON.stringify([cwd, recordedBaseRef ?? null]);
|
|
1439
|
+
let pending = pendingReviews.get(key);
|
|
1440
|
+
if (pending === void 0) {
|
|
1441
|
+
pending = branchDiff.review(cwd, recordedBaseRef === void 0 ? {} : { recordedBaseRef }).finally(() => pendingReviews.delete(key));
|
|
1442
|
+
pendingReviews.set(key, pending);
|
|
1443
|
+
}
|
|
1444
|
+
const result = await pending;
|
|
1445
|
+
if (url.pathname === "/review") return Response.json(result?.summary ?? {
|
|
1446
|
+
repository: false,
|
|
1447
|
+
files: []
|
|
1448
|
+
});
|
|
1449
|
+
const filePath = url.searchParams.get("path") ?? "";
|
|
1450
|
+
if (result === void 0 || !result.summary.files.some((file) => file.path === filePath)) return Response.json({ error: "That file is not part of this review." }, { status: 404 });
|
|
1451
|
+
return Response.json(await branchDiff.fileDiff(result, filePath));
|
|
1452
|
+
};
|
|
1453
|
+
return {
|
|
1454
|
+
async fetch(request) {
|
|
1455
|
+
const url = new URL(request.url);
|
|
1456
|
+
try {
|
|
1457
|
+
const answered = await newSession(request, url) ?? await auth(request, url) ?? await review(request, url);
|
|
1458
|
+
if (answered !== void 0) return answered;
|
|
1459
|
+
} catch (error) {
|
|
1460
|
+
context.onNotice(`git request failed: ${error.name}`);
|
|
1461
|
+
return failed(error);
|
|
1271
1462
|
}
|
|
1272
|
-
const
|
|
1273
|
-
if (
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1463
|
+
const root = rootOf(url);
|
|
1464
|
+
if (root instanceof Response) return root;
|
|
1465
|
+
const callerContext = {
|
|
1466
|
+
cwd: root,
|
|
1467
|
+
sessionId: url.searchParams.get("sessionId") ?? ""
|
|
1468
|
+
};
|
|
1469
|
+
if ((request.method === "POST" && url.pathname === "/worktrees" || request.method === "DELETE" && /^\/worktrees\/[^/]+$/u.test(url.pathname)) && callerContext.sessionId === "") return badRequest("A sessionId is required for worktree mutations.");
|
|
1470
|
+
try {
|
|
1471
|
+
if (request.method === "GET" && url.pathname === "/worktrees") return Response.json({ worktrees: await operations.list(callerContext) });
|
|
1472
|
+
if (request.method === "POST" && url.pathname === "/worktrees") {
|
|
1473
|
+
const body = await request.json();
|
|
1474
|
+
if (typeof body.branch !== "string" || body.branch.trim() === "") return badRequest("A branch is required.");
|
|
1475
|
+
const record = await operations.spawn(callerContext, {
|
|
1476
|
+
branch: body.branch,
|
|
1477
|
+
...body.baseRef === void 0 ? {} : { baseRef: body.baseRef },
|
|
1478
|
+
...body.name === void 0 ? {} : { name: body.name }
|
|
1479
|
+
});
|
|
1480
|
+
publishLifecycle(callerContext.sessionId, record.repositoryRoot);
|
|
1481
|
+
return Response.json({ worktree: record }, { status: 201 });
|
|
1482
|
+
}
|
|
1483
|
+
const closing = /^\/worktrees\/([^/]+)$/u.exec(url.pathname);
|
|
1484
|
+
if (request.method === "DELETE" && closing) {
|
|
1485
|
+
const record = await operations.close(callerContext, decodeURIComponent(String(closing[1])), url.searchParams.get("force") === "true");
|
|
1486
|
+
publishLifecycle(callerContext.sessionId, record.repositoryRoot);
|
|
1487
|
+
return Response.json({ worktree: record });
|
|
1488
|
+
}
|
|
1489
|
+
return Response.json({ error: "No such worktree route." }, { status: 404 });
|
|
1490
|
+
} catch (error) {
|
|
1491
|
+
context.onNotice(`worktree request failed: ${error.name}`);
|
|
1492
|
+
return failed(error);
|
|
1277
1493
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1494
|
+
},
|
|
1495
|
+
close() {}
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
//#endregion
|
|
1501
|
+
//#region src/extensions/(backend)/api/git/route.server.ts
|
|
1502
|
+
var route_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoute)(({ context, host, signal }) => {
|
|
1503
|
+
let openDirectory;
|
|
1504
|
+
context.inject([_agimon_ai_po_core_platform.PO_SHELL_SERVICE], (child) => {
|
|
1505
|
+
const shell = (0, _agimon_ai_po_core_platform.requirePoShell)(child);
|
|
1506
|
+
const env = Object.fromEntries(Object.entries(host.context.environment ?? {}).filter((entry) => entry[1] !== void 0));
|
|
1507
|
+
openDirectory = async (path) => {
|
|
1508
|
+
const result = await shell.exec([
|
|
1509
|
+
"/usr/bin/open",
|
|
1510
|
+
"-a",
|
|
1511
|
+
"Finder",
|
|
1512
|
+
path
|
|
1513
|
+
], {
|
|
1514
|
+
env,
|
|
1515
|
+
inheritEnv: false
|
|
1516
|
+
}, (0, _agimon_ai_po_core_platform.withAbortSignal)(AbortSignal.any([signal, AbortSignal.timeout(5e3)]), _agimon_ai_po_core_platform.BACKGROUND_CONTEXT));
|
|
1517
|
+
if (!result.ok || result.value.exitCode !== 0) throw new Error("Finder could not be opened.");
|
|
1285
1518
|
};
|
|
1286
|
-
|
|
1519
|
+
return () => {
|
|
1520
|
+
openDirectory = void 0;
|
|
1521
|
+
};
|
|
1522
|
+
});
|
|
1523
|
+
return createGitApi(async (path) => {
|
|
1524
|
+
if (openDirectory === void 0) throw new Error("The host shell is unavailable.");
|
|
1525
|
+
await openDirectory(path);
|
|
1526
|
+
});
|
|
1287
1527
|
});
|
|
1288
1528
|
//#endregion
|
|
1289
1529
|
//#region src/services/gitFailure/index.ts
|
|
@@ -1420,7 +1660,7 @@ async function rebasing(root) {
|
|
|
1420
1660
|
"--git-path",
|
|
1421
1661
|
name
|
|
1422
1662
|
]);
|
|
1423
|
-
if (gitPath !== void 0 &&
|
|
1663
|
+
if (gitPath !== void 0 && await node_fs_promises.default.access(node_path.default.resolve(root, gitPath)).then(() => true, () => false)) return true;
|
|
1424
1664
|
}
|
|
1425
1665
|
return false;
|
|
1426
1666
|
}
|
|
@@ -1590,9 +1830,9 @@ const GIT_CHANGES_TYPE = "git_changes";
|
|
|
1590
1830
|
* DESIGN PATTERNS:
|
|
1591
1831
|
* - Apart from the worktrees channel. Worktrees are repository management;
|
|
1592
1832
|
* this is the session's own code change.
|
|
1593
|
-
* - Push, not poll. The session
|
|
1594
|
-
* tool call and settled turn; recounts are coalesced to one in
|
|
1595
|
-
* one rerun, so a burst of tool calls costs at most two.
|
|
1833
|
+
* - Push, not poll. The session's server plugin publishes a "changed" event
|
|
1834
|
+
* after every tool call and settled turn; recounts are coalesced to one in
|
|
1835
|
+
* flight plus one rerun, so a burst of tool calls costs at most two.
|
|
1596
1836
|
* - One sync command per session. A rebase and a push must never overlap.
|
|
1597
1837
|
* - Auth is read fresh for each command from the session's workspace, and goes
|
|
1598
1838
|
* only into that command's git child.
|
|
@@ -1653,9 +1893,9 @@ function createGitChangesChannel(options = {}) {
|
|
|
1653
1893
|
const rerun = /* @__PURE__ */ new Set();
|
|
1654
1894
|
const busy = /* @__PURE__ */ new Set();
|
|
1655
1895
|
/** The base the session's own worktree was created from, when it has one. */
|
|
1656
|
-
const recordedBaseRef = (scope) => {
|
|
1896
|
+
const recordedBaseRef = async (scope) => {
|
|
1657
1897
|
try {
|
|
1658
|
-
return require_worktreeOperations.createWorktreeRegistry(require_worktreeOperations.registryFile(scope.cwd, homeDir)).list().find((record) => record.sessionId === scope.sessionId)?.baseRef;
|
|
1898
|
+
return (await require_worktreeOperations.createWorktreeRegistry(await require_worktreeOperations.registryFile(scope.cwd, homeDir)).list()).find((record) => record.sessionId === scope.sessionId)?.baseRef;
|
|
1659
1899
|
} catch {
|
|
1660
1900
|
return;
|
|
1661
1901
|
}
|
|
@@ -1688,8 +1928,7 @@ function createGitChangesChannel(options = {}) {
|
|
|
1688
1928
|
return;
|
|
1689
1929
|
}
|
|
1690
1930
|
inFlight.add(sessionId);
|
|
1691
|
-
|
|
1692
|
-
diff.review(scope.cwd, base === void 0 ? {} : { recordedBaseRef: base }).then((review) => {
|
|
1931
|
+
recordedBaseRef(scope).then((base) => diff.review(scope.cwd, base === void 0 ? {} : { recordedBaseRef: base })).then((review) => {
|
|
1693
1932
|
if (scopes.has(sessionId)) update(sessionId, { changes: review?.changes });
|
|
1694
1933
|
}).catch((error) => {
|
|
1695
1934
|
host.onNotice(`git diff refresh failed for ${sessionId} (${error instanceof Error ? error.name : "unknown"})`);
|
|
@@ -1698,12 +1937,12 @@ function createGitChangesChannel(options = {}) {
|
|
|
1698
1937
|
if (rerun.delete(sessionId)) refresh(sessionId);
|
|
1699
1938
|
});
|
|
1700
1939
|
};
|
|
1701
|
-
const run = (scope, command) => {
|
|
1940
|
+
const run = async (scope, command) => {
|
|
1702
1941
|
const workspaceRoot = scope.sessionContext?.workspaceRoot ?? scope.cwd;
|
|
1703
|
-
const base = recordedBaseRef(scope);
|
|
1942
|
+
const base = await recordedBaseRef(scope);
|
|
1704
1943
|
const context = {
|
|
1705
1944
|
cwd: scope.cwd,
|
|
1706
|
-
auth: auth.read(workspaceRoot),
|
|
1945
|
+
auth: await auth.read(workspaceRoot),
|
|
1707
1946
|
...base === void 0 ? {} : { recordedBaseRef: base }
|
|
1708
1947
|
};
|
|
1709
1948
|
if (command.action === "pull") return sync.pull(context);
|
|
@@ -1801,9 +2040,9 @@ function payloadOf(state) {
|
|
|
1801
2040
|
};
|
|
1802
2041
|
}
|
|
1803
2042
|
/** Worktrees visible to this session, using the hub's live session table for ownership. */
|
|
1804
|
-
function viewsFor(scope, deps) {
|
|
2043
|
+
async function viewsFor(scope, deps) {
|
|
1805
2044
|
try {
|
|
1806
|
-
return require_worktreeOperations.createWorktreeRegistry(require_worktreeOperations.registryFile(scope.cwd, deps.homeDir)).list().flatMap((record) => {
|
|
2045
|
+
return (await require_worktreeOperations.createWorktreeRegistry(await require_worktreeOperations.registryFile(scope.cwd, deps.homeDir)).list()).flatMap((record) => {
|
|
1807
2046
|
if (record.parentSessionId === void 0 && record.sessionId !== scope.sessionId) return [];
|
|
1808
2047
|
const owned = record.parentSessionId === void 0 || record.parentSessionId === scope.sessionId;
|
|
1809
2048
|
const unowned = !owned && !deps.sessionService.isLive(record.parentSessionId);
|
|
@@ -1828,11 +2067,15 @@ function sameList(left, right) {
|
|
|
1828
2067
|
return other !== void 0 && entry.id === other.id && entry.branch === other.branch && entry.sessionId === other.sessionId && entry.orphaned === other.orphaned && entry.unowned === other.unowned;
|
|
1829
2068
|
});
|
|
1830
2069
|
}
|
|
1831
|
-
function errorTargetFor(command) {
|
|
1832
|
-
|
|
2070
|
+
function errorTargetFor(command, error) {
|
|
2071
|
+
if (command.action === "close") return {
|
|
1833
2072
|
action: "close",
|
|
1834
2073
|
id: command.id
|
|
1835
2074
|
};
|
|
2075
|
+
return error instanceof require_worktreeOperations.PoGitExpectedError && error.code === "rift_missing" ? {
|
|
2076
|
+
action: "create",
|
|
2077
|
+
riftMissing: true
|
|
2078
|
+
} : { action: "create" };
|
|
1836
2079
|
}
|
|
1837
2080
|
/** A command the page sent, or undefined when the frame was not one. */
|
|
1838
2081
|
function parseCommand(payload) {
|
|
@@ -1860,11 +2103,15 @@ function parseCommand(payload) {
|
|
|
1860
2103
|
/**
|
|
1861
2104
|
* What the reader is told when a command fails.
|
|
1862
2105
|
*
|
|
1863
|
-
* An expected failure explains itself
|
|
1864
|
-
*
|
|
2106
|
+
* An expected failure explains itself, minus the `[code]` tag and recovery line
|
|
2107
|
+
* written for the agent's tool result. A dirty close is said in the person's
|
|
2108
|
+
* terms, because its message tells the agent to pass `force`. Anything else is
|
|
2109
|
+
* summarised, never echoed: a git error can carry a remote URL with a token in it.
|
|
1865
2110
|
*/
|
|
1866
2111
|
function failureText(error) {
|
|
1867
|
-
|
|
2112
|
+
if (!(error instanceof require_worktreeOperations.PoGitExpectedError)) return "The worktree operation failed.";
|
|
2113
|
+
if (error.code === "worktree_dirty") return "The worktree has uncommitted changes.";
|
|
2114
|
+
return error.message.replace(/^\[[a-z_]+\] /u, "").split("\nRecovery:")[0] ?? error.message;
|
|
1868
2115
|
}
|
|
1869
2116
|
/** Starts one direct-event subscription per managed session. */
|
|
1870
2117
|
function createWorktreesChannel(options = {}) {
|
|
@@ -1917,10 +2164,13 @@ function createWorktreesChannel(options = {}) {
|
|
|
1917
2164
|
return generation;
|
|
1918
2165
|
};
|
|
1919
2166
|
/** Republishes a session, skipping a frame that would say nothing new. */
|
|
1920
|
-
const publish = (scope, force) => {
|
|
2167
|
+
const publish = async (scope, force) => {
|
|
2168
|
+
const generation = generations.get(scope.sessionId);
|
|
2169
|
+
const worktreeViews = await viewsFor(scope, views);
|
|
2170
|
+
if (generation === void 0 || generations.get(scope.sessionId) !== generation) return;
|
|
1921
2171
|
const previous = latest.get(scope.sessionId);
|
|
1922
2172
|
const next = {
|
|
1923
|
-
worktrees:
|
|
2173
|
+
worktrees: worktreeViews,
|
|
1924
2174
|
pending: previous?.pending,
|
|
1925
2175
|
error: previous?.error,
|
|
1926
2176
|
errorTarget: previous?.errorTarget
|
|
@@ -1932,7 +2182,7 @@ function createWorktreesChannel(options = {}) {
|
|
|
1932
2182
|
const mark = (scope, pending, error, errorTarget) => {
|
|
1933
2183
|
const previous = latest.get(scope.sessionId);
|
|
1934
2184
|
latest.set(scope.sessionId, {
|
|
1935
|
-
worktrees: previous?.worktrees ??
|
|
2185
|
+
worktrees: previous?.worktrees ?? [],
|
|
1936
2186
|
pending,
|
|
1937
2187
|
error,
|
|
1938
2188
|
errorTarget
|
|
@@ -1972,15 +2222,15 @@ function createWorktreesChannel(options = {}) {
|
|
|
1972
2222
|
sessionAdded(scope) {
|
|
1973
2223
|
newGeneration(scope.sessionId);
|
|
1974
2224
|
publish(scope, false);
|
|
1975
|
-
const unsubscribe = host.directEvents.subscribe(require_worktreeOperations.GIT_WORKTREE_LIFECYCLE_EVENT, scope.sessionId, (payload) => {
|
|
2225
|
+
const unsubscribe = host.directEvents.subscribe(require_worktreeOperations.GIT_WORKTREE_LIFECYCLE_EVENT, scope.sessionId, async (payload) => {
|
|
1976
2226
|
if (!require_worktreeOperations.isWorktreeLifecycleEvent(payload)) return;
|
|
1977
2227
|
let sameRepository = false;
|
|
1978
2228
|
try {
|
|
1979
|
-
sameRepository = require_worktreeOperations.registryFile(scope.cwd, options.homeDir) === require_worktreeOperations.registryFile(payload.repositoryRoot, options.homeDir);
|
|
2229
|
+
sameRepository = await require_worktreeOperations.registryFile(scope.cwd, options.homeDir) === await require_worktreeOperations.registryFile(payload.repositoryRoot, options.homeDir);
|
|
1980
2230
|
} catch {
|
|
1981
2231
|
sameRepository = false;
|
|
1982
2232
|
}
|
|
1983
|
-
if (sameRepository) publish(scope, false);
|
|
2233
|
+
if (sameRepository) await publish(scope, false);
|
|
1984
2234
|
});
|
|
1985
2235
|
subscriptions.set(scope.sessionId, unsubscribe);
|
|
1986
2236
|
},
|
|
@@ -2013,7 +2263,7 @@ function createWorktreesChannel(options = {}) {
|
|
|
2013
2263
|
}).catch((error) => {
|
|
2014
2264
|
if (generations.get(scope.sessionId) !== generation) return;
|
|
2015
2265
|
host.onNotice(`worktree command failed for ${scope.sessionId} (${failureText(error)})`);
|
|
2016
|
-
mark(scope, void 0, failureText(error), errorTargetFor(command));
|
|
2266
|
+
mark(scope, void 0, failureText(error), errorTargetFor(command, error));
|
|
2017
2267
|
}).finally(() => {
|
|
2018
2268
|
if (generations.get(scope.sessionId) === generation) busy.delete(scope.sessionId);
|
|
2019
2269
|
});
|
|
@@ -2035,20 +2285,8 @@ var agent_settled_server_default = (0, _agimon_ai_po_core_extensionFile.defineHo
|
|
|
2035
2285
|
//#region src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts
|
|
2036
2286
|
var tool_execution_end_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)((context) => context.root.hooks[0]);
|
|
2037
2287
|
//#endregion
|
|
2038
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poGit.server.ts
|
|
2039
|
-
var poGit_server_default$1 = (context) => context.root.resources[0];
|
|
2040
|
-
//#endregion
|
|
2041
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/poGit.server.ts
|
|
2042
|
-
var poGit_server_default = (0, _agimon_ai_po_core_extensionFile.defineResource)(poGit_server_default$1);
|
|
2043
|
-
//#endregion
|
|
2044
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poGitReadme.server.ts
|
|
2045
|
-
var poGitReadme_server_default$1 = (context) => context.root.resources[2];
|
|
2046
|
-
//#endregion
|
|
2047
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/poGitReadme.server.ts
|
|
2048
|
-
var poGitReadme_server_default = (0, _agimon_ai_po_core_extensionFile.defineResource)(poGitReadme_server_default$1);
|
|
2049
|
-
//#endregion
|
|
2050
2288
|
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poUseGit.server.ts
|
|
2051
|
-
var poUseGit_server_default$1 = (context) => context.root.resources[
|
|
2289
|
+
var poUseGit_server_default$1 = (context) => context.root.resources[0];
|
|
2052
2290
|
//#endregion
|
|
2053
2291
|
//#region src/extensions/workspaces/sessions/(backend)/resource/poUseGit.server.ts
|
|
2054
2292
|
var poUseGit_server_default = (0, _agimon_ai_po_core_extensionFile.defineResource)(poUseGit_server_default$1);
|
|
@@ -2058,19 +2296,22 @@ var runWorktree_server_default = (0, _agimon_ai_po_core_extensionFile.defineServ
|
|
|
2058
2296
|
//#endregion
|
|
2059
2297
|
//#region generated/server.ts
|
|
2060
2298
|
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
2299
|
+
/* v8 ignore next -- generated normalization branch */
|
|
2300
|
+
const defined = (values) => values.filter((value) => value !== void 0);
|
|
2061
2301
|
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
2062
2302
|
...identity,
|
|
2063
2303
|
...value
|
|
2064
2304
|
};
|
|
2065
|
-
const
|
|
2305
|
+
const serverPlugin = (0, _agimon_ai_po_core_serverPlugin.defineServerPlugin)({
|
|
2066
2306
|
name: "@agimon-ai/po-git",
|
|
2067
|
-
providerOverrides: (0,
|
|
2307
|
+
providerOverrides: (0, _agimon_ai_po_core_serverPlugin.composeServerProviderOverrides)(root_server_default),
|
|
2308
|
+
injects: (0, _agimon_ai_po_core_extensionFile.composeRootInjects)({ session: [root_server_default] }),
|
|
2068
2309
|
global: (context) => ({
|
|
2069
2310
|
get api() {
|
|
2070
2311
|
return [at(route_server_default, context)];
|
|
2071
2312
|
},
|
|
2072
2313
|
get channels() {
|
|
2073
|
-
return [() => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2314
|
+
return [() => via({ frameType: "git_changes" }, at(git_changes_server_default, context)), () => via({ frameType: "git_worktrees" }, at(git_worktrees_server_default, context))];
|
|
2074
2315
|
}
|
|
2075
2316
|
}),
|
|
2076
2317
|
workspace: (context) => ({
|
|
@@ -2078,11 +2319,11 @@ const facet = (0, _agimon_ai_po_core_serverFacet.defineServerPlugin)({
|
|
|
2078
2319
|
return [at(route_server_default, context)];
|
|
2079
2320
|
},
|
|
2080
2321
|
get channels() {
|
|
2081
|
-
return [() => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2322
|
+
return defined([context.hosted("global") ? void 0 : () => via({ frameType: "git_changes" }, at(git_changes_server_default, context)), context.hosted("global") ? void 0 : () => via({ frameType: "git_worktrees" }, at(git_worktrees_server_default, context))]);
|
|
2082
2323
|
}
|
|
2083
2324
|
}),
|
|
2084
2325
|
session: async (context) => {
|
|
2085
|
-
const scopeSession = await root_server_default
|
|
2326
|
+
const scopeSession = await context.scopeRoot("session", root_server_default, context);
|
|
2086
2327
|
const contextSession = {
|
|
2087
2328
|
...context,
|
|
2088
2329
|
root: scopeSession.value
|
|
@@ -2093,17 +2334,13 @@ const facet = (0, _agimon_ai_po_core_serverFacet.defineServerPlugin)({
|
|
|
2093
2334
|
return [at(route_server_default, context)];
|
|
2094
2335
|
},
|
|
2095
2336
|
get channels() {
|
|
2096
|
-
return [() => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2337
|
+
return defined([context.hosted("global") ? void 0 : () => via({ frameType: "git_changes" }, at(git_changes_server_default, context)), context.hosted("global") ? void 0 : () => via({ frameType: "git_worktrees" }, at(git_worktrees_server_default, context))]);
|
|
2097
2338
|
},
|
|
2098
2339
|
get hooks() {
|
|
2099
2340
|
return [via({ event: "agent_settled" }, at(agent_settled_server_default, contextSession)), via({ event: "tool_execution_end" }, at(tool_execution_end_server_default, contextSession))];
|
|
2100
2341
|
},
|
|
2101
2342
|
get resources() {
|
|
2102
|
-
return [
|
|
2103
|
-
at(poGit_server_default, contextSession),
|
|
2104
|
-
at(poGitReadme_server_default, contextSession),
|
|
2105
|
-
at(poUseGit_server_default, contextSession)
|
|
2106
|
-
];
|
|
2343
|
+
return [at(poUseGit_server_default, contextSession)];
|
|
2107
2344
|
},
|
|
2108
2345
|
get tools() {
|
|
2109
2346
|
return [via({ name: "run_worktree" }, at(runWorktree_server_default, contextSession))];
|
|
@@ -2116,7 +2353,7 @@ const facet = (0, _agimon_ai_po_core_serverFacet.defineServerPlugin)({
|
|
|
2116
2353
|
}
|
|
2117
2354
|
});
|
|
2118
2355
|
//#endregion
|
|
2119
|
-
exports.default =
|
|
2120
|
-
exports.
|
|
2356
|
+
exports.default = serverPlugin;
|
|
2357
|
+
exports.serverPlugin = serverPlugin;
|
|
2121
2358
|
|
|
2122
2359
|
//# sourceMappingURL=server.cjs.map
|