@kw-yeh/vibeflow 4.11.28 → 4.11.30
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 +273 -154
- package/dist/web/404/index.html +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/_next/static/{Zo3ZVJ1bFZjYU3Bt-ZmLO → BTRiVzJ-AA12GlLg1YtO3}/_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-ad6be2a298405ba4.js} +1 -1
- package/dist/web/_next/static/chunks/pages/home-1bb9109f75a2f42d.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-a8c6396c4ddf261a.js +0 -13
- package/dist/web/_next/static/chunks/webpack-30f420d055cbcd0c.js +0 -1
- /package/dist/web/_next/static/{Zo3ZVJ1bFZjYU3Bt-ZmLO → BTRiVzJ-AA12GlLg1YtO3}/_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,8 @@ 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
|
+
openFile: (sessionKey, path33) => t.invoke("terminal:openFile", { sessionKey, path: path33 }),
|
|
2604
2606
|
/**
|
|
2605
2607
|
* Start a terminal for a task: its agent when `launch` is given, else an
|
|
2606
2608
|
* interactive shell. Core resolves the cwd and builds the command.
|
|
@@ -4714,8 +4716,8 @@ async function taskCommand(argv) {
|
|
|
4714
4716
|
}
|
|
4715
4717
|
|
|
4716
4718
|
// packages/cli/src/host.ts
|
|
4717
|
-
import
|
|
4718
|
-
import
|
|
4719
|
+
import fs25 from "fs";
|
|
4720
|
+
import path28 from "path";
|
|
4719
4721
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
4720
4722
|
|
|
4721
4723
|
// packages/core/src/events.ts
|
|
@@ -5122,21 +5124,98 @@ async function syncBuiltinsAtStartup() {
|
|
|
5122
5124
|
}
|
|
5123
5125
|
|
|
5124
5126
|
// packages/core/src/service.ts
|
|
5125
|
-
import
|
|
5126
|
-
|
|
5127
|
-
|
|
5127
|
+
import fs21 from "fs";
|
|
5128
|
+
|
|
5129
|
+
// packages/core/src/terminal-files.ts
|
|
5130
|
+
import { constants } from "fs";
|
|
5131
|
+
import fs13 from "fs/promises";
|
|
5132
|
+
import os3 from "os";
|
|
5133
|
+
import path16 from "path";
|
|
5134
|
+
import { fileURLToPath } from "url";
|
|
5135
|
+
var MAX_PREVIEW_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
5136
|
+
var MAX_TEXT_BYTES2 = 256 * 1024;
|
|
5137
|
+
var IMAGE_MIME2 = {
|
|
5138
|
+
".png": "image/png",
|
|
5139
|
+
".jpg": "image/jpeg",
|
|
5140
|
+
".jpeg": "image/jpeg",
|
|
5141
|
+
".gif": "image/gif",
|
|
5142
|
+
".webp": "image/webp",
|
|
5143
|
+
".avif": "image/avif",
|
|
5144
|
+
".bmp": "image/bmp",
|
|
5145
|
+
".svg": "image/svg+xml"
|
|
5146
|
+
};
|
|
5147
|
+
function resolveTerminalFile(cwd, input) {
|
|
5148
|
+
if (!input || input.includes("\0") || input.length > 32768) throw new Error("\u6A94\u6848\u8DEF\u5F91\u7121\u6548\u3002");
|
|
5149
|
+
let target = input;
|
|
5150
|
+
if (/^file:/i.test(target)) {
|
|
5151
|
+
const url = new URL(target);
|
|
5152
|
+
if (url.hostname && url.hostname !== "localhost") throw new Error("\u53EA\u652F\u63F4\u672C\u6A5F\u6A94\u6848\u3002");
|
|
5153
|
+
target = fileURLToPath(url);
|
|
5154
|
+
} else if (/^[a-z][a-z\d+.-]*:/i.test(target) && !/^[a-z]:[\\/]/i.test(target)) {
|
|
5155
|
+
throw new Error("\u53EA\u652F\u63F4\u672C\u6A5F\u6A94\u6848\u8DEF\u5F91\u3002");
|
|
5156
|
+
}
|
|
5157
|
+
if (target.startsWith("~/") || target.startsWith("~\\")) target = path16.join(os3.homedir(), target.slice(2));
|
|
5158
|
+
if (target.includes("\0")) throw new Error("\u6A94\u6848\u8DEF\u5F91\u7121\u6548\u3002");
|
|
5159
|
+
return path16.resolve(cwd, target);
|
|
5160
|
+
}
|
|
5161
|
+
async function readTerminalFile(cwd, input, metadataOnly = false) {
|
|
5162
|
+
let target;
|
|
5163
|
+
try {
|
|
5164
|
+
target = resolveTerminalFile(cwd, input);
|
|
5165
|
+
} catch (error) {
|
|
5166
|
+
return { ok: false, error: "invalid_path", message: error instanceof Error ? error.message : "\u6A94\u6848\u8DEF\u5F91\u7121\u6548\u3002" };
|
|
5167
|
+
}
|
|
5168
|
+
let handle;
|
|
5169
|
+
try {
|
|
5170
|
+
if (!(await fs13.stat(target)).isFile()) return { ok: false, error: "not_file", message: "\u9019\u500B\u8DEF\u5F91\u4E0D\u662F\u4E00\u822C\u6A94\u6848\u3002" };
|
|
5171
|
+
handle = await fs13.open(target, constants.O_RDONLY | constants.O_NONBLOCK);
|
|
5172
|
+
const stat = await handle.stat();
|
|
5173
|
+
if (!stat.isFile()) return { ok: false, error: "not_file", message: "\u9019\u500B\u8DEF\u5F91\u4E0D\u662F\u4E00\u822C\u6A94\u6848\u3002" };
|
|
5174
|
+
if (metadataOnly) return { ok: true, path: target, kind: "binary", reason: "" };
|
|
5175
|
+
const mime = IMAGE_MIME2[path16.extname(target).toLowerCase()];
|
|
5176
|
+
if (mime && stat.size > MAX_PREVIEW_IMAGE_BYTES) {
|
|
5177
|
+
return { ok: true, path: target, kind: "binary", reason: "\u5716\u7247\u8D85\u904E 20 MB \u9810\u89BD\u4E0A\u9650\u3002" };
|
|
5178
|
+
}
|
|
5179
|
+
const limit = mime ? MAX_PREVIEW_IMAGE_BYTES : MAX_TEXT_BYTES2;
|
|
5180
|
+
const buffer = Buffer.alloc(Math.min(stat.size, limit + 1));
|
|
5181
|
+
let bytes = 0;
|
|
5182
|
+
while (bytes < buffer.length) {
|
|
5183
|
+
const { bytesRead } = await handle.read(buffer, bytes, buffer.length - bytes, bytes);
|
|
5184
|
+
if (!bytesRead) break;
|
|
5185
|
+
bytes += bytesRead;
|
|
5186
|
+
}
|
|
5187
|
+
if (mime) {
|
|
5188
|
+
if (bytes > limit) return { ok: true, path: target, kind: "binary", reason: "\u5716\u7247\u8D85\u904E 20 MB \u9810\u89BD\u4E0A\u9650\u3002" };
|
|
5189
|
+
return { ok: true, path: target, kind: "image", dataUrl: `data:${mime};base64,${buffer.subarray(0, bytes).toString("base64")}` };
|
|
5190
|
+
}
|
|
5191
|
+
const sample = buffer.subarray(0, Math.min(bytes, 8192));
|
|
5192
|
+
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))) {
|
|
5193
|
+
return { ok: true, path: target, kind: "binary", reason: "\u6B64\u683C\u5F0F\u7121\u6CD5\u5728 VibeFlow \u5167\u9810\u89BD\u3002" };
|
|
5194
|
+
}
|
|
5195
|
+
return { ok: true, path: target, kind: "text", text: buffer.subarray(0, Math.min(bytes, limit)).toString("utf8"), truncated: bytes > limit };
|
|
5196
|
+
} catch (error) {
|
|
5197
|
+
const code2 = error.code;
|
|
5198
|
+
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" };
|
|
5199
|
+
} finally {
|
|
5200
|
+
await handle?.close();
|
|
5201
|
+
}
|
|
5202
|
+
}
|
|
5203
|
+
|
|
5204
|
+
// packages/core/src/service.ts
|
|
5205
|
+
import os8 from "os";
|
|
5206
|
+
import path24 from "path";
|
|
5128
5207
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
5129
5208
|
|
|
5130
5209
|
// packages/core/src/agent-models.ts
|
|
5131
|
-
import
|
|
5132
|
-
import
|
|
5133
|
-
import
|
|
5210
|
+
import fs15 from "fs";
|
|
5211
|
+
import os6 from "os";
|
|
5212
|
+
import path18 from "path";
|
|
5134
5213
|
|
|
5135
5214
|
// packages/core/src/claude-models.ts
|
|
5136
5215
|
init_effort();
|
|
5137
|
-
import
|
|
5138
|
-
import
|
|
5139
|
-
import
|
|
5216
|
+
import fs14 from "fs";
|
|
5217
|
+
import os4 from "os";
|
|
5218
|
+
import path17 from "path";
|
|
5140
5219
|
|
|
5141
5220
|
// packages/core/src/json-lines-process.ts
|
|
5142
5221
|
import { spawn as spawn4 } from "child_process";
|
|
@@ -5240,11 +5319,11 @@ var CLAUDE_MODELS_TIMEOUT_MS = 15e3;
|
|
|
5240
5319
|
var REQUEST_ID = "vibeflow-models";
|
|
5241
5320
|
var CLAUDE_ARGS = ["-p", "--input-format", "stream-json", "--output-format", "stream-json", "--verbose"];
|
|
5242
5321
|
function openClaude() {
|
|
5243
|
-
const cwd =
|
|
5322
|
+
const cwd = fs14.mkdtempSync(path17.join(os4.tmpdir(), "vf-claude-models-"));
|
|
5244
5323
|
return spawnJsonLines("claude", CLAUDE_ARGS, {
|
|
5245
5324
|
cwd,
|
|
5246
5325
|
timeoutMs: CLAUDE_MODELS_TIMEOUT_MS,
|
|
5247
|
-
onExit: () =>
|
|
5326
|
+
onExit: () => fs14.rmSync(cwd, { recursive: true, force: true })
|
|
5248
5327
|
});
|
|
5249
5328
|
}
|
|
5250
5329
|
function isRecord(value) {
|
|
@@ -5291,7 +5370,7 @@ async function fetchClaudeModels(open = openClaude) {
|
|
|
5291
5370
|
// packages/core/src/codex-models.ts
|
|
5292
5371
|
init_effort();
|
|
5293
5372
|
init_env();
|
|
5294
|
-
import
|
|
5373
|
+
import os5 from "os";
|
|
5295
5374
|
var CODEX_MODELS_TIMEOUT_MS = 2e4;
|
|
5296
5375
|
var MAX_PAGES = 20;
|
|
5297
5376
|
var CodexLoginRequiredError = class extends Error {
|
|
@@ -5304,7 +5383,7 @@ function codexAppServerCommand() {
|
|
|
5304
5383
|
return {
|
|
5305
5384
|
bin: "codex",
|
|
5306
5385
|
args: ["app-server", "--listen", "stdio://"],
|
|
5307
|
-
cwd:
|
|
5386
|
+
cwd: os5.homedir(),
|
|
5308
5387
|
env: execEnv()
|
|
5309
5388
|
};
|
|
5310
5389
|
}
|
|
@@ -5393,7 +5472,7 @@ function builtinModels(id) {
|
|
|
5393
5472
|
}
|
|
5394
5473
|
function defaultCodexHome() {
|
|
5395
5474
|
const configured = process.env.CODEX_HOME?.trim();
|
|
5396
|
-
return configured ? configured :
|
|
5475
|
+
return configured ? configured : path18.join(os6.homedir(), ".codex");
|
|
5397
5476
|
}
|
|
5398
5477
|
function parseCodexCatalog(raw) {
|
|
5399
5478
|
const body = JSON.parse(raw);
|
|
@@ -5419,9 +5498,9 @@ function errorMessage(err) {
|
|
|
5419
5498
|
return err instanceof Error ? err.message : String(err);
|
|
5420
5499
|
}
|
|
5421
5500
|
async function readCodexCache(codexHome) {
|
|
5422
|
-
const cachePath =
|
|
5501
|
+
const cachePath = path18.join(codexHome, "models_cache.json");
|
|
5423
5502
|
try {
|
|
5424
|
-
const raw = await
|
|
5503
|
+
const raw = await fs15.promises.readFile(cachePath, "utf8");
|
|
5425
5504
|
return { models: parseCodexCatalog(raw), source: "codex-cache" };
|
|
5426
5505
|
} catch (err) {
|
|
5427
5506
|
const missing = err.code === "ENOENT";
|
|
@@ -5543,11 +5622,11 @@ function effortDescription(level) {
|
|
|
5543
5622
|
}
|
|
5544
5623
|
|
|
5545
5624
|
// packages/core/src/jev-credentials.ts
|
|
5546
|
-
import
|
|
5547
|
-
import
|
|
5625
|
+
import fs16 from "fs";
|
|
5626
|
+
import path19 from "path";
|
|
5548
5627
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5549
5628
|
function keyPath(dir) {
|
|
5550
|
-
return
|
|
5629
|
+
return path19.join(dir, "jev-api-key");
|
|
5551
5630
|
}
|
|
5552
5631
|
function dataDir() {
|
|
5553
5632
|
return getPlatform().userDataDir();
|
|
@@ -5555,8 +5634,8 @@ function dataDir() {
|
|
|
5555
5634
|
function readJevApiKey(dir = dataDir()) {
|
|
5556
5635
|
try {
|
|
5557
5636
|
const file = keyPath(dir);
|
|
5558
|
-
if (
|
|
5559
|
-
const saved =
|
|
5637
|
+
if (fs16.lstatSync(file).isSymbolicLink()) throw new Error("Jev key file must not be a symlink");
|
|
5638
|
+
const saved = fs16.readFileSync(file, "utf8").trim();
|
|
5560
5639
|
if (saved) return saved;
|
|
5561
5640
|
} catch (err) {
|
|
5562
5641
|
if (err.code !== "ENOENT") throw err;
|
|
@@ -5566,8 +5645,8 @@ function readJevApiKey(dir = dataDir()) {
|
|
|
5566
5645
|
function jevKeyStatus(dir = dataDir()) {
|
|
5567
5646
|
try {
|
|
5568
5647
|
const file = keyPath(dir);
|
|
5569
|
-
if (
|
|
5570
|
-
if (
|
|
5648
|
+
if (fs16.lstatSync(file).isSymbolicLink()) throw new Error("Jev key file must not be a symlink");
|
|
5649
|
+
if (fs16.readFileSync(file, "utf8").trim()) return { configured: true, source: "saved" };
|
|
5571
5650
|
} catch (err) {
|
|
5572
5651
|
if (err.code !== "ENOENT") throw err;
|
|
5573
5652
|
}
|
|
@@ -5578,20 +5657,20 @@ function saveJevApiKey(value, dir = dataDir()) {
|
|
|
5578
5657
|
if (!key || key.length > 4096 || !/^[\x21-\x7e]+$/.test(key)) {
|
|
5579
5658
|
throw new Error("Jev API key \u683C\u5F0F\u7121\u6548");
|
|
5580
5659
|
}
|
|
5581
|
-
|
|
5660
|
+
fs16.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
5582
5661
|
const file = keyPath(dir);
|
|
5583
5662
|
const tmp = `${file}.${randomUUID3()}.tmp`;
|
|
5584
5663
|
try {
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
if (process.platform !== "win32")
|
|
5664
|
+
fs16.writeFileSync(tmp, key, { mode: 384, flag: "wx" });
|
|
5665
|
+
fs16.renameSync(tmp, file);
|
|
5666
|
+
if (process.platform !== "win32") fs16.chmodSync(file, 384);
|
|
5588
5667
|
} finally {
|
|
5589
|
-
|
|
5668
|
+
fs16.rmSync(tmp, { force: true });
|
|
5590
5669
|
}
|
|
5591
5670
|
return { configured: true, source: "saved" };
|
|
5592
5671
|
}
|
|
5593
5672
|
function removeJevApiKey(dir = dataDir()) {
|
|
5594
|
-
|
|
5673
|
+
fs16.rmSync(keyPath(dir), { force: true });
|
|
5595
5674
|
return jevKeyStatus(dir);
|
|
5596
5675
|
}
|
|
5597
5676
|
|
|
@@ -6103,45 +6182,45 @@ function createJiraService(options) {
|
|
|
6103
6182
|
}
|
|
6104
6183
|
|
|
6105
6184
|
// packages/core/src/board-cli.ts
|
|
6106
|
-
import
|
|
6107
|
-
import
|
|
6185
|
+
import fs17 from "fs";
|
|
6186
|
+
import path20 from "path";
|
|
6108
6187
|
import { pathToFileURL } from "url";
|
|
6109
6188
|
async function boardCliLaunchInfo() {
|
|
6110
|
-
const storeDir =
|
|
6189
|
+
const storeDir = path20.dirname(getStorePath());
|
|
6111
6190
|
const entry = getPlatform().cliEntry?.();
|
|
6112
|
-
if (entry &&
|
|
6191
|
+
if (entry && fs17.existsSync(entry)) return { storeDir, cliPath: entry };
|
|
6113
6192
|
const root = getPlatform().sourceRoot();
|
|
6114
6193
|
if (!root) return { storeDir };
|
|
6115
|
-
const cliPath =
|
|
6116
|
-
const loaderPath =
|
|
6117
|
-
if (!
|
|
6194
|
+
const cliPath = path20.join(root, "scripts", "vibeflow.mjs");
|
|
6195
|
+
const loaderPath = path20.join(root, "test", "support", "register.mjs");
|
|
6196
|
+
if (!fs17.existsSync(cliPath) || !fs17.existsSync(loaderPath)) return { storeDir };
|
|
6118
6197
|
return { storeDir, cliPath, loaderPath: pathToFileURL(loaderPath).href };
|
|
6119
6198
|
}
|
|
6120
6199
|
|
|
6121
6200
|
// packages/core/src/decisions.ts
|
|
6122
|
-
import
|
|
6123
|
-
import
|
|
6201
|
+
import fs18 from "fs";
|
|
6202
|
+
import path21 from "path";
|
|
6124
6203
|
var DECISIONS_FILE_SUFFIX = ".DECISIONS.md";
|
|
6125
6204
|
function decisionsKey(worktreePath, branch) {
|
|
6126
|
-
return worktreePath ?
|
|
6205
|
+
return worktreePath ? path21.basename(worktreePath) : branch.replace(/\//g, "-");
|
|
6127
6206
|
}
|
|
6128
6207
|
function decisionsPath(workspacePath, key) {
|
|
6129
|
-
return
|
|
6208
|
+
return path21.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
|
|
6130
6209
|
}
|
|
6131
6210
|
function deleteDecisions(workspacePath, key) {
|
|
6132
6211
|
try {
|
|
6133
|
-
|
|
6212
|
+
fs18.rmSync(decisionsPath(workspacePath, key), { force: true });
|
|
6134
6213
|
} catch {
|
|
6135
6214
|
}
|
|
6136
6215
|
}
|
|
6137
6216
|
|
|
6138
6217
|
// packages/core/src/specs.ts
|
|
6139
|
-
import
|
|
6140
|
-
import
|
|
6218
|
+
import fs19 from "fs";
|
|
6219
|
+
import path22 from "path";
|
|
6141
6220
|
var SPEC_FILE_NAME = "spec.md";
|
|
6142
6221
|
var MAX_SPEC_BYTES = 256 * 1024;
|
|
6143
6222
|
function isSpecPath(p) {
|
|
6144
|
-
return
|
|
6223
|
+
return path22.posix.basename(p) === SPEC_FILE_NAME;
|
|
6145
6224
|
}
|
|
6146
6225
|
function clip2(buf) {
|
|
6147
6226
|
return {
|
|
@@ -6156,10 +6235,10 @@ async function readWorktreeSpecs(worktreePath, baseBranch) {
|
|
|
6156
6235
|
for (const entry of entries) {
|
|
6157
6236
|
if (entry.status === "D" || !isSpecPath(entry.path)) continue;
|
|
6158
6237
|
try {
|
|
6159
|
-
const full =
|
|
6160
|
-
const stat =
|
|
6238
|
+
const full = path22.join(worktreePath, entry.path);
|
|
6239
|
+
const stat = fs19.statSync(full);
|
|
6161
6240
|
if (!stat.isFile()) continue;
|
|
6162
|
-
specs.push({ path: entry.path, ...clip2(
|
|
6241
|
+
specs.push({ path: entry.path, ...clip2(fs19.readFileSync(full)), updatedAt: stat.mtimeMs });
|
|
6163
6242
|
} catch {
|
|
6164
6243
|
}
|
|
6165
6244
|
}
|
|
@@ -6511,17 +6590,17 @@ init_launch();
|
|
|
6511
6590
|
// packages/core/src/progress-tracker.ts
|
|
6512
6591
|
init_progress();
|
|
6513
6592
|
init_launch();
|
|
6514
|
-
import
|
|
6515
|
-
import
|
|
6516
|
-
import
|
|
6593
|
+
import fs20 from "fs";
|
|
6594
|
+
import os7 from "os";
|
|
6595
|
+
import path23 from "path";
|
|
6517
6596
|
var SINCE_SLACK_MS = 5e3;
|
|
6518
6597
|
function claudeProjectDir(home, cwd) {
|
|
6519
|
-
return
|
|
6598
|
+
return path23.join(home, ".claude", "projects", cwd.replace(/[^a-zA-Z0-9]/g, "-"));
|
|
6520
6599
|
}
|
|
6521
6600
|
function pathAliases(p) {
|
|
6522
|
-
const resolved =
|
|
6601
|
+
const resolved = path23.resolve(p);
|
|
6523
6602
|
try {
|
|
6524
|
-
const real =
|
|
6603
|
+
const real = fs20.realpathSync(resolved);
|
|
6525
6604
|
return real === resolved ? [resolved] : [resolved, real];
|
|
6526
6605
|
} catch {
|
|
6527
6606
|
return [resolved];
|
|
@@ -6537,7 +6616,7 @@ function samePath(a, b) {
|
|
|
6537
6616
|
}
|
|
6538
6617
|
function listFiles(dir, test) {
|
|
6539
6618
|
try {
|
|
6540
|
-
return
|
|
6619
|
+
return fs20.readdirSync(dir).filter(test).map((name) => path23.join(dir, name));
|
|
6541
6620
|
} catch {
|
|
6542
6621
|
return [];
|
|
6543
6622
|
}
|
|
@@ -6545,22 +6624,22 @@ function listFiles(dir, test) {
|
|
|
6545
6624
|
function firstLine(file) {
|
|
6546
6625
|
let fd;
|
|
6547
6626
|
try {
|
|
6548
|
-
fd =
|
|
6627
|
+
fd = fs20.openSync(file, "r");
|
|
6549
6628
|
const buf = Buffer.alloc(64 * 1024);
|
|
6550
|
-
const n =
|
|
6629
|
+
const n = fs20.readSync(fd, buf, 0, buf.length, 0);
|
|
6551
6630
|
const text = buf.subarray(0, n).toString("utf8");
|
|
6552
6631
|
const nl = text.indexOf("\n");
|
|
6553
6632
|
return nl === -1 ? null : text.slice(0, nl);
|
|
6554
6633
|
} catch {
|
|
6555
6634
|
return null;
|
|
6556
6635
|
} finally {
|
|
6557
|
-
if (fd !== void 0)
|
|
6636
|
+
if (fd !== void 0) fs20.closeSync(fd);
|
|
6558
6637
|
}
|
|
6559
6638
|
}
|
|
6560
6639
|
function advance(file, cursor, reduce, initial) {
|
|
6561
6640
|
let size;
|
|
6562
6641
|
try {
|
|
6563
|
-
size =
|
|
6642
|
+
size = fs20.statSync(file).size;
|
|
6564
6643
|
} catch {
|
|
6565
6644
|
return;
|
|
6566
6645
|
}
|
|
@@ -6572,9 +6651,9 @@ function advance(file, cursor, reduce, initial) {
|
|
|
6572
6651
|
if (size === cursor.offset) return;
|
|
6573
6652
|
let fd;
|
|
6574
6653
|
try {
|
|
6575
|
-
fd =
|
|
6654
|
+
fd = fs20.openSync(file, "r");
|
|
6576
6655
|
const chunk = Buffer.alloc(size - cursor.offset);
|
|
6577
|
-
const n =
|
|
6656
|
+
const n = fs20.readSync(fd, chunk, 0, chunk.length, cursor.offset);
|
|
6578
6657
|
cursor.offset += n;
|
|
6579
6658
|
const data = Buffer.concat([cursor.rest, chunk.subarray(0, n)]);
|
|
6580
6659
|
const nl = data.lastIndexOf(10);
|
|
@@ -6588,7 +6667,7 @@ function advance(file, cursor, reduce, initial) {
|
|
|
6588
6667
|
}
|
|
6589
6668
|
} catch {
|
|
6590
6669
|
} finally {
|
|
6591
|
-
if (fd !== void 0)
|
|
6670
|
+
if (fd !== void 0) fs20.closeSync(fd);
|
|
6592
6671
|
}
|
|
6593
6672
|
}
|
|
6594
6673
|
function newCursor(state) {
|
|
@@ -6607,14 +6686,14 @@ function codexDayDirs(sessionsRoot, since) {
|
|
|
6607
6686
|
const cur = new Date(start.getFullYear(), start.getMonth(), start.getDate());
|
|
6608
6687
|
while (cur <= end) {
|
|
6609
6688
|
const pad = (n) => String(n).padStart(2, "0");
|
|
6610
|
-
days.push(
|
|
6689
|
+
days.push(path23.join(sessionsRoot, String(cur.getFullYear()), pad(cur.getMonth() + 1), pad(cur.getDate())));
|
|
6611
6690
|
cur.setDate(cur.getDate() + 1);
|
|
6612
6691
|
}
|
|
6613
6692
|
return days;
|
|
6614
6693
|
}
|
|
6615
6694
|
function createProgressTracker(opts) {
|
|
6616
|
-
const home = opts.homeDir ??
|
|
6617
|
-
const codexHomes = opts.codexHomes ?? (() => [
|
|
6695
|
+
const home = opts.homeDir ?? os7.homedir();
|
|
6696
|
+
const codexHomes = opts.codexHomes ?? (() => [path23.join(home, ".codex")]);
|
|
6618
6697
|
const pollMs = opts.pollMs ?? 2e3;
|
|
6619
6698
|
const entries = /* @__PURE__ */ new Map();
|
|
6620
6699
|
const belongsToRun = (target, startedAt) => target.since == null || startedAt != null && startedAt >= target.since - SINCE_SLACK_MS;
|
|
@@ -6623,7 +6702,7 @@ function createProgressTracker(opts) {
|
|
|
6623
6702
|
const dirs = pathAliases(target.worktreePath).map((p) => claudeProjectDir(home, p));
|
|
6624
6703
|
for (const file of dirs.flatMap((dir) => listFiles(dir, (n) => n.endsWith(".jsonl")))) {
|
|
6625
6704
|
if (entry.claude.has(file) || entry.rejected.has(file)) continue;
|
|
6626
|
-
const session =
|
|
6705
|
+
const session = path23.basename(file, ".jsonl");
|
|
6627
6706
|
if (session !== target.sessionId) {
|
|
6628
6707
|
const head = firstLine(file);
|
|
6629
6708
|
if (head == null) continue;
|
|
@@ -6636,8 +6715,8 @@ function createProgressTracker(opts) {
|
|
|
6636
6715
|
entry.claude.set(file, newCursor(initialClaudeState()));
|
|
6637
6716
|
}
|
|
6638
6717
|
for (const file of Array.from(entry.claude.keys())) {
|
|
6639
|
-
if (file.includes(`${
|
|
6640
|
-
const sub =
|
|
6718
|
+
if (file.includes(`${path23.sep}subagents${path23.sep}`)) continue;
|
|
6719
|
+
const sub = path23.join(file.slice(0, -".jsonl".length), "subagents");
|
|
6641
6720
|
for (const f of listFiles(sub, (n) => n.endsWith(".jsonl"))) {
|
|
6642
6721
|
if (!entry.claude.has(f)) entry.claude.set(f, newCursor(initialClaudeState()));
|
|
6643
6722
|
}
|
|
@@ -6646,7 +6725,7 @@ function createProgressTracker(opts) {
|
|
|
6646
6725
|
function discoverCodex(entry) {
|
|
6647
6726
|
const { target } = entry;
|
|
6648
6727
|
for (const codexHome of codexHomes()) {
|
|
6649
|
-
for (const day of codexDayDirs(
|
|
6728
|
+
for (const day of codexDayDirs(path23.join(codexHome, "sessions"), target.since)) {
|
|
6650
6729
|
for (const file of listFiles(day, (n) => n.startsWith("rollout-") && n.endsWith(".jsonl"))) {
|
|
6651
6730
|
if (entry.codex.has(file) || entry.rejected.has(file)) continue;
|
|
6652
6731
|
const head = firstLine(file);
|
|
@@ -6663,11 +6742,11 @@ function createProgressTracker(opts) {
|
|
|
6663
6742
|
}
|
|
6664
6743
|
}
|
|
6665
6744
|
function drainHooks(entry) {
|
|
6666
|
-
const dir =
|
|
6745
|
+
const dir = path23.join(entry.target.worktreePath, PROGRESS_EVENTS_DIR);
|
|
6667
6746
|
for (const file of listFiles(dir, (n) => n.endsWith(".json")).sort()) {
|
|
6668
6747
|
try {
|
|
6669
|
-
const at =
|
|
6670
|
-
const event = JSON.parse(
|
|
6748
|
+
const at = fs20.statSync(file).mtimeMs;
|
|
6749
|
+
const event = JSON.parse(fs20.readFileSync(file, "utf8"));
|
|
6671
6750
|
const foreign = entry.target.sessionId && typeof event.session_id === "string" && event.session_id !== entry.target.sessionId;
|
|
6672
6751
|
const activity = foreign ? null : activityFromHook(event, at);
|
|
6673
6752
|
if (activity) entry.hookActivity = latestActivity(entry.hookActivity ?? activity, activity);
|
|
@@ -6675,7 +6754,7 @@ function createProgressTracker(opts) {
|
|
|
6675
6754
|
continue;
|
|
6676
6755
|
}
|
|
6677
6756
|
try {
|
|
6678
|
-
|
|
6757
|
+
fs20.unlinkSync(file);
|
|
6679
6758
|
} catch {
|
|
6680
6759
|
}
|
|
6681
6760
|
}
|
|
@@ -6687,8 +6766,8 @@ function createProgressTracker(opts) {
|
|
|
6687
6766
|
if (target.agent === "claude") {
|
|
6688
6767
|
for (const [file, cursor] of entry.claude) {
|
|
6689
6768
|
runUsage = addUsage(runUsage, claudeUsageTotal(cursor.state));
|
|
6690
|
-
if (file.includes(`${
|
|
6691
|
-
const isExecutor =
|
|
6769
|
+
if (file.includes(`${path23.sep}subagents${path23.sep}`)) continue;
|
|
6770
|
+
const isExecutor = path23.basename(file, ".jsonl") === target.sessionId;
|
|
6692
6771
|
const s = cursor.state;
|
|
6693
6772
|
if (isExecutor || !target.sessionId && (!primary || (s.startedAt ?? 0) > (primary.startedAt ?? 0))) {
|
|
6694
6773
|
primary = { todos: s.todos, contextTokens: s.contextTokens, activity: s.activity, startedAt: s.startedAt };
|
|
@@ -6767,10 +6846,10 @@ function createProgressTracker(opts) {
|
|
|
6767
6846
|
};
|
|
6768
6847
|
entry.timer.unref?.();
|
|
6769
6848
|
entries.set(target.taskId, entry);
|
|
6770
|
-
const eventsDir =
|
|
6849
|
+
const eventsDir = path23.join(target.worktreePath, PROGRESS_EVENTS_DIR);
|
|
6771
6850
|
try {
|
|
6772
|
-
|
|
6773
|
-
entry.watcher =
|
|
6851
|
+
fs20.mkdirSync(eventsDir, { recursive: true });
|
|
6852
|
+
entry.watcher = fs20.watch(eventsDir, () => {
|
|
6774
6853
|
if (entry.pending) return;
|
|
6775
6854
|
entry.pending = setTimeout(() => {
|
|
6776
6855
|
entry.pending = null;
|
|
@@ -6862,15 +6941,16 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6862
6941
|
const router = jevRouter ?? createJevRouter({ getApiKey: readJevApiKey });
|
|
6863
6942
|
const routing = /* @__PURE__ */ new Map();
|
|
6864
6943
|
const standaloneTerminals = /* @__PURE__ */ new Map();
|
|
6944
|
+
const terminalDirectories = /* @__PURE__ */ new Map();
|
|
6865
6945
|
let libraryCodexHome = null;
|
|
6866
6946
|
void libraryRoot().then((root) => {
|
|
6867
6947
|
libraryCodexHome = libraryPaths(root).codexHome;
|
|
6868
6948
|
}).catch(() => {
|
|
6869
6949
|
});
|
|
6870
|
-
const home = homeDir ??
|
|
6950
|
+
const home = homeDir ?? os8.homedir();
|
|
6871
6951
|
const progress = createProgressTracker({
|
|
6872
6952
|
homeDir: home,
|
|
6873
|
-
codexHomes: () => [
|
|
6953
|
+
codexHomes: () => [path24.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
|
|
6874
6954
|
onUpdate: (taskId, value) => bus.emit("progress:update", { taskId, progress: value }),
|
|
6875
6955
|
onNotify: (taskId, list) => {
|
|
6876
6956
|
const prefs = resolveNotificationSettings(getSettings());
|
|
@@ -6923,6 +7003,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6923
7003
|
removeTerminals([key]);
|
|
6924
7004
|
}
|
|
6925
7005
|
standaloneTerminals.delete(key);
|
|
7006
|
+
terminalDirectories.delete(key);
|
|
6926
7007
|
unwatchSubAgents(key);
|
|
6927
7008
|
}
|
|
6928
7009
|
function isStandaloneKey(key) {
|
|
@@ -6944,6 +7025,21 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6944
7025
|
requireTask(key.split(":")[0]);
|
|
6945
7026
|
return key;
|
|
6946
7027
|
}
|
|
7028
|
+
function terminalDirectory(sessionKey) {
|
|
7029
|
+
const key = requireSessionKey(sessionKey);
|
|
7030
|
+
const known = standaloneTerminals.get(key) ?? terminalDirectories.get(key);
|
|
7031
|
+
if (known) return known;
|
|
7032
|
+
if (isStandaloneKey(key)) {
|
|
7033
|
+
const saved = listTerminals().find((item) => item.sessionKey === key);
|
|
7034
|
+
if (saved?.taskId) {
|
|
7035
|
+
const task2 = requireTask(saved.taskId);
|
|
7036
|
+
return str(task2.worktreePath ?? task2.projectPath, "directory");
|
|
7037
|
+
}
|
|
7038
|
+
return saved?.projectPath ?? os8.homedir();
|
|
7039
|
+
}
|
|
7040
|
+
const task = requireTask(key.split(":")[0]);
|
|
7041
|
+
return str(task.worktreePath ?? task.projectPath, "directory");
|
|
7042
|
+
}
|
|
6947
7043
|
function launchEffort(task, route) {
|
|
6948
7044
|
const effort = task.effort === "auto" ? route?.effort : task.effort;
|
|
6949
7045
|
const model = models.findModel(taskAgent(task), taskModel(task));
|
|
@@ -6988,13 +7084,13 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6988
7084
|
}
|
|
6989
7085
|
function claudeSessionFile(cwd, sessionId) {
|
|
6990
7086
|
const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
6991
|
-
return
|
|
7087
|
+
return path24.join(os8.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
|
|
6992
7088
|
}
|
|
6993
7089
|
async function removeTaskWorktree(task) {
|
|
6994
7090
|
if (!task.projectPath || !task.worktreePath) return;
|
|
6995
7091
|
const branch = task.branch || fallbackBranchName(task.id);
|
|
6996
7092
|
await removeWorktree(task.projectPath, task.worktreePath);
|
|
6997
|
-
deleteArtifacts(task.workspacePath ??
|
|
7093
|
+
deleteArtifacts(task.workspacePath ?? path24.dirname(task.worktreePath), task.worktreePath);
|
|
6998
7094
|
await deleteBranch(task.projectPath, branch);
|
|
6999
7095
|
}
|
|
7000
7096
|
function columnOf(taskId) {
|
|
@@ -7139,8 +7235,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7139
7235
|
"projects:listRecent": () => listRecentProjects(getStore()),
|
|
7140
7236
|
// `vibeflow open <path>` while a host runs: only the host writes the store.
|
|
7141
7237
|
"projects:record": (projectPath) => {
|
|
7142
|
-
const p =
|
|
7143
|
-
if (!
|
|
7238
|
+
const p = path24.resolve(str(projectPath, "projectPath"));
|
|
7239
|
+
if (!fs21.existsSync(p) || !fs21.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
|
|
7144
7240
|
recordRecentProject(getStore(), p);
|
|
7145
7241
|
return listRecentProjects(getStore());
|
|
7146
7242
|
},
|
|
@@ -7193,7 +7289,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7193
7289
|
if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
|
|
7194
7290
|
await teardownTask(task.id);
|
|
7195
7291
|
await resetWorktreeToBase(task.worktreePath, task.baseBranch);
|
|
7196
|
-
const workspacePath = task.workspacePath ??
|
|
7292
|
+
const workspacePath = task.workspacePath ?? path24.dirname(task.worktreePath);
|
|
7197
7293
|
deleteArtifacts(workspacePath, task.worktreePath);
|
|
7198
7294
|
deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
|
|
7199
7295
|
resetSubAgents(task.worktreePath);
|
|
@@ -7237,7 +7333,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7237
7333
|
}
|
|
7238
7334
|
const info = await getGitInfo(nextProject);
|
|
7239
7335
|
if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
|
|
7240
|
-
const projectName =
|
|
7336
|
+
const projectName = path24.basename(nextProject);
|
|
7241
7337
|
const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
|
|
7242
7338
|
await teardownTask(existing.id);
|
|
7243
7339
|
await removeTaskWorktree(existing);
|
|
@@ -7299,6 +7395,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7299
7395
|
// Session ended (natural exit included), so stop its watcher.
|
|
7300
7396
|
onExit: () => unwatchSubAgents(key)
|
|
7301
7397
|
});
|
|
7398
|
+
terminalDirectories.set(key, cwd);
|
|
7302
7399
|
watchSubAgents(key, cwd, (subAgents) => {
|
|
7303
7400
|
bus.emit("subagents:update", { taskId: task.id, subAgents });
|
|
7304
7401
|
});
|
|
@@ -7312,7 +7409,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7312
7409
|
const p = obj(payload, "payload");
|
|
7313
7410
|
const key = str(p.sessionKey, "sessionKey");
|
|
7314
7411
|
if (!STANDALONE_KEY.test(key)) invalid("invalid standalone terminal key");
|
|
7315
|
-
if (
|
|
7412
|
+
if (p.taskId && p.projectPath) invalid("provide taskId or projectPath, not both");
|
|
7316
7413
|
const saved = listTerminals().find((t) => t.sessionKey === key);
|
|
7317
7414
|
if (saved && (saved.taskId ? saved.taskId !== p.taskId : saved.projectPath !== p.projectPath)) {
|
|
7318
7415
|
invalid("terminal key already belongs to another directory");
|
|
@@ -7325,17 +7422,24 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7325
7422
|
const task = column === "in_progress" && !requested.worktreePath ? await ensureProvisioned(requested) : requested;
|
|
7326
7423
|
cwd = str(column === "backlog" ? task.projectPath : task.worktreePath ?? task.projectPath, "directory");
|
|
7327
7424
|
projectPath = task.projectPath ?? cwd;
|
|
7328
|
-
} else {
|
|
7425
|
+
} else if (p.projectPath) {
|
|
7329
7426
|
cwd = str(p.projectPath, "projectPath");
|
|
7330
7427
|
projectPath = cwd;
|
|
7428
|
+
} else {
|
|
7429
|
+
cwd = os8.homedir();
|
|
7331
7430
|
}
|
|
7332
|
-
if (!
|
|
7431
|
+
if (!path24.isAbsolute(cwd) || !fs21.existsSync(cwd) || !fs21.statSync(cwd).isDirectory()) {
|
|
7333
7432
|
invalid("terminal directory must be an existing absolute directory");
|
|
7334
7433
|
}
|
|
7335
|
-
const title = (typeof p.title === "string" ? p.title.trim() : "") || saved?.title ||
|
|
7336
|
-
saveTerminal({
|
|
7434
|
+
const title = (typeof p.title === "string" ? p.title.trim() : "") || saved?.title || (projectPath ? path24.basename(projectPath) : "~");
|
|
7435
|
+
saveTerminal({
|
|
7436
|
+
sessionKey: key,
|
|
7437
|
+
title,
|
|
7438
|
+
...projectPath ? { projectPath } : {},
|
|
7439
|
+
...p.taskId ? { taskId: p.taskId } : {}
|
|
7440
|
+
});
|
|
7337
7441
|
standaloneTerminals.set(key, cwd);
|
|
7338
|
-
if (!p.taskId) recordRecentProject(getStore(), cwd);
|
|
7442
|
+
if (!p.taskId && projectPath) recordRecentProject(getStore(), cwd);
|
|
7339
7443
|
const cols = typeof p.cols === "number" ? p.cols : void 0;
|
|
7340
7444
|
const rows = typeof p.rows === "number" ? p.rows : void 0;
|
|
7341
7445
|
if (sessions.kind === "pty" && await sessions.isAlive(key)) {
|
|
@@ -7344,6 +7448,21 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7344
7448
|
}
|
|
7345
7449
|
return sessions.start(key, { cwd, cols, rows });
|
|
7346
7450
|
},
|
|
7451
|
+
"terminal:readFile": async (payload) => {
|
|
7452
|
+
const p = obj(payload, "payload");
|
|
7453
|
+
return readTerminalFile(terminalDirectory(p.sessionKey), str(p.path, "path"));
|
|
7454
|
+
},
|
|
7455
|
+
"terminal:openFile": async (payload) => {
|
|
7456
|
+
const p = obj(payload, "payload");
|
|
7457
|
+
const file = await readTerminalFile(terminalDirectory(p.sessionKey), str(p.path, "path"), true);
|
|
7458
|
+
if (!file.ok) return { ok: false, message: file.message };
|
|
7459
|
+
try {
|
|
7460
|
+
const message = await getPlatform().openPath(file.path);
|
|
7461
|
+
return message ? { ok: false, message } : { ok: true };
|
|
7462
|
+
} catch (error) {
|
|
7463
|
+
return { ok: false, message: error instanceof Error ? error.message : "\u7121\u6CD5\u958B\u555F\u6A94\u6848\u3002" };
|
|
7464
|
+
}
|
|
7465
|
+
},
|
|
7347
7466
|
"terminal:rename": (payload) => {
|
|
7348
7467
|
const p = obj(payload, "payload");
|
|
7349
7468
|
const key = str(p.sessionKey, "sessionKey");
|
|
@@ -7382,7 +7501,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7382
7501
|
"claude:sessionExists": (taskId) => {
|
|
7383
7502
|
const task = requireTask(taskId);
|
|
7384
7503
|
if (!task.worktreePath) return false;
|
|
7385
|
-
return
|
|
7504
|
+
return fs21.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
|
|
7386
7505
|
},
|
|
7387
7506
|
"sessions:list": async () => {
|
|
7388
7507
|
const keys = await sessions.list();
|
|
@@ -7429,7 +7548,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7429
7548
|
const task = requireTask(taskId);
|
|
7430
7549
|
if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
|
|
7431
7550
|
const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
|
|
7432
|
-
if (!
|
|
7551
|
+
if (!fs21.existsSync(dir)) return "artifacts directory does not exist yet";
|
|
7433
7552
|
return getPlatform().openPath(dir);
|
|
7434
7553
|
},
|
|
7435
7554
|
"task:getSpecs": async (taskId) => {
|
|
@@ -7564,7 +7683,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7564
7683
|
}
|
|
7565
7684
|
await removeTaskWorktree(task);
|
|
7566
7685
|
if (task.workspacePath) {
|
|
7567
|
-
|
|
7686
|
+
fs21.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
|
|
7568
7687
|
}
|
|
7569
7688
|
clearConversation(task.id);
|
|
7570
7689
|
removeTask(task.id);
|
|
@@ -7617,9 +7736,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7617
7736
|
// Watch the containing directory, not the file: JsonStore renames a temp
|
|
7618
7737
|
// file over the target and so replaces the inode on every save.
|
|
7619
7738
|
watchStore() {
|
|
7620
|
-
const file =
|
|
7739
|
+
const file = path24.basename(getStorePath());
|
|
7621
7740
|
let debounce = null;
|
|
7622
|
-
storeWatcher =
|
|
7741
|
+
storeWatcher = fs21.watch(path24.dirname(getStorePath()), (_event, filename) => {
|
|
7623
7742
|
if (filename !== file) return;
|
|
7624
7743
|
if (debounce) clearTimeout(debounce);
|
|
7625
7744
|
debounce = setTimeout(() => {
|
|
@@ -7656,9 +7775,9 @@ function createSessionBackend(sink, choice) {
|
|
|
7656
7775
|
}
|
|
7657
7776
|
|
|
7658
7777
|
// packages/core/src/web-server.ts
|
|
7659
|
-
import
|
|
7778
|
+
import fs22 from "fs";
|
|
7660
7779
|
import http from "http";
|
|
7661
|
-
import
|
|
7780
|
+
import path25 from "path";
|
|
7662
7781
|
import { timingSafeEqual } from "crypto";
|
|
7663
7782
|
import { WebSocketServer } from "ws";
|
|
7664
7783
|
var TOKEN_COOKIE = "vf_token";
|
|
@@ -7713,14 +7832,14 @@ function resolveStatic(root, urlPath) {
|
|
|
7713
7832
|
return null;
|
|
7714
7833
|
}
|
|
7715
7834
|
if (decoded.includes("\0")) return null;
|
|
7716
|
-
const rel =
|
|
7717
|
-
const base2 =
|
|
7718
|
-
const candidates = [rel,
|
|
7835
|
+
const rel = path25.posix.normalize(decoded).replace(/^\/+/, "");
|
|
7836
|
+
const base2 = path25.resolve(root);
|
|
7837
|
+
const candidates = [rel, path25.posix.join(rel, "index.html"), `${rel}.html`];
|
|
7719
7838
|
for (const c of candidates) {
|
|
7720
|
-
const full =
|
|
7721
|
-
if (full !== base2 && !full.startsWith(base2 +
|
|
7839
|
+
const full = path25.resolve(base2, c);
|
|
7840
|
+
if (full !== base2 && !full.startsWith(base2 + path25.sep)) return null;
|
|
7722
7841
|
try {
|
|
7723
|
-
if (
|
|
7842
|
+
if (fs22.statSync(full).isFile()) return full;
|
|
7724
7843
|
} catch {
|
|
7725
7844
|
}
|
|
7726
7845
|
}
|
|
@@ -7781,7 +7900,7 @@ async function startWebServer(options) {
|
|
|
7781
7900
|
send(res, 404, "Not found");
|
|
7782
7901
|
return;
|
|
7783
7902
|
}
|
|
7784
|
-
const ext =
|
|
7903
|
+
const ext = path25.extname(file).toLowerCase();
|
|
7785
7904
|
const immutable = url.pathname.startsWith("/_next/static/");
|
|
7786
7905
|
res.writeHead(200, {
|
|
7787
7906
|
"Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
|
|
@@ -7794,7 +7913,7 @@ async function startWebServer(options) {
|
|
|
7794
7913
|
res.end();
|
|
7795
7914
|
return;
|
|
7796
7915
|
}
|
|
7797
|
-
|
|
7916
|
+
fs22.createReadStream(file).pipe(res);
|
|
7798
7917
|
}
|
|
7799
7918
|
const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
|
|
7800
7919
|
function reject(socket, status, reason) {
|
|
@@ -7893,8 +8012,8 @@ Content-Length: 0\r
|
|
|
7893
8012
|
}
|
|
7894
8013
|
|
|
7895
8014
|
// packages/cli/src/update-command.ts
|
|
7896
|
-
import
|
|
7897
|
-
import
|
|
8015
|
+
import fs23 from "node:fs";
|
|
8016
|
+
import path26 from "node:path";
|
|
7898
8017
|
import { spawnSync } from "node:child_process";
|
|
7899
8018
|
function npmRunner(args, inherit) {
|
|
7900
8019
|
const spawnOpts = {
|
|
@@ -7906,7 +8025,7 @@ function npmRunner(args, inherit) {
|
|
|
7906
8025
|
}
|
|
7907
8026
|
function packageName(packageRoot) {
|
|
7908
8027
|
try {
|
|
7909
|
-
const pkg = JSON.parse(
|
|
8028
|
+
const pkg = JSON.parse(fs23.readFileSync(path26.join(packageRoot, "package.json"), "utf8"));
|
|
7910
8029
|
if (typeof pkg.name !== "string") return null;
|
|
7911
8030
|
return /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i.test(pkg.name) ? pkg.name : null;
|
|
7912
8031
|
} catch {
|
|
@@ -7921,7 +8040,7 @@ function runUpdate(options) {
|
|
|
7921
8040
|
error("vibeflow update \u53EA\u9069\u7528\u65BC\u5168\u57DF npm \u5B89\u88DD\uFF1B\u76EE\u524D\u662F\u539F\u59CB\u78BC\u7248\u672C\u3002");
|
|
7922
8041
|
return 1;
|
|
7923
8042
|
}
|
|
7924
|
-
const packageRoot =
|
|
8043
|
+
const packageRoot = path26.dirname(path26.dirname(options.cliEntry));
|
|
7925
8044
|
const name = packageName(packageRoot);
|
|
7926
8045
|
if (!name) {
|
|
7927
8046
|
error("\u7121\u6CD5\u8FA8\u8B58\u76EE\u524D VibeFlow \u7684 npm \u5957\u4EF6\u540D\u7A31\uFF0C\u5DF2\u53D6\u6D88\u66F4\u65B0\u3002");
|
|
@@ -7933,8 +8052,8 @@ function runUpdate(options) {
|
|
|
7933
8052
|
return 1;
|
|
7934
8053
|
}
|
|
7935
8054
|
try {
|
|
7936
|
-
const globalPackage =
|
|
7937
|
-
if (
|
|
8055
|
+
const globalPackage = path26.join(String(root.stdout).trim(), ...name.split("/"));
|
|
8056
|
+
if (fs23.lstatSync(globalPackage).isSymbolicLink() || fs23.realpathSync(globalPackage) !== fs23.realpathSync(packageRoot)) {
|
|
7938
8057
|
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");
|
|
7939
8058
|
return 1;
|
|
7940
8059
|
}
|
|
@@ -8079,8 +8198,8 @@ async function openUpdateTerminal(platform = process.platform, run3 = launch) {
|
|
|
8079
8198
|
}
|
|
8080
8199
|
|
|
8081
8200
|
// packages/cli/src/self-update.ts
|
|
8082
|
-
import
|
|
8083
|
-
import
|
|
8201
|
+
import fs24 from "node:fs";
|
|
8202
|
+
import path27 from "node:path";
|
|
8084
8203
|
import { execFile as execFile10 } from "node:child_process";
|
|
8085
8204
|
import { promisify as promisify7 } from "node:util";
|
|
8086
8205
|
var execFileAsync2 = promisify7(execFile10);
|
|
@@ -8089,7 +8208,7 @@ var PROBE_RETRY_MS = 3e4;
|
|
|
8089
8208
|
var HOST_TOKEN_ENV = "VIBEFLOW_HOST_TOKEN";
|
|
8090
8209
|
function readInstalledVersion(packageRoot) {
|
|
8091
8210
|
try {
|
|
8092
|
-
const pkg = JSON.parse(
|
|
8211
|
+
const pkg = JSON.parse(fs24.readFileSync(path27.join(packageRoot, "package.json"), "utf8"));
|
|
8093
8212
|
return typeof pkg.version === "string" ? pkg.version : null;
|
|
8094
8213
|
} catch {
|
|
8095
8214
|
return null;
|
|
@@ -8185,7 +8304,7 @@ async function startHost(options) {
|
|
|
8185
8304
|
const core = createCore({ sessions, bus, version: options.version });
|
|
8186
8305
|
let updateTimer = null;
|
|
8187
8306
|
let installTimer = null;
|
|
8188
|
-
const packageRoot = options.cliEntry ?
|
|
8307
|
+
const packageRoot = options.cliEntry ? path28.dirname(path28.dirname(options.cliEntry)) : null;
|
|
8189
8308
|
const updates = createUpdateMonitor({
|
|
8190
8309
|
packageName: options.sourceRoot || !packageRoot ? null : packageName(packageRoot),
|
|
8191
8310
|
currentVersion: options.version,
|
|
@@ -8273,7 +8392,7 @@ function loginUrl(lock, pathname = "/home/") {
|
|
|
8273
8392
|
return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
|
|
8274
8393
|
}
|
|
8275
8394
|
function isWebDir(dir) {
|
|
8276
|
-
return
|
|
8395
|
+
return fs25.existsSync(path28.join(dir, "home", "index.html"));
|
|
8277
8396
|
}
|
|
8278
8397
|
|
|
8279
8398
|
// packages/cli/src/remote.ts
|
|
@@ -8390,8 +8509,8 @@ function connectToHost(lock) {
|
|
|
8390
8509
|
}
|
|
8391
8510
|
|
|
8392
8511
|
// packages/cli/src/doctor.ts
|
|
8393
|
-
import
|
|
8394
|
-
import
|
|
8512
|
+
import fs26 from "fs";
|
|
8513
|
+
import path29 from "path";
|
|
8395
8514
|
import { execFile as execFile11 } from "child_process";
|
|
8396
8515
|
import { promisify as promisify8 } from "util";
|
|
8397
8516
|
init_env();
|
|
@@ -8531,10 +8650,10 @@ async function runDoctor(options) {
|
|
|
8531
8650
|
});
|
|
8532
8651
|
}
|
|
8533
8652
|
try {
|
|
8534
|
-
|
|
8535
|
-
const probe =
|
|
8536
|
-
|
|
8537
|
-
|
|
8653
|
+
fs26.mkdirSync(options.userDataDir, { recursive: true });
|
|
8654
|
+
const probe = path29.join(options.userDataDir, `.doctor-${process.pid}`);
|
|
8655
|
+
fs26.writeFileSync(probe, "");
|
|
8656
|
+
fs26.rmSync(probe);
|
|
8538
8657
|
checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
|
|
8539
8658
|
} catch (err) {
|
|
8540
8659
|
checks.push({
|
|
@@ -8588,25 +8707,25 @@ Options:
|
|
|
8588
8707
|
-h, --help Show this help
|
|
8589
8708
|
`.trim();
|
|
8590
8709
|
function detectLayout() {
|
|
8591
|
-
const file =
|
|
8592
|
-
const here =
|
|
8593
|
-
const bundledWeb =
|
|
8594
|
-
if (isWebDir(bundledWeb) ||
|
|
8710
|
+
const file = fileURLToPath2(import.meta.url);
|
|
8711
|
+
const here = path32.dirname(file);
|
|
8712
|
+
const bundledWeb = path32.join(here, "web");
|
|
8713
|
+
if (isWebDir(bundledWeb) || path32.basename(here) === "dist") {
|
|
8595
8714
|
return {
|
|
8596
|
-
version: readVersion(
|
|
8715
|
+
version: readVersion(path32.join(here, "..", "package.json")),
|
|
8597
8716
|
webDir: isWebDir(bundledWeb) ? bundledWeb : null,
|
|
8598
8717
|
sourceRoot: null,
|
|
8599
8718
|
cliEntry: file
|
|
8600
8719
|
};
|
|
8601
8720
|
}
|
|
8602
|
-
const root =
|
|
8721
|
+
const root = path32.resolve(here, "..", "..", "..");
|
|
8603
8722
|
const override = process.env.VIBEFLOW_WEB_DIR;
|
|
8604
|
-
const webDir = override && isWebDir(override) ? override : isWebDir(
|
|
8605
|
-
return { version: readVersion(
|
|
8723
|
+
const webDir = override && isWebDir(override) ? override : isWebDir(path32.join(root, "app")) ? path32.join(root, "app") : null;
|
|
8724
|
+
return { version: readVersion(path32.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
|
|
8606
8725
|
}
|
|
8607
8726
|
function readVersion(pkgPath) {
|
|
8608
8727
|
try {
|
|
8609
|
-
return JSON.parse(
|
|
8728
|
+
return JSON.parse(fs28.readFileSync(pkgPath, "utf8")).version;
|
|
8610
8729
|
} catch {
|
|
8611
8730
|
return "0.0.0";
|
|
8612
8731
|
}
|
|
@@ -8844,8 +8963,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
|
|
|
8844
8963
|
console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
|
|
8845
8964
|
return 1;
|
|
8846
8965
|
}
|
|
8847
|
-
const dir =
|
|
8848
|
-
if (!
|
|
8966
|
+
const dir = path32.resolve(target);
|
|
8967
|
+
if (!fs28.existsSync(dir) || !fs28.statSync(dir).isDirectory()) {
|
|
8849
8968
|
console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
|
|
8850
8969
|
return 1;
|
|
8851
8970
|
}
|