turbollm 1.9.6 → 1.9.7
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/cli.js +284 -113
- package/dist/webdist/assets/{AgentEditPage-DtVYUbnL.js → AgentEditPage-C8RGdWJF.js} +1 -1
- package/dist/webdist/assets/{ChatScreen-CFqvsPyx.js → ChatScreen-N3uMBH5o.js} +1 -1
- package/dist/webdist/assets/{CodeComposer-CEjCtZkU.js → CodeComposer-CiCzwc9F.js} +1 -1
- package/dist/webdist/assets/{CodeHomeScreen-DMVcD76o.js → CodeHomeScreen-CL0EstEl.js} +1 -1
- package/dist/webdist/assets/{CodeSessionScreen-DAtsPyto.js → CodeSessionScreen-Cmpd5nk-.js} +1 -1
- package/dist/webdist/assets/{CustomizeScreen-Cbki-6V9.js → CustomizeScreen-BIdHpMx-.js} +1 -1
- package/dist/webdist/assets/{DeveloperScreen-WD9BR3c2.js → DeveloperScreen-Du9T6j_f.js} +1 -1
- package/dist/webdist/assets/{EnginesScreen-Dn2h0ZJu.js → EnginesScreen-CFhygpHp.js} +1 -1
- package/dist/webdist/assets/{FsBrowser-B3FpjXmq.js → FsBrowser-yCDndpa8.js} +1 -1
- package/dist/webdist/assets/{ModelLoadMenu-gr5_lfaC.js → ModelLoadMenu-B6OgZ7AZ.js} +1 -1
- package/dist/webdist/assets/{ModelsScreen-CB62SK43.js → ModelsScreen-DAcHEngS.js} +1 -1
- package/dist/webdist/assets/{SettingsScreen-YEjZw2_s.js → SettingsScreen-CgwmrEYG.js} +1 -1
- package/dist/webdist/assets/{SkillEditPage-CwUXP2-z.js → SkillEditPage-swxEgg6z.js} +1 -1
- package/dist/webdist/assets/{TokensScreen-Dr8A9OL9.js → TokensScreen-1gAoLbZq.js} +1 -1
- package/dist/webdist/assets/{ToolApprovalBar-bXZAEk7U.js → ToolApprovalBar-D-VPdhAA.js} +3 -3
- package/dist/webdist/assets/WorkspaceScreen-DA4l7OyH.js +1 -0
- package/dist/webdist/assets/{agent-api-DZH-y2xg.js → agent-api-C4rG-Jhm.js} +1 -1
- package/dist/webdist/assets/{arc-D5l_g5is.js → arc-DN-erBAi.js} +1 -1
- package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-CguxoWho.js → architectureDiagram-3BPJPVTR-C_Y3sT8y.js} +1 -1
- package/dist/webdist/assets/{arrow-left-wlX0VpDE.js → arrow-left-Dv89StCL.js} +1 -1
- package/dist/webdist/assets/{badge-BRepTaJr.js → badge-C367M9ui.js} +1 -1
- package/dist/webdist/assets/{blockDiagram-GPEHLZMM-B9xsnW_4.js → blockDiagram-GPEHLZMM-DIhPpxoy.js} +1 -1
- package/dist/webdist/assets/{bot-D2tkiUsh.js → bot-CkS7SpAq.js} +1 -1
- package/dist/webdist/assets/{c4Diagram-AAUBKEIU-CYTOwpJg.js → c4Diagram-AAUBKEIU-DhxTzCHS.js} +1 -1
- package/dist/webdist/assets/channel-B5rf-0pa.js +1 -0
- package/dist/webdist/assets/{chat-api-FD0R9dNF.js → chat-api-BjLw__7G.js} +1 -1
- package/dist/webdist/assets/{chat-queries-BKXFq3YC.js → chat-queries-DkW-crMK.js} +1 -1
- package/dist/webdist/assets/{chevron-left-BKw66_4_.js → chevron-left-CvC9QlMY.js} +1 -1
- package/dist/webdist/assets/{chevron-right-CFMIMfHC.js → chevron-right-dxKQD1jQ.js} +1 -1
- package/dist/webdist/assets/{chunk-2J33WTMH-DpsCpZbI.js → chunk-2J33WTMH-BStbX98A.js} +1 -1
- package/dist/webdist/assets/{chunk-4BX2VUAB-C0i72mr2.js → chunk-4BX2VUAB-Dt_NYZlI.js} +1 -1
- package/dist/webdist/assets/{chunk-55IACEB6-DmbNCy4Z.js → chunk-55IACEB6-Bhp_kq7k.js} +1 -1
- package/dist/webdist/assets/{chunk-727SXJPM-CiKdPdL2.js → chunk-727SXJPM-DSlmyTXu.js} +1 -1
- package/dist/webdist/assets/{chunk-AQP2D5EJ-Bc6RXr7f.js → chunk-AQP2D5EJ-DTjPEmPv.js} +1 -1
- package/dist/webdist/assets/{chunk-FMBD7UC4-CVnhYSu6.js → chunk-FMBD7UC4-CJFy7xKH.js} +1 -1
- package/dist/webdist/assets/{chunk-ND2GUHAM-CW8vQLlh.js → chunk-ND2GUHAM-U5KxKYoG.js} +1 -1
- package/dist/webdist/assets/{chunk-QZHKN3VN-x4JaoE5f.js → chunk-QZHKN3VN-DqU8C1o_.js} +1 -1
- package/dist/webdist/assets/{circle-check-8hmejhyG.js → circle-check-X2gW5Lao.js} +1 -1
- package/dist/webdist/assets/{circle-x-C9EQG8zH.js → circle-x-EHdG_-2l.js} +1 -1
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-B6iRj6y1.js +1 -0
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-B6iRj6y1.js +1 -0
- package/dist/webdist/assets/{common-CqVKpWIL.js → common-CR_Lyg91.js} +1 -1
- package/dist/webdist/assets/{cose-bilkent-S5V4N54A-Cv-MBe40.js → cose-bilkent-S5V4N54A-BYKK4Cf1.js} +1 -1
- package/dist/webdist/assets/{dagre-BM42HDAG-CZWDfsjb.js → dagre-BM42HDAG-g4hhdP8F.js} +1 -1
- package/dist/webdist/assets/{diagram-2AECGRRQ-CsDxXM77.js → diagram-2AECGRRQ-Dr_agpgo.js} +1 -1
- package/dist/webdist/assets/{diagram-5GNKFQAL-t3BVl_nH.js → diagram-5GNKFQAL-HhGT-1mW.js} +1 -1
- package/dist/webdist/assets/{diagram-KO2AKTUF-Ck_gIw7B.js → diagram-KO2AKTUF-DAbDCiEN.js} +1 -1
- package/dist/webdist/assets/{diagram-LMA3HP47-w5BRZa6G.js → diagram-LMA3HP47-69Vaqnrj.js} +1 -1
- package/dist/webdist/assets/{diagram-OG6HWLK6-DLKPbcMA.js → diagram-OG6HWLK6-BfoIa3zL.js} +1 -1
- package/dist/webdist/assets/{dialog-6Wb-Xczv.js → dialog-J9fwiM_6.js} +1 -1
- package/dist/webdist/assets/{dropdown-menu-BAw7TO38.js → dropdown-menu-CzGm9m23.js} +1 -1
- package/dist/webdist/assets/{erDiagram-TEJ5UH35-3L9k9gBp.js → erDiagram-TEJ5UH35-_kvCnb2f.js} +1 -1
- package/dist/webdist/assets/{flowDiagram-I6XJVG4X-l8IUdo3a.js → flowDiagram-I6XJVG4X-CyoD5NFD.js} +1 -1
- package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-Dj69Fodg.js → ganttDiagram-6RSMTGT7-BHbeuBsb.js} +1 -1
- package/dist/webdist/assets/{git-branch-BQTTe9uX.js → git-branch-DwkQBy50.js} +1 -1
- package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-Du_5XKj1.js → gitGraphDiagram-PVQCEYII-CLiLu2eD.js} +1 -1
- package/dist/webdist/assets/{index-HEbqFffw.js → index-sdlWNoLv.js} +2 -2
- package/dist/webdist/assets/{infoDiagram-5YYISTIA-Cna43URi.js → infoDiagram-5YYISTIA-DUsHPouL.js} +1 -1
- package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-hNRD3nFQ.js → ishikawaDiagram-YF4QCWOH-dmOwTeXN.js} +1 -1
- package/dist/webdist/assets/{journeyDiagram-JHISSGLW-DDZl1KbG.js → journeyDiagram-JHISSGLW-B-ROjE-9.js} +1 -1
- package/dist/webdist/assets/{kanban-definition-UN3LZRKU-Dj3MHmQh.js → kanban-definition-UN3LZRKU-BcmeWTJv.js} +1 -1
- package/dist/webdist/assets/{linear-BIBN6AEG.js → linear-D_7tH0LK.js} +1 -1
- package/dist/webdist/assets/{lock-D9JNrPro.js → lock-B6DBc8H2.js} +1 -1
- package/dist/webdist/assets/{mermaid.core-Cx_BdpgL.js → mermaid.core-COPgjG_G.js} +4 -4
- package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-AghKUeZq.js → mindmap-definition-RKZ34NQL-DYa-6SsO.js} +1 -1
- package/dist/webdist/assets/{pencil-BKwhK8ii.js → pencil-dcCQp7PP.js} +1 -1
- package/dist/webdist/assets/{personas-DpoGW3xD.js → personas-sJAzYoeu.js} +2 -1
- package/dist/webdist/assets/{pieDiagram-4H26LBE5-BNVbKhn5.js → pieDiagram-4H26LBE5-Btte96Dk.js} +1 -1
- package/dist/webdist/assets/{plus-BwuuyzoK.js → plus-BvjpqMip.js} +1 -1
- package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-Br5J-7iq.js → quadrantDiagram-W4KKPZXB-NS-IUaYv.js} +1 -1
- package/dist/webdist/assets/{refresh-cw-WDCjpgA4.js → refresh-cw-CJO18sMX.js} +1 -1
- package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-BRepNBqi.js → requirementDiagram-4Y6WPE33-6S8LhS5B.js} +1 -1
- package/dist/webdist/assets/{rotate-ccw-CsT9aYGg.js → rotate-ccw-Dg4hTNG6.js} +1 -1
- package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-CT1B03d0.js → sankeyDiagram-5OEKKPKP-DzP_aBJ0.js} +1 -1
- package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-B0h8Ocb4.js → sequenceDiagram-3UESZ5HK-DbM6WtvX.js} +1 -1
- package/dist/webdist/assets/{skeleton-C_8cUA8S.js → skeleton-BWg_uQbt.js} +1 -1
- package/dist/webdist/assets/{sliders-horizontal-DnZxStk1.js → sliders-horizontal-BEN8YNq8.js} +1 -1
- package/dist/webdist/assets/{sparkles-CgOPZTJx.js → sparkles-BXKX67cv.js} +1 -1
- package/dist/webdist/assets/{star-DRDhG9iP.js → star-UYX7iaVC.js} +1 -1
- package/dist/webdist/assets/{stateDiagram-AJRCARHV-C6W3BUAK.js → stateDiagram-AJRCARHV-DQd2hln9.js} +1 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-DnzMvaiS.js +1 -0
- package/dist/webdist/assets/{switch-Qya-6vsg.js → switch-CbjIB99k.js} +1 -1
- package/dist/webdist/assets/{terminal-CR5oCvI7.js → terminal-DVNTdtpQ.js} +1 -1
- package/dist/webdist/assets/{timeline-definition-PNZ67QCA-Dtggo9rb.js → timeline-definition-PNZ67QCA-BnfK2zzg.js} +1 -1
- package/dist/webdist/assets/{trash-2-Ejk7so01.js → trash-2-DkWlMuY9.js} +1 -1
- package/dist/webdist/assets/{useIsDesktop-CmQXgI2O.js → useIsDesktop-DvCa8GTi.js} +1 -1
- package/dist/webdist/assets/{vennDiagram-CIIHVFJN-mYXtI-Zx.js → vennDiagram-CIIHVFJN-tPTfe0OZ.js} +1 -1
- package/dist/webdist/assets/{wardley-L42UT6IY-D4Qq_cbT.js → wardley-L42UT6IY-D7FeB03v.js} +1 -1
- package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-dGqgsAhD.js → wardleyDiagram-YWT4CUSO-BDRCgpY_.js} +1 -1
- package/dist/webdist/assets/{wrench-C42l7xRT.js → wrench-BoYGiR8D.js} +1 -1
- package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-brgk11Hq.js → xychartDiagram-2RQKCTM6-CVQgje-B.js} +1 -1
- package/dist/webdist/index.html +1 -1
- package/package.json +1 -1
- package/dist/webdist/assets/WorkspaceScreen-DoG03BQz.js +0 -1
- package/dist/webdist/assets/channel-BCf5gRqW.js +0 -1
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-Bg74ry_y.js +0 -1
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-Bg74ry_y.js +0 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-CdjOVW2f.js +0 -1
package/dist/cli.js
CHANGED
|
@@ -30,10 +30,10 @@ function hideChildConsoleWindows() {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// src/cli.ts
|
|
33
|
-
import { spawn as
|
|
33
|
+
import { spawn as spawn9 } from "child_process";
|
|
34
34
|
import { openSync as openSync3, readFileSync as readFileSync19 } from "fs";
|
|
35
35
|
import { serve } from "@hono/node-server";
|
|
36
|
-
import { dirname as dirname12, join as
|
|
36
|
+
import { dirname as dirname12, join as join36 } from "path";
|
|
37
37
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
38
38
|
|
|
39
39
|
// src/config/config.ts
|
|
@@ -5435,6 +5435,7 @@ function rowToAgentRun(r) {
|
|
|
5435
5435
|
useWorktree: r.use_worktree === null ? void 0 : r.use_worktree === 1,
|
|
5436
5436
|
worktreeBranch: r.worktree_branch ?? void 0,
|
|
5437
5437
|
worktreeBase: r.worktree_base ?? void 0,
|
|
5438
|
+
worktreePath: r.worktree_path ?? void 0,
|
|
5438
5439
|
linesAdded: r.lines_added ?? void 0,
|
|
5439
5440
|
linesRemoved: r.lines_removed ?? void 0,
|
|
5440
5441
|
compactionSummary: r.compaction_summary ?? void 0,
|
|
@@ -5752,6 +5753,10 @@ var ConversationStore = class {
|
|
|
5752
5753
|
if (!this.hasColumn("api_usage", "gen_tps")) this.db.exec(`ALTER TABLE api_usage ADD COLUMN gen_tps REAL;`);
|
|
5753
5754
|
this.db.exec(`PRAGMA user_version = 39;`);
|
|
5754
5755
|
}
|
|
5756
|
+
if (v < 40) {
|
|
5757
|
+
if (!this.hasColumn("agent_runs", "worktree_path")) this.db.exec(`ALTER TABLE agent_runs ADD COLUMN worktree_path TEXT;`);
|
|
5758
|
+
this.db.exec(`PRAGMA user_version = 40;`);
|
|
5759
|
+
}
|
|
5755
5760
|
}
|
|
5756
5761
|
listConversations(q, kind = "all") {
|
|
5757
5762
|
if (q) {
|
|
@@ -6404,7 +6409,7 @@ var ConversationStore = class {
|
|
|
6404
6409
|
const id = randomUUID3();
|
|
6405
6410
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6406
6411
|
const agentId = params.agentId ?? null;
|
|
6407
|
-
this.db.prepare(`INSERT INTO agent_runs (id,conv_id,title,status,allowed_tools,agent_id,repo_root,repo_branch,use_worktree,worktree_branch,worktree_base,lines_added,lines_removed,code_agent,created_at,updated_at) VALUES ($id,$cid,$title,'queued',$at,$aid,$rr,$rb,$uw,$wb,$wbase,$la,$lr,$ca,$now,$now)`).run({
|
|
6412
|
+
this.db.prepare(`INSERT INTO agent_runs (id,conv_id,title,status,allowed_tools,agent_id,repo_root,repo_branch,use_worktree,worktree_branch,worktree_base,worktree_path,lines_added,lines_removed,code_agent,created_at,updated_at) VALUES ($id,$cid,$title,'queued',$at,$aid,$rr,$rb,$uw,$wb,$wbase,$wpath,$la,$lr,$ca,$now,$now)`).run({
|
|
6408
6413
|
$id: id,
|
|
6409
6414
|
$cid: params.convId,
|
|
6410
6415
|
$title: params.title,
|
|
@@ -6415,6 +6420,7 @@ var ConversationStore = class {
|
|
|
6415
6420
|
$uw: params.useWorktree === void 0 ? null : params.useWorktree ? 1 : 0,
|
|
6416
6421
|
$wb: params.worktreeBranch ?? null,
|
|
6417
6422
|
$wbase: params.worktreeBase ?? null,
|
|
6423
|
+
$wpath: params.worktreePath ?? null,
|
|
6418
6424
|
// Phase 1: diff stats are not computed yet — seed a code run's counters at 0 so the
|
|
6419
6425
|
// sidebar shows +0/-0 rather than a blank; a background 'agent' run leaves them NULL.
|
|
6420
6426
|
$la: params.repoRoot !== void 0 ? 0 : null,
|
|
@@ -11610,14 +11616,14 @@ import { Hono } from "hono";
|
|
|
11610
11616
|
import { cors } from "hono/cors";
|
|
11611
11617
|
import { existsSync as existsSync24, readFileSync as readFileSync17, statSync as statSync6 } from "fs";
|
|
11612
11618
|
import { fileURLToPath } from "url";
|
|
11613
|
-
import { dirname as dirname11, join as
|
|
11619
|
+
import { dirname as dirname11, join as join33, normalize as normalize2 } from "path";
|
|
11614
11620
|
import { Agent, setGlobalDispatcher } from "undici";
|
|
11615
11621
|
|
|
11616
11622
|
// src/api/routes.ts
|
|
11617
11623
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
11618
11624
|
import { existsSync as existsSync22, mkdirSync as mkdirSync16, readFileSync as readFileSync14, readdirSync as readdirSync10, realpathSync, rmSync as rmSync14, statSync as statSync5, writeFileSync as writeFileSync14 } from "fs";
|
|
11619
11625
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
11620
|
-
import { basename as basename3, dirname as dirname9, join as
|
|
11626
|
+
import { basename as basename3, dirname as dirname9, join as join29, resolve } from "path";
|
|
11621
11627
|
|
|
11622
11628
|
// src/comfyui/gate-template.ts
|
|
11623
11629
|
var GATE_VERSION = 2;
|
|
@@ -13701,7 +13707,7 @@ function resolveClaudePermissionMode(mode, choices) {
|
|
|
13701
13707
|
return prefs.find((p) => choices.includes(p)) ?? null;
|
|
13702
13708
|
}
|
|
13703
13709
|
var realHelpRunner = async () => {
|
|
13704
|
-
const { spawn:
|
|
13710
|
+
const { spawn: spawn10 } = await import("child_process");
|
|
13705
13711
|
const { buildShellCommand: buildShellCommand2 } = await import("./shell-command-QI6VPNNL.js");
|
|
13706
13712
|
const { requiresShell: requiresShell2, resolveExecutable: resolveExecutable2 } = await import("./resolve-executable-ZWQDUVY3.js");
|
|
13707
13713
|
return await new Promise((resolve5) => {
|
|
@@ -13716,7 +13722,7 @@ var realHelpRunner = async () => {
|
|
|
13716
13722
|
try {
|
|
13717
13723
|
const resolved = resolveExecutable2("claude");
|
|
13718
13724
|
const stdio = ["ignore", "pipe", "ignore"];
|
|
13719
|
-
const child = requiresShell2(resolved) ?
|
|
13725
|
+
const child = requiresShell2(resolved) ? spawn10(buildShellCommand2("claude", ["--help"]), { shell: true, stdio }) : spawn10(resolved ?? "claude", ["--help"], { stdio });
|
|
13720
13726
|
const timer = setTimeout(() => {
|
|
13721
13727
|
try {
|
|
13722
13728
|
child.kill();
|
|
@@ -14158,6 +14164,143 @@ var TerminalManager = class {
|
|
|
14158
14164
|
}
|
|
14159
14165
|
};
|
|
14160
14166
|
|
|
14167
|
+
// src/code/worktree.ts
|
|
14168
|
+
import { spawn as spawn5 } from "child_process";
|
|
14169
|
+
import { appendFile, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
|
|
14170
|
+
import { join as join25 } from "path";
|
|
14171
|
+
var WORKTREES_SUBDIR = join25(".turbollm", "worktrees");
|
|
14172
|
+
var EXCLUDE_LINE = "/.turbollm/";
|
|
14173
|
+
var MAX_BRANCH_SUFFIX = 20;
|
|
14174
|
+
function runGit(cwd, args, timeoutMs = 3e4) {
|
|
14175
|
+
return new Promise((resolve5) => {
|
|
14176
|
+
let stdout = "";
|
|
14177
|
+
let stderr = "";
|
|
14178
|
+
let settled = false;
|
|
14179
|
+
const done = (r) => {
|
|
14180
|
+
if (!settled) {
|
|
14181
|
+
settled = true;
|
|
14182
|
+
resolve5(r);
|
|
14183
|
+
}
|
|
14184
|
+
};
|
|
14185
|
+
try {
|
|
14186
|
+
const child = spawn5("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
14187
|
+
const timer = setTimeout(() => {
|
|
14188
|
+
try {
|
|
14189
|
+
child.kill();
|
|
14190
|
+
} catch {
|
|
14191
|
+
}
|
|
14192
|
+
done({ ok: false, stdout, stderr: stderr || `git ${args[0]} timed out after ${timeoutMs}ms` });
|
|
14193
|
+
}, timeoutMs);
|
|
14194
|
+
child.stdout?.on("data", (b) => {
|
|
14195
|
+
stdout += b.toString("utf8");
|
|
14196
|
+
});
|
|
14197
|
+
child.stderr?.on("data", (b) => {
|
|
14198
|
+
stderr += b.toString("utf8");
|
|
14199
|
+
});
|
|
14200
|
+
child.on("error", (e) => {
|
|
14201
|
+
clearTimeout(timer);
|
|
14202
|
+
done({ ok: false, stdout, stderr: e.message });
|
|
14203
|
+
});
|
|
14204
|
+
child.on("close", (code) => {
|
|
14205
|
+
clearTimeout(timer);
|
|
14206
|
+
done({ ok: code === 0, stdout, stderr });
|
|
14207
|
+
});
|
|
14208
|
+
} catch (e) {
|
|
14209
|
+
done({ ok: false, stdout: "", stderr: e.message });
|
|
14210
|
+
}
|
|
14211
|
+
});
|
|
14212
|
+
}
|
|
14213
|
+
async function isGitRepo(root) {
|
|
14214
|
+
const r = await runGit(root, ["rev-parse", "--is-inside-work-tree"]);
|
|
14215
|
+
return r.ok && r.stdout.trim() === "true";
|
|
14216
|
+
}
|
|
14217
|
+
function sanitizeBranchName(raw) {
|
|
14218
|
+
const cleaned = raw.trim().toLowerCase().replace(/\s+/g, "-").replace(/[~^:?*[\]\\]/g, "").replace(/\.\.+/g, "-").replace(/\/+/g, "/").replace(/^[/.]+/, "").replace(/\.lock$/, "").replace(/-+/g, "-").slice(0, 80).replace(/[-./]+$/, "");
|
|
14219
|
+
return cleaned || "turbollm-session";
|
|
14220
|
+
}
|
|
14221
|
+
async function refExists(repoRoot, name) {
|
|
14222
|
+
const r = await runGit(repoRoot, ["rev-parse", "--verify", "--quiet", `refs/heads/${name}`]);
|
|
14223
|
+
return r.ok && r.stdout.trim().length > 0;
|
|
14224
|
+
}
|
|
14225
|
+
async function findFreeBranchName(repoRoot, desired) {
|
|
14226
|
+
if (!await refExists(repoRoot, desired)) return desired;
|
|
14227
|
+
for (let n = 2; n <= MAX_BRANCH_SUFFIX; n++) {
|
|
14228
|
+
const candidate = `${desired}-${n}`;
|
|
14229
|
+
if (!await refExists(repoRoot, candidate)) return candidate;
|
|
14230
|
+
}
|
|
14231
|
+
return null;
|
|
14232
|
+
}
|
|
14233
|
+
async function ensureLocalExclude(repoRoot) {
|
|
14234
|
+
try {
|
|
14235
|
+
const gitDir = await resolveGitDir(repoRoot);
|
|
14236
|
+
if (!gitDir) return;
|
|
14237
|
+
const infoDir = join25(gitDir, "info");
|
|
14238
|
+
const excludePath = join25(infoDir, "exclude");
|
|
14239
|
+
let current = "";
|
|
14240
|
+
try {
|
|
14241
|
+
current = await readFile2(excludePath, "utf8");
|
|
14242
|
+
} catch {
|
|
14243
|
+
}
|
|
14244
|
+
if (current.split(/\r?\n/).some((l) => l.trim() === EXCLUDE_LINE)) return;
|
|
14245
|
+
await mkdir2(infoDir, { recursive: true });
|
|
14246
|
+
await appendFile(excludePath, `${current.endsWith("\n") || current === "" ? "" : "\n"}${EXCLUDE_LINE}
|
|
14247
|
+
`, "utf8");
|
|
14248
|
+
} catch {
|
|
14249
|
+
}
|
|
14250
|
+
}
|
|
14251
|
+
async function resolveGitDir(repoRoot) {
|
|
14252
|
+
const r = await runGit(repoRoot, ["rev-parse", "--absolute-git-dir"]);
|
|
14253
|
+
const p = r.stdout.trim();
|
|
14254
|
+
return r.ok && p ? p : null;
|
|
14255
|
+
}
|
|
14256
|
+
function agentCwd(run) {
|
|
14257
|
+
return run.worktreePath ?? run.repoRoot ?? "";
|
|
14258
|
+
}
|
|
14259
|
+
async function createSessionWorktree(params) {
|
|
14260
|
+
const { repoRoot, base: base2 } = params;
|
|
14261
|
+
if (!await isGitRepo(repoRoot)) {
|
|
14262
|
+
return {
|
|
14263
|
+
ok: false,
|
|
14264
|
+
code: "not_a_repo",
|
|
14265
|
+
message: `Cannot create a worktree: ${repoRoot} is not a git repository. Untick "Use worktree" to work in the folder directly, or run \`git init\` there first.`
|
|
14266
|
+
};
|
|
14267
|
+
}
|
|
14268
|
+
const desired = sanitizeBranchName(params.branch);
|
|
14269
|
+
const branch = await findFreeBranchName(repoRoot, desired);
|
|
14270
|
+
if (!branch) {
|
|
14271
|
+
return {
|
|
14272
|
+
ok: false,
|
|
14273
|
+
code: "branch_unavailable",
|
|
14274
|
+
message: `Cannot create a worktree: branches "${desired}" through "${desired}-${MAX_BRANCH_SUFFIX}" all already exist. Choose a different branch name.`
|
|
14275
|
+
};
|
|
14276
|
+
}
|
|
14277
|
+
await ensureLocalExclude(repoRoot);
|
|
14278
|
+
const path = join25(repoRoot, WORKTREES_SUBDIR, branch);
|
|
14279
|
+
const baseArgs = base2 && await refExists(repoRoot, base2) ? [base2] : [];
|
|
14280
|
+
const r = await runGit(repoRoot, ["worktree", "add", "-b", branch, path, ...baseArgs], 12e4);
|
|
14281
|
+
if (!r.ok) {
|
|
14282
|
+
return {
|
|
14283
|
+
ok: false,
|
|
14284
|
+
code: "git_failed",
|
|
14285
|
+
message: `Cannot create a worktree: ${(r.stderr || r.stdout).trim().slice(0, 400)}`
|
|
14286
|
+
};
|
|
14287
|
+
}
|
|
14288
|
+
return { ok: true, path, branch };
|
|
14289
|
+
}
|
|
14290
|
+
async function removeSessionWorktree(repoRoot, worktreePath) {
|
|
14291
|
+
const r = await runGit(repoRoot, ["worktree", "remove", worktreePath], 6e4);
|
|
14292
|
+
if (r.ok) return { ok: true };
|
|
14293
|
+
const err7 = (r.stderr || r.stdout).trim();
|
|
14294
|
+
if (/contains modified or untracked files|use --force/i.test(err7)) {
|
|
14295
|
+
return {
|
|
14296
|
+
ok: false,
|
|
14297
|
+
code: "dirty",
|
|
14298
|
+
message: `The worktree at ${worktreePath} still has uncommitted changes, so it was left in place. Commit or discard them, then remove it with: git worktree remove "${worktreePath}"`
|
|
14299
|
+
};
|
|
14300
|
+
}
|
|
14301
|
+
return { ok: false, code: "git_failed", message: err7.slice(0, 400) };
|
|
14302
|
+
}
|
|
14303
|
+
|
|
14161
14304
|
// src/terminal/terminal-routes.ts
|
|
14162
14305
|
async function body2(c) {
|
|
14163
14306
|
try {
|
|
@@ -14269,7 +14412,7 @@ function registerTerminalRoutes(app2, d) {
|
|
|
14269
14412
|
mode,
|
|
14270
14413
|
firstMessage
|
|
14271
14414
|
);
|
|
14272
|
-
const terminalId = m.create(run
|
|
14415
|
+
const terminalId = m.create(agentCwd(run), run.id, cols, rows, launchCommand);
|
|
14273
14416
|
if (!run.terminalLaunchedOnce) d.db.updateAgentRun(run.id, { terminalLaunchedOnce: true });
|
|
14274
14417
|
return c.json({ terminalId }, 201);
|
|
14275
14418
|
} catch (e) {
|
|
@@ -14797,7 +14940,7 @@ function catalogEngine(id) {
|
|
|
14797
14940
|
import { execFile as execFile11 } from "child_process";
|
|
14798
14941
|
import { cpSync as cpSync2, existsSync as existsSync20, mkdirSync as mkdirSync14, readdirSync as readdirSync9, rmSync as rmSync13 } from "fs";
|
|
14799
14942
|
import { tmpdir } from "os";
|
|
14800
|
-
import { join as
|
|
14943
|
+
import { join as join26 } from "path";
|
|
14801
14944
|
import { promisify as promisify8 } from "util";
|
|
14802
14945
|
var execFileP8 = promisify8(execFile11);
|
|
14803
14946
|
var REDIST_BASE = "https://developer.download.nvidia.com/compute/cuda/redist/";
|
|
@@ -14852,9 +14995,9 @@ async function provisionCuda(dataDir, onProgress, signal) {
|
|
|
14852
14995
|
phase: "resolving",
|
|
14853
14996
|
message: `Using CUDA ${version2}${driverMax ? ` (driver supports up to ${driverMax})` : ""}.`
|
|
14854
14997
|
});
|
|
14855
|
-
const root =
|
|
14856
|
-
const binDir =
|
|
14857
|
-
if (existsSync20(
|
|
14998
|
+
const root = join26(dataDir, "cuda", version2);
|
|
14999
|
+
const binDir = join26(root, "bin");
|
|
15000
|
+
if (existsSync20(join26(binDir, "nvcc.exe"))) {
|
|
14858
15001
|
onProgress({ phase: "assembling", message: `CUDA ${version2} already downloaded \u2014 reusing it.` });
|
|
14859
15002
|
return { root, binDir, version: version2 };
|
|
14860
15003
|
}
|
|
@@ -14869,7 +15012,7 @@ async function provisionCuda(dataDir, onProgress, signal) {
|
|
|
14869
15012
|
const rel = relOf(id);
|
|
14870
15013
|
if (rel) components.push({ id, url: REDIST_BASE + rel });
|
|
14871
15014
|
}
|
|
14872
|
-
const work =
|
|
15015
|
+
const work = join26(tmpdir(), `tllm-cuda-${version2}`);
|
|
14873
15016
|
rmSync13(work, { recursive: true, force: true });
|
|
14874
15017
|
mkdirSync14(work, { recursive: true });
|
|
14875
15018
|
const stageRoot = `${root}.partial`;
|
|
@@ -14878,23 +15021,23 @@ async function provisionCuda(dataDir, onProgress, signal) {
|
|
|
14878
15021
|
try {
|
|
14879
15022
|
for (let i = 0; i < components.length; i++) {
|
|
14880
15023
|
const { id, url } = components[i];
|
|
14881
|
-
const zip =
|
|
15024
|
+
const zip = join26(work, `${id}.zip`);
|
|
14882
15025
|
onProgress({ phase: "downloading", message: `Downloading ${id} (${i + 1}/${components.length})\u2026`, pct: 0 });
|
|
14883
15026
|
await downloadFile(url, zip, (p) => onProgress({ phase: "downloading", message: `Downloading ${id} (${i + 1}/${components.length})\u2026`, pct: p.pct }), signal);
|
|
14884
|
-
const exDir =
|
|
15027
|
+
const exDir = join26(work, id);
|
|
14885
15028
|
mkdirSync14(exDir, { recursive: true });
|
|
14886
15029
|
onProgress({ phase: "extracting", message: `Extracting ${id}\u2026` });
|
|
14887
15030
|
await extractArchive(zip, exDir);
|
|
14888
|
-
const inner = readdirSync9(exDir).map((n) =>
|
|
14889
|
-
const srcRoot = inner ??
|
|
15031
|
+
const inner = readdirSync9(exDir).map((n) => join26(exDir, n)).find((p) => existsSync20(join26(p, "include")) || existsSync20(join26(p, "bin")) || existsSync20(join26(p, "lib")));
|
|
15032
|
+
const srcRoot = inner ?? join26(exDir, readdirSync9(exDir)[0] ?? "");
|
|
14890
15033
|
cpSync2(srcRoot, stageRoot, { recursive: true, force: true });
|
|
14891
15034
|
rmSync13(zip, { force: true });
|
|
14892
15035
|
}
|
|
14893
|
-
if (!existsSync20(
|
|
15036
|
+
if (!existsSync20(join26(stageRoot, "bin", "nvcc.exe"))) {
|
|
14894
15037
|
throw new Error("Assembled CUDA toolkit is missing bin/nvcc.exe \u2014 the component set may have changed.");
|
|
14895
15038
|
}
|
|
14896
15039
|
rmSync13(root, { recursive: true, force: true });
|
|
14897
|
-
mkdirSync14(
|
|
15040
|
+
mkdirSync14(join26(dataDir, "cuda"), { recursive: true });
|
|
14898
15041
|
cpSync2(stageRoot, root, { recursive: true, force: true });
|
|
14899
15042
|
onProgress({ phase: "assembling", message: `CUDA ${version2} ready.` });
|
|
14900
15043
|
return { root, binDir, version: version2 };
|
|
@@ -14977,7 +15120,7 @@ function recommendEngines(p, engines) {
|
|
|
14977
15120
|
// src/artifacts/screenshot.ts
|
|
14978
15121
|
import { existsSync as existsSync21 } from "fs";
|
|
14979
15122
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
14980
|
-
import { join as
|
|
15123
|
+
import { join as join27 } from "path";
|
|
14981
15124
|
function chromeCandidates() {
|
|
14982
15125
|
const env = process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_PATH;
|
|
14983
15126
|
const out = env ? [env] : [];
|
|
@@ -14986,10 +15129,10 @@ function chromeCandidates() {
|
|
|
14986
15129
|
const pfx86 = process.env["PROGRAMFILES(X86)"] || "C:\\Program Files (x86)";
|
|
14987
15130
|
const local = process.env["LOCALAPPDATA"] || "";
|
|
14988
15131
|
for (const base2 of [pf, pfx86, local].filter(Boolean)) {
|
|
14989
|
-
out.push(
|
|
15132
|
+
out.push(join27(base2, "Google", "Chrome", "Application", "chrome.exe"));
|
|
14990
15133
|
}
|
|
14991
15134
|
for (const base2 of [pfx86, pf].filter(Boolean)) {
|
|
14992
|
-
out.push(
|
|
15135
|
+
out.push(join27(base2, "Microsoft", "Edge", "Application", "msedge.exe"));
|
|
14993
15136
|
}
|
|
14994
15137
|
} else if (process.platform === "darwin") {
|
|
14995
15138
|
out.push(
|
|
@@ -15072,10 +15215,10 @@ async function screenshotArtifact(html, opts) {
|
|
|
15072
15215
|
|
|
15073
15216
|
// src/telemetry/log.ts
|
|
15074
15217
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync13, mkdirSync as mkdirSync15 } from "fs";
|
|
15075
|
-
import { join as
|
|
15218
|
+
import { join as join28 } from "path";
|
|
15076
15219
|
var MAX_LOGGED = 200;
|
|
15077
15220
|
function logPath(dataDir) {
|
|
15078
|
-
return
|
|
15221
|
+
return join28(dataDir, "telemetry", "sent.json");
|
|
15079
15222
|
}
|
|
15080
15223
|
function readSentLog(dataDir) {
|
|
15081
15224
|
try {
|
|
@@ -15090,7 +15233,7 @@ function recordSent(dataDir, events) {
|
|
|
15090
15233
|
const sentAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
15091
15234
|
const fresh = events.map((event) => ({ sentAt, event })).reverse();
|
|
15092
15235
|
const next = [...fresh, ...readSentLog(dataDir)].slice(0, MAX_LOGGED);
|
|
15093
|
-
mkdirSync15(
|
|
15236
|
+
mkdirSync15(join28(dataDir, "telemetry"), { recursive: true });
|
|
15094
15237
|
writeFileSync13(logPath(dataDir), JSON.stringify(next));
|
|
15095
15238
|
} catch {
|
|
15096
15239
|
}
|
|
@@ -15213,7 +15356,7 @@ function registerApi(app2, d) {
|
|
|
15213
15356
|
let installedVersion = null;
|
|
15214
15357
|
if (gatePath) {
|
|
15215
15358
|
try {
|
|
15216
|
-
const src = readFileSync14(
|
|
15359
|
+
const src = readFileSync14(join29(gatePath, "__init__.py"), "utf-8");
|
|
15217
15360
|
const m = src.match(/^GATE_VERSION\s*=\s*(\d+)/m);
|
|
15218
15361
|
if (m) installedVersion = Number(m[1]);
|
|
15219
15362
|
} catch {
|
|
@@ -15253,7 +15396,7 @@ function registerApi(app2, d) {
|
|
|
15253
15396
|
const sys = getSysInfo();
|
|
15254
15397
|
const vendor = primaryVendor(sys);
|
|
15255
15398
|
const recommended = recommendBackendId(vendor, sys.gpus.length > 0);
|
|
15256
|
-
const root =
|
|
15399
|
+
const root = join29(d.store.dir(), "engines");
|
|
15257
15400
|
const active = d.registry.active();
|
|
15258
15401
|
const regEngines = d.registry.list().engines;
|
|
15259
15402
|
const backendOf = (p) => p.match(/llama\.cpp-[^/\\]+-(cuda|rocm|sycl|vulkan|metal|cpu)/i)?.[1]?.toLowerCase();
|
|
@@ -15274,7 +15417,7 @@ function registerApi(app2, d) {
|
|
|
15274
15417
|
engineId: eng?.id ?? ""
|
|
15275
15418
|
};
|
|
15276
15419
|
});
|
|
15277
|
-
const mlxVenvPy =
|
|
15420
|
+
const mlxVenvPy = join29(root, "mlx", "venv", process.platform === "win32" ? "Scripts/python.exe" : "bin/python");
|
|
15278
15421
|
const mlxInstalledOnDisk = existsSync22(mlxVenvPy);
|
|
15279
15422
|
const mlxEngine = regEngines.find((e) => e.kind === "mlx");
|
|
15280
15423
|
const mlx = {
|
|
@@ -15290,7 +15433,7 @@ function registerApi(app2, d) {
|
|
|
15290
15433
|
const b = await body3(c);
|
|
15291
15434
|
const def = availableBackends().find((x) => x.id === b.backend);
|
|
15292
15435
|
if (!def) return err3(c, 400, "invalid_config_value", "Unknown backend for this platform.");
|
|
15293
|
-
const root =
|
|
15436
|
+
const root = join29(d.store.dir(), "engines");
|
|
15294
15437
|
const existing = installedBackendBuild(root, def.id);
|
|
15295
15438
|
if (existing) {
|
|
15296
15439
|
return c.json({ accepted: false, alreadyInstalled: true, build: existing.tag });
|
|
@@ -15334,7 +15477,7 @@ function registerApi(app2, d) {
|
|
|
15334
15477
|
app2.post("/api/v1/engines/backends/:id/update", async (c) => {
|
|
15335
15478
|
const def = availableBackends().find((x) => x.id === c.req.param("id"));
|
|
15336
15479
|
if (!def) return err3(c, 400, "invalid_config_value", "Unknown backend for this platform.");
|
|
15337
|
-
const root =
|
|
15480
|
+
const root = join29(d.store.dir(), "engines");
|
|
15338
15481
|
const oldBin = installedBackendBuild(root, def.id)?.bin ?? null;
|
|
15339
15482
|
if (!oldBin) return err3(c, 409, "not_installed", "Backend is not installed \u2014 download it first.");
|
|
15340
15483
|
{
|
|
@@ -15393,7 +15536,7 @@ function registerApi(app2, d) {
|
|
|
15393
15536
|
app2.post("/api/v1/engines/backends/:id/enable", async (c) => {
|
|
15394
15537
|
const def = availableBackends().find((x) => x.id === c.req.param("id"));
|
|
15395
15538
|
if (!def) return err3(c, 400, "invalid_config_value", "Unknown backend for this platform.");
|
|
15396
|
-
const root =
|
|
15539
|
+
const root = join29(d.store.dir(), "engines");
|
|
15397
15540
|
const build2 = installedBackendBuild(root, def.id);
|
|
15398
15541
|
if (!build2) return err3(c, 409, "not_installed", "Backend is not installed on disk \u2014 download it first.");
|
|
15399
15542
|
let eng = d.registry.list().engines.find((e) => e.binPath === build2.bin);
|
|
@@ -15404,7 +15547,7 @@ function registerApi(app2, d) {
|
|
|
15404
15547
|
app2.delete("/api/v1/engines/backends/:id", async (c) => {
|
|
15405
15548
|
const def = availableBackends().find((x) => x.id === c.req.param("id"));
|
|
15406
15549
|
if (!def) return err3(c, 400, "invalid_config_value", "Unknown backend for this platform.");
|
|
15407
|
-
const root =
|
|
15550
|
+
const root = join29(d.store.dir(), "engines");
|
|
15408
15551
|
const backendDirRe = new RegExp(`[\\\\/]engines[\\\\/]llama\\.cpp-[^\\\\/]+-${def.id}[\\\\/]`);
|
|
15409
15552
|
const engs = d.registry.list().engines.filter((e) => backendDirRe.test(e.binPath));
|
|
15410
15553
|
const activeId = d.registry.active()?.id;
|
|
@@ -15426,7 +15569,7 @@ function registerApi(app2, d) {
|
|
|
15426
15569
|
const busy = engineWorkBusy(d);
|
|
15427
15570
|
if (busy) return err3(c, 409, "engine_already_running", busy);
|
|
15428
15571
|
}
|
|
15429
|
-
const root =
|
|
15572
|
+
const root = join29(d.store.dir(), "engines");
|
|
15430
15573
|
const upgrade = c.req.query("update") === "1";
|
|
15431
15574
|
void (async () => {
|
|
15432
15575
|
try {
|
|
@@ -15449,7 +15592,7 @@ function registerApi(app2, d) {
|
|
|
15449
15592
|
const busy = engineWorkBusy(d);
|
|
15450
15593
|
if (busy) return err3(c, 409, "engine_already_running", busy);
|
|
15451
15594
|
}
|
|
15452
|
-
const root =
|
|
15595
|
+
const root = join29(d.store.dir(), "engines");
|
|
15453
15596
|
const upgrade = c.req.query("update") === "1";
|
|
15454
15597
|
void (async () => {
|
|
15455
15598
|
try {
|
|
@@ -15466,12 +15609,12 @@ function registerApi(app2, d) {
|
|
|
15466
15609
|
});
|
|
15467
15610
|
app2.get("/api/v1/engines/catalog", (c) => {
|
|
15468
15611
|
const regEngines = d.registry.list().engines;
|
|
15469
|
-
const enginesRoot =
|
|
15612
|
+
const enginesRoot = join29(d.store.dir(), "engines");
|
|
15470
15613
|
const items = catalogForPlatform().map((e) => {
|
|
15471
15614
|
let installed2;
|
|
15472
15615
|
let enabled;
|
|
15473
15616
|
if (e.provision === "pip") {
|
|
15474
|
-
const pyPath =
|
|
15617
|
+
const pyPath = join29(
|
|
15475
15618
|
enginesRoot,
|
|
15476
15619
|
e.id,
|
|
15477
15620
|
"venv",
|
|
@@ -15480,7 +15623,7 @@ function registerApi(app2, d) {
|
|
|
15480
15623
|
installed2 = existsSync22(pyPath);
|
|
15481
15624
|
enabled = regEngines.some((x) => x.kind === e.kind);
|
|
15482
15625
|
} else if (e.id === "turboquant") {
|
|
15483
|
-
const tqDir =
|
|
15626
|
+
const tqDir = join29(enginesRoot, "turboquant");
|
|
15484
15627
|
installed2 = existsSync22(tqDir);
|
|
15485
15628
|
enabled = regEngines.some((x) => /[\\/]engines[\\/]turboquant[\\/]/.test(x.binPath));
|
|
15486
15629
|
} else if (e.id === "koboldcpp") {
|
|
@@ -15550,9 +15693,9 @@ function registerApi(app2, d) {
|
|
|
15550
15693
|
const busy = engineWorkBusy(d);
|
|
15551
15694
|
if (busy) return err3(c, 409, "engine_already_running", busy);
|
|
15552
15695
|
const name = (b.name ?? "").trim() || void 0;
|
|
15553
|
-
const enginesRoot =
|
|
15696
|
+
const enginesRoot = join29(d.store.dir(), "engines");
|
|
15554
15697
|
const toolchainDirs = d.store.snapshot().build.toolchainDirs;
|
|
15555
|
-
const buildRoot =
|
|
15698
|
+
const buildRoot = join29(enginesRoot, "build", buildDirName(repoUrl, branch, commit));
|
|
15556
15699
|
const ac = new AbortController();
|
|
15557
15700
|
buildAbort = ac;
|
|
15558
15701
|
d.build.start(name ?? repoUrl);
|
|
@@ -15662,7 +15805,7 @@ function registerApi(app2, d) {
|
|
|
15662
15805
|
const busy = engineWorkBusy(d);
|
|
15663
15806
|
if (busy) return err3(c, 409, "engine_already_running", busy);
|
|
15664
15807
|
}
|
|
15665
|
-
const root =
|
|
15808
|
+
const root = join29(d.store.dir(), "engines");
|
|
15666
15809
|
const upgrade = c.req.query("update") === "1";
|
|
15667
15810
|
void (async () => {
|
|
15668
15811
|
try {
|
|
@@ -15682,7 +15825,7 @@ function registerApi(app2, d) {
|
|
|
15682
15825
|
const busy = engineWorkBusy(d);
|
|
15683
15826
|
if (busy) return err3(c, 409, "engine_already_running", busy);
|
|
15684
15827
|
}
|
|
15685
|
-
const root =
|
|
15828
|
+
const root = join29(d.store.dir(), "engines");
|
|
15686
15829
|
const upgrade = c.req.query("update") === "1";
|
|
15687
15830
|
void (async () => {
|
|
15688
15831
|
try {
|
|
@@ -15707,13 +15850,13 @@ function registerApi(app2, d) {
|
|
|
15707
15850
|
const busy = engineWorkBusy(d);
|
|
15708
15851
|
if (busy) return err3(c, 409, "engine_already_running", busy);
|
|
15709
15852
|
}
|
|
15710
|
-
const root =
|
|
15853
|
+
const root = join29(d.store.dir(), "engines");
|
|
15711
15854
|
const upgrade = c.req.query("update") === "1";
|
|
15712
15855
|
void (async () => {
|
|
15713
15856
|
try {
|
|
15714
15857
|
d.provision.start("turboquant");
|
|
15715
15858
|
if (upgrade) {
|
|
15716
|
-
const tqDir =
|
|
15859
|
+
const tqDir = join29(root, "turboquant");
|
|
15717
15860
|
if (existsSync22(tqDir)) rmSync14(tqDir, { recursive: true, force: true });
|
|
15718
15861
|
}
|
|
15719
15862
|
const bin = await provisionTurboquant(
|
|
@@ -15742,7 +15885,7 @@ function registerApi(app2, d) {
|
|
|
15742
15885
|
const busy = engineWorkBusy(d);
|
|
15743
15886
|
if (busy) return err3(c, 409, "engine_already_running", busy);
|
|
15744
15887
|
}
|
|
15745
|
-
const root =
|
|
15888
|
+
const root = join29(d.store.dir(), "engines");
|
|
15746
15889
|
const upgrade = c.req.query("update") === "1";
|
|
15747
15890
|
const hasNvidia = primaryVendor(getSysInfo()) === "nvidia";
|
|
15748
15891
|
void (async () => {
|
|
@@ -15773,7 +15916,7 @@ function registerApi(app2, d) {
|
|
|
15773
15916
|
const busy = engineWorkBusy(d);
|
|
15774
15917
|
if (busy) return err3(c, 409, "engine_already_running", busy);
|
|
15775
15918
|
}
|
|
15776
|
-
const root =
|
|
15919
|
+
const root = join29(d.store.dir(), "engines");
|
|
15777
15920
|
const upgrade = c.req.query("update") === "1";
|
|
15778
15921
|
void (async () => {
|
|
15779
15922
|
try {
|
|
@@ -15880,7 +16023,7 @@ function registerApi(app2, d) {
|
|
|
15880
16023
|
}
|
|
15881
16024
|
d.registry.remove(id);
|
|
15882
16025
|
if (purge && eng) {
|
|
15883
|
-
const enginesRoot =
|
|
16026
|
+
const enginesRoot = join29(d.store.dir(), "engines");
|
|
15884
16027
|
const purgeDir = engineInstallDir(eng, enginesRoot);
|
|
15885
16028
|
if (purgeDir && existsSync22(purgeDir)) {
|
|
15886
16029
|
rmSync14(purgeDir, { recursive: true, force: true });
|
|
@@ -15969,12 +16112,12 @@ function registerApi(app2, d) {
|
|
|
15969
16112
|
let isDir = d2.isDirectory();
|
|
15970
16113
|
if (d2.isSymbolicLink()) {
|
|
15971
16114
|
try {
|
|
15972
|
-
isDir = statSync5(
|
|
16115
|
+
isDir = statSync5(join29(real, d2.name)).isDirectory();
|
|
15973
16116
|
} catch {
|
|
15974
16117
|
isDir = false;
|
|
15975
16118
|
}
|
|
15976
16119
|
}
|
|
15977
|
-
return { name: d2.name, path:
|
|
16120
|
+
return { name: d2.name, path: join29(real, d2.name), isDir };
|
|
15978
16121
|
}).sort((a, b) => a.isDir === b.isDir ? a.name.localeCompare(b.name) : a.isDir ? -1 : 1);
|
|
15979
16122
|
} catch {
|
|
15980
16123
|
return err3(c, 400, "fs_read_failed", "Could not read that folder (permission denied or not a directory).");
|
|
@@ -16117,17 +16260,17 @@ function registerApi(app2, d) {
|
|
|
16117
16260
|
}
|
|
16118
16261
|
let customNodes;
|
|
16119
16262
|
if (basename3(root).toLowerCase() === "custom_nodes") customNodes = root;
|
|
16120
|
-
else if (existsSync22(
|
|
16263
|
+
else if (existsSync22(join29(root, "custom_nodes"))) customNodes = join29(root, "custom_nodes");
|
|
16121
16264
|
else {
|
|
16122
16265
|
return err3(c, 400, "invalid_config_value", "No 'custom_nodes' folder here. Point me at your ComfyUI folder or its custom_nodes folder.");
|
|
16123
16266
|
}
|
|
16124
16267
|
const reqUrl = new URL(c.req.url);
|
|
16125
16268
|
const port2 = reqUrl.port || (reqUrl.protocol === "https:" ? "443" : "80");
|
|
16126
16269
|
const base2 = `http://127.0.0.1:${port2}`;
|
|
16127
|
-
const gateDir =
|
|
16270
|
+
const gateDir = join29(customNodes, "turbollm_gate");
|
|
16128
16271
|
try {
|
|
16129
16272
|
mkdirSync16(gateDir, { recursive: true });
|
|
16130
|
-
writeFileSync14(
|
|
16273
|
+
writeFileSync14(join29(gateDir, "__init__.py"), gateNodeSource(base2));
|
|
16131
16274
|
} catch (e) {
|
|
16132
16275
|
return err3(c, 500, "fs_write_failed", `Could not write the gate node: ${e instanceof Error ? e.message : e}`);
|
|
16133
16276
|
}
|
|
@@ -16494,7 +16637,7 @@ function registerApi(app2, d) {
|
|
|
16494
16637
|
};
|
|
16495
16638
|
const isMemoryServer = server2.transport === "stdio" && (server2.command?.includes("server-memory") || server2.args?.some((a) => a.includes("server-memory")));
|
|
16496
16639
|
if (isMemoryServer && !server2.env?.MEMORY_FILE_PATH) {
|
|
16497
|
-
server2.env = { ...server2.env, MEMORY_FILE_PATH:
|
|
16640
|
+
server2.env = { ...server2.env, MEMORY_FILE_PATH: join29(homedir4(), ".turbollm", "mcp-memory.jsonl") };
|
|
16498
16641
|
}
|
|
16499
16642
|
d.store.update((cfg2) => {
|
|
16500
16643
|
cfg2.mcp.servers.push(server2);
|
|
@@ -16953,19 +17096,19 @@ function engineInstallDir(eng, enginesRoot) {
|
|
|
16953
17096
|
return n.startsWith(norm + "/") || n.startsWith(norm + "\\");
|
|
16954
17097
|
};
|
|
16955
17098
|
if (eng.kind === "mlx") {
|
|
16956
|
-
const d =
|
|
17099
|
+
const d = join29(enginesRoot, "mlx", "venv");
|
|
16957
17100
|
return inside(d) ? d : null;
|
|
16958
17101
|
}
|
|
16959
17102
|
if (eng.kind === "rapid-mlx") {
|
|
16960
|
-
const d =
|
|
17103
|
+
const d = join29(enginesRoot, "rapid-mlx", "venv");
|
|
16961
17104
|
return inside(d) ? d : null;
|
|
16962
17105
|
}
|
|
16963
17106
|
if (eng.kind === "vllm") {
|
|
16964
|
-
const d =
|
|
17107
|
+
const d = join29(enginesRoot, "vllm", "venv");
|
|
16965
17108
|
return inside(d) ? d : null;
|
|
16966
17109
|
}
|
|
16967
17110
|
if (/[\\/]engines[\\/]turboquant[\\/]/.test(eng.binPath)) {
|
|
16968
|
-
const d =
|
|
17111
|
+
const d = join29(enginesRoot, "turboquant");
|
|
16969
17112
|
return inside(d) ? d : null;
|
|
16970
17113
|
}
|
|
16971
17114
|
if (eng.kind === "koboldcpp") {
|
|
@@ -16980,7 +17123,7 @@ function engineInstallDir(eng, enginesRoot) {
|
|
|
16980
17123
|
if (srcDir) return inside(srcDir) ? srcDir : null;
|
|
16981
17124
|
const llamaMatch = eng.binPath.replace(/\\/g, "/").match(/\/(llama\.cpp-[^/]+)\//i);
|
|
16982
17125
|
if (llamaMatch) {
|
|
16983
|
-
const d =
|
|
17126
|
+
const d = join29(enginesRoot, llamaMatch[1]);
|
|
16984
17127
|
return inside(d) ? d : null;
|
|
16985
17128
|
}
|
|
16986
17129
|
return null;
|
|
@@ -17402,8 +17545,8 @@ import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
|
17402
17545
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
17403
17546
|
|
|
17404
17547
|
// src/code/code-session.ts
|
|
17405
|
-
import { join as
|
|
17406
|
-
import { readFile as
|
|
17548
|
+
import { join as join32, resolve as resolve3 } from "path";
|
|
17549
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
17407
17550
|
import { Type, Unsafe } from "typebox";
|
|
17408
17551
|
import {
|
|
17409
17552
|
createAgentSession,
|
|
@@ -17419,7 +17562,7 @@ import {
|
|
|
17419
17562
|
// src/code/robust-bash.ts
|
|
17420
17563
|
import { access as fsAccess } from "fs/promises";
|
|
17421
17564
|
import { constants } from "fs";
|
|
17422
|
-
import { spawn as
|
|
17565
|
+
import { spawn as spawn6 } from "child_process";
|
|
17423
17566
|
import { getShellConfig } from "@earendil-works/pi-coding-agent";
|
|
17424
17567
|
var EXIT_STDIO_GRACE_MS = 100;
|
|
17425
17568
|
function waitForChildProcess(child) {
|
|
@@ -17506,7 +17649,7 @@ function sleep3(ms) {
|
|
|
17506
17649
|
async function killDescendantsViaWmi(rootPid) {
|
|
17507
17650
|
const psScript = `$all = Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId; $ids = @(${rootPid}); $frontier = @(${rootPid}); for ($i = 0; $i -lt 8; $i++) { $children = $all | Where-Object { $frontier -contains $_.ParentProcessId } | Select-Object -ExpandProperty ProcessId; if (-not $children) { break }; $ids += $children; $frontier = $children }; $ids | Select-Object -Unique | ForEach-Object { Write-Output $_ }`;
|
|
17508
17651
|
const pids = await new Promise((resolve5) => {
|
|
17509
|
-
const proc =
|
|
17652
|
+
const proc = spawn6("powershell", ["-NoProfile", "-NonInteractive", "-Command", psScript], {
|
|
17510
17653
|
stdio: ["ignore", "pipe", "ignore"],
|
|
17511
17654
|
windowsHide: true
|
|
17512
17655
|
});
|
|
@@ -17522,7 +17665,7 @@ async function killDescendantsViaWmi(rootPid) {
|
|
|
17522
17665
|
});
|
|
17523
17666
|
for (const pid of pids) {
|
|
17524
17667
|
try {
|
|
17525
|
-
|
|
17668
|
+
spawn6("taskkill", ["/F", "/PID", String(pid)], { stdio: "ignore", detached: true, windowsHide: true });
|
|
17526
17669
|
} catch {
|
|
17527
17670
|
}
|
|
17528
17671
|
}
|
|
@@ -17531,7 +17674,7 @@ async function killDescendantsViaWmi(rootPid) {
|
|
|
17531
17674
|
function killProcessTreeFast(pid) {
|
|
17532
17675
|
if (process.platform === "win32") {
|
|
17533
17676
|
try {
|
|
17534
|
-
|
|
17677
|
+
spawn6("taskkill", ["/F", "/T", "/PID", String(pid)], { stdio: "ignore", detached: true, windowsHide: true });
|
|
17535
17678
|
} catch {
|
|
17536
17679
|
}
|
|
17537
17680
|
} else {
|
|
@@ -17583,7 +17726,7 @@ function createRobustBashOperations(options) {
|
|
|
17583
17726
|
Cannot execute bash commands.`);
|
|
17584
17727
|
}
|
|
17585
17728
|
const commandFromStdin = shellConfig.commandTransport === "stdin";
|
|
17586
|
-
const child =
|
|
17729
|
+
const child = spawn6(
|
|
17587
17730
|
shellConfig.shell,
|
|
17588
17731
|
commandFromStdin ? shellConfig.args : [...shellConfig.args, command],
|
|
17589
17732
|
{
|
|
@@ -17631,7 +17774,7 @@ Cannot execute bash commands.`);
|
|
|
17631
17774
|
|
|
17632
17775
|
// src/code/containment.ts
|
|
17633
17776
|
import { realpathSync as realpathSync2 } from "fs";
|
|
17634
|
-
import { resolve as resolve2, dirname as dirname10, basename as basename4, join as
|
|
17777
|
+
import { resolve as resolve2, dirname as dirname10, basename as basename4, join as join30, sep } from "path";
|
|
17635
17778
|
var isWin = process.platform === "win32";
|
|
17636
17779
|
function canonicalize(input) {
|
|
17637
17780
|
if (typeof input !== "string" || input.length === 0) return null;
|
|
@@ -17647,7 +17790,7 @@ function canonicalize(input) {
|
|
|
17647
17790
|
for (let i = 0; i < 4096; i++) {
|
|
17648
17791
|
try {
|
|
17649
17792
|
const real = realpathSync2(cur);
|
|
17650
|
-
const full = tail.length ?
|
|
17793
|
+
const full = tail.length ? join30(real, ...[...tail].reverse()) : real;
|
|
17651
17794
|
return normForCompare(full);
|
|
17652
17795
|
} catch {
|
|
17653
17796
|
const parent = dirname10(cur);
|
|
@@ -17691,7 +17834,7 @@ function isContainedFromRoot(input, root) {
|
|
|
17691
17834
|
|
|
17692
17835
|
// src/code/persona.ts
|
|
17693
17836
|
import { readFileSync as readFileSync15 } from "fs";
|
|
17694
|
-
import { join as
|
|
17837
|
+
import { join as join31 } from "path";
|
|
17695
17838
|
var MAX_SKILL_CATALOG_CHARS = 16e3;
|
|
17696
17839
|
var MAX_AGENTS_MD_CHARS = 2e4;
|
|
17697
17840
|
var MAX_AGENTS_MD_CANDIDATES = 8;
|
|
@@ -17819,7 +17962,7 @@ function readAgentsFile(path) {
|
|
|
17819
17962
|
function resolveAgentsFile(root, candidates) {
|
|
17820
17963
|
for (const entry of candidates.slice(0, MAX_AGENTS_MD_CANDIDATES)) {
|
|
17821
17964
|
if (!isContainedFromRoot(entry, root)) continue;
|
|
17822
|
-
const text = readAgentsFile(
|
|
17965
|
+
const text = readAgentsFile(join31(root, entry));
|
|
17823
17966
|
if (text !== null) return { name: entry, text };
|
|
17824
17967
|
}
|
|
17825
17968
|
return null;
|
|
@@ -17914,7 +18057,7 @@ function isDependencyAddCommand(command) {
|
|
|
17914
18057
|
}
|
|
17915
18058
|
|
|
17916
18059
|
// src/code/lsp-client.ts
|
|
17917
|
-
import { spawn as
|
|
18060
|
+
import { spawn as spawn7 } from "child_process";
|
|
17918
18061
|
import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc/node";
|
|
17919
18062
|
import { URI } from "vscode-uri";
|
|
17920
18063
|
var toFileUri = (path) => URI.file(path).toString();
|
|
@@ -17931,7 +18074,7 @@ function resolveFallbackTsserverPath() {
|
|
|
17931
18074
|
cachedTsserverPath = new Promise((resolvePromise) => {
|
|
17932
18075
|
const npxCmd = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
17933
18076
|
const script = "process.stdout.write(require.resolve('typescript/lib/tsserver.js'))";
|
|
17934
|
-
const proc =
|
|
18077
|
+
const proc = spawn7(npxCmd, ["-y", "-p", "typescript", "node", "-e", script], npxSpawnOptions());
|
|
17935
18078
|
let out = "";
|
|
17936
18079
|
proc.stdout?.on("data", (d) => {
|
|
17937
18080
|
out += String(d);
|
|
@@ -17977,7 +18120,7 @@ var LspClient = class {
|
|
|
17977
18120
|
try {
|
|
17978
18121
|
const fallbackTsserverPath = this.spec.language === "typescript" ? await resolveFallbackTsserverPath() : null;
|
|
17979
18122
|
const npxCmd = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
17980
|
-
const proc =
|
|
18123
|
+
const proc = spawn7(npxCmd, this.spec.npxArgs, npxSpawnOptions(this.rootDir));
|
|
17981
18124
|
this.proc = proc;
|
|
17982
18125
|
const spawnFailure = new Promise((resolve5) => {
|
|
17983
18126
|
proc.once("error", (e) => resolve5(e instanceof Error ? e.message : String(e)));
|
|
@@ -18353,7 +18496,7 @@ async function runCodeSession(params) {
|
|
|
18353
18496
|
console.warn(`[code-session] history seed for session ${sessionId} produced ${sessionManager.getEntries().length}/${expectedEntries} expected entries \u2014 falling back to a plain-text history prefix on this turn's prompt`);
|
|
18354
18497
|
task = `${HISTORY_FALLBACK_PREFIX}${renderHistoryFallbackText(summaryText, priorMessages)}${HISTORY_FALLBACK_SUFFIX}${rawTask}`;
|
|
18355
18498
|
}
|
|
18356
|
-
const agentDir =
|
|
18499
|
+
const agentDir = join32(d.store.dir(), "pi-agent");
|
|
18357
18500
|
modelRegistry.registerProvider("local", {
|
|
18358
18501
|
baseUrl: `${target}/v1`,
|
|
18359
18502
|
apiKey: "agent-key",
|
|
@@ -18484,7 +18627,7 @@ async function runCodeSession(params) {
|
|
|
18484
18627
|
await sink({ event: "tool_call", data });
|
|
18485
18628
|
});
|
|
18486
18629
|
};
|
|
18487
|
-
const skillAgentDir =
|
|
18630
|
+
const skillAgentDir = join32(d.store.dir(), "pi-agent");
|
|
18488
18631
|
const skillResourceLoader = new DefaultResourceLoader({
|
|
18489
18632
|
cwd: repoRoot,
|
|
18490
18633
|
agentDir: skillAgentDir,
|
|
@@ -18627,7 +18770,7 @@ async function runCodeSession(params) {
|
|
|
18627
18770
|
await sink({ event: "tool_call", data });
|
|
18628
18771
|
});
|
|
18629
18772
|
};
|
|
18630
|
-
const subAgentDir =
|
|
18773
|
+
const subAgentDir = join32(d.store.dir(), "pi-agent");
|
|
18631
18774
|
const subResourceLoader = new DefaultResourceLoader({
|
|
18632
18775
|
cwd: repoRoot,
|
|
18633
18776
|
agentDir: subAgentDir,
|
|
@@ -18829,7 +18972,7 @@ async function runCodeSession(params) {
|
|
|
18829
18972
|
if (spec) {
|
|
18830
18973
|
try {
|
|
18831
18974
|
const absPath = resolve3(repoRoot, relPath);
|
|
18832
|
-
const content = await
|
|
18975
|
+
const content = await readFile3(absPath, "utf8");
|
|
18833
18976
|
const diagnostics = await getLspClient(spec).getDiagnostics(absPath, content);
|
|
18834
18977
|
if (diagnostics.length > 0) {
|
|
18835
18978
|
const shown = diagnostics.slice(0, 20);
|
|
@@ -19185,7 +19328,7 @@ async function compactCodeSession(params) {
|
|
|
19185
19328
|
return payload;
|
|
19186
19329
|
});
|
|
19187
19330
|
};
|
|
19188
|
-
const compactAgentDir =
|
|
19331
|
+
const compactAgentDir = join32(d.store.dir(), "pi-agent");
|
|
19189
19332
|
const compactResourceLoader = new DefaultResourceLoader({
|
|
19190
19333
|
cwd: repoRoot,
|
|
19191
19334
|
agentDir: compactAgentDir,
|
|
@@ -19287,7 +19430,7 @@ async function lookbackPreCompactionHistory(params) {
|
|
|
19287
19430
|
return payload;
|
|
19288
19431
|
});
|
|
19289
19432
|
};
|
|
19290
|
-
const lookbackAgentDir =
|
|
19433
|
+
const lookbackAgentDir = join32(d.store.dir(), "pi-agent");
|
|
19291
19434
|
const lookbackResourceLoader = new DefaultResourceLoader({
|
|
19292
19435
|
cwd: repoRoot,
|
|
19293
19436
|
agentDir: lookbackAgentDir,
|
|
@@ -20150,7 +20293,10 @@ function toSidebarRow(run) {
|
|
|
20150
20293
|
convId: run.convId,
|
|
20151
20294
|
title: run.title,
|
|
20152
20295
|
status: toSessionStatus(run.status),
|
|
20153
|
-
branch
|
|
20296
|
+
// A worktree session's real branch is the one git created for it — `repoBranch` is only ever
|
|
20297
|
+
// set for a session working directly in the repo, so without this the sidebar showed a blank
|
|
20298
|
+
// branch for exactly the sessions that have a dedicated one (caught in end-to-end testing).
|
|
20299
|
+
branch: run.worktreeBranch ?? run.repoBranch ?? "",
|
|
20154
20300
|
when: relativeTime(run.createdAt),
|
|
20155
20301
|
add: run.linesAdded ?? 0,
|
|
20156
20302
|
del: run.linesRemoved ?? 0,
|
|
@@ -20159,7 +20305,11 @@ function toSidebarRow(run) {
|
|
|
20159
20305
|
running: void 0,
|
|
20160
20306
|
// filled from CodeRunManager below when available
|
|
20161
20307
|
createdAt: run.createdAt,
|
|
20308
|
+
// `repoRoot` stays the PROJECT — that's what the user picked and what identifies the session.
|
|
20309
|
+
// `worktreePath` is where the agent actually works; surfaced alongside rather than replacing
|
|
20310
|
+
// it, so the UI can show both without either being a lie.
|
|
20162
20311
|
repoRoot: run.repoRoot ?? "",
|
|
20312
|
+
worktreePath: run.worktreePath ?? "",
|
|
20163
20313
|
codeAgent: run.codeAgent ?? "turbollm",
|
|
20164
20314
|
error: run.error,
|
|
20165
20315
|
archivedAt: run.archivedAt,
|
|
@@ -20179,6 +20329,18 @@ function registerCodeRoutes(app2, d) {
|
|
|
20179
20329
|
if (!repoRoot) return err6(c, 400, "invalid_input", "repoRoot is required.");
|
|
20180
20330
|
if (!task) return err6(c, 400, "invalid_input", "A task description is required.");
|
|
20181
20331
|
if (!VALID_MODES.has(mode)) return err6(c, 400, "invalid_input", "mode must be one of: auto, plan, ask.");
|
|
20332
|
+
let worktreePath;
|
|
20333
|
+
let worktreeBranch = b.worktreeBranch;
|
|
20334
|
+
if (b.useWorktree) {
|
|
20335
|
+
const created = await createSessionWorktree({
|
|
20336
|
+
repoRoot,
|
|
20337
|
+
branch: b.worktreeBranch || task,
|
|
20338
|
+
base: b.worktreeBase
|
|
20339
|
+
});
|
|
20340
|
+
if (!created.ok) return err6(c, 400, created.code, created.message);
|
|
20341
|
+
worktreePath = created.path;
|
|
20342
|
+
worktreeBranch = created.branch;
|
|
20343
|
+
}
|
|
20182
20344
|
const conv = db2.createConversation({ kind: "code", modelKey: b.modelKey });
|
|
20183
20345
|
db2.setConversationMode(conv.id, mode);
|
|
20184
20346
|
const codeAgent = d.store.snapshot().code.defaultAgent;
|
|
@@ -20189,11 +20351,13 @@ function registerCodeRoutes(app2, d) {
|
|
|
20189
20351
|
repoRoot,
|
|
20190
20352
|
repoBranch: b.repoBranch,
|
|
20191
20353
|
useWorktree: b.useWorktree,
|
|
20192
|
-
|
|
20193
|
-
|
|
20194
|
-
// captured; NOT acted on in Phase 1
|
|
20354
|
+
worktreeBranch,
|
|
20355
|
+
// the branch git actually created, not the one requested
|
|
20195
20356
|
worktreeBase: b.worktreeBase,
|
|
20196
|
-
//
|
|
20357
|
+
// Absent unless a worktree was really created above. `repoRoot` deliberately stays the BASE
|
|
20358
|
+
// repository: it is what `git worktree remove` has to run from, and what the session should
|
|
20359
|
+
// still show as its project. `agentCwd()` is the single place that picks between them.
|
|
20360
|
+
worktreePath,
|
|
20197
20361
|
codeAgent
|
|
20198
20362
|
});
|
|
20199
20363
|
db2.addMessage(conv.id, "user", task, { textAttachments: b.contextFiles });
|
|
@@ -20245,7 +20409,9 @@ function registerCodeRoutes(app2, d) {
|
|
|
20245
20409
|
// with the SAME lookup persona.ts injects into the prompt, so "shown as loaded" == "actually
|
|
20246
20410
|
// fed to the model". Both false when the session has no repoRoot.
|
|
20247
20411
|
hasAgentsMd: run.repoRoot ? agentsMdPresence(
|
|
20248
|
-
|
|
20412
|
+
// The worktree when there is one — that is the tree the model's prompt is built from,
|
|
20413
|
+
// so "shown as loaded" must be resolved against the same directory persona.ts reads.
|
|
20414
|
+
agentCwd(run),
|
|
20249
20415
|
d.store.dir(),
|
|
20250
20416
|
d.store.snapshot().code.agentsMdProjectCandidates,
|
|
20251
20417
|
d.store.snapshot().code.agentsMdGlobalCandidates
|
|
@@ -20311,14 +20477,19 @@ function registerCodeRoutes(app2, d) {
|
|
|
20311
20477
|
db2.setAgentRunArchived(id, archived);
|
|
20312
20478
|
return c.json({ ok: true, archived });
|
|
20313
20479
|
});
|
|
20314
|
-
app2.delete("/api/v1/code/sessions/:id", (c) => {
|
|
20480
|
+
app2.delete("/api/v1/code/sessions/:id", async (c) => {
|
|
20315
20481
|
const id = c.req.param("id");
|
|
20316
20482
|
const run = db2.getAgentRun(id);
|
|
20317
20483
|
if (!run) return err6(c, 404, "not_found", "Session not found.");
|
|
20318
20484
|
if (runs.isActive(id)) return err6(c, 409, "run_active", "Stop the current run before deleting this session.");
|
|
20319
20485
|
disposeLspClientsForConv(run.convId);
|
|
20486
|
+
let worktreeNote;
|
|
20487
|
+
if (run.worktreePath && run.repoRoot) {
|
|
20488
|
+
const removed = await removeSessionWorktree(run.repoRoot, run.worktreePath);
|
|
20489
|
+
if (!removed.ok) worktreeNote = removed.message;
|
|
20490
|
+
}
|
|
20320
20491
|
db2.deleteCodeSession(id);
|
|
20321
|
-
return c.json({ ok: true });
|
|
20492
|
+
return c.json(worktreeNote ? { ok: true, worktreeNote } : { ok: true });
|
|
20322
20493
|
});
|
|
20323
20494
|
app2.post("/api/v1/code/sessions/:id/compact", async (c) => {
|
|
20324
20495
|
const id = c.req.param("id");
|
|
@@ -20335,7 +20506,7 @@ function registerCodeRoutes(app2, d) {
|
|
|
20335
20506
|
d,
|
|
20336
20507
|
convId: run.convId,
|
|
20337
20508
|
sessionId: id,
|
|
20338
|
-
repoRoot: run
|
|
20509
|
+
repoRoot: agentCwd(run),
|
|
20339
20510
|
customInstructions: b.instructions?.trim() || void 0
|
|
20340
20511
|
});
|
|
20341
20512
|
return c.json({ ok: true, ...result });
|
|
@@ -20398,7 +20569,7 @@ function registerCodeRoutes(app2, d) {
|
|
|
20398
20569
|
let failedFiles = [];
|
|
20399
20570
|
if (b.revertFiles) {
|
|
20400
20571
|
const rangeMessages = db2.getMessagesFromIncludingInactive(run.convId, messageId);
|
|
20401
|
-
const result = revertFileEdits(rangeMessages, run
|
|
20572
|
+
const result = revertFileEdits(rangeMessages, agentCwd(run));
|
|
20402
20573
|
revertedFiles = result.reverted;
|
|
20403
20574
|
failedFiles = result.failed;
|
|
20404
20575
|
}
|
|
@@ -20410,7 +20581,7 @@ function registerCodeRoutes(app2, d) {
|
|
|
20410
20581
|
const run = db2.getAgentRun(c.req.param("id"));
|
|
20411
20582
|
if (!run) return err6(c, 404, "not_found", "Session not found.");
|
|
20412
20583
|
if (!run.repoRoot) return err6(c, 400, "invalid_input", "Session has no repo root.");
|
|
20413
|
-
const status = await getGitStatus(run
|
|
20584
|
+
const status = await getGitStatus(agentCwd(run));
|
|
20414
20585
|
return c.json({ ok: true, status });
|
|
20415
20586
|
});
|
|
20416
20587
|
app2.post("/api/v1/code/sessions/:id/git/commit", async (c) => {
|
|
@@ -20421,7 +20592,7 @@ function registerCodeRoutes(app2, d) {
|
|
|
20421
20592
|
if (runs.isActive(id)) return err6(c, 409, "run_active", "Stop or wait for the current run before committing.");
|
|
20422
20593
|
const b = await body6(c);
|
|
20423
20594
|
try {
|
|
20424
|
-
const result = await commitGitChanges(run
|
|
20595
|
+
const result = await commitGitChanges(agentCwd(run), b.message ?? "", b.files);
|
|
20425
20596
|
return c.json({ ok: true, ...result });
|
|
20426
20597
|
} catch (e) {
|
|
20427
20598
|
const message = e instanceof Error ? e.message : "Commit failed.";
|
|
@@ -20438,21 +20609,21 @@ function registerCodeRoutes(app2, d) {
|
|
|
20438
20609
|
if (!run) return err6(c, 404, "not_found", "Session not found.");
|
|
20439
20610
|
if (!run.repoRoot) return err6(c, 400, "invalid_input", "Session has no repo root.");
|
|
20440
20611
|
if (runs.isActive(id)) return err6(c, 409, "run_active", "Stop or wait for the current run before pushing.");
|
|
20441
|
-
const result = await pushGitBranch(run
|
|
20612
|
+
const result = await pushGitBranch(agentCwd(run));
|
|
20442
20613
|
if (!result.ok) {
|
|
20443
20614
|
const status = result.reason === "diverged" ? 409 : 400;
|
|
20444
20615
|
return err6(c, status, result.reason, result.message);
|
|
20445
20616
|
}
|
|
20446
|
-
const compareUrl = await getGithubCompareUrl(run
|
|
20617
|
+
const compareUrl = await getGithubCompareUrl(agentCwd(run), result.branch);
|
|
20447
20618
|
return c.json({ ok: true, remote: result.remote, branch: result.branch, compareUrl });
|
|
20448
20619
|
});
|
|
20449
20620
|
app2.get("/api/v1/code/sessions/:id/git/compare-url", async (c) => {
|
|
20450
20621
|
const run = db2.getAgentRun(c.req.param("id"));
|
|
20451
20622
|
if (!run) return err6(c, 404, "not_found", "Session not found.");
|
|
20452
20623
|
if (!run.repoRoot) return err6(c, 400, "invalid_input", "Session has no repo root.");
|
|
20453
|
-
const status = await getGitStatus(run
|
|
20624
|
+
const status = await getGitStatus(agentCwd(run));
|
|
20454
20625
|
if (!status.isRepo || status.detached || !status.branch) return c.json({ ok: true, compareUrl: null });
|
|
20455
|
-
const compareUrl = await getGithubCompareUrl(run
|
|
20626
|
+
const compareUrl = await getGithubCompareUrl(agentCwd(run), status.branch);
|
|
20456
20627
|
return c.json({ ok: true, compareUrl });
|
|
20457
20628
|
});
|
|
20458
20629
|
app2.post("/api/v1/code/sessions/:id/exec", async (c) => {
|
|
@@ -20467,7 +20638,7 @@ function registerCodeRoutes(app2, d) {
|
|
|
20467
20638
|
const feedToModel = b.feedToModel !== false;
|
|
20468
20639
|
let result;
|
|
20469
20640
|
try {
|
|
20470
|
-
result = await runShellCommand(command, run
|
|
20641
|
+
result = await runShellCommand(command, agentCwd(run), id);
|
|
20471
20642
|
} catch (e) {
|
|
20472
20643
|
return err6(c, 500, "exec_failed", e instanceof Error ? e.message : "Command failed to run.");
|
|
20473
20644
|
}
|
|
@@ -20528,7 +20699,7 @@ function registerCodeRoutes(app2, d) {
|
|
|
20528
20699
|
userMsgId = lastUser.id;
|
|
20529
20700
|
task = contextFilesBlock(lastUser.textAttachments) + lastUser.content;
|
|
20530
20701
|
}
|
|
20531
|
-
const enqueueParams = { convId: run.convId, repoRoot: run
|
|
20702
|
+
const enqueueParams = { convId: run.convId, repoRoot: agentCwd(run), task, userMsgId, thinkingBudget: b.thinkingBudget };
|
|
20532
20703
|
if (kind === "steer") {
|
|
20533
20704
|
const { steered, queued: queued2 } = await runs.steer(id, enqueueParams);
|
|
20534
20705
|
return c.json({ ok: true, queued: queued2, steered, userMessageId: userMsgId }, 202);
|
|
@@ -21709,7 +21880,7 @@ function featureForPath(path) {
|
|
|
21709
21880
|
|
|
21710
21881
|
// src/server.ts
|
|
21711
21882
|
setGlobalDispatcher(new Agent({ keepAliveMaxTimeout: 6e4, connections: 10 }));
|
|
21712
|
-
var WEB_ROOT =
|
|
21883
|
+
var WEB_ROOT = join33(dirname11(fileURLToPath(import.meta.url)), "webdist");
|
|
21713
21884
|
function createApp(d) {
|
|
21714
21885
|
const app2 = new Hono();
|
|
21715
21886
|
app2.use(
|
|
@@ -21752,9 +21923,9 @@ function createApp(d) {
|
|
|
21752
21923
|
if (path.startsWith("api/") || path.startsWith("v1/")) {
|
|
21753
21924
|
return c.json({ error: { code: "not_found", message: "Unknown endpoint." } }, 404);
|
|
21754
21925
|
}
|
|
21755
|
-
let file = normalize2(
|
|
21926
|
+
let file = normalize2(join33(WEB_ROOT, path || "index.html"));
|
|
21756
21927
|
if (!file.startsWith(WEB_ROOT) || !existsSync24(file) || statSync6(file).isDirectory()) {
|
|
21757
|
-
file =
|
|
21928
|
+
file = join33(WEB_ROOT, "index.html");
|
|
21758
21929
|
}
|
|
21759
21930
|
if (!existsSync24(file)) return c.text("web ui not built \u2014 run `npm run build:web`", 500);
|
|
21760
21931
|
return new Response(readFileSync17(file), { status: 200, headers: { "Content-Type": contentType(file) } });
|
|
@@ -21782,22 +21953,22 @@ function contentType(file) {
|
|
|
21782
21953
|
}
|
|
21783
21954
|
|
|
21784
21955
|
// src/tunnel/manager.ts
|
|
21785
|
-
import { execFile as execFile13, spawn as
|
|
21956
|
+
import { execFile as execFile13, spawn as spawn8, spawnSync as spawnSync3 } from "child_process";
|
|
21786
21957
|
import { mkdirSync as mkdirSync18, readFileSync as readFileSync18, readdirSync as readdirSync11, rmSync as rmSync16, writeFileSync as writeFileSync16 } from "fs";
|
|
21787
|
-
import { join as
|
|
21958
|
+
import { join as join35 } from "path";
|
|
21788
21959
|
|
|
21789
21960
|
// src/tunnel/provision.ts
|
|
21790
21961
|
import { chmodSync as chmodSync3, existsSync as existsSync25, mkdirSync as mkdirSync17, rmSync as rmSync15 } from "fs";
|
|
21791
|
-
import { join as
|
|
21962
|
+
import { join as join34 } from "path";
|
|
21792
21963
|
var CLOUDFLARED_REPO = "cloudflare/cloudflared";
|
|
21793
21964
|
function cloudflaredDir(dataDir) {
|
|
21794
|
-
return
|
|
21965
|
+
return join34(dataDir, "cloudflared");
|
|
21795
21966
|
}
|
|
21796
21967
|
function cloudflaredBinName(platform = process.platform) {
|
|
21797
21968
|
return platform === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
21798
21969
|
}
|
|
21799
21970
|
function cloudflaredBinPath(dataDir, platform = process.platform) {
|
|
21800
|
-
return
|
|
21971
|
+
return join34(cloudflaredDir(dataDir), cloudflaredBinName(platform));
|
|
21801
21972
|
}
|
|
21802
21973
|
function cloudflaredAssetName(platform = process.platform, archStr = process.arch) {
|
|
21803
21974
|
if (platform === "win32") {
|
|
@@ -21829,7 +22000,7 @@ async function ensureCloudflared(dataDir, onProgress, signal) {
|
|
|
21829
22000
|
mkdirSync17(dir, { recursive: true });
|
|
21830
22001
|
onProgress?.({ phase: "downloading", pct: 0 });
|
|
21831
22002
|
if (asset.name.endsWith(".tgz")) {
|
|
21832
|
-
const tmp =
|
|
22003
|
+
const tmp = join34(dir, asset.name);
|
|
21833
22004
|
try {
|
|
21834
22005
|
await downloadFile(asset.browser_download_url, tmp, onProgress, signal);
|
|
21835
22006
|
onProgress?.({ phase: "extracting", pct: -1 });
|
|
@@ -21873,10 +22044,10 @@ function forceKill2(child) {
|
|
|
21873
22044
|
else child.kill("SIGKILL");
|
|
21874
22045
|
}
|
|
21875
22046
|
function tunnelPidDir(dataDir) {
|
|
21876
|
-
return
|
|
22047
|
+
return join35(dataDir, "run");
|
|
21877
22048
|
}
|
|
21878
22049
|
function tunnelPidFile(dataDir, pid) {
|
|
21879
|
-
return
|
|
22050
|
+
return join35(tunnelPidDir(dataDir), `tunnel-${pid}.pid`);
|
|
21880
22051
|
}
|
|
21881
22052
|
function writeTunnelPid(dataDir, pid) {
|
|
21882
22053
|
try {
|
|
@@ -21910,7 +22081,7 @@ function readTunnelPidFiles(dataDir) {
|
|
|
21910
22081
|
}
|
|
21911
22082
|
const out = [];
|
|
21912
22083
|
for (const name of names) {
|
|
21913
|
-
const file =
|
|
22084
|
+
const file = join35(dir, name);
|
|
21914
22085
|
try {
|
|
21915
22086
|
const { pid, owner } = JSON.parse(readFileSync18(file, "utf8"));
|
|
21916
22087
|
if (typeof pid === "number" && pid > 0) out.push({ pid, owner: typeof owner === "number" ? owner : 0, file });
|
|
@@ -21982,7 +22153,7 @@ var TunnelManager = class {
|
|
|
21982
22153
|
this._error = null;
|
|
21983
22154
|
const { binPath } = await ensureCloudflared(this.dataDir);
|
|
21984
22155
|
return new Promise((resolve5, reject) => {
|
|
21985
|
-
const child =
|
|
22156
|
+
const child = spawn8(binPath, ["tunnel", "--url", `http://127.0.0.1:${localPort}`], { windowsHide: true });
|
|
21986
22157
|
this.child = child;
|
|
21987
22158
|
writeTunnelPid(this.dataDir, child.pid ?? 0);
|
|
21988
22159
|
let settled = false;
|
|
@@ -22125,7 +22296,7 @@ var Emitter = class {
|
|
|
22125
22296
|
hideChildConsoleWindows();
|
|
22126
22297
|
var version = "0.1.1";
|
|
22127
22298
|
try {
|
|
22128
|
-
const pkgPath =
|
|
22299
|
+
const pkgPath = join36(dirname12(fileURLToPath2(import.meta.url)), "..", "package.json");
|
|
22129
22300
|
version = JSON.parse(readFileSync19(pkgPath, "utf8")).version ?? version;
|
|
22130
22301
|
} catch {
|
|
22131
22302
|
}
|
|
@@ -22277,7 +22448,7 @@ if (pruned > 0) console.log(`pruned ${pruned} dangling engine build(s)`);
|
|
|
22277
22448
|
var provision = new ProvisionState();
|
|
22278
22449
|
var build = new BuildState();
|
|
22279
22450
|
var updates = new UpdateChecker();
|
|
22280
|
-
var enginesDir =
|
|
22451
|
+
var enginesDir = join36(store.dir(), "engines");
|
|
22281
22452
|
void seedDefaultEngines(registry, enginesDir, provision).then(() => registry.ensureProbed());
|
|
22282
22453
|
var manager = new Manager(store);
|
|
22283
22454
|
var scanner = new Scanner(store);
|
|
@@ -22360,7 +22531,7 @@ function openBrowser(url) {
|
|
|
22360
22531
|
cmd = "xdg-open";
|
|
22361
22532
|
args = [url];
|
|
22362
22533
|
}
|
|
22363
|
-
const child =
|
|
22534
|
+
const child = spawn9(cmd, args, { detached: true, stdio: "ignore" });
|
|
22364
22535
|
child.unref();
|
|
22365
22536
|
child.on("error", () => {
|
|
22366
22537
|
console.log(` Could not open browser automatically. Visit the URL above manually.`);
|
|
@@ -22464,11 +22635,11 @@ var restarting = false;
|
|
|
22464
22635
|
function spawnReplacement() {
|
|
22465
22636
|
let out = "ignore";
|
|
22466
22637
|
try {
|
|
22467
|
-
out = openSync3(
|
|
22638
|
+
out = openSync3(join36(store.dir(), "restart.log"), "a");
|
|
22468
22639
|
} catch {
|
|
22469
22640
|
out = "ignore";
|
|
22470
22641
|
}
|
|
22471
|
-
const child =
|
|
22642
|
+
const child = spawn9(process.execPath, process.argv.slice(1), {
|
|
22472
22643
|
cwd: process.cwd(),
|
|
22473
22644
|
detached: true,
|
|
22474
22645
|
stdio: ["ignore", out, out]
|