@kw-yeh/vibeflow 4.11.3 → 4.11.5
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/README.md +1 -1
- package/dist/vibeflow.mjs +322 -128
- package/dist/web/404/index.html +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/_next/static/chunks/{4434-1852678f484c974b.js → 6350-9e5cd43f220aab51.js} +2 -2
- package/dist/web/_next/static/chunks/pages/{_app-70ce066e3ac8e039.js → _app-33cd991a71572ffc.js} +1 -1
- package/dist/web/_next/static/chunks/pages/home-f757bac66e1055ea.js +13 -0
- package/dist/web/_next/static/css/9a4845312c779a77.css +2 -0
- package/dist/web/_next/static/{sZPeIltIrJDddiLXt5zx1 → gz-Jcn6bTdAo_ye2V2k2D}/_buildManifest.js +1 -1
- package/dist/web/home/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/_next/static/chunks/pages/home-82b9b5d7e210320a.js +0 -13
- package/dist/web/_next/static/css/9b4fcf22ca8f6bc9.css +0 -2
- /package/dist/web/_next/static/{sZPeIltIrJDddiLXt5zx1 → gz-Jcn6bTdAo_ye2V2k2D}/_ssgManifest.js +0 -0
package/dist/vibeflow.mjs
CHANGED
|
@@ -653,7 +653,8 @@ function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
|
|
|
653
653
|
const customPrompt = resolveSystemPrompt(systemPrompt);
|
|
654
654
|
const sys = [builtInPrompt, libraryPrompt, customPrompt].filter(Boolean).join("\n\n");
|
|
655
655
|
const includeTaskPrompt = opts?.includeTaskPrompt !== false;
|
|
656
|
-
const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task)
|
|
656
|
+
const prompt = includeTaskPrompt && !opts?.resume ? [buildPrompt(task), opts?.preflightPlan ? `\u524D\u7F6E\u898F\u5283\uFF08\u4F9B\u53C3\u8003\uFF0C\u8ACB\u5148\u6838\u5C0D\u7A0B\u5F0F\u78BC\u518D\u57F7\u884C\uFF09\uFF1A
|
|
657
|
+
${opts.preflightPlan}` : ""].filter(Boolean).join("\n\n") : "";
|
|
657
658
|
const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
|
|
658
659
|
return boardEnvPrefix(task, opts) + assembleCommand(
|
|
659
660
|
agent,
|
|
@@ -1102,9 +1103,9 @@ var init_progress = __esm({
|
|
|
1102
1103
|
|
|
1103
1104
|
// packages/tui/src/task-form.ts
|
|
1104
1105
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1105
|
-
import
|
|
1106
|
+
import fs24 from "fs";
|
|
1106
1107
|
import os9 from "os";
|
|
1107
|
-
import
|
|
1108
|
+
import path28 from "path";
|
|
1108
1109
|
import React, { useEffect, useRef, useState } from "react";
|
|
1109
1110
|
import { Box, Text, render as render2, useApp, useInput } from "ink";
|
|
1110
1111
|
function newFormState(defaultAutoMode, agents) {
|
|
@@ -1230,17 +1231,17 @@ function editorCommand(env, platform) {
|
|
|
1230
1231
|
return env.VISUAL?.trim() || env.EDITOR?.trim() || (platform === "win32" ? "notepad" : "vi");
|
|
1231
1232
|
}
|
|
1232
1233
|
function editInEditor(text) {
|
|
1233
|
-
const dir =
|
|
1234
|
-
const file =
|
|
1234
|
+
const dir = fs24.mkdtempSync(path28.join(os9.tmpdir(), "vibeflow-"));
|
|
1235
|
+
const file = path28.join(dir, "description.md");
|
|
1235
1236
|
try {
|
|
1236
|
-
|
|
1237
|
+
fs24.writeFileSync(file, text);
|
|
1237
1238
|
const cmd = editorCommand(process.env, process.platform);
|
|
1238
1239
|
const r = spawnSync2(`${cmd} "${file}"`, { stdio: "inherit", shell: true });
|
|
1239
1240
|
if (r.error) return { error: `\u7121\u6CD5\u555F\u52D5\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\uFF1A${r.error.message}` };
|
|
1240
1241
|
if (r.status !== 0) return { error: `\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\u7D50\u675F\u78BC ${r.status}\uFF0C\u63CF\u8FF0\u672A\u8B8A\u66F4` };
|
|
1241
|
-
return { text:
|
|
1242
|
+
return { text: fs24.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
|
|
1242
1243
|
} finally {
|
|
1243
|
-
|
|
1244
|
+
fs24.rmSync(dir, { recursive: true, force: true });
|
|
1244
1245
|
}
|
|
1245
1246
|
}
|
|
1246
1247
|
function cycle(list, current, step) {
|
|
@@ -1311,10 +1312,10 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1311
1312
|
if (editing === "title") setS((prev) => ({ ...prev, title: draft }));
|
|
1312
1313
|
else if (editing === "branch") setS((prev) => ({ ...prev, branch: value2 }));
|
|
1313
1314
|
else if (editing === "project") {
|
|
1314
|
-
if (value2) setProject(
|
|
1315
|
+
if (value2) setProject(path28.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir())));
|
|
1315
1316
|
} else if (editing === "attachments" && value2) {
|
|
1316
|
-
const file =
|
|
1317
|
-
if (!
|
|
1317
|
+
const file = path28.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir()));
|
|
1318
|
+
if (!fs24.existsSync(file) || !fs24.statSync(file).isFile()) {
|
|
1318
1319
|
setMessage(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${file}`);
|
|
1319
1320
|
return;
|
|
1320
1321
|
}
|
|
@@ -1397,7 +1398,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1397
1398
|
case "branch":
|
|
1398
1399
|
return s.branch || "\uFF08\u7559\u7A7A\u5247\u81EA\u52D5\u547D\u540D\uFF0C\u4F8B\u5982 feature/login-fix\uFF09";
|
|
1399
1400
|
case "attachments":
|
|
1400
|
-
return s.attachments.length ? s.attachments.map((a) =>
|
|
1401
|
+
return s.attachments.length ? s.attachments.map((a) => path28.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
|
|
1401
1402
|
case "description": {
|
|
1402
1403
|
if (!s.description) return "\uFF08Enter \u7528\u7DE8\u8F2F\u5668\u64B0\u5BEB\uFF09";
|
|
1403
1404
|
const lines = s.description.split("\n");
|
|
@@ -1433,7 +1434,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1433
1434
|
if (info && !info.isRepo) return "\u9019\u500B\u8CC7\u6599\u593E\u4E0D\u662F Git repository\u3002\u8ACB\u6539\u9078\u4E00\u500B Git \u5C08\u6848\uFF0C\u6216\u5728\u4E0B\u65B9\u521D\u59CB\u5316\u3002";
|
|
1434
1435
|
if (s.mode === "edit" && projectChanged(s)) return "\u5206\u652F\u6703\u5728\u65B0\u5C08\u6848\u3001\u5361\u7247\u958B\u59CB\u57F7\u884C\u6642\u624D\u5EFA\u7ACB\u3002";
|
|
1435
1436
|
if (info?.isRepo && !info.hasRemote) return `\u6B64 repository \u6C92\u6709 remote\uFF0C\u5C07\u4EE5\u76EE\u524D\u5206\u652F (${info.currentBranch ?? "HEAD"}) \u70BA\u57FA\u6E96\u5EFA\u7ACB\u672C\u5730 worktree\u3002`;
|
|
1436
|
-
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${
|
|
1437
|
+
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path28.basename(s.projectPath)}`;
|
|
1437
1438
|
}
|
|
1438
1439
|
if (field === "branch") {
|
|
1439
1440
|
const when = info?.hasRemote ? "\u958B\u59CB\u57F7\u884C\u6642\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u4E26\u63A8\u5230 origin\uFF1B\u82E5 remote \u5DF2\u6709\u9019\u500B\u5206\u652F\uFF0C\u5247\u76F4\u63A5\u53D6\u56DE\u4E26\u63A5\u7E8C\u4E0A\u9762\u7684\u5DE5\u4F5C\u3002" : "\u958B\u59CB\u57F7\u884C\u6642\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u3002";
|
|
@@ -1561,7 +1562,7 @@ __export(src_exports, {
|
|
|
1561
1562
|
showDiff: () => showDiff
|
|
1562
1563
|
});
|
|
1563
1564
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
1564
|
-
import
|
|
1565
|
+
import path29 from "path";
|
|
1565
1566
|
import React2, { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1566
1567
|
import { Box as Box2, Text as Text2, render as render3, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
|
|
1567
1568
|
function findDetachKey(text) {
|
|
@@ -1857,7 +1858,7 @@ function showDiff(task) {
|
|
|
1857
1858
|
});
|
|
1858
1859
|
}
|
|
1859
1860
|
function tmuxAttach(opts, sessionName) {
|
|
1860
|
-
const conf =
|
|
1861
|
+
const conf = path29.join(opts.userDataDir, "tmux.conf");
|
|
1861
1862
|
const inOurServer = (process.env.TMUX ?? "").split(",")[0].endsWith(`/${TMUX_SOCKET}`);
|
|
1862
1863
|
const args = inOurServer ? ["-L", TMUX_SOCKET, "-f", conf, "switch-client", "-t", `=${sessionName}`] : ["-L", TMUX_SOCKET, "-f", conf, "attach-session", "-t", `=${sessionName}`];
|
|
1863
1864
|
spawnSync3("tmux", args, { stdio: "inherit", env: { ...process.env, TMUX: inOurServer ? process.env.TMUX : "" } });
|
|
@@ -1988,8 +1989,8 @@ var init_src = __esm({
|
|
|
1988
1989
|
});
|
|
1989
1990
|
|
|
1990
1991
|
// packages/cli/src/main.ts
|
|
1991
|
-
import
|
|
1992
|
-
import
|
|
1992
|
+
import fs25 from "fs";
|
|
1993
|
+
import path30 from "path";
|
|
1993
1994
|
import { fileURLToPath } from "url";
|
|
1994
1995
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
1995
1996
|
|
|
@@ -2463,6 +2464,9 @@ function createBridge(t) {
|
|
|
2463
2464
|
},
|
|
2464
2465
|
setBoard: (board) => t.invoke("vibeflow:setBoard", board),
|
|
2465
2466
|
setSettings: (patch) => t.invoke("vibeflow:setSettings", patch),
|
|
2467
|
+
getJevKeyStatus: () => t.invoke("settings:jevKeyStatus"),
|
|
2468
|
+
saveJevApiKey: (apiKey) => t.invoke("settings:saveJevApiKey", { apiKey }),
|
|
2469
|
+
removeJevApiKey: () => t.invoke("settings:removeJevApiKey"),
|
|
2466
2470
|
getGithubAuthStatus: () => t.invoke("settings:githubAuthStatus"),
|
|
2467
2471
|
startGithubAuthLogin: () => t.invoke("settings:startGithubAuthLogin"),
|
|
2468
2472
|
cancelGithubAuthLogin: () => t.invoke("settings:cancelGithubAuthLogin"),
|
|
@@ -4638,8 +4642,8 @@ async function taskCommand(argv) {
|
|
|
4638
4642
|
}
|
|
4639
4643
|
|
|
4640
4644
|
// packages/cli/src/host.ts
|
|
4641
|
-
import
|
|
4642
|
-
import
|
|
4645
|
+
import fs22 from "fs";
|
|
4646
|
+
import path26 from "path";
|
|
4643
4647
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
4644
4648
|
|
|
4645
4649
|
// packages/core/src/events.ts
|
|
@@ -5177,10 +5181,10 @@ async function syncBuiltinsAtStartup() {
|
|
|
5177
5181
|
}
|
|
5178
5182
|
|
|
5179
5183
|
// packages/core/src/service.ts
|
|
5180
|
-
import
|
|
5184
|
+
import fs19 from "fs";
|
|
5181
5185
|
import os8 from "os";
|
|
5182
|
-
import
|
|
5183
|
-
import { randomUUID as
|
|
5186
|
+
import path23 from "path";
|
|
5187
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
5184
5188
|
|
|
5185
5189
|
// packages/core/src/agent-models.ts
|
|
5186
5190
|
import fs13 from "fs";
|
|
@@ -5533,6 +5537,170 @@ function createModelCatalog(opts) {
|
|
|
5533
5537
|
};
|
|
5534
5538
|
}
|
|
5535
5539
|
|
|
5540
|
+
// packages/core/src/jev-router.ts
|
|
5541
|
+
init_launch();
|
|
5542
|
+
var API_URL = "https://api.typesafe.ai/v1/systemone";
|
|
5543
|
+
var MAX_TASK_CHARS = 12e3;
|
|
5544
|
+
var MAX_PLAN_CHARS = 8e3;
|
|
5545
|
+
function tier(model, agent) {
|
|
5546
|
+
const name = `${model.id} ${model.label}`.toLowerCase();
|
|
5547
|
+
if (/haiku|mini|nano|luna|flash/.test(name)) return 0;
|
|
5548
|
+
if (/opus|astra|pro\b/.test(name)) return 2;
|
|
5549
|
+
if (agent === "codex" && /gpt-5\.5|gpt-5\.4(?!-mini)/.test(name)) return 2;
|
|
5550
|
+
return 1;
|
|
5551
|
+
}
|
|
5552
|
+
function routeCandidates(agent, models) {
|
|
5553
|
+
const valid = models.filter((m) => /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(m.id));
|
|
5554
|
+
const byTier = /* @__PURE__ */ new Map();
|
|
5555
|
+
for (const model of valid) {
|
|
5556
|
+
const rank = tier(model, agent);
|
|
5557
|
+
if (!byTier.has(rank)) byTier.set(rank, model);
|
|
5558
|
+
}
|
|
5559
|
+
return [...byTier.entries()].sort(([a], [b]) => a - b).map(([, model]) => model);
|
|
5560
|
+
}
|
|
5561
|
+
function isChoice(value) {
|
|
5562
|
+
return !!value && typeof value === "object" && value.type === "choice" && typeof value.choice === "string";
|
|
5563
|
+
}
|
|
5564
|
+
function createJevRouter(opts = {}) {
|
|
5565
|
+
const getApiKey = opts.getApiKey ?? (() => opts.apiKey ?? process.env.TYPESAFE_API_KEY?.trim());
|
|
5566
|
+
const fetcher = opts.fetcher ?? fetch;
|
|
5567
|
+
const planner = opts.planner ?? (async (agent, model, prompt, cwd) => {
|
|
5568
|
+
const args = agent === "claude" ? ["-p", "--model", model, "--permission-mode", "plan"] : ["exec", "--model", model, "--sandbox", "read-only", "--skip-git-repo-check", "-"];
|
|
5569
|
+
return (opts.printAgent ?? runAgentPrint)(agent, args, prompt, { cwd, timeout: 12e4, maxBuffer: 128 * 1024 });
|
|
5570
|
+
});
|
|
5571
|
+
async function ask(state, questions) {
|
|
5572
|
+
const apiKey = getApiKey();
|
|
5573
|
+
if (!apiKey) throw new Error("\u672A\u8A2D\u5B9A Jev API key\uFF08\u53EF\u5728\u8A2D\u5B9A\u4E2D\u8F38\u5165\u6216\u63D0\u4F9B TYPESAFE_API_KEY\uFF09");
|
|
5574
|
+
const response = await fetcher(API_URL, {
|
|
5575
|
+
method: "POST",
|
|
5576
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
5577
|
+
body: JSON.stringify({ model: "jev-latest", state, questions }),
|
|
5578
|
+
signal: AbortSignal.timeout(15e3)
|
|
5579
|
+
});
|
|
5580
|
+
if (!response.ok) throw new Error(`Jev API \u56DE\u61C9 ${response.status}`);
|
|
5581
|
+
const body = await response.json();
|
|
5582
|
+
if (!body.answers) throw new Error("Jev API \u6C92\u6709\u56DE\u50B3\u5224\u65B7");
|
|
5583
|
+
return body.answers;
|
|
5584
|
+
}
|
|
5585
|
+
return {
|
|
5586
|
+
get enabled() {
|
|
5587
|
+
return Boolean(getApiKey());
|
|
5588
|
+
},
|
|
5589
|
+
async route(task, catalog) {
|
|
5590
|
+
const agent = task.agentCli === "codex" ? "codex" : "claude";
|
|
5591
|
+
const candidates = routeCandidates(agent, catalog);
|
|
5592
|
+
if (candidates.length === 0) return { status: "fallback", reason: "\u6A21\u578B\u6E05\u55AE\u6C92\u6709\u53EF\u7528\u7684\u6A21\u578B" };
|
|
5593
|
+
const expensive = candidates[candidates.length - 1];
|
|
5594
|
+
const taskText = buildPrompt(task).slice(0, MAX_TASK_CHARS);
|
|
5595
|
+
try {
|
|
5596
|
+
const first = await ask({ task: taskText, agent }, {
|
|
5597
|
+
complexity: {
|
|
5598
|
+
type: "choice",
|
|
5599
|
+
instructions: "How difficult is this software task to implement correctly?",
|
|
5600
|
+
criteria: {
|
|
5601
|
+
simple: "Small, well scoped and routine",
|
|
5602
|
+
moderate: "Some design choices or multiple files",
|
|
5603
|
+
complex: "Architecture, ambiguous requirements, high risk, or many interacting parts"
|
|
5604
|
+
}
|
|
5605
|
+
},
|
|
5606
|
+
preparation: {
|
|
5607
|
+
type: "choice",
|
|
5608
|
+
instructions: "Would a separate read-only planning pass materially improve the implementation?",
|
|
5609
|
+
criteria: {
|
|
5610
|
+
direct: "The request is clear enough to start implementation directly",
|
|
5611
|
+
plan: "The request is unclear, broad or complex enough to benefit from a plan first"
|
|
5612
|
+
}
|
|
5613
|
+
}
|
|
5614
|
+
});
|
|
5615
|
+
if (!isChoice(first.complexity) || !isChoice(first.preparation) || !["simple", "moderate", "complex"].includes(first.complexity.choice) || !["direct", "plan"].includes(first.preparation.choice)) throw new Error("Jev \u56DE\u50B3\u4E0D\u53D7\u652F\u63F4\u7684\u5224\u65B7");
|
|
5616
|
+
let plan;
|
|
5617
|
+
if (first.preparation.choice === "plan") {
|
|
5618
|
+
if (!task.worktreePath) throw new Error("\u898F\u5283\u6642\u627E\u4E0D\u5230 worktree");
|
|
5619
|
+
const instruction = `Read the repository and make a concise implementation plan for the following task. Do not edit files. Identify unclear requirements, affected areas, and verification. Return only the plan.
|
|
5620
|
+
|
|
5621
|
+
${taskText}`;
|
|
5622
|
+
plan = (await planner(agent, expensive.id, instruction, task.worktreePath)).trim().slice(0, MAX_PLAN_CHARS);
|
|
5623
|
+
if (!plan) throw new Error("\u898F\u5283\u6A21\u578B\u6C92\u6709\u56DE\u50B3\u8A08\u756B");
|
|
5624
|
+
}
|
|
5625
|
+
const options = Object.fromEntries(candidates.map((m) => [
|
|
5626
|
+
m.id,
|
|
5627
|
+
`${m.label}; relative cost tier ${tier(m, agent) + 1}/3 (${tier(m, agent) === 2 ? "highest capability" : tier(m, agent) === 0 ? "lowest cost" : "balanced"})`
|
|
5628
|
+
]));
|
|
5629
|
+
const second = await ask({ task: taskText, complexity: first.complexity.choice, plan }, {
|
|
5630
|
+
model: {
|
|
5631
|
+
type: "choice",
|
|
5632
|
+
instructions: "Choose the lowest-cost available model likely to implement this task correctly. Prefer more capable models as complexity or uncertainty rises.",
|
|
5633
|
+
criteria: options
|
|
5634
|
+
}
|
|
5635
|
+
});
|
|
5636
|
+
if (!isChoice(second.model) || !candidates.some((m) => m.id === second.model.choice)) {
|
|
5637
|
+
throw new Error("Jev \u9078\u4E86\u4E0D\u5728\u6A21\u578B\u6E05\u55AE\u4E2D\u7684\u6A21\u578B");
|
|
5638
|
+
}
|
|
5639
|
+
return {
|
|
5640
|
+
status: "routed",
|
|
5641
|
+
model: second.model.choice,
|
|
5642
|
+
...plan ? { plannerModel: expensive.id, plan } : {}
|
|
5643
|
+
};
|
|
5644
|
+
} catch (err) {
|
|
5645
|
+
return { status: "fallback", reason: err instanceof Error ? err.message : String(err) };
|
|
5646
|
+
}
|
|
5647
|
+
}
|
|
5648
|
+
};
|
|
5649
|
+
}
|
|
5650
|
+
|
|
5651
|
+
// packages/core/src/jev-credentials.ts
|
|
5652
|
+
import fs14 from "fs";
|
|
5653
|
+
import path18 from "path";
|
|
5654
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5655
|
+
function keyPath(dir) {
|
|
5656
|
+
return path18.join(dir, "jev-api-key");
|
|
5657
|
+
}
|
|
5658
|
+
function dataDir() {
|
|
5659
|
+
return getPlatform().userDataDir();
|
|
5660
|
+
}
|
|
5661
|
+
function readJevApiKey(dir = dataDir()) {
|
|
5662
|
+
try {
|
|
5663
|
+
const file = keyPath(dir);
|
|
5664
|
+
if (fs14.lstatSync(file).isSymbolicLink()) throw new Error("Jev key file must not be a symlink");
|
|
5665
|
+
const saved = fs14.readFileSync(file, "utf8").trim();
|
|
5666
|
+
if (saved) return saved;
|
|
5667
|
+
} catch (err) {
|
|
5668
|
+
if (err.code !== "ENOENT") throw err;
|
|
5669
|
+
}
|
|
5670
|
+
return process.env.TYPESAFE_API_KEY?.trim() || void 0;
|
|
5671
|
+
}
|
|
5672
|
+
function jevKeyStatus(dir = dataDir()) {
|
|
5673
|
+
try {
|
|
5674
|
+
const file = keyPath(dir);
|
|
5675
|
+
if (fs14.lstatSync(file).isSymbolicLink()) throw new Error("Jev key file must not be a symlink");
|
|
5676
|
+
if (fs14.readFileSync(file, "utf8").trim()) return { configured: true, source: "saved" };
|
|
5677
|
+
} catch (err) {
|
|
5678
|
+
if (err.code !== "ENOENT") throw err;
|
|
5679
|
+
}
|
|
5680
|
+
return process.env.TYPESAFE_API_KEY?.trim() ? { configured: true, source: "environment" } : { configured: false, source: "none" };
|
|
5681
|
+
}
|
|
5682
|
+
function saveJevApiKey(value, dir = dataDir()) {
|
|
5683
|
+
const key = value.trim();
|
|
5684
|
+
if (!key || key.length > 4096 || !/^[\x21-\x7e]+$/.test(key)) {
|
|
5685
|
+
throw new Error("Jev API key \u683C\u5F0F\u7121\u6548");
|
|
5686
|
+
}
|
|
5687
|
+
fs14.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
5688
|
+
const file = keyPath(dir);
|
|
5689
|
+
const tmp = `${file}.${randomUUID3()}.tmp`;
|
|
5690
|
+
try {
|
|
5691
|
+
fs14.writeFileSync(tmp, key, { mode: 384, flag: "wx" });
|
|
5692
|
+
fs14.renameSync(tmp, file);
|
|
5693
|
+
if (process.platform !== "win32") fs14.chmodSync(file, 384);
|
|
5694
|
+
} finally {
|
|
5695
|
+
fs14.rmSync(tmp, { force: true });
|
|
5696
|
+
}
|
|
5697
|
+
return { configured: true, source: "saved" };
|
|
5698
|
+
}
|
|
5699
|
+
function removeJevApiKey(dir = dataDir()) {
|
|
5700
|
+
fs14.rmSync(keyPath(dir), { force: true });
|
|
5701
|
+
return jevKeyStatus(dir);
|
|
5702
|
+
}
|
|
5703
|
+
|
|
5536
5704
|
// packages/core/src/service.ts
|
|
5537
5705
|
init_effort();
|
|
5538
5706
|
|
|
@@ -6035,45 +6203,45 @@ function createJiraService(options) {
|
|
|
6035
6203
|
}
|
|
6036
6204
|
|
|
6037
6205
|
// packages/core/src/board-cli.ts
|
|
6038
|
-
import
|
|
6039
|
-
import
|
|
6206
|
+
import fs15 from "fs";
|
|
6207
|
+
import path19 from "path";
|
|
6040
6208
|
import { pathToFileURL } from "url";
|
|
6041
6209
|
async function boardCliLaunchInfo() {
|
|
6042
|
-
const storeDir =
|
|
6210
|
+
const storeDir = path19.dirname(getStorePath());
|
|
6043
6211
|
const entry = getPlatform().cliEntry?.();
|
|
6044
|
-
if (entry &&
|
|
6212
|
+
if (entry && fs15.existsSync(entry)) return { storeDir, cliPath: entry };
|
|
6045
6213
|
const root = getPlatform().sourceRoot();
|
|
6046
6214
|
if (!root) return { storeDir };
|
|
6047
|
-
const cliPath =
|
|
6048
|
-
const loaderPath =
|
|
6049
|
-
if (!
|
|
6215
|
+
const cliPath = path19.join(root, "scripts", "vibeflow.mjs");
|
|
6216
|
+
const loaderPath = path19.join(root, "test", "support", "register.mjs");
|
|
6217
|
+
if (!fs15.existsSync(cliPath) || !fs15.existsSync(loaderPath)) return { storeDir };
|
|
6050
6218
|
return { storeDir, cliPath, loaderPath: pathToFileURL(loaderPath).href };
|
|
6051
6219
|
}
|
|
6052
6220
|
|
|
6053
6221
|
// packages/core/src/decisions.ts
|
|
6054
|
-
import
|
|
6055
|
-
import
|
|
6222
|
+
import fs16 from "fs";
|
|
6223
|
+
import path20 from "path";
|
|
6056
6224
|
var DECISIONS_FILE_SUFFIX = ".DECISIONS.md";
|
|
6057
6225
|
function decisionsKey(worktreePath, branch) {
|
|
6058
|
-
return worktreePath ?
|
|
6226
|
+
return worktreePath ? path20.basename(worktreePath) : branch.replace(/\//g, "-");
|
|
6059
6227
|
}
|
|
6060
6228
|
function decisionsPath(workspacePath, key) {
|
|
6061
|
-
return
|
|
6229
|
+
return path20.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
|
|
6062
6230
|
}
|
|
6063
6231
|
function deleteDecisions(workspacePath, key) {
|
|
6064
6232
|
try {
|
|
6065
|
-
|
|
6233
|
+
fs16.rmSync(decisionsPath(workspacePath, key), { force: true });
|
|
6066
6234
|
} catch {
|
|
6067
6235
|
}
|
|
6068
6236
|
}
|
|
6069
6237
|
|
|
6070
6238
|
// packages/core/src/specs.ts
|
|
6071
|
-
import
|
|
6072
|
-
import
|
|
6239
|
+
import fs17 from "fs";
|
|
6240
|
+
import path21 from "path";
|
|
6073
6241
|
var SPEC_FILE_NAME = "spec.md";
|
|
6074
6242
|
var MAX_SPEC_BYTES = 256 * 1024;
|
|
6075
6243
|
function isSpecPath(p) {
|
|
6076
|
-
return
|
|
6244
|
+
return path21.posix.basename(p) === SPEC_FILE_NAME;
|
|
6077
6245
|
}
|
|
6078
6246
|
function clip2(buf) {
|
|
6079
6247
|
return {
|
|
@@ -6088,10 +6256,10 @@ async function readWorktreeSpecs(worktreePath, baseBranch) {
|
|
|
6088
6256
|
for (const entry of entries) {
|
|
6089
6257
|
if (entry.status === "D" || !isSpecPath(entry.path)) continue;
|
|
6090
6258
|
try {
|
|
6091
|
-
const full =
|
|
6092
|
-
const stat =
|
|
6259
|
+
const full = path21.join(worktreePath, entry.path);
|
|
6260
|
+
const stat = fs17.statSync(full);
|
|
6093
6261
|
if (!stat.isFile()) continue;
|
|
6094
|
-
specs.push({ path: entry.path, ...clip2(
|
|
6262
|
+
specs.push({ path: entry.path, ...clip2(fs17.readFileSync(full)), updatedAt: stat.mtimeMs });
|
|
6095
6263
|
} catch {
|
|
6096
6264
|
}
|
|
6097
6265
|
}
|
|
@@ -6157,7 +6325,7 @@ async function readSpecsAt(cwd, ref, paths) {
|
|
|
6157
6325
|
init_env();
|
|
6158
6326
|
init_git_bash();
|
|
6159
6327
|
import { spawn as spawn7 } from "child_process";
|
|
6160
|
-
import { randomUUID as
|
|
6328
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
6161
6329
|
|
|
6162
6330
|
// packages/core/src/chat-store.ts
|
|
6163
6331
|
var _chatStore = null;
|
|
@@ -6287,7 +6455,7 @@ function sendChatMessage(opts, sender) {
|
|
|
6287
6455
|
const fullText = attachmentLines.length ? `${text}
|
|
6288
6456
|
|
|
6289
6457
|
${attachmentLines.join("\n")}` : text;
|
|
6290
|
-
const userMsgId =
|
|
6458
|
+
const userMsgId = randomUUID4().slice(0, 8);
|
|
6291
6459
|
appendMessage(taskId, {
|
|
6292
6460
|
id: userMsgId,
|
|
6293
6461
|
role: "user",
|
|
@@ -6406,7 +6574,7 @@ ${attachmentLines.join("\n")}` : text;
|
|
|
6406
6574
|
proc.on("close", (code2) => {
|
|
6407
6575
|
const assistantText = accumulated || "\uFF08\u7121\u56DE\u61C9\uFF09";
|
|
6408
6576
|
appendMessage(taskId, {
|
|
6409
|
-
id:
|
|
6577
|
+
id: randomUUID4().slice(0, 8),
|
|
6410
6578
|
role: "assistant",
|
|
6411
6579
|
text: assistantText,
|
|
6412
6580
|
ts: Date.now()
|
|
@@ -6443,17 +6611,17 @@ init_launch();
|
|
|
6443
6611
|
// packages/core/src/progress-tracker.ts
|
|
6444
6612
|
init_progress();
|
|
6445
6613
|
init_launch();
|
|
6446
|
-
import
|
|
6614
|
+
import fs18 from "fs";
|
|
6447
6615
|
import os7 from "os";
|
|
6448
|
-
import
|
|
6616
|
+
import path22 from "path";
|
|
6449
6617
|
var SINCE_SLACK_MS = 5e3;
|
|
6450
6618
|
function claudeProjectDir(home, cwd) {
|
|
6451
|
-
return
|
|
6619
|
+
return path22.join(home, ".claude", "projects", cwd.replace(/[^a-zA-Z0-9]/g, "-"));
|
|
6452
6620
|
}
|
|
6453
6621
|
function pathAliases(p) {
|
|
6454
|
-
const resolved =
|
|
6622
|
+
const resolved = path22.resolve(p);
|
|
6455
6623
|
try {
|
|
6456
|
-
const real =
|
|
6624
|
+
const real = fs18.realpathSync(resolved);
|
|
6457
6625
|
return real === resolved ? [resolved] : [resolved, real];
|
|
6458
6626
|
} catch {
|
|
6459
6627
|
return [resolved];
|
|
@@ -6469,7 +6637,7 @@ function samePath(a, b) {
|
|
|
6469
6637
|
}
|
|
6470
6638
|
function listFiles(dir, test) {
|
|
6471
6639
|
try {
|
|
6472
|
-
return
|
|
6640
|
+
return fs18.readdirSync(dir).filter(test).map((name) => path22.join(dir, name));
|
|
6473
6641
|
} catch {
|
|
6474
6642
|
return [];
|
|
6475
6643
|
}
|
|
@@ -6477,22 +6645,22 @@ function listFiles(dir, test) {
|
|
|
6477
6645
|
function firstLine(file) {
|
|
6478
6646
|
let fd;
|
|
6479
6647
|
try {
|
|
6480
|
-
fd =
|
|
6648
|
+
fd = fs18.openSync(file, "r");
|
|
6481
6649
|
const buf = Buffer.alloc(64 * 1024);
|
|
6482
|
-
const n =
|
|
6650
|
+
const n = fs18.readSync(fd, buf, 0, buf.length, 0);
|
|
6483
6651
|
const text = buf.subarray(0, n).toString("utf8");
|
|
6484
6652
|
const nl = text.indexOf("\n");
|
|
6485
6653
|
return nl === -1 ? null : text.slice(0, nl);
|
|
6486
6654
|
} catch {
|
|
6487
6655
|
return null;
|
|
6488
6656
|
} finally {
|
|
6489
|
-
if (fd !== void 0)
|
|
6657
|
+
if (fd !== void 0) fs18.closeSync(fd);
|
|
6490
6658
|
}
|
|
6491
6659
|
}
|
|
6492
6660
|
function advance(file, cursor, reduce, initial) {
|
|
6493
6661
|
let size;
|
|
6494
6662
|
try {
|
|
6495
|
-
size =
|
|
6663
|
+
size = fs18.statSync(file).size;
|
|
6496
6664
|
} catch {
|
|
6497
6665
|
return;
|
|
6498
6666
|
}
|
|
@@ -6504,9 +6672,9 @@ function advance(file, cursor, reduce, initial) {
|
|
|
6504
6672
|
if (size === cursor.offset) return;
|
|
6505
6673
|
let fd;
|
|
6506
6674
|
try {
|
|
6507
|
-
fd =
|
|
6675
|
+
fd = fs18.openSync(file, "r");
|
|
6508
6676
|
const chunk = Buffer.alloc(size - cursor.offset);
|
|
6509
|
-
const n =
|
|
6677
|
+
const n = fs18.readSync(fd, chunk, 0, chunk.length, cursor.offset);
|
|
6510
6678
|
cursor.offset += n;
|
|
6511
6679
|
const data = Buffer.concat([cursor.rest, chunk.subarray(0, n)]);
|
|
6512
6680
|
const nl = data.lastIndexOf(10);
|
|
@@ -6520,7 +6688,7 @@ function advance(file, cursor, reduce, initial) {
|
|
|
6520
6688
|
}
|
|
6521
6689
|
} catch {
|
|
6522
6690
|
} finally {
|
|
6523
|
-
if (fd !== void 0)
|
|
6691
|
+
if (fd !== void 0) fs18.closeSync(fd);
|
|
6524
6692
|
}
|
|
6525
6693
|
}
|
|
6526
6694
|
function newCursor(state) {
|
|
@@ -6539,14 +6707,14 @@ function codexDayDirs(sessionsRoot, since) {
|
|
|
6539
6707
|
const cur = new Date(start.getFullYear(), start.getMonth(), start.getDate());
|
|
6540
6708
|
while (cur <= end) {
|
|
6541
6709
|
const pad = (n) => String(n).padStart(2, "0");
|
|
6542
|
-
days.push(
|
|
6710
|
+
days.push(path22.join(sessionsRoot, String(cur.getFullYear()), pad(cur.getMonth() + 1), pad(cur.getDate())));
|
|
6543
6711
|
cur.setDate(cur.getDate() + 1);
|
|
6544
6712
|
}
|
|
6545
6713
|
return days;
|
|
6546
6714
|
}
|
|
6547
6715
|
function createProgressTracker(opts) {
|
|
6548
6716
|
const home = opts.homeDir ?? os7.homedir();
|
|
6549
|
-
const codexHomes = opts.codexHomes ?? (() => [
|
|
6717
|
+
const codexHomes = opts.codexHomes ?? (() => [path22.join(home, ".codex")]);
|
|
6550
6718
|
const pollMs = opts.pollMs ?? 2e3;
|
|
6551
6719
|
const entries = /* @__PURE__ */ new Map();
|
|
6552
6720
|
const belongsToRun = (target, startedAt) => target.since == null || startedAt != null && startedAt >= target.since - SINCE_SLACK_MS;
|
|
@@ -6555,7 +6723,7 @@ function createProgressTracker(opts) {
|
|
|
6555
6723
|
const dirs = pathAliases(target.worktreePath).map((p) => claudeProjectDir(home, p));
|
|
6556
6724
|
for (const file of dirs.flatMap((dir) => listFiles(dir, (n) => n.endsWith(".jsonl")))) {
|
|
6557
6725
|
if (entry.claude.has(file) || entry.rejected.has(file)) continue;
|
|
6558
|
-
const session =
|
|
6726
|
+
const session = path22.basename(file, ".jsonl");
|
|
6559
6727
|
if (session !== target.sessionId) {
|
|
6560
6728
|
const head = firstLine(file);
|
|
6561
6729
|
if (head == null) continue;
|
|
@@ -6568,8 +6736,8 @@ function createProgressTracker(opts) {
|
|
|
6568
6736
|
entry.claude.set(file, newCursor(initialClaudeState()));
|
|
6569
6737
|
}
|
|
6570
6738
|
for (const file of Array.from(entry.claude.keys())) {
|
|
6571
|
-
if (file.includes(`${
|
|
6572
|
-
const sub =
|
|
6739
|
+
if (file.includes(`${path22.sep}subagents${path22.sep}`)) continue;
|
|
6740
|
+
const sub = path22.join(file.slice(0, -".jsonl".length), "subagents");
|
|
6573
6741
|
for (const f of listFiles(sub, (n) => n.endsWith(".jsonl"))) {
|
|
6574
6742
|
if (!entry.claude.has(f)) entry.claude.set(f, newCursor(initialClaudeState()));
|
|
6575
6743
|
}
|
|
@@ -6578,7 +6746,7 @@ function createProgressTracker(opts) {
|
|
|
6578
6746
|
function discoverCodex(entry) {
|
|
6579
6747
|
const { target } = entry;
|
|
6580
6748
|
for (const codexHome of codexHomes()) {
|
|
6581
|
-
for (const day of codexDayDirs(
|
|
6749
|
+
for (const day of codexDayDirs(path22.join(codexHome, "sessions"), target.since)) {
|
|
6582
6750
|
for (const file of listFiles(day, (n) => n.startsWith("rollout-") && n.endsWith(".jsonl"))) {
|
|
6583
6751
|
if (entry.codex.has(file) || entry.rejected.has(file)) continue;
|
|
6584
6752
|
const head = firstLine(file);
|
|
@@ -6595,11 +6763,11 @@ function createProgressTracker(opts) {
|
|
|
6595
6763
|
}
|
|
6596
6764
|
}
|
|
6597
6765
|
function drainHooks(entry) {
|
|
6598
|
-
const dir =
|
|
6766
|
+
const dir = path22.join(entry.target.worktreePath, PROGRESS_EVENTS_DIR);
|
|
6599
6767
|
for (const file of listFiles(dir, (n) => n.endsWith(".json")).sort()) {
|
|
6600
6768
|
try {
|
|
6601
|
-
const at =
|
|
6602
|
-
const event = JSON.parse(
|
|
6769
|
+
const at = fs18.statSync(file).mtimeMs;
|
|
6770
|
+
const event = JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
6603
6771
|
const foreign = entry.target.sessionId && typeof event.session_id === "string" && event.session_id !== entry.target.sessionId;
|
|
6604
6772
|
const activity = foreign ? null : activityFromHook(event, at);
|
|
6605
6773
|
if (activity) entry.hookActivity = latestActivity(entry.hookActivity ?? activity, activity);
|
|
@@ -6607,7 +6775,7 @@ function createProgressTracker(opts) {
|
|
|
6607
6775
|
continue;
|
|
6608
6776
|
}
|
|
6609
6777
|
try {
|
|
6610
|
-
|
|
6778
|
+
fs18.unlinkSync(file);
|
|
6611
6779
|
} catch {
|
|
6612
6780
|
}
|
|
6613
6781
|
}
|
|
@@ -6619,8 +6787,8 @@ function createProgressTracker(opts) {
|
|
|
6619
6787
|
if (target.agent === "claude") {
|
|
6620
6788
|
for (const [file, cursor] of entry.claude) {
|
|
6621
6789
|
runUsage = addUsage(runUsage, claudeUsageTotal(cursor.state));
|
|
6622
|
-
if (file.includes(`${
|
|
6623
|
-
const isExecutor =
|
|
6790
|
+
if (file.includes(`${path22.sep}subagents${path22.sep}`)) continue;
|
|
6791
|
+
const isExecutor = path22.basename(file, ".jsonl") === target.sessionId;
|
|
6624
6792
|
const s = cursor.state;
|
|
6625
6793
|
if (isExecutor || !target.sessionId && (!primary || (s.startedAt ?? 0) > (primary.startedAt ?? 0))) {
|
|
6626
6794
|
primary = { todos: s.todos, contextTokens: s.contextTokens, activity: s.activity, startedAt: s.startedAt };
|
|
@@ -6699,10 +6867,10 @@ function createProgressTracker(opts) {
|
|
|
6699
6867
|
};
|
|
6700
6868
|
entry.timer.unref?.();
|
|
6701
6869
|
entries.set(target.taskId, entry);
|
|
6702
|
-
const eventsDir =
|
|
6870
|
+
const eventsDir = path22.join(target.worktreePath, PROGRESS_EVENTS_DIR);
|
|
6703
6871
|
try {
|
|
6704
|
-
|
|
6705
|
-
entry.watcher =
|
|
6872
|
+
fs18.mkdirSync(eventsDir, { recursive: true });
|
|
6873
|
+
entry.watcher = fs18.watch(eventsDir, () => {
|
|
6706
6874
|
if (entry.pending) return;
|
|
6707
6875
|
entry.pending = setTimeout(() => {
|
|
6708
6876
|
entry.pending = null;
|
|
@@ -6787,9 +6955,11 @@ function validBoard(value) {
|
|
|
6787
6955
|
}
|
|
6788
6956
|
return next;
|
|
6789
6957
|
}
|
|
6790
|
-
function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatus, acliRunner, jiraAuthStatus, modelCatalog }) {
|
|
6958
|
+
function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatus, acliRunner, jiraAuthStatus, modelCatalog, jevRouter }) {
|
|
6791
6959
|
const sink = bus.sink();
|
|
6792
6960
|
const models = modelCatalog ?? createModelCatalog({ version });
|
|
6961
|
+
const router = jevRouter ?? createJevRouter({ getApiKey: readJevApiKey });
|
|
6962
|
+
const routing = /* @__PURE__ */ new Map();
|
|
6793
6963
|
const standaloneTerminals = /* @__PURE__ */ new Map();
|
|
6794
6964
|
let libraryCodexHome = null;
|
|
6795
6965
|
void libraryRoot().then((root) => {
|
|
@@ -6799,7 +6969,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6799
6969
|
const home = homeDir ?? os8.homedir();
|
|
6800
6970
|
const progress = createProgressTracker({
|
|
6801
6971
|
homeDir: home,
|
|
6802
|
-
codexHomes: () => [
|
|
6972
|
+
codexHomes: () => [path23.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
|
|
6803
6973
|
onUpdate: (taskId, value) => bus.emit("progress:update", { taskId, progress: value }),
|
|
6804
6974
|
onNotify: (taskId, list) => {
|
|
6805
6975
|
const prefs = resolveNotificationSettings(getSettings());
|
|
@@ -6867,36 +7037,53 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6867
7037
|
requireTask(key.split(":")[0]);
|
|
6868
7038
|
return key;
|
|
6869
7039
|
}
|
|
6870
|
-
function launchEffort(task) {
|
|
6871
|
-
const model = models.findModel(taskAgent(task),
|
|
7040
|
+
function launchEffort(task, selectedModel = taskModel(task)) {
|
|
7041
|
+
const model = models.findModel(taskAgent(task), selectedModel);
|
|
6872
7042
|
return model?.efforts ? clampEffort(task.effort, model.efforts) : task.effort;
|
|
6873
7043
|
}
|
|
6874
7044
|
async function launchCommand(task, intent) {
|
|
6875
7045
|
const settings = getSettings();
|
|
6876
7046
|
const library = await libraryLaunchInfo(task.worktreePath);
|
|
6877
7047
|
const boardCli = await boardCliLaunchInfo();
|
|
7048
|
+
let route = task.model?.trim() ? void 0 : task.jevRoute;
|
|
7049
|
+
if (!task.model?.trim() && !intent.resume && intent.includeTaskPrompt !== false) {
|
|
7050
|
+
let pending = routing.get(task.id);
|
|
7051
|
+
if (!pending) {
|
|
7052
|
+
pending = (async () => {
|
|
7053
|
+
const candidateModels = (await (router.enabled === false ? models.get(taskAgent(task)) : models.refresh(taskAgent(task)))).models;
|
|
7054
|
+
const selected = await router.route(task, candidateModels);
|
|
7055
|
+
updateTask(task.id, { jevRoute: selected });
|
|
7056
|
+
bus.emit("state:changed", getState());
|
|
7057
|
+
return selected;
|
|
7058
|
+
})().finally(() => routing.delete(task.id));
|
|
7059
|
+
routing.set(task.id, pending);
|
|
7060
|
+
}
|
|
7061
|
+
route = await pending;
|
|
7062
|
+
}
|
|
7063
|
+
const selectedModel = task.model?.trim() ? taskModel(task) : route?.model;
|
|
6878
7064
|
return buildAgentCommand(
|
|
6879
|
-
{ ...task, effort: launchEffort(task) },
|
|
7065
|
+
{ ...task, model: selectedModel, effort: launchEffort(task, selectedModel) },
|
|
6880
7066
|
settings.systemPrompt ?? "",
|
|
6881
7067
|
{
|
|
6882
7068
|
resume: intent.resume === true,
|
|
6883
7069
|
includeTaskPrompt: intent.includeTaskPrompt !== false,
|
|
6884
7070
|
library: library ?? void 0,
|
|
6885
7071
|
boardCli,
|
|
6886
|
-
autoMode: task.autoMode ?? settings.autoMode
|
|
7072
|
+
autoMode: task.autoMode ?? settings.autoMode,
|
|
7073
|
+
preflightPlan: !intent.resume ? route?.plan : void 0
|
|
6887
7074
|
},
|
|
6888
7075
|
task.workspacePath
|
|
6889
7076
|
);
|
|
6890
7077
|
}
|
|
6891
7078
|
function claudeSessionFile(cwd, sessionId) {
|
|
6892
7079
|
const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
6893
|
-
return
|
|
7080
|
+
return path23.join(os8.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
|
|
6894
7081
|
}
|
|
6895
7082
|
async function removeTaskWorktree(task) {
|
|
6896
7083
|
if (!task.projectPath || !task.worktreePath) return;
|
|
6897
7084
|
const branch = task.branch || fallbackBranchName(task.id);
|
|
6898
7085
|
await removeWorktree(task.projectPath, task.worktreePath);
|
|
6899
|
-
deleteArtifacts(task.workspacePath ??
|
|
7086
|
+
deleteArtifacts(task.workspacePath ?? path23.dirname(task.worktreePath), task.worktreePath);
|
|
6900
7087
|
await deleteBranch(task.projectPath, branch);
|
|
6901
7088
|
}
|
|
6902
7089
|
function columnOf(taskId) {
|
|
@@ -6977,6 +7164,12 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6977
7164
|
setSettings(value);
|
|
6978
7165
|
return getState();
|
|
6979
7166
|
},
|
|
7167
|
+
"settings:jevKeyStatus": () => jevKeyStatus(),
|
|
7168
|
+
"settings:saveJevApiKey": (payload) => {
|
|
7169
|
+
const p = obj(payload, "payload");
|
|
7170
|
+
return saveJevApiKey(str(p.apiKey, "apiKey"));
|
|
7171
|
+
},
|
|
7172
|
+
"settings:removeJevApiKey": () => removeJevApiKey(),
|
|
6980
7173
|
"settings:githubAuthStatus": () => getGitHubCliAuthStatus(),
|
|
6981
7174
|
"settings:startGithubAuthLogin": () => {
|
|
6982
7175
|
startGitHubCliLogin((payload) => bus.emit("github-auth:event", payload));
|
|
@@ -7032,8 +7225,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7032
7225
|
"projects:listRecent": () => listRecentProjects(getStore()),
|
|
7033
7226
|
// `vibeflow open <path>` while a host runs: only the host writes the store.
|
|
7034
7227
|
"projects:record": (projectPath) => {
|
|
7035
|
-
const p =
|
|
7036
|
-
if (!
|
|
7228
|
+
const p = path23.resolve(str(projectPath, "projectPath"));
|
|
7229
|
+
if (!fs19.existsSync(p) || !fs19.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
|
|
7037
7230
|
recordRecentProject(getStore(), p);
|
|
7038
7231
|
return listRecentProjects(getStore());
|
|
7039
7232
|
},
|
|
@@ -7078,7 +7271,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7078
7271
|
if (!task.worktreePath) {
|
|
7079
7272
|
await teardownTask(task.id);
|
|
7080
7273
|
progress.untrack(task.id);
|
|
7081
|
-
updateTask(task.id, { launchedAt: Date.now(), runId:
|
|
7274
|
+
updateTask(task.id, { launchedAt: Date.now(), runId: randomUUID5(), jevRoute: void 0 });
|
|
7082
7275
|
const reset2 = findTask(task.id);
|
|
7083
7276
|
if (!reset2) throw new Error("\u627E\u4E0D\u5230\u8981\u91CD\u7F6E\u7684\u4EFB\u52D9");
|
|
7084
7277
|
return { state: getState(), task: reset2 };
|
|
@@ -7086,13 +7279,13 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7086
7279
|
if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
|
|
7087
7280
|
await teardownTask(task.id);
|
|
7088
7281
|
await resetWorktreeToBase(task.worktreePath, task.baseBranch);
|
|
7089
|
-
const workspacePath = task.workspacePath ??
|
|
7282
|
+
const workspacePath = task.workspacePath ?? path23.dirname(task.worktreePath);
|
|
7090
7283
|
deleteArtifacts(workspacePath, task.worktreePath);
|
|
7091
7284
|
deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
|
|
7092
7285
|
resetSubAgents(task.worktreePath);
|
|
7093
7286
|
const usage = usageThroughCurrentRun(task);
|
|
7094
7287
|
progress.untrack(task.id);
|
|
7095
|
-
updateTask(task.id, { launchedAt: Date.now(), runId:
|
|
7288
|
+
updateTask(task.id, { launchedAt: Date.now(), runId: randomUUID5(), jevRoute: void 0, ...usage ? { usage } : {} });
|
|
7096
7289
|
const reset = findTask(task.id);
|
|
7097
7290
|
if (!reset) throw new Error("\u627E\u4E0D\u5230\u8981\u91CD\u7F6E\u7684\u4EFB\u52D9");
|
|
7098
7291
|
bus.emit("subagents:update", { taskId: task.id, subAgents: [] });
|
|
@@ -7130,7 +7323,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7130
7323
|
}
|
|
7131
7324
|
const info = await getGitInfo(nextProject);
|
|
7132
7325
|
if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
|
|
7133
|
-
const projectName =
|
|
7326
|
+
const projectName = path23.basename(nextProject);
|
|
7134
7327
|
const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
|
|
7135
7328
|
await teardownTask(existing.id);
|
|
7136
7329
|
await removeTaskWorktree(existing);
|
|
@@ -7154,6 +7347,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7154
7347
|
description: p.description?.trim() || void 0,
|
|
7155
7348
|
agentCli: p.agentCli,
|
|
7156
7349
|
model: p.model || void 0,
|
|
7350
|
+
jevRoute: void 0,
|
|
7157
7351
|
effort: p.effort,
|
|
7158
7352
|
autoMode: p.autoMode,
|
|
7159
7353
|
...gitPatch
|
|
@@ -7205,7 +7399,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7205
7399
|
if (!/^terminal_[0-9a-f-]{36}$/.test(key)) invalid("invalid standalone terminal key");
|
|
7206
7400
|
if (Boolean(p.taskId) === Boolean(p.projectPath)) invalid("provide taskId or projectPath");
|
|
7207
7401
|
const cwd = str(p.taskId ? requireTask(p.taskId).projectPath : p.projectPath, "projectPath");
|
|
7208
|
-
if (!
|
|
7402
|
+
if (!path23.isAbsolute(cwd) || !fs19.existsSync(cwd) || !fs19.statSync(cwd).isDirectory()) {
|
|
7209
7403
|
invalid("projectPath must be an existing absolute directory");
|
|
7210
7404
|
}
|
|
7211
7405
|
const previous = standaloneTerminals.get(key);
|
|
@@ -7244,7 +7438,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7244
7438
|
"claude:sessionExists": (taskId) => {
|
|
7245
7439
|
const task = requireTask(taskId);
|
|
7246
7440
|
if (!task.worktreePath) return false;
|
|
7247
|
-
return
|
|
7441
|
+
return fs19.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
|
|
7248
7442
|
},
|
|
7249
7443
|
"sessions:list": async () => {
|
|
7250
7444
|
const keys = await sessions.list();
|
|
@@ -7263,9 +7457,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7263
7457
|
"git:getDiffEntries": (taskId, opts) => {
|
|
7264
7458
|
const task = requireTask(taskId);
|
|
7265
7459
|
if (!task.worktreePath) return [];
|
|
7266
|
-
const
|
|
7460
|
+
const fetch2 = opts && typeof opts === "object" ? opts.fetch : void 0;
|
|
7267
7461
|
return getWorktreeDiffEntries(task.worktreePath, task.baseBranch ?? "HEAD", {
|
|
7268
|
-
fetch: typeof
|
|
7462
|
+
fetch: typeof fetch2 === "boolean" ? fetch2 : void 0
|
|
7269
7463
|
});
|
|
7270
7464
|
},
|
|
7271
7465
|
"git:getDiffFile": (taskId, filePath) => {
|
|
@@ -7291,7 +7485,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7291
7485
|
const task = requireTask(taskId);
|
|
7292
7486
|
if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
|
|
7293
7487
|
const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
|
|
7294
|
-
if (!
|
|
7488
|
+
if (!fs19.existsSync(dir)) return "artifacts directory does not exist yet";
|
|
7295
7489
|
return getPlatform().openPath(dir);
|
|
7296
7490
|
},
|
|
7297
7491
|
"task:getSpecs": async (taskId) => {
|
|
@@ -7425,7 +7619,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7425
7619
|
}
|
|
7426
7620
|
await removeTaskWorktree(task);
|
|
7427
7621
|
if (task.workspacePath) {
|
|
7428
|
-
|
|
7622
|
+
fs19.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
|
|
7429
7623
|
}
|
|
7430
7624
|
clearConversation(task.id);
|
|
7431
7625
|
removeTask(task.id);
|
|
@@ -7465,7 +7659,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7465
7659
|
);
|
|
7466
7660
|
},
|
|
7467
7661
|
"chat:compact": (taskId) => {
|
|
7468
|
-
const newSessionId =
|
|
7662
|
+
const newSessionId = randomUUID5();
|
|
7469
7663
|
clearMessages(requireTask(taskId).id, newSessionId);
|
|
7470
7664
|
return { newSessionId };
|
|
7471
7665
|
}
|
|
@@ -7478,9 +7672,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7478
7672
|
// Watch the containing directory, not the file: JsonStore renames a temp
|
|
7479
7673
|
// file over the target and so replaces the inode on every save.
|
|
7480
7674
|
watchStore() {
|
|
7481
|
-
const file =
|
|
7675
|
+
const file = path23.basename(getStorePath());
|
|
7482
7676
|
let debounce = null;
|
|
7483
|
-
storeWatcher =
|
|
7677
|
+
storeWatcher = fs19.watch(path23.dirname(getStorePath()), (_event, filename) => {
|
|
7484
7678
|
if (filename !== file) return;
|
|
7485
7679
|
if (debounce) clearTimeout(debounce);
|
|
7486
7680
|
debounce = setTimeout(() => {
|
|
@@ -7517,9 +7711,9 @@ function createSessionBackend(sink, choice) {
|
|
|
7517
7711
|
}
|
|
7518
7712
|
|
|
7519
7713
|
// packages/core/src/web-server.ts
|
|
7520
|
-
import
|
|
7714
|
+
import fs20 from "fs";
|
|
7521
7715
|
import http from "http";
|
|
7522
|
-
import
|
|
7716
|
+
import path24 from "path";
|
|
7523
7717
|
import { timingSafeEqual } from "crypto";
|
|
7524
7718
|
import { WebSocketServer } from "ws";
|
|
7525
7719
|
var TOKEN_COOKIE = "vf_token";
|
|
@@ -7572,14 +7766,14 @@ function resolveStatic(root, urlPath) {
|
|
|
7572
7766
|
return null;
|
|
7573
7767
|
}
|
|
7574
7768
|
if (decoded.includes("\0")) return null;
|
|
7575
|
-
const rel =
|
|
7576
|
-
const base2 =
|
|
7577
|
-
const candidates = [rel,
|
|
7769
|
+
const rel = path24.posix.normalize(decoded).replace(/^\/+/, "");
|
|
7770
|
+
const base2 = path24.resolve(root);
|
|
7771
|
+
const candidates = [rel, path24.posix.join(rel, "index.html"), `${rel}.html`];
|
|
7578
7772
|
for (const c of candidates) {
|
|
7579
|
-
const full =
|
|
7580
|
-
if (full !== base2 && !full.startsWith(base2 +
|
|
7773
|
+
const full = path24.resolve(base2, c);
|
|
7774
|
+
if (full !== base2 && !full.startsWith(base2 + path24.sep)) return null;
|
|
7581
7775
|
try {
|
|
7582
|
-
if (
|
|
7776
|
+
if (fs20.statSync(full).isFile()) return full;
|
|
7583
7777
|
} catch {
|
|
7584
7778
|
}
|
|
7585
7779
|
}
|
|
@@ -7640,7 +7834,7 @@ async function startWebServer(options) {
|
|
|
7640
7834
|
send(res, 404, "Not found");
|
|
7641
7835
|
return;
|
|
7642
7836
|
}
|
|
7643
|
-
const ext =
|
|
7837
|
+
const ext = path24.extname(file).toLowerCase();
|
|
7644
7838
|
const immutable = url.pathname.startsWith("/_next/static/");
|
|
7645
7839
|
res.writeHead(200, {
|
|
7646
7840
|
"Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
|
|
@@ -7653,7 +7847,7 @@ async function startWebServer(options) {
|
|
|
7653
7847
|
res.end();
|
|
7654
7848
|
return;
|
|
7655
7849
|
}
|
|
7656
|
-
|
|
7850
|
+
fs20.createReadStream(file).pipe(res);
|
|
7657
7851
|
}
|
|
7658
7852
|
const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
|
|
7659
7853
|
function reject(socket, status, reason) {
|
|
@@ -7752,8 +7946,8 @@ Content-Length: 0\r
|
|
|
7752
7946
|
}
|
|
7753
7947
|
|
|
7754
7948
|
// packages/cli/src/update-command.ts
|
|
7755
|
-
import
|
|
7756
|
-
import
|
|
7949
|
+
import fs21 from "node:fs";
|
|
7950
|
+
import path25 from "node:path";
|
|
7757
7951
|
import { spawnSync } from "node:child_process";
|
|
7758
7952
|
function npmRunner(args, inherit) {
|
|
7759
7953
|
const windows = process.platform === "win32";
|
|
@@ -7766,7 +7960,7 @@ function npmRunner(args, inherit) {
|
|
|
7766
7960
|
}
|
|
7767
7961
|
function packageName(packageRoot) {
|
|
7768
7962
|
try {
|
|
7769
|
-
const pkg = JSON.parse(
|
|
7963
|
+
const pkg = JSON.parse(fs21.readFileSync(path25.join(packageRoot, "package.json"), "utf8"));
|
|
7770
7964
|
if (typeof pkg.name !== "string") return null;
|
|
7771
7965
|
return /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i.test(pkg.name) ? pkg.name : null;
|
|
7772
7966
|
} catch {
|
|
@@ -7781,7 +7975,7 @@ function runUpdate(options) {
|
|
|
7781
7975
|
error("vibeflow update \u53EA\u9069\u7528\u65BC\u5168\u57DF npm \u5B89\u88DD\uFF1B\u76EE\u524D\u662F\u539F\u59CB\u78BC\u7248\u672C\u3002");
|
|
7782
7976
|
return 1;
|
|
7783
7977
|
}
|
|
7784
|
-
const packageRoot =
|
|
7978
|
+
const packageRoot = path25.dirname(path25.dirname(options.cliEntry));
|
|
7785
7979
|
const name = packageName(packageRoot);
|
|
7786
7980
|
if (!name) {
|
|
7787
7981
|
error("\u7121\u6CD5\u8FA8\u8B58\u76EE\u524D VibeFlow \u7684 npm \u5957\u4EF6\u540D\u7A31\uFF0C\u5DF2\u53D6\u6D88\u66F4\u65B0\u3002");
|
|
@@ -7793,8 +7987,8 @@ function runUpdate(options) {
|
|
|
7793
7987
|
return 1;
|
|
7794
7988
|
}
|
|
7795
7989
|
try {
|
|
7796
|
-
const globalPackage =
|
|
7797
|
-
if (
|
|
7990
|
+
const globalPackage = path25.join(String(root.stdout).trim(), ...name.split("/"));
|
|
7991
|
+
if (fs21.lstatSync(globalPackage).isSymbolicLink() || fs21.realpathSync(globalPackage) !== fs21.realpathSync(packageRoot)) {
|
|
7798
7992
|
error("vibeflow update \u53EA\u9069\u7528\u65BC\u5168\u57DF npm \u5B89\u88DD\uFF1B\u76EE\u524D\u57F7\u884C\u7684\u5957\u4EF6\u4F86\u81EA\u5176\u4ED6\u4F4D\u7F6E\u3002");
|
|
7799
7993
|
return 1;
|
|
7800
7994
|
}
|
|
@@ -7890,7 +8084,7 @@ async function startHost(options) {
|
|
|
7890
8084
|
let availableUpdate = null;
|
|
7891
8085
|
let updateTimer = null;
|
|
7892
8086
|
let stopped = false;
|
|
7893
|
-
const installedPackage = options.sourceRoot || !options.cliEntry ? null : packageName(
|
|
8087
|
+
const installedPackage = options.sourceRoot || !options.cliEntry ? null : packageName(path26.dirname(path26.dirname(options.cliEntry)));
|
|
7894
8088
|
const checkUpdates = async () => {
|
|
7895
8089
|
if (!installedPackage) return;
|
|
7896
8090
|
try {
|
|
@@ -7952,7 +8146,7 @@ function loginUrl(lock, pathname = "/home/") {
|
|
|
7952
8146
|
return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
|
|
7953
8147
|
}
|
|
7954
8148
|
function isWebDir(dir) {
|
|
7955
|
-
return
|
|
8149
|
+
return fs22.existsSync(path26.join(dir, "home", "index.html"));
|
|
7956
8150
|
}
|
|
7957
8151
|
|
|
7958
8152
|
// packages/cli/src/remote.ts
|
|
@@ -8069,8 +8263,8 @@ function connectToHost(lock) {
|
|
|
8069
8263
|
}
|
|
8070
8264
|
|
|
8071
8265
|
// packages/cli/src/doctor.ts
|
|
8072
|
-
import
|
|
8073
|
-
import
|
|
8266
|
+
import fs23 from "fs";
|
|
8267
|
+
import path27 from "path";
|
|
8074
8268
|
import { execFile as execFile10 } from "child_process";
|
|
8075
8269
|
import { promisify as promisify7 } from "util";
|
|
8076
8270
|
init_env();
|
|
@@ -8210,10 +8404,10 @@ async function runDoctor(options) {
|
|
|
8210
8404
|
});
|
|
8211
8405
|
}
|
|
8212
8406
|
try {
|
|
8213
|
-
|
|
8214
|
-
const probe =
|
|
8215
|
-
|
|
8216
|
-
|
|
8407
|
+
fs23.mkdirSync(options.userDataDir, { recursive: true });
|
|
8408
|
+
const probe = path27.join(options.userDataDir, `.doctor-${process.pid}`);
|
|
8409
|
+
fs23.writeFileSync(probe, "");
|
|
8410
|
+
fs23.rmSync(probe);
|
|
8217
8411
|
checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
|
|
8218
8412
|
} catch (err) {
|
|
8219
8413
|
checks.push({
|
|
@@ -8268,24 +8462,24 @@ Options:
|
|
|
8268
8462
|
`.trim();
|
|
8269
8463
|
function detectLayout() {
|
|
8270
8464
|
const file = fileURLToPath(import.meta.url);
|
|
8271
|
-
const here =
|
|
8272
|
-
const bundledWeb =
|
|
8273
|
-
if (isWebDir(bundledWeb) ||
|
|
8465
|
+
const here = path30.dirname(file);
|
|
8466
|
+
const bundledWeb = path30.join(here, "web");
|
|
8467
|
+
if (isWebDir(bundledWeb) || path30.basename(here) === "dist") {
|
|
8274
8468
|
return {
|
|
8275
|
-
version: readVersion(
|
|
8469
|
+
version: readVersion(path30.join(here, "..", "package.json")),
|
|
8276
8470
|
webDir: isWebDir(bundledWeb) ? bundledWeb : null,
|
|
8277
8471
|
sourceRoot: null,
|
|
8278
8472
|
cliEntry: file
|
|
8279
8473
|
};
|
|
8280
8474
|
}
|
|
8281
|
-
const root =
|
|
8475
|
+
const root = path30.resolve(here, "..", "..", "..");
|
|
8282
8476
|
const override = process.env.VIBEFLOW_WEB_DIR;
|
|
8283
|
-
const webDir = override && isWebDir(override) ? override : isWebDir(
|
|
8284
|
-
return { version: readVersion(
|
|
8477
|
+
const webDir = override && isWebDir(override) ? override : isWebDir(path30.join(root, "app")) ? path30.join(root, "app") : null;
|
|
8478
|
+
return { version: readVersion(path30.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
|
|
8285
8479
|
}
|
|
8286
8480
|
function readVersion(pkgPath) {
|
|
8287
8481
|
try {
|
|
8288
|
-
return JSON.parse(
|
|
8482
|
+
return JSON.parse(fs25.readFileSync(pkgPath, "utf8")).version;
|
|
8289
8483
|
} catch {
|
|
8290
8484
|
return "0.0.0";
|
|
8291
8485
|
}
|
|
@@ -8491,8 +8685,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
|
|
|
8491
8685
|
console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
|
|
8492
8686
|
return 1;
|
|
8493
8687
|
}
|
|
8494
|
-
const dir =
|
|
8495
|
-
if (!
|
|
8688
|
+
const dir = path30.resolve(target);
|
|
8689
|
+
if (!fs25.existsSync(dir) || !fs25.statSync(dir).isDirectory()) {
|
|
8496
8690
|
console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
|
|
8497
8691
|
return 1;
|
|
8498
8692
|
}
|