@kw-yeh/vibeflow 4.11.29 → 4.11.32
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/vibeflow.mjs +280 -151
- package/dist/web/404/index.html +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/_next/static/{rb9awHxbd75L3Vz0R-TtG → 7sUchVzedIXEiwH9u5qkP}/_buildManifest.js +1 -1
- package/dist/web/_next/static/chunks/1068-2abb4920b8ee6ac6.js +9 -0
- package/dist/web/_next/static/chunks/1225.a647bd4cfed39edf.js +1 -0
- package/dist/web/_next/static/chunks/pages/{_app-401e9322101b6533.js → _app-6528e28fe89b0344.js} +1 -1
- package/dist/web/_next/static/chunks/pages/home-5fcfcde1fead971f.js +13 -0
- package/dist/web/_next/static/chunks/webpack-a3102c3e2504881b.js +1 -0
- package/dist/web/home/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/_next/static/chunks/8760-7450ee0068d05bca.js +0 -9
- package/dist/web/_next/static/chunks/pages/home-8e76d4b251bec98a.js +0 -13
- package/dist/web/_next/static/chunks/webpack-30f420d055cbcd0c.js +0 -1
- /package/dist/web/_next/static/{rb9awHxbd75L3Vz0R-TtG → 7sUchVzedIXEiwH9u5qkP}/_ssgManifest.js +0 -0
package/dist/vibeflow.mjs
CHANGED
|
@@ -1107,9 +1107,9 @@ var init_progress = __esm({
|
|
|
1107
1107
|
|
|
1108
1108
|
// packages/tui/src/task-form.ts
|
|
1109
1109
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1110
|
-
import
|
|
1111
|
-
import
|
|
1112
|
-
import
|
|
1110
|
+
import fs27 from "fs";
|
|
1111
|
+
import os9 from "os";
|
|
1112
|
+
import path30 from "path";
|
|
1113
1113
|
import React, { useEffect, useRef, useState } from "react";
|
|
1114
1114
|
import { Box, Text, render as render2, useApp, useInput } from "ink";
|
|
1115
1115
|
function newFormState(defaultAutoMode, agents) {
|
|
@@ -1235,17 +1235,17 @@ function editorCommand(env, platform) {
|
|
|
1235
1235
|
return env.VISUAL?.trim() || env.EDITOR?.trim() || (platform === "win32" ? "notepad" : "vi");
|
|
1236
1236
|
}
|
|
1237
1237
|
function editInEditor(text) {
|
|
1238
|
-
const dir =
|
|
1239
|
-
const file =
|
|
1238
|
+
const dir = fs27.mkdtempSync(path30.join(os9.tmpdir(), "vibeflow-"));
|
|
1239
|
+
const file = path30.join(dir, "description.md");
|
|
1240
1240
|
try {
|
|
1241
|
-
|
|
1241
|
+
fs27.writeFileSync(file, text);
|
|
1242
1242
|
const cmd = editorCommand(process.env, process.platform);
|
|
1243
1243
|
const r = spawnSync2(`${cmd} "${file}"`, { stdio: "inherit", shell: true });
|
|
1244
1244
|
if (r.error) return { error: `\u7121\u6CD5\u555F\u52D5\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\uFF1A${r.error.message}` };
|
|
1245
1245
|
if (r.status !== 0) return { error: `\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\u7D50\u675F\u78BC ${r.status}\uFF0C\u63CF\u8FF0\u672A\u8B8A\u66F4` };
|
|
1246
|
-
return { text:
|
|
1246
|
+
return { text: fs27.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
|
|
1247
1247
|
} finally {
|
|
1248
|
-
|
|
1248
|
+
fs27.rmSync(dir, { recursive: true, force: true });
|
|
1249
1249
|
}
|
|
1250
1250
|
}
|
|
1251
1251
|
function cycle(list, current, step) {
|
|
@@ -1316,10 +1316,10 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1316
1316
|
if (editing === "title") setS((prev) => ({ ...prev, title: draft }));
|
|
1317
1317
|
else if (editing === "branch") setS((prev) => ({ ...prev, branch: value2 }));
|
|
1318
1318
|
else if (editing === "project") {
|
|
1319
|
-
if (value2) setProject(
|
|
1319
|
+
if (value2) setProject(path30.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir())));
|
|
1320
1320
|
} else if (editing === "attachments" && value2) {
|
|
1321
|
-
const file =
|
|
1322
|
-
if (!
|
|
1321
|
+
const file = path30.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir()));
|
|
1322
|
+
if (!fs27.existsSync(file) || !fs27.statSync(file).isFile()) {
|
|
1323
1323
|
setMessage(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${file}`);
|
|
1324
1324
|
return;
|
|
1325
1325
|
}
|
|
@@ -1402,7 +1402,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1402
1402
|
case "branch":
|
|
1403
1403
|
return s.branch || "\uFF08\u7559\u7A7A\u5247\u81EA\u52D5\u547D\u540D\uFF0C\u4F8B\u5982 feature/login-fix\uFF09";
|
|
1404
1404
|
case "attachments":
|
|
1405
|
-
return s.attachments.length ? s.attachments.map((a) =>
|
|
1405
|
+
return s.attachments.length ? s.attachments.map((a) => path30.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
|
|
1406
1406
|
case "description": {
|
|
1407
1407
|
if (!s.description) return "\uFF08Enter \u7528\u7DE8\u8F2F\u5668\u64B0\u5BEB\uFF09";
|
|
1408
1408
|
const lines = s.description.split("\n");
|
|
@@ -1437,7 +1437,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1437
1437
|
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";
|
|
1438
1438
|
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";
|
|
1439
1439
|
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`;
|
|
1440
|
-
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${
|
|
1440
|
+
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path30.basename(s.projectPath)}`;
|
|
1441
1441
|
}
|
|
1442
1442
|
if (field === "branch") {
|
|
1443
1443
|
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";
|
|
@@ -1566,7 +1566,7 @@ __export(src_exports, {
|
|
|
1566
1566
|
showDiff: () => showDiff
|
|
1567
1567
|
});
|
|
1568
1568
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
1569
|
-
import
|
|
1569
|
+
import path31 from "path";
|
|
1570
1570
|
import React2, { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1571
1571
|
import { Box as Box2, Text as Text2, render as render3, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
|
|
1572
1572
|
function findDetachKey(text) {
|
|
@@ -1862,7 +1862,7 @@ function showDiff(task) {
|
|
|
1862
1862
|
});
|
|
1863
1863
|
}
|
|
1864
1864
|
function tmuxAttach(opts, sessionName) {
|
|
1865
|
-
const conf =
|
|
1865
|
+
const conf = path31.join(opts.userDataDir, "tmux.conf");
|
|
1866
1866
|
const inOurServer = (process.env.TMUX ?? "").split(",")[0].endsWith(`/${TMUX_SOCKET}`);
|
|
1867
1867
|
const args = inOurServer ? ["-L", TMUX_SOCKET, "-f", conf, "switch-client", "-t", `=${sessionName}`] : ["-L", TMUX_SOCKET, "-f", conf, "attach-session", "-t", `=${sessionName}`];
|
|
1868
1868
|
spawnSync3("tmux", args, { stdio: "inherit", env: { ...process.env, TMUX: inOurServer ? process.env.TMUX : "" } });
|
|
@@ -1993,9 +1993,9 @@ var init_src = __esm({
|
|
|
1993
1993
|
});
|
|
1994
1994
|
|
|
1995
1995
|
// packages/cli/src/main.ts
|
|
1996
|
-
import
|
|
1997
|
-
import
|
|
1998
|
-
import { fileURLToPath } from "url";
|
|
1996
|
+
import fs28 from "fs";
|
|
1997
|
+
import path32 from "path";
|
|
1998
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1999
1999
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
2000
2000
|
import { spawn as spawn9 } from "node:child_process";
|
|
2001
2001
|
|
|
@@ -2601,6 +2601,9 @@ function createBridge(t) {
|
|
|
2601
2601
|
},
|
|
2602
2602
|
// Interactive terminal bridge (Phase 3).
|
|
2603
2603
|
term: {
|
|
2604
|
+
readFile: (sessionKey, path33) => t.invoke("terminal:readFile", { sessionKey, path: path33 }),
|
|
2605
|
+
existingFiles: (sessionKey, paths) => t.invoke("terminal:existingFiles", { sessionKey, paths }),
|
|
2606
|
+
openFile: (sessionKey, path33) => t.invoke("terminal:openFile", { sessionKey, path: path33 }),
|
|
2604
2607
|
/**
|
|
2605
2608
|
* Start a terminal for a task: its agent when `launch` is given, else an
|
|
2606
2609
|
* interactive shell. Core resolves the cwd and builds the command.
|
|
@@ -4714,8 +4717,8 @@ async function taskCommand(argv) {
|
|
|
4714
4717
|
}
|
|
4715
4718
|
|
|
4716
4719
|
// packages/cli/src/host.ts
|
|
4717
|
-
import
|
|
4718
|
-
import
|
|
4720
|
+
import fs25 from "fs";
|
|
4721
|
+
import path28 from "path";
|
|
4719
4722
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
4720
4723
|
|
|
4721
4724
|
// packages/core/src/events.ts
|
|
@@ -5122,21 +5125,109 @@ async function syncBuiltinsAtStartup() {
|
|
|
5122
5125
|
}
|
|
5123
5126
|
|
|
5124
5127
|
// packages/core/src/service.ts
|
|
5125
|
-
import
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
+
import fs21 from "fs";
|
|
5129
|
+
|
|
5130
|
+
// packages/core/src/terminal-files.ts
|
|
5131
|
+
import { constants } from "fs";
|
|
5132
|
+
import fs13 from "fs/promises";
|
|
5133
|
+
import os3 from "os";
|
|
5134
|
+
import path16 from "path";
|
|
5135
|
+
import { fileURLToPath } from "url";
|
|
5136
|
+
var MAX_PREVIEW_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
5137
|
+
var MAX_TEXT_BYTES2 = 256 * 1024;
|
|
5138
|
+
var IMAGE_MIME2 = {
|
|
5139
|
+
".png": "image/png",
|
|
5140
|
+
".jpg": "image/jpeg",
|
|
5141
|
+
".jpeg": "image/jpeg",
|
|
5142
|
+
".gif": "image/gif",
|
|
5143
|
+
".webp": "image/webp",
|
|
5144
|
+
".avif": "image/avif",
|
|
5145
|
+
".bmp": "image/bmp",
|
|
5146
|
+
".svg": "image/svg+xml"
|
|
5147
|
+
};
|
|
5148
|
+
function resolveTerminalFile(cwd, input) {
|
|
5149
|
+
if (!input || input.includes("\0") || input.length > 32768) throw new Error("\u6A94\u6848\u8DEF\u5F91\u7121\u6548\u3002");
|
|
5150
|
+
let target = input;
|
|
5151
|
+
if (/^file:/i.test(target)) {
|
|
5152
|
+
const url = new URL(target);
|
|
5153
|
+
if (url.hostname && url.hostname !== "localhost") throw new Error("\u53EA\u652F\u63F4\u672C\u6A5F\u6A94\u6848\u3002");
|
|
5154
|
+
target = fileURLToPath(url);
|
|
5155
|
+
} else if (/^[a-z][a-z\d+.-]*:/i.test(target) && !/^[a-z]:[\\/]/i.test(target)) {
|
|
5156
|
+
throw new Error("\u53EA\u652F\u63F4\u672C\u6A5F\u6A94\u6848\u8DEF\u5F91\u3002");
|
|
5157
|
+
}
|
|
5158
|
+
if (target.startsWith("~/") || target.startsWith("~\\")) target = path16.join(os3.homedir(), target.slice(2));
|
|
5159
|
+
if (target.includes("\0")) throw new Error("\u6A94\u6848\u8DEF\u5F91\u7121\u6548\u3002");
|
|
5160
|
+
return path16.resolve(cwd, target);
|
|
5161
|
+
}
|
|
5162
|
+
var MAX_FILE_CANDIDATES = 128;
|
|
5163
|
+
async function existingTerminalFiles(cwd, inputs) {
|
|
5164
|
+
const found2 = await Promise.all(inputs.slice(0, MAX_FILE_CANDIDATES).map(async (input) => {
|
|
5165
|
+
try {
|
|
5166
|
+
return (await fs13.stat(resolveTerminalFile(cwd, input))).isFile();
|
|
5167
|
+
} catch {
|
|
5168
|
+
return false;
|
|
5169
|
+
}
|
|
5170
|
+
}));
|
|
5171
|
+
return inputs.filter((_, i) => found2[i]);
|
|
5172
|
+
}
|
|
5173
|
+
async function readTerminalFile(cwd, input, metadataOnly = false) {
|
|
5174
|
+
let target;
|
|
5175
|
+
try {
|
|
5176
|
+
target = resolveTerminalFile(cwd, input);
|
|
5177
|
+
} catch (error) {
|
|
5178
|
+
return { ok: false, error: "invalid_path", message: error instanceof Error ? error.message : "\u6A94\u6848\u8DEF\u5F91\u7121\u6548\u3002" };
|
|
5179
|
+
}
|
|
5180
|
+
let handle;
|
|
5181
|
+
try {
|
|
5182
|
+
if (!(await fs13.stat(target)).isFile()) return { ok: false, error: "not_file", message: "\u9019\u500B\u8DEF\u5F91\u4E0D\u662F\u4E00\u822C\u6A94\u6848\u3002" };
|
|
5183
|
+
handle = await fs13.open(target, constants.O_RDONLY | constants.O_NONBLOCK);
|
|
5184
|
+
const stat = await handle.stat();
|
|
5185
|
+
if (!stat.isFile()) return { ok: false, error: "not_file", message: "\u9019\u500B\u8DEF\u5F91\u4E0D\u662F\u4E00\u822C\u6A94\u6848\u3002" };
|
|
5186
|
+
if (metadataOnly) return { ok: true, path: target, kind: "binary", reason: "" };
|
|
5187
|
+
const mime = IMAGE_MIME2[path16.extname(target).toLowerCase()];
|
|
5188
|
+
if (mime && stat.size > MAX_PREVIEW_IMAGE_BYTES) {
|
|
5189
|
+
return { ok: true, path: target, kind: "binary", reason: "\u5716\u7247\u8D85\u904E 20 MB \u9810\u89BD\u4E0A\u9650\u3002" };
|
|
5190
|
+
}
|
|
5191
|
+
const limit = mime ? MAX_PREVIEW_IMAGE_BYTES : MAX_TEXT_BYTES2;
|
|
5192
|
+
const buffer = Buffer.alloc(Math.min(stat.size, limit + 1));
|
|
5193
|
+
let bytes = 0;
|
|
5194
|
+
while (bytes < buffer.length) {
|
|
5195
|
+
const { bytesRead } = await handle.read(buffer, bytes, buffer.length - bytes, bytes);
|
|
5196
|
+
if (!bytesRead) break;
|
|
5197
|
+
bytes += bytesRead;
|
|
5198
|
+
}
|
|
5199
|
+
if (mime) {
|
|
5200
|
+
if (bytes > limit) return { ok: true, path: target, kind: "binary", reason: "\u5716\u7247\u8D85\u904E 20 MB \u9810\u89BD\u4E0A\u9650\u3002" };
|
|
5201
|
+
return { ok: true, path: target, kind: "image", dataUrl: `data:${mime};base64,${buffer.subarray(0, bytes).toString("base64")}` };
|
|
5202
|
+
}
|
|
5203
|
+
const sample = buffer.subarray(0, Math.min(bytes, 8192));
|
|
5204
|
+
if (/\.(pdf|zip|gz|tar|exe|dll|woff2?|mp4|mov|webm|mp3|wav)$/i.test(target) || sample.some((b) => b === 0 || (b < 9 || b > 13 && b < 32))) {
|
|
5205
|
+
return { ok: true, path: target, kind: "binary", reason: "\u6B64\u683C\u5F0F\u7121\u6CD5\u5728 VibeFlow \u5167\u9810\u89BD\u3002" };
|
|
5206
|
+
}
|
|
5207
|
+
return { ok: true, path: target, kind: "text", text: buffer.subarray(0, Math.min(bytes, limit)).toString("utf8"), truncated: bytes > limit };
|
|
5208
|
+
} catch (error) {
|
|
5209
|
+
const code2 = error.code;
|
|
5210
|
+
return code2 === "ENOENT" || code2 === "ENOTDIR" ? { ok: false, error: "not_found", message: "\u627E\u4E0D\u5230\u9019\u500B\u6A94\u6848\uFF0C\u53EF\u80FD\u5DF2\u88AB\u79FB\u52D5\u6216\u522A\u9664\u3002" } : { ok: false, error: "unreadable", message: "\u7121\u6CD5\u8B80\u53D6\u9019\u500B\u6A94\u6848\uFF0C\u8ACB\u78BA\u8A8D\u5B58\u53D6\u6B0A\u9650\u3002" };
|
|
5211
|
+
} finally {
|
|
5212
|
+
await handle?.close();
|
|
5213
|
+
}
|
|
5214
|
+
}
|
|
5215
|
+
|
|
5216
|
+
// packages/core/src/service.ts
|
|
5217
|
+
import os8 from "os";
|
|
5218
|
+
import path24 from "path";
|
|
5128
5219
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
5129
5220
|
|
|
5130
5221
|
// packages/core/src/agent-models.ts
|
|
5131
|
-
import
|
|
5132
|
-
import
|
|
5133
|
-
import
|
|
5222
|
+
import fs15 from "fs";
|
|
5223
|
+
import os6 from "os";
|
|
5224
|
+
import path18 from "path";
|
|
5134
5225
|
|
|
5135
5226
|
// packages/core/src/claude-models.ts
|
|
5136
5227
|
init_effort();
|
|
5137
|
-
import
|
|
5138
|
-
import
|
|
5139
|
-
import
|
|
5228
|
+
import fs14 from "fs";
|
|
5229
|
+
import os4 from "os";
|
|
5230
|
+
import path17 from "path";
|
|
5140
5231
|
|
|
5141
5232
|
// packages/core/src/json-lines-process.ts
|
|
5142
5233
|
import { spawn as spawn4 } from "child_process";
|
|
@@ -5240,11 +5331,11 @@ var CLAUDE_MODELS_TIMEOUT_MS = 15e3;
|
|
|
5240
5331
|
var REQUEST_ID = "vibeflow-models";
|
|
5241
5332
|
var CLAUDE_ARGS = ["-p", "--input-format", "stream-json", "--output-format", "stream-json", "--verbose"];
|
|
5242
5333
|
function openClaude() {
|
|
5243
|
-
const cwd =
|
|
5334
|
+
const cwd = fs14.mkdtempSync(path17.join(os4.tmpdir(), "vf-claude-models-"));
|
|
5244
5335
|
return spawnJsonLines("claude", CLAUDE_ARGS, {
|
|
5245
5336
|
cwd,
|
|
5246
5337
|
timeoutMs: CLAUDE_MODELS_TIMEOUT_MS,
|
|
5247
|
-
onExit: () =>
|
|
5338
|
+
onExit: () => fs14.rmSync(cwd, { recursive: true, force: true })
|
|
5248
5339
|
});
|
|
5249
5340
|
}
|
|
5250
5341
|
function isRecord(value) {
|
|
@@ -5291,7 +5382,7 @@ async function fetchClaudeModels(open = openClaude) {
|
|
|
5291
5382
|
// packages/core/src/codex-models.ts
|
|
5292
5383
|
init_effort();
|
|
5293
5384
|
init_env();
|
|
5294
|
-
import
|
|
5385
|
+
import os5 from "os";
|
|
5295
5386
|
var CODEX_MODELS_TIMEOUT_MS = 2e4;
|
|
5296
5387
|
var MAX_PAGES = 20;
|
|
5297
5388
|
var CodexLoginRequiredError = class extends Error {
|
|
@@ -5304,7 +5395,7 @@ function codexAppServerCommand() {
|
|
|
5304
5395
|
return {
|
|
5305
5396
|
bin: "codex",
|
|
5306
5397
|
args: ["app-server", "--listen", "stdio://"],
|
|
5307
|
-
cwd:
|
|
5398
|
+
cwd: os5.homedir(),
|
|
5308
5399
|
env: execEnv()
|
|
5309
5400
|
};
|
|
5310
5401
|
}
|
|
@@ -5393,7 +5484,7 @@ function builtinModels(id) {
|
|
|
5393
5484
|
}
|
|
5394
5485
|
function defaultCodexHome() {
|
|
5395
5486
|
const configured = process.env.CODEX_HOME?.trim();
|
|
5396
|
-
return configured ? configured :
|
|
5487
|
+
return configured ? configured : path18.join(os6.homedir(), ".codex");
|
|
5397
5488
|
}
|
|
5398
5489
|
function parseCodexCatalog(raw) {
|
|
5399
5490
|
const body = JSON.parse(raw);
|
|
@@ -5419,9 +5510,9 @@ function errorMessage(err) {
|
|
|
5419
5510
|
return err instanceof Error ? err.message : String(err);
|
|
5420
5511
|
}
|
|
5421
5512
|
async function readCodexCache(codexHome) {
|
|
5422
|
-
const cachePath =
|
|
5513
|
+
const cachePath = path18.join(codexHome, "models_cache.json");
|
|
5423
5514
|
try {
|
|
5424
|
-
const raw = await
|
|
5515
|
+
const raw = await fs15.promises.readFile(cachePath, "utf8");
|
|
5425
5516
|
return { models: parseCodexCatalog(raw), source: "codex-cache" };
|
|
5426
5517
|
} catch (err) {
|
|
5427
5518
|
const missing = err.code === "ENOENT";
|
|
@@ -5543,11 +5634,11 @@ function effortDescription(level) {
|
|
|
5543
5634
|
}
|
|
5544
5635
|
|
|
5545
5636
|
// packages/core/src/jev-credentials.ts
|
|
5546
|
-
import
|
|
5547
|
-
import
|
|
5637
|
+
import fs16 from "fs";
|
|
5638
|
+
import path19 from "path";
|
|
5548
5639
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5549
5640
|
function keyPath(dir) {
|
|
5550
|
-
return
|
|
5641
|
+
return path19.join(dir, "jev-api-key");
|
|
5551
5642
|
}
|
|
5552
5643
|
function dataDir() {
|
|
5553
5644
|
return getPlatform().userDataDir();
|
|
@@ -5555,8 +5646,8 @@ function dataDir() {
|
|
|
5555
5646
|
function readJevApiKey(dir = dataDir()) {
|
|
5556
5647
|
try {
|
|
5557
5648
|
const file = keyPath(dir);
|
|
5558
|
-
if (
|
|
5559
|
-
const saved =
|
|
5649
|
+
if (fs16.lstatSync(file).isSymbolicLink()) throw new Error("Jev key file must not be a symlink");
|
|
5650
|
+
const saved = fs16.readFileSync(file, "utf8").trim();
|
|
5560
5651
|
if (saved) return saved;
|
|
5561
5652
|
} catch (err) {
|
|
5562
5653
|
if (err.code !== "ENOENT") throw err;
|
|
@@ -5566,8 +5657,8 @@ function readJevApiKey(dir = dataDir()) {
|
|
|
5566
5657
|
function jevKeyStatus(dir = dataDir()) {
|
|
5567
5658
|
try {
|
|
5568
5659
|
const file = keyPath(dir);
|
|
5569
|
-
if (
|
|
5570
|
-
if (
|
|
5660
|
+
if (fs16.lstatSync(file).isSymbolicLink()) throw new Error("Jev key file must not be a symlink");
|
|
5661
|
+
if (fs16.readFileSync(file, "utf8").trim()) return { configured: true, source: "saved" };
|
|
5571
5662
|
} catch (err) {
|
|
5572
5663
|
if (err.code !== "ENOENT") throw err;
|
|
5573
5664
|
}
|
|
@@ -5578,20 +5669,20 @@ function saveJevApiKey(value, dir = dataDir()) {
|
|
|
5578
5669
|
if (!key || key.length > 4096 || !/^[\x21-\x7e]+$/.test(key)) {
|
|
5579
5670
|
throw new Error("Jev API key \u683C\u5F0F\u7121\u6548");
|
|
5580
5671
|
}
|
|
5581
|
-
|
|
5672
|
+
fs16.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
5582
5673
|
const file = keyPath(dir);
|
|
5583
5674
|
const tmp = `${file}.${randomUUID3()}.tmp`;
|
|
5584
5675
|
try {
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
if (process.platform !== "win32")
|
|
5676
|
+
fs16.writeFileSync(tmp, key, { mode: 384, flag: "wx" });
|
|
5677
|
+
fs16.renameSync(tmp, file);
|
|
5678
|
+
if (process.platform !== "win32") fs16.chmodSync(file, 384);
|
|
5588
5679
|
} finally {
|
|
5589
|
-
|
|
5680
|
+
fs16.rmSync(tmp, { force: true });
|
|
5590
5681
|
}
|
|
5591
5682
|
return { configured: true, source: "saved" };
|
|
5592
5683
|
}
|
|
5593
5684
|
function removeJevApiKey(dir = dataDir()) {
|
|
5594
|
-
|
|
5685
|
+
fs16.rmSync(keyPath(dir), { force: true });
|
|
5595
5686
|
return jevKeyStatus(dir);
|
|
5596
5687
|
}
|
|
5597
5688
|
|
|
@@ -6103,45 +6194,45 @@ function createJiraService(options) {
|
|
|
6103
6194
|
}
|
|
6104
6195
|
|
|
6105
6196
|
// packages/core/src/board-cli.ts
|
|
6106
|
-
import
|
|
6107
|
-
import
|
|
6197
|
+
import fs17 from "fs";
|
|
6198
|
+
import path20 from "path";
|
|
6108
6199
|
import { pathToFileURL } from "url";
|
|
6109
6200
|
async function boardCliLaunchInfo() {
|
|
6110
|
-
const storeDir =
|
|
6201
|
+
const storeDir = path20.dirname(getStorePath());
|
|
6111
6202
|
const entry = getPlatform().cliEntry?.();
|
|
6112
|
-
if (entry &&
|
|
6203
|
+
if (entry && fs17.existsSync(entry)) return { storeDir, cliPath: entry };
|
|
6113
6204
|
const root = getPlatform().sourceRoot();
|
|
6114
6205
|
if (!root) return { storeDir };
|
|
6115
|
-
const cliPath =
|
|
6116
|
-
const loaderPath =
|
|
6117
|
-
if (!
|
|
6206
|
+
const cliPath = path20.join(root, "scripts", "vibeflow.mjs");
|
|
6207
|
+
const loaderPath = path20.join(root, "test", "support", "register.mjs");
|
|
6208
|
+
if (!fs17.existsSync(cliPath) || !fs17.existsSync(loaderPath)) return { storeDir };
|
|
6118
6209
|
return { storeDir, cliPath, loaderPath: pathToFileURL(loaderPath).href };
|
|
6119
6210
|
}
|
|
6120
6211
|
|
|
6121
6212
|
// packages/core/src/decisions.ts
|
|
6122
|
-
import
|
|
6123
|
-
import
|
|
6213
|
+
import fs18 from "fs";
|
|
6214
|
+
import path21 from "path";
|
|
6124
6215
|
var DECISIONS_FILE_SUFFIX = ".DECISIONS.md";
|
|
6125
6216
|
function decisionsKey(worktreePath, branch) {
|
|
6126
|
-
return worktreePath ?
|
|
6217
|
+
return worktreePath ? path21.basename(worktreePath) : branch.replace(/\//g, "-");
|
|
6127
6218
|
}
|
|
6128
6219
|
function decisionsPath(workspacePath, key) {
|
|
6129
|
-
return
|
|
6220
|
+
return path21.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
|
|
6130
6221
|
}
|
|
6131
6222
|
function deleteDecisions(workspacePath, key) {
|
|
6132
6223
|
try {
|
|
6133
|
-
|
|
6224
|
+
fs18.rmSync(decisionsPath(workspacePath, key), { force: true });
|
|
6134
6225
|
} catch {
|
|
6135
6226
|
}
|
|
6136
6227
|
}
|
|
6137
6228
|
|
|
6138
6229
|
// packages/core/src/specs.ts
|
|
6139
|
-
import
|
|
6140
|
-
import
|
|
6230
|
+
import fs19 from "fs";
|
|
6231
|
+
import path22 from "path";
|
|
6141
6232
|
var SPEC_FILE_NAME = "spec.md";
|
|
6142
6233
|
var MAX_SPEC_BYTES = 256 * 1024;
|
|
6143
6234
|
function isSpecPath(p) {
|
|
6144
|
-
return
|
|
6235
|
+
return path22.posix.basename(p) === SPEC_FILE_NAME;
|
|
6145
6236
|
}
|
|
6146
6237
|
function clip2(buf) {
|
|
6147
6238
|
return {
|
|
@@ -6156,10 +6247,10 @@ async function readWorktreeSpecs(worktreePath, baseBranch) {
|
|
|
6156
6247
|
for (const entry of entries) {
|
|
6157
6248
|
if (entry.status === "D" || !isSpecPath(entry.path)) continue;
|
|
6158
6249
|
try {
|
|
6159
|
-
const full =
|
|
6160
|
-
const stat =
|
|
6250
|
+
const full = path22.join(worktreePath, entry.path);
|
|
6251
|
+
const stat = fs19.statSync(full);
|
|
6161
6252
|
if (!stat.isFile()) continue;
|
|
6162
|
-
specs.push({ path: entry.path, ...clip2(
|
|
6253
|
+
specs.push({ path: entry.path, ...clip2(fs19.readFileSync(full)), updatedAt: stat.mtimeMs });
|
|
6163
6254
|
} catch {
|
|
6164
6255
|
}
|
|
6165
6256
|
}
|
|
@@ -6511,17 +6602,17 @@ init_launch();
|
|
|
6511
6602
|
// packages/core/src/progress-tracker.ts
|
|
6512
6603
|
init_progress();
|
|
6513
6604
|
init_launch();
|
|
6514
|
-
import
|
|
6515
|
-
import
|
|
6516
|
-
import
|
|
6605
|
+
import fs20 from "fs";
|
|
6606
|
+
import os7 from "os";
|
|
6607
|
+
import path23 from "path";
|
|
6517
6608
|
var SINCE_SLACK_MS = 5e3;
|
|
6518
6609
|
function claudeProjectDir(home, cwd) {
|
|
6519
|
-
return
|
|
6610
|
+
return path23.join(home, ".claude", "projects", cwd.replace(/[^a-zA-Z0-9]/g, "-"));
|
|
6520
6611
|
}
|
|
6521
6612
|
function pathAliases(p) {
|
|
6522
|
-
const resolved =
|
|
6613
|
+
const resolved = path23.resolve(p);
|
|
6523
6614
|
try {
|
|
6524
|
-
const real =
|
|
6615
|
+
const real = fs20.realpathSync(resolved);
|
|
6525
6616
|
return real === resolved ? [resolved] : [resolved, real];
|
|
6526
6617
|
} catch {
|
|
6527
6618
|
return [resolved];
|
|
@@ -6537,7 +6628,7 @@ function samePath(a, b) {
|
|
|
6537
6628
|
}
|
|
6538
6629
|
function listFiles(dir, test) {
|
|
6539
6630
|
try {
|
|
6540
|
-
return
|
|
6631
|
+
return fs20.readdirSync(dir).filter(test).map((name) => path23.join(dir, name));
|
|
6541
6632
|
} catch {
|
|
6542
6633
|
return [];
|
|
6543
6634
|
}
|
|
@@ -6545,22 +6636,22 @@ function listFiles(dir, test) {
|
|
|
6545
6636
|
function firstLine(file) {
|
|
6546
6637
|
let fd;
|
|
6547
6638
|
try {
|
|
6548
|
-
fd =
|
|
6639
|
+
fd = fs20.openSync(file, "r");
|
|
6549
6640
|
const buf = Buffer.alloc(64 * 1024);
|
|
6550
|
-
const n =
|
|
6641
|
+
const n = fs20.readSync(fd, buf, 0, buf.length, 0);
|
|
6551
6642
|
const text = buf.subarray(0, n).toString("utf8");
|
|
6552
6643
|
const nl = text.indexOf("\n");
|
|
6553
6644
|
return nl === -1 ? null : text.slice(0, nl);
|
|
6554
6645
|
} catch {
|
|
6555
6646
|
return null;
|
|
6556
6647
|
} finally {
|
|
6557
|
-
if (fd !== void 0)
|
|
6648
|
+
if (fd !== void 0) fs20.closeSync(fd);
|
|
6558
6649
|
}
|
|
6559
6650
|
}
|
|
6560
6651
|
function advance(file, cursor, reduce, initial) {
|
|
6561
6652
|
let size;
|
|
6562
6653
|
try {
|
|
6563
|
-
size =
|
|
6654
|
+
size = fs20.statSync(file).size;
|
|
6564
6655
|
} catch {
|
|
6565
6656
|
return;
|
|
6566
6657
|
}
|
|
@@ -6572,9 +6663,9 @@ function advance(file, cursor, reduce, initial) {
|
|
|
6572
6663
|
if (size === cursor.offset) return;
|
|
6573
6664
|
let fd;
|
|
6574
6665
|
try {
|
|
6575
|
-
fd =
|
|
6666
|
+
fd = fs20.openSync(file, "r");
|
|
6576
6667
|
const chunk = Buffer.alloc(size - cursor.offset);
|
|
6577
|
-
const n =
|
|
6668
|
+
const n = fs20.readSync(fd, chunk, 0, chunk.length, cursor.offset);
|
|
6578
6669
|
cursor.offset += n;
|
|
6579
6670
|
const data = Buffer.concat([cursor.rest, chunk.subarray(0, n)]);
|
|
6580
6671
|
const nl = data.lastIndexOf(10);
|
|
@@ -6588,7 +6679,7 @@ function advance(file, cursor, reduce, initial) {
|
|
|
6588
6679
|
}
|
|
6589
6680
|
} catch {
|
|
6590
6681
|
} finally {
|
|
6591
|
-
if (fd !== void 0)
|
|
6682
|
+
if (fd !== void 0) fs20.closeSync(fd);
|
|
6592
6683
|
}
|
|
6593
6684
|
}
|
|
6594
6685
|
function newCursor(state) {
|
|
@@ -6607,14 +6698,14 @@ function codexDayDirs(sessionsRoot, since) {
|
|
|
6607
6698
|
const cur = new Date(start.getFullYear(), start.getMonth(), start.getDate());
|
|
6608
6699
|
while (cur <= end) {
|
|
6609
6700
|
const pad = (n) => String(n).padStart(2, "0");
|
|
6610
|
-
days.push(
|
|
6701
|
+
days.push(path23.join(sessionsRoot, String(cur.getFullYear()), pad(cur.getMonth() + 1), pad(cur.getDate())));
|
|
6611
6702
|
cur.setDate(cur.getDate() + 1);
|
|
6612
6703
|
}
|
|
6613
6704
|
return days;
|
|
6614
6705
|
}
|
|
6615
6706
|
function createProgressTracker(opts) {
|
|
6616
|
-
const home = opts.homeDir ??
|
|
6617
|
-
const codexHomes = opts.codexHomes ?? (() => [
|
|
6707
|
+
const home = opts.homeDir ?? os7.homedir();
|
|
6708
|
+
const codexHomes = opts.codexHomes ?? (() => [path23.join(home, ".codex")]);
|
|
6618
6709
|
const pollMs = opts.pollMs ?? 2e3;
|
|
6619
6710
|
const entries = /* @__PURE__ */ new Map();
|
|
6620
6711
|
const belongsToRun = (target, startedAt) => target.since == null || startedAt != null && startedAt >= target.since - SINCE_SLACK_MS;
|
|
@@ -6623,7 +6714,7 @@ function createProgressTracker(opts) {
|
|
|
6623
6714
|
const dirs = pathAliases(target.worktreePath).map((p) => claudeProjectDir(home, p));
|
|
6624
6715
|
for (const file of dirs.flatMap((dir) => listFiles(dir, (n) => n.endsWith(".jsonl")))) {
|
|
6625
6716
|
if (entry.claude.has(file) || entry.rejected.has(file)) continue;
|
|
6626
|
-
const session =
|
|
6717
|
+
const session = path23.basename(file, ".jsonl");
|
|
6627
6718
|
if (session !== target.sessionId) {
|
|
6628
6719
|
const head = firstLine(file);
|
|
6629
6720
|
if (head == null) continue;
|
|
@@ -6636,8 +6727,8 @@ function createProgressTracker(opts) {
|
|
|
6636
6727
|
entry.claude.set(file, newCursor(initialClaudeState()));
|
|
6637
6728
|
}
|
|
6638
6729
|
for (const file of Array.from(entry.claude.keys())) {
|
|
6639
|
-
if (file.includes(`${
|
|
6640
|
-
const sub =
|
|
6730
|
+
if (file.includes(`${path23.sep}subagents${path23.sep}`)) continue;
|
|
6731
|
+
const sub = path23.join(file.slice(0, -".jsonl".length), "subagents");
|
|
6641
6732
|
for (const f of listFiles(sub, (n) => n.endsWith(".jsonl"))) {
|
|
6642
6733
|
if (!entry.claude.has(f)) entry.claude.set(f, newCursor(initialClaudeState()));
|
|
6643
6734
|
}
|
|
@@ -6646,7 +6737,7 @@ function createProgressTracker(opts) {
|
|
|
6646
6737
|
function discoverCodex(entry) {
|
|
6647
6738
|
const { target } = entry;
|
|
6648
6739
|
for (const codexHome of codexHomes()) {
|
|
6649
|
-
for (const day of codexDayDirs(
|
|
6740
|
+
for (const day of codexDayDirs(path23.join(codexHome, "sessions"), target.since)) {
|
|
6650
6741
|
for (const file of listFiles(day, (n) => n.startsWith("rollout-") && n.endsWith(".jsonl"))) {
|
|
6651
6742
|
if (entry.codex.has(file) || entry.rejected.has(file)) continue;
|
|
6652
6743
|
const head = firstLine(file);
|
|
@@ -6663,11 +6754,11 @@ function createProgressTracker(opts) {
|
|
|
6663
6754
|
}
|
|
6664
6755
|
}
|
|
6665
6756
|
function drainHooks(entry) {
|
|
6666
|
-
const dir =
|
|
6757
|
+
const dir = path23.join(entry.target.worktreePath, PROGRESS_EVENTS_DIR);
|
|
6667
6758
|
for (const file of listFiles(dir, (n) => n.endsWith(".json")).sort()) {
|
|
6668
6759
|
try {
|
|
6669
|
-
const at =
|
|
6670
|
-
const event = JSON.parse(
|
|
6760
|
+
const at = fs20.statSync(file).mtimeMs;
|
|
6761
|
+
const event = JSON.parse(fs20.readFileSync(file, "utf8"));
|
|
6671
6762
|
const foreign = entry.target.sessionId && typeof event.session_id === "string" && event.session_id !== entry.target.sessionId;
|
|
6672
6763
|
const activity = foreign ? null : activityFromHook(event, at);
|
|
6673
6764
|
if (activity) entry.hookActivity = latestActivity(entry.hookActivity ?? activity, activity);
|
|
@@ -6675,7 +6766,7 @@ function createProgressTracker(opts) {
|
|
|
6675
6766
|
continue;
|
|
6676
6767
|
}
|
|
6677
6768
|
try {
|
|
6678
|
-
|
|
6769
|
+
fs20.unlinkSync(file);
|
|
6679
6770
|
} catch {
|
|
6680
6771
|
}
|
|
6681
6772
|
}
|
|
@@ -6687,8 +6778,8 @@ function createProgressTracker(opts) {
|
|
|
6687
6778
|
if (target.agent === "claude") {
|
|
6688
6779
|
for (const [file, cursor] of entry.claude) {
|
|
6689
6780
|
runUsage = addUsage(runUsage, claudeUsageTotal(cursor.state));
|
|
6690
|
-
if (file.includes(`${
|
|
6691
|
-
const isExecutor =
|
|
6781
|
+
if (file.includes(`${path23.sep}subagents${path23.sep}`)) continue;
|
|
6782
|
+
const isExecutor = path23.basename(file, ".jsonl") === target.sessionId;
|
|
6692
6783
|
const s = cursor.state;
|
|
6693
6784
|
if (isExecutor || !target.sessionId && (!primary || (s.startedAt ?? 0) > (primary.startedAt ?? 0))) {
|
|
6694
6785
|
primary = { todos: s.todos, contextTokens: s.contextTokens, activity: s.activity, startedAt: s.startedAt };
|
|
@@ -6767,10 +6858,10 @@ function createProgressTracker(opts) {
|
|
|
6767
6858
|
};
|
|
6768
6859
|
entry.timer.unref?.();
|
|
6769
6860
|
entries.set(target.taskId, entry);
|
|
6770
|
-
const eventsDir =
|
|
6861
|
+
const eventsDir = path23.join(target.worktreePath, PROGRESS_EVENTS_DIR);
|
|
6771
6862
|
try {
|
|
6772
|
-
|
|
6773
|
-
entry.watcher =
|
|
6863
|
+
fs20.mkdirSync(eventsDir, { recursive: true });
|
|
6864
|
+
entry.watcher = fs20.watch(eventsDir, () => {
|
|
6774
6865
|
if (entry.pending) return;
|
|
6775
6866
|
entry.pending = setTimeout(() => {
|
|
6776
6867
|
entry.pending = null;
|
|
@@ -6862,15 +6953,16 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6862
6953
|
const router = jevRouter ?? createJevRouter({ getApiKey: readJevApiKey });
|
|
6863
6954
|
const routing = /* @__PURE__ */ new Map();
|
|
6864
6955
|
const standaloneTerminals = /* @__PURE__ */ new Map();
|
|
6956
|
+
const terminalDirectories = /* @__PURE__ */ new Map();
|
|
6865
6957
|
let libraryCodexHome = null;
|
|
6866
6958
|
void libraryRoot().then((root) => {
|
|
6867
6959
|
libraryCodexHome = libraryPaths(root).codexHome;
|
|
6868
6960
|
}).catch(() => {
|
|
6869
6961
|
});
|
|
6870
|
-
const home = homeDir ??
|
|
6962
|
+
const home = homeDir ?? os8.homedir();
|
|
6871
6963
|
const progress = createProgressTracker({
|
|
6872
6964
|
homeDir: home,
|
|
6873
|
-
codexHomes: () => [
|
|
6965
|
+
codexHomes: () => [path24.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
|
|
6874
6966
|
onUpdate: (taskId, value) => bus.emit("progress:update", { taskId, progress: value }),
|
|
6875
6967
|
onNotify: (taskId, list) => {
|
|
6876
6968
|
const prefs = resolveNotificationSettings(getSettings());
|
|
@@ -6923,6 +7015,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6923
7015
|
removeTerminals([key]);
|
|
6924
7016
|
}
|
|
6925
7017
|
standaloneTerminals.delete(key);
|
|
7018
|
+
terminalDirectories.delete(key);
|
|
6926
7019
|
unwatchSubAgents(key);
|
|
6927
7020
|
}
|
|
6928
7021
|
function isStandaloneKey(key) {
|
|
@@ -6944,6 +7037,21 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6944
7037
|
requireTask(key.split(":")[0]);
|
|
6945
7038
|
return key;
|
|
6946
7039
|
}
|
|
7040
|
+
function terminalDirectory(sessionKey) {
|
|
7041
|
+
const key = requireSessionKey(sessionKey);
|
|
7042
|
+
const known = standaloneTerminals.get(key) ?? terminalDirectories.get(key);
|
|
7043
|
+
if (known) return known;
|
|
7044
|
+
if (isStandaloneKey(key)) {
|
|
7045
|
+
const saved = listTerminals().find((item) => item.sessionKey === key);
|
|
7046
|
+
if (saved?.taskId) {
|
|
7047
|
+
const task2 = requireTask(saved.taskId);
|
|
7048
|
+
return str(task2.worktreePath ?? task2.projectPath, "directory");
|
|
7049
|
+
}
|
|
7050
|
+
return saved?.projectPath ?? os8.homedir();
|
|
7051
|
+
}
|
|
7052
|
+
const task = requireTask(key.split(":")[0]);
|
|
7053
|
+
return str(task.worktreePath ?? task.projectPath, "directory");
|
|
7054
|
+
}
|
|
6947
7055
|
function launchEffort(task, route) {
|
|
6948
7056
|
const effort = task.effort === "auto" ? route?.effort : task.effort;
|
|
6949
7057
|
const model = models.findModel(taskAgent(task), taskModel(task));
|
|
@@ -6988,13 +7096,13 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6988
7096
|
}
|
|
6989
7097
|
function claudeSessionFile(cwd, sessionId) {
|
|
6990
7098
|
const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
6991
|
-
return
|
|
7099
|
+
return path24.join(os8.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
|
|
6992
7100
|
}
|
|
6993
7101
|
async function removeTaskWorktree(task) {
|
|
6994
7102
|
if (!task.projectPath || !task.worktreePath) return;
|
|
6995
7103
|
const branch = task.branch || fallbackBranchName(task.id);
|
|
6996
7104
|
await removeWorktree(task.projectPath, task.worktreePath);
|
|
6997
|
-
deleteArtifacts(task.workspacePath ??
|
|
7105
|
+
deleteArtifacts(task.workspacePath ?? path24.dirname(task.worktreePath), task.worktreePath);
|
|
6998
7106
|
await deleteBranch(task.projectPath, branch);
|
|
6999
7107
|
}
|
|
7000
7108
|
function columnOf(taskId) {
|
|
@@ -7139,8 +7247,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7139
7247
|
"projects:listRecent": () => listRecentProjects(getStore()),
|
|
7140
7248
|
// `vibeflow open <path>` while a host runs: only the host writes the store.
|
|
7141
7249
|
"projects:record": (projectPath) => {
|
|
7142
|
-
const p =
|
|
7143
|
-
if (!
|
|
7250
|
+
const p = path24.resolve(str(projectPath, "projectPath"));
|
|
7251
|
+
if (!fs21.existsSync(p) || !fs21.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
|
|
7144
7252
|
recordRecentProject(getStore(), p);
|
|
7145
7253
|
return listRecentProjects(getStore());
|
|
7146
7254
|
},
|
|
@@ -7193,7 +7301,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7193
7301
|
if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
|
|
7194
7302
|
await teardownTask(task.id);
|
|
7195
7303
|
await resetWorktreeToBase(task.worktreePath, task.baseBranch);
|
|
7196
|
-
const workspacePath = task.workspacePath ??
|
|
7304
|
+
const workspacePath = task.workspacePath ?? path24.dirname(task.worktreePath);
|
|
7197
7305
|
deleteArtifacts(workspacePath, task.worktreePath);
|
|
7198
7306
|
deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
|
|
7199
7307
|
resetSubAgents(task.worktreePath);
|
|
@@ -7237,7 +7345,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7237
7345
|
}
|
|
7238
7346
|
const info = await getGitInfo(nextProject);
|
|
7239
7347
|
if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
|
|
7240
|
-
const projectName =
|
|
7348
|
+
const projectName = path24.basename(nextProject);
|
|
7241
7349
|
const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
|
|
7242
7350
|
await teardownTask(existing.id);
|
|
7243
7351
|
await removeTaskWorktree(existing);
|
|
@@ -7299,6 +7407,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7299
7407
|
// Session ended (natural exit included), so stop its watcher.
|
|
7300
7408
|
onExit: () => unwatchSubAgents(key)
|
|
7301
7409
|
});
|
|
7410
|
+
terminalDirectories.set(key, cwd);
|
|
7302
7411
|
watchSubAgents(key, cwd, (subAgents) => {
|
|
7303
7412
|
bus.emit("subagents:update", { taskId: task.id, subAgents });
|
|
7304
7413
|
});
|
|
@@ -7329,12 +7438,12 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7329
7438
|
cwd = str(p.projectPath, "projectPath");
|
|
7330
7439
|
projectPath = cwd;
|
|
7331
7440
|
} else {
|
|
7332
|
-
cwd =
|
|
7441
|
+
cwd = os8.homedir();
|
|
7333
7442
|
}
|
|
7334
|
-
if (!
|
|
7443
|
+
if (!path24.isAbsolute(cwd) || !fs21.existsSync(cwd) || !fs21.statSync(cwd).isDirectory()) {
|
|
7335
7444
|
invalid("terminal directory must be an existing absolute directory");
|
|
7336
7445
|
}
|
|
7337
|
-
const title = (typeof p.title === "string" ? p.title.trim() : "") || saved?.title || (projectPath ?
|
|
7446
|
+
const title = (typeof p.title === "string" ? p.title.trim() : "") || saved?.title || (projectPath ? path24.basename(projectPath) : "~");
|
|
7338
7447
|
saveTerminal({
|
|
7339
7448
|
sessionKey: key,
|
|
7340
7449
|
title,
|
|
@@ -7351,6 +7460,26 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7351
7460
|
}
|
|
7352
7461
|
return sessions.start(key, { cwd, cols, rows });
|
|
7353
7462
|
},
|
|
7463
|
+
"terminal:readFile": async (payload) => {
|
|
7464
|
+
const p = obj(payload, "payload");
|
|
7465
|
+
return readTerminalFile(terminalDirectory(p.sessionKey), str(p.path, "path"));
|
|
7466
|
+
},
|
|
7467
|
+
"terminal:existingFiles": async (payload) => {
|
|
7468
|
+
const p = obj(payload, "payload");
|
|
7469
|
+
if (!Array.isArray(p.paths)) invalid("paths must be an array");
|
|
7470
|
+
return existingTerminalFiles(terminalDirectory(p.sessionKey), p.paths.map((value) => str(value, "path")));
|
|
7471
|
+
},
|
|
7472
|
+
"terminal:openFile": async (payload) => {
|
|
7473
|
+
const p = obj(payload, "payload");
|
|
7474
|
+
const file = await readTerminalFile(terminalDirectory(p.sessionKey), str(p.path, "path"), true);
|
|
7475
|
+
if (!file.ok) return { ok: false, message: file.message };
|
|
7476
|
+
try {
|
|
7477
|
+
const message = await getPlatform().openPath(file.path);
|
|
7478
|
+
return message ? { ok: false, message } : { ok: true };
|
|
7479
|
+
} catch (error) {
|
|
7480
|
+
return { ok: false, message: error instanceof Error ? error.message : "\u7121\u6CD5\u958B\u555F\u6A94\u6848\u3002" };
|
|
7481
|
+
}
|
|
7482
|
+
},
|
|
7354
7483
|
"terminal:rename": (payload) => {
|
|
7355
7484
|
const p = obj(payload, "payload");
|
|
7356
7485
|
const key = str(p.sessionKey, "sessionKey");
|
|
@@ -7389,7 +7518,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7389
7518
|
"claude:sessionExists": (taskId) => {
|
|
7390
7519
|
const task = requireTask(taskId);
|
|
7391
7520
|
if (!task.worktreePath) return false;
|
|
7392
|
-
return
|
|
7521
|
+
return fs21.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
|
|
7393
7522
|
},
|
|
7394
7523
|
"sessions:list": async () => {
|
|
7395
7524
|
const keys = await sessions.list();
|
|
@@ -7436,7 +7565,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7436
7565
|
const task = requireTask(taskId);
|
|
7437
7566
|
if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
|
|
7438
7567
|
const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
|
|
7439
|
-
if (!
|
|
7568
|
+
if (!fs21.existsSync(dir)) return "artifacts directory does not exist yet";
|
|
7440
7569
|
return getPlatform().openPath(dir);
|
|
7441
7570
|
},
|
|
7442
7571
|
"task:getSpecs": async (taskId) => {
|
|
@@ -7571,7 +7700,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7571
7700
|
}
|
|
7572
7701
|
await removeTaskWorktree(task);
|
|
7573
7702
|
if (task.workspacePath) {
|
|
7574
|
-
|
|
7703
|
+
fs21.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
|
|
7575
7704
|
}
|
|
7576
7705
|
clearConversation(task.id);
|
|
7577
7706
|
removeTask(task.id);
|
|
@@ -7624,9 +7753,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7624
7753
|
// Watch the containing directory, not the file: JsonStore renames a temp
|
|
7625
7754
|
// file over the target and so replaces the inode on every save.
|
|
7626
7755
|
watchStore() {
|
|
7627
|
-
const file =
|
|
7756
|
+
const file = path24.basename(getStorePath());
|
|
7628
7757
|
let debounce = null;
|
|
7629
|
-
storeWatcher =
|
|
7758
|
+
storeWatcher = fs21.watch(path24.dirname(getStorePath()), (_event, filename) => {
|
|
7630
7759
|
if (filename !== file) return;
|
|
7631
7760
|
if (debounce) clearTimeout(debounce);
|
|
7632
7761
|
debounce = setTimeout(() => {
|
|
@@ -7663,9 +7792,9 @@ function createSessionBackend(sink, choice) {
|
|
|
7663
7792
|
}
|
|
7664
7793
|
|
|
7665
7794
|
// packages/core/src/web-server.ts
|
|
7666
|
-
import
|
|
7795
|
+
import fs22 from "fs";
|
|
7667
7796
|
import http from "http";
|
|
7668
|
-
import
|
|
7797
|
+
import path25 from "path";
|
|
7669
7798
|
import { timingSafeEqual } from "crypto";
|
|
7670
7799
|
import { WebSocketServer } from "ws";
|
|
7671
7800
|
var TOKEN_COOKIE = "vf_token";
|
|
@@ -7720,14 +7849,14 @@ function resolveStatic(root, urlPath) {
|
|
|
7720
7849
|
return null;
|
|
7721
7850
|
}
|
|
7722
7851
|
if (decoded.includes("\0")) return null;
|
|
7723
|
-
const rel =
|
|
7724
|
-
const base2 =
|
|
7725
|
-
const candidates = [rel,
|
|
7852
|
+
const rel = path25.posix.normalize(decoded).replace(/^\/+/, "");
|
|
7853
|
+
const base2 = path25.resolve(root);
|
|
7854
|
+
const candidates = [rel, path25.posix.join(rel, "index.html"), `${rel}.html`];
|
|
7726
7855
|
for (const c of candidates) {
|
|
7727
|
-
const full =
|
|
7728
|
-
if (full !== base2 && !full.startsWith(base2 +
|
|
7856
|
+
const full = path25.resolve(base2, c);
|
|
7857
|
+
if (full !== base2 && !full.startsWith(base2 + path25.sep)) return null;
|
|
7729
7858
|
try {
|
|
7730
|
-
if (
|
|
7859
|
+
if (fs22.statSync(full).isFile()) return full;
|
|
7731
7860
|
} catch {
|
|
7732
7861
|
}
|
|
7733
7862
|
}
|
|
@@ -7788,7 +7917,7 @@ async function startWebServer(options) {
|
|
|
7788
7917
|
send(res, 404, "Not found");
|
|
7789
7918
|
return;
|
|
7790
7919
|
}
|
|
7791
|
-
const ext =
|
|
7920
|
+
const ext = path25.extname(file).toLowerCase();
|
|
7792
7921
|
const immutable = url.pathname.startsWith("/_next/static/");
|
|
7793
7922
|
res.writeHead(200, {
|
|
7794
7923
|
"Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
|
|
@@ -7801,7 +7930,7 @@ async function startWebServer(options) {
|
|
|
7801
7930
|
res.end();
|
|
7802
7931
|
return;
|
|
7803
7932
|
}
|
|
7804
|
-
|
|
7933
|
+
fs22.createReadStream(file).pipe(res);
|
|
7805
7934
|
}
|
|
7806
7935
|
const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
|
|
7807
7936
|
function reject(socket, status, reason) {
|
|
@@ -7900,8 +8029,8 @@ Content-Length: 0\r
|
|
|
7900
8029
|
}
|
|
7901
8030
|
|
|
7902
8031
|
// packages/cli/src/update-command.ts
|
|
7903
|
-
import
|
|
7904
|
-
import
|
|
8032
|
+
import fs23 from "node:fs";
|
|
8033
|
+
import path26 from "node:path";
|
|
7905
8034
|
import { spawnSync } from "node:child_process";
|
|
7906
8035
|
function npmRunner(args, inherit) {
|
|
7907
8036
|
const spawnOpts = {
|
|
@@ -7913,7 +8042,7 @@ function npmRunner(args, inherit) {
|
|
|
7913
8042
|
}
|
|
7914
8043
|
function packageName(packageRoot) {
|
|
7915
8044
|
try {
|
|
7916
|
-
const pkg = JSON.parse(
|
|
8045
|
+
const pkg = JSON.parse(fs23.readFileSync(path26.join(packageRoot, "package.json"), "utf8"));
|
|
7917
8046
|
if (typeof pkg.name !== "string") return null;
|
|
7918
8047
|
return /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i.test(pkg.name) ? pkg.name : null;
|
|
7919
8048
|
} catch {
|
|
@@ -7928,7 +8057,7 @@ function runUpdate(options) {
|
|
|
7928
8057
|
error("vibeflow update \u53EA\u9069\u7528\u65BC\u5168\u57DF npm \u5B89\u88DD\uFF1B\u76EE\u524D\u662F\u539F\u59CB\u78BC\u7248\u672C\u3002");
|
|
7929
8058
|
return 1;
|
|
7930
8059
|
}
|
|
7931
|
-
const packageRoot =
|
|
8060
|
+
const packageRoot = path26.dirname(path26.dirname(options.cliEntry));
|
|
7932
8061
|
const name = packageName(packageRoot);
|
|
7933
8062
|
if (!name) {
|
|
7934
8063
|
error("\u7121\u6CD5\u8FA8\u8B58\u76EE\u524D VibeFlow \u7684 npm \u5957\u4EF6\u540D\u7A31\uFF0C\u5DF2\u53D6\u6D88\u66F4\u65B0\u3002");
|
|
@@ -7940,8 +8069,8 @@ function runUpdate(options) {
|
|
|
7940
8069
|
return 1;
|
|
7941
8070
|
}
|
|
7942
8071
|
try {
|
|
7943
|
-
const globalPackage =
|
|
7944
|
-
if (
|
|
8072
|
+
const globalPackage = path26.join(String(root.stdout).trim(), ...name.split("/"));
|
|
8073
|
+
if (fs23.lstatSync(globalPackage).isSymbolicLink() || fs23.realpathSync(globalPackage) !== fs23.realpathSync(packageRoot)) {
|
|
7945
8074
|
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");
|
|
7946
8075
|
return 1;
|
|
7947
8076
|
}
|
|
@@ -8086,8 +8215,8 @@ async function openUpdateTerminal(platform = process.platform, run3 = launch) {
|
|
|
8086
8215
|
}
|
|
8087
8216
|
|
|
8088
8217
|
// packages/cli/src/self-update.ts
|
|
8089
|
-
import
|
|
8090
|
-
import
|
|
8218
|
+
import fs24 from "node:fs";
|
|
8219
|
+
import path27 from "node:path";
|
|
8091
8220
|
import { execFile as execFile10 } from "node:child_process";
|
|
8092
8221
|
import { promisify as promisify7 } from "node:util";
|
|
8093
8222
|
var execFileAsync2 = promisify7(execFile10);
|
|
@@ -8096,7 +8225,7 @@ var PROBE_RETRY_MS = 3e4;
|
|
|
8096
8225
|
var HOST_TOKEN_ENV = "VIBEFLOW_HOST_TOKEN";
|
|
8097
8226
|
function readInstalledVersion(packageRoot) {
|
|
8098
8227
|
try {
|
|
8099
|
-
const pkg = JSON.parse(
|
|
8228
|
+
const pkg = JSON.parse(fs24.readFileSync(path27.join(packageRoot, "package.json"), "utf8"));
|
|
8100
8229
|
return typeof pkg.version === "string" ? pkg.version : null;
|
|
8101
8230
|
} catch {
|
|
8102
8231
|
return null;
|
|
@@ -8192,7 +8321,7 @@ async function startHost(options) {
|
|
|
8192
8321
|
const core = createCore({ sessions, bus, version: options.version });
|
|
8193
8322
|
let updateTimer = null;
|
|
8194
8323
|
let installTimer = null;
|
|
8195
|
-
const packageRoot = options.cliEntry ?
|
|
8324
|
+
const packageRoot = options.cliEntry ? path28.dirname(path28.dirname(options.cliEntry)) : null;
|
|
8196
8325
|
const updates = createUpdateMonitor({
|
|
8197
8326
|
packageName: options.sourceRoot || !packageRoot ? null : packageName(packageRoot),
|
|
8198
8327
|
currentVersion: options.version,
|
|
@@ -8280,7 +8409,7 @@ function loginUrl(lock, pathname = "/home/") {
|
|
|
8280
8409
|
return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
|
|
8281
8410
|
}
|
|
8282
8411
|
function isWebDir(dir) {
|
|
8283
|
-
return
|
|
8412
|
+
return fs25.existsSync(path28.join(dir, "home", "index.html"));
|
|
8284
8413
|
}
|
|
8285
8414
|
|
|
8286
8415
|
// packages/cli/src/remote.ts
|
|
@@ -8397,8 +8526,8 @@ function connectToHost(lock) {
|
|
|
8397
8526
|
}
|
|
8398
8527
|
|
|
8399
8528
|
// packages/cli/src/doctor.ts
|
|
8400
|
-
import
|
|
8401
|
-
import
|
|
8529
|
+
import fs26 from "fs";
|
|
8530
|
+
import path29 from "path";
|
|
8402
8531
|
import { execFile as execFile11 } from "child_process";
|
|
8403
8532
|
import { promisify as promisify8 } from "util";
|
|
8404
8533
|
init_env();
|
|
@@ -8538,10 +8667,10 @@ async function runDoctor(options) {
|
|
|
8538
8667
|
});
|
|
8539
8668
|
}
|
|
8540
8669
|
try {
|
|
8541
|
-
|
|
8542
|
-
const probe =
|
|
8543
|
-
|
|
8544
|
-
|
|
8670
|
+
fs26.mkdirSync(options.userDataDir, { recursive: true });
|
|
8671
|
+
const probe = path29.join(options.userDataDir, `.doctor-${process.pid}`);
|
|
8672
|
+
fs26.writeFileSync(probe, "");
|
|
8673
|
+
fs26.rmSync(probe);
|
|
8545
8674
|
checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
|
|
8546
8675
|
} catch (err) {
|
|
8547
8676
|
checks.push({
|
|
@@ -8595,25 +8724,25 @@ Options:
|
|
|
8595
8724
|
-h, --help Show this help
|
|
8596
8725
|
`.trim();
|
|
8597
8726
|
function detectLayout() {
|
|
8598
|
-
const file =
|
|
8599
|
-
const here =
|
|
8600
|
-
const bundledWeb =
|
|
8601
|
-
if (isWebDir(bundledWeb) ||
|
|
8727
|
+
const file = fileURLToPath2(import.meta.url);
|
|
8728
|
+
const here = path32.dirname(file);
|
|
8729
|
+
const bundledWeb = path32.join(here, "web");
|
|
8730
|
+
if (isWebDir(bundledWeb) || path32.basename(here) === "dist") {
|
|
8602
8731
|
return {
|
|
8603
|
-
version: readVersion(
|
|
8732
|
+
version: readVersion(path32.join(here, "..", "package.json")),
|
|
8604
8733
|
webDir: isWebDir(bundledWeb) ? bundledWeb : null,
|
|
8605
8734
|
sourceRoot: null,
|
|
8606
8735
|
cliEntry: file
|
|
8607
8736
|
};
|
|
8608
8737
|
}
|
|
8609
|
-
const root =
|
|
8738
|
+
const root = path32.resolve(here, "..", "..", "..");
|
|
8610
8739
|
const override = process.env.VIBEFLOW_WEB_DIR;
|
|
8611
|
-
const webDir = override && isWebDir(override) ? override : isWebDir(
|
|
8612
|
-
return { version: readVersion(
|
|
8740
|
+
const webDir = override && isWebDir(override) ? override : isWebDir(path32.join(root, "app")) ? path32.join(root, "app") : null;
|
|
8741
|
+
return { version: readVersion(path32.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
|
|
8613
8742
|
}
|
|
8614
8743
|
function readVersion(pkgPath) {
|
|
8615
8744
|
try {
|
|
8616
|
-
return JSON.parse(
|
|
8745
|
+
return JSON.parse(fs28.readFileSync(pkgPath, "utf8")).version;
|
|
8617
8746
|
} catch {
|
|
8618
8747
|
return "0.0.0";
|
|
8619
8748
|
}
|
|
@@ -8851,8 +8980,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
|
|
|
8851
8980
|
console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
|
|
8852
8981
|
return 1;
|
|
8853
8982
|
}
|
|
8854
|
-
const dir =
|
|
8855
|
-
if (!
|
|
8983
|
+
const dir = path32.resolve(target);
|
|
8984
|
+
if (!fs28.existsSync(dir) || !fs28.statSync(dir).isDirectory()) {
|
|
8856
8985
|
console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
|
|
8857
8986
|
return 1;
|
|
8858
8987
|
}
|