@agimon-ai/po-git 0.0.0-alpha.78 → 0.0.0-alpha.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apiContracts.cjs +30 -0
- package/dist/apiContracts.cjs.map +1 -1
- package/dist/apiContracts.mjs +30 -0
- package/dist/apiContracts.mjs.map +1 -1
- package/dist/extensions/native.mjs +10 -0
- package/dist/extensions/native.mjs.map +1 -1
- package/dist/extensions/server.cjs +222 -161
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.mjs +222 -161
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +134 -30
- package/dist/extensions/web.mjs.map +1 -1
- package/package.json +8 -8
|
@@ -7,6 +7,8 @@ import { composeRootHooks, composeRootInjects, defineChannel, defineHook, define
|
|
|
7
7
|
import { PO_CODEMODE_SERVICE, readPoCodemode } from "@agimon-ai/po-core/codemode";
|
|
8
8
|
import { readPoSessionDelivery } from "@agimon-ai/po-session";
|
|
9
9
|
import { Type } from "typebox";
|
|
10
|
+
import { BACKGROUND_CONTEXT, PO_SHELL_SERVICE, requirePoShell, withAbortSignal } from "@agimon-ai/po-core/platform";
|
|
11
|
+
import { poApiCallerFrom } from "@agimon-ai/po-core/packageApi";
|
|
10
12
|
//#region src/schemas/runWorktreeTool.ts
|
|
11
13
|
/**
|
|
12
14
|
* The tool's action vocabulary.
|
|
@@ -1285,177 +1287,236 @@ function failed(error) {
|
|
|
1285
1287
|
}, { status: error.retryable ? 503 : 409 });
|
|
1286
1288
|
return Response.json({ error: "The worktree operation failed." }, { status: 500 });
|
|
1287
1289
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1290
|
+
function createGitApi(openDirectory) {
|
|
1291
|
+
return {
|
|
1292
|
+
basePath: "git",
|
|
1293
|
+
start(context) {
|
|
1294
|
+
if (context.directEvents === void 0) throw new Error("Git hub API requires the direct event bus.");
|
|
1295
|
+
const directEvents = context.directEvents;
|
|
1296
|
+
const publishLifecycle = (sessionId, repositoryRoot) => {
|
|
1297
|
+
directEvents.publish(GIT_WORKTREE_LIFECYCLE_EVENT, sessionId, {
|
|
1298
|
+
version: 1,
|
|
1299
|
+
repositoryRoot
|
|
1300
|
+
});
|
|
1301
|
+
};
|
|
1302
|
+
const git = createWorktreeGit();
|
|
1303
|
+
const operations = createWorktreeOperations({
|
|
1304
|
+
git,
|
|
1305
|
+
sessionService: context.sessionService,
|
|
1306
|
+
...context.homeDirectory === void 0 ? {} : { homeDir: context.homeDirectory }
|
|
1299
1307
|
});
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1308
|
+
/**
|
|
1309
|
+
* The new-session dialog's routes: the workspace's branches, and a top-level
|
|
1310
|
+
* worktree session on one. Workspace-mounted only, and always rooted at the
|
|
1311
|
+
* mount's own workspace, so a request can never name another directory.
|
|
1312
|
+
*/
|
|
1313
|
+
const newSession = async (request, url) => {
|
|
1314
|
+
const isBranches = request.method === "GET" && url.pathname === "/branches";
|
|
1315
|
+
const isCreate = request.method === "POST" && url.pathname === "/sessions";
|
|
1316
|
+
const isDelete = request.method === "DELETE" && url.pathname === "/branches";
|
|
1317
|
+
const isOpen = request.method === "POST" && url.pathname === `/branches/open`;
|
|
1318
|
+
if (!isBranches && !isCreate && !isDelete && !isOpen) return void 0;
|
|
1319
|
+
const workspaceRoot = context.scope === "workspace" ? context.workspaceRoot : void 0;
|
|
1320
|
+
if (workspaceRoot === void 0) return Response.json({ error: "This route belongs to a workspace." }, { status: 404 });
|
|
1321
|
+
if (isBranches) {
|
|
1322
|
+
const repositoryRoot = await git.repositoryRoot(workspaceRoot);
|
|
1323
|
+
const branches = repositoryRoot === void 0 ? {
|
|
1324
|
+
repository: false,
|
|
1325
|
+
local: [],
|
|
1326
|
+
remote: []
|
|
1327
|
+
} : {
|
|
1328
|
+
repository: true,
|
|
1329
|
+
...await git.listBranches(repositoryRoot)
|
|
1330
|
+
};
|
|
1331
|
+
return Response.json(branches);
|
|
1332
|
+
}
|
|
1333
|
+
if (isDelete || isOpen) {
|
|
1334
|
+
if (isOpen && poApiCallerFrom(request.headers)?.locality !== "local") return Response.json({ error: "Opening Finder is only available on the host." }, { status: 403 });
|
|
1335
|
+
const body = await request.json().catch(() => void 0);
|
|
1336
|
+
const branch = typeof body === "object" && body !== null ? body.branch : void 0;
|
|
1337
|
+
if (typeof branch !== "string" || !validRefName(branch)) return badRequest("A valid branch name is required.");
|
|
1338
|
+
const repositoryRoot = await git.repositoryRoot(workspaceRoot);
|
|
1339
|
+
if (repositoryRoot === void 0) return badRequest("This workspace is not a git checkout.");
|
|
1340
|
+
const branches = await git.listBranches(repositoryRoot);
|
|
1341
|
+
const local = branches.local.find((item) => item.name === branch);
|
|
1342
|
+
if (local === void 0) return Response.json({ error: "That local branch no longer exists." }, { status: 404 });
|
|
1343
|
+
if (isOpen) {
|
|
1344
|
+
if (local.checkedOutAt === void 0) return Response.json({ error: "This branch has no checked-out folder." }, { status: 409 });
|
|
1345
|
+
if (openDirectory === void 0) return Response.json({ error: "Finder is not available on this host." }, { status: 409 });
|
|
1346
|
+
try {
|
|
1347
|
+
await openDirectory(local.checkedOutAt);
|
|
1348
|
+
return Response.json({ opened: true });
|
|
1349
|
+
} catch {
|
|
1350
|
+
return Response.json({ error: "The branch folder could not be opened in Finder." }, { status: 409 });
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
if (local.checkedOutAt !== void 0 || branches.current === branch) return Response.json({ error: "Close this branch’s worktree before deleting it." }, { status: 409 });
|
|
1354
|
+
if (!await git.deleteBranch({
|
|
1355
|
+
repositoryRoot,
|
|
1356
|
+
branch
|
|
1357
|
+
})) return Response.json({ error: "Git refused to delete this branch. It may contain unmerged commits or be checked out." }, { status: 409 });
|
|
1358
|
+
return Response.json({ deleted: true });
|
|
1359
|
+
}
|
|
1360
|
+
const parsed = newSessionRequest(await request.json().catch(() => void 0));
|
|
1361
|
+
if ("error" in parsed) return badRequest(parsed.error);
|
|
1362
|
+
const record = await operations.spawn({ cwd: workspaceRoot }, {
|
|
1363
|
+
branch: parsed.branch,
|
|
1364
|
+
checkout: parsed.mode === "existing-branch" ? "existing" : "new",
|
|
1365
|
+
...parsed.mode === "existing-branch" && parsed.remote !== void 0 ? { remote: parsed.remote } : {},
|
|
1366
|
+
...parsed.mode === "new-branch" && parsed.baseRef !== void 0 ? { baseRef: parsed.baseRef } : {},
|
|
1367
|
+
...parsed.name === void 0 ? {} : { name: parsed.name }
|
|
1368
|
+
});
|
|
1369
|
+
publishLifecycle(record.sessionId, record.repositoryRoot);
|
|
1370
|
+
const created = {
|
|
1371
|
+
sessionId: record.sessionId,
|
|
1372
|
+
worktreeId: record.id
|
|
1327
1373
|
};
|
|
1328
|
-
return Response.json(
|
|
1329
|
-
}
|
|
1330
|
-
const parsed = newSessionRequest(await request.json().catch(() => void 0));
|
|
1331
|
-
if ("error" in parsed) return badRequest(parsed.error);
|
|
1332
|
-
const record = await operations.spawn({ cwd: workspaceRoot }, {
|
|
1333
|
-
branch: parsed.branch,
|
|
1334
|
-
checkout: parsed.mode === "existing-branch" ? "existing" : "new",
|
|
1335
|
-
...parsed.mode === "existing-branch" && parsed.remote !== void 0 ? { remote: parsed.remote } : {},
|
|
1336
|
-
...parsed.mode === "new-branch" && parsed.baseRef !== void 0 ? { baseRef: parsed.baseRef } : {},
|
|
1337
|
-
...parsed.name === void 0 ? {} : { name: parsed.name }
|
|
1338
|
-
});
|
|
1339
|
-
publishLifecycle(record.sessionId, record.repositoryRoot);
|
|
1340
|
-
const created = {
|
|
1341
|
-
sessionId: record.sessionId,
|
|
1342
|
-
worktreeId: record.id
|
|
1374
|
+
return Response.json(created, { status: 201 });
|
|
1343
1375
|
};
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
if (request.method === "GET") return Response.json(await gitAuth.view(root));
|
|
1370
|
-
try {
|
|
1371
|
-
return Response.json(await gitAuth.save(root, await request.json().catch(() => void 0)));
|
|
1372
|
-
} catch (error) {
|
|
1373
|
-
if (error instanceof PoGitExpectedError && error.code === "invalid_request") {
|
|
1374
|
-
const text = error.message.replace(/^\[[a-z_]+\] /u, "").replace("\nRecovery: ", " ");
|
|
1375
|
-
return Response.json({
|
|
1376
|
-
error: text,
|
|
1377
|
-
code: error.code
|
|
1378
|
-
}, { status: 400 });
|
|
1379
|
-
}
|
|
1380
|
-
throw error;
|
|
1381
|
-
}
|
|
1382
|
-
};
|
|
1383
|
-
/**
|
|
1384
|
-
* The session's change against its base. Session mounts only, rooted at the
|
|
1385
|
-
* session's own checkout; a file is served only when it is in the change set
|
|
1386
|
-
* computed for the request. Overlapping requests share an in-flight snapshot.
|
|
1387
|
-
*/
|
|
1388
|
-
const review = async (request, url) => {
|
|
1389
|
-
if (url.pathname !== "/review" && url.pathname !== "/review/file") return void 0;
|
|
1390
|
-
const cwd = context.scope === "session" ? context.cwd : void 0;
|
|
1391
|
-
if (cwd === void 0) return Response.json({ error: "This route belongs to a session." }, { status: 404 });
|
|
1392
|
-
if (request.method !== "GET") return Response.json({ error: "Use GET." }, { status: 405 });
|
|
1393
|
-
let recordedBaseRef;
|
|
1394
|
-
try {
|
|
1395
|
-
recordedBaseRef = (await createWorktreeRegistry(await registryFile(cwd, homeDir)).list()).find((record) => record.sessionId === context.sessionId)?.baseRef;
|
|
1396
|
-
} catch {
|
|
1397
|
-
recordedBaseRef = void 0;
|
|
1398
|
-
}
|
|
1399
|
-
const key = JSON.stringify([cwd, recordedBaseRef ?? null]);
|
|
1400
|
-
let pending = pendingReviews.get(key);
|
|
1401
|
-
if (pending === void 0) {
|
|
1402
|
-
pending = branchDiff.review(cwd, recordedBaseRef === void 0 ? {} : { recordedBaseRef }).finally(() => pendingReviews.delete(key));
|
|
1403
|
-
pendingReviews.set(key, pending);
|
|
1404
|
-
}
|
|
1405
|
-
const result = await pending;
|
|
1406
|
-
if (url.pathname === "/review") return Response.json(result?.summary ?? {
|
|
1407
|
-
repository: false,
|
|
1408
|
-
files: []
|
|
1409
|
-
});
|
|
1410
|
-
const filePath = url.searchParams.get("path") ?? "";
|
|
1411
|
-
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 });
|
|
1412
|
-
return Response.json(await branchDiff.fileDiff(result, filePath));
|
|
1413
|
-
};
|
|
1414
|
-
return {
|
|
1415
|
-
async fetch(request) {
|
|
1416
|
-
const url = new URL(request.url);
|
|
1376
|
+
/** The admitted root for a request, or a response explaining why not. */
|
|
1377
|
+
const rootOf = (url) => {
|
|
1378
|
+
const repositoryId = url.searchParams.get("repositoryId");
|
|
1379
|
+
if (repositoryId === null || repositoryId === "") return badRequest("A repositoryId is required.");
|
|
1380
|
+
const root = context.resolveRepository?.(repositoryId);
|
|
1381
|
+
if (root === void 0) return badRequest("That repository is not admitted to this cockpit.");
|
|
1382
|
+
return root;
|
|
1383
|
+
};
|
|
1384
|
+
const homeDir = context.homeDirectory ?? context.environment?.HOME;
|
|
1385
|
+
const gitAuth = homeDir === void 0 ? void 0 : createGitAuthStore(homeDir);
|
|
1386
|
+
const branchDiff = createBranchDiff();
|
|
1387
|
+
const pendingReviews = /* @__PURE__ */ new Map();
|
|
1388
|
+
/**
|
|
1389
|
+
* A workspace's remote auth. Workspace mounts only, so the step-up gate on
|
|
1390
|
+
* `PUT /api/workspaces/<id>/plugins/git/auth` is the one way to write it.
|
|
1391
|
+
* The view never carries the token.
|
|
1392
|
+
*/
|
|
1393
|
+
const auth = async (request, url) => {
|
|
1394
|
+
if (url.pathname !== "/auth") return void 0;
|
|
1395
|
+
if (context.scope !== "workspace") return Response.json({ error: "This route belongs to a workspace." }, { status: 404 });
|
|
1396
|
+
if (request.method !== "GET" && request.method !== "PUT") return Response.json({ error: "Use GET or PUT." }, { status: 405 });
|
|
1397
|
+
if (gitAuth === void 0) throw new Error("Git auth requires the admitted host home directory.");
|
|
1398
|
+
const root = rootOf(url);
|
|
1399
|
+
if (root instanceof Response) return root;
|
|
1400
|
+
if (request.method === "GET") return Response.json(await gitAuth.view(root));
|
|
1417
1401
|
try {
|
|
1418
|
-
|
|
1419
|
-
if (answered !== void 0) return answered;
|
|
1402
|
+
return Response.json(await gitAuth.save(root, await request.json().catch(() => void 0)));
|
|
1420
1403
|
} catch (error) {
|
|
1421
|
-
|
|
1422
|
-
|
|
1404
|
+
if (error instanceof PoGitExpectedError && error.code === "invalid_request") {
|
|
1405
|
+
const text = error.message.replace(/^\[[a-z_]+\] /u, "").replace("\nRecovery: ", " ");
|
|
1406
|
+
return Response.json({
|
|
1407
|
+
error: text,
|
|
1408
|
+
code: error.code
|
|
1409
|
+
}, { status: 400 });
|
|
1410
|
+
}
|
|
1411
|
+
throw error;
|
|
1423
1412
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1413
|
+
};
|
|
1414
|
+
/**
|
|
1415
|
+
* The session's change against its base. Session mounts only, rooted at the
|
|
1416
|
+
* session's own checkout; a file is served only when it is in the change set
|
|
1417
|
+
* computed for the request. Overlapping requests share an in-flight snapshot.
|
|
1418
|
+
*/
|
|
1419
|
+
const review = async (request, url) => {
|
|
1420
|
+
if (url.pathname !== "/review" && url.pathname !== "/review/file") return void 0;
|
|
1421
|
+
const cwd = context.scope === "session" ? context.cwd : void 0;
|
|
1422
|
+
if (cwd === void 0) return Response.json({ error: "This route belongs to a session." }, { status: 404 });
|
|
1423
|
+
if (request.method !== "GET") return Response.json({ error: "Use GET." }, { status: 405 });
|
|
1424
|
+
if (homeDir === void 0) throw new Error("Git review requires the admitted host home directory.");
|
|
1425
|
+
let recordedBaseRef;
|
|
1431
1426
|
try {
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1427
|
+
recordedBaseRef = (await createWorktreeRegistry(await registryFile(cwd, homeDir)).list()).find((record) => record.sessionId === context.sessionId)?.baseRef;
|
|
1428
|
+
} catch {
|
|
1429
|
+
recordedBaseRef = void 0;
|
|
1430
|
+
}
|
|
1431
|
+
const key = JSON.stringify([cwd, recordedBaseRef ?? null]);
|
|
1432
|
+
let pending = pendingReviews.get(key);
|
|
1433
|
+
if (pending === void 0) {
|
|
1434
|
+
pending = branchDiff.review(cwd, recordedBaseRef === void 0 ? {} : { recordedBaseRef }).finally(() => pendingReviews.delete(key));
|
|
1435
|
+
pendingReviews.set(key, pending);
|
|
1436
|
+
}
|
|
1437
|
+
const result = await pending;
|
|
1438
|
+
if (url.pathname === "/review") return Response.json(result?.summary ?? {
|
|
1439
|
+
repository: false,
|
|
1440
|
+
files: []
|
|
1441
|
+
});
|
|
1442
|
+
const filePath = url.searchParams.get("path") ?? "";
|
|
1443
|
+
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 });
|
|
1444
|
+
return Response.json(await branchDiff.fileDiff(result, filePath));
|
|
1445
|
+
};
|
|
1446
|
+
return {
|
|
1447
|
+
async fetch(request) {
|
|
1448
|
+
const url = new URL(request.url);
|
|
1449
|
+
try {
|
|
1450
|
+
const answered = await newSession(request, url) ?? await auth(request, url) ?? await review(request, url);
|
|
1451
|
+
if (answered !== void 0) return answered;
|
|
1452
|
+
} catch (error) {
|
|
1453
|
+
context.onNotice(`git request failed: ${error.name}`);
|
|
1454
|
+
return failed(error);
|
|
1443
1455
|
}
|
|
1444
|
-
const
|
|
1445
|
-
if (
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1456
|
+
const root = rootOf(url);
|
|
1457
|
+
if (root instanceof Response) return root;
|
|
1458
|
+
const callerContext = {
|
|
1459
|
+
cwd: root,
|
|
1460
|
+
sessionId: url.searchParams.get("sessionId") ?? ""
|
|
1461
|
+
};
|
|
1462
|
+
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.");
|
|
1463
|
+
try {
|
|
1464
|
+
if (request.method === "GET" && url.pathname === "/worktrees") return Response.json({ worktrees: await operations.list(callerContext) });
|
|
1465
|
+
if (request.method === "POST" && url.pathname === "/worktrees") {
|
|
1466
|
+
const body = await request.json();
|
|
1467
|
+
if (typeof body.branch !== "string" || body.branch.trim() === "") return badRequest("A branch is required.");
|
|
1468
|
+
const record = await operations.spawn(callerContext, {
|
|
1469
|
+
branch: body.branch,
|
|
1470
|
+
...body.baseRef === void 0 ? {} : { baseRef: body.baseRef },
|
|
1471
|
+
...body.name === void 0 ? {} : { name: body.name }
|
|
1472
|
+
});
|
|
1473
|
+
publishLifecycle(callerContext.sessionId, record.repositoryRoot);
|
|
1474
|
+
return Response.json({ worktree: record }, { status: 201 });
|
|
1475
|
+
}
|
|
1476
|
+
const closing = /^\/worktrees\/([^/]+)$/u.exec(url.pathname);
|
|
1477
|
+
if (request.method === "DELETE" && closing) {
|
|
1478
|
+
const record = await operations.close(callerContext, decodeURIComponent(String(closing[1])), url.searchParams.get("force") === "true");
|
|
1479
|
+
publishLifecycle(callerContext.sessionId, record.repositoryRoot);
|
|
1480
|
+
return Response.json({ worktree: record });
|
|
1481
|
+
}
|
|
1482
|
+
return Response.json({ error: "No such worktree route." }, { status: 404 });
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
context.onNotice(`worktree request failed: ${error.name}`);
|
|
1485
|
+
return failed(error);
|
|
1449
1486
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1487
|
+
},
|
|
1488
|
+
close() {}
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
//#endregion
|
|
1494
|
+
//#region src/extensions/(backend)/api/git/route.server.ts
|
|
1495
|
+
var route_server_default = defineRoute(({ context, host, signal }) => {
|
|
1496
|
+
let openDirectory;
|
|
1497
|
+
context.inject([PO_SHELL_SERVICE], (child) => {
|
|
1498
|
+
const shell = requirePoShell(child);
|
|
1499
|
+
const env = Object.fromEntries(Object.entries(host.context.environment ?? {}).filter((entry) => entry[1] !== void 0));
|
|
1500
|
+
openDirectory = async (path) => {
|
|
1501
|
+
const result = await shell.exec([
|
|
1502
|
+
"/usr/bin/open",
|
|
1503
|
+
"-a",
|
|
1504
|
+
"Finder",
|
|
1505
|
+
path
|
|
1506
|
+
], {
|
|
1507
|
+
env,
|
|
1508
|
+
inheritEnv: false
|
|
1509
|
+
}, withAbortSignal(AbortSignal.any([signal, AbortSignal.timeout(5e3)]), BACKGROUND_CONTEXT));
|
|
1510
|
+
if (!result.ok || result.value.exitCode !== 0) throw new Error("Finder could not be opened.");
|
|
1457
1511
|
};
|
|
1458
|
-
|
|
1512
|
+
return () => {
|
|
1513
|
+
openDirectory = void 0;
|
|
1514
|
+
};
|
|
1515
|
+
});
|
|
1516
|
+
return createGitApi(async (path) => {
|
|
1517
|
+
if (openDirectory === void 0) throw new Error("The host shell is unavailable.");
|
|
1518
|
+
await openDirectory(path);
|
|
1519
|
+
});
|
|
1459
1520
|
});
|
|
1460
1521
|
//#endregion
|
|
1461
1522
|
//#region src/services/gitFailure/index.ts
|