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