tarsk 0.5.41 → 0.5.42
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/index.js +1697 -493
- package/dist/public/assets/{account-view-D-dJ0y-D.js → account-view-BAm4Az95.js} +1 -1
- package/dist/public/assets/api-C44iPrsZ.js +1 -0
- package/dist/public/assets/browser-tab-DV6mFCbu.js +1 -0
- package/dist/public/assets/commit-dialog-DYdmiA4p.js +1 -0
- package/dist/public/assets/context-menu-BVP2PUoS.js +1 -0
- package/dist/public/assets/create-repo-dialog-DrGJUrGO.js +1 -0
- package/dist/public/assets/{dialogs-config-B-LZ4nOb.js → dialogs-config-BPzX42HH.js} +14 -14
- package/dist/public/assets/diff-view-BnNtDcmG.js +3 -0
- package/dist/public/assets/explorer-tab-view-OGWG1dH9.js +2 -0
- package/dist/public/assets/explorer-tree--Ho2K8-i.js +1 -0
- package/dist/public/assets/explorer-view-Dw0Kyxb8.js +1 -0
- package/dist/public/assets/git-history-dialog-BpOpNbze.js +1 -0
- package/dist/public/assets/git-ops-button-DKOfNm4s.js +2 -0
- package/dist/public/assets/history-view-DwSqsTGI.js +9 -0
- package/dist/public/assets/index-BdLH7zyV.css +1 -0
- package/dist/public/assets/index-BqaiWs7x.js +88 -0
- package/dist/public/assets/mcp-server-card-BbAUiI-X.js +1 -0
- package/dist/public/assets/merged-pr-dialog-Cvo93bRH.js +1 -0
- package/dist/public/assets/onboarding-BmBbv0At.js +1 -0
- package/dist/public/assets/project-settings-view-D-8Ua1Tu.js +1 -0
- package/dist/public/assets/providers-list-view-Gm13Fagd.js +1 -0
- package/dist/public/assets/pull-request-dialog-BkzdP5F2.js +1 -0
- package/dist/public/assets/pull-with-changes-dialog-DQU7jHYL.js +1 -0
- package/dist/public/assets/push-before-pr-dialog-DII3gUVw.js +1 -0
- package/dist/public/assets/radio-group-DkDRaQdF.js +1 -0
- package/dist/public/assets/react-vendor-BmFalJ0W.js +16 -0
- package/dist/public/assets/settings-general-view-Br89Vrbv.js +1 -0
- package/dist/public/assets/{settings-instructions-view-C57XGLha.js → settings-instructions-view-ByxlAb9U.js} +1 -1
- package/dist/public/assets/settings-list-DSrSoTv7.js +1 -0
- package/dist/public/assets/settings-mcp-servers-view-U_la7C8z.js +5 -0
- package/dist/public/assets/{settings-models-skeleton-ClrbJy_p.js → settings-models-skeleton-C5XrzM39.js} +1 -1
- package/dist/public/assets/settings-models-view-obQa4doG.js +1 -0
- package/dist/public/assets/settings-rules-view-Cd9WHEAE.js +8 -0
- package/dist/public/assets/settings-skills-view-B81njz98.js +2 -0
- package/dist/public/assets/settings-slash-commands-view-Ct8WC1Z0.js +1 -0
- package/dist/public/assets/settings-subagents-view-BwNTnGkz.js +2 -0
- package/dist/public/assets/{settings-system-prompt-view-Dl66VFaj.js → settings-system-prompt-view-C4uLn68y.js} +1 -1
- package/dist/public/assets/settings-view-DQW_9pnm.js +2 -0
- package/dist/public/assets/skeleton-HsHVnOKO.js +1 -0
- package/dist/public/assets/{terminal-panel-BGQxckfH.js → terminal-panel-wOltBu70.js} +2 -2
- package/dist/public/assets/{ui-components-C4RrfJEJ.js → ui-components-CaeQlGTd.js} +1 -1
- package/dist/public/assets/{utils-B7FQXlI6.js → utils-5mrXWXCs.js} +1 -1
- package/dist/public/assets/web-Gjw6klhQ.js +1 -0
- package/dist/public/assets/web-NsuCXAqU.js +1 -0
- package/dist/public/browser-preview-rpc.js +484 -0
- package/dist/public/index.html +7 -7
- package/package.json +1 -1
- package/dist/public/assets/api-DJaJqkc6.js +0 -1
- package/dist/public/assets/browser-tab-D7wEj-BD.js +0 -1
- package/dist/public/assets/commit-dialog-DdhGDH4F.js +0 -1
- package/dist/public/assets/context-menu-asf2g-KX.js +0 -1
- package/dist/public/assets/create-repo-dialog-Bltp6PKZ.js +0 -1
- package/dist/public/assets/diff-view-Bi545EPj.js +0 -3
- package/dist/public/assets/explorer-tab-view-B-P555GE.js +0 -2
- package/dist/public/assets/explorer-tree-CyXhVrI7.js +0 -1
- package/dist/public/assets/explorer-view-BAHDhIGN.js +0 -1
- package/dist/public/assets/git-history-dialog-Bci_iQmi.js +0 -1
- package/dist/public/assets/git-ops-button-1lum9QXI.js +0 -2
- package/dist/public/assets/history-view-CAkN8PCo.js +0 -9
- package/dist/public/assets/index-BLO68CQl.js +0 -69
- package/dist/public/assets/index-jIBJk8xl.css +0 -1
- package/dist/public/assets/mcp-server-card-DQUpkDFV.js +0 -1
- package/dist/public/assets/merged-pr-dialog-tHPrJ2CK.js +0 -1
- package/dist/public/assets/onboarding-Bbvb7kO4.js +0 -1
- package/dist/public/assets/project-settings-view-BRkHOq_q.js +0 -1
- package/dist/public/assets/providers-list-view-Dex879vv.js +0 -1
- package/dist/public/assets/pull-request-dialog-WHdmrW83.js +0 -1
- package/dist/public/assets/pull-with-changes-dialog-Ck3OwINV.js +0 -1
- package/dist/public/assets/push-before-pr-dialog-Bvqvz04U.js +0 -1
- package/dist/public/assets/radio-group-B0xvu5B9.js +0 -1
- package/dist/public/assets/react-vendor-D8PTA4EX.js +0 -16
- package/dist/public/assets/settings-general-view-lUxshNA9.js +0 -1
- package/dist/public/assets/settings-list-CHGKmGl_.js +0 -1
- package/dist/public/assets/settings-mcp-servers-view-DpqkhrgB.js +0 -5
- package/dist/public/assets/settings-models-view-QPEdnibD.js +0 -1
- package/dist/public/assets/settings-rules-view-WJU--cRq.js +0 -8
- package/dist/public/assets/settings-skills-view-mgHy4G_g.js +0 -2
- package/dist/public/assets/settings-slash-commands-view-LB5tVqy1.js +0 -1
- package/dist/public/assets/settings-subagents-view-QR2qlA_y.js +0 -2
- package/dist/public/assets/settings-view-BlVJv4Pz.js +0 -2
- package/dist/public/assets/skeleton-K-fVduHt.js +0 -1
package/dist/index.js
CHANGED
|
@@ -50,7 +50,7 @@ import { homedir } from "node:os";
|
|
|
50
50
|
import { join } from "node:path";
|
|
51
51
|
import { spawn, spawnSync, execSync } from "child_process";
|
|
52
52
|
function delay(ms) {
|
|
53
|
-
return new Promise((
|
|
53
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
54
54
|
}
|
|
55
55
|
function mergePath(parentPath, shellPath) {
|
|
56
56
|
const parent = (parentPath ?? "").split(":").filter(Boolean);
|
|
@@ -1296,6 +1296,7 @@ function parseGeneratedImagePayload(value) {
|
|
|
1296
1296
|
function stripGeneratedImagePayload(payload) {
|
|
1297
1297
|
const next = { ...payload };
|
|
1298
1298
|
delete next.imageData;
|
|
1299
|
+
delete next.image_data;
|
|
1299
1300
|
if (typeof next.imageUrl === "string" && isDataImageUrl(next.imageUrl)) {
|
|
1300
1301
|
delete next.imageUrl;
|
|
1301
1302
|
}
|
|
@@ -1317,12 +1318,13 @@ function stripGeneratedImageText(text) {
|
|
|
1317
1318
|
return text;
|
|
1318
1319
|
}
|
|
1319
1320
|
}
|
|
1321
|
+
var IMAGE_RESULT_TOOL_NAMES = /* @__PURE__ */ new Set(["generate_image", "find_images", "browser"]);
|
|
1320
1322
|
function sanitizeToolResultBlock(block, collected) {
|
|
1321
1323
|
if (block.type !== "tool-result") {
|
|
1322
1324
|
return block;
|
|
1323
1325
|
}
|
|
1324
1326
|
const toolName = typeof block.toolName === "string" ? block.toolName : "";
|
|
1325
|
-
if (toolName
|
|
1327
|
+
if (!IMAGE_RESULT_TOOL_NAMES.has(toolName)) {
|
|
1326
1328
|
return block;
|
|
1327
1329
|
}
|
|
1328
1330
|
const blockRefs = parseGeneratedImagePayload(block);
|
|
@@ -1467,7 +1469,7 @@ function extractGeneratedImageFromEvents(events) {
|
|
|
1467
1469
|
const parsed = JSON.parse(event.content.trim());
|
|
1468
1470
|
const blocks = Array.isArray(parsed) ? parsed : [parsed];
|
|
1469
1471
|
for (const block of blocks) {
|
|
1470
|
-
if (block && typeof block === "object" && "type" in block && block.type === "tool-result" && block.toolName
|
|
1472
|
+
if (block && typeof block === "object" && "type" in block && block.type === "tool-result" && IMAGE_RESULT_TOOL_NAMES.has(block.toolName)) {
|
|
1471
1473
|
const refs = parseGeneratedImagePayload(block);
|
|
1472
1474
|
if (refs?.imageUrl) {
|
|
1473
1475
|
collected.imageUrl = refs.imageUrl;
|
|
@@ -1490,11 +1492,29 @@ function extractGeneratedImageFromEvents(events) {
|
|
|
1490
1492
|
return collected;
|
|
1491
1493
|
}
|
|
1492
1494
|
|
|
1495
|
+
// ../shared/dist/image-url.js
|
|
1496
|
+
function isAbsoluteHttpOrDataUrl(url) {
|
|
1497
|
+
return !url || url.startsWith("data:") || /^https?:\/\//i.test(url);
|
|
1498
|
+
}
|
|
1499
|
+
function buildThreadExplorerMediaUrl(threadId, projectPath, origin = "http://localhost:641") {
|
|
1500
|
+
const base = origin.replace(/\/$/, "");
|
|
1501
|
+
return `${base}/api/threads/${encodeURIComponent(threadId)}/explorer/media?path=${encodeURIComponent(projectPath)}`;
|
|
1502
|
+
}
|
|
1503
|
+
function resolveAbsoluteImageUrl(imageUrl, baseUrl) {
|
|
1504
|
+
if (isAbsoluteHttpOrDataUrl(imageUrl)) {
|
|
1505
|
+
return imageUrl;
|
|
1506
|
+
}
|
|
1507
|
+
const origin = baseUrl.replace(/\/$/, "");
|
|
1508
|
+
const path7 = imageUrl.startsWith("/") ? imageUrl : `/${imageUrl}`;
|
|
1509
|
+
return `${origin}${path7}`;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1493
1512
|
// ../shared/dist/tool-display.js
|
|
1494
1513
|
var toolDisplayRegistry = [
|
|
1495
1514
|
{ name: "agent", displayName: "Subagent", activeDisplayName: "Subagent" },
|
|
1496
1515
|
{ name: "ask_user", displayName: "Ask", activeDisplayName: "Asking" },
|
|
1497
1516
|
{ name: "bash", displayName: "Ran", activeDisplayName: "Running" },
|
|
1517
|
+
{ name: "browser", displayName: "Browser", activeDisplayName: "Browsing" },
|
|
1498
1518
|
{ name: "code_search", displayName: "Code Search", activeDisplayName: "Searching" },
|
|
1499
1519
|
{ name: "edit", displayName: "Edit", activeDisplayName: "Editing" },
|
|
1500
1520
|
{ name: "execute_skill_script", displayName: "Run Script", activeDisplayName: "Running Script" },
|
|
@@ -1642,9 +1662,27 @@ function getExplorerFileMediaType(path7, name) {
|
|
|
1642
1662
|
return null;
|
|
1643
1663
|
}
|
|
1644
1664
|
|
|
1665
|
+
// ../shared/dist/browser-tool.js
|
|
1666
|
+
var BROWSER_HOST_ACTIONS = [
|
|
1667
|
+
"reload",
|
|
1668
|
+
"go_back",
|
|
1669
|
+
"go_forward",
|
|
1670
|
+
"get_state",
|
|
1671
|
+
"set_viewport"
|
|
1672
|
+
];
|
|
1673
|
+
function isBrowserHostAction(action) {
|
|
1674
|
+
return BROWSER_HOST_ACTIONS.includes(action);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
// ../shared/dist/tmp-images.js
|
|
1678
|
+
var TARSK_TMP_IMAGES_DIR = ".tarsk/tmp-images";
|
|
1679
|
+
function buildTmpImagePath(filename) {
|
|
1680
|
+
return `${TARSK_TMP_IMAGES_DIR}/${filename}`;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1645
1683
|
// src/server.ts
|
|
1646
1684
|
import fs3 from "fs";
|
|
1647
|
-
import { Hono as
|
|
1685
|
+
import { Hono as Hono27 } from "hono";
|
|
1648
1686
|
import { cors } from "hono/cors";
|
|
1649
1687
|
import open3 from "open";
|
|
1650
1688
|
import path5 from "path";
|
|
@@ -2453,7 +2491,7 @@ var bashSchema = Type.Object({
|
|
|
2453
2491
|
});
|
|
2454
2492
|
var defaultBashOperations = {
|
|
2455
2493
|
exec: (command, cwd, { onData, signal, timeout, env }) => {
|
|
2456
|
-
return new Promise((
|
|
2494
|
+
return new Promise((resolve7, reject) => {
|
|
2457
2495
|
const { shell, args: args2 } = getShellConfig();
|
|
2458
2496
|
if (!existsSync3(cwd)) {
|
|
2459
2497
|
reject(new Error(`Working directory does not exist: ${cwd}`));
|
|
@@ -2502,7 +2540,7 @@ var defaultBashOperations = {
|
|
|
2502
2540
|
reject(new Error(`timeout:${timeout}`));
|
|
2503
2541
|
return;
|
|
2504
2542
|
}
|
|
2505
|
-
|
|
2543
|
+
resolve7({ exitCode: code });
|
|
2506
2544
|
});
|
|
2507
2545
|
});
|
|
2508
2546
|
}
|
|
@@ -2525,7 +2563,7 @@ ${command}` : command;
|
|
|
2525
2563
|
if (shellPermissionGate) {
|
|
2526
2564
|
await shellPermissionGate.ensureAllowed(toolCallId, resolvedCommand, "bash", signal);
|
|
2527
2565
|
}
|
|
2528
|
-
return new Promise((
|
|
2566
|
+
return new Promise((resolve7, reject) => {
|
|
2529
2567
|
let tempFilePath;
|
|
2530
2568
|
let tempFileStream;
|
|
2531
2569
|
let totalBytes = 0;
|
|
@@ -2594,7 +2632,7 @@ ${command}` : command;
|
|
|
2594
2632
|
Command exited with code ${exitCode}`;
|
|
2595
2633
|
reject(new Error(outputText));
|
|
2596
2634
|
} else {
|
|
2597
|
-
|
|
2635
|
+
resolve7({ content: [{ type: "text", text: outputText }], details });
|
|
2598
2636
|
}
|
|
2599
2637
|
}).catch((err) => {
|
|
2600
2638
|
tarskDebugLog(`[ai] bash-end: ${resolvedCommand}`);
|
|
@@ -2750,7 +2788,7 @@ function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
|
2750
2788
|
|
|
2751
2789
|
// src/tools/tool-helpers.ts
|
|
2752
2790
|
async function withAbortSignal(signal, operation) {
|
|
2753
|
-
return new Promise((
|
|
2791
|
+
return new Promise((resolve7, reject) => {
|
|
2754
2792
|
if (signal?.aborted) {
|
|
2755
2793
|
reject(new Error("Operation aborted"));
|
|
2756
2794
|
return;
|
|
@@ -2769,7 +2807,7 @@ async function withAbortSignal(signal, operation) {
|
|
|
2769
2807
|
const abortCheck = () => aborted;
|
|
2770
2808
|
operation(abortCheck).then((result) => {
|
|
2771
2809
|
cleanup();
|
|
2772
|
-
if (!aborted)
|
|
2810
|
+
if (!aborted) resolve7(result);
|
|
2773
2811
|
}).catch((error) => {
|
|
2774
2812
|
cleanup();
|
|
2775
2813
|
if (!aborted) reject(error);
|
|
@@ -2892,7 +2930,7 @@ function createFindTool(cwd, options) {
|
|
|
2892
2930
|
description: `Search for files by glob pattern. Returns matching file paths relative to the search directory. Output truncated to ${DEFAULT_LIMIT} results or ${DEFAULT_MAX_BYTES / 1024}KB.`,
|
|
2893
2931
|
parameters: findSchema,
|
|
2894
2932
|
execute: async (_toolCallId, { pattern, path: searchDir, limit }, signal) => {
|
|
2895
|
-
return new Promise((
|
|
2933
|
+
return new Promise((resolve7, reject) => {
|
|
2896
2934
|
if (signal?.aborted) {
|
|
2897
2935
|
reject(new Error("Operation aborted"));
|
|
2898
2936
|
return;
|
|
@@ -2925,7 +2963,7 @@ function createFindTool(cwd, options) {
|
|
|
2925
2963
|
});
|
|
2926
2964
|
signal?.removeEventListener("abort", onAbort);
|
|
2927
2965
|
if (results.length === 0) {
|
|
2928
|
-
|
|
2966
|
+
resolve7({
|
|
2929
2967
|
content: [{ type: "text", text: "No files found matching pattern" }],
|
|
2930
2968
|
details: void 0
|
|
2931
2969
|
});
|
|
@@ -2956,7 +2994,7 @@ function createFindTool(cwd, options) {
|
|
|
2956
2994
|
if (notices.length > 0) resultOutput += `
|
|
2957
2995
|
|
|
2958
2996
|
[${notices.join(". ")}]`;
|
|
2959
|
-
|
|
2997
|
+
resolve7({
|
|
2960
2998
|
content: [{ type: "text", text: resultOutput }],
|
|
2961
2999
|
details: Object.keys(details).length > 0 ? details : void 0
|
|
2962
3000
|
});
|
|
@@ -2981,7 +3019,7 @@ import path2 from "path";
|
|
|
2981
3019
|
// src/tools/resolve-bin.ts
|
|
2982
3020
|
init_utils();
|
|
2983
3021
|
function resolveBin(name) {
|
|
2984
|
-
return new Promise((
|
|
3022
|
+
return new Promise((resolve7) => {
|
|
2985
3023
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
2986
3024
|
const args2 = process.platform === "win32" ? [name + ".exe", name] : [name];
|
|
2987
3025
|
try {
|
|
@@ -2989,13 +3027,13 @@ function resolveBin(name) {
|
|
|
2989
3027
|
if (result.status === 0 && result.stdout) {
|
|
2990
3028
|
const first = (typeof result.stdout === "string" ? result.stdout : result.stdout?.toString() || "").trim().split(/\r?\n/)[0]?.trim();
|
|
2991
3029
|
if (first) {
|
|
2992
|
-
|
|
3030
|
+
resolve7(first);
|
|
2993
3031
|
return;
|
|
2994
3032
|
}
|
|
2995
3033
|
}
|
|
2996
3034
|
} catch {
|
|
2997
3035
|
}
|
|
2998
|
-
|
|
3036
|
+
resolve7(null);
|
|
2999
3037
|
});
|
|
3000
3038
|
}
|
|
3001
3039
|
|
|
@@ -3034,7 +3072,7 @@ function createSimpleGrepTool(cwd, options) {
|
|
|
3034
3072
|
description: `Search file contents for a pattern. Returns matching lines with file paths and line numbers. Respects .gitignore. Output truncated to ${DEFAULT_LIMIT2} matches or ${DEFAULT_MAX_BYTES / 1024}KB. Long lines truncated to ${GREP_MAX_LINE_LENGTH} chars.`,
|
|
3035
3073
|
parameters: grepSchema,
|
|
3036
3074
|
execute: async (_toolCallId, { pattern, path: searchDir, glob: glob3, ignoreCase, literal, context, limit }, signal) => {
|
|
3037
|
-
return new Promise((
|
|
3075
|
+
return new Promise((resolve7, reject) => {
|
|
3038
3076
|
if (signal?.aborted) {
|
|
3039
3077
|
reject(new Error("Operation aborted"));
|
|
3040
3078
|
return;
|
|
@@ -3183,7 +3221,7 @@ function createSimpleGrepTool(cwd, options) {
|
|
|
3183
3221
|
}
|
|
3184
3222
|
if (matchCount === 0) {
|
|
3185
3223
|
settle(
|
|
3186
|
-
() =>
|
|
3224
|
+
() => resolve7({
|
|
3187
3225
|
content: [{ type: "text", text: "No matches found" }],
|
|
3188
3226
|
details: void 0
|
|
3189
3227
|
})
|
|
@@ -3219,7 +3257,7 @@ function createSimpleGrepTool(cwd, options) {
|
|
|
3219
3257
|
|
|
3220
3258
|
[${notices.join(". ")}]`;
|
|
3221
3259
|
settle(
|
|
3222
|
-
() =>
|
|
3260
|
+
() => resolve7({
|
|
3223
3261
|
content: [{ type: "text", text: output }],
|
|
3224
3262
|
details: Object.keys(details).length > 0 ? details : void 0
|
|
3225
3263
|
})
|
|
@@ -4246,7 +4284,7 @@ function createLsTool(cwd, options) {
|
|
|
4246
4284
|
description: `List directory contents. Returns entries sorted alphabetically, with '/' suffix for directories. Output truncated to ${DEFAULT_LIMIT4} entries or ${DEFAULT_MAX_BYTES / 1024}KB.`,
|
|
4247
4285
|
parameters: lsSchema,
|
|
4248
4286
|
execute: async (_toolCallId, { path: dirPathArg, limit }, signal) => {
|
|
4249
|
-
return new Promise((
|
|
4287
|
+
return new Promise((resolve7, reject) => {
|
|
4250
4288
|
if (signal?.aborted) {
|
|
4251
4289
|
reject(new Error("Operation aborted"));
|
|
4252
4290
|
return;
|
|
@@ -4294,7 +4332,7 @@ function createLsTool(cwd, options) {
|
|
|
4294
4332
|
}
|
|
4295
4333
|
signal?.removeEventListener("abort", onAbort);
|
|
4296
4334
|
if (results.length === 0) {
|
|
4297
|
-
|
|
4335
|
+
resolve7({
|
|
4298
4336
|
content: [{ type: "text", text: "(empty directory)" }],
|
|
4299
4337
|
details: void 0
|
|
4300
4338
|
});
|
|
@@ -4318,7 +4356,7 @@ function createLsTool(cwd, options) {
|
|
|
4318
4356
|
if (notices.length > 0) output += `
|
|
4319
4357
|
|
|
4320
4358
|
[${notices.join(". ")}]`;
|
|
4321
|
-
|
|
4359
|
+
resolve7({
|
|
4322
4360
|
content: [{ type: "text", text: output }],
|
|
4323
4361
|
details: Object.keys(details).length > 0 ? details : void 0
|
|
4324
4362
|
});
|
|
@@ -4444,7 +4482,7 @@ function createWriteTool(cwd, options) {
|
|
|
4444
4482
|
validatePathWithinCwd(absolutePath, cwd);
|
|
4445
4483
|
const dir = dirname(absolutePath);
|
|
4446
4484
|
return new Promise(
|
|
4447
|
-
(
|
|
4485
|
+
(resolve7, reject) => {
|
|
4448
4486
|
if (signal?.aborted) {
|
|
4449
4487
|
reject(new Error("Operation aborted"));
|
|
4450
4488
|
return;
|
|
@@ -4462,7 +4500,7 @@ function createWriteTool(cwd, options) {
|
|
|
4462
4500
|
await ops.writeFile(absolutePath, content);
|
|
4463
4501
|
if (aborted) return;
|
|
4464
4502
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
4465
|
-
|
|
4503
|
+
resolve7({
|
|
4466
4504
|
content: [
|
|
4467
4505
|
{ type: "text", text: `Successfully wrote ${content.length} bytes to ${path7}` }
|
|
4468
4506
|
],
|
|
@@ -4514,7 +4552,7 @@ async function executeScript(scriptPath, args2, cwd, timeout = 30) {
|
|
|
4514
4552
|
if (!interpreter) {
|
|
4515
4553
|
throw new Error(`Unsupported script type: ${scriptPath}`);
|
|
4516
4554
|
}
|
|
4517
|
-
return new Promise((
|
|
4555
|
+
return new Promise((resolve7, reject) => {
|
|
4518
4556
|
const child = spawnProcess(interpreter.command, [...interpreter.args, ...args2], {
|
|
4519
4557
|
cwd,
|
|
4520
4558
|
timeout: timeout * 1e3
|
|
@@ -4546,7 +4584,7 @@ async function executeScript(scriptPath, args2, cwd, timeout = 30) {
|
|
|
4546
4584
|
reject(new Error(`Script execution timed out after ${timeout}s`));
|
|
4547
4585
|
return;
|
|
4548
4586
|
}
|
|
4549
|
-
|
|
4587
|
+
resolve7({ stdout, stderr, exitCode: code });
|
|
4550
4588
|
});
|
|
4551
4589
|
});
|
|
4552
4590
|
}
|
|
@@ -4825,9 +4863,9 @@ function createAskUserTool() {
|
|
|
4825
4863
|
if (signal?.aborted) {
|
|
4826
4864
|
throw new Error("Operation aborted");
|
|
4827
4865
|
}
|
|
4828
|
-
const answer = await new Promise((
|
|
4866
|
+
const answer = await new Promise((resolve7, reject) => {
|
|
4829
4867
|
pendingQuestions.set(toolCallId, {
|
|
4830
|
-
resolve:
|
|
4868
|
+
resolve: resolve7,
|
|
4831
4869
|
reject,
|
|
4832
4870
|
question,
|
|
4833
4871
|
options,
|
|
@@ -6555,8 +6593,46 @@ async function executeGenerateImage(options) {
|
|
|
6555
6593
|
|
|
6556
6594
|
// src/tools/find-images.ts
|
|
6557
6595
|
import { Type as Type15 } from "@sinclair/typebox";
|
|
6596
|
+
import { join as join9 } from "path";
|
|
6597
|
+
|
|
6598
|
+
// src/core/server-origin.ts
|
|
6599
|
+
function getServerOrigin() {
|
|
6600
|
+
const port = process.env.PORT ?? "641";
|
|
6601
|
+
return `http://localhost:${port}`;
|
|
6602
|
+
}
|
|
6603
|
+
|
|
6604
|
+
// src/tools/tool-image-storage.ts
|
|
6558
6605
|
import { mkdir as fsMkdir3, writeFile as fsWriteFile4 } from "fs/promises";
|
|
6559
6606
|
import { dirname as dirname3 } from "path";
|
|
6607
|
+
function decodeBase64ImageData(imageData) {
|
|
6608
|
+
const binary = atob(imageData);
|
|
6609
|
+
const bytes = new Uint8Array(binary.length);
|
|
6610
|
+
for (let i = 0; i < binary.length; i++) {
|
|
6611
|
+
bytes[i] = binary.charCodeAt(i);
|
|
6612
|
+
}
|
|
6613
|
+
return bytes;
|
|
6614
|
+
}
|
|
6615
|
+
async function saveToolImage(options) {
|
|
6616
|
+
const bytes = decodeBase64ImageData(options.imageData);
|
|
6617
|
+
const absolutePath = resolveToCwd(options.relativePath, options.cwd);
|
|
6618
|
+
validatePathWithinCwd(absolutePath, options.cwd);
|
|
6619
|
+
await fsMkdir3(dirname3(absolutePath), { recursive: true });
|
|
6620
|
+
await fsWriteFile4(absolutePath, bytes);
|
|
6621
|
+
const saved = {
|
|
6622
|
+
savedPath: options.relativePath,
|
|
6623
|
+
sizeInBytes: bytes.length
|
|
6624
|
+
};
|
|
6625
|
+
if (options.threadId && options.serverOrigin) {
|
|
6626
|
+
saved.imageUrl = buildThreadExplorerMediaUrl(
|
|
6627
|
+
options.threadId,
|
|
6628
|
+
options.relativePath,
|
|
6629
|
+
options.serverOrigin
|
|
6630
|
+
);
|
|
6631
|
+
}
|
|
6632
|
+
return saved;
|
|
6633
|
+
}
|
|
6634
|
+
|
|
6635
|
+
// src/tools/find-images.ts
|
|
6560
6636
|
var findImagesSchema = Type15.Object({
|
|
6561
6637
|
query: Type15.String({ description: "Search term for the image (e.g. 'sunset over mountains')" }),
|
|
6562
6638
|
size: Type15.Optional(
|
|
@@ -6578,15 +6654,36 @@ var findImagesSchema = Type15.Object({
|
|
|
6578
6654
|
),
|
|
6579
6655
|
save_to_file: Type15.Optional(
|
|
6580
6656
|
Type15.String({
|
|
6581
|
-
description: "File path relative to the project root to save the image to (e.g. 'assets/hero.jpg'). Parent directories are created automatically. If omitted the image is
|
|
6657
|
+
description: "File path relative to the project root to save the image to (e.g. 'assets/hero.jpg'). Parent directories are created automatically. If omitted the image is saved under .tarsk/tmp-images/."
|
|
6582
6658
|
})
|
|
6583
6659
|
)
|
|
6584
6660
|
});
|
|
6585
|
-
function
|
|
6661
|
+
function extensionForContentType(contentType) {
|
|
6662
|
+
if (contentType.includes("png")) {
|
|
6663
|
+
return ".png";
|
|
6664
|
+
}
|
|
6665
|
+
if (contentType.includes("webp")) {
|
|
6666
|
+
return ".webp";
|
|
6667
|
+
}
|
|
6668
|
+
if (contentType.includes("gif")) {
|
|
6669
|
+
return ".gif";
|
|
6670
|
+
}
|
|
6671
|
+
if (contentType.includes("jpeg") || contentType.includes("jpg")) {
|
|
6672
|
+
return ".jpg";
|
|
6673
|
+
}
|
|
6674
|
+
return ".jpg";
|
|
6675
|
+
}
|
|
6676
|
+
function defaultSavePath(query, contentType) {
|
|
6677
|
+
const slug = query.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "image";
|
|
6678
|
+
const id = Math.random().toString(36).slice(2, 8);
|
|
6679
|
+
return buildTmpImagePath(`${slug}-${id}${extensionForContentType(contentType)}`);
|
|
6680
|
+
}
|
|
6681
|
+
function createFindImagesTool(options = {}) {
|
|
6682
|
+
const { cwd, threadId, serverOrigin = getServerOrigin() } = options;
|
|
6586
6683
|
return {
|
|
6587
6684
|
name: "find_images",
|
|
6588
6685
|
label: "find_images",
|
|
6589
|
-
description: "Find a stock image matching a search query. Returns
|
|
6686
|
+
description: "Find a stock image matching a search query. Returns a URL served by the Tarsk API and optionally saves the image to a file. Use this when the user needs an image and the user has not requested it be generated.",
|
|
6590
6687
|
parameters: findImagesSchema,
|
|
6591
6688
|
execute: async (_toolCallId, { query, size, save_to_file }, signal) => {
|
|
6592
6689
|
return withAbortSignal(signal, async (isAborted) => {
|
|
@@ -6607,52 +6704,889 @@ function createFindImagesTool(cwd) {
|
|
|
6607
6704
|
};
|
|
6608
6705
|
}
|
|
6609
6706
|
if (isAborted()) return { content: [], details: void 0 };
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6707
|
+
if (!cwd) {
|
|
6708
|
+
return {
|
|
6709
|
+
content: [
|
|
6710
|
+
{
|
|
6711
|
+
type: "text",
|
|
6712
|
+
text: "Image search succeeded but no project directory is available to save the image."
|
|
6713
|
+
}
|
|
6714
|
+
],
|
|
6715
|
+
details: void 0
|
|
6716
|
+
};
|
|
6717
|
+
}
|
|
6718
|
+
const relativePath = save_to_file ?? defaultSavePath(query, contentType);
|
|
6719
|
+
let saved;
|
|
6720
|
+
try {
|
|
6721
|
+
if (save_to_file) {
|
|
6722
|
+
const absolutePath = resolveToCwd(relativePath, cwd);
|
|
6614
6723
|
validatePathWithinCwd(absolutePath, cwd);
|
|
6615
|
-
const binary = atob(imageData);
|
|
6616
|
-
const bytes = new Uint8Array(binary.length);
|
|
6617
|
-
for (let i = 0; i < binary.length; i++) {
|
|
6618
|
-
bytes[i] = binary.charCodeAt(i);
|
|
6619
|
-
}
|
|
6620
|
-
await fsMkdir3(dirname3(absolutePath), { recursive: true });
|
|
6621
|
-
await fsWriteFile4(absolutePath, bytes);
|
|
6622
|
-
savedPath = save_to_file;
|
|
6623
|
-
console.log(`[find_images] Saved image to ${absolutePath}`);
|
|
6624
|
-
} catch (saveError) {
|
|
6625
|
-
const errMsg = saveError instanceof Error ? saveError.message : String(saveError);
|
|
6626
|
-
return {
|
|
6627
|
-
content: [
|
|
6628
|
-
{
|
|
6629
|
-
type: "text",
|
|
6630
|
-
text: `Image was found but failed to save to "${save_to_file}": ${errMsg}`
|
|
6631
|
-
}
|
|
6632
|
-
],
|
|
6633
|
-
details: { imageData, contentType }
|
|
6634
|
-
};
|
|
6635
6724
|
}
|
|
6725
|
+
saved = await saveToolImage({
|
|
6726
|
+
imageData,
|
|
6727
|
+
cwd,
|
|
6728
|
+
relativePath,
|
|
6729
|
+
threadId,
|
|
6730
|
+
serverOrigin
|
|
6731
|
+
});
|
|
6732
|
+
console.log(`[find_images] Saved image to ${join9(cwd, saved.savedPath)}`);
|
|
6733
|
+
} catch (saveError) {
|
|
6734
|
+
const errMsg = saveError instanceof Error ? saveError.message : String(saveError);
|
|
6735
|
+
return {
|
|
6736
|
+
content: [
|
|
6737
|
+
{
|
|
6738
|
+
type: "text",
|
|
6739
|
+
text: `Image was found but failed to save to "${relativePath}": ${errMsg}`
|
|
6740
|
+
}
|
|
6741
|
+
],
|
|
6742
|
+
details: void 0
|
|
6743
|
+
};
|
|
6744
|
+
}
|
|
6745
|
+
const resultPayload = {
|
|
6746
|
+
contentType,
|
|
6747
|
+
savedPath: saved.savedPath,
|
|
6748
|
+
sizeInBytes: saved.sizeInBytes
|
|
6749
|
+
};
|
|
6750
|
+
if (saved.imageUrl) {
|
|
6751
|
+
resultPayload.imageUrl = saved.imageUrl;
|
|
6752
|
+
}
|
|
6753
|
+
return {
|
|
6754
|
+
content: [{ type: "text", text: buildGenerateImageToolText(resultPayload) }],
|
|
6755
|
+
details: saved.imageUrl ? { imageUrl: saved.imageUrl } : void 0
|
|
6756
|
+
};
|
|
6757
|
+
});
|
|
6758
|
+
}
|
|
6759
|
+
};
|
|
6760
|
+
}
|
|
6761
|
+
|
|
6762
|
+
// src/tools/browser.ts
|
|
6763
|
+
import { Type as Type16 } from "@sinclair/typebox";
|
|
6764
|
+
|
|
6765
|
+
// src/core/browser-preview-bridge.ts
|
|
6766
|
+
async function invokePreviewApi(webview, method, params) {
|
|
6767
|
+
if (!webview.rpc?.request?.evaluateJavascriptWithResponse) {
|
|
6768
|
+
throw new Error("Browser preview webview RPC is not available");
|
|
6769
|
+
}
|
|
6770
|
+
const paramsLiteral = JSON.stringify(params ?? null);
|
|
6771
|
+
const script = `return (async () => {
|
|
6772
|
+
const api = window.__tarskBrowserPreview;
|
|
6773
|
+
if (!api) {
|
|
6774
|
+
throw new Error("Browser preview API is not available");
|
|
6775
|
+
}
|
|
6776
|
+
const fn = api[${JSON.stringify(method)}];
|
|
6777
|
+
if (typeof fn !== "function") {
|
|
6778
|
+
throw new Error("Unknown browser preview API method: ${method}");
|
|
6779
|
+
}
|
|
6780
|
+
return await fn.call(api, ${paramsLiteral});
|
|
6781
|
+
})()`;
|
|
6782
|
+
return webview.rpc.request.evaluateJavascriptWithResponse({ script });
|
|
6783
|
+
}
|
|
6784
|
+
|
|
6785
|
+
// src/core/browser-native-screenshot.ts
|
|
6786
|
+
import { execFile } from "node:child_process";
|
|
6787
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
6788
|
+
import { readFile as readFile5, unlink } from "node:fs/promises";
|
|
6789
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
6790
|
+
import { join as join10 } from "node:path";
|
|
6791
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
6792
|
+
import { promisify } from "node:util";
|
|
6793
|
+
|
|
6794
|
+
// src/core/browser-view-access.ts
|
|
6795
|
+
var electrobunModule = null;
|
|
6796
|
+
var electrobunLoadAttempted = false;
|
|
6797
|
+
async function loadElectrobunBrowserView() {
|
|
6798
|
+
if (electrobunLoadAttempted) {
|
|
6799
|
+
return electrobunModule;
|
|
6800
|
+
}
|
|
6801
|
+
electrobunLoadAttempted = true;
|
|
6802
|
+
try {
|
|
6803
|
+
electrobunModule = await import("electrobun/bun");
|
|
6804
|
+
} catch {
|
|
6805
|
+
electrobunModule = null;
|
|
6806
|
+
}
|
|
6807
|
+
return electrobunModule;
|
|
6808
|
+
}
|
|
6809
|
+
function getBrowserViewById(webviewId) {
|
|
6810
|
+
if (!electrobunModule) {
|
|
6811
|
+
return null;
|
|
6812
|
+
}
|
|
6813
|
+
return electrobunModule.BrowserView.getById(webviewId) ?? null;
|
|
6814
|
+
}
|
|
6815
|
+
function getBrowserWindowById(windowId) {
|
|
6816
|
+
if (!electrobunModule) {
|
|
6817
|
+
return null;
|
|
6818
|
+
}
|
|
6819
|
+
return electrobunModule.BrowserWindow.getById(windowId) ?? null;
|
|
6820
|
+
}
|
|
6821
|
+
function waitForBrowserEvent(webview, eventName, timeoutMs) {
|
|
6822
|
+
return new Promise((resolve7, reject) => {
|
|
6823
|
+
const timeout = setTimeout(() => {
|
|
6824
|
+
reject(new Error(`Timed out waiting for ${eventName} after ${timeoutMs}ms`));
|
|
6825
|
+
}, timeoutMs);
|
|
6826
|
+
webview.on(eventName, () => {
|
|
6827
|
+
clearTimeout(timeout);
|
|
6828
|
+
resolve7();
|
|
6829
|
+
});
|
|
6830
|
+
});
|
|
6831
|
+
}
|
|
6832
|
+
|
|
6833
|
+
// src/core/browser-native-screenshot.ts
|
|
6834
|
+
var execFileAsync = promisify(execFile);
|
|
6835
|
+
var NATIVE_FOCUS_DELAY_MS = 75;
|
|
6836
|
+
var PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
6837
|
+
var SCREEN_RECORDING_HINT = "Grant Screen Recording permission to Tarsk in System Settings \u2192 Privacy & Security \u2192 Screen Recording, then restart Tarsk.";
|
|
6838
|
+
function parsePngDimensions(bytes) {
|
|
6839
|
+
if (bytes.length < 24 || !bytes.subarray(0, 8).equals(PNG_SIGNATURE)) {
|
|
6840
|
+
return null;
|
|
6841
|
+
}
|
|
6842
|
+
return {
|
|
6843
|
+
width: bytes.readUInt32BE(16),
|
|
6844
|
+
height: bytes.readUInt32BE(20)
|
|
6845
|
+
};
|
|
6846
|
+
}
|
|
6847
|
+
function pngHasImageData(bytes) {
|
|
6848
|
+
return bytes.includes(Buffer.from("IDAT")) && bytes.includes(Buffer.from("IEND"));
|
|
6849
|
+
}
|
|
6850
|
+
function parseJpegDimensions(bytes) {
|
|
6851
|
+
if (bytes.length < 4 || bytes[0] !== 255 || bytes[1] !== 216) {
|
|
6852
|
+
return null;
|
|
6853
|
+
}
|
|
6854
|
+
let offset = 2;
|
|
6855
|
+
while (offset + 9 < bytes.length) {
|
|
6856
|
+
if (bytes[offset] !== 255) {
|
|
6857
|
+
return null;
|
|
6858
|
+
}
|
|
6859
|
+
const marker = bytes[offset + 1];
|
|
6860
|
+
if (marker === 217) {
|
|
6861
|
+
break;
|
|
6862
|
+
}
|
|
6863
|
+
const segmentLength = bytes.readUInt16BE(offset + 2);
|
|
6864
|
+
if (segmentLength < 2 || offset + 2 + segmentLength > bytes.length) {
|
|
6865
|
+
return null;
|
|
6866
|
+
}
|
|
6867
|
+
const isStartOfFrame = marker === 192 || marker === 193 || marker === 194 || marker === 195;
|
|
6868
|
+
if (isStartOfFrame) {
|
|
6869
|
+
return {
|
|
6870
|
+
height: bytes.readUInt16BE(offset + 5),
|
|
6871
|
+
width: bytes.readUInt16BE(offset + 7)
|
|
6872
|
+
};
|
|
6873
|
+
}
|
|
6874
|
+
offset += 2 + segmentLength;
|
|
6875
|
+
}
|
|
6876
|
+
return null;
|
|
6877
|
+
}
|
|
6878
|
+
function isTruncatedCapture(bytes, width, height) {
|
|
6879
|
+
if (width >= 64 || height >= 64) {
|
|
6880
|
+
return bytes.length < 1024;
|
|
6881
|
+
}
|
|
6882
|
+
return bytes.length < 68;
|
|
6883
|
+
}
|
|
6884
|
+
function validateCapturedImage(bytes, format) {
|
|
6885
|
+
if (bytes.length === 0) {
|
|
6886
|
+
return { error: "macOS screencapture produced an empty image file." };
|
|
6887
|
+
}
|
|
6888
|
+
if (format === "png") {
|
|
6889
|
+
const dimensions2 = parsePngDimensions(bytes);
|
|
6890
|
+
if (!dimensions2) {
|
|
6891
|
+
return { error: "macOS screencapture produced an invalid PNG file." };
|
|
6892
|
+
}
|
|
6893
|
+
if (!pngHasImageData(bytes) || isTruncatedCapture(bytes, dimensions2.width, dimensions2.height)) {
|
|
6894
|
+
return {
|
|
6895
|
+
error: `macOS screencapture wrote a truncated PNG (${bytes.length} bytes for ${dimensions2.width}x${dimensions2.height}). ` + SCREEN_RECORDING_HINT
|
|
6896
|
+
};
|
|
6897
|
+
}
|
|
6898
|
+
return { ...dimensions2, format: "png" };
|
|
6899
|
+
}
|
|
6900
|
+
const dimensions = parseJpegDimensions(bytes);
|
|
6901
|
+
if (!dimensions) {
|
|
6902
|
+
return { error: "macOS screencapture produced an invalid JPEG file." };
|
|
6903
|
+
}
|
|
6904
|
+
if (isTruncatedCapture(bytes, dimensions.width, dimensions.height)) {
|
|
6905
|
+
return {
|
|
6906
|
+
error: `macOS screencapture wrote a truncated JPEG (${bytes.length} bytes for ${dimensions.width}x${dimensions.height}). ` + SCREEN_RECORDING_HINT
|
|
6907
|
+
};
|
|
6908
|
+
}
|
|
6909
|
+
return { ...dimensions, format: "jpeg" };
|
|
6910
|
+
}
|
|
6911
|
+
function isValidRect(rect) {
|
|
6912
|
+
return Number.isFinite(rect.x) && Number.isFinite(rect.y) && rect.width > 0 && rect.height > 0;
|
|
6913
|
+
}
|
|
6914
|
+
async function resolveViewFrame(view) {
|
|
6915
|
+
if (view.getFrame) {
|
|
6916
|
+
return await view.getFrame();
|
|
6917
|
+
}
|
|
6918
|
+
if (view.frame) {
|
|
6919
|
+
return view.frame;
|
|
6920
|
+
}
|
|
6921
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
6922
|
+
}
|
|
6923
|
+
async function captureMacOSRect(options) {
|
|
6924
|
+
const formatFlag = options.format === "jpeg" ? "jpg" : "png";
|
|
6925
|
+
const region = `${Math.round(options.rect.x)},${Math.round(options.rect.y)},${Math.round(options.rect.width)},${Math.round(options.rect.height)}`;
|
|
6926
|
+
await execFileAsync("screencapture", ["-x", "-t", formatFlag, "-R", region, options.path]);
|
|
6927
|
+
}
|
|
6928
|
+
async function resolveCaptureRect(webviewId) {
|
|
6929
|
+
const view = getBrowserViewById(webviewId);
|
|
6930
|
+
if (!view) {
|
|
6931
|
+
throw new Error(`Browser preview webview ${webviewId} is not available`);
|
|
6932
|
+
}
|
|
6933
|
+
const window = getBrowserWindowById(view.windowId);
|
|
6934
|
+
if (!window) {
|
|
6935
|
+
throw new Error(`Electrobun window ${view.windowId} is not available for screenshot capture`);
|
|
6936
|
+
}
|
|
6937
|
+
const windowFrame = window.getFrame();
|
|
6938
|
+
const viewFrame = await resolveViewFrame(view);
|
|
6939
|
+
if (!isValidRect(windowFrame)) {
|
|
6940
|
+
throw new Error(`Window ${window.id} does not expose a usable frame for screenshots`);
|
|
6941
|
+
}
|
|
6942
|
+
if (!isValidRect(viewFrame)) {
|
|
6943
|
+
throw new Error(`View ${view.id} does not expose a usable frame for screenshots`);
|
|
6944
|
+
}
|
|
6945
|
+
return {
|
|
6946
|
+
rect: {
|
|
6947
|
+
x: windowFrame.x + viewFrame.x,
|
|
6948
|
+
y: windowFrame.y + viewFrame.y,
|
|
6949
|
+
width: viewFrame.width,
|
|
6950
|
+
height: viewFrame.height
|
|
6951
|
+
},
|
|
6952
|
+
viewId: view.id,
|
|
6953
|
+
windowId: window.id
|
|
6954
|
+
};
|
|
6955
|
+
}
|
|
6956
|
+
async function captureNativeBrowserScreenshot(webviewId, format) {
|
|
6957
|
+
if (process.platform !== "darwin") {
|
|
6958
|
+
return {
|
|
6959
|
+
ok: false,
|
|
6960
|
+
reason: "Native Electrobun screenshots are only supported on macOS."
|
|
6961
|
+
};
|
|
6962
|
+
}
|
|
6963
|
+
const outputPath = join10(
|
|
6964
|
+
tmpdir2(),
|
|
6965
|
+
`tarsk-browser-screenshot-${randomUUID3()}.${format === "jpeg" ? "jpg" : "png"}`
|
|
6966
|
+
);
|
|
6967
|
+
try {
|
|
6968
|
+
const target = await resolveCaptureRect(webviewId);
|
|
6969
|
+
const window = getBrowserWindowById(target.windowId);
|
|
6970
|
+
window?.focus();
|
|
6971
|
+
await sleep(NATIVE_FOCUS_DELAY_MS);
|
|
6972
|
+
await captureMacOSRect({
|
|
6973
|
+
format,
|
|
6974
|
+
path: outputPath,
|
|
6975
|
+
rect: target.rect
|
|
6976
|
+
});
|
|
6977
|
+
const imageBytes = await readFile5(outputPath);
|
|
6978
|
+
const validated = validateCapturedImage(imageBytes, format);
|
|
6979
|
+
if ("error" in validated) {
|
|
6980
|
+
return {
|
|
6981
|
+
ok: false,
|
|
6982
|
+
reason: validated.error
|
|
6983
|
+
};
|
|
6984
|
+
}
|
|
6985
|
+
return {
|
|
6986
|
+
ok: true,
|
|
6987
|
+
result: {
|
|
6988
|
+
format: validated.format,
|
|
6989
|
+
width: validated.width,
|
|
6990
|
+
height: validated.height,
|
|
6991
|
+
image_data: imageBytes.toString("base64"),
|
|
6992
|
+
capture_method: "native-screencapture",
|
|
6993
|
+
window_id: target.windowId,
|
|
6994
|
+
view_id: target.viewId
|
|
6995
|
+
}
|
|
6996
|
+
};
|
|
6997
|
+
} catch (error) {
|
|
6998
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6999
|
+
return {
|
|
7000
|
+
ok: false,
|
|
7001
|
+
reason: `${message} Native screenshots require a visible on-screen preview. ${SCREEN_RECORDING_HINT}`
|
|
7002
|
+
};
|
|
7003
|
+
} finally {
|
|
7004
|
+
await unlink(outputPath).catch(() => void 0);
|
|
7005
|
+
}
|
|
7006
|
+
}
|
|
7007
|
+
|
|
7008
|
+
// src/tools/browser.ts
|
|
7009
|
+
var browserSchema = Type16.Object({
|
|
7010
|
+
action: Type16.Union(
|
|
7011
|
+
[
|
|
7012
|
+
Type16.Literal("open_url"),
|
|
7013
|
+
Type16.Literal("evaluate_javascript"),
|
|
7014
|
+
Type16.Literal("find_in_page"),
|
|
7015
|
+
Type16.Literal("stop_find_in_page"),
|
|
7016
|
+
Type16.Literal("reload"),
|
|
7017
|
+
Type16.Literal("go_back"),
|
|
7018
|
+
Type16.Literal("go_forward"),
|
|
7019
|
+
Type16.Literal("get_state"),
|
|
7020
|
+
Type16.Literal("screenshot"),
|
|
7021
|
+
Type16.Literal("set_viewport"),
|
|
7022
|
+
Type16.Literal("snapshot"),
|
|
7023
|
+
Type16.Literal("click"),
|
|
7024
|
+
Type16.Literal("fill"),
|
|
7025
|
+
Type16.Literal("press"),
|
|
7026
|
+
Type16.Literal("hover"),
|
|
7027
|
+
Type16.Literal("wait_for"),
|
|
7028
|
+
Type16.Literal("get_console_logs")
|
|
7029
|
+
],
|
|
7030
|
+
{
|
|
7031
|
+
description: "Browser action. Navigation: open_url, reload, go_back, go_forward, get_state. Inspection: snapshot, screenshot, get_console_logs, find_in_page, stop_find_in_page. Interaction: click, fill, press, hover, wait_for, evaluate_javascript. Layout: set_viewport."
|
|
7032
|
+
}
|
|
7033
|
+
),
|
|
7034
|
+
url: Type16.Optional(
|
|
7035
|
+
Type16.String({
|
|
7036
|
+
description: "URL to open in the Browser tab preview (required for open_url)."
|
|
7037
|
+
})
|
|
7038
|
+
),
|
|
7039
|
+
script: Type16.Optional(
|
|
7040
|
+
Type16.String({
|
|
7041
|
+
description: "JavaScript to execute in the preview page context (required for evaluate_javascript)."
|
|
7042
|
+
})
|
|
7043
|
+
),
|
|
7044
|
+
text: Type16.Optional(
|
|
7045
|
+
Type16.String({
|
|
7046
|
+
description: "Text to find in the page (required for find_in_page)."
|
|
7047
|
+
})
|
|
7048
|
+
),
|
|
7049
|
+
forward: Type16.Optional(
|
|
7050
|
+
Type16.Boolean({
|
|
7051
|
+
description: "Search forward through matches for find_in_page (default true)."
|
|
7052
|
+
})
|
|
7053
|
+
),
|
|
7054
|
+
match_case: Type16.Optional(
|
|
7055
|
+
Type16.Boolean({
|
|
7056
|
+
description: "Case-sensitive search for find_in_page (default false)."
|
|
7057
|
+
})
|
|
7058
|
+
),
|
|
7059
|
+
wait_for_load_ms: Type16.Optional(
|
|
7060
|
+
Type16.Number({
|
|
7061
|
+
description: "Milliseconds to wait for the dom-ready event after navigation (default 5000, max 30000)."
|
|
7062
|
+
})
|
|
7063
|
+
),
|
|
7064
|
+
preset: Type16.Optional(
|
|
7065
|
+
Type16.Union([Type16.Literal("desktop"), Type16.Literal("tablet"), Type16.Literal("mobile")], {
|
|
7066
|
+
description: "Viewport preset for set_viewport."
|
|
7067
|
+
})
|
|
7068
|
+
),
|
|
7069
|
+
format: Type16.Optional(
|
|
7070
|
+
Type16.Union([Type16.Literal("png"), Type16.Literal("jpeg")], {
|
|
7071
|
+
description: "Image format for screenshot (default png)."
|
|
7072
|
+
})
|
|
7073
|
+
),
|
|
7074
|
+
ref: Type16.Optional(
|
|
7075
|
+
Type16.String({
|
|
7076
|
+
description: "Element ref from snapshot for click, fill, or hover (e.g. @e1)."
|
|
7077
|
+
})
|
|
7078
|
+
),
|
|
7079
|
+
value: Type16.Optional(
|
|
7080
|
+
Type16.String({
|
|
7081
|
+
description: "Value to type for fill."
|
|
7082
|
+
})
|
|
7083
|
+
),
|
|
7084
|
+
key: Type16.Optional(
|
|
7085
|
+
Type16.String({
|
|
7086
|
+
description: "Key to press for press (e.g. Enter, Tab, Escape)."
|
|
7087
|
+
})
|
|
7088
|
+
),
|
|
7089
|
+
selector: Type16.Optional(
|
|
7090
|
+
Type16.String({
|
|
7091
|
+
description: "CSS selector to wait for in wait_for."
|
|
7092
|
+
})
|
|
7093
|
+
),
|
|
7094
|
+
timeout_ms: Type16.Optional(
|
|
7095
|
+
Type16.Number({
|
|
7096
|
+
description: "Timeout in milliseconds for wait_for (default 10000, max 60000)."
|
|
7097
|
+
})
|
|
7098
|
+
),
|
|
7099
|
+
interactive_only: Type16.Optional(
|
|
7100
|
+
Type16.Boolean({
|
|
7101
|
+
description: "Only include interactive elements in snapshot (default false)."
|
|
7102
|
+
})
|
|
7103
|
+
),
|
|
7104
|
+
max_depth: Type16.Optional(
|
|
7105
|
+
Type16.Number({
|
|
7106
|
+
description: "Maximum DOM depth for snapshot (default 15)."
|
|
7107
|
+
})
|
|
7108
|
+
),
|
|
7109
|
+
since_ms: Type16.Optional(
|
|
7110
|
+
Type16.Number({
|
|
7111
|
+
description: "Only return console logs from the last N milliseconds for get_console_logs."
|
|
7112
|
+
})
|
|
7113
|
+
),
|
|
7114
|
+
levels: Type16.Optional(
|
|
7115
|
+
Type16.Array(Type16.String(), {
|
|
7116
|
+
description: 'Console levels to include for get_console_logs (e.g. ["error", "warn"]).'
|
|
7117
|
+
})
|
|
7118
|
+
),
|
|
7119
|
+
clear: Type16.Optional(
|
|
7120
|
+
Type16.Boolean({
|
|
7121
|
+
description: "Clear buffered console logs after reading for get_console_logs (default false)."
|
|
7122
|
+
})
|
|
7123
|
+
)
|
|
7124
|
+
});
|
|
7125
|
+
var pendingBrowserTasks = /* @__PURE__ */ new Map();
|
|
7126
|
+
var bufferedWebviewIds = /* @__PURE__ */ new Map();
|
|
7127
|
+
var pendingBrowserActionResults = /* @__PURE__ */ new Map();
|
|
7128
|
+
var bufferedBrowserActionResults = /* @__PURE__ */ new Map();
|
|
7129
|
+
var DEFAULT_WAIT_FOR_LOAD_MS = 5e3;
|
|
7130
|
+
var MAX_WAIT_FOR_LOAD_MS = 3e4;
|
|
7131
|
+
var DEFAULT_WEBVIEW_READY_TIMEOUT_MS = 2e4;
|
|
7132
|
+
var DEFAULT_WAIT_FOR_TIMEOUT_MS = 1e4;
|
|
7133
|
+
var MAX_WAIT_FOR_TIMEOUT_MS = 6e4;
|
|
7134
|
+
var DEFAULT_BROWSER_ACTION_TIMEOUT_MS = 2e4;
|
|
7135
|
+
function submitBrowserWebviewReady(toolCallId, webviewId) {
|
|
7136
|
+
const pending = pendingBrowserTasks.get(toolCallId);
|
|
7137
|
+
if (pending) {
|
|
7138
|
+
pending.resolve(webviewId);
|
|
7139
|
+
pendingBrowserTasks.delete(toolCallId);
|
|
7140
|
+
return true;
|
|
7141
|
+
}
|
|
7142
|
+
bufferedWebviewIds.set(toolCallId, webviewId);
|
|
7143
|
+
return true;
|
|
7144
|
+
}
|
|
7145
|
+
function submitBrowserActionResult(toolCallId, result) {
|
|
7146
|
+
const pending = pendingBrowserActionResults.get(toolCallId);
|
|
7147
|
+
if (pending) {
|
|
7148
|
+
pending.resolve(result);
|
|
7149
|
+
pendingBrowserActionResults.delete(toolCallId);
|
|
7150
|
+
return true;
|
|
7151
|
+
}
|
|
7152
|
+
bufferedBrowserActionResults.set(toolCallId, result);
|
|
7153
|
+
return true;
|
|
7154
|
+
}
|
|
7155
|
+
function cancelPendingBrowserTask(toolCallId) {
|
|
7156
|
+
const pending = pendingBrowserTasks.get(toolCallId);
|
|
7157
|
+
if (!pending) return false;
|
|
7158
|
+
pending.reject(new Error("Browser tool execution cancelled"));
|
|
7159
|
+
pendingBrowserTasks.delete(toolCallId);
|
|
7160
|
+
return true;
|
|
7161
|
+
}
|
|
7162
|
+
function cancelPendingBrowserActionResult(toolCallId) {
|
|
7163
|
+
const pending = pendingBrowserActionResults.get(toolCallId);
|
|
7164
|
+
if (!pending) return false;
|
|
7165
|
+
pending.reject(new Error("Browser tool execution cancelled"));
|
|
7166
|
+
pendingBrowserActionResults.delete(toolCallId);
|
|
7167
|
+
return true;
|
|
7168
|
+
}
|
|
7169
|
+
function normalizeUrl(url) {
|
|
7170
|
+
const trimmed = url.trim();
|
|
7171
|
+
if (!trimmed) {
|
|
7172
|
+
throw new Error("url must not be empty");
|
|
7173
|
+
}
|
|
7174
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
7175
|
+
return trimmed;
|
|
7176
|
+
}
|
|
7177
|
+
return `http://${trimmed}`;
|
|
7178
|
+
}
|
|
7179
|
+
function waitForPreviewWebview(toolCallId, signal, timeoutMs = DEFAULT_WEBVIEW_READY_TIMEOUT_MS) {
|
|
7180
|
+
const buffered = bufferedWebviewIds.get(toolCallId);
|
|
7181
|
+
if (buffered !== void 0) {
|
|
7182
|
+
bufferedWebviewIds.delete(toolCallId);
|
|
7183
|
+
return Promise.resolve(buffered);
|
|
7184
|
+
}
|
|
7185
|
+
return new Promise((resolve7, reject) => {
|
|
7186
|
+
if (signal?.aborted) {
|
|
7187
|
+
reject(new Error("Operation aborted"));
|
|
7188
|
+
return;
|
|
7189
|
+
}
|
|
7190
|
+
let timeoutHandle = null;
|
|
7191
|
+
const pending = pendingBrowserTasks.get(toolCallId);
|
|
7192
|
+
const onAbort = () => {
|
|
7193
|
+
cancelPendingBrowserTask(toolCallId);
|
|
7194
|
+
};
|
|
7195
|
+
function cleanup() {
|
|
7196
|
+
signal?.removeEventListener("abort", onAbort);
|
|
7197
|
+
if (timeoutHandle) {
|
|
7198
|
+
clearTimeout(timeoutHandle);
|
|
7199
|
+
}
|
|
7200
|
+
}
|
|
7201
|
+
const wrappedResolve = (webviewId) => {
|
|
7202
|
+
cleanup();
|
|
7203
|
+
resolve7(webviewId);
|
|
7204
|
+
};
|
|
7205
|
+
const wrappedReject = (error) => {
|
|
7206
|
+
cleanup();
|
|
7207
|
+
reject(error);
|
|
7208
|
+
};
|
|
7209
|
+
if (pending) {
|
|
7210
|
+
pending.resolve = wrappedResolve;
|
|
7211
|
+
pending.reject = wrappedReject;
|
|
7212
|
+
} else {
|
|
7213
|
+
pendingBrowserTasks.set(toolCallId, {
|
|
7214
|
+
resolve: wrappedResolve,
|
|
7215
|
+
reject: wrappedReject
|
|
7216
|
+
});
|
|
7217
|
+
}
|
|
7218
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
7219
|
+
timeoutHandle = setTimeout(() => {
|
|
7220
|
+
if (!pendingBrowserTasks.has(toolCallId)) return;
|
|
7221
|
+
pendingBrowserTasks.delete(toolCallId);
|
|
7222
|
+
wrappedReject(
|
|
7223
|
+
new Error(
|
|
7224
|
+
`browser tool timed out after ${timeoutMs}ms waiting for the Browser tab preview webview`
|
|
7225
|
+
)
|
|
7226
|
+
);
|
|
7227
|
+
}, timeoutMs);
|
|
7228
|
+
});
|
|
7229
|
+
}
|
|
7230
|
+
function waitForBrowserActionResult(toolCallId, signal, timeoutMs = DEFAULT_BROWSER_ACTION_TIMEOUT_MS) {
|
|
7231
|
+
const buffered = bufferedBrowserActionResults.get(toolCallId);
|
|
7232
|
+
if (buffered) {
|
|
7233
|
+
bufferedBrowserActionResults.delete(toolCallId);
|
|
7234
|
+
return Promise.resolve(buffered);
|
|
7235
|
+
}
|
|
7236
|
+
return new Promise((resolve7, reject) => {
|
|
7237
|
+
if (signal?.aborted) {
|
|
7238
|
+
reject(new Error("Operation aborted"));
|
|
7239
|
+
return;
|
|
7240
|
+
}
|
|
7241
|
+
let timeoutHandle = null;
|
|
7242
|
+
const onAbort = () => {
|
|
7243
|
+
cancelPendingBrowserActionResult(toolCallId);
|
|
7244
|
+
};
|
|
7245
|
+
function cleanup() {
|
|
7246
|
+
signal?.removeEventListener("abort", onAbort);
|
|
7247
|
+
if (timeoutHandle) {
|
|
7248
|
+
clearTimeout(timeoutHandle);
|
|
7249
|
+
}
|
|
7250
|
+
}
|
|
7251
|
+
const wrappedResolve = (result) => {
|
|
7252
|
+
cleanup();
|
|
7253
|
+
resolve7(result);
|
|
7254
|
+
};
|
|
7255
|
+
const wrappedReject = (error) => {
|
|
7256
|
+
cleanup();
|
|
7257
|
+
reject(error);
|
|
7258
|
+
};
|
|
7259
|
+
pendingBrowserActionResults.set(toolCallId, {
|
|
7260
|
+
resolve: wrappedResolve,
|
|
7261
|
+
reject: wrappedReject
|
|
7262
|
+
});
|
|
7263
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
7264
|
+
timeoutHandle = setTimeout(() => {
|
|
7265
|
+
if (!pendingBrowserActionResults.has(toolCallId)) return;
|
|
7266
|
+
pendingBrowserActionResults.delete(toolCallId);
|
|
7267
|
+
wrappedReject(
|
|
7268
|
+
new Error(
|
|
7269
|
+
`browser tool timed out after ${timeoutMs}ms waiting for the Browser tab host action`
|
|
7270
|
+
)
|
|
7271
|
+
);
|
|
7272
|
+
}, timeoutMs);
|
|
7273
|
+
});
|
|
7274
|
+
}
|
|
7275
|
+
function logBrowserPreviewFailure(action, webviewId, webview, error, context) {
|
|
7276
|
+
console.error(`[browser] ${action} failed`, {
|
|
7277
|
+
webview_id: webviewId,
|
|
7278
|
+
current_url: webview.url,
|
|
7279
|
+
...context,
|
|
7280
|
+
error: error instanceof Error ? error.message : String(error),
|
|
7281
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
7282
|
+
});
|
|
7283
|
+
}
|
|
7284
|
+
async function executeBrowserAction(webviewId, input) {
|
|
7285
|
+
const webview = getBrowserViewById(webviewId);
|
|
7286
|
+
if (!webview) {
|
|
7287
|
+
throw new Error(`Browser preview webview ${webviewId} is not available`);
|
|
7288
|
+
}
|
|
7289
|
+
switch (input.action) {
|
|
7290
|
+
case "open_url": {
|
|
7291
|
+
if (!input.url?.trim()) {
|
|
7292
|
+
throw new Error("url is required for open_url");
|
|
7293
|
+
}
|
|
7294
|
+
const url = normalizeUrl(input.url);
|
|
7295
|
+
const waitMs = Math.min(
|
|
7296
|
+
Math.max(input.wait_for_load_ms ?? DEFAULT_WAIT_FOR_LOAD_MS, 0),
|
|
7297
|
+
MAX_WAIT_FOR_LOAD_MS
|
|
7298
|
+
);
|
|
7299
|
+
const domReadyPromise = waitForBrowserEvent(webview, "dom-ready", waitMs);
|
|
7300
|
+
webview.loadURL(url);
|
|
7301
|
+
let domReady = false;
|
|
7302
|
+
try {
|
|
7303
|
+
await domReadyPromise;
|
|
7304
|
+
domReady = true;
|
|
7305
|
+
} catch {
|
|
7306
|
+
}
|
|
7307
|
+
return {
|
|
7308
|
+
action: "open_url",
|
|
7309
|
+
url,
|
|
7310
|
+
webview_id: webviewId,
|
|
7311
|
+
current_url: webview.url,
|
|
7312
|
+
dom_ready: domReady
|
|
7313
|
+
};
|
|
7314
|
+
}
|
|
7315
|
+
case "evaluate_javascript": {
|
|
7316
|
+
if (!input.script?.trim()) {
|
|
7317
|
+
throw new Error("script is required for evaluate_javascript");
|
|
7318
|
+
}
|
|
7319
|
+
if (!webview.rpc?.request?.evaluateJavascriptWithResponse) {
|
|
7320
|
+
throw new Error("Browser preview webview RPC is not available for JavaScript execution");
|
|
7321
|
+
}
|
|
7322
|
+
const result = await webview.rpc.request.evaluateJavascriptWithResponse({
|
|
7323
|
+
script: input.script
|
|
7324
|
+
});
|
|
7325
|
+
return {
|
|
7326
|
+
action: "evaluate_javascript",
|
|
7327
|
+
webview_id: webviewId,
|
|
7328
|
+
result
|
|
7329
|
+
};
|
|
7330
|
+
}
|
|
7331
|
+
case "find_in_page": {
|
|
7332
|
+
if (!input.text?.trim()) {
|
|
7333
|
+
throw new Error("text is required for find_in_page");
|
|
7334
|
+
}
|
|
7335
|
+
webview.findInPage(input.text, {
|
|
7336
|
+
forward: input.forward ?? true,
|
|
7337
|
+
matchCase: input.match_case ?? false
|
|
7338
|
+
});
|
|
7339
|
+
return {
|
|
7340
|
+
action: "find_in_page",
|
|
7341
|
+
webview_id: webviewId,
|
|
7342
|
+
text: input.text,
|
|
7343
|
+
forward: input.forward ?? true,
|
|
7344
|
+
match_case: input.match_case ?? false
|
|
7345
|
+
};
|
|
7346
|
+
}
|
|
7347
|
+
case "stop_find_in_page": {
|
|
7348
|
+
webview.stopFindInPage();
|
|
7349
|
+
return {
|
|
7350
|
+
action: "stop_find_in_page",
|
|
7351
|
+
webview_id: webviewId
|
|
7352
|
+
};
|
|
7353
|
+
}
|
|
7354
|
+
case "snapshot": {
|
|
7355
|
+
const result = await invokePreviewApi(webview, "snapshot", {
|
|
7356
|
+
interactive_only: input.interactive_only ?? false,
|
|
7357
|
+
max_depth: input.max_depth ?? 15
|
|
7358
|
+
});
|
|
7359
|
+
return {
|
|
7360
|
+
action: "snapshot",
|
|
7361
|
+
webview_id: webviewId,
|
|
7362
|
+
...typeof result === "object" && result !== null ? result : { result }
|
|
7363
|
+
};
|
|
7364
|
+
}
|
|
7365
|
+
case "click": {
|
|
7366
|
+
if (!input.ref?.trim()) {
|
|
7367
|
+
throw new Error("ref is required for click");
|
|
7368
|
+
}
|
|
7369
|
+
const result = await invokePreviewApi(webview, "click", { ref: input.ref });
|
|
7370
|
+
return {
|
|
7371
|
+
action: "click",
|
|
7372
|
+
webview_id: webviewId,
|
|
7373
|
+
ref: input.ref,
|
|
7374
|
+
result
|
|
7375
|
+
};
|
|
7376
|
+
}
|
|
7377
|
+
case "fill": {
|
|
7378
|
+
if (!input.ref?.trim()) {
|
|
7379
|
+
throw new Error("ref is required for fill");
|
|
7380
|
+
}
|
|
7381
|
+
if (input.value === void 0) {
|
|
7382
|
+
throw new Error("value is required for fill");
|
|
7383
|
+
}
|
|
7384
|
+
const result = await invokePreviewApi(webview, "fill", {
|
|
7385
|
+
ref: input.ref,
|
|
7386
|
+
value: input.value
|
|
7387
|
+
});
|
|
7388
|
+
return {
|
|
7389
|
+
action: "fill",
|
|
7390
|
+
webview_id: webviewId,
|
|
7391
|
+
ref: input.ref,
|
|
7392
|
+
value: input.value,
|
|
7393
|
+
result
|
|
7394
|
+
};
|
|
7395
|
+
}
|
|
7396
|
+
case "press": {
|
|
7397
|
+
if (!input.key?.trim()) {
|
|
7398
|
+
throw new Error("key is required for press");
|
|
7399
|
+
}
|
|
7400
|
+
const result = await invokePreviewApi(webview, "press", { key: input.key });
|
|
7401
|
+
return {
|
|
7402
|
+
action: "press",
|
|
7403
|
+
webview_id: webviewId,
|
|
7404
|
+
key: input.key,
|
|
7405
|
+
result
|
|
7406
|
+
};
|
|
7407
|
+
}
|
|
7408
|
+
case "hover": {
|
|
7409
|
+
if (!input.ref?.trim()) {
|
|
7410
|
+
throw new Error("ref is required for hover");
|
|
7411
|
+
}
|
|
7412
|
+
const result = await invokePreviewApi(webview, "hover", { ref: input.ref });
|
|
7413
|
+
return {
|
|
7414
|
+
action: "hover",
|
|
7415
|
+
webview_id: webviewId,
|
|
7416
|
+
ref: input.ref,
|
|
7417
|
+
result
|
|
7418
|
+
};
|
|
7419
|
+
}
|
|
7420
|
+
case "wait_for": {
|
|
7421
|
+
if (!input.selector?.trim() && !input.text?.trim()) {
|
|
7422
|
+
throw new Error("selector or text is required for wait_for");
|
|
7423
|
+
}
|
|
7424
|
+
const timeoutMs = Math.min(
|
|
7425
|
+
Math.max(input.timeout_ms ?? DEFAULT_WAIT_FOR_TIMEOUT_MS, 0),
|
|
7426
|
+
MAX_WAIT_FOR_TIMEOUT_MS
|
|
7427
|
+
);
|
|
7428
|
+
const result = await invokePreviewApi(webview, "waitFor", {
|
|
7429
|
+
selector: input.selector,
|
|
7430
|
+
text: input.text,
|
|
7431
|
+
timeout_ms: timeoutMs
|
|
7432
|
+
});
|
|
7433
|
+
return {
|
|
7434
|
+
action: "wait_for",
|
|
7435
|
+
webview_id: webviewId,
|
|
7436
|
+
selector: input.selector,
|
|
7437
|
+
text: input.text,
|
|
7438
|
+
timeout_ms: timeoutMs,
|
|
7439
|
+
result
|
|
7440
|
+
};
|
|
7441
|
+
}
|
|
7442
|
+
case "get_console_logs": {
|
|
7443
|
+
const result = await invokePreviewApi(webview, "getConsoleLogs", {
|
|
7444
|
+
since_ms: input.since_ms ?? 0,
|
|
7445
|
+
levels: input.levels,
|
|
7446
|
+
clear: input.clear ?? false
|
|
7447
|
+
});
|
|
7448
|
+
return {
|
|
7449
|
+
action: "get_console_logs",
|
|
7450
|
+
webview_id: webviewId,
|
|
7451
|
+
...typeof result === "object" && result !== null ? result : { result }
|
|
7452
|
+
};
|
|
7453
|
+
}
|
|
7454
|
+
case "screenshot": {
|
|
7455
|
+
const format = input.format ?? "png";
|
|
7456
|
+
const nativeCapture = await captureNativeBrowserScreenshot(webviewId, format);
|
|
7457
|
+
if (nativeCapture.ok) {
|
|
7458
|
+
return {
|
|
7459
|
+
action: "screenshot",
|
|
7460
|
+
webview_id: webviewId,
|
|
7461
|
+
...nativeCapture.result
|
|
7462
|
+
};
|
|
7463
|
+
}
|
|
7464
|
+
try {
|
|
7465
|
+
const result = await invokePreviewApi(webview, "takeScreenshot", { format });
|
|
7466
|
+
return {
|
|
7467
|
+
action: "screenshot",
|
|
7468
|
+
webview_id: webviewId,
|
|
7469
|
+
capture_method: "svg-foreign-object",
|
|
7470
|
+
native_capture_unavailable: nativeCapture.reason,
|
|
7471
|
+
...typeof result === "object" && result !== null ? result : { result }
|
|
7472
|
+
};
|
|
7473
|
+
} catch (error) {
|
|
7474
|
+
logBrowserPreviewFailure("screenshot", webviewId, webview, error, {
|
|
7475
|
+
format,
|
|
7476
|
+
capture_method: "svg-foreign-object",
|
|
7477
|
+
native_capture_unavailable: nativeCapture.reason,
|
|
7478
|
+
troubleshooting: "Native macOS capture uses screencapture on the visible preview rect. If native capture fails, grant Screen Recording permission to Tarsk and restart the app. SVG fallback can fail on large third-party pages. Check tarsk-logs.txt for [browser-preview] takeScreenshot failed entries."
|
|
7479
|
+
});
|
|
7480
|
+
throw error;
|
|
7481
|
+
}
|
|
7482
|
+
}
|
|
7483
|
+
default:
|
|
7484
|
+
throw new Error(`Unsupported browser action: ${String(input.action)}`);
|
|
7485
|
+
}
|
|
7486
|
+
}
|
|
7487
|
+
function formatBrowserResult(payload) {
|
|
7488
|
+
if (payload.action === "screenshot") {
|
|
7489
|
+
const display = stripScreenshotPayload(payload);
|
|
7490
|
+
return {
|
|
7491
|
+
text: buildGenerateImageToolText(display),
|
|
7492
|
+
details: payload
|
|
7493
|
+
};
|
|
7494
|
+
}
|
|
7495
|
+
return {
|
|
7496
|
+
text: JSON.stringify(payload, null, 2),
|
|
7497
|
+
details: payload
|
|
7498
|
+
};
|
|
7499
|
+
}
|
|
7500
|
+
function stripScreenshotPayload(payload) {
|
|
7501
|
+
const display = {};
|
|
7502
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
7503
|
+
if (key === "image_data") continue;
|
|
7504
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
7505
|
+
display[key] = value;
|
|
7506
|
+
}
|
|
7507
|
+
}
|
|
7508
|
+
return display;
|
|
7509
|
+
}
|
|
7510
|
+
async function persistScreenshotResult(payload, options) {
|
|
7511
|
+
const imageData = payload.image_data;
|
|
7512
|
+
if (typeof imageData !== "string" || !imageData) {
|
|
7513
|
+
return payload;
|
|
7514
|
+
}
|
|
7515
|
+
const sizeInBytes = decodeBase64ImageData(imageData).length;
|
|
7516
|
+
const { image_data: _removed, ...rest } = payload;
|
|
7517
|
+
if (!options.cwd) {
|
|
7518
|
+
return { ...rest, sizeInBytes };
|
|
7519
|
+
}
|
|
7520
|
+
const format = payload.format === "jpeg" ? "jpeg" : "png";
|
|
7521
|
+
const extension = format === "jpeg" ? "jpg" : "png";
|
|
7522
|
+
const id = Math.random().toString(36).slice(2, 8);
|
|
7523
|
+
const relativePath = buildTmpImagePath(`screenshot-${id}.${extension}`);
|
|
7524
|
+
const saved = await saveToolImage({
|
|
7525
|
+
imageData,
|
|
7526
|
+
cwd: options.cwd,
|
|
7527
|
+
relativePath,
|
|
7528
|
+
threadId: options.threadId,
|
|
7529
|
+
serverOrigin: options.serverOrigin
|
|
7530
|
+
});
|
|
7531
|
+
return {
|
|
7532
|
+
...rest,
|
|
7533
|
+
savedPath: saved.savedPath,
|
|
7534
|
+
sizeInBytes: saved.sizeInBytes,
|
|
7535
|
+
...saved.imageUrl ? { imageUrl: saved.imageUrl } : {}
|
|
7536
|
+
};
|
|
7537
|
+
}
|
|
7538
|
+
var BROWSER_TOOL_DESCRIPTION = "Control the in-app Browser. Use to read information from websites \u2014 open_url then snapshot for page content and structure, get_state for URL/title, find_in_page to locate text, evaluate_javascript to extract data, or screenshot for visual reference. Also use for interacting with pages: click, fill, press, hover, wait_for. Actions: open_url, reload, go_back, go_forward, get_state, set_viewport, screenshot, snapshot, click, fill, press, hover, wait_for, get_console_logs, evaluate_javascript, find_in_page, stop_find_in_page. Use snapshot to get @eN element refs, then click/fill/hover by ref. The Browser tab opens automatically. Start the dev server first when testing localhost apps.";
|
|
7539
|
+
function createBrowserTool(options = {}) {
|
|
7540
|
+
const serverOrigin = options.serverOrigin ?? getServerOrigin();
|
|
7541
|
+
return {
|
|
7542
|
+
name: "browser",
|
|
7543
|
+
label: "browser",
|
|
7544
|
+
description: BROWSER_TOOL_DESCRIPTION,
|
|
7545
|
+
parameters: browserSchema,
|
|
7546
|
+
execute: async (toolCallId, input, signal) => {
|
|
7547
|
+
return withAbortSignal(signal, async () => {
|
|
7548
|
+
const electrobun = await loadElectrobunBrowserView();
|
|
7549
|
+
if (!electrobun) {
|
|
7550
|
+
throw new Error(
|
|
7551
|
+
"browser tool is only available in the Tarsk desktop app (Electrobun), not in server-only mode"
|
|
7552
|
+
);
|
|
7553
|
+
}
|
|
7554
|
+
if (isBrowserHostAction(input.action)) {
|
|
7555
|
+
const result2 = await waitForBrowserActionResult(toolCallId, signal);
|
|
7556
|
+
const formatted2 = formatBrowserResult(result2);
|
|
7557
|
+
return {
|
|
7558
|
+
content: [{ type: "text", text: formatted2.text }],
|
|
7559
|
+
details: formatted2.details
|
|
7560
|
+
};
|
|
6636
7561
|
}
|
|
6637
|
-
const
|
|
6638
|
-
|
|
7562
|
+
const webviewId = await waitForPreviewWebview(toolCallId, signal);
|
|
7563
|
+
let result = await executeBrowserAction(webviewId, input);
|
|
7564
|
+
if (input.action === "screenshot") {
|
|
7565
|
+
result = await persistScreenshotResult(result, {
|
|
7566
|
+
cwd: options.cwd,
|
|
7567
|
+
threadId: options.threadId,
|
|
7568
|
+
serverOrigin
|
|
7569
|
+
});
|
|
7570
|
+
}
|
|
7571
|
+
const formatted = formatBrowserResult(result);
|
|
6639
7572
|
return {
|
|
6640
|
-
content: [{ type: "text", text:
|
|
6641
|
-
details: {
|
|
7573
|
+
content: [{ type: "text", text: formatted.text }],
|
|
7574
|
+
details: typeof formatted.details.imageUrl === "string" ? { imageUrl: formatted.details.imageUrl } : formatted.details
|
|
6642
7575
|
};
|
|
6643
7576
|
});
|
|
6644
7577
|
}
|
|
6645
7578
|
};
|
|
6646
7579
|
}
|
|
7580
|
+
var browserTool = createBrowserTool();
|
|
6647
7581
|
|
|
6648
7582
|
// src/tools/execute-browser-js.ts
|
|
6649
7583
|
init_tarsk_debug();
|
|
6650
|
-
import { Type as
|
|
6651
|
-
var runWebWorkerSchema =
|
|
6652
|
-
intention:
|
|
7584
|
+
import { Type as Type17 } from "@sinclair/typebox";
|
|
7585
|
+
var runWebWorkerSchema = Type17.Object({
|
|
7586
|
+
intention: Type17.String({
|
|
6653
7587
|
description: "A short description of what this code execution is trying to accomplish."
|
|
6654
7588
|
}),
|
|
6655
|
-
code:
|
|
7589
|
+
code: Type17.String({
|
|
6656
7590
|
description: "The javascript code to execute in browser context(not nodejs). The environment provides an async function `callTool(toolName, params)` which allows calling any of the other available tools by name. callTool always returns `{ text: string | null, error: string | null }`. On success `text` contains the result and `error` is null. On failure `error` contains the error message and `text` is null. Example: `const r = await callTool('read', { path: 'foo.ts' }); if (r.error) return r.error; return r.text;`."
|
|
6657
7591
|
})
|
|
6658
7592
|
});
|
|
@@ -6699,11 +7633,11 @@ function createRunWebWorkerTool(options) {
|
|
|
6699
7633
|
if (signal?.aborted) {
|
|
6700
7634
|
throw new Error("Operation aborted");
|
|
6701
7635
|
}
|
|
6702
|
-
const result = await new Promise((
|
|
7636
|
+
const result = await new Promise((resolve7, reject) => {
|
|
6703
7637
|
const earlyResult = bufferedResults.get(toolCallId);
|
|
6704
7638
|
if (earlyResult !== void 0) {
|
|
6705
7639
|
bufferedResults.delete(toolCallId);
|
|
6706
|
-
|
|
7640
|
+
resolve7(earlyResult);
|
|
6707
7641
|
return;
|
|
6708
7642
|
}
|
|
6709
7643
|
let timeoutHandle = null;
|
|
@@ -6718,7 +7652,7 @@ function createRunWebWorkerTool(options) {
|
|
|
6718
7652
|
}
|
|
6719
7653
|
const wrappedResolve = (value) => {
|
|
6720
7654
|
cleanup();
|
|
6721
|
-
|
|
7655
|
+
resolve7(value);
|
|
6722
7656
|
};
|
|
6723
7657
|
const wrappedReject = (error) => {
|
|
6724
7658
|
cleanup();
|
|
@@ -6786,7 +7720,7 @@ function looksLikeMissingReturn(code) {
|
|
|
6786
7720
|
var runWebWorkerTool = createRunWebWorkerTool();
|
|
6787
7721
|
|
|
6788
7722
|
// src/tools/fetch.ts
|
|
6789
|
-
import { Type as
|
|
7723
|
+
import { Type as Type18 } from "@sinclair/typebox";
|
|
6790
7724
|
|
|
6791
7725
|
// src/features/web-fetch/content-processor.ts
|
|
6792
7726
|
import { completeSimple as completeSimple2 } from "@earendil-works/pi-ai";
|
|
@@ -6817,7 +7751,7 @@ function runCommandTimed(stepName, command, args2, cwd) {
|
|
|
6817
7751
|
let tFirstIo = null;
|
|
6818
7752
|
let tExit = null;
|
|
6819
7753
|
logSubprocessMark(scope, stepName, "spawn-call", 0);
|
|
6820
|
-
return new Promise((
|
|
7754
|
+
return new Promise((resolve7, reject) => {
|
|
6821
7755
|
const proc = spawnProcess(command, args2, { cwd });
|
|
6822
7756
|
proc.on("spawn", () => {
|
|
6823
7757
|
tSpawnEvent = Date.now();
|
|
@@ -6856,7 +7790,7 @@ function runCommandTimed(stepName, command, args2, cwd) {
|
|
|
6856
7790
|
console.log(
|
|
6857
7791
|
`${GITOPS_TIMING_PREFIX} subprocess ${scope} step=${stepName} summary total=${tClose - t0}ms` + (preSpawnMs !== null ? ` pre-spawn=${preSpawnMs}ms` : "") + (processActiveMs !== null ? ` process-active=${processActiveMs}ms` : "") + (exitToCloseMs !== null ? ` exit-to-close=${exitToCloseMs}ms` : "") + ` cmd=${command} ${args2.join(" ")}`
|
|
6858
7792
|
);
|
|
6859
|
-
|
|
7793
|
+
resolve7({ code, stdout, stderr });
|
|
6860
7794
|
});
|
|
6861
7795
|
proc.on("error", (error) => {
|
|
6862
7796
|
logSubprocessMark(scope, stepName, "spawn-error", Date.now() - t0);
|
|
@@ -6889,13 +7823,13 @@ function startEventLoopLagMonitor(threadId) {
|
|
|
6889
7823
|
|
|
6890
7824
|
// src/features/git/git.utils.ts
|
|
6891
7825
|
import { existsSync as existsSync8, readFileSync as readFileSync3, statSync as statSync5 } from "fs";
|
|
6892
|
-
import { isAbsolute as isAbsolute2, normalize as normalize2, resolve, join as
|
|
7826
|
+
import { isAbsolute as isAbsolute2, normalize as normalize2, resolve, join as join12 } from "path";
|
|
6893
7827
|
|
|
6894
7828
|
// src/core/paths.ts
|
|
6895
|
-
import { join as
|
|
7829
|
+
import { join as join11 } from "path";
|
|
6896
7830
|
import { homedir as homedir4 } from "os";
|
|
6897
|
-
var APP_SUPPORT_DIR =
|
|
6898
|
-
var DATA_DIR =
|
|
7831
|
+
var APP_SUPPORT_DIR = join11(homedir4(), "Library", "Application Support", "Tarsk");
|
|
7832
|
+
var DATA_DIR = join11(APP_SUPPORT_DIR, "data");
|
|
6899
7833
|
function getDataDir() {
|
|
6900
7834
|
return DATA_DIR;
|
|
6901
7835
|
}
|
|
@@ -7163,7 +8097,7 @@ async function getCurrentBranch(gitRoot) {
|
|
|
7163
8097
|
]);
|
|
7164
8098
|
return result.stdout.trim();
|
|
7165
8099
|
}
|
|
7166
|
-
return new Promise((
|
|
8100
|
+
return new Promise((resolve7) => {
|
|
7167
8101
|
const proc = spawnProcess("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: gitRoot });
|
|
7168
8102
|
let out = "";
|
|
7169
8103
|
if (proc.stdout) {
|
|
@@ -7172,9 +8106,9 @@ async function getCurrentBranch(gitRoot) {
|
|
|
7172
8106
|
});
|
|
7173
8107
|
}
|
|
7174
8108
|
proc.on("close", () => {
|
|
7175
|
-
|
|
8109
|
+
resolve7(out.trim());
|
|
7176
8110
|
});
|
|
7177
|
-
proc.on("error", () =>
|
|
8111
|
+
proc.on("error", () => resolve7(""));
|
|
7178
8112
|
});
|
|
7179
8113
|
}
|
|
7180
8114
|
async function getGitDiff(gitRoot) {
|
|
@@ -7251,7 +8185,7 @@ async function getUntrackedFilesDiff(gitRoot) {
|
|
|
7251
8185
|
const parts = [];
|
|
7252
8186
|
const maxFileSize = 1e5;
|
|
7253
8187
|
for (const relPath of untrackedPaths) {
|
|
7254
|
-
const fullPath =
|
|
8188
|
+
const fullPath = join12(gitRoot, relPath);
|
|
7255
8189
|
if (!existsSync8(fullPath)) continue;
|
|
7256
8190
|
try {
|
|
7257
8191
|
if (statSync5(fullPath).isDirectory()) continue;
|
|
@@ -7284,7 +8218,7 @@ async function getGitStatus(gitRoot) {
|
|
|
7284
8218
|
]);
|
|
7285
8219
|
return result.stdout;
|
|
7286
8220
|
}
|
|
7287
|
-
return new Promise((
|
|
8221
|
+
return new Promise((resolve7) => {
|
|
7288
8222
|
const proc = spawnProcess("git", ["status", "--porcelain", "--untracked-files=all"], {
|
|
7289
8223
|
cwd: gitRoot
|
|
7290
8224
|
});
|
|
@@ -7294,15 +8228,15 @@ async function getGitStatus(gitRoot) {
|
|
|
7294
8228
|
out += d.toString();
|
|
7295
8229
|
});
|
|
7296
8230
|
}
|
|
7297
|
-
proc.on("close", () =>
|
|
7298
|
-
proc.on("error", () =>
|
|
8231
|
+
proc.on("close", () => resolve7(out));
|
|
8232
|
+
proc.on("error", () => resolve7(""));
|
|
7299
8233
|
});
|
|
7300
8234
|
}
|
|
7301
8235
|
function runGit(stepName, gitRoot, args2) {
|
|
7302
8236
|
if (isGitSubprocessTimingEnabled()) {
|
|
7303
8237
|
return runGitCommandTimed(stepName, gitRoot, args2);
|
|
7304
8238
|
}
|
|
7305
|
-
return new Promise((
|
|
8239
|
+
return new Promise((resolve7) => {
|
|
7306
8240
|
const proc = spawnProcess("git", args2, { cwd: gitRoot });
|
|
7307
8241
|
let stdout = "";
|
|
7308
8242
|
let stderr = "";
|
|
@@ -7316,8 +8250,8 @@ function runGit(stepName, gitRoot, args2) {
|
|
|
7316
8250
|
stderr += d.toString();
|
|
7317
8251
|
});
|
|
7318
8252
|
}
|
|
7319
|
-
proc.on("close", (code) =>
|
|
7320
|
-
proc.on("error", () =>
|
|
8253
|
+
proc.on("close", (code) => resolve7({ code, stdout, stderr }));
|
|
8254
|
+
proc.on("error", () => resolve7({ code: -1, stdout, stderr }));
|
|
7321
8255
|
});
|
|
7322
8256
|
}
|
|
7323
8257
|
async function getStatusPorcelainV2(gitRoot) {
|
|
@@ -7415,7 +8349,7 @@ async function fetchRemoteRefs(gitRoot, refs) {
|
|
|
7415
8349
|
await runGit("fetch-remote-refs", gitRoot, ["fetch", "--no-tags", "origin", ...refs]);
|
|
7416
8350
|
}
|
|
7417
8351
|
function hasUnpushedCommits(gitRoot, currentBranch) {
|
|
7418
|
-
return new Promise((
|
|
8352
|
+
return new Promise((resolve7) => {
|
|
7419
8353
|
const proc = spawnProcess("git", ["log", `origin/${currentBranch}..HEAD`, "--oneline"], {
|
|
7420
8354
|
cwd: gitRoot
|
|
7421
8355
|
});
|
|
@@ -7426,13 +8360,13 @@ function hasUnpushedCommits(gitRoot, currentBranch) {
|
|
|
7426
8360
|
});
|
|
7427
8361
|
}
|
|
7428
8362
|
proc.on("close", () => {
|
|
7429
|
-
|
|
8363
|
+
resolve7(out.trim().length > 0);
|
|
7430
8364
|
});
|
|
7431
|
-
proc.on("error", () =>
|
|
8365
|
+
proc.on("error", () => resolve7(false));
|
|
7432
8366
|
});
|
|
7433
8367
|
}
|
|
7434
8368
|
function hasCommitsAheadOfDefault(gitRoot, defaultBranch) {
|
|
7435
|
-
return new Promise((
|
|
8369
|
+
return new Promise((resolve7) => {
|
|
7436
8370
|
const proc = spawnProcess("git", ["rev-list", "--count", `origin/${defaultBranch}..HEAD`], {
|
|
7437
8371
|
cwd: gitRoot
|
|
7438
8372
|
});
|
|
@@ -7444,12 +8378,12 @@ function hasCommitsAheadOfDefault(gitRoot, defaultBranch) {
|
|
|
7444
8378
|
}
|
|
7445
8379
|
proc.on("close", (code) => {
|
|
7446
8380
|
if (code === 0) {
|
|
7447
|
-
|
|
8381
|
+
resolve7(parseInt(out.trim(), 10) > 0);
|
|
7448
8382
|
} else {
|
|
7449
|
-
|
|
8383
|
+
resolve7(false);
|
|
7450
8384
|
}
|
|
7451
8385
|
});
|
|
7452
|
-
proc.on("error", () =>
|
|
8386
|
+
proc.on("error", () => resolve7(false));
|
|
7453
8387
|
});
|
|
7454
8388
|
}
|
|
7455
8389
|
async function getRemoteOrigin(gitRoot) {
|
|
@@ -7464,7 +8398,7 @@ async function getRemoteOrigin(gitRoot) {
|
|
|
7464
8398
|
}
|
|
7465
8399
|
return "";
|
|
7466
8400
|
}
|
|
7467
|
-
return new Promise((
|
|
8401
|
+
return new Promise((resolve7) => {
|
|
7468
8402
|
const proc = spawnProcess("git", ["remote", "get-url", "origin"], { cwd: gitRoot });
|
|
7469
8403
|
let out = "";
|
|
7470
8404
|
if (proc.stdout) {
|
|
@@ -7474,12 +8408,12 @@ async function getRemoteOrigin(gitRoot) {
|
|
|
7474
8408
|
}
|
|
7475
8409
|
proc.on("close", (code) => {
|
|
7476
8410
|
if (code === 0) {
|
|
7477
|
-
|
|
8411
|
+
resolve7(out.trim());
|
|
7478
8412
|
} else {
|
|
7479
|
-
|
|
8413
|
+
resolve7("");
|
|
7480
8414
|
}
|
|
7481
8415
|
});
|
|
7482
|
-
proc.on("error", () =>
|
|
8416
|
+
proc.on("error", () => resolve7(""));
|
|
7483
8417
|
});
|
|
7484
8418
|
}
|
|
7485
8419
|
async function getGitSyncStatus(gitRoot, branch, options) {
|
|
@@ -7487,20 +8421,20 @@ async function getGitSyncStatus(gitRoot, branch, options) {
|
|
|
7487
8421
|
console.log(`[git-status] Checking sync status for branch: ${branch}`);
|
|
7488
8422
|
if (fetchRemote) {
|
|
7489
8423
|
console.log(`[git-status] Fetching from origin...`);
|
|
7490
|
-
await new Promise((
|
|
8424
|
+
await new Promise((resolve7) => {
|
|
7491
8425
|
const fetchProc = spawnProcess("git", ["fetch", "origin"], { cwd: gitRoot });
|
|
7492
8426
|
fetchProc.on("close", () => {
|
|
7493
8427
|
console.log(`[git-status] \u2713 Fetch completed`);
|
|
7494
|
-
|
|
8428
|
+
resolve7();
|
|
7495
8429
|
});
|
|
7496
8430
|
fetchProc.on("error", () => {
|
|
7497
8431
|
console.log(`[git-status] Fetch error (continuing anyway)`);
|
|
7498
|
-
|
|
8432
|
+
resolve7();
|
|
7499
8433
|
});
|
|
7500
8434
|
});
|
|
7501
8435
|
}
|
|
7502
8436
|
console.log(`[git-status] Checking status against remote tracking branch...`);
|
|
7503
|
-
return new Promise((
|
|
8437
|
+
return new Promise((resolve7) => {
|
|
7504
8438
|
const statusProc = spawnProcess("git", ["status", "-sb"], { cwd: gitRoot });
|
|
7505
8439
|
let statusOut = "";
|
|
7506
8440
|
let statusErr = "";
|
|
@@ -7520,49 +8454,49 @@ async function getGitSyncStatus(gitRoot, branch, options) {
|
|
|
7520
8454
|
const firstLine = output.split("\n")[0] ?? "";
|
|
7521
8455
|
if (!firstLine.includes(branch)) {
|
|
7522
8456
|
console.log(`[git-status] Branch name not found in status output, returning 'Up to date'`);
|
|
7523
|
-
|
|
8457
|
+
resolve7("Up to date");
|
|
7524
8458
|
return;
|
|
7525
8459
|
}
|
|
7526
8460
|
if (!firstLine.includes("...")) {
|
|
7527
8461
|
console.log(`[git-status] No upstream tracking branch detected, returning 'Up to date'`);
|
|
7528
|
-
|
|
8462
|
+
resolve7("Up to date");
|
|
7529
8463
|
return;
|
|
7530
8464
|
}
|
|
7531
8465
|
if (firstLine.includes("ahead") && firstLine.includes("behind")) {
|
|
7532
8466
|
console.log(`[git-status] \u2713 Status: Diverged`);
|
|
7533
|
-
|
|
8467
|
+
resolve7("Diverged");
|
|
7534
8468
|
} else if (firstLine.includes("behind")) {
|
|
7535
8469
|
console.log(`[git-status] \u2713 Status: Behind`);
|
|
7536
|
-
|
|
8470
|
+
resolve7("Behind");
|
|
7537
8471
|
} else if (firstLine.includes("ahead")) {
|
|
7538
8472
|
console.log(`[git-status] \u2713 Status: Ahead`);
|
|
7539
|
-
|
|
8473
|
+
resolve7("Ahead");
|
|
7540
8474
|
} else {
|
|
7541
8475
|
console.log(`[git-status] \u2713 Status: Up to date`);
|
|
7542
|
-
|
|
8476
|
+
resolve7("Up to date");
|
|
7543
8477
|
}
|
|
7544
8478
|
});
|
|
7545
8479
|
statusProc.on("error", () => {
|
|
7546
8480
|
console.log(`[git-status] Error getting status, defaulting to 'Up to date'`);
|
|
7547
|
-
|
|
8481
|
+
resolve7("Up to date");
|
|
7548
8482
|
});
|
|
7549
8483
|
});
|
|
7550
8484
|
}
|
|
7551
8485
|
async function hasRemoteBranch(gitRoot, branch) {
|
|
7552
|
-
return new Promise((
|
|
8486
|
+
return new Promise((resolve7) => {
|
|
7553
8487
|
const proc = spawnProcess("git", ["rev-parse", "--verify", `origin/${branch}`], {
|
|
7554
8488
|
cwd: gitRoot
|
|
7555
8489
|
});
|
|
7556
8490
|
proc.on("close", (code) => {
|
|
7557
|
-
|
|
8491
|
+
resolve7(code === 0);
|
|
7558
8492
|
});
|
|
7559
8493
|
proc.on("error", () => {
|
|
7560
|
-
|
|
8494
|
+
resolve7(false);
|
|
7561
8495
|
});
|
|
7562
8496
|
});
|
|
7563
8497
|
}
|
|
7564
8498
|
function getDefaultBranch(gitRoot) {
|
|
7565
|
-
return new Promise((
|
|
8499
|
+
return new Promise((resolve7) => {
|
|
7566
8500
|
const proc = spawnProcess("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
7567
8501
|
cwd: gitRoot
|
|
7568
8502
|
});
|
|
@@ -7575,12 +8509,12 @@ function getDefaultBranch(gitRoot) {
|
|
|
7575
8509
|
proc.on("close", (code) => {
|
|
7576
8510
|
if (code === 0 && out.trim()) {
|
|
7577
8511
|
const match = out.trim().match(/refs\/remotes\/origin\/(.+)/);
|
|
7578
|
-
|
|
8512
|
+
resolve7(match ? match[1] : null);
|
|
7579
8513
|
} else {
|
|
7580
|
-
|
|
8514
|
+
resolve7(null);
|
|
7581
8515
|
}
|
|
7582
8516
|
});
|
|
7583
|
-
proc.on("error", () =>
|
|
8517
|
+
proc.on("error", () => resolve7(null));
|
|
7584
8518
|
});
|
|
7585
8519
|
}
|
|
7586
8520
|
async function findDefaultBranch(gitRoot) {
|
|
@@ -7600,7 +8534,7 @@ async function resolvePrCommitLogRange(gitRoot, baseBranch, currentBranch) {
|
|
|
7600
8534
|
}
|
|
7601
8535
|
async function getPrBranchDiff(gitRoot, baseBranch) {
|
|
7602
8536
|
const diffRange = await hasRemoteBranch(gitRoot, baseBranch) ? `origin/${baseBranch}...HEAD` : `${baseBranch}...HEAD`;
|
|
7603
|
-
return new Promise((
|
|
8537
|
+
return new Promise((resolve7) => {
|
|
7604
8538
|
const proc = spawnProcess("git", ["diff", diffRange], { cwd: gitRoot });
|
|
7605
8539
|
let out = "";
|
|
7606
8540
|
if (proc.stdout) {
|
|
@@ -7608,13 +8542,13 @@ async function getPrBranchDiff(gitRoot, baseBranch) {
|
|
|
7608
8542
|
out += d.toString();
|
|
7609
8543
|
});
|
|
7610
8544
|
}
|
|
7611
|
-
proc.on("close", () =>
|
|
7612
|
-
proc.on("error", () =>
|
|
8545
|
+
proc.on("close", () => resolve7(out.trim()));
|
|
8546
|
+
proc.on("error", () => resolve7(""));
|
|
7613
8547
|
});
|
|
7614
8548
|
}
|
|
7615
8549
|
async function getCommitLog(gitRoot, baseBranch, currentBranch) {
|
|
7616
8550
|
const range = await resolvePrCommitLogRange(gitRoot, baseBranch, currentBranch);
|
|
7617
|
-
return new Promise((
|
|
8551
|
+
return new Promise((resolve7) => {
|
|
7618
8552
|
const proc = spawnProcess("git", ["log", "--oneline", range], {
|
|
7619
8553
|
cwd: gitRoot
|
|
7620
8554
|
});
|
|
@@ -7624,13 +8558,13 @@ async function getCommitLog(gitRoot, baseBranch, currentBranch) {
|
|
|
7624
8558
|
out += d.toString();
|
|
7625
8559
|
});
|
|
7626
8560
|
}
|
|
7627
|
-
proc.on("close", () =>
|
|
7628
|
-
proc.on("error", () =>
|
|
8561
|
+
proc.on("close", () => resolve7(out.trim()));
|
|
8562
|
+
proc.on("error", () => resolve7(""));
|
|
7629
8563
|
});
|
|
7630
8564
|
}
|
|
7631
8565
|
async function getDetailedCommits(gitRoot, baseBranch, currentBranch) {
|
|
7632
8566
|
const range = await resolvePrCommitLogRange(gitRoot, baseBranch, currentBranch);
|
|
7633
|
-
return new Promise((
|
|
8567
|
+
return new Promise((resolve7) => {
|
|
7634
8568
|
const proc = spawnProcess("git", ["log", range, "--format=%h %s%n%b%n---"], { cwd: gitRoot });
|
|
7635
8569
|
let out = "";
|
|
7636
8570
|
if (proc.stdout) {
|
|
@@ -7639,14 +8573,14 @@ async function getDetailedCommits(gitRoot, baseBranch, currentBranch) {
|
|
|
7639
8573
|
});
|
|
7640
8574
|
}
|
|
7641
8575
|
proc.on("close", () => {
|
|
7642
|
-
|
|
8576
|
+
resolve7(out.length > 2e3 ? out.substring(0, 2e3) + "\n...(more commits)" : out);
|
|
7643
8577
|
});
|
|
7644
|
-
proc.on("error", () =>
|
|
8578
|
+
proc.on("error", () => resolve7(""));
|
|
7645
8579
|
});
|
|
7646
8580
|
}
|
|
7647
8581
|
function getGitLog(gitRoot, limit) {
|
|
7648
8582
|
return new Promise(
|
|
7649
|
-
(
|
|
8583
|
+
(resolve7, reject) => {
|
|
7650
8584
|
const proc = spawnProcess(
|
|
7651
8585
|
"git",
|
|
7652
8586
|
["log", "--oneline", "-n", limit.toString(), "--format=%H|%s|%an|%ai"],
|
|
@@ -7676,7 +8610,7 @@ function getGitLog(gitRoot, limit) {
|
|
|
7676
8610
|
date: parts[3] || ""
|
|
7677
8611
|
};
|
|
7678
8612
|
});
|
|
7679
|
-
|
|
8613
|
+
resolve7(parsed);
|
|
7680
8614
|
} else {
|
|
7681
8615
|
reject(new Error(err || "Failed to get git log"));
|
|
7682
8616
|
}
|
|
@@ -7686,27 +8620,27 @@ function getGitLog(gitRoot, limit) {
|
|
|
7686
8620
|
);
|
|
7687
8621
|
}
|
|
7688
8622
|
function stageAllChanges(gitRoot) {
|
|
7689
|
-
return new Promise((
|
|
8623
|
+
return new Promise((resolve7, reject) => {
|
|
7690
8624
|
const proc = spawnProcess("git", ["add", "-A"], { cwd: gitRoot });
|
|
7691
8625
|
proc.on("close", (code) => {
|
|
7692
|
-
if (code === 0)
|
|
8626
|
+
if (code === 0) resolve7();
|
|
7693
8627
|
else reject(new Error("Failed to stage changes"));
|
|
7694
8628
|
});
|
|
7695
8629
|
proc.on("error", reject);
|
|
7696
8630
|
});
|
|
7697
8631
|
}
|
|
7698
8632
|
function commitChanges(gitRoot, message) {
|
|
7699
|
-
return new Promise((
|
|
8633
|
+
return new Promise((resolve7, reject) => {
|
|
7700
8634
|
const proc = spawnProcess("git", ["commit", "-m", message], { cwd: gitRoot });
|
|
7701
8635
|
proc.on("close", (code) => {
|
|
7702
|
-
if (code === 0)
|
|
8636
|
+
if (code === 0) resolve7();
|
|
7703
8637
|
else reject(new Error("Failed to commit changes"));
|
|
7704
8638
|
});
|
|
7705
8639
|
proc.on("error", reject);
|
|
7706
8640
|
});
|
|
7707
8641
|
}
|
|
7708
8642
|
function pushToOrigin(gitRoot, currentBranch) {
|
|
7709
|
-
return new Promise((
|
|
8643
|
+
return new Promise((resolve7, reject) => {
|
|
7710
8644
|
console.log(`[git-push] Executing: git push -u origin ${currentBranch}`);
|
|
7711
8645
|
const proc = spawnProcess("git", ["push", "-u", "origin", currentBranch], { cwd: gitRoot });
|
|
7712
8646
|
let err = "";
|
|
@@ -7727,7 +8661,7 @@ function pushToOrigin(gitRoot, currentBranch) {
|
|
|
7727
8661
|
console.log(`[git-push] git push exited with code: ${code}`);
|
|
7728
8662
|
if (code === 0) {
|
|
7729
8663
|
console.log(`[git-push] \u2713 Push completed successfully`);
|
|
7730
|
-
|
|
8664
|
+
resolve7();
|
|
7731
8665
|
} else {
|
|
7732
8666
|
console.log(`[git-push] \u2717 Push failed with error: ${err || "Unknown error"}`);
|
|
7733
8667
|
reject(new Error(err || "Failed to push changes"));
|
|
@@ -7742,7 +8676,7 @@ function pushToOrigin(gitRoot, currentBranch) {
|
|
|
7742
8676
|
});
|
|
7743
8677
|
}
|
|
7744
8678
|
function fetchFromOrigin(gitRoot) {
|
|
7745
|
-
return new Promise((
|
|
8679
|
+
return new Promise((resolve7, reject) => {
|
|
7746
8680
|
const proc = spawnProcess("git", ["fetch", "origin"], { cwd: gitRoot });
|
|
7747
8681
|
let err = "";
|
|
7748
8682
|
if (proc.stderr) {
|
|
@@ -7751,14 +8685,14 @@ function fetchFromOrigin(gitRoot) {
|
|
|
7751
8685
|
});
|
|
7752
8686
|
}
|
|
7753
8687
|
proc.on("close", (code) => {
|
|
7754
|
-
if (code === 0)
|
|
8688
|
+
if (code === 0) resolve7();
|
|
7755
8689
|
else reject(new Error(err || "Failed to fetch from origin"));
|
|
7756
8690
|
});
|
|
7757
8691
|
proc.on("error", reject);
|
|
7758
8692
|
});
|
|
7759
8693
|
}
|
|
7760
8694
|
function pullFromOrigin(gitRoot) {
|
|
7761
|
-
return new Promise((
|
|
8695
|
+
return new Promise((resolve7, reject) => {
|
|
7762
8696
|
const proc = spawnProcess("git", ["pull"], { cwd: gitRoot });
|
|
7763
8697
|
let err = "";
|
|
7764
8698
|
if (proc.stderr) {
|
|
@@ -7767,14 +8701,14 @@ function pullFromOrigin(gitRoot) {
|
|
|
7767
8701
|
});
|
|
7768
8702
|
}
|
|
7769
8703
|
proc.on("close", (code) => {
|
|
7770
|
-
if (code === 0)
|
|
8704
|
+
if (code === 0) resolve7();
|
|
7771
8705
|
else reject(new Error(err || "Failed to pull from origin"));
|
|
7772
8706
|
});
|
|
7773
8707
|
proc.on("error", reject);
|
|
7774
8708
|
});
|
|
7775
8709
|
}
|
|
7776
8710
|
function createGitHubRepo(gitRoot, repoName, description, isPrivate) {
|
|
7777
|
-
return new Promise((
|
|
8711
|
+
return new Promise((resolve7, reject) => {
|
|
7778
8712
|
const args2 = ["repo", "create"];
|
|
7779
8713
|
if (repoName) {
|
|
7780
8714
|
args2.push(repoName);
|
|
@@ -7805,7 +8739,7 @@ function createGitHubRepo(gitRoot, repoName, description, isPrivate) {
|
|
|
7805
8739
|
if (code === 0) {
|
|
7806
8740
|
invalidateRepoInfoCache(gitRoot);
|
|
7807
8741
|
const urlMatch = out.match(/https:\/\/[^\s]+/);
|
|
7808
|
-
|
|
8742
|
+
resolve7(urlMatch ? urlMatch[0] : out.trim());
|
|
7809
8743
|
} else {
|
|
7810
8744
|
reject(new Error(err || "Failed to create repository"));
|
|
7811
8745
|
}
|
|
@@ -7817,7 +8751,7 @@ function createGitHubRepo(gitRoot, repoName, description, isPrivate) {
|
|
|
7817
8751
|
});
|
|
7818
8752
|
}
|
|
7819
8753
|
function createPullRequest(gitRoot, title, description) {
|
|
7820
|
-
return new Promise((
|
|
8754
|
+
return new Promise((resolve7, reject) => {
|
|
7821
8755
|
const args2 = ["pr", "create", "--title", title ?? "", "--body", description ?? ""];
|
|
7822
8756
|
console.log(`[git-create-pr] Executing: gh ${args2.join(" ")}`);
|
|
7823
8757
|
const proc = spawnProcess("gh", args2, { cwd: gitRoot });
|
|
@@ -7842,7 +8776,7 @@ function createPullRequest(gitRoot, title, description) {
|
|
|
7842
8776
|
const urlMatch = out.match(/https:\/\/[^\s]+/);
|
|
7843
8777
|
const prUrl = urlMatch ? urlMatch[0] : out.trim();
|
|
7844
8778
|
console.log(`[git-create-pr] \u2713 PR URL extracted: ${prUrl}`);
|
|
7845
|
-
|
|
8779
|
+
resolve7(prUrl);
|
|
7846
8780
|
} else {
|
|
7847
8781
|
console.log(`[git-create-pr] \u2717 gh pr create failed with error: ${err || "Unknown error"}`);
|
|
7848
8782
|
reject(new Error(err || "Failed to create PR"));
|
|
@@ -7881,7 +8815,7 @@ async function getPullRequestStatus(gitRoot) {
|
|
|
7881
8815
|
}
|
|
7882
8816
|
throw new Error(result.stderr || "Failed to check PR status");
|
|
7883
8817
|
}
|
|
7884
|
-
return new Promise((
|
|
8818
|
+
return new Promise((resolve7, reject) => {
|
|
7885
8819
|
const args2 = ["pr", "view", "--json", "url,state"];
|
|
7886
8820
|
const proc = spawnProcess("gh", args2, { cwd: gitRoot });
|
|
7887
8821
|
let out = "";
|
|
@@ -7900,7 +8834,7 @@ async function getPullRequestStatus(gitRoot) {
|
|
|
7900
8834
|
if (code === 0) {
|
|
7901
8835
|
try {
|
|
7902
8836
|
const prData = JSON.parse(out.trim());
|
|
7903
|
-
|
|
8837
|
+
resolve7({
|
|
7904
8838
|
exists: true,
|
|
7905
8839
|
url: prData.url,
|
|
7906
8840
|
state: prData.state
|
|
@@ -7910,7 +8844,7 @@ async function getPullRequestStatus(gitRoot) {
|
|
|
7910
8844
|
}
|
|
7911
8845
|
} else {
|
|
7912
8846
|
if (err.includes("no pull requests") || err.includes("not found")) {
|
|
7913
|
-
|
|
8847
|
+
resolve7({ exists: false });
|
|
7914
8848
|
} else {
|
|
7915
8849
|
reject(new Error(err || "Failed to check PR status"));
|
|
7916
8850
|
}
|
|
@@ -8097,7 +9031,7 @@ function buildCrossHostRedirectResult(redirectUrl) {
|
|
|
8097
9031
|
|
|
8098
9032
|
// src/features/web-fetch/web-fetch.client.ts
|
|
8099
9033
|
import { mkdir, writeFile as writeFile2 } from "fs/promises";
|
|
8100
|
-
import { join as
|
|
9034
|
+
import { join as join13, extname as extname3 } from "path";
|
|
8101
9035
|
import { createHash as createHash2 } from "crypto";
|
|
8102
9036
|
|
|
8103
9037
|
// src/features/web-fetch/web-fetch-cache.ts
|
|
@@ -8202,7 +9136,7 @@ async function readResponseBody(response, signal) {
|
|
|
8202
9136
|
return { body, bytes: body.length };
|
|
8203
9137
|
}
|
|
8204
9138
|
async function saveBinaryContent(body, contentType, url) {
|
|
8205
|
-
const downloadsDir =
|
|
9139
|
+
const downloadsDir = join13(getDataDir(), "fetch-downloads");
|
|
8206
9140
|
await mkdir(downloadsDir, { recursive: true });
|
|
8207
9141
|
const hash = createHash2("sha256").update(url).digest("hex").slice(0, 12);
|
|
8208
9142
|
let extension = extname3(new URL(url).pathname);
|
|
@@ -8217,7 +9151,7 @@ async function saveBinaryContent(body, contentType, url) {
|
|
|
8217
9151
|
if (!extension) {
|
|
8218
9152
|
extension = ".bin";
|
|
8219
9153
|
}
|
|
8220
|
-
const filePath =
|
|
9154
|
+
const filePath = join13(downloadsDir, `${hash}${extension}`);
|
|
8221
9155
|
await writeFile2(filePath, body);
|
|
8222
9156
|
return filePath;
|
|
8223
9157
|
}
|
|
@@ -8378,9 +9312,9 @@ function truncateFetchUrl(url, maxLength = 80) {
|
|
|
8378
9312
|
}
|
|
8379
9313
|
|
|
8380
9314
|
// src/tools/fetch.ts
|
|
8381
|
-
var fetchSchema =
|
|
8382
|
-
url:
|
|
8383
|
-
prompt:
|
|
9315
|
+
var fetchSchema = Type18.Object({
|
|
9316
|
+
url: Type18.String({ description: "Fully qualified URL to fetch (http or https)" }),
|
|
9317
|
+
prompt: Type18.String({
|
|
8384
9318
|
description: "What to extract or summarize from the page content"
|
|
8385
9319
|
})
|
|
8386
9320
|
});
|
|
@@ -8477,7 +9411,7 @@ function createFetchTool(options) {
|
|
|
8477
9411
|
}
|
|
8478
9412
|
|
|
8479
9413
|
// src/tools/web-search.ts
|
|
8480
|
-
import { Type as
|
|
9414
|
+
import { Type as Type19 } from "@sinclair/typebox";
|
|
8481
9415
|
|
|
8482
9416
|
// src/features/web-search/web-search.client.ts
|
|
8483
9417
|
var WEB_SEARCH_API = "https://api.webnative.dev/websearch";
|
|
@@ -8547,37 +9481,37 @@ async function searchWeb(options) {
|
|
|
8547
9481
|
}
|
|
8548
9482
|
|
|
8549
9483
|
// src/tools/web-search.ts
|
|
8550
|
-
var searchTypeSchema =
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
9484
|
+
var searchTypeSchema = Type19.Union([
|
|
9485
|
+
Type19.Literal("auto"),
|
|
9486
|
+
Type19.Literal("fast"),
|
|
9487
|
+
Type19.Literal("instant"),
|
|
9488
|
+
Type19.Literal("deep-lite"),
|
|
9489
|
+
Type19.Literal("deep"),
|
|
9490
|
+
Type19.Literal("deep-reasoning")
|
|
8557
9491
|
]);
|
|
8558
|
-
var webSearchSchema =
|
|
8559
|
-
query:
|
|
9492
|
+
var webSearchSchema = Type19.Object({
|
|
9493
|
+
query: Type19.String({
|
|
8560
9494
|
description: "Natural language search query (e.g. 'Next.js route handler authentication example')"
|
|
8561
9495
|
}),
|
|
8562
|
-
numResults:
|
|
8563
|
-
|
|
9496
|
+
numResults: Type19.Optional(
|
|
9497
|
+
Type19.Number({
|
|
8564
9498
|
description: "Number of results to return (1-100, default 10)",
|
|
8565
9499
|
minimum: 1,
|
|
8566
9500
|
maximum: 100
|
|
8567
9501
|
})
|
|
8568
9502
|
),
|
|
8569
|
-
type:
|
|
8570
|
-
|
|
9503
|
+
type: Type19.Optional(
|
|
9504
|
+
Type19.Union([searchTypeSchema], {
|
|
8571
9505
|
description: "Search depth/latency tradeoff. Use auto (default) for most queries; fast/instant for low latency; deep variants for research."
|
|
8572
9506
|
})
|
|
8573
9507
|
),
|
|
8574
|
-
includeDomains:
|
|
8575
|
-
|
|
9508
|
+
includeDomains: Type19.Optional(
|
|
9509
|
+
Type19.Array(Type19.String(), {
|
|
8576
9510
|
description: "Only return results from these domains (e.g. ['docs.github.com', 'nodejs.org'])"
|
|
8577
9511
|
})
|
|
8578
9512
|
),
|
|
8579
|
-
excludeDomains:
|
|
8580
|
-
|
|
9513
|
+
excludeDomains: Type19.Optional(
|
|
9514
|
+
Type19.Array(Type19.String(), {
|
|
8581
9515
|
description: "Exclude results from these domains"
|
|
8582
9516
|
})
|
|
8583
9517
|
)
|
|
@@ -8651,13 +9585,13 @@ function createWebSearchTool() {
|
|
|
8651
9585
|
}
|
|
8652
9586
|
|
|
8653
9587
|
// src/tools/tool-search.ts
|
|
8654
|
-
import { Type as
|
|
8655
|
-
var toolSearchSchema =
|
|
8656
|
-
query:
|
|
9588
|
+
import { Type as Type20 } from "@sinclair/typebox";
|
|
9589
|
+
var toolSearchSchema = Type20.Object({
|
|
9590
|
+
query: Type20.String({
|
|
8657
9591
|
description: 'Search query to find tools. Use "select:name1,name2" to fetch exact tools by name, or keywords to search by description.'
|
|
8658
9592
|
}),
|
|
8659
|
-
max_results:
|
|
8660
|
-
|
|
9593
|
+
max_results: Type20.Optional(
|
|
9594
|
+
Type20.Number({
|
|
8661
9595
|
description: "Maximum number of results to return (default: 5)"
|
|
8662
9596
|
})
|
|
8663
9597
|
)
|
|
@@ -8785,6 +9719,7 @@ var toolRegistry = [
|
|
|
8785
9719
|
{ name: "agent", enabled: true, displayName: "Subagent", activeDisplayName: "Subagent" },
|
|
8786
9720
|
{ name: "ask_user", enabled: true, displayName: "Ask", activeDisplayName: "Asking" },
|
|
8787
9721
|
{ name: "bash", enabled: true, displayName: "Ran", activeDisplayName: "Running" },
|
|
9722
|
+
{ name: "browser", enabled: true, displayName: "Browser", activeDisplayName: "Browsing" },
|
|
8788
9723
|
{
|
|
8789
9724
|
name: "code_search",
|
|
8790
9725
|
enabled: false,
|
|
@@ -8971,7 +9906,7 @@ var WebFetchPermissionGate = class {
|
|
|
8971
9906
|
wildcardOptions: buildWildcardOptions2(normalized)
|
|
8972
9907
|
};
|
|
8973
9908
|
this.config.onPermissionRequired?.(request);
|
|
8974
|
-
return new Promise((
|
|
9909
|
+
return new Promise((resolve7, reject) => {
|
|
8975
9910
|
const cleanup = () => {
|
|
8976
9911
|
pendingPermissions.delete(toolCallId);
|
|
8977
9912
|
signal?.removeEventListener("abort", onAbort);
|
|
@@ -8989,7 +9924,7 @@ var WebFetchPermissionGate = class {
|
|
|
8989
9924
|
pendingPermissions.set(toolCallId, {
|
|
8990
9925
|
resolve: (decision) => {
|
|
8991
9926
|
cleanup();
|
|
8992
|
-
void this.applyDecision(decision, normalized).then(() =>
|
|
9927
|
+
void this.applyDecision(decision, normalized).then(() => resolve7(decision));
|
|
8993
9928
|
},
|
|
8994
9929
|
reject: (error) => {
|
|
8995
9930
|
cleanup();
|
|
@@ -9135,7 +10070,10 @@ async function createCodingTools(cwd, options) {
|
|
|
9135
10070
|
if (options?.metadataManager && isToolEnabled("generate_image")) {
|
|
9136
10071
|
coreTools.push(createGenerateImageTool({ metadataManager: options.metadataManager, cwd }));
|
|
9137
10072
|
}
|
|
9138
|
-
const optionalTools = filterEnabledTools([
|
|
10073
|
+
const optionalTools = filterEnabledTools([
|
|
10074
|
+
createFindImagesTool({ cwd, threadId: options?.threadId }),
|
|
10075
|
+
createBrowserTool({ cwd, threadId: options?.threadId })
|
|
10076
|
+
]);
|
|
9139
10077
|
if (options?.skills && options.skills.length > 0) {
|
|
9140
10078
|
optionalTools.push(
|
|
9141
10079
|
...filterEnabledTools([
|
|
@@ -9214,7 +10152,7 @@ function createAllTools(cwd, options) {
|
|
|
9214
10152
|
});
|
|
9215
10153
|
}
|
|
9216
10154
|
if (isToolEnabled("find_images")) {
|
|
9217
|
-
tools.find_images = createFindImagesTool(cwd);
|
|
10155
|
+
tools.find_images = createFindImagesTool({ cwd, threadId: options?.threadId });
|
|
9218
10156
|
}
|
|
9219
10157
|
if (options?.threadId) {
|
|
9220
10158
|
for (const [name, tool] of Object.entries(tools)) {
|
|
@@ -9248,10 +10186,10 @@ async function* loadCodingToolsWithMcpStatus(cwd, options) {
|
|
|
9248
10186
|
if (completed) {
|
|
9249
10187
|
break;
|
|
9250
10188
|
}
|
|
9251
|
-
await new Promise((
|
|
9252
|
-
notify =
|
|
10189
|
+
await new Promise((resolve7) => {
|
|
10190
|
+
notify = resolve7;
|
|
9253
10191
|
if (completed) {
|
|
9254
|
-
|
|
10192
|
+
resolve7();
|
|
9255
10193
|
}
|
|
9256
10194
|
});
|
|
9257
10195
|
notify = null;
|
|
@@ -9263,7 +10201,7 @@ async function* loadCodingToolsWithMcpStatus(cwd, options) {
|
|
|
9263
10201
|
}
|
|
9264
10202
|
|
|
9265
10203
|
// src/tools/agent-tool.ts
|
|
9266
|
-
import { Type as
|
|
10204
|
+
import { Type as Type21 } from "@sinclair/typebox";
|
|
9267
10205
|
|
|
9268
10206
|
// src/agent/agent.subagent-executor.ts
|
|
9269
10207
|
import { Agent } from "@earendil-works/pi-agent-core";
|
|
@@ -9381,7 +10319,7 @@ var EventQueue = class {
|
|
|
9381
10319
|
content: resultContent,
|
|
9382
10320
|
isError: event.isError
|
|
9383
10321
|
};
|
|
9384
|
-
if (event.toolName === "generate_image") {
|
|
10322
|
+
if (event.toolName === "generate_image" || event.toolName === "find_images" || event.toolName === "browser") {
|
|
9385
10323
|
toolResultBlock.content = stripGeneratedImageText(resultContent);
|
|
9386
10324
|
const details = event.result?.details;
|
|
9387
10325
|
if (details?.imageUrl) {
|
|
@@ -9588,8 +10526,8 @@ async function executeSubagent(options) {
|
|
|
9588
10526
|
const e = eventQueue.shift();
|
|
9589
10527
|
if (e) onEvent(e);
|
|
9590
10528
|
} else {
|
|
9591
|
-
await new Promise((
|
|
9592
|
-
eventQueue.setResolver(
|
|
10529
|
+
await new Promise((resolve7) => {
|
|
10530
|
+
eventQueue.setResolver(resolve7);
|
|
9593
10531
|
});
|
|
9594
10532
|
}
|
|
9595
10533
|
}
|
|
@@ -9613,15 +10551,15 @@ async function executeSubagent(options) {
|
|
|
9613
10551
|
}
|
|
9614
10552
|
|
|
9615
10553
|
// src/tools/agent-tool.ts
|
|
9616
|
-
var agentToolSchema =
|
|
9617
|
-
prompt:
|
|
9618
|
-
agentName:
|
|
9619
|
-
|
|
10554
|
+
var agentToolSchema = Type21.Object({
|
|
10555
|
+
prompt: Type21.String({ description: "The task or question for the subagent to work on" }),
|
|
10556
|
+
agentName: Type21.Optional(
|
|
10557
|
+
Type21.String({
|
|
9620
10558
|
description: "Name of a defined agent (from AGENT.md) to use. If omitted, a general-purpose subagent is created."
|
|
9621
10559
|
})
|
|
9622
10560
|
),
|
|
9623
|
-
description:
|
|
9624
|
-
|
|
10561
|
+
description: Type21.Optional(
|
|
10562
|
+
Type21.String({
|
|
9625
10563
|
description: "A short (3-5 word) label describing what the subagent will do"
|
|
9626
10564
|
})
|
|
9627
10565
|
)
|
|
@@ -9732,7 +10670,7 @@ import { readFileSync as readFileSync5 } from "fs";
|
|
|
9732
10670
|
|
|
9733
10671
|
// src/project-analyzer.ts
|
|
9734
10672
|
import { readFileSync as readFileSync4, existsSync as existsSync9 } from "fs";
|
|
9735
|
-
import { join as
|
|
10673
|
+
import { join as join14 } from "path";
|
|
9736
10674
|
var ProjectAnalyzer = class {
|
|
9737
10675
|
projectPath;
|
|
9738
10676
|
constructor(projectPath = process.cwd()) {
|
|
@@ -9743,7 +10681,7 @@ var ProjectAnalyzer = class {
|
|
|
9743
10681
|
return this.generateDescription(info);
|
|
9744
10682
|
}
|
|
9745
10683
|
getProjectInfo() {
|
|
9746
|
-
const packageJsonPath =
|
|
10684
|
+
const packageJsonPath = join14(this.projectPath, "package.json");
|
|
9747
10685
|
const info = { description: "" };
|
|
9748
10686
|
if (existsSync9(packageJsonPath)) {
|
|
9749
10687
|
const packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf-8"));
|
|
@@ -9785,15 +10723,15 @@ var ProjectAnalyzer = class {
|
|
|
9785
10723
|
}
|
|
9786
10724
|
}
|
|
9787
10725
|
detectBuildTool(_scripts = {}, deps, info) {
|
|
9788
|
-
if (deps.vite || existsSync9(
|
|
10726
|
+
if (deps.vite || existsSync9(join14(this.projectPath, "vite.config.js")) || existsSync9(join14(this.projectPath, "vite.config.ts"))) {
|
|
9789
10727
|
info.buildTool = "Vite";
|
|
9790
10728
|
return;
|
|
9791
10729
|
}
|
|
9792
|
-
if (deps.webpack || existsSync9(
|
|
10730
|
+
if (deps.webpack || existsSync9(join14(this.projectPath, "webpack.config.js"))) {
|
|
9793
10731
|
info.buildTool = "Webpack";
|
|
9794
10732
|
return;
|
|
9795
10733
|
}
|
|
9796
|
-
if (deps.rollup || existsSync9(
|
|
10734
|
+
if (deps.rollup || existsSync9(join14(this.projectPath, "rollup.config.js"))) {
|
|
9797
10735
|
info.buildTool = "Rollup";
|
|
9798
10736
|
return;
|
|
9799
10737
|
}
|
|
@@ -9843,40 +10781,40 @@ var ProjectAnalyzer = class {
|
|
|
9843
10781
|
info.uiLibraries = [...new Set(uiLibs)].filter(Boolean);
|
|
9844
10782
|
}
|
|
9845
10783
|
detectProjectType(info) {
|
|
9846
|
-
if (existsSync9(
|
|
10784
|
+
if (existsSync9(join14(this.projectPath, ".xcodeproj")) || existsSync9(join14(this.projectPath, ".xcworkspace")) || existsSync9(join14(this.projectPath, "project.pbxproj"))) {
|
|
9847
10785
|
info.projectType = "Xcode";
|
|
9848
|
-
} else if (existsSync9(
|
|
10786
|
+
} else if (existsSync9(join14(this.projectPath, "build.gradle")) || existsSync9(join14(this.projectPath, "build.gradle.kts")) || existsSync9(join14(this.projectPath, "app/build.gradle")) || existsSync9(join14(this.projectPath, "settings.gradle"))) {
|
|
9849
10787
|
info.projectType = "Android Studio";
|
|
9850
|
-
} else if (existsSync9(
|
|
10788
|
+
} else if (existsSync9(join14(this.projectPath, "pubspec.yaml"))) {
|
|
9851
10789
|
info.projectType = "Flutter";
|
|
9852
|
-
} else if (existsSync9(
|
|
10790
|
+
} else if (existsSync9(join14(this.projectPath, "go.mod"))) {
|
|
9853
10791
|
info.projectType = "Go";
|
|
9854
|
-
} else if (existsSync9(
|
|
10792
|
+
} else if (existsSync9(join14(this.projectPath, "Cargo.toml"))) {
|
|
9855
10793
|
info.projectType = "Rust";
|
|
9856
|
-
} else if (existsSync9(
|
|
10794
|
+
} else if (existsSync9(join14(this.projectPath, "requirements.txt")) || existsSync9(join14(this.projectPath, "pyproject.toml")) || existsSync9(join14(this.projectPath, "setup.py"))) {
|
|
9857
10795
|
info.projectType = "Python";
|
|
9858
|
-
} else if (existsSync9(
|
|
10796
|
+
} else if (existsSync9(join14(this.projectPath, "Gemfile"))) {
|
|
9859
10797
|
info.projectType = "Ruby";
|
|
9860
|
-
} else if (existsSync9(
|
|
10798
|
+
} else if (existsSync9(join14(this.projectPath, "composer.json"))) {
|
|
9861
10799
|
info.projectType = "PHP";
|
|
9862
|
-
} else if (existsSync9(
|
|
10800
|
+
} else if (existsSync9(join14(this.projectPath, "pom.xml")) || existsSync9(join14(this.projectPath, "build.xml"))) {
|
|
9863
10801
|
info.projectType = "Java";
|
|
9864
|
-
} else if (existsSync9(
|
|
10802
|
+
} else if (existsSync9(join14(this.projectPath, ".csproj")) || existsSync9(join14(this.projectPath, "project.json"))) {
|
|
9865
10803
|
info.projectType = ".NET";
|
|
9866
10804
|
}
|
|
9867
10805
|
}
|
|
9868
10806
|
detectPackageManager(info) {
|
|
9869
|
-
if (existsSync9(
|
|
10807
|
+
if (existsSync9(join14(this.projectPath, "bun.lockb"))) {
|
|
9870
10808
|
info.packageManager = "Bun";
|
|
9871
|
-
} else if (existsSync9(
|
|
10809
|
+
} else if (existsSync9(join14(this.projectPath, "bun.lock"))) {
|
|
9872
10810
|
info.packageManager = "Bun";
|
|
9873
|
-
} else if (existsSync9(
|
|
10811
|
+
} else if (existsSync9(join14(this.projectPath, "pnpm-lock.yaml"))) {
|
|
9874
10812
|
info.packageManager = "pnpm";
|
|
9875
|
-
} else if (existsSync9(
|
|
10813
|
+
} else if (existsSync9(join14(this.projectPath, "yarn.lock"))) {
|
|
9876
10814
|
info.packageManager = "Yarn";
|
|
9877
|
-
} else if (existsSync9(
|
|
10815
|
+
} else if (existsSync9(join14(this.projectPath, "package-lock.json"))) {
|
|
9878
10816
|
info.packageManager = "npm";
|
|
9879
|
-
} else if (existsSync9(
|
|
10817
|
+
} else if (existsSync9(join14(this.projectPath, "npm-shrinkwrap.json"))) {
|
|
9880
10818
|
info.packageManager = "npm";
|
|
9881
10819
|
}
|
|
9882
10820
|
}
|
|
@@ -9938,8 +10876,8 @@ function analyzeProject(projectPath) {
|
|
|
9938
10876
|
}
|
|
9939
10877
|
|
|
9940
10878
|
// src/features/rules/rules.manager.ts
|
|
9941
|
-
import { readdir as readdir3, readFile as
|
|
9942
|
-
import { join as
|
|
10879
|
+
import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
|
|
10880
|
+
import { join as join15, relative as relative4 } from "path";
|
|
9943
10881
|
import { existsSync as existsSync10 } from "fs";
|
|
9944
10882
|
import { homedir as homedir5 } from "os";
|
|
9945
10883
|
function parseRuleFrontmatter(markdown) {
|
|
@@ -9983,10 +10921,10 @@ function parseRuleFrontmatter(markdown) {
|
|
|
9983
10921
|
return { metadata, content };
|
|
9984
10922
|
}
|
|
9985
10923
|
function getGlobalRulesDir() {
|
|
9986
|
-
return
|
|
10924
|
+
return join15(homedir5(), ".agents", "rules");
|
|
9987
10925
|
}
|
|
9988
10926
|
function getProjectRulesDir(threadPath) {
|
|
9989
|
-
return
|
|
10927
|
+
return join15(threadPath, ".agents", "rules");
|
|
9990
10928
|
}
|
|
9991
10929
|
var RuleManager = class {
|
|
9992
10930
|
/**
|
|
@@ -10019,13 +10957,13 @@ var RuleManager = class {
|
|
|
10019
10957
|
try {
|
|
10020
10958
|
const entries = await readdir3(dir, { withFileTypes: true });
|
|
10021
10959
|
for (const entry of entries) {
|
|
10022
|
-
const fullPath =
|
|
10960
|
+
const fullPath = join15(dir, entry.name);
|
|
10023
10961
|
if (entry.isDirectory()) {
|
|
10024
10962
|
const subRules = await this.loadRulesFromDir(fullPath, threadPath, scope);
|
|
10025
10963
|
rules.push(...subRules);
|
|
10026
10964
|
} else if (entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".mdc"))) {
|
|
10027
10965
|
try {
|
|
10028
|
-
const fileContent = await
|
|
10966
|
+
const fileContent = await readFile6(fullPath, "utf-8");
|
|
10029
10967
|
const { metadata, content } = parseRuleFrontmatter(fileContent);
|
|
10030
10968
|
const ruleName = entry.name.replace(/\.(md|mdc)$/, "");
|
|
10031
10969
|
const relativePath = relative4(threadPath, fullPath);
|
|
@@ -10527,7 +11465,7 @@ var ShellPermissionGate = class {
|
|
|
10527
11465
|
wildcardOptions: buildWildcardOptions(command)
|
|
10528
11466
|
};
|
|
10529
11467
|
this.config.onPermissionRequired?.(request);
|
|
10530
|
-
return new Promise((
|
|
11468
|
+
return new Promise((resolve7, reject) => {
|
|
10531
11469
|
const cleanup = () => {
|
|
10532
11470
|
pendingPermissions2.delete(toolCallId);
|
|
10533
11471
|
signal?.removeEventListener("abort", onAbort);
|
|
@@ -10545,7 +11483,7 @@ var ShellPermissionGate = class {
|
|
|
10545
11483
|
pendingPermissions2.set(toolCallId, {
|
|
10546
11484
|
resolve: (decision, pattern) => {
|
|
10547
11485
|
cleanup();
|
|
10548
|
-
void this.applyDecision(decision, command, pattern).then(() =>
|
|
11486
|
+
void this.applyDecision(decision, command, pattern).then(() => resolve7(decision));
|
|
10549
11487
|
},
|
|
10550
11488
|
reject: (error) => {
|
|
10551
11489
|
cleanup();
|
|
@@ -10974,8 +11912,8 @@ ${userPrompt}`;
|
|
|
10974
11912
|
if (eventQueue.length > 0) {
|
|
10975
11913
|
yield eventQueue.shift();
|
|
10976
11914
|
} else {
|
|
10977
|
-
await new Promise((
|
|
10978
|
-
eventQueue.setResolver(
|
|
11915
|
+
await new Promise((resolve7) => {
|
|
11916
|
+
eventQueue.setResolver(resolve7);
|
|
10979
11917
|
});
|
|
10980
11918
|
}
|
|
10981
11919
|
}
|
|
@@ -11131,14 +12069,14 @@ var ProcessingStateManagerImpl = class {
|
|
|
11131
12069
|
|
|
11132
12070
|
// src/core/env-manager.ts
|
|
11133
12071
|
import { promises as fs } from "fs";
|
|
11134
|
-
import { join as
|
|
12072
|
+
import { join as join16 } from "path";
|
|
11135
12073
|
function updateRuntimeEnv(values) {
|
|
11136
12074
|
for (const [key, value] of Object.entries(values)) {
|
|
11137
12075
|
process.env[key] = value;
|
|
11138
12076
|
}
|
|
11139
12077
|
}
|
|
11140
12078
|
async function updateEnvFile(keyNames) {
|
|
11141
|
-
const envPath =
|
|
12079
|
+
const envPath = join16(process.cwd(), ".env");
|
|
11142
12080
|
let content = "";
|
|
11143
12081
|
try {
|
|
11144
12082
|
content = await fs.readFile(envPath, "utf-8");
|
|
@@ -11167,7 +12105,7 @@ async function updateEnvFile(keyNames) {
|
|
|
11167
12105
|
await fs.writeFile(envPath, newLines.join("\n") + "\n", "utf-8");
|
|
11168
12106
|
}
|
|
11169
12107
|
async function readEnvFile() {
|
|
11170
|
-
const envPath =
|
|
12108
|
+
const envPath = join16(process.cwd(), ".env");
|
|
11171
12109
|
const envMap = {};
|
|
11172
12110
|
try {
|
|
11173
12111
|
const content = await fs.readFile(envPath, "utf-8");
|
|
@@ -11190,7 +12128,7 @@ async function readEnvFile() {
|
|
|
11190
12128
|
import { Hono } from "hono";
|
|
11191
12129
|
|
|
11192
12130
|
// src/agent/agent-run-guard.ts
|
|
11193
|
-
import { randomUUID as
|
|
12131
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
11194
12132
|
var DEFAULT_LONG_RUNNING_TURN_LIMIT = 50;
|
|
11195
12133
|
var LONG_RUNNING_CONFIRMATION_QUESTION = "The agent has been working for a long time. Do you want to continue?";
|
|
11196
12134
|
var LONG_RUNNING_CONFIRMATION_OPTIONS = ["Yes", "No"];
|
|
@@ -11232,7 +12170,7 @@ function shouldPromptForLongRunningConfirmation(state) {
|
|
|
11232
12170
|
return state.turnCount >= state.maximumTurnCount;
|
|
11233
12171
|
}
|
|
11234
12172
|
function createLongRunningConfirmationRequest(maximumTurnCount) {
|
|
11235
|
-
const toolCallId = `long-running-confirmation-${
|
|
12173
|
+
const toolCallId = `long-running-confirmation-${randomUUID4()}`;
|
|
11236
12174
|
const event = {
|
|
11237
12175
|
type: "long_running_confirmation",
|
|
11238
12176
|
prompt: {
|
|
@@ -11258,7 +12196,7 @@ function createLongRunningConfirmationRequest(maximumTurnCount) {
|
|
|
11258
12196
|
toolCallId,
|
|
11259
12197
|
event,
|
|
11260
12198
|
waitForAnswer: (signal) => {
|
|
11261
|
-
return new Promise((
|
|
12199
|
+
return new Promise((resolve7, reject) => {
|
|
11262
12200
|
const resolvers = getLongRunningResolvers();
|
|
11263
12201
|
const cleanup = () => {
|
|
11264
12202
|
resolvers.delete(toolCallId);
|
|
@@ -11281,7 +12219,7 @@ function createLongRunningConfirmationRequest(maximumTurnCount) {
|
|
|
11281
12219
|
resolvers.set(toolCallId, {
|
|
11282
12220
|
resolve: (answer) => {
|
|
11283
12221
|
cleanup();
|
|
11284
|
-
|
|
12222
|
+
resolve7(answer);
|
|
11285
12223
|
},
|
|
11286
12224
|
reject: (error) => {
|
|
11287
12225
|
cleanup();
|
|
@@ -11608,11 +12546,11 @@ function createWebFetchPermissionRoutes() {
|
|
|
11608
12546
|
import { Hono as Hono4 } from "hono";
|
|
11609
12547
|
|
|
11610
12548
|
// src/features/chat/chat-post.route.ts
|
|
11611
|
-
import { randomUUID as
|
|
12549
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
11612
12550
|
|
|
11613
12551
|
// src/features/skills/skills.manager.ts
|
|
11614
|
-
import { readdir as readdir4, readFile as
|
|
11615
|
-
import { join as
|
|
12552
|
+
import { readdir as readdir4, readFile as readFile7 } from "fs/promises";
|
|
12553
|
+
import { join as join17 } from "path";
|
|
11616
12554
|
import { existsSync as existsSync11 } from "fs";
|
|
11617
12555
|
import { homedir as homedir6 } from "os";
|
|
11618
12556
|
function parseFrontmatter(markdown) {
|
|
@@ -11660,10 +12598,10 @@ function parseFrontmatter(markdown) {
|
|
|
11660
12598
|
return { metadata, content };
|
|
11661
12599
|
}
|
|
11662
12600
|
function getGlobalSkillsDir() {
|
|
11663
|
-
return
|
|
12601
|
+
return join17(homedir6(), ".agents", "skills");
|
|
11664
12602
|
}
|
|
11665
12603
|
function getProjectSkillsDir(threadPath) {
|
|
11666
|
-
return
|
|
12604
|
+
return join17(threadPath, ".agents", "skills");
|
|
11667
12605
|
}
|
|
11668
12606
|
function validateSkillName(name) {
|
|
11669
12607
|
if (!name || name.length === 0 || name.length > 64) {
|
|
@@ -11716,14 +12654,14 @@ var SkillManager = class {
|
|
|
11716
12654
|
for (const entry of entries) {
|
|
11717
12655
|
if (!entry.isDirectory()) continue;
|
|
11718
12656
|
const skillDirName = entry.name;
|
|
11719
|
-
const skillPath =
|
|
11720
|
-
const skillFilePath =
|
|
12657
|
+
const skillPath = join17(dir, skillDirName);
|
|
12658
|
+
const skillFilePath = join17(skillPath, "SKILL.md");
|
|
11721
12659
|
if (!existsSync11(skillFilePath)) {
|
|
11722
12660
|
console.warn(`Skipping skill directory ${skillDirName}: SKILL.md not found`);
|
|
11723
12661
|
continue;
|
|
11724
12662
|
}
|
|
11725
12663
|
try {
|
|
11726
|
-
const fileContent = await
|
|
12664
|
+
const fileContent = await readFile7(skillFilePath, "utf-8");
|
|
11727
12665
|
const { metadata, content } = parseFrontmatter(fileContent);
|
|
11728
12666
|
if (!metadata.name) {
|
|
11729
12667
|
console.warn(`Skipping skill in ${skillDirName}: missing 'name' in frontmatter`);
|
|
@@ -11896,8 +12834,8 @@ async function activateSkills(allSkills, taskDescription, thread, options) {
|
|
|
11896
12834
|
}
|
|
11897
12835
|
|
|
11898
12836
|
// src/features/agents/agents.manager.ts
|
|
11899
|
-
import { readdir as readdir5, readFile as
|
|
11900
|
-
import { join as
|
|
12837
|
+
import { readdir as readdir5, readFile as readFile8 } from "fs/promises";
|
|
12838
|
+
import { join as join18 } from "path";
|
|
11901
12839
|
import { existsSync as existsSync12 } from "fs";
|
|
11902
12840
|
import { homedir as homedir7 } from "os";
|
|
11903
12841
|
function parseFrontmatter2(markdown) {
|
|
@@ -11955,10 +12893,10 @@ function validateDescription2(description) {
|
|
|
11955
12893
|
return !!description && description.length > 0 && description.length <= 1024;
|
|
11956
12894
|
}
|
|
11957
12895
|
function getGlobalAgentsDir() {
|
|
11958
|
-
return
|
|
12896
|
+
return join18(homedir7(), ".agents", "agents");
|
|
11959
12897
|
}
|
|
11960
12898
|
function getProjectAgentsDir(threadPath) {
|
|
11961
|
-
return
|
|
12899
|
+
return join18(threadPath, ".agents", "agents");
|
|
11962
12900
|
}
|
|
11963
12901
|
var AgentsManager = class {
|
|
11964
12902
|
/**
|
|
@@ -11990,14 +12928,14 @@ var AgentsManager = class {
|
|
|
11990
12928
|
for (const entry of entries) {
|
|
11991
12929
|
if (!entry.isDirectory()) continue;
|
|
11992
12930
|
const agentDirName = entry.name;
|
|
11993
|
-
const agentPath =
|
|
11994
|
-
const agentFilePath =
|
|
12931
|
+
const agentPath = join18(dir, agentDirName);
|
|
12932
|
+
const agentFilePath = join18(agentPath, "AGENT.md");
|
|
11995
12933
|
if (!existsSync12(agentFilePath)) {
|
|
11996
12934
|
console.warn(`[agents] Skipping agent directory ${agentDirName}: AGENT.md not found`);
|
|
11997
12935
|
continue;
|
|
11998
12936
|
}
|
|
11999
12937
|
try {
|
|
12000
|
-
const fileContent = await
|
|
12938
|
+
const fileContent = await readFile8(agentFilePath, "utf-8");
|
|
12001
12939
|
const { metadata, content } = parseFrontmatter2(fileContent);
|
|
12002
12940
|
if (!metadata.name) {
|
|
12003
12941
|
console.warn(
|
|
@@ -12180,12 +13118,12 @@ function extractAssistantContent(events, fallback) {
|
|
|
12180
13118
|
|
|
12181
13119
|
// src/features/chat/chat-post.route.ts
|
|
12182
13120
|
init_database();
|
|
12183
|
-
import { readFile as
|
|
12184
|
-
import { join as
|
|
13121
|
+
import { readFile as readFile9, unlink as unlink2 } from "fs/promises";
|
|
13122
|
+
import { join as join19 } from "path";
|
|
12185
13123
|
|
|
12186
13124
|
// src/features/project-todos/project-todos.database.ts
|
|
12187
13125
|
init_database();
|
|
12188
|
-
import { randomUUID as
|
|
13126
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
12189
13127
|
function serializeTodo(row) {
|
|
12190
13128
|
return {
|
|
12191
13129
|
...row,
|
|
@@ -12210,7 +13148,7 @@ async function getTodoById(db, todoId) {
|
|
|
12210
13148
|
}
|
|
12211
13149
|
async function insertTodo(projectId, title, description) {
|
|
12212
13150
|
const db = await getDatabase();
|
|
12213
|
-
const id =
|
|
13151
|
+
const id = randomUUID5();
|
|
12214
13152
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12215
13153
|
await db.execute({
|
|
12216
13154
|
sql: `INSERT INTO project_todos (id, projectId, title, description, status, working, createdAt, updatedAt)
|
|
@@ -12332,7 +13270,7 @@ async function invalidateGitStatusCache(db, threadId) {
|
|
|
12332
13270
|
|
|
12333
13271
|
// src/features/account/account-get-info.route.ts
|
|
12334
13272
|
init_database();
|
|
12335
|
-
import { randomUUID as
|
|
13273
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
12336
13274
|
var KEY_BALANCE = "PromptBalance";
|
|
12337
13275
|
var KEY_VERIFICATION = "PromptBalanceVerification";
|
|
12338
13276
|
var KEY_PLAN = "Plan";
|
|
@@ -12378,7 +13316,7 @@ async function getOrCreateClientReferenceId() {
|
|
|
12378
13316
|
if (existing !== null && existing !== void 0) {
|
|
12379
13317
|
return existing;
|
|
12380
13318
|
}
|
|
12381
|
-
const id =
|
|
13319
|
+
const id = randomUUID6();
|
|
12382
13320
|
await setState(db, KEY_CLIENT_REFERENCE_ID, id);
|
|
12383
13321
|
return id;
|
|
12384
13322
|
}
|
|
@@ -12659,7 +13597,7 @@ async function deserializeProject(db, row) {
|
|
|
12659
13597
|
// src/features/chat/chat-post.route.ts
|
|
12660
13598
|
init_utils();
|
|
12661
13599
|
function runValidationScript(script, cwd) {
|
|
12662
|
-
return new Promise((
|
|
13600
|
+
return new Promise((resolve7) => {
|
|
12663
13601
|
const child = spawnShell(script, { cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
12664
13602
|
let output = "";
|
|
12665
13603
|
const stdoutDecoder = new TextDecoder();
|
|
@@ -12673,12 +13611,12 @@ function runValidationScript(script, cwd) {
|
|
|
12673
13611
|
child.on("close", (code) => {
|
|
12674
13612
|
output += stdoutDecoder.decode();
|
|
12675
13613
|
output += stderrDecoder.decode();
|
|
12676
|
-
|
|
13614
|
+
resolve7({ exitCode: code ?? 1, output: output.trim() });
|
|
12677
13615
|
});
|
|
12678
13616
|
child.on("error", (err) => {
|
|
12679
13617
|
output += stdoutDecoder.decode();
|
|
12680
13618
|
output += stderrDecoder.decode();
|
|
12681
|
-
|
|
13619
|
+
resolve7({
|
|
12682
13620
|
exitCode: 1,
|
|
12683
13621
|
output: `${output}${output ? "\n" : ""}${err.message}`.trim()
|
|
12684
13622
|
});
|
|
@@ -12795,7 +13733,7 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
|
|
|
12795
13733
|
}
|
|
12796
13734
|
let conversationId = thread.currentConversationId;
|
|
12797
13735
|
if (!conversationId) {
|
|
12798
|
-
conversationId =
|
|
13736
|
+
conversationId = randomUUID7();
|
|
12799
13737
|
const db2 = await getDatabase();
|
|
12800
13738
|
await db2.execute({
|
|
12801
13739
|
sql: "DELETE FROM todos WHERE threadId = ?",
|
|
@@ -13105,7 +14043,7 @@ ${result.output}`;
|
|
|
13105
14043
|
};
|
|
13106
14044
|
processingStateManager.pushEvent(threadId, iterationEvent);
|
|
13107
14045
|
yield iterationEvent;
|
|
13108
|
-
const newConversationId =
|
|
14046
|
+
const newConversationId = randomUUID7();
|
|
13109
14047
|
await threadManager.updateThread(threadId, {
|
|
13110
14048
|
currentConversationId: newConversationId
|
|
13111
14049
|
});
|
|
@@ -13197,11 +14135,11 @@ ${result.output}`;
|
|
|
13197
14135
|
try {
|
|
13198
14136
|
const todo = await getTodoByThreadId(db2, threadId);
|
|
13199
14137
|
if (todo && todo.status === "Plan") {
|
|
13200
|
-
const planFilePath =
|
|
14138
|
+
const planFilePath = join19(threadPath, `${todo.id}-plan.md`);
|
|
13201
14139
|
try {
|
|
13202
|
-
const planContent = await
|
|
14140
|
+
const planContent = await readFile9(planFilePath, "utf-8");
|
|
13203
14141
|
await updateTodo(db2, todo.id, { description: planContent.trim() });
|
|
13204
|
-
await
|
|
14142
|
+
await unlink2(planFilePath);
|
|
13205
14143
|
} catch {
|
|
13206
14144
|
}
|
|
13207
14145
|
}
|
|
@@ -13538,7 +14476,7 @@ ${summary}`;
|
|
|
13538
14476
|
}
|
|
13539
14477
|
|
|
13540
14478
|
// src/features/chat/chat-delete.route.ts
|
|
13541
|
-
import { randomUUID as
|
|
14479
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
13542
14480
|
init_database();
|
|
13543
14481
|
async function deleteChat(c, threadManager) {
|
|
13544
14482
|
try {
|
|
@@ -13560,7 +14498,7 @@ async function deleteChat(c, threadManager) {
|
|
|
13560
14498
|
sql: "DELETE FROM todos WHERE threadId = ?",
|
|
13561
14499
|
args: [threadId]
|
|
13562
14500
|
});
|
|
13563
|
-
const newConversationId =
|
|
14501
|
+
const newConversationId = randomUUID8();
|
|
13564
14502
|
await threadManager.updateThread(threadId, { currentConversationId: newConversationId });
|
|
13565
14503
|
return successResponse(
|
|
13566
14504
|
c,
|
|
@@ -13589,7 +14527,7 @@ function subscribeToChatStream(c, processingStateManager) {
|
|
|
13589
14527
|
return c.json({ error: "Thread is not currently processing" }, 404);
|
|
13590
14528
|
}
|
|
13591
14529
|
return stream2(c, async (writer) => {
|
|
13592
|
-
let
|
|
14530
|
+
let resolve7 = null;
|
|
13593
14531
|
const queue = [];
|
|
13594
14532
|
let done = false;
|
|
13595
14533
|
const replay = processingStateManager.getBufferedEvents(threadId).slice();
|
|
@@ -13600,18 +14538,18 @@ function subscribeToChatStream(c, processingStateManager) {
|
|
|
13600
14538
|
} else {
|
|
13601
14539
|
queue.push(event);
|
|
13602
14540
|
}
|
|
13603
|
-
if (
|
|
13604
|
-
|
|
13605
|
-
|
|
14541
|
+
if (resolve7) {
|
|
14542
|
+
resolve7();
|
|
14543
|
+
resolve7 = null;
|
|
13606
14544
|
}
|
|
13607
14545
|
});
|
|
13608
14546
|
const finishedBeforeSubscribe = !processingStateManager.isProcessing(threadId);
|
|
13609
14547
|
writer.onAbort(() => {
|
|
13610
14548
|
done = true;
|
|
13611
14549
|
unsubscribe();
|
|
13612
|
-
if (
|
|
13613
|
-
|
|
13614
|
-
|
|
14550
|
+
if (resolve7) {
|
|
14551
|
+
resolve7();
|
|
14552
|
+
resolve7 = null;
|
|
13615
14553
|
}
|
|
13616
14554
|
});
|
|
13617
14555
|
try {
|
|
@@ -13636,7 +14574,7 @@ function subscribeToChatStream(c, processingStateManager) {
|
|
|
13636
14574
|
}
|
|
13637
14575
|
if (!done) {
|
|
13638
14576
|
await new Promise((r) => {
|
|
13639
|
-
|
|
14577
|
+
resolve7 = r;
|
|
13640
14578
|
});
|
|
13641
14579
|
}
|
|
13642
14580
|
}
|
|
@@ -13684,10 +14622,10 @@ function createChatRoutes(threadManager, agentExecutor, conversationManager, pro
|
|
|
13684
14622
|
init_database();
|
|
13685
14623
|
|
|
13686
14624
|
// src/features/conversations/conversations.database.ts
|
|
13687
|
-
import { randomUUID as
|
|
14625
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
13688
14626
|
async function insertMessage(db, threadId, conversationId, message, model, attachments, planMode, imageMode, ralphMode, checkpointRef) {
|
|
13689
14627
|
try {
|
|
13690
|
-
const messageId =
|
|
14628
|
+
const messageId = randomUUID9();
|
|
13691
14629
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
13692
14630
|
await db.execute(
|
|
13693
14631
|
`
|
|
@@ -15986,17 +16924,17 @@ async function getOnboardingStatus(c, metadataManager) {
|
|
|
15986
16924
|
init_utils();
|
|
15987
16925
|
async function getGitCheck(c) {
|
|
15988
16926
|
try {
|
|
15989
|
-
const gitInstalled = await new Promise((
|
|
16927
|
+
const gitInstalled = await new Promise((resolve7) => {
|
|
15990
16928
|
const proc = spawnProcess("git", ["--version"]);
|
|
15991
16929
|
let _err = "";
|
|
15992
16930
|
proc.stderr?.on("data", (d) => {
|
|
15993
16931
|
_err += d.toString();
|
|
15994
16932
|
});
|
|
15995
16933
|
proc.on("close", (code) => {
|
|
15996
|
-
|
|
16934
|
+
resolve7(code === 0);
|
|
15997
16935
|
});
|
|
15998
16936
|
proc.on("error", () => {
|
|
15999
|
-
|
|
16937
|
+
resolve7(false);
|
|
16000
16938
|
});
|
|
16001
16939
|
});
|
|
16002
16940
|
return c.json({ gitInstalled });
|
|
@@ -16030,13 +16968,13 @@ async function postOnboardingReset(c, metadataManager) {
|
|
|
16030
16968
|
|
|
16031
16969
|
// src/features/onboarding/onboarding-get-recommended-models.route.ts
|
|
16032
16970
|
import { readFileSync as readFileSync6, existsSync as existsSync13 } from "fs";
|
|
16033
|
-
import { join as
|
|
16971
|
+
import { join as join20, dirname as dirname4 } from "path";
|
|
16034
16972
|
import { fileURLToPath } from "url";
|
|
16035
16973
|
function getRecommendedModelsFilePath() {
|
|
16036
16974
|
const moduleDir = dirname4(fileURLToPath(import.meta.url));
|
|
16037
16975
|
const candidates = [
|
|
16038
|
-
|
|
16039
|
-
|
|
16976
|
+
join20(moduleDir, "../../../recommended-models.txt"),
|
|
16977
|
+
join20(moduleDir, "recommended-models.txt")
|
|
16040
16978
|
];
|
|
16041
16979
|
for (const candidate of candidates) {
|
|
16042
16980
|
if (existsSync13(candidate)) {
|
|
@@ -16691,12 +17629,12 @@ async function handleCheckRunning(c, projectManager) {
|
|
|
16691
17629
|
}
|
|
16692
17630
|
|
|
16693
17631
|
// src/core/run-command-detector.ts
|
|
16694
|
-
import { readFile as
|
|
16695
|
-
import { join as
|
|
17632
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
17633
|
+
import { join as join21 } from "path";
|
|
16696
17634
|
async function detectPackageManager(projectPath) {
|
|
16697
17635
|
try {
|
|
16698
|
-
const packageJsonPath =
|
|
16699
|
-
const content = await
|
|
17636
|
+
const packageJsonPath = join21(projectPath, "package.json");
|
|
17637
|
+
const content = await readFile10(packageJsonPath, "utf-8");
|
|
16700
17638
|
const packageJson = JSON.parse(content);
|
|
16701
17639
|
if (packageJson.packageManager) {
|
|
16702
17640
|
const pm = packageJson.packageManager.split("@")[0];
|
|
@@ -16711,8 +17649,8 @@ async function detectPackageManager(projectPath) {
|
|
|
16711
17649
|
}
|
|
16712
17650
|
async function detectRunScripts(projectPath) {
|
|
16713
17651
|
try {
|
|
16714
|
-
const packageJsonPath =
|
|
16715
|
-
const content = await
|
|
17652
|
+
const packageJsonPath = join21(projectPath, "package.json");
|
|
17653
|
+
const content = await readFile10(packageJsonPath, "utf-8");
|
|
16716
17654
|
const packageJson = JSON.parse(content);
|
|
16717
17655
|
const scripts = packageJson.scripts ?? {};
|
|
16718
17656
|
return {
|
|
@@ -16757,8 +17695,8 @@ async function suggestRunCommand(projectPath) {
|
|
|
16757
17695
|
}
|
|
16758
17696
|
|
|
16759
17697
|
// src/features/projects/projects-package-scripts.route.ts
|
|
16760
|
-
import { readFile as
|
|
16761
|
-
import { join as
|
|
17698
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
17699
|
+
import { join as join22 } from "path";
|
|
16762
17700
|
import { glob } from "glob";
|
|
16763
17701
|
function formatScriptName(scriptName) {
|
|
16764
17702
|
return scriptName.replace(/[-:_]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
|
|
@@ -16786,8 +17724,8 @@ async function findPackageScripts(projectPath) {
|
|
|
16786
17724
|
const packageManager = await detectPackageManager(projectPath);
|
|
16787
17725
|
for (const filePath of packageJsonFiles) {
|
|
16788
17726
|
try {
|
|
16789
|
-
const fullPath =
|
|
16790
|
-
const content = await
|
|
17727
|
+
const fullPath = join22(projectPath, filePath);
|
|
17728
|
+
const content = await readFile11(fullPath, "utf-8");
|
|
16791
17729
|
const packageJson = JSON.parse(content);
|
|
16792
17730
|
if (packageJson.scripts) {
|
|
16793
17731
|
for (const [scriptName] of Object.entries(packageJson.scripts)) {
|
|
@@ -16834,8 +17772,8 @@ async function handleGetPackageScripts(c, projectManager) {
|
|
|
16834
17772
|
}
|
|
16835
17773
|
|
|
16836
17774
|
// src/features/projects/projects-ai-files.route.ts
|
|
16837
|
-
import { readdir as readdir6, readFile as
|
|
16838
|
-
import { join as
|
|
17775
|
+
import { readdir as readdir6, readFile as readFile12, writeFile as writeFile3, mkdir as mkdir2, access as access2, rm } from "fs/promises";
|
|
17776
|
+
import { join as join23, extname as extname4, basename } from "path";
|
|
16839
17777
|
import { existsSync as existsSync14 } from "fs";
|
|
16840
17778
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
16841
17779
|
".git",
|
|
@@ -16854,8 +17792,8 @@ async function buildFullTree(dirPath, relativeDirPath) {
|
|
|
16854
17792
|
try {
|
|
16855
17793
|
const entries = await readdir6(dirPath, { withFileTypes: true });
|
|
16856
17794
|
for (const entry of entries) {
|
|
16857
|
-
const entryRelPath =
|
|
16858
|
-
const entryAbsPath =
|
|
17795
|
+
const entryRelPath = join23(relativeDirPath, entry.name);
|
|
17796
|
+
const entryAbsPath = join23(dirPath, entry.name);
|
|
16859
17797
|
if (entry.isDirectory()) {
|
|
16860
17798
|
const children = await buildFullTree(entryAbsPath, entryRelPath);
|
|
16861
17799
|
nodes.push({
|
|
@@ -16883,8 +17821,8 @@ async function buildMarkdownFilteredTree(dirPath, relativeDirPath) {
|
|
|
16883
17821
|
try {
|
|
16884
17822
|
const entries = await readdir6(dirPath, { withFileTypes: true });
|
|
16885
17823
|
for (const entry of entries) {
|
|
16886
|
-
const entryRelPath =
|
|
16887
|
-
const entryAbsPath =
|
|
17824
|
+
const entryRelPath = join23(relativeDirPath, entry.name);
|
|
17825
|
+
const entryAbsPath = join23(dirPath, entry.name);
|
|
16888
17826
|
if (entry.isDirectory()) {
|
|
16889
17827
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
16890
17828
|
const children = await buildMarkdownFilteredTree(entryAbsPath, entryRelPath);
|
|
@@ -16919,8 +17857,8 @@ async function buildAIFileTree(projectPath) {
|
|
|
16919
17857
|
{ key: "agents", label: "Agents" }
|
|
16920
17858
|
];
|
|
16921
17859
|
for (const { key, label } of agentsFolders) {
|
|
16922
|
-
const relPath =
|
|
16923
|
-
const absPath =
|
|
17860
|
+
const relPath = join23(".agents", key);
|
|
17861
|
+
const absPath = join23(projectPath, relPath);
|
|
16924
17862
|
const exists = existsSync14(absPath);
|
|
16925
17863
|
if (exists) {
|
|
16926
17864
|
const children = await buildFullTree(absPath, relPath);
|
|
@@ -16955,7 +17893,7 @@ async function buildAIFileTree(projectPath) {
|
|
|
16955
17893
|
if (entry.name === ".agents" || entry.name === "AGENTS.md" || IGNORED_DIRS.has(entry.name))
|
|
16956
17894
|
continue;
|
|
16957
17895
|
const entryRelPath = entry.name;
|
|
16958
|
-
const entryAbsPath =
|
|
17896
|
+
const entryAbsPath = join23(projectPath, entry.name);
|
|
16959
17897
|
if (entry.isFile() && MARKDOWN_EXTS.has(extname4(entry.name))) {
|
|
16960
17898
|
nodes.push({
|
|
16961
17899
|
id: entryRelPath,
|
|
@@ -16982,7 +17920,7 @@ async function buildAIFileTree(projectPath) {
|
|
|
16982
17920
|
}
|
|
16983
17921
|
function validateFilePath(projectPath, filePath) {
|
|
16984
17922
|
if (!filePath) return null;
|
|
16985
|
-
const abs =
|
|
17923
|
+
const abs = join23(projectPath, filePath);
|
|
16986
17924
|
if (!abs.startsWith(projectPath + "/") && abs !== projectPath) return null;
|
|
16987
17925
|
return abs;
|
|
16988
17926
|
}
|
|
@@ -17069,7 +18007,7 @@ Links to important documentation, tools, or references.
|
|
|
17069
18007
|
} catch {
|
|
17070
18008
|
return c.json({ error: { code: "NOT_FOUND", message: `File not found: ${filePath}` } }, 404);
|
|
17071
18009
|
}
|
|
17072
|
-
const content = await
|
|
18010
|
+
const content = await readFile12(absPath, "utf-8");
|
|
17073
18011
|
return c.json({ content, path: filePath });
|
|
17074
18012
|
} catch (error) {
|
|
17075
18013
|
return errorResponse(
|
|
@@ -17108,7 +18046,7 @@ async function handleSaveAIFile(c, projectManager) {
|
|
|
17108
18046
|
if (!absPath) {
|
|
17109
18047
|
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
|
|
17110
18048
|
}
|
|
17111
|
-
const parentDir =
|
|
18049
|
+
const parentDir = join23(absPath, "..");
|
|
17112
18050
|
await mkdir2(parentDir, { recursive: true });
|
|
17113
18051
|
await writeFile3(absPath, content, "utf-8");
|
|
17114
18052
|
return c.json({ success: true, path: filePath });
|
|
@@ -17208,10 +18146,10 @@ async function handleCreateSkill(c, projectManager) {
|
|
|
17208
18146
|
if (!project) {
|
|
17209
18147
|
return errorResponse(c, ErrorCodes.PROJECT_NOT_FOUND, `Project not found: ${projectId}`, 404);
|
|
17210
18148
|
}
|
|
17211
|
-
const skillRelPath =
|
|
17212
|
-
const skillAbsPath =
|
|
17213
|
-
const skillFileRelPath =
|
|
17214
|
-
const skillFileAbsPath =
|
|
18149
|
+
const skillRelPath = join23(".agents", "skills", name);
|
|
18150
|
+
const skillAbsPath = join23(project.path, skillRelPath);
|
|
18151
|
+
const skillFileRelPath = join23(skillRelPath, "SKILL.md");
|
|
18152
|
+
const skillFileAbsPath = join23(skillAbsPath, "SKILL.md");
|
|
17215
18153
|
if (existsSync14(skillAbsPath)) {
|
|
17216
18154
|
return c.json(
|
|
17217
18155
|
{ error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
|
|
@@ -17283,11 +18221,14 @@ async function handleGetProjectEnvVars(c, projectManager) {
|
|
|
17283
18221
|
return errorResponse(c, ErrorCodes.PROJECT_NOT_FOUND, `Project not found: ${projectId}`, 404);
|
|
17284
18222
|
}
|
|
17285
18223
|
const db = await getDatabase();
|
|
17286
|
-
const rows = await
|
|
17287
|
-
const vars =
|
|
17288
|
-
|
|
17289
|
-
|
|
17290
|
-
|
|
18224
|
+
const rows = await listProjectEnvVars(db, projectId);
|
|
18225
|
+
const vars = await Promise.all(
|
|
18226
|
+
rows.map(async (row) => ({
|
|
18227
|
+
name: row.name,
|
|
18228
|
+
hasValue: true,
|
|
18229
|
+
value: await decryptProviderKey(row.encryptedValue)
|
|
18230
|
+
}))
|
|
18231
|
+
);
|
|
17291
18232
|
return c.json({ vars });
|
|
17292
18233
|
} catch (error) {
|
|
17293
18234
|
return errorResponse(
|
|
@@ -17457,7 +18398,7 @@ function createProjectRoutes(projectManager, threadManager) {
|
|
|
17457
18398
|
|
|
17458
18399
|
// src/features/projects/projects.manager.ts
|
|
17459
18400
|
init_utils();
|
|
17460
|
-
import { join as
|
|
18401
|
+
import { join as join26 } from "path";
|
|
17461
18402
|
import { realpathSync as realpathSync2 } from "fs";
|
|
17462
18403
|
import { rm as rm4 } from "fs/promises";
|
|
17463
18404
|
|
|
@@ -17524,13 +18465,13 @@ var ProcessManager = class extends EventEmitter {
|
|
|
17524
18465
|
}
|
|
17525
18466
|
};
|
|
17526
18467
|
const waitForQueue = () => {
|
|
17527
|
-
return new Promise((
|
|
18468
|
+
return new Promise((resolve7) => {
|
|
17528
18469
|
const queue = this.queues.get(projectId);
|
|
17529
18470
|
if (queue && queue.length > 0) {
|
|
17530
|
-
|
|
18471
|
+
resolve7();
|
|
17531
18472
|
} else {
|
|
17532
18473
|
const res = this.resolvers.get(projectId);
|
|
17533
|
-
if (res) res.push(
|
|
18474
|
+
if (res) res.push(resolve7);
|
|
17534
18475
|
}
|
|
17535
18476
|
});
|
|
17536
18477
|
};
|
|
@@ -17678,14 +18619,14 @@ var ProcessManager = class extends EventEmitter {
|
|
|
17678
18619
|
|
|
17679
18620
|
// src/features/projects/projects.creator.ts
|
|
17680
18621
|
init_utils();
|
|
17681
|
-
import { randomUUID as
|
|
17682
|
-
import { basename as basename2, join as
|
|
18622
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
18623
|
+
import { basename as basename2, join as join25, relative as relative5 } from "path";
|
|
17683
18624
|
import { access as access3, mkdir as mkdir4, rm as rm3, stat as stat3, readdir as readdir8 } from "fs/promises";
|
|
17684
18625
|
|
|
17685
18626
|
// src/features/scaffold/scaffold.runner.ts
|
|
17686
18627
|
init_utils();
|
|
17687
18628
|
import { existsSync as existsSync15 } from "fs";
|
|
17688
|
-
import { join as
|
|
18629
|
+
import { join as join24 } from "path";
|
|
17689
18630
|
import { mkdir as mkdir3, readdir as readdir7, stat as stat2, rename, rm as rm2, writeFile as writeFile4 } from "fs/promises";
|
|
17690
18631
|
import { createInterface as createInterface2 } from "readline";
|
|
17691
18632
|
|
|
@@ -18407,7 +19348,7 @@ function loadCatalog() {
|
|
|
18407
19348
|
}
|
|
18408
19349
|
async function writeAgentsMd(projectPath, agentsMd) {
|
|
18409
19350
|
if (!agentsMd) return;
|
|
18410
|
-
const agentsPath =
|
|
19351
|
+
const agentsPath = join24(projectPath, "AGENTS.md");
|
|
18411
19352
|
if (existsSync15(agentsPath)) return;
|
|
18412
19353
|
await writeFile4(agentsPath, agentsMd, "utf-8");
|
|
18413
19354
|
}
|
|
@@ -18565,7 +19506,7 @@ async function* runCommandStreaming(cwd, command) {
|
|
|
18565
19506
|
}
|
|
18566
19507
|
}
|
|
18567
19508
|
async function* createScaffoldedProjectStreaming(options) {
|
|
18568
|
-
const projectPath =
|
|
19509
|
+
const projectPath = join24(options.parentDir, options.threadId);
|
|
18569
19510
|
if (!existsSync15(options.parentDir)) {
|
|
18570
19511
|
yield {
|
|
18571
19512
|
type: "result",
|
|
@@ -18655,7 +19596,7 @@ async function* createScaffoldedProjectStreaming(options) {
|
|
|
18655
19596
|
}
|
|
18656
19597
|
try {
|
|
18657
19598
|
const projectName2 = getProjectName(options.projectName);
|
|
18658
|
-
const projectSubDir =
|
|
19599
|
+
const projectSubDir = join24(projectPath, projectName2);
|
|
18659
19600
|
try {
|
|
18660
19601
|
const subDirStat = await stat2(projectSubDir);
|
|
18661
19602
|
if (subDirStat.isDirectory()) {
|
|
@@ -18665,8 +19606,8 @@ async function* createScaffoldedProjectStreaming(options) {
|
|
|
18665
19606
|
};
|
|
18666
19607
|
const items = await readdir7(projectSubDir);
|
|
18667
19608
|
for (const item of items) {
|
|
18668
|
-
const oldPath =
|
|
18669
|
-
const newPath =
|
|
19609
|
+
const oldPath = join24(projectSubDir, item);
|
|
19610
|
+
const newPath = join24(projectPath, item);
|
|
18670
19611
|
await rename(oldPath, newPath);
|
|
18671
19612
|
}
|
|
18672
19613
|
await rm2(projectSubDir, { recursive: true, force: true });
|
|
@@ -18864,14 +19805,14 @@ var ProjectCreator = class {
|
|
|
18864
19805
|
}
|
|
18865
19806
|
let initialThreadId;
|
|
18866
19807
|
try {
|
|
18867
|
-
const projectId =
|
|
19808
|
+
const projectId = randomUUID10();
|
|
18868
19809
|
const existingProjects = await this.metadataManager.loadProjects();
|
|
18869
19810
|
const existingNames = existingProjects.map((p) => p.name);
|
|
18870
19811
|
const projectName = this.ensureUniqueProjectName(
|
|
18871
19812
|
this.deriveProjectName(gitUrl),
|
|
18872
19813
|
existingNames
|
|
18873
19814
|
);
|
|
18874
|
-
initialThreadId =
|
|
19815
|
+
initialThreadId = randomUUID10();
|
|
18875
19816
|
const initialThreadTitle = generateRandomThreadName();
|
|
18876
19817
|
const baseFolderName = this.toFolderName(this.deriveProjectName(gitUrl));
|
|
18877
19818
|
if (!baseFolderName) {
|
|
@@ -18882,7 +19823,7 @@ var ProjectCreator = class {
|
|
|
18882
19823
|
return;
|
|
18883
19824
|
}
|
|
18884
19825
|
const uniqueFolderName = await this.ensureUniqueFolderName(baseFolderName);
|
|
18885
|
-
const firstThreadPath =
|
|
19826
|
+
const firstThreadPath = join25(this.rootFolder, uniqueFolderName);
|
|
18886
19827
|
this.processingStateManager?.setProcessing(initialThreadId);
|
|
18887
19828
|
yield {
|
|
18888
19829
|
type: "progress",
|
|
@@ -19020,8 +19961,8 @@ var ProjectCreator = class {
|
|
|
19020
19961
|
};
|
|
19021
19962
|
return;
|
|
19022
19963
|
}
|
|
19023
|
-
const projectId =
|
|
19024
|
-
const initialThreadId =
|
|
19964
|
+
const projectId = randomUUID10();
|
|
19965
|
+
const initialThreadId = randomUUID10();
|
|
19025
19966
|
const initialThreadTitle = generateRandomThreadName();
|
|
19026
19967
|
this.processingStateManager?.setProcessing(initialThreadId);
|
|
19027
19968
|
try {
|
|
@@ -19132,15 +20073,15 @@ var ProjectCreator = class {
|
|
|
19132
20073
|
};
|
|
19133
20074
|
return;
|
|
19134
20075
|
}
|
|
19135
|
-
const projectId =
|
|
19136
|
-
const initialThreadId =
|
|
20076
|
+
const projectId = randomUUID10();
|
|
20077
|
+
const initialThreadId = randomUUID10();
|
|
19137
20078
|
const initialThreadTitle = generateRandomThreadName();
|
|
19138
20079
|
let folderPath;
|
|
19139
20080
|
let projectMetadataSaved = false;
|
|
19140
20081
|
this.processingStateManager?.setProcessing(initialThreadId);
|
|
19141
20082
|
try {
|
|
19142
20083
|
const uniqueFolderName = await this.ensureUniqueFolderName(folderName);
|
|
19143
|
-
folderPath =
|
|
20084
|
+
folderPath = join25(this.rootFolder, uniqueFolderName);
|
|
19144
20085
|
yield {
|
|
19145
20086
|
type: "progress",
|
|
19146
20087
|
message: `Creating folder "${uniqueFolderName}"...`,
|
|
@@ -19227,7 +20168,7 @@ var ProjectCreator = class {
|
|
|
19227
20168
|
return name;
|
|
19228
20169
|
}
|
|
19229
20170
|
generateThreadPath(_projectId, threadId) {
|
|
19230
|
-
return
|
|
20171
|
+
return join25(this.rootFolder, threadId);
|
|
19231
20172
|
}
|
|
19232
20173
|
toFolderName(name) {
|
|
19233
20174
|
return name.trim().toLowerCase().replace(/[\\/]+/g, " ").replace(/[^a-z0-9._ -]+/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -19243,7 +20184,7 @@ var ProjectCreator = class {
|
|
|
19243
20184
|
async ensureUniqueFolderName(baseFolderName) {
|
|
19244
20185
|
let candidate = baseFolderName;
|
|
19245
20186
|
let counter = 1;
|
|
19246
|
-
while (await this.folderExists(
|
|
20187
|
+
while (await this.folderExists(join25(this.rootFolder, candidate))) {
|
|
19247
20188
|
candidate = `${baseFolderName}-${counter}`;
|
|
19248
20189
|
counter++;
|
|
19249
20190
|
}
|
|
@@ -19275,8 +20216,8 @@ var ProjectCreator = class {
|
|
|
19275
20216
|
yield { type: "error", message: `Cannot access folder: ${folderPath}` };
|
|
19276
20217
|
return;
|
|
19277
20218
|
}
|
|
19278
|
-
const projectId =
|
|
19279
|
-
const initialThreadId =
|
|
20219
|
+
const projectId = randomUUID10();
|
|
20220
|
+
const initialThreadId = randomUUID10();
|
|
19280
20221
|
const initialThreadTitle = generateRandomThreadName();
|
|
19281
20222
|
this.processingStateManager?.setProcessing(initialThreadId);
|
|
19282
20223
|
try {
|
|
@@ -19397,19 +20338,19 @@ var ProjectCreator = class {
|
|
|
19397
20338
|
}
|
|
19398
20339
|
async findAllPackageJsonFiles(rootPath, currentPath, setupScripts) {
|
|
19399
20340
|
try {
|
|
19400
|
-
await access3(
|
|
20341
|
+
await access3(join25(currentPath, "package.json"));
|
|
19401
20342
|
const relativePath = relative5(rootPath, currentPath);
|
|
19402
20343
|
let installCommand = "";
|
|
19403
20344
|
try {
|
|
19404
|
-
await access3(
|
|
20345
|
+
await access3(join25(currentPath, "yarn.lock"));
|
|
19405
20346
|
installCommand = "yarn install";
|
|
19406
20347
|
} catch {
|
|
19407
20348
|
try {
|
|
19408
|
-
await access3(
|
|
20349
|
+
await access3(join25(currentPath, "bun.lock"));
|
|
19409
20350
|
installCommand = "bun install";
|
|
19410
20351
|
} catch {
|
|
19411
20352
|
try {
|
|
19412
|
-
await access3(
|
|
20353
|
+
await access3(join25(currentPath, "pnpm-lock.yaml"));
|
|
19413
20354
|
installCommand = "pnpm install";
|
|
19414
20355
|
} catch {
|
|
19415
20356
|
installCommand = "npm install";
|
|
@@ -19426,7 +20367,7 @@ var ProjectCreator = class {
|
|
|
19426
20367
|
try {
|
|
19427
20368
|
const entries = await readdir8(currentPath);
|
|
19428
20369
|
for (const entry of entries) {
|
|
19429
|
-
const entryPath =
|
|
20370
|
+
const entryPath = join25(currentPath, entry);
|
|
19430
20371
|
try {
|
|
19431
20372
|
const stats = await stat3(entryPath);
|
|
19432
20373
|
if (stats.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
|
|
@@ -19772,7 +20713,7 @@ var ProjectManagerImpl = class {
|
|
|
19772
20713
|
const session = this.terminalSessionManager.getOrCreateSession(threadId, thread.path);
|
|
19773
20714
|
let workingDir;
|
|
19774
20715
|
if (cwd) {
|
|
19775
|
-
workingDir = cwd.startsWith("/") ? cwd :
|
|
20716
|
+
workingDir = cwd.startsWith("/") ? cwd : join26(thread.path, cwd);
|
|
19776
20717
|
this.terminalSessionManager.updateWorkingDirectory(threadId, workingDir);
|
|
19777
20718
|
} else {
|
|
19778
20719
|
workingDir = session.currentWorkingDirectory;
|
|
@@ -19828,7 +20769,7 @@ ___CWD___%s
|
|
|
19828
20769
|
if (outputBuffer.length > 0) {
|
|
19829
20770
|
yield outputBuffer.shift();
|
|
19830
20771
|
} else {
|
|
19831
|
-
await new Promise((
|
|
20772
|
+
await new Promise((resolve7) => setTimeout(resolve7, 50));
|
|
19832
20773
|
}
|
|
19833
20774
|
}
|
|
19834
20775
|
}
|
|
@@ -20563,8 +21504,8 @@ function createScaffoldRoutes(projectManager) {
|
|
|
20563
21504
|
}
|
|
20564
21505
|
|
|
20565
21506
|
// src/features/slash-commands/slash-commands.manager.ts
|
|
20566
|
-
import { readdir as readdir9, readFile as
|
|
20567
|
-
import { join as
|
|
21507
|
+
import { readdir as readdir9, readFile as readFile13, mkdir as mkdir5, writeFile as writeFile5, unlink as unlink3 } from "fs/promises";
|
|
21508
|
+
import { join as join27, basename as basename3, extname as extname5, relative as relative6 } from "path";
|
|
20568
21509
|
import { existsSync as existsSync16 } from "fs";
|
|
20569
21510
|
import { homedir as homedir8 } from "os";
|
|
20570
21511
|
function slugify(filename) {
|
|
@@ -20610,10 +21551,10 @@ function parseFrontmatter3(markdown) {
|
|
|
20610
21551
|
return { metadata, content };
|
|
20611
21552
|
}
|
|
20612
21553
|
function getGlobalCommandsDir() {
|
|
20613
|
-
return
|
|
21554
|
+
return join27(homedir8(), ".agents", "commands");
|
|
20614
21555
|
}
|
|
20615
21556
|
function getProjectCommandsDir(threadPath) {
|
|
20616
|
-
return
|
|
21557
|
+
return join27(threadPath, ".agents", "commands");
|
|
20617
21558
|
}
|
|
20618
21559
|
var SlashCommandManager = class _SlashCommandManager {
|
|
20619
21560
|
/**
|
|
@@ -20686,8 +21627,8 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
20686
21627
|
const files = await readdir9(dir);
|
|
20687
21628
|
for (const file of files) {
|
|
20688
21629
|
if (!file.endsWith(".md")) continue;
|
|
20689
|
-
const filePath =
|
|
20690
|
-
const fileContent = await
|
|
21630
|
+
const filePath = join27(dir, file);
|
|
21631
|
+
const fileContent = await readFile13(filePath, "utf-8");
|
|
20691
21632
|
const { metadata, content } = parseFrontmatter3(fileContent);
|
|
20692
21633
|
const nameWithoutExt = basename3(file, extname5(file));
|
|
20693
21634
|
const commandName = slugify(nameWithoutExt);
|
|
@@ -20721,7 +21662,7 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
20721
21662
|
await mkdir5(dir, { recursive: true });
|
|
20722
21663
|
}
|
|
20723
21664
|
const filename = `${name}.md`;
|
|
20724
|
-
const filePath =
|
|
21665
|
+
const filePath = join27(dir, filename);
|
|
20725
21666
|
if (existsSync16(filePath)) {
|
|
20726
21667
|
throw new Error(`Command already exists: ${name}`);
|
|
20727
21668
|
}
|
|
@@ -20786,7 +21727,7 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
20786
21727
|
if (!existsSync16(filePath)) {
|
|
20787
21728
|
throw new Error(`Command file not found: ${filePath}`);
|
|
20788
21729
|
}
|
|
20789
|
-
await
|
|
21730
|
+
await unlink3(filePath);
|
|
20790
21731
|
}
|
|
20791
21732
|
/**
|
|
20792
21733
|
* Get a specific command by name
|
|
@@ -21315,8 +22256,8 @@ function createRuleRoutes(router, projectManager) {
|
|
|
21315
22256
|
|
|
21316
22257
|
// src/features/threads/threads.manager.ts
|
|
21317
22258
|
init_utils();
|
|
21318
|
-
import { randomUUID as
|
|
21319
|
-
import { join as
|
|
22259
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
22260
|
+
import { join as join28 } from "path";
|
|
21320
22261
|
import { execSync as execSync3 } from "child_process";
|
|
21321
22262
|
import { rm as rm5, stat as stat4, mkdir as mkdir6 } from "fs/promises";
|
|
21322
22263
|
init_database();
|
|
@@ -21382,7 +22323,7 @@ var ThreadManagerImpl = class {
|
|
|
21382
22323
|
} catch {
|
|
21383
22324
|
await mkdir6(project.path, { recursive: true });
|
|
21384
22325
|
}
|
|
21385
|
-
threadId =
|
|
22326
|
+
threadId = randomUUID11();
|
|
21386
22327
|
const threadPath = this.generateThreadPath(projectId, threadId);
|
|
21387
22328
|
const existingThreads = await this.metadataManager.loadThreads();
|
|
21388
22329
|
const existingTitles = new Set(
|
|
@@ -21706,8 +22647,8 @@ var ThreadManagerImpl = class {
|
|
|
21706
22647
|
isDone = true;
|
|
21707
22648
|
resolveNext();
|
|
21708
22649
|
});
|
|
21709
|
-
const waitForData = () => new Promise((
|
|
21710
|
-
resolveNext =
|
|
22650
|
+
const waitForData = () => new Promise((resolve7) => {
|
|
22651
|
+
resolveNext = resolve7;
|
|
21711
22652
|
});
|
|
21712
22653
|
while (!isDone || outputQueue.length > 0 || errorQueue.length > 0) {
|
|
21713
22654
|
if (outputQueue.length > 0) {
|
|
@@ -21740,7 +22681,7 @@ var ThreadManagerImpl = class {
|
|
|
21740
22681
|
* - 7.4 - THE CLI SHALL ensure each Thread clone is stored in a unique directory path
|
|
21741
22682
|
*/
|
|
21742
22683
|
generateThreadPath(_projectId, threadId) {
|
|
21743
|
-
return
|
|
22684
|
+
return join28(this.rootFolder, threadId);
|
|
21744
22685
|
}
|
|
21745
22686
|
/**
|
|
21746
22687
|
* Generates a thread title if not provided
|
|
@@ -21770,7 +22711,7 @@ var ThreadManagerImpl = class {
|
|
|
21770
22711
|
if (!thread) {
|
|
21771
22712
|
throw new Error(`Thread not found: ${threadId}`);
|
|
21772
22713
|
}
|
|
21773
|
-
return new Promise((
|
|
22714
|
+
return new Promise((resolve7, reject) => {
|
|
21774
22715
|
const gitProcess = spawnProcess(
|
|
21775
22716
|
"git",
|
|
21776
22717
|
["ls-files", "--cached", "--others", "--exclude-standard"],
|
|
@@ -21789,7 +22730,7 @@ var ThreadManagerImpl = class {
|
|
|
21789
22730
|
gitProcess.on("close", (code) => {
|
|
21790
22731
|
if (code === 0) {
|
|
21791
22732
|
const files = output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
21792
|
-
|
|
22733
|
+
resolve7(files);
|
|
21793
22734
|
} else {
|
|
21794
22735
|
reject(new Error(`git ls-files failed with code ${code}: ${errorOutput}`));
|
|
21795
22736
|
}
|
|
@@ -21933,14 +22874,14 @@ async function handleDeleteThread(c, threadManager) {
|
|
|
21933
22874
|
}
|
|
21934
22875
|
|
|
21935
22876
|
// src/core/project-inspector.ts
|
|
21936
|
-
import { readFile as
|
|
22877
|
+
import { readFile as readFile14, readdir as readdir10 } from "fs/promises";
|
|
21937
22878
|
import { existsSync as existsSync17 } from "fs";
|
|
21938
|
-
import { join as
|
|
22879
|
+
import { join as join29, basename as basename4, relative as relative7 } from "path";
|
|
21939
22880
|
import { glob as glob2 } from "glob";
|
|
21940
22881
|
|
|
21941
22882
|
// src/features/project-scripts/project-scripts.database.ts
|
|
21942
22883
|
init_database();
|
|
21943
|
-
import { randomUUID as
|
|
22884
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
21944
22885
|
async function getScriptsByProject(db, projectId) {
|
|
21945
22886
|
const result = await db.execute({
|
|
21946
22887
|
sql: `SELECT id, projectId, workspace, name, command, friendlyName, updatedAt
|
|
@@ -21957,7 +22898,7 @@ async function upsertProjectScripts(projectId, scripts) {
|
|
|
21957
22898
|
sql: `INSERT OR REPLACE INTO project_scripts (id, projectId, workspace, name, command, friendlyName, updatedAt)
|
|
21958
22899
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
21959
22900
|
args: [
|
|
21960
|
-
|
|
22901
|
+
randomUUID12(),
|
|
21961
22902
|
projectId,
|
|
21962
22903
|
script.workspace,
|
|
21963
22904
|
script.name,
|
|
@@ -21979,13 +22920,13 @@ async function inspectProject(projectPath, projectId) {
|
|
|
21979
22920
|
return scripts;
|
|
21980
22921
|
}
|
|
21981
22922
|
async function detectPackageManager2(projectPath) {
|
|
21982
|
-
if (existsSync17(
|
|
22923
|
+
if (existsSync17(join29(projectPath, "bun.lockb")) || existsSync17(join29(projectPath, "bun.lock"))) {
|
|
21983
22924
|
return "bun";
|
|
21984
22925
|
}
|
|
21985
|
-
if (existsSync17(
|
|
22926
|
+
if (existsSync17(join29(projectPath, "pnpm-lock.yaml"))) {
|
|
21986
22927
|
return "pnpm";
|
|
21987
22928
|
}
|
|
21988
|
-
if (existsSync17(
|
|
22929
|
+
if (existsSync17(join29(projectPath, "yarn.lock"))) {
|
|
21989
22930
|
return "yarn";
|
|
21990
22931
|
}
|
|
21991
22932
|
try {
|
|
@@ -22004,8 +22945,8 @@ async function detectPackageManager2(projectPath) {
|
|
|
22004
22945
|
return "npm";
|
|
22005
22946
|
}
|
|
22006
22947
|
async function detectMonoRepoType(projectPath) {
|
|
22007
|
-
const hasPnpmWorkspace = existsSync17(
|
|
22008
|
-
if (existsSync17(
|
|
22948
|
+
const hasPnpmWorkspace = existsSync17(join29(projectPath, "pnpm-workspace.yaml"));
|
|
22949
|
+
if (existsSync17(join29(projectPath, "nx.json"))) {
|
|
22009
22950
|
return "nx";
|
|
22010
22951
|
}
|
|
22011
22952
|
const pkg = await readPackageJson(projectPath);
|
|
@@ -22019,10 +22960,10 @@ async function detectMonoRepoType(projectPath) {
|
|
|
22019
22960
|
if (hasPnpmWorkspace) {
|
|
22020
22961
|
return "pnpm";
|
|
22021
22962
|
}
|
|
22022
|
-
if (existsSync17(
|
|
22963
|
+
if (existsSync17(join29(projectPath, "lerna.json"))) {
|
|
22023
22964
|
return "lerna";
|
|
22024
22965
|
}
|
|
22025
|
-
if (existsSync17(
|
|
22966
|
+
if (existsSync17(join29(projectPath, "turbo.json"))) {
|
|
22026
22967
|
return "turbo";
|
|
22027
22968
|
}
|
|
22028
22969
|
const folderWorkspaces = await detectFolderBasedWorkspaces(projectPath);
|
|
@@ -22063,8 +23004,8 @@ async function resolvePackageJsonWorkspaces(projectPath) {
|
|
|
22063
23004
|
});
|
|
22064
23005
|
const workspaces = [];
|
|
22065
23006
|
for (const folder of folders) {
|
|
22066
|
-
const absPath =
|
|
22067
|
-
if (existsSync17(
|
|
23007
|
+
const absPath = join29(projectPath, folder);
|
|
23008
|
+
if (existsSync17(join29(absPath, "package.json"))) {
|
|
22068
23009
|
workspaces.push({
|
|
22069
23010
|
name: basename4(folder),
|
|
22070
23011
|
folder: absPath,
|
|
@@ -22079,12 +23020,12 @@ async function resolvePackageJsonWorkspaces(projectPath) {
|
|
|
22079
23020
|
return workspaces;
|
|
22080
23021
|
}
|
|
22081
23022
|
async function resolvePnpmWorkspaces(projectPath) {
|
|
22082
|
-
const yamlPath =
|
|
23023
|
+
const yamlPath = join29(projectPath, "pnpm-workspace.yaml");
|
|
22083
23024
|
if (!existsSync17(yamlPath)) {
|
|
22084
23025
|
return [{ name: "root", folder: projectPath, relativePath: "." }];
|
|
22085
23026
|
}
|
|
22086
23027
|
try {
|
|
22087
|
-
const yaml = await
|
|
23028
|
+
const yaml = await readFile14(yamlPath, "utf-8");
|
|
22088
23029
|
const patterns = [];
|
|
22089
23030
|
for (const line of yaml.split("\n")) {
|
|
22090
23031
|
const trimmed = line.trim();
|
|
@@ -22105,8 +23046,8 @@ async function resolvePnpmWorkspaces(projectPath) {
|
|
|
22105
23046
|
});
|
|
22106
23047
|
const workspaces = [];
|
|
22107
23048
|
for (const folder of folders) {
|
|
22108
|
-
const absPath =
|
|
22109
|
-
if (existsSync17(
|
|
23049
|
+
const absPath = join29(projectPath, folder);
|
|
23050
|
+
if (existsSync17(join29(absPath, "package.json"))) {
|
|
22110
23051
|
const wsPkg = await readPackageJson(absPath);
|
|
22111
23052
|
workspaces.push({
|
|
22112
23053
|
name: wsPkg?.name ?? basename4(folder),
|
|
@@ -22125,12 +23066,12 @@ async function resolvePnpmWorkspaces(projectPath) {
|
|
|
22125
23066
|
}
|
|
22126
23067
|
}
|
|
22127
23068
|
async function resolveLernaWorkspaces(projectPath) {
|
|
22128
|
-
const lernaPath =
|
|
23069
|
+
const lernaPath = join29(projectPath, "lerna.json");
|
|
22129
23070
|
if (!existsSync17(lernaPath)) {
|
|
22130
23071
|
return [{ name: "root", folder: projectPath, relativePath: "." }];
|
|
22131
23072
|
}
|
|
22132
23073
|
try {
|
|
22133
|
-
const content = await
|
|
23074
|
+
const content = await readFile14(lernaPath, "utf-8");
|
|
22134
23075
|
const lerna = JSON.parse(content);
|
|
22135
23076
|
const patterns = lerna.packages ?? ["packages/*"];
|
|
22136
23077
|
const folders = await glob2(patterns, {
|
|
@@ -22139,8 +23080,8 @@ async function resolveLernaWorkspaces(projectPath) {
|
|
|
22139
23080
|
});
|
|
22140
23081
|
const workspaces = [];
|
|
22141
23082
|
for (const folder of folders) {
|
|
22142
|
-
const absPath =
|
|
22143
|
-
if (existsSync17(
|
|
23083
|
+
const absPath = join29(projectPath, folder);
|
|
23084
|
+
if (existsSync17(join29(absPath, "package.json"))) {
|
|
22144
23085
|
const wsPkg = await readPackageJson(absPath);
|
|
22145
23086
|
workspaces.push({
|
|
22146
23087
|
name: wsPkg?.name ?? basename4(folder),
|
|
@@ -22160,17 +23101,17 @@ async function resolveLernaWorkspaces(projectPath) {
|
|
|
22160
23101
|
}
|
|
22161
23102
|
async function resolveNxWorkspaces(projectPath) {
|
|
22162
23103
|
const workspaces = [];
|
|
22163
|
-
const workspaceJsonPath =
|
|
23104
|
+
const workspaceJsonPath = join29(projectPath, "workspace.json");
|
|
22164
23105
|
if (existsSync17(workspaceJsonPath)) {
|
|
22165
23106
|
try {
|
|
22166
|
-
const content = await
|
|
23107
|
+
const content = await readFile14(workspaceJsonPath, "utf-8");
|
|
22167
23108
|
const wsJson = JSON.parse(content);
|
|
22168
23109
|
for (const [name, value] of Object.entries(wsJson.projects ?? {})) {
|
|
22169
23110
|
const folder = typeof value === "string" ? value : value.root;
|
|
22170
23111
|
if (folder) {
|
|
22171
23112
|
workspaces.push({
|
|
22172
23113
|
name,
|
|
22173
|
-
folder:
|
|
23114
|
+
folder: join29(projectPath, folder),
|
|
22174
23115
|
relativePath: folder
|
|
22175
23116
|
});
|
|
22176
23117
|
}
|
|
@@ -22185,13 +23126,13 @@ async function resolveNxWorkspaces(projectPath) {
|
|
|
22185
23126
|
});
|
|
22186
23127
|
for (const file of projectJsonFiles) {
|
|
22187
23128
|
try {
|
|
22188
|
-
const content = await
|
|
23129
|
+
const content = await readFile14(join29(projectPath, file), "utf-8");
|
|
22189
23130
|
const project = JSON.parse(content);
|
|
22190
23131
|
if (project.name) {
|
|
22191
23132
|
const folder = file.replace(/\/project\.json$/, "");
|
|
22192
23133
|
workspaces.push({
|
|
22193
23134
|
name: project.name,
|
|
22194
|
-
folder:
|
|
23135
|
+
folder: join29(projectPath, folder),
|
|
22195
23136
|
relativePath: folder
|
|
22196
23137
|
});
|
|
22197
23138
|
}
|
|
@@ -22199,15 +23140,15 @@ async function resolveNxWorkspaces(projectPath) {
|
|
|
22199
23140
|
}
|
|
22200
23141
|
}
|
|
22201
23142
|
if (workspaces.length > 0) return workspaces;
|
|
22202
|
-
const appsDir =
|
|
23143
|
+
const appsDir = join29(projectPath, "apps");
|
|
22203
23144
|
if (existsSync17(appsDir)) {
|
|
22204
23145
|
const entries = await readdir10(appsDir, { withFileTypes: true });
|
|
22205
23146
|
for (const entry of entries) {
|
|
22206
23147
|
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
22207
|
-
const folder =
|
|
23148
|
+
const folder = join29("apps", entry.name);
|
|
22208
23149
|
workspaces.push({
|
|
22209
23150
|
name: entry.name,
|
|
22210
|
-
folder:
|
|
23151
|
+
folder: join29(projectPath, folder),
|
|
22211
23152
|
relativePath: folder
|
|
22212
23153
|
});
|
|
22213
23154
|
}
|
|
@@ -22241,13 +23182,13 @@ async function detectFolderBasedWorkspaces(projectPath) {
|
|
|
22241
23182
|
const results = [];
|
|
22242
23183
|
for (const entry of entries) {
|
|
22243
23184
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "build") {
|
|
22244
|
-
const pkgPath =
|
|
23185
|
+
const pkgPath = join29(projectPath, entry.name, "package.json");
|
|
22245
23186
|
if (existsSync17(pkgPath)) {
|
|
22246
23187
|
try {
|
|
22247
|
-
const content = await
|
|
23188
|
+
const content = await readFile14(pkgPath, "utf-8");
|
|
22248
23189
|
const pkg = JSON.parse(content);
|
|
22249
23190
|
if (pkg.dependencies || pkg.devDependencies || pkg.scripts) {
|
|
22250
|
-
results.push({ name: entry.name, absPath:
|
|
23191
|
+
results.push({ name: entry.name, absPath: join29(projectPath, entry.name) });
|
|
22251
23192
|
}
|
|
22252
23193
|
} catch {
|
|
22253
23194
|
}
|
|
@@ -22303,7 +23244,7 @@ function pmRunCommand(packageManager, scriptName) {
|
|
|
22303
23244
|
}
|
|
22304
23245
|
async function readPackageJson(dir) {
|
|
22305
23246
|
try {
|
|
22306
|
-
const content = await
|
|
23247
|
+
const content = await readFile14(join29(dir, "package.json"), "utf-8");
|
|
22307
23248
|
return JSON.parse(content);
|
|
22308
23249
|
} catch {
|
|
22309
23250
|
return null;
|
|
@@ -22495,8 +23436,8 @@ async function handleListThreadFiles(c, threadManager) {
|
|
|
22495
23436
|
}
|
|
22496
23437
|
|
|
22497
23438
|
// src/features/threads/threads-explorer.route.ts
|
|
22498
|
-
import { readdir as readdir11, readFile as
|
|
22499
|
-
import { join as
|
|
23439
|
+
import { readdir as readdir11, readFile as readFile15, rename as rename2, writeFile as writeFile6, mkdir as mkdir7, rm as rm6, access as access4 } from "fs/promises";
|
|
23440
|
+
import { join as join30 } from "path";
|
|
22500
23441
|
import { spawn as spawn2 } from "child_process";
|
|
22501
23442
|
var Utils3 = null;
|
|
22502
23443
|
var utilsLoaded3 = false;
|
|
@@ -22511,7 +23452,7 @@ async function loadUtils3() {
|
|
|
22511
23452
|
}
|
|
22512
23453
|
async function getIgnoredPaths(repoPath, relativePaths) {
|
|
22513
23454
|
if (relativePaths.length === 0) return /* @__PURE__ */ new Set();
|
|
22514
|
-
return new Promise((
|
|
23455
|
+
return new Promise((resolve7) => {
|
|
22515
23456
|
const proc = spawn2("git", ["check-ignore", "--stdin"], { cwd: repoPath });
|
|
22516
23457
|
let output = "";
|
|
22517
23458
|
proc.stdout?.on("data", (d) => {
|
|
@@ -22523,16 +23464,16 @@ async function getIgnoredPaths(repoPath, relativePaths) {
|
|
|
22523
23464
|
const ignored = new Set(
|
|
22524
23465
|
output.split("\n").map((l) => l.trim()).filter(Boolean)
|
|
22525
23466
|
);
|
|
22526
|
-
|
|
23467
|
+
resolve7(ignored);
|
|
22527
23468
|
});
|
|
22528
|
-
proc.on("error", () =>
|
|
23469
|
+
proc.on("error", () => resolve7(/* @__PURE__ */ new Set()));
|
|
22529
23470
|
});
|
|
22530
23471
|
}
|
|
22531
23472
|
async function listExplorerEntriesRecursive(rootPath, currentPath, relativePath) {
|
|
22532
23473
|
const dirents = await readdir11(currentPath, { withFileTypes: true });
|
|
22533
23474
|
const isRoot = relativePath.length === 0;
|
|
22534
23475
|
const entries = dirents.filter((d) => !(isRoot && d.name === ".git") && d.name !== "node_modules").map((d) => {
|
|
22535
|
-
const entryPath = relativePath ?
|
|
23476
|
+
const entryPath = relativePath ? join30(relativePath, d.name) : d.name;
|
|
22536
23477
|
return {
|
|
22537
23478
|
name: d.name,
|
|
22538
23479
|
type: d.isDirectory() ? "folder" : "file",
|
|
@@ -22544,7 +23485,7 @@ async function listExplorerEntriesRecursive(rootPath, currentPath, relativePath)
|
|
|
22544
23485
|
});
|
|
22545
23486
|
const nestedEntries = await Promise.all(
|
|
22546
23487
|
entries.filter((entry) => entry.type === "folder").map(
|
|
22547
|
-
(entry) => listExplorerEntriesRecursive(rootPath,
|
|
23488
|
+
(entry) => listExplorerEntriesRecursive(rootPath, join30(rootPath, entry.path), entry.path)
|
|
22548
23489
|
)
|
|
22549
23490
|
);
|
|
22550
23491
|
return [...entries, ...nestedEntries.flat()];
|
|
@@ -22580,7 +23521,7 @@ async function handleListThreadExplorerDir(c, threadManager) {
|
|
|
22580
23521
|
const entries = recursive ? await listExplorerEntriesRecursive(thread.path, absDir, queryPath) : dirents.filter((d) => !(!queryPath && d.name === ".git")).map((d) => ({
|
|
22581
23522
|
name: d.name,
|
|
22582
23523
|
type: d.isDirectory() ? "folder" : "file",
|
|
22583
|
-
path: queryPath ?
|
|
23524
|
+
path: queryPath ? join30(queryPath, d.name) : d.name
|
|
22584
23525
|
})).sort((a, b) => {
|
|
22585
23526
|
if (a.type !== b.type) return a.type === "folder" ? -1 : 1;
|
|
22586
23527
|
return a.name.localeCompare(b.name);
|
|
@@ -22726,7 +23667,7 @@ async function handleCreateExplorerFile(c, threadManager) {
|
|
|
22726
23667
|
absParent = validated;
|
|
22727
23668
|
}
|
|
22728
23669
|
await mkdir7(absParent, { recursive: true });
|
|
22729
|
-
await writeFile6(
|
|
23670
|
+
await writeFile6(join30(absParent, name), "", "utf-8");
|
|
22730
23671
|
const relPath = dirPath ? `${dirPath}/${name}` : name;
|
|
22731
23672
|
return c.json({ success: true, path: relPath });
|
|
22732
23673
|
} catch (error) {
|
|
@@ -22768,7 +23709,7 @@ async function handleGetExplorerMedia(c, threadManager) {
|
|
|
22768
23709
|
}
|
|
22769
23710
|
const ext = getFileExtension(filePath);
|
|
22770
23711
|
const contentType = MEDIA_MIME_TYPES[ext] ?? "application/octet-stream";
|
|
22771
|
-
const data = await
|
|
23712
|
+
const data = await readFile15(absPath);
|
|
22772
23713
|
return new Response(data, {
|
|
22773
23714
|
headers: {
|
|
22774
23715
|
"Content-Type": contentType,
|
|
@@ -22818,7 +23759,7 @@ async function handleCreateExplorerFolder(c, threadManager) {
|
|
|
22818
23759
|
}
|
|
22819
23760
|
absParent = validated;
|
|
22820
23761
|
}
|
|
22821
|
-
await mkdir7(
|
|
23762
|
+
await mkdir7(join30(absParent, name), { recursive: true });
|
|
22822
23763
|
const relPath = dirPath ? `${dirPath}/${name}` : name;
|
|
22823
23764
|
return c.json({ success: true, path: relPath });
|
|
22824
23765
|
} catch (error) {
|
|
@@ -22991,13 +23932,13 @@ async function handleFixComments(c, threadManager) {
|
|
|
22991
23932
|
}
|
|
22992
23933
|
|
|
22993
23934
|
// src/features/threads/threads-ai-files.route.ts
|
|
22994
|
-
import { readFile as
|
|
22995
|
-
import { join as
|
|
23935
|
+
import { readFile as readFile16, writeFile as writeFile8, mkdir as mkdir9, access as access5, rm as rm7, stat as stat5 } from "fs/promises";
|
|
23936
|
+
import { join as join32 } from "path";
|
|
22996
23937
|
import { existsSync as existsSync18 } from "fs";
|
|
22997
23938
|
|
|
22998
23939
|
// src/features/git/git-download-folder.ts
|
|
22999
23940
|
import { mkdir as mkdir8, writeFile as writeFile7 } from "fs/promises";
|
|
23000
|
-
import { join as
|
|
23941
|
+
import { join as join31, dirname as dirname5 } from "path";
|
|
23001
23942
|
async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
|
|
23002
23943
|
const { token, ref } = options;
|
|
23003
23944
|
const match = repoUrl.replace(/\.git$/, "").match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
@@ -23033,7 +23974,7 @@ async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
|
|
|
23033
23974
|
await Promise.all(
|
|
23034
23975
|
files.map(async (file) => {
|
|
23035
23976
|
const relativePath = file.path.slice(prefix.length);
|
|
23036
|
-
const localPath =
|
|
23977
|
+
const localPath = join31(destPath, relativePath);
|
|
23037
23978
|
await mkdir8(dirname5(localPath), { recursive: true });
|
|
23038
23979
|
const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${refParam}/${file.path}`;
|
|
23039
23980
|
const fileRes = await fetch(rawUrl, { headers });
|
|
@@ -23151,7 +24092,7 @@ Links to important documentation, tools, or references.
|
|
|
23151
24092
|
400
|
|
23152
24093
|
);
|
|
23153
24094
|
}
|
|
23154
|
-
const content = await
|
|
24095
|
+
const content = await readFile16(absPath, "utf-8");
|
|
23155
24096
|
return c.json({ content, path: filePath });
|
|
23156
24097
|
} catch (error) {
|
|
23157
24098
|
return errorResponse(
|
|
@@ -23190,7 +24131,7 @@ async function handleSaveThreadAIFile(c, threadManager) {
|
|
|
23190
24131
|
if (!absPath) {
|
|
23191
24132
|
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
|
|
23192
24133
|
}
|
|
23193
|
-
const parentDir =
|
|
24134
|
+
const parentDir = join32(absPath, "..");
|
|
23194
24135
|
await mkdir9(parentDir, { recursive: true });
|
|
23195
24136
|
await writeFile8(absPath, content, "utf-8");
|
|
23196
24137
|
return c.json({ success: true, path: filePath });
|
|
@@ -23279,10 +24220,10 @@ async function handleCreateThreadAgent(c, threadManager) {
|
|
|
23279
24220
|
if (!thread) {
|
|
23280
24221
|
return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
|
|
23281
24222
|
}
|
|
23282
|
-
const agentRelPath =
|
|
23283
|
-
const agentAbsPath =
|
|
23284
|
-
const agentFileRelPath =
|
|
23285
|
-
const agentFileAbsPath =
|
|
24223
|
+
const agentRelPath = join32(".agents", "agents", name);
|
|
24224
|
+
const agentAbsPath = join32(thread.path, agentRelPath);
|
|
24225
|
+
const agentFileRelPath = join32(agentRelPath, "AGENT.md");
|
|
24226
|
+
const agentFileAbsPath = join32(agentAbsPath, "AGENT.md");
|
|
23286
24227
|
if (existsSync18(agentAbsPath)) {
|
|
23287
24228
|
return c.json(
|
|
23288
24229
|
{ error: { code: "CONFLICT", message: `Agent '${name}' already exists` } },
|
|
@@ -23349,10 +24290,10 @@ async function handleCreateThreadSkill(c, threadManager) {
|
|
|
23349
24290
|
if (!thread) {
|
|
23350
24291
|
return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
|
|
23351
24292
|
}
|
|
23352
|
-
const skillRelPath =
|
|
23353
|
-
const skillAbsPath =
|
|
23354
|
-
const skillFileRelPath =
|
|
23355
|
-
const skillFileAbsPath =
|
|
24293
|
+
const skillRelPath = join32(".agents", "skills", name);
|
|
24294
|
+
const skillAbsPath = join32(thread.path, skillRelPath);
|
|
24295
|
+
const skillFileRelPath = join32(skillRelPath, "SKILL.md");
|
|
24296
|
+
const skillFileAbsPath = join32(skillAbsPath, "SKILL.md");
|
|
23356
24297
|
if (existsSync18(skillAbsPath)) {
|
|
23357
24298
|
return c.json(
|
|
23358
24299
|
{ error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
|
|
@@ -23483,7 +24424,7 @@ var ScriptProcessManager = class {
|
|
|
23483
24424
|
if (outputBuffer.length > 0) {
|
|
23484
24425
|
yield outputBuffer.shift();
|
|
23485
24426
|
} else {
|
|
23486
|
-
await new Promise((
|
|
24427
|
+
await new Promise((resolve7) => setTimeout(resolve7, 50));
|
|
23487
24428
|
}
|
|
23488
24429
|
}
|
|
23489
24430
|
} finally {
|
|
@@ -23817,7 +24758,7 @@ var GitManagerImpl = class {
|
|
|
23817
24758
|
yield event;
|
|
23818
24759
|
}
|
|
23819
24760
|
} else {
|
|
23820
|
-
await new Promise((
|
|
24761
|
+
await new Promise((resolve7) => setTimeout(resolve7, 10));
|
|
23821
24762
|
}
|
|
23822
24763
|
}
|
|
23823
24764
|
if (processError) {
|
|
@@ -23880,7 +24821,7 @@ var GitManagerImpl = class {
|
|
|
23880
24821
|
* @returns true if the branch exists, false otherwise
|
|
23881
24822
|
*/
|
|
23882
24823
|
async checkBranchExists(repoPath, branchName) {
|
|
23883
|
-
return new Promise((
|
|
24824
|
+
return new Promise((resolve7) => {
|
|
23884
24825
|
const gitProcess = spawnProcess(
|
|
23885
24826
|
"git",
|
|
23886
24827
|
["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
|
|
@@ -23889,10 +24830,10 @@ var GitManagerImpl = class {
|
|
|
23889
24830
|
}
|
|
23890
24831
|
);
|
|
23891
24832
|
gitProcess.on("close", (code) => {
|
|
23892
|
-
|
|
24833
|
+
resolve7(code === 0);
|
|
23893
24834
|
});
|
|
23894
24835
|
gitProcess.on("error", () => {
|
|
23895
|
-
|
|
24836
|
+
resolve7(false);
|
|
23896
24837
|
});
|
|
23897
24838
|
});
|
|
23898
24839
|
}
|
|
@@ -23950,7 +24891,7 @@ var GitManagerImpl = class {
|
|
|
23950
24891
|
yield event;
|
|
23951
24892
|
}
|
|
23952
24893
|
} else {
|
|
23953
|
-
await new Promise((
|
|
24894
|
+
await new Promise((resolve7) => setTimeout(resolve7, 10));
|
|
23954
24895
|
}
|
|
23955
24896
|
}
|
|
23956
24897
|
if (processError) {
|
|
@@ -24006,7 +24947,7 @@ import { Hono as Hono13 } from "hono";
|
|
|
24006
24947
|
// src/features/git/git-user.route.ts
|
|
24007
24948
|
init_utils();
|
|
24008
24949
|
function readGitConfigValue(key) {
|
|
24009
|
-
return new Promise((
|
|
24950
|
+
return new Promise((resolve7) => {
|
|
24010
24951
|
const proc = spawnProcess("git", ["config", key]);
|
|
24011
24952
|
let out = "";
|
|
24012
24953
|
proc.stdout?.on("data", (d) => {
|
|
@@ -24014,12 +24955,12 @@ function readGitConfigValue(key) {
|
|
|
24014
24955
|
});
|
|
24015
24956
|
proc.on("close", (code) => {
|
|
24016
24957
|
if (code === 0) {
|
|
24017
|
-
|
|
24958
|
+
resolve7(out.trim());
|
|
24018
24959
|
} else {
|
|
24019
|
-
|
|
24960
|
+
resolve7("");
|
|
24020
24961
|
}
|
|
24021
24962
|
});
|
|
24022
|
-
proc.on("error", () =>
|
|
24963
|
+
proc.on("error", () => resolve7(""));
|
|
24023
24964
|
});
|
|
24024
24965
|
}
|
|
24025
24966
|
async function gitUserHandler(c) {
|
|
@@ -24064,14 +25005,14 @@ async function gitStatusHandler(c, metadataManager) {
|
|
|
24064
25005
|
400
|
|
24065
25006
|
);
|
|
24066
25007
|
}
|
|
24067
|
-
const { hasChanges, changedFilesCount } = await new Promise((
|
|
25008
|
+
const { hasChanges, changedFilesCount } = await new Promise((resolve7) => {
|
|
24068
25009
|
getGitStatus(gitRoot).then((statusOutput) => {
|
|
24069
25010
|
const lines = statusOutput.trim().split("\n").filter((line) => line.length > 0);
|
|
24070
|
-
|
|
25011
|
+
resolve7({
|
|
24071
25012
|
hasChanges: lines.length > 0,
|
|
24072
25013
|
changedFilesCount: lines.length
|
|
24073
25014
|
});
|
|
24074
|
-
}).catch(() =>
|
|
25015
|
+
}).catch(() => resolve7({ hasChanges: false, changedFilesCount: 0 }));
|
|
24075
25016
|
});
|
|
24076
25017
|
const currentBranch = await getCurrentBranch(gitRoot);
|
|
24077
25018
|
const hasUnpushed = await hasUnpushedCommits(gitRoot, currentBranch);
|
|
@@ -24138,15 +25079,26 @@ async function gitDiffHandler(c, metadataManager) {
|
|
|
24138
25079
|
let oldContent = "";
|
|
24139
25080
|
let newContent = "";
|
|
24140
25081
|
const hunks = [];
|
|
25082
|
+
const isBinary = getExplorerFileMediaType(file.path) !== null;
|
|
25083
|
+
if (isBinary) {
|
|
25084
|
+
filesWithDiff.push({
|
|
25085
|
+
...file,
|
|
25086
|
+
oldContent: "",
|
|
25087
|
+
newContent: "",
|
|
25088
|
+
isBinary: true,
|
|
25089
|
+
hunks: []
|
|
25090
|
+
});
|
|
25091
|
+
continue;
|
|
25092
|
+
}
|
|
24141
25093
|
if (file.status === "deleted") {
|
|
24142
|
-
const content = await new Promise((
|
|
25094
|
+
const content = await new Promise((resolve7) => {
|
|
24143
25095
|
const proc = spawnProcess("git", ["show", `HEAD:${file.path}`], { cwd: gitRoot });
|
|
24144
25096
|
let out = "";
|
|
24145
25097
|
proc.stdout?.on("data", (d) => {
|
|
24146
25098
|
out += d.toString();
|
|
24147
25099
|
});
|
|
24148
|
-
proc.on("close", () =>
|
|
24149
|
-
proc.on("error", () =>
|
|
25100
|
+
proc.on("close", () => resolve7(out));
|
|
25101
|
+
proc.on("error", () => resolve7(""));
|
|
24150
25102
|
});
|
|
24151
25103
|
oldContent = content;
|
|
24152
25104
|
} else if (file.status === "added" && file.path.startsWith("(new)")) {
|
|
@@ -24156,7 +25108,7 @@ async function gitDiffHandler(c, metadataManager) {
|
|
|
24156
25108
|
newContent = "";
|
|
24157
25109
|
}
|
|
24158
25110
|
} else {
|
|
24159
|
-
const diffOutput = await new Promise((
|
|
25111
|
+
const diffOutput = await new Promise((resolve7) => {
|
|
24160
25112
|
const proc = spawnProcess(
|
|
24161
25113
|
"git",
|
|
24162
25114
|
file.status === "added" ? ["diff", "--cached", "--", file.path] : ["diff", "HEAD", "--", file.path],
|
|
@@ -24166,21 +25118,21 @@ async function gitDiffHandler(c, metadataManager) {
|
|
|
24166
25118
|
proc.stdout?.on("data", (d) => {
|
|
24167
25119
|
out += d.toString();
|
|
24168
25120
|
});
|
|
24169
|
-
proc.on("close", () =>
|
|
24170
|
-
proc.on("error", () =>
|
|
25121
|
+
proc.on("close", () => resolve7(out));
|
|
25122
|
+
proc.on("error", () => resolve7(""));
|
|
24171
25123
|
});
|
|
24172
25124
|
const lines = diffOutput.split("\n");
|
|
24173
25125
|
let currentHunk = null;
|
|
24174
25126
|
let oldLineNum = 0;
|
|
24175
25127
|
let newLineNum = 0;
|
|
24176
|
-
const oldContentOutput = await new Promise((
|
|
25128
|
+
const oldContentOutput = await new Promise((resolve7) => {
|
|
24177
25129
|
const proc = spawnProcess("git", ["show", `HEAD:${file.path}`], { cwd: gitRoot });
|
|
24178
25130
|
let out = "";
|
|
24179
25131
|
proc.stdout?.on("data", (d) => {
|
|
24180
25132
|
out += d.toString();
|
|
24181
25133
|
});
|
|
24182
|
-
proc.on("close", () =>
|
|
24183
|
-
proc.on("error", () =>
|
|
25134
|
+
proc.on("close", () => resolve7(out));
|
|
25135
|
+
proc.on("error", () => resolve7(""));
|
|
24184
25136
|
});
|
|
24185
25137
|
oldContent = oldContentOutput;
|
|
24186
25138
|
try {
|
|
@@ -24237,6 +25189,7 @@ async function gitDiffHandler(c, metadataManager) {
|
|
|
24237
25189
|
...file,
|
|
24238
25190
|
oldContent,
|
|
24239
25191
|
newContent,
|
|
25192
|
+
isBinary: false,
|
|
24240
25193
|
hunks
|
|
24241
25194
|
});
|
|
24242
25195
|
}
|
|
@@ -25016,7 +25969,7 @@ function sanitizeBranchName(name) {
|
|
|
25016
25969
|
return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-zA-Z0-9\-_/.]/g, "").replace(/^[/.]+|[/.]+$/g, "").replace(/-+/g, "-") || "branch";
|
|
25017
25970
|
}
|
|
25018
25971
|
function checkBranchExists(gitRoot, branchName) {
|
|
25019
|
-
return new Promise((
|
|
25972
|
+
return new Promise((resolve7) => {
|
|
25020
25973
|
const proc = spawnProcess(
|
|
25021
25974
|
"git",
|
|
25022
25975
|
["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
|
|
@@ -25025,15 +25978,15 @@ function checkBranchExists(gitRoot, branchName) {
|
|
|
25025
25978
|
}
|
|
25026
25979
|
);
|
|
25027
25980
|
proc.on("close", (code) => {
|
|
25028
|
-
|
|
25981
|
+
resolve7(code === 0);
|
|
25029
25982
|
});
|
|
25030
25983
|
proc.on("error", () => {
|
|
25031
|
-
|
|
25984
|
+
resolve7(false);
|
|
25032
25985
|
});
|
|
25033
25986
|
});
|
|
25034
25987
|
}
|
|
25035
25988
|
function createAndCheckoutBranch(gitRoot, branchName) {
|
|
25036
|
-
return new Promise((
|
|
25989
|
+
return new Promise((resolve7, reject) => {
|
|
25037
25990
|
const proc = spawnProcess("git", ["checkout", "-b", branchName], {
|
|
25038
25991
|
cwd: gitRoot
|
|
25039
25992
|
});
|
|
@@ -25045,7 +25998,7 @@ function createAndCheckoutBranch(gitRoot, branchName) {
|
|
|
25045
25998
|
}
|
|
25046
25999
|
proc.on("close", (code) => {
|
|
25047
26000
|
if (code === 0) {
|
|
25048
|
-
|
|
26001
|
+
resolve7();
|
|
25049
26002
|
} else {
|
|
25050
26003
|
reject(new Error(err || `Failed to create branch "${branchName}"`));
|
|
25051
26004
|
}
|
|
@@ -25138,7 +26091,7 @@ function sanitizeBranchName2(name) {
|
|
|
25138
26091
|
return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-zA-Z0-9\-_/.]/g, "").replace(/^[/.]+|[/.]+$/g, "").replace(/-+/g, "-") || "branch";
|
|
25139
26092
|
}
|
|
25140
26093
|
function checkBranchExists2(gitRoot, branchName) {
|
|
25141
|
-
return new Promise((
|
|
26094
|
+
return new Promise((resolve7) => {
|
|
25142
26095
|
const proc = spawnProcess(
|
|
25143
26096
|
"git",
|
|
25144
26097
|
["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
|
|
@@ -25147,16 +26100,16 @@ function checkBranchExists2(gitRoot, branchName) {
|
|
|
25147
26100
|
}
|
|
25148
26101
|
);
|
|
25149
26102
|
proc.on("close", (code) => {
|
|
25150
|
-
|
|
26103
|
+
resolve7(code === 0);
|
|
25151
26104
|
});
|
|
25152
26105
|
proc.on("error", () => {
|
|
25153
|
-
|
|
26106
|
+
resolve7(false);
|
|
25154
26107
|
});
|
|
25155
26108
|
});
|
|
25156
26109
|
}
|
|
25157
26110
|
function createAndCheckoutBranch2(gitRoot, branchName, baseBranch) {
|
|
25158
26111
|
const args2 = baseBranch ? ["checkout", "-b", branchName, `origin/${baseBranch}`] : ["checkout", "-b", branchName];
|
|
25159
|
-
return new Promise((
|
|
26112
|
+
return new Promise((resolve7, reject) => {
|
|
25160
26113
|
const proc = spawnProcess("git", args2, {
|
|
25161
26114
|
cwd: gitRoot
|
|
25162
26115
|
});
|
|
@@ -25168,7 +26121,7 @@ function createAndCheckoutBranch2(gitRoot, branchName, baseBranch) {
|
|
|
25168
26121
|
}
|
|
25169
26122
|
proc.on("close", (code) => {
|
|
25170
26123
|
if (code === 0) {
|
|
25171
|
-
|
|
26124
|
+
resolve7();
|
|
25172
26125
|
} else {
|
|
25173
26126
|
reject(new Error(err || `Failed to create branch "${branchName}"`));
|
|
25174
26127
|
}
|
|
@@ -25283,14 +26236,14 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
25283
26236
|
);
|
|
25284
26237
|
}
|
|
25285
26238
|
const { spawnProcess: spawnProcess2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
|
|
25286
|
-
return new Promise((
|
|
26239
|
+
return new Promise((resolve7) => {
|
|
25287
26240
|
console.log(`[sync-branch] Checking branch status...`);
|
|
25288
26241
|
const statusProc = spawnProcess2("git", ["status", "-sb"], { cwd: gitRoot });
|
|
25289
26242
|
let statusOutput = "";
|
|
25290
26243
|
statusProc.on("close", (statusCode) => {
|
|
25291
26244
|
if (statusCode !== 0) {
|
|
25292
26245
|
console.log(`[sync-branch] Failed to get git status: code ${statusCode}`);
|
|
25293
|
-
|
|
26246
|
+
resolve7(
|
|
25294
26247
|
c.json(
|
|
25295
26248
|
{
|
|
25296
26249
|
error: "Failed to check git status",
|
|
@@ -25324,7 +26277,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
25324
26277
|
pushProc.on("close", (pushCode) => {
|
|
25325
26278
|
if (pushCode !== 0) {
|
|
25326
26279
|
console.log(`[sync-branch] Failed to push commits: code ${pushCode}`);
|
|
25327
|
-
|
|
26280
|
+
resolve7(
|
|
25328
26281
|
c.json(
|
|
25329
26282
|
{
|
|
25330
26283
|
error: `Failed to push commits before sync: ${pushErr || pushOut}`,
|
|
@@ -25342,7 +26295,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
25342
26295
|
console.log(
|
|
25343
26296
|
`[sync-branch] \u2717 Push process error: ${error instanceof Error ? error.message : String(error)}`
|
|
25344
26297
|
);
|
|
25345
|
-
|
|
26298
|
+
resolve7(
|
|
25346
26299
|
c.json(
|
|
25347
26300
|
{
|
|
25348
26301
|
error: `Failed to push commits: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -25370,7 +26323,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
25370
26323
|
console.log(
|
|
25371
26324
|
`[sync-branch] \u2717 Status process error: ${error instanceof Error ? error.message : String(error)}`
|
|
25372
26325
|
);
|
|
25373
|
-
|
|
26326
|
+
resolve7(
|
|
25374
26327
|
c.json(
|
|
25375
26328
|
{
|
|
25376
26329
|
error: `Failed to check git status: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -25385,7 +26338,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
25385
26338
|
configProc.on("close", (configCode) => {
|
|
25386
26339
|
if (configCode !== 0) {
|
|
25387
26340
|
console.log(`[sync-branch] Failed to configure git: code ${configCode}`);
|
|
25388
|
-
|
|
26341
|
+
resolve7(
|
|
25389
26342
|
c.json(
|
|
25390
26343
|
{
|
|
25391
26344
|
error: "Failed to configure git pull strategy",
|
|
@@ -25419,7 +26372,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
25419
26372
|
if (code === 0) {
|
|
25420
26373
|
const message = currentBranch === defaultBranch ? `Successfully pulled latest changes from origin/${defaultBranch}` : `Successfully synced ${currentBranch} with ${defaultBranch}`;
|
|
25421
26374
|
console.log(`[sync-branch] \u2713 ${message}`);
|
|
25422
|
-
|
|
26375
|
+
resolve7(
|
|
25423
26376
|
c.json({
|
|
25424
26377
|
success: true,
|
|
25425
26378
|
message,
|
|
@@ -25430,7 +26383,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
25430
26383
|
} else {
|
|
25431
26384
|
console.log(`[sync-branch] \u2717 Failed to sync with exit code ${code}`);
|
|
25432
26385
|
console.log(`[sync-branch] Error output: ${err || out}`);
|
|
25433
|
-
|
|
26386
|
+
resolve7(
|
|
25434
26387
|
c.json(
|
|
25435
26388
|
{
|
|
25436
26389
|
error: `Failed to sync with ${defaultBranch}: ${err || out}`,
|
|
@@ -25445,7 +26398,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
25445
26398
|
console.log(
|
|
25446
26399
|
`[sync-branch] \u2717 Process error: ${error instanceof Error ? error.message : String(error)}`
|
|
25447
26400
|
);
|
|
25448
|
-
|
|
26401
|
+
resolve7(
|
|
25449
26402
|
c.json(
|
|
25450
26403
|
{
|
|
25451
26404
|
error: `Failed to sync branch: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -25459,7 +26412,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
25459
26412
|
console.log(
|
|
25460
26413
|
`[sync-branch] \u2717 Config process error: ${error instanceof Error ? error.message : String(error)}`
|
|
25461
26414
|
);
|
|
25462
|
-
|
|
26415
|
+
resolve7(
|
|
25463
26416
|
c.json(
|
|
25464
26417
|
{
|
|
25465
26418
|
error: `Failed to configure git: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -25480,7 +26433,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
25480
26433
|
// src/features/git/git-list-branches.route.ts
|
|
25481
26434
|
init_utils();
|
|
25482
26435
|
function listLocalBranches(gitRoot) {
|
|
25483
|
-
return new Promise((
|
|
26436
|
+
return new Promise((resolve7) => {
|
|
25484
26437
|
const proc = spawnProcess("git", ["branch", "--format=%(refname:short)"], { cwd: gitRoot });
|
|
25485
26438
|
let out = "";
|
|
25486
26439
|
if (proc.stdout) {
|
|
@@ -25490,9 +26443,9 @@ function listLocalBranches(gitRoot) {
|
|
|
25490
26443
|
}
|
|
25491
26444
|
proc.on("close", () => {
|
|
25492
26445
|
const branches = out.split("\n").map((b) => b.trim()).filter(Boolean);
|
|
25493
|
-
|
|
26446
|
+
resolve7(branches);
|
|
25494
26447
|
});
|
|
25495
|
-
proc.on("error", () =>
|
|
26448
|
+
proc.on("error", () => resolve7([]));
|
|
25496
26449
|
});
|
|
25497
26450
|
}
|
|
25498
26451
|
async function gitListBranchesHandler(c, metadataManager) {
|
|
@@ -25524,7 +26477,7 @@ async function gitListBranchesHandler(c, metadataManager) {
|
|
|
25524
26477
|
// src/features/git/git-checkout-branch.route.ts
|
|
25525
26478
|
init_utils();
|
|
25526
26479
|
function checkoutBranch(gitRoot, branchName) {
|
|
25527
|
-
return new Promise((
|
|
26480
|
+
return new Promise((resolve7, reject) => {
|
|
25528
26481
|
const proc = spawnProcess("git", ["checkout", branchName], { cwd: gitRoot });
|
|
25529
26482
|
let err = "";
|
|
25530
26483
|
if (proc.stderr) {
|
|
@@ -25534,7 +26487,7 @@ function checkoutBranch(gitRoot, branchName) {
|
|
|
25534
26487
|
}
|
|
25535
26488
|
proc.on("close", (code) => {
|
|
25536
26489
|
if (code === 0) {
|
|
25537
|
-
|
|
26490
|
+
resolve7();
|
|
25538
26491
|
} else {
|
|
25539
26492
|
reject(new Error(err.trim() || `Failed to checkout branch "${branchName}"`));
|
|
25540
26493
|
}
|
|
@@ -25584,7 +26537,7 @@ import { unlinkSync } from "fs";
|
|
|
25584
26537
|
import { resolve as resolve5 } from "path";
|
|
25585
26538
|
init_utils();
|
|
25586
26539
|
function runGit2(args2, cwd) {
|
|
25587
|
-
return new Promise((
|
|
26540
|
+
return new Promise((resolve7, reject) => {
|
|
25588
26541
|
const proc = spawnProcess("git", args2, { cwd });
|
|
25589
26542
|
let out = "";
|
|
25590
26543
|
let err = "";
|
|
@@ -25595,7 +26548,7 @@ function runGit2(args2, cwd) {
|
|
|
25595
26548
|
err += d.toString();
|
|
25596
26549
|
});
|
|
25597
26550
|
proc.on("close", (code) => {
|
|
25598
|
-
if (code === 0)
|
|
26551
|
+
if (code === 0) resolve7(out.trim());
|
|
25599
26552
|
else reject(new Error(err.trim() || `git ${args2[0]} failed with code ${code}`));
|
|
25600
26553
|
});
|
|
25601
26554
|
proc.on("error", reject);
|
|
@@ -25647,7 +26600,7 @@ async function gitRevertFileHandler(c, metadataManager) {
|
|
|
25647
26600
|
// src/features/git/git-checkpoint.route.ts
|
|
25648
26601
|
init_utils();
|
|
25649
26602
|
function stashWithLabel(gitRoot, label) {
|
|
25650
|
-
return new Promise((
|
|
26603
|
+
return new Promise((resolve7, reject) => {
|
|
25651
26604
|
const proc = spawnProcess("git", ["stash", "push", "--include-untracked", "-m", label], {
|
|
25652
26605
|
cwd: gitRoot
|
|
25653
26606
|
});
|
|
@@ -25667,7 +26620,7 @@ function stashWithLabel(gitRoot, label) {
|
|
|
25667
26620
|
if (code === 0) {
|
|
25668
26621
|
const nothingSaved = out.includes("No local changes to save");
|
|
25669
26622
|
if (nothingSaved) {
|
|
25670
|
-
|
|
26623
|
+
resolve7(false);
|
|
25671
26624
|
return;
|
|
25672
26625
|
}
|
|
25673
26626
|
const apply = spawnProcess("git", ["stash", "apply", "--index"], { cwd: gitRoot });
|
|
@@ -25679,7 +26632,7 @@ function stashWithLabel(gitRoot, label) {
|
|
|
25679
26632
|
}
|
|
25680
26633
|
apply.on("close", (applyCode) => {
|
|
25681
26634
|
if (applyCode === 0) {
|
|
25682
|
-
|
|
26635
|
+
resolve7(true);
|
|
25683
26636
|
} else {
|
|
25684
26637
|
reject(new Error(applyErr || "Failed to re-apply stash after checkpoint"));
|
|
25685
26638
|
}
|
|
@@ -25728,7 +26681,7 @@ async function gitCheckpointHandler(c, metadataManager) {
|
|
|
25728
26681
|
// src/features/git/git-checkpoint-restore.route.ts
|
|
25729
26682
|
init_utils();
|
|
25730
26683
|
function listStashes(gitRoot) {
|
|
25731
|
-
return new Promise((
|
|
26684
|
+
return new Promise((resolve7, reject) => {
|
|
25732
26685
|
const proc = spawnProcess("git", ["stash", "list", "--format=%gd %s"], { cwd: gitRoot });
|
|
25733
26686
|
let out = "";
|
|
25734
26687
|
let err = "";
|
|
@@ -25752,18 +26705,18 @@ function listStashes(gitRoot) {
|
|
|
25752
26705
|
if (!match) return null;
|
|
25753
26706
|
return { index: parseInt(match[1], 10), label: match[2].trim() };
|
|
25754
26707
|
}).filter((e) => e !== null);
|
|
25755
|
-
|
|
26708
|
+
resolve7(entries);
|
|
25756
26709
|
});
|
|
25757
26710
|
proc.on("error", reject);
|
|
25758
26711
|
});
|
|
25759
26712
|
}
|
|
25760
26713
|
function discardWorkingChanges(gitRoot) {
|
|
25761
|
-
return new Promise((
|
|
26714
|
+
return new Promise((resolve7, reject) => {
|
|
25762
26715
|
const proc = spawnProcess("git", ["checkout", "--", "."], { cwd: gitRoot });
|
|
25763
26716
|
proc.on("close", () => {
|
|
25764
26717
|
const clean = spawnProcess("git", ["clean", "-fd"], { cwd: gitRoot });
|
|
25765
26718
|
clean.on("close", (code) => {
|
|
25766
|
-
if (code === 0)
|
|
26719
|
+
if (code === 0) resolve7();
|
|
25767
26720
|
else reject(new Error("Failed to clean working tree"));
|
|
25768
26721
|
});
|
|
25769
26722
|
clean.on("error", reject);
|
|
@@ -25772,7 +26725,7 @@ function discardWorkingChanges(gitRoot) {
|
|
|
25772
26725
|
});
|
|
25773
26726
|
}
|
|
25774
26727
|
function applyStash(gitRoot, index) {
|
|
25775
|
-
return new Promise((
|
|
26728
|
+
return new Promise((resolve7, reject) => {
|
|
25776
26729
|
const proc = spawnProcess("git", ["stash", "apply", `stash@{${index}}`], { cwd: gitRoot });
|
|
25777
26730
|
let err = "";
|
|
25778
26731
|
if (proc.stderr) {
|
|
@@ -25781,7 +26734,7 @@ function applyStash(gitRoot, index) {
|
|
|
25781
26734
|
});
|
|
25782
26735
|
}
|
|
25783
26736
|
proc.on("close", (code) => {
|
|
25784
|
-
if (code === 0)
|
|
26737
|
+
if (code === 0) resolve7();
|
|
25785
26738
|
else reject(new Error(err || "Failed to apply stash"));
|
|
25786
26739
|
});
|
|
25787
26740
|
proc.on("error", reject);
|
|
@@ -25825,6 +26778,175 @@ async function gitCheckpointRestoreHandler(c, metadataManager) {
|
|
|
25825
26778
|
}
|
|
25826
26779
|
}
|
|
25827
26780
|
|
|
26781
|
+
// src/features/git/git-rename-branch.route.ts
|
|
26782
|
+
init_utils();
|
|
26783
|
+
function sanitizeBranchName3(name) {
|
|
26784
|
+
if (!name || typeof name !== "string") {
|
|
26785
|
+
return "";
|
|
26786
|
+
}
|
|
26787
|
+
return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-zA-Z0-9\-_/.]/g, "").replace(/^[/.]+|[/.]+$/g, "").replace(/-+/g, "-") || "";
|
|
26788
|
+
}
|
|
26789
|
+
function checkBranchExists3(gitRoot, branchName) {
|
|
26790
|
+
return new Promise((resolve7) => {
|
|
26791
|
+
const proc = spawnProcess(
|
|
26792
|
+
"git",
|
|
26793
|
+
["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
|
|
26794
|
+
{
|
|
26795
|
+
cwd: gitRoot
|
|
26796
|
+
}
|
|
26797
|
+
);
|
|
26798
|
+
proc.on("close", (code) => {
|
|
26799
|
+
resolve7(code === 0);
|
|
26800
|
+
});
|
|
26801
|
+
proc.on("error", () => {
|
|
26802
|
+
resolve7(false);
|
|
26803
|
+
});
|
|
26804
|
+
});
|
|
26805
|
+
}
|
|
26806
|
+
function renameBranch(gitRoot, newBranchName) {
|
|
26807
|
+
return new Promise((resolve7, reject) => {
|
|
26808
|
+
const proc = spawnProcess("git", ["branch", "-m", newBranchName], {
|
|
26809
|
+
cwd: gitRoot
|
|
26810
|
+
});
|
|
26811
|
+
let err = "";
|
|
26812
|
+
if (proc.stderr) {
|
|
26813
|
+
proc.stderr.on("data", (d) => {
|
|
26814
|
+
err += d.toString();
|
|
26815
|
+
});
|
|
26816
|
+
}
|
|
26817
|
+
proc.on("close", (code) => {
|
|
26818
|
+
if (code === 0) {
|
|
26819
|
+
resolve7();
|
|
26820
|
+
} else {
|
|
26821
|
+
reject(new Error(err || `Failed to rename branch to "${newBranchName}"`));
|
|
26822
|
+
}
|
|
26823
|
+
});
|
|
26824
|
+
proc.on("error", reject);
|
|
26825
|
+
});
|
|
26826
|
+
}
|
|
26827
|
+
async function gitRenameBranchHandler(c, metadataManager) {
|
|
26828
|
+
try {
|
|
26829
|
+
const threadId = c.req.param("threadId");
|
|
26830
|
+
const thread = await metadataManager.loadThreads().then((threads2) => threads2.find((t) => t.id === threadId));
|
|
26831
|
+
if (!thread) {
|
|
26832
|
+
return c.json({ error: "Thread not found" }, 404);
|
|
26833
|
+
}
|
|
26834
|
+
const repoPath = thread.path;
|
|
26835
|
+
if (!repoPath) {
|
|
26836
|
+
return c.json({ error: "Thread path not found" }, 404);
|
|
26837
|
+
}
|
|
26838
|
+
const body = await c.req.json().catch(() => ({}));
|
|
26839
|
+
const rawName = body.newBranchName?.trim();
|
|
26840
|
+
if (!rawName) {
|
|
26841
|
+
return c.json({ error: "newBranchName is required" }, 400);
|
|
26842
|
+
}
|
|
26843
|
+
const newBranchName = sanitizeBranchName3(rawName);
|
|
26844
|
+
if (!newBranchName) {
|
|
26845
|
+
return c.json({ error: "Invalid branch name" }, 400);
|
|
26846
|
+
}
|
|
26847
|
+
const absolutePath = resolveThreadPath(repoPath);
|
|
26848
|
+
let gitRoot;
|
|
26849
|
+
try {
|
|
26850
|
+
gitRoot = await getGitRoot(absolutePath);
|
|
26851
|
+
} catch {
|
|
26852
|
+
return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
|
|
26853
|
+
}
|
|
26854
|
+
const branchExists = await checkBranchExists3(gitRoot, newBranchName);
|
|
26855
|
+
if (branchExists) {
|
|
26856
|
+
return c.json({ error: `Branch "${newBranchName}" already exists` }, 409);
|
|
26857
|
+
}
|
|
26858
|
+
await renameBranch(gitRoot, newBranchName);
|
|
26859
|
+
const threads = await metadataManager.loadThreads();
|
|
26860
|
+
const threadIndex = threads.findIndex((t) => t.id === threadId);
|
|
26861
|
+
if (threadIndex !== -1) {
|
|
26862
|
+
threads[threadIndex].currentBranch = newBranchName;
|
|
26863
|
+
await metadataManager.saveThreads(threads);
|
|
26864
|
+
}
|
|
26865
|
+
return c.json({ success: true, branchName: newBranchName });
|
|
26866
|
+
} catch (error) {
|
|
26867
|
+
const message = error instanceof Error ? error.message : "Failed to rename branch";
|
|
26868
|
+
return c.json({ error: message }, 500);
|
|
26869
|
+
}
|
|
26870
|
+
}
|
|
26871
|
+
|
|
26872
|
+
// src/features/git/git-media.route.ts
|
|
26873
|
+
import { readFileSync as readFileSync8, existsSync as existsSync23 } from "fs";
|
|
26874
|
+
import { resolve as resolve6 } from "path";
|
|
26875
|
+
import { spawn as spawn3 } from "child_process";
|
|
26876
|
+
function spawnBinary(command, args2, cwd) {
|
|
26877
|
+
return new Promise((resolve7, reject) => {
|
|
26878
|
+
const proc = spawn3(command, args2, { cwd });
|
|
26879
|
+
const chunks = [];
|
|
26880
|
+
proc.stdout?.on("data", (d) => {
|
|
26881
|
+
chunks.push(Buffer.from(d));
|
|
26882
|
+
});
|
|
26883
|
+
proc.on("close", (code) => {
|
|
26884
|
+
if (code === 0) {
|
|
26885
|
+
resolve7(Buffer.concat(chunks));
|
|
26886
|
+
} else {
|
|
26887
|
+
reject(new Error(`Process exited with code ${code}`));
|
|
26888
|
+
}
|
|
26889
|
+
});
|
|
26890
|
+
proc.on("error", reject);
|
|
26891
|
+
});
|
|
26892
|
+
}
|
|
26893
|
+
async function gitMediaHandler(c, metadataManager) {
|
|
26894
|
+
try {
|
|
26895
|
+
const threadId = c.req.param("threadId");
|
|
26896
|
+
const filePath = c.req.query("path");
|
|
26897
|
+
const version = c.req.query("version");
|
|
26898
|
+
if (!threadId) {
|
|
26899
|
+
return c.json({ error: "Thread ID is required" }, 400);
|
|
26900
|
+
}
|
|
26901
|
+
if (!filePath) {
|
|
26902
|
+
return c.json({ error: "path query parameter is required" }, 400);
|
|
26903
|
+
}
|
|
26904
|
+
if (version !== "old" && version !== "new") {
|
|
26905
|
+
return c.json({ error: "version query parameter must be 'old' or 'new'" }, 400);
|
|
26906
|
+
}
|
|
26907
|
+
const thread = await metadataManager.loadThreads().then((threads) => threads.find((t) => t.id === threadId));
|
|
26908
|
+
if (!thread) {
|
|
26909
|
+
return c.json({ error: "Thread not found" }, 404);
|
|
26910
|
+
}
|
|
26911
|
+
const repoPath = thread.path;
|
|
26912
|
+
if (!repoPath) {
|
|
26913
|
+
return c.json({ error: "Thread path not found" }, 404);
|
|
26914
|
+
}
|
|
26915
|
+
const absolutePath = resolveThreadPath(repoPath);
|
|
26916
|
+
let gitRoot;
|
|
26917
|
+
try {
|
|
26918
|
+
gitRoot = await getGitRoot(absolutePath);
|
|
26919
|
+
} catch {
|
|
26920
|
+
return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
|
|
26921
|
+
}
|
|
26922
|
+
const ext = getFileExtension(filePath);
|
|
26923
|
+
const contentType = EXPLORER_MEDIA_MIME_TYPES[ext] ?? "application/octet-stream";
|
|
26924
|
+
let data;
|
|
26925
|
+
if (version === "old") {
|
|
26926
|
+
try {
|
|
26927
|
+
data = await spawnBinary("git", ["show", `HEAD:${filePath}`], gitRoot);
|
|
26928
|
+
} catch {
|
|
26929
|
+
return c.json({ error: `File not found in HEAD: ${filePath}` }, 404);
|
|
26930
|
+
}
|
|
26931
|
+
} else {
|
|
26932
|
+
const absFilePath = resolve6(gitRoot, filePath);
|
|
26933
|
+
if (!existsSync23(absFilePath)) {
|
|
26934
|
+
return c.json({ error: `File not found in working directory: ${filePath}` }, 404);
|
|
26935
|
+
}
|
|
26936
|
+
data = readFileSync8(absFilePath);
|
|
26937
|
+
}
|
|
26938
|
+
return new Response(data.buffer, {
|
|
26939
|
+
headers: {
|
|
26940
|
+
"Content-Type": contentType,
|
|
26941
|
+
"Cross-Origin-Resource-Policy": "cross-origin"
|
|
26942
|
+
}
|
|
26943
|
+
});
|
|
26944
|
+
} catch (error) {
|
|
26945
|
+
const message = error instanceof Error ? error.message : "Failed to get git media";
|
|
26946
|
+
return c.json({ error: message }, 500);
|
|
26947
|
+
}
|
|
26948
|
+
}
|
|
26949
|
+
|
|
25828
26950
|
// src/features/git/git.routes.ts
|
|
25829
26951
|
function createGitRoutes(metadataManager) {
|
|
25830
26952
|
const router = new Hono13();
|
|
@@ -25847,6 +26969,9 @@ function createGitRoutes(metadataManager) {
|
|
|
25847
26969
|
router.get("/diff/:threadId", async (c) => {
|
|
25848
26970
|
return gitDiffHandler(c, metadataManager);
|
|
25849
26971
|
});
|
|
26972
|
+
router.get("/media/:threadId", async (c) => {
|
|
26973
|
+
return gitMediaHandler(c, metadataManager);
|
|
26974
|
+
});
|
|
25850
26975
|
router.post("/generate-commit-message/:threadId", async (c) => {
|
|
25851
26976
|
return gitGenerateCommitMessageHandler(c, metadataManager);
|
|
25852
26977
|
});
|
|
@@ -25911,6 +27036,11 @@ function createGitRoutes(metadataManager) {
|
|
|
25911
27036
|
await invalidateGitStatusCache(db, c.req.param("threadId"));
|
|
25912
27037
|
return gitCheckoutBranchHandler(c, metadataManager);
|
|
25913
27038
|
});
|
|
27039
|
+
router.post("/rename-branch/:threadId", async (c) => {
|
|
27040
|
+
const db = await getDatabase();
|
|
27041
|
+
await invalidateGitStatusCache(db, c.req.param("threadId"));
|
|
27042
|
+
return gitRenameBranchHandler(c, metadataManager);
|
|
27043
|
+
});
|
|
25914
27044
|
router.post("/revert-file/:threadId", async (c) => {
|
|
25915
27045
|
const db = await getDatabase();
|
|
25916
27046
|
await invalidateGitStatusCache(db, c.req.param("threadId"));
|
|
@@ -25966,8 +27096,8 @@ function createUpdateRoutes(updater) {
|
|
|
25966
27096
|
|
|
25967
27097
|
// src/features/mcp/mcp.routes.ts
|
|
25968
27098
|
import { Hono as Hono15 } from "hono";
|
|
25969
|
-
import { readFile as
|
|
25970
|
-
import { join as
|
|
27099
|
+
import { readFile as readFile17, writeFile as writeFile9, mkdir as mkdir11, access as access6 } from "fs/promises";
|
|
27100
|
+
import { join as join33, dirname as dirname7 } from "path";
|
|
25971
27101
|
|
|
25972
27102
|
// src/features/mcp/mcp.popular.json
|
|
25973
27103
|
var mcp_popular_default = [
|
|
@@ -26089,10 +27219,10 @@ var mcp_popular_default = [
|
|
|
26089
27219
|
var MCP_CONFIG_PATHS2 = [".agents/mcp.json", "mcp.json"];
|
|
26090
27220
|
async function readMCPConfig(projectPath) {
|
|
26091
27221
|
for (const configPath of MCP_CONFIG_PATHS2) {
|
|
26092
|
-
const fullPath =
|
|
27222
|
+
const fullPath = join33(projectPath, configPath);
|
|
26093
27223
|
try {
|
|
26094
27224
|
await access6(fullPath);
|
|
26095
|
-
const content = await
|
|
27225
|
+
const content = await readFile17(fullPath, "utf-8");
|
|
26096
27226
|
const config = JSON.parse(content);
|
|
26097
27227
|
return { config, filePath: fullPath };
|
|
26098
27228
|
} catch {
|
|
@@ -26103,7 +27233,7 @@ async function readMCPConfig(projectPath) {
|
|
|
26103
27233
|
}
|
|
26104
27234
|
async function writeMCPConfig(projectPath, config) {
|
|
26105
27235
|
const existing = await readMCPConfig(projectPath);
|
|
26106
|
-
const targetPath = existing?.filePath ??
|
|
27236
|
+
const targetPath = existing?.filePath ?? join33(projectPath, ".agents/mcp.json");
|
|
26107
27237
|
await mkdir11(dirname7(targetPath), { recursive: true });
|
|
26108
27238
|
await writeFile9(targetPath, JSON.stringify(config, null, 2), "utf-8");
|
|
26109
27239
|
}
|
|
@@ -26519,8 +27649,8 @@ function getLocalNetworkAddresses() {
|
|
|
26519
27649
|
}
|
|
26520
27650
|
|
|
26521
27651
|
// src/core/server-bind-mode-store.ts
|
|
26522
|
-
import { existsSync as
|
|
26523
|
-
import { join as
|
|
27652
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync } from "fs";
|
|
27653
|
+
import { join as join34 } from "path";
|
|
26524
27654
|
|
|
26525
27655
|
// src/core/server-bind-mode.ts
|
|
26526
27656
|
var DEFAULT_SERVER_BIND_MODE = "local";
|
|
@@ -26532,16 +27662,16 @@ function getServerBindHostname(mode) {
|
|
|
26532
27662
|
}
|
|
26533
27663
|
|
|
26534
27664
|
// src/core/server-bind-mode-store.ts
|
|
26535
|
-
var SERVER_BIND_MODE_FILE =
|
|
27665
|
+
var SERVER_BIND_MODE_FILE = join34(DATA_DIR, "server-bind-mode.json");
|
|
26536
27666
|
function hasServerBindModeFile() {
|
|
26537
|
-
return
|
|
27667
|
+
return existsSync24(SERVER_BIND_MODE_FILE);
|
|
26538
27668
|
}
|
|
26539
27669
|
function readServerBindMode() {
|
|
26540
27670
|
try {
|
|
26541
|
-
if (!
|
|
27671
|
+
if (!existsSync24(SERVER_BIND_MODE_FILE)) {
|
|
26542
27672
|
return DEFAULT_SERVER_BIND_MODE;
|
|
26543
27673
|
}
|
|
26544
|
-
const raw =
|
|
27674
|
+
const raw = readFileSync9(SERVER_BIND_MODE_FILE, "utf8");
|
|
26545
27675
|
const parsed = JSON.parse(raw);
|
|
26546
27676
|
return normalizeServerBindMode(parsed.serverBindMode);
|
|
26547
27677
|
} catch {
|
|
@@ -26865,7 +27995,8 @@ async function clipboardWriteImage(c) {
|
|
|
26865
27995
|
pngData[i] = binary.charCodeAt(i);
|
|
26866
27996
|
}
|
|
26867
27997
|
} else if (imageUrl) {
|
|
26868
|
-
const
|
|
27998
|
+
const fetchUrl2 = resolveAbsoluteImageUrl(imageUrl, new URL(c.req.url).origin);
|
|
27999
|
+
const res = await fetch(fetchUrl2);
|
|
26869
28000
|
if (!res.ok) {
|
|
26870
28001
|
return c.json({ error: `Failed to fetch image: ${res.status}` }, 400);
|
|
26871
28002
|
}
|
|
@@ -26882,7 +28013,7 @@ async function clipboardWriteImage(c) {
|
|
|
26882
28013
|
}
|
|
26883
28014
|
|
|
26884
28015
|
// src/features/image/image-save.route.ts
|
|
26885
|
-
import { join as
|
|
28016
|
+
import { join as join35 } from "path";
|
|
26886
28017
|
var Utils5 = null;
|
|
26887
28018
|
var utilsLoaded5 = false;
|
|
26888
28019
|
async function loadUtils5() {
|
|
@@ -26910,7 +28041,8 @@ async function saveImage(c) {
|
|
|
26910
28041
|
data[i] = binary.charCodeAt(i);
|
|
26911
28042
|
}
|
|
26912
28043
|
} else if (imageUrl) {
|
|
26913
|
-
const
|
|
28044
|
+
const fetchUrl2 = resolveAbsoluteImageUrl(imageUrl, new URL(c.req.url).origin);
|
|
28045
|
+
const res = await fetch(fetchUrl2);
|
|
26914
28046
|
if (!res.ok) {
|
|
26915
28047
|
return c.json({ error: `Failed to fetch image: ${res.status}` }, 400);
|
|
26916
28048
|
}
|
|
@@ -26919,7 +28051,7 @@ async function saveImage(c) {
|
|
|
26919
28051
|
return c.json({ error: "imageUrl or imageData is required" }, 400);
|
|
26920
28052
|
}
|
|
26921
28053
|
const name = filename ?? `generated-image-${Date.now()}.png`;
|
|
26922
|
-
const savePath =
|
|
28054
|
+
const savePath = join35(Utils5.paths.downloads, name);
|
|
26923
28055
|
await Bun.write(savePath, data);
|
|
26924
28056
|
return c.json({ success: true, path: savePath });
|
|
26925
28057
|
} catch (error) {
|
|
@@ -27011,7 +28143,7 @@ function createBrowserJsRoutes(threadManager, projectManager, processingStateMan
|
|
|
27011
28143
|
});
|
|
27012
28144
|
}
|
|
27013
28145
|
}) : void 0;
|
|
27014
|
-
const tools = createAllTools(thread.path, { shellPermissionGate });
|
|
28146
|
+
const tools = createAllTools(thread.path, { shellPermissionGate, threadId });
|
|
27015
28147
|
const tool = tools[toolName];
|
|
27016
28148
|
if (!tool) {
|
|
27017
28149
|
return errorResponse(c, ErrorCodes.INVALID_REQUEST, `Tool ${toolName} not found`, 404);
|
|
@@ -27027,8 +28159,79 @@ function createBrowserJsRoutes(threadManager, projectManager, processingStateMan
|
|
|
27027
28159
|
return router;
|
|
27028
28160
|
}
|
|
27029
28161
|
|
|
27030
|
-
// src/features/
|
|
28162
|
+
// src/features/browser/browser.routes.ts
|
|
27031
28163
|
import { Hono as Hono23 } from "hono";
|
|
28164
|
+
function createBrowserRoutes() {
|
|
28165
|
+
const router = new Hono23();
|
|
28166
|
+
router.post("/ready", async (c) => {
|
|
28167
|
+
try {
|
|
28168
|
+
const body = await c.req.json();
|
|
28169
|
+
const { toolCallId, webviewId } = body;
|
|
28170
|
+
if (!toolCallId || typeof toolCallId !== "string") {
|
|
28171
|
+
return errorResponse(
|
|
28172
|
+
c,
|
|
28173
|
+
ErrorCodes.INVALID_REQUEST,
|
|
28174
|
+
"toolCallId is required and must be a string",
|
|
28175
|
+
400
|
|
28176
|
+
);
|
|
28177
|
+
}
|
|
28178
|
+
if (typeof webviewId !== "number" || !Number.isFinite(webviewId)) {
|
|
28179
|
+
return errorResponse(
|
|
28180
|
+
c,
|
|
28181
|
+
ErrorCodes.INVALID_REQUEST,
|
|
28182
|
+
"webviewId is required and must be a number",
|
|
28183
|
+
400
|
|
28184
|
+
);
|
|
28185
|
+
}
|
|
28186
|
+
submitBrowserWebviewReady(toolCallId, webviewId);
|
|
28187
|
+
return c.json({ success: true });
|
|
28188
|
+
} catch (error) {
|
|
28189
|
+
return errorResponse(
|
|
28190
|
+
c,
|
|
28191
|
+
ErrorCodes.REQUEST_PARSE_ERROR,
|
|
28192
|
+
"Failed to parse request body",
|
|
28193
|
+
400,
|
|
28194
|
+
error instanceof Error ? error.message : String(error)
|
|
28195
|
+
);
|
|
28196
|
+
}
|
|
28197
|
+
});
|
|
28198
|
+
router.post("/action-result", async (c) => {
|
|
28199
|
+
try {
|
|
28200
|
+
const body = await c.req.json();
|
|
28201
|
+
const { toolCallId, result } = body;
|
|
28202
|
+
if (!toolCallId || typeof toolCallId !== "string") {
|
|
28203
|
+
return errorResponse(
|
|
28204
|
+
c,
|
|
28205
|
+
ErrorCodes.INVALID_REQUEST,
|
|
28206
|
+
"toolCallId is required and must be a string",
|
|
28207
|
+
400
|
|
28208
|
+
);
|
|
28209
|
+
}
|
|
28210
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
|
28211
|
+
return errorResponse(
|
|
28212
|
+
c,
|
|
28213
|
+
ErrorCodes.INVALID_REQUEST,
|
|
28214
|
+
"result is required and must be an object",
|
|
28215
|
+
400
|
|
28216
|
+
);
|
|
28217
|
+
}
|
|
28218
|
+
submitBrowserActionResult(toolCallId, result);
|
|
28219
|
+
return c.json({ success: true });
|
|
28220
|
+
} catch (error) {
|
|
28221
|
+
return errorResponse(
|
|
28222
|
+
c,
|
|
28223
|
+
ErrorCodes.REQUEST_PARSE_ERROR,
|
|
28224
|
+
"Failed to parse request body",
|
|
28225
|
+
400,
|
|
28226
|
+
error instanceof Error ? error.message : String(error)
|
|
28227
|
+
);
|
|
28228
|
+
}
|
|
28229
|
+
});
|
|
28230
|
+
return router;
|
|
28231
|
+
}
|
|
28232
|
+
|
|
28233
|
+
// src/features/voice-model/voice-model.routes.ts
|
|
28234
|
+
import { Hono as Hono24 } from "hono";
|
|
27032
28235
|
var VOICE_MODEL_URLS = {
|
|
27033
28236
|
default: "https://install.tarsk.io/voice-models/ggml-tiny.en.bin",
|
|
27034
28237
|
tiny: "https://install.tarsk.io/voice-models/ggml-tiny.en-q5_1.bin"
|
|
@@ -27040,7 +28243,7 @@ function getVoiceModelUrl(model) {
|
|
|
27040
28243
|
return VOICE_MODEL_URLS.default;
|
|
27041
28244
|
}
|
|
27042
28245
|
function createVoiceModelRoutes() {
|
|
27043
|
-
const router = new
|
|
28246
|
+
const router = new Hono24();
|
|
27044
28247
|
router.get("/download", async (c) => {
|
|
27045
28248
|
const selectedModel = c.req.query("model");
|
|
27046
28249
|
const modelUrl = getVoiceModelUrl(selectedModel);
|
|
@@ -27081,9 +28284,9 @@ function createVoiceModelRoutes() {
|
|
|
27081
28284
|
}
|
|
27082
28285
|
|
|
27083
28286
|
// src/features/logs/logs.routes.ts
|
|
27084
|
-
import { Hono as
|
|
28287
|
+
import { Hono as Hono25 } from "hono";
|
|
27085
28288
|
function createLogsRoutes() {
|
|
27086
|
-
const router = new
|
|
28289
|
+
const router = new Hono25();
|
|
27087
28290
|
router.post("/", async (c) => {
|
|
27088
28291
|
let body = {};
|
|
27089
28292
|
try {
|
|
@@ -27121,11 +28324,11 @@ function createLogsRoutes() {
|
|
|
27121
28324
|
|
|
27122
28325
|
// src/features/user-tasks/user-tasks.routes.ts
|
|
27123
28326
|
init_database();
|
|
27124
|
-
import { Hono as
|
|
28327
|
+
import { Hono as Hono26 } from "hono";
|
|
27125
28328
|
|
|
27126
28329
|
// src/features/user-tasks/user-tasks.database.ts
|
|
27127
28330
|
init_database();
|
|
27128
|
-
import { randomUUID as
|
|
28331
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
27129
28332
|
async function getUserTasksByThread(db, threadId) {
|
|
27130
28333
|
const result = await db.execute({
|
|
27131
28334
|
sql: `SELECT id, threadId, content, createdAt, updatedAt
|
|
@@ -27138,7 +28341,7 @@ async function getUserTasksByThread(db, threadId) {
|
|
|
27138
28341
|
}
|
|
27139
28342
|
async function insertUserTask(threadId, content) {
|
|
27140
28343
|
const db = await getDatabase();
|
|
27141
|
-
const id =
|
|
28344
|
+
const id = randomUUID13();
|
|
27142
28345
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
27143
28346
|
await db.execute({
|
|
27144
28347
|
sql: `INSERT INTO user_tasks (id, threadId, content, createdAt, updatedAt)
|
|
@@ -27162,7 +28365,7 @@ async function deleteUserTaskById(db, id) {
|
|
|
27162
28365
|
|
|
27163
28366
|
// src/features/user-tasks/user-tasks.routes.ts
|
|
27164
28367
|
function createUserTaskRoutes() {
|
|
27165
|
-
const router = new
|
|
28368
|
+
const router = new Hono26();
|
|
27166
28369
|
router.get("/:threadId/user-tasks", async (c) => {
|
|
27167
28370
|
const threadId = c.req.param("threadId");
|
|
27168
28371
|
const db = await getDatabase();
|
|
@@ -27202,7 +28405,7 @@ async function startTarskServer(options) {
|
|
|
27202
28405
|
async function startTarskServerInternal(options) {
|
|
27203
28406
|
const { isDebug: isDebug2, publicDir: publicDirOverride } = options;
|
|
27204
28407
|
const port = isDebug2 ? 462 : process.env.PORT ? parseInt(process.env.PORT) : 641;
|
|
27205
|
-
const app = new
|
|
28408
|
+
const app = new Hono27();
|
|
27206
28409
|
app.use("/*", cors());
|
|
27207
28410
|
app.use("/*", async (c, next) => {
|
|
27208
28411
|
c.header("Cross-Origin-Opener-Policy", "same-origin");
|
|
@@ -27295,6 +28498,7 @@ async function startTarskServerInternal(options) {
|
|
|
27295
28498
|
"/api/browser-js",
|
|
27296
28499
|
createBrowserJsRoutes(threadManager, projectManager, processingStateManager)
|
|
27297
28500
|
);
|
|
28501
|
+
app.route("/api/browser", createBrowserRoutes());
|
|
27298
28502
|
app.route("/api/voice-model", createVoiceModelRoutes());
|
|
27299
28503
|
app.route("/api/logs", createLogsRoutes());
|
|
27300
28504
|
app.route("/api/update", createUpdateRoutes(options.updater));
|
|
@@ -27366,10 +28570,10 @@ async function listenForTarskServer(options) {
|
|
|
27366
28570
|
hostname: options.hostname,
|
|
27367
28571
|
port: options.port
|
|
27368
28572
|
});
|
|
27369
|
-
return new Promise((
|
|
28573
|
+
return new Promise((resolve7, reject) => {
|
|
27370
28574
|
server.once("error", (error) => {
|
|
27371
28575
|
if (error.code === "EADDRINUSE") {
|
|
27372
|
-
|
|
28576
|
+
resolve7(false);
|
|
27373
28577
|
return;
|
|
27374
28578
|
}
|
|
27375
28579
|
startPromise = null;
|
|
@@ -27377,7 +28581,7 @@ async function listenForTarskServer(options) {
|
|
|
27377
28581
|
});
|
|
27378
28582
|
function onListening() {
|
|
27379
28583
|
options.onListening();
|
|
27380
|
-
|
|
28584
|
+
resolve7(true);
|
|
27381
28585
|
}
|
|
27382
28586
|
if (options.hostname) {
|
|
27383
28587
|
server.listen(options.port, options.hostname, onListening);
|