@kw-yeh/vibeflow 4.11.17 → 4.11.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/vibeflow.mjs +204 -58
- package/dist/web/404/index.html +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/_next/static/{xoex8UtSV4DX9eSoX3lTZ → 76irYAZpsrcN0YAWmxJ47}/_buildManifest.js +1 -1
- package/dist/web/_next/static/chunks/pages/{_app-83dfa6752c321c56.js → _app-2571a2564f593899.js} +1 -1
- package/dist/web/_next/static/chunks/pages/{home-61c688b1d20e56ff.js → home-307d0d7c49e4ee48.js} +8 -8
- package/dist/web/_next/static/css/{3cafb8bddc730b60.css → f8f64877c884206d.css} +1 -1
- package/dist/web/home/index.html +1 -1
- package/package.json +1 -1
- /package/dist/web/_next/static/{xoex8UtSV4DX9eSoX3lTZ → 76irYAZpsrcN0YAWmxJ47}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ vibeflow
|
|
|
26
26
|
- **看得到 agent 做了什麼**:每張卡有 diff 檢視、artifacts,以及「決策」分頁:顯示這張卡的分支新增或修改的 `spec.md`(agent 做了哪些決定、為什麼)。卡片完成後打開時預設顯示決策;本地分支刪掉後,會改從 `origin/<branch>` 或 GitHub PR 的 merge commit 讀回合併當時的內容(squash/rebase merge 的卡片讀不回來)。
|
|
27
27
|
- **選你要的 agent**:Claude Code 或 Codex,每張卡可以各自指定 model、推理強度(effort)與 Auto Mode(是否免確認執行)。Effort 預設為 `auto`,由 Jev 依任務與目前模型選擇支援的級距;在「設定 → Jev 自動選擇 effort」輸入 TypeSafe Jev API key,或在啟動 VibeFlow 的環境設定 `TYPESAFE_API_KEY`。若沒有金鑰或 Jev 暫時無法使用,會顯示原因並使用模型預設的 effort。未指定 model 時,使用該 CLI 的預設模型。
|
|
28
28
|
- **Web UI 與終端機 UI 共用同一個看板**:習慣瀏覽器用 Web UI,習慣終端機用 `vibeflow tui`。
|
|
29
|
-
- **Jira 工作收件匣**:安裝 [Atlassian CLI(Windows)](https://developer.atlassian.com/cloud/acli/guides/install-windows/) 或 [Atlassian CLI(macOS)](https://developer.atlassian.com/cloud/acli/guides/install-macos/),再到設定登入,就能在「工作項目」的 Jira
|
|
29
|
+
- **Jira 工作收件匣**:安裝 [Atlassian CLI(Windows)](https://developer.atlassian.com/cloud/acli/guides/install-windows/) 或 [Atlassian CLI(macOS)](https://developer.atlassian.com/cloud/acli/guides/install-macos/),再到設定登入,就能在「工作項目」的 Jira 分頁以固定四欄查看指派給自己的 ticket、選擇抓取範圍、開啟附件,並選擇本機專案建立 Backlog 卡片。
|
|
30
30
|
- **GitHub Issues 與 PRs**:使用已登入的 `gh` 跨所有相關 repo 顯示 open 項目,即使該 repo 尚未出現在看板;建卡時可選擇本機專案資料夾。
|
|
31
31
|
- **內建 skill,可自行調整**:每個人的 Library(設定 → Library)都附帶 `visual-parity`、`pr` 兩個 skill,可以直接編輯或刪除,隨時能還原成 VibeFlow 出貨的版本;沒改過的會跟著 VibeFlow 升級更新。Library 只負責保存,啟動任務時不會載入任何 Library 項目——agent 只收到你在設定填的 system prompt 與卡片內容。`visual-parity` 需要 Python 與 `pip install playwright && playwright install chromium`。
|
|
32
32
|
- **也能用指令操作**:`vibeflow task create` 不開 UI 直接建卡;agent 自己也能在看板上建立後續的卡片。
|
package/dist/vibeflow.mjs
CHANGED
|
@@ -1090,9 +1090,9 @@ var init_progress = __esm({
|
|
|
1090
1090
|
|
|
1091
1091
|
// packages/tui/src/task-form.ts
|
|
1092
1092
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1093
|
-
import
|
|
1093
|
+
import fs25 from "fs";
|
|
1094
1094
|
import os8 from "os";
|
|
1095
|
-
import
|
|
1095
|
+
import path29 from "path";
|
|
1096
1096
|
import React, { useEffect, useRef, useState } from "react";
|
|
1097
1097
|
import { Box, Text, render as render2, useApp, useInput } from "ink";
|
|
1098
1098
|
function newFormState(defaultAutoMode, agents) {
|
|
@@ -1218,17 +1218,17 @@ function editorCommand(env, platform) {
|
|
|
1218
1218
|
return env.VISUAL?.trim() || env.EDITOR?.trim() || (platform === "win32" ? "notepad" : "vi");
|
|
1219
1219
|
}
|
|
1220
1220
|
function editInEditor(text) {
|
|
1221
|
-
const dir =
|
|
1222
|
-
const file =
|
|
1221
|
+
const dir = fs25.mkdtempSync(path29.join(os8.tmpdir(), "vibeflow-"));
|
|
1222
|
+
const file = path29.join(dir, "description.md");
|
|
1223
1223
|
try {
|
|
1224
|
-
|
|
1224
|
+
fs25.writeFileSync(file, text);
|
|
1225
1225
|
const cmd = editorCommand(process.env, process.platform);
|
|
1226
1226
|
const r = spawnSync2(`${cmd} "${file}"`, { stdio: "inherit", shell: true });
|
|
1227
1227
|
if (r.error) return { error: `\u7121\u6CD5\u555F\u52D5\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\uFF1A${r.error.message}` };
|
|
1228
1228
|
if (r.status !== 0) return { error: `\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\u7D50\u675F\u78BC ${r.status}\uFF0C\u63CF\u8FF0\u672A\u8B8A\u66F4` };
|
|
1229
|
-
return { text:
|
|
1229
|
+
return { text: fs25.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
|
|
1230
1230
|
} finally {
|
|
1231
|
-
|
|
1231
|
+
fs25.rmSync(dir, { recursive: true, force: true });
|
|
1232
1232
|
}
|
|
1233
1233
|
}
|
|
1234
1234
|
function cycle(list, current, step) {
|
|
@@ -1299,10 +1299,10 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1299
1299
|
if (editing === "title") setS((prev) => ({ ...prev, title: draft }));
|
|
1300
1300
|
else if (editing === "branch") setS((prev) => ({ ...prev, branch: value2 }));
|
|
1301
1301
|
else if (editing === "project") {
|
|
1302
|
-
if (value2) setProject(
|
|
1302
|
+
if (value2) setProject(path29.resolve(value2.replace(/^~(?=$|\/)/, os8.homedir())));
|
|
1303
1303
|
} else if (editing === "attachments" && value2) {
|
|
1304
|
-
const file =
|
|
1305
|
-
if (!
|
|
1304
|
+
const file = path29.resolve(value2.replace(/^~(?=$|\/)/, os8.homedir()));
|
|
1305
|
+
if (!fs25.existsSync(file) || !fs25.statSync(file).isFile()) {
|
|
1306
1306
|
setMessage(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${file}`);
|
|
1307
1307
|
return;
|
|
1308
1308
|
}
|
|
@@ -1385,7 +1385,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1385
1385
|
case "branch":
|
|
1386
1386
|
return s.branch || "\uFF08\u7559\u7A7A\u5247\u81EA\u52D5\u547D\u540D\uFF0C\u4F8B\u5982 feature/login-fix\uFF09";
|
|
1387
1387
|
case "attachments":
|
|
1388
|
-
return s.attachments.length ? s.attachments.map((a) =>
|
|
1388
|
+
return s.attachments.length ? s.attachments.map((a) => path29.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
|
|
1389
1389
|
case "description": {
|
|
1390
1390
|
if (!s.description) return "\uFF08Enter \u7528\u7DE8\u8F2F\u5668\u64B0\u5BEB\uFF09";
|
|
1391
1391
|
const lines = s.description.split("\n");
|
|
@@ -1420,7 +1420,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1420
1420
|
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";
|
|
1421
1421
|
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";
|
|
1422
1422
|
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`;
|
|
1423
|
-
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${
|
|
1423
|
+
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path29.basename(s.projectPath)}`;
|
|
1424
1424
|
}
|
|
1425
1425
|
if (field === "branch") {
|
|
1426
1426
|
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";
|
|
@@ -1549,7 +1549,7 @@ __export(src_exports, {
|
|
|
1549
1549
|
showDiff: () => showDiff
|
|
1550
1550
|
});
|
|
1551
1551
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
1552
|
-
import
|
|
1552
|
+
import path30 from "path";
|
|
1553
1553
|
import React2, { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1554
1554
|
import { Box as Box2, Text as Text2, render as render3, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
|
|
1555
1555
|
function findDetachKey(text) {
|
|
@@ -1845,7 +1845,7 @@ function showDiff(task) {
|
|
|
1845
1845
|
});
|
|
1846
1846
|
}
|
|
1847
1847
|
function tmuxAttach(opts, sessionName) {
|
|
1848
|
-
const conf =
|
|
1848
|
+
const conf = path30.join(opts.userDataDir, "tmux.conf");
|
|
1849
1849
|
const inOurServer = (process.env.TMUX ?? "").split(",")[0].endsWith(`/${TMUX_SOCKET}`);
|
|
1850
1850
|
const args = inOurServer ? ["-L", TMUX_SOCKET, "-f", conf, "switch-client", "-t", `=${sessionName}`] : ["-L", TMUX_SOCKET, "-f", conf, "attach-session", "-t", `=${sessionName}`];
|
|
1851
1851
|
spawnSync3("tmux", args, { stdio: "inherit", env: { ...process.env, TMUX: inOurServer ? process.env.TMUX : "" } });
|
|
@@ -1976,10 +1976,11 @@ var init_src = __esm({
|
|
|
1976
1976
|
});
|
|
1977
1977
|
|
|
1978
1978
|
// packages/cli/src/main.ts
|
|
1979
|
-
import
|
|
1980
|
-
import
|
|
1979
|
+
import fs26 from "fs";
|
|
1980
|
+
import path31 from "path";
|
|
1981
1981
|
import { fileURLToPath } from "url";
|
|
1982
1982
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
1983
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
1983
1984
|
|
|
1984
1985
|
// packages/core/src/lock.ts
|
|
1985
1986
|
import fs from "fs";
|
|
@@ -4693,8 +4694,8 @@ async function taskCommand(argv) {
|
|
|
4693
4694
|
}
|
|
4694
4695
|
|
|
4695
4696
|
// packages/cli/src/host.ts
|
|
4696
|
-
import
|
|
4697
|
-
import
|
|
4697
|
+
import fs23 from "fs";
|
|
4698
|
+
import path27 from "path";
|
|
4698
4699
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
4699
4700
|
|
|
4700
4701
|
// packages/core/src/events.ts
|
|
@@ -5928,21 +5929,25 @@ async function logoutJira(run3 = defaultAcliRunner) {
|
|
|
5928
5929
|
var asRaw = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
5929
5930
|
var nameOf = (v) => typeof v === "string" ? v : String(asRaw(v).name ?? "");
|
|
5930
5931
|
var JIRA_INBOX_JQL = "assignee = currentUser() AND (statusCategory != Done OR sprint in openSprints()) ORDER BY updated DESC";
|
|
5932
|
+
var JIRA_SCOPE_JQL = {
|
|
5933
|
+
current: JIRA_INBOX_JQL,
|
|
5934
|
+
sprint: "assignee = currentUser() AND sprint in openSprints() ORDER BY updated DESC",
|
|
5935
|
+
all: "assignee = currentUser() ORDER BY updated DESC"
|
|
5936
|
+
};
|
|
5931
5937
|
var DEFAULT_POINT_FIELDS = ["customfield_10033", "customfield_10016"];
|
|
5932
5938
|
var JIRA_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
5933
|
-
function buildSearchArgs() {
|
|
5939
|
+
function buildSearchArgs(scope = "current") {
|
|
5934
5940
|
return [
|
|
5935
5941
|
"jira",
|
|
5936
5942
|
"workitem",
|
|
5937
5943
|
"search",
|
|
5938
5944
|
"--jql",
|
|
5939
|
-
|
|
5945
|
+
JIRA_SCOPE_JQL[scope],
|
|
5940
5946
|
"--fields",
|
|
5941
5947
|
"key,summary,status,issuetype,priority,description",
|
|
5942
5948
|
"--json",
|
|
5943
5949
|
"--paginate",
|
|
5944
|
-
"--limit",
|
|
5945
|
-
"200"
|
|
5950
|
+
...scope === "all" ? [] : ["--limit", "200"]
|
|
5946
5951
|
];
|
|
5947
5952
|
}
|
|
5948
5953
|
function buildViewArgs(key, pointFields = DEFAULT_POINT_FIELDS) {
|
|
@@ -6039,19 +6044,22 @@ function createJiraService(options) {
|
|
|
6039
6044
|
const run3 = options.run ?? defaultAcliRunner;
|
|
6040
6045
|
const authStatus = options.authStatus ?? (() => getJiraAuthStatus(run3));
|
|
6041
6046
|
const now = options.now ?? Date.now;
|
|
6042
|
-
|
|
6047
|
+
const cache = /* @__PURE__ */ new Map();
|
|
6043
6048
|
return {
|
|
6044
6049
|
clear() {
|
|
6045
|
-
cache
|
|
6050
|
+
cache.clear();
|
|
6046
6051
|
},
|
|
6047
6052
|
async inbox(opts = {}) {
|
|
6048
6053
|
const auth = await authStatus();
|
|
6049
6054
|
if (!auth.installed) return { status: "acli-missing", fetchedAt: 0, tickets: [] };
|
|
6050
6055
|
if (!auth.authenticated || !auth.site) return { status: "unauthenticated", fetchedAt: 0, tickets: [] };
|
|
6051
|
-
|
|
6056
|
+
const scope = opts.scope ?? "current";
|
|
6057
|
+
const cacheKey = `${auth.site}:${scope}`;
|
|
6058
|
+
const cached2 = cache.get(cacheKey);
|
|
6059
|
+
if (cached2 && !opts.force && now() - cached2.at < JIRA_CACHE_TTL_MS) return cached2.value;
|
|
6052
6060
|
const at = now();
|
|
6053
6061
|
const pointFields = options.pointFields?.() ?? DEFAULT_POINT_FIELDS;
|
|
6054
|
-
const value = run3(buildSearchArgs()).then(async (output) => {
|
|
6062
|
+
const value = run3(buildSearchArgs(scope)).then(async (output) => {
|
|
6055
6063
|
const detail = await enrichRows(searchRows(output), run3, pointFields);
|
|
6056
6064
|
return {
|
|
6057
6065
|
status: "ok",
|
|
@@ -6069,7 +6077,7 @@ function createJiraService(options) {
|
|
|
6069
6077
|
tickets: [],
|
|
6070
6078
|
error: String(err.stderr || err.message || err)
|
|
6071
6079
|
}));
|
|
6072
|
-
cache
|
|
6080
|
+
cache.set(cacheKey, { at, value });
|
|
6073
6081
|
return value;
|
|
6074
6082
|
}
|
|
6075
6083
|
};
|
|
@@ -7089,7 +7097,10 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7089
7097
|
},
|
|
7090
7098
|
"jira:inbox": (payload) => {
|
|
7091
7099
|
const p = payload === void 0 ? {} : obj(payload, "payload");
|
|
7092
|
-
|
|
7100
|
+
if (p.scope !== void 0 && p.scope !== "current" && p.scope !== "sprint" && p.scope !== "all") {
|
|
7101
|
+
throw Object.assign(new Error("Invalid Jira scope"), { code: "INVALID_REQUEST" });
|
|
7102
|
+
}
|
|
7103
|
+
return jira.inbox({ force: p.force === true, scope: p.scope });
|
|
7093
7104
|
},
|
|
7094
7105
|
"env:detectAgents": () => detectAgents(),
|
|
7095
7106
|
"agents:listModels": (payload) => {
|
|
@@ -7917,7 +7928,7 @@ function runUpdate(options) {
|
|
|
7917
7928
|
error(`\u66F4\u65B0\u5931\u6557\uFF1A${result.error?.message ?? `npm \u7D50\u675F\u78BC ${result.status ?? "unknown"}`}`);
|
|
7918
7929
|
return result.status && result.status > 0 ? result.status : 1;
|
|
7919
7930
|
}
|
|
7920
|
-
log("\u66F4\u65B0\u5B8C\u6210\u3002\
|
|
7931
|
+
log("\u66F4\u65B0\u5B8C\u6210\u3002\u57F7\u884C\u4E2D\u7684 Web UI host \u6703\u5728\u6578\u79D2\u5167\u81EA\u52D5\u91CD\u65B0\u555F\u52D5\u6210\u65B0\u7248\u672C\uFF1BTUI host \u6216\u6709\u57F7\u884C\u4E2D session \u7684 pty host \u8ACB\u81EA\u884C\u91CD\u65B0\u555F\u52D5\u3002");
|
|
7921
7932
|
return 0;
|
|
7922
7933
|
}
|
|
7923
7934
|
|
|
@@ -7971,6 +7982,17 @@ function createUpdateMonitor(options) {
|
|
|
7971
7982
|
}
|
|
7972
7983
|
return next ? { status: "available", ...next } : { status: "current", currentVersion: options.currentVersion };
|
|
7973
7984
|
},
|
|
7985
|
+
/** A newer version is on disk but the host must be restarted by hand to run it. */
|
|
7986
|
+
markInstalled(version, reason) {
|
|
7987
|
+
if (stopped || available?.installedVersion === version && available.restartBlocked === reason) return;
|
|
7988
|
+
available = {
|
|
7989
|
+
currentVersion: options.currentVersion,
|
|
7990
|
+
latestVersion: available && isNewerVersion(available.latestVersion, version) ? available.latestVersion : version,
|
|
7991
|
+
installedVersion: version,
|
|
7992
|
+
restartBlocked: reason
|
|
7993
|
+
};
|
|
7994
|
+
options.onChange(available);
|
|
7995
|
+
},
|
|
7974
7996
|
stop() {
|
|
7975
7997
|
stopped = true;
|
|
7976
7998
|
}
|
|
@@ -8037,6 +8059,70 @@ async function openUpdateTerminal(platform = process.platform, run3 = launch) {
|
|
|
8037
8059
|
throw new Error("\u627E\u4E0D\u5230\u53EF\u7528\u7684\u7D42\u7AEF\u6A5F\uFF0C\u8ACB\u624B\u52D5\u57F7\u884C\u66F4\u65B0\u6307\u4EE4\u3002");
|
|
8038
8060
|
}
|
|
8039
8061
|
|
|
8062
|
+
// packages/cli/src/self-update.ts
|
|
8063
|
+
import fs22 from "node:fs";
|
|
8064
|
+
import path26 from "node:path";
|
|
8065
|
+
import { execFile as execFile10 } from "node:child_process";
|
|
8066
|
+
import { promisify as promisify7 } from "node:util";
|
|
8067
|
+
var execFileAsync2 = promisify7(execFile10);
|
|
8068
|
+
var INSTALL_POLL_INTERVAL_MS = 3e3;
|
|
8069
|
+
var PROBE_RETRY_MS = 3e4;
|
|
8070
|
+
var HOST_TOKEN_ENV = "VIBEFLOW_HOST_TOKEN";
|
|
8071
|
+
function readInstalledVersion(packageRoot) {
|
|
8072
|
+
try {
|
|
8073
|
+
const pkg = JSON.parse(fs22.readFileSync(path26.join(packageRoot, "package.json"), "utf8"));
|
|
8074
|
+
return typeof pkg.version === "string" ? pkg.version : null;
|
|
8075
|
+
} catch {
|
|
8076
|
+
return null;
|
|
8077
|
+
}
|
|
8078
|
+
}
|
|
8079
|
+
async function probeCli(cliEntry) {
|
|
8080
|
+
try {
|
|
8081
|
+
const { stdout } = await execFileAsync2(process.execPath, [cliEntry, "--version"], { timeout: 15e3, windowsHide: true });
|
|
8082
|
+
return stdout.trim();
|
|
8083
|
+
} catch {
|
|
8084
|
+
return null;
|
|
8085
|
+
}
|
|
8086
|
+
}
|
|
8087
|
+
function watchInstalledVersion(options) {
|
|
8088
|
+
const readVersion2 = options.readVersion ?? readInstalledVersion;
|
|
8089
|
+
const probe = options.probe ?? probeCli;
|
|
8090
|
+
const now = options.now ?? Date.now;
|
|
8091
|
+
let previous = null;
|
|
8092
|
+
let installed = null;
|
|
8093
|
+
const probedAt = /* @__PURE__ */ new Map();
|
|
8094
|
+
let busy = false;
|
|
8095
|
+
const tick = async () => {
|
|
8096
|
+
if (busy) return;
|
|
8097
|
+
busy = true;
|
|
8098
|
+
try {
|
|
8099
|
+
const version = readVersion2(options.packageRoot);
|
|
8100
|
+
const seenTwice = version !== null && version === previous;
|
|
8101
|
+
previous = version;
|
|
8102
|
+
if (!version || !isNewerVersion(version, options.currentVersion)) return;
|
|
8103
|
+
if (installed !== version) {
|
|
8104
|
+
const last = probedAt.get(version);
|
|
8105
|
+
if (!seenTwice || last !== void 0 && now() - last < PROBE_RETRY_MS) return;
|
|
8106
|
+
probedAt.set(version, now());
|
|
8107
|
+
if (await probe(options.cliEntry) !== version) return;
|
|
8108
|
+
installed = version;
|
|
8109
|
+
}
|
|
8110
|
+
options.onInstalled(version);
|
|
8111
|
+
} finally {
|
|
8112
|
+
busy = false;
|
|
8113
|
+
}
|
|
8114
|
+
};
|
|
8115
|
+
return { tick };
|
|
8116
|
+
}
|
|
8117
|
+
function relaunchArgs(cliEntry, options) {
|
|
8118
|
+
return [cliEntry, "web", "--no-open", "--store-path", options.userDataDir, "--port", String(options.port), "--backend", options.backend];
|
|
8119
|
+
}
|
|
8120
|
+
function takeHandedOverToken(env = process.env) {
|
|
8121
|
+
const token = env[HOST_TOKEN_ENV];
|
|
8122
|
+
delete env[HOST_TOKEN_ENV];
|
|
8123
|
+
return token && /^[A-Za-z0-9_-]{43}$/.test(token) ? token : void 0;
|
|
8124
|
+
}
|
|
8125
|
+
|
|
8040
8126
|
// packages/cli/src/host.ts
|
|
8041
8127
|
var DEFAULT_HOST_PORT = 47820;
|
|
8042
8128
|
async function waitForPort(userDataDir, timeoutMs = 1e4) {
|
|
@@ -8060,7 +8146,7 @@ async function startHost(options) {
|
|
|
8060
8146
|
const lock = {
|
|
8061
8147
|
pid: process.pid,
|
|
8062
8148
|
port: 0,
|
|
8063
|
-
token: randomBytes2(32).toString("base64url"),
|
|
8149
|
+
token: options.token ?? randomBytes2(32).toString("base64url"),
|
|
8064
8150
|
version: options.version,
|
|
8065
8151
|
startedAt: Date.now()
|
|
8066
8152
|
};
|
|
@@ -8079,8 +8165,10 @@ async function startHost(options) {
|
|
|
8079
8165
|
});
|
|
8080
8166
|
const core = createCore({ sessions, bus, version: options.version });
|
|
8081
8167
|
let updateTimer = null;
|
|
8168
|
+
let installTimer = null;
|
|
8169
|
+
const packageRoot = options.cliEntry ? path27.dirname(path27.dirname(options.cliEntry)) : null;
|
|
8082
8170
|
const updates = createUpdateMonitor({
|
|
8083
|
-
packageName: options.sourceRoot || !
|
|
8171
|
+
packageName: options.sourceRoot || !packageRoot ? null : packageName(packageRoot),
|
|
8084
8172
|
currentVersion: options.version,
|
|
8085
8173
|
onChange: (update) => bus.emit("update:available", update)
|
|
8086
8174
|
});
|
|
@@ -8118,19 +8206,45 @@ async function startHost(options) {
|
|
|
8118
8206
|
updateLock(options.userDataDir, lock);
|
|
8119
8207
|
const stopWatch = core.watchStore();
|
|
8120
8208
|
core.warmModelCatalog();
|
|
8121
|
-
|
|
8209
|
+
let requestRestart = () => {
|
|
8210
|
+
};
|
|
8211
|
+
const restartRequested = new Promise((resolve) => {
|
|
8212
|
+
requestRestart = resolve;
|
|
8213
|
+
});
|
|
8214
|
+
if (updates.supported && options.cliEntry && packageRoot) {
|
|
8122
8215
|
checkUpdates();
|
|
8123
8216
|
updateTimer = setInterval(checkUpdates, UPDATE_CHECK_INTERVAL_MS);
|
|
8217
|
+
const installs = watchInstalledVersion({
|
|
8218
|
+
packageRoot,
|
|
8219
|
+
cliEntry: options.cliEntry,
|
|
8220
|
+
currentVersion: options.version,
|
|
8221
|
+
onInstalled: (version) => {
|
|
8222
|
+
if (!options.autoRestart) {
|
|
8223
|
+
updates.markInstalled(version, "\u7D42\u7AEF\u6A5F\u4ECB\u9762\uFF08TUI\uFF09host \u4E0D\u6703\u81EA\u52D5\u91CD\u65B0\u555F\u52D5");
|
|
8224
|
+
return;
|
|
8225
|
+
}
|
|
8226
|
+
void (sessions.kind === "pty" ? sessions.list() : Promise.resolve([])).then((running) => {
|
|
8227
|
+
if (running.length > 0) {
|
|
8228
|
+
updates.markInstalled(version, `\u6709 ${running.length} \u500B\u57F7\u884C\u4E2D\u7684\u7D42\u7AEF\u6A5F session\uFF0C\u91CD\u65B0\u555F\u52D5\u6703\u4E2D\u65B7\u5B83\u5011\uFF1B\u5168\u90E8\u7D50\u675F\u5F8C\u6703\u81EA\u52D5\u91CD\u65B0\u555F\u52D5`);
|
|
8229
|
+
} else {
|
|
8230
|
+
requestRestart(version);
|
|
8231
|
+
}
|
|
8232
|
+
}).catch(() => {
|
|
8233
|
+
});
|
|
8234
|
+
}
|
|
8235
|
+
});
|
|
8236
|
+
installTimer = setInterval(() => void installs.tick(), INSTALL_POLL_INTERVAL_MS);
|
|
8124
8237
|
}
|
|
8125
8238
|
const stop = () => stopping ??= (async () => {
|
|
8126
8239
|
updates.stop();
|
|
8127
8240
|
if (updateTimer) clearInterval(updateTimer);
|
|
8241
|
+
if (installTimer) clearInterval(installTimer);
|
|
8128
8242
|
stopWatch();
|
|
8129
8243
|
core.shutdown();
|
|
8130
8244
|
await server.close();
|
|
8131
8245
|
releaseLock(options.userDataDir);
|
|
8132
8246
|
})();
|
|
8133
|
-
return { kind: "started", host: { core, server, lock, stop } };
|
|
8247
|
+
return { kind: "started", host: { core, server, lock, restartRequested, stop } };
|
|
8134
8248
|
} catch (err) {
|
|
8135
8249
|
releaseLock(options.userDataDir);
|
|
8136
8250
|
throw err;
|
|
@@ -8140,7 +8254,7 @@ function loginUrl(lock, pathname = "/home/") {
|
|
|
8140
8254
|
return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
|
|
8141
8255
|
}
|
|
8142
8256
|
function isWebDir(dir) {
|
|
8143
|
-
return
|
|
8257
|
+
return fs23.existsSync(path27.join(dir, "home", "index.html"));
|
|
8144
8258
|
}
|
|
8145
8259
|
|
|
8146
8260
|
// packages/cli/src/remote.ts
|
|
@@ -8257,14 +8371,14 @@ function connectToHost(lock) {
|
|
|
8257
8371
|
}
|
|
8258
8372
|
|
|
8259
8373
|
// packages/cli/src/doctor.ts
|
|
8260
|
-
import
|
|
8261
|
-
import
|
|
8262
|
-
import { execFile as
|
|
8263
|
-
import { promisify as
|
|
8374
|
+
import fs24 from "fs";
|
|
8375
|
+
import path28 from "path";
|
|
8376
|
+
import { execFile as execFile11 } from "child_process";
|
|
8377
|
+
import { promisify as promisify8 } from "util";
|
|
8264
8378
|
init_env();
|
|
8265
8379
|
init_git_bash();
|
|
8266
8380
|
init_tmux_backend();
|
|
8267
|
-
var pexec6 =
|
|
8381
|
+
var pexec6 = promisify8(execFile11);
|
|
8268
8382
|
function parseVersion(text) {
|
|
8269
8383
|
const m = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(text);
|
|
8270
8384
|
return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : [];
|
|
@@ -8398,10 +8512,10 @@ async function runDoctor(options) {
|
|
|
8398
8512
|
});
|
|
8399
8513
|
}
|
|
8400
8514
|
try {
|
|
8401
|
-
|
|
8402
|
-
const probe =
|
|
8403
|
-
|
|
8404
|
-
|
|
8515
|
+
fs24.mkdirSync(options.userDataDir, { recursive: true });
|
|
8516
|
+
const probe = path28.join(options.userDataDir, `.doctor-${process.pid}`);
|
|
8517
|
+
fs24.writeFileSync(probe, "");
|
|
8518
|
+
fs24.rmSync(probe);
|
|
8405
8519
|
checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
|
|
8406
8520
|
} catch (err) {
|
|
8407
8521
|
checks.push({
|
|
@@ -8441,7 +8555,7 @@ Usage:
|
|
|
8441
8555
|
vibeflow open <path> Add a project folder to the recent list, then open the Web UI
|
|
8442
8556
|
vibeflow doctor Check Node, git, tmux, node-pty and the agent CLIs
|
|
8443
8557
|
(--skip agents,web,\u2026 leaves checks out, e.g. on CI)
|
|
8444
|
-
vibeflow update Install the latest global npm release (
|
|
8558
|
+
vibeflow update Install the latest global npm release (a running Web UI host restarts into it)
|
|
8445
8559
|
vibeflow task \u2026 Create / update cards; add artifacts (vibeflow task --help)
|
|
8446
8560
|
|
|
8447
8561
|
Options:
|
|
@@ -8456,24 +8570,24 @@ Options:
|
|
|
8456
8570
|
`.trim();
|
|
8457
8571
|
function detectLayout() {
|
|
8458
8572
|
const file = fileURLToPath(import.meta.url);
|
|
8459
|
-
const here =
|
|
8460
|
-
const bundledWeb =
|
|
8461
|
-
if (isWebDir(bundledWeb) ||
|
|
8573
|
+
const here = path31.dirname(file);
|
|
8574
|
+
const bundledWeb = path31.join(here, "web");
|
|
8575
|
+
if (isWebDir(bundledWeb) || path31.basename(here) === "dist") {
|
|
8462
8576
|
return {
|
|
8463
|
-
version: readVersion(
|
|
8577
|
+
version: readVersion(path31.join(here, "..", "package.json")),
|
|
8464
8578
|
webDir: isWebDir(bundledWeb) ? bundledWeb : null,
|
|
8465
8579
|
sourceRoot: null,
|
|
8466
8580
|
cliEntry: file
|
|
8467
8581
|
};
|
|
8468
8582
|
}
|
|
8469
|
-
const root =
|
|
8583
|
+
const root = path31.resolve(here, "..", "..", "..");
|
|
8470
8584
|
const override = process.env.VIBEFLOW_WEB_DIR;
|
|
8471
|
-
const webDir = override && isWebDir(override) ? override : isWebDir(
|
|
8472
|
-
return { version: readVersion(
|
|
8585
|
+
const webDir = override && isWebDir(override) ? override : isWebDir(path31.join(root, "app")) ? path31.join(root, "app") : null;
|
|
8586
|
+
return { version: readVersion(path31.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
|
|
8473
8587
|
}
|
|
8474
8588
|
function readVersion(pkgPath) {
|
|
8475
8589
|
try {
|
|
8476
|
-
return JSON.parse(
|
|
8590
|
+
return JSON.parse(fs26.readFileSync(pkgPath, "utf8")).version;
|
|
8477
8591
|
} catch {
|
|
8478
8592
|
return "0.0.0";
|
|
8479
8593
|
}
|
|
@@ -8497,6 +8611,7 @@ function parse(argv) {
|
|
|
8497
8611
|
strict: true
|
|
8498
8612
|
});
|
|
8499
8613
|
}
|
|
8614
|
+
var handedOverToken = takeHandedOverToken();
|
|
8500
8615
|
function hostOptions(values, layout, userDataDir, withWeb = true) {
|
|
8501
8616
|
const backend = values.backend ?? "auto";
|
|
8502
8617
|
if (!["auto", "tmux", "pty"].includes(backend)) throw new Error("--backend must be auto | tmux | pty");
|
|
@@ -8509,7 +8624,8 @@ function hostOptions(values, layout, userDataDir, withWeb = true) {
|
|
|
8509
8624
|
sourceRoot: layout.sourceRoot,
|
|
8510
8625
|
cliEntry: layout.cliEntry,
|
|
8511
8626
|
backend,
|
|
8512
|
-
port
|
|
8627
|
+
port,
|
|
8628
|
+
token: handedOverToken
|
|
8513
8629
|
};
|
|
8514
8630
|
}
|
|
8515
8631
|
async function openBrowser(url) {
|
|
@@ -8518,6 +8634,33 @@ async function openBrowser(url) {
|
|
|
8518
8634
|
} catch {
|
|
8519
8635
|
}
|
|
8520
8636
|
}
|
|
8637
|
+
async function relaunchHost(host, cliEntry, userDataDir, version) {
|
|
8638
|
+
const port = host.lock.port;
|
|
8639
|
+
const token = host.lock.token;
|
|
8640
|
+
const backend = host.core.sessions.kind;
|
|
8641
|
+
console.log(`
|
|
8642
|
+
\u5DF2\u5B89\u88DD VibeFlow ${version}\uFF0C\u6B63\u5728\u91CD\u65B0\u555F\u52D5 host\u2026`);
|
|
8643
|
+
await host.stop();
|
|
8644
|
+
const child = spawn9(process.execPath, relaunchArgs(cliEntry, { userDataDir, port, backend }), {
|
|
8645
|
+
stdio: "inherit",
|
|
8646
|
+
env: { ...process.env, [HOST_TOKEN_ENV]: token }
|
|
8647
|
+
});
|
|
8648
|
+
const forward = () => {
|
|
8649
|
+
child.kill("SIGTERM");
|
|
8650
|
+
};
|
|
8651
|
+
process.on("SIGINT", () => {
|
|
8652
|
+
});
|
|
8653
|
+
process.on("SIGHUP", () => {
|
|
8654
|
+
});
|
|
8655
|
+
process.on("SIGTERM", forward);
|
|
8656
|
+
return new Promise((resolve) => {
|
|
8657
|
+
child.once("error", (err) => {
|
|
8658
|
+
console.error(`\u7121\u6CD5\u555F\u52D5\u65B0\u7248 host\uFF1A${err.message}\u3002\u8ACB\u57F7\u884C vibeflow \u91CD\u65B0\u555F\u52D5\u3002`);
|
|
8659
|
+
resolve(1);
|
|
8660
|
+
});
|
|
8661
|
+
child.once("exit", (code2, signal) => resolve(code2 ?? (signal ? 1 : 0)));
|
|
8662
|
+
});
|
|
8663
|
+
}
|
|
8521
8664
|
function stopOnSignals(host) {
|
|
8522
8665
|
return new Promise((resolve) => {
|
|
8523
8666
|
const stop = () => {
|
|
@@ -8540,7 +8683,7 @@ async function cmdWeb(values, layout, userDataDir, pathname = "/home/") {
|
|
|
8540
8683
|
console.error("\u627E\u4E0D\u5230 Web UI \u7684\u975C\u614B\u6A94\u3002\u5F9E\u539F\u59CB\u78BC\u57F7\u884C\u6642\u8ACB\u5148\u57F7\u884C\uFF1Anpm run build:web");
|
|
8541
8684
|
return 1;
|
|
8542
8685
|
}
|
|
8543
|
-
const started = await startHost(hostOptions(values, layout, userDataDir));
|
|
8686
|
+
const started = await startHost({ ...hostOptions(values, layout, userDataDir), autoRestart: true });
|
|
8544
8687
|
if (started.kind === "running") return cmdWeb(values, layout, userDataDir, pathname);
|
|
8545
8688
|
const { host } = started;
|
|
8546
8689
|
const url = loginUrl(host.lock, pathname);
|
|
@@ -8553,8 +8696,11 @@ async function cmdWeb(values, layout, userDataDir, pathname = "/home/") {
|
|
|
8553
8696
|
${formatChecks(checks)}
|
|
8554
8697
|
`);
|
|
8555
8698
|
if (!values["no-open"]) await openBrowser(url);
|
|
8556
|
-
await
|
|
8557
|
-
|
|
8699
|
+
const ended = await Promise.race([
|
|
8700
|
+
stopOnSignals(host).then(() => null),
|
|
8701
|
+
host.restartRequested
|
|
8702
|
+
]);
|
|
8703
|
+
return ended === null || !layout.cliEntry ? 0 : relaunchHost(host, layout.cliEntry, userDataDir, ended);
|
|
8558
8704
|
}
|
|
8559
8705
|
async function cmdTui(values, layout, userDataDir) {
|
|
8560
8706
|
const { runTui: runTui2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
@@ -8679,8 +8825,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
|
|
|
8679
8825
|
console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
|
|
8680
8826
|
return 1;
|
|
8681
8827
|
}
|
|
8682
|
-
const dir =
|
|
8683
|
-
if (!
|
|
8828
|
+
const dir = path31.resolve(target);
|
|
8829
|
+
if (!fs26.existsSync(dir) || !fs26.statSync(dir).isDirectory()) {
|
|
8684
8830
|
console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
|
|
8685
8831
|
return 1;
|
|
8686
8832
|
}
|
package/dist/web/404/index.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/
|
|
1
|
+
<!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/f8f64877c884206d.css" as="style"/><link rel="stylesheet" href="/_next/static/css/f8f64877c884206d.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-33344f7100190436.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-2571a2564f593899.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/76irYAZpsrcN0YAWmxJ47/_buildManifest.js" defer=""></script><script src="/_next/static/76irYAZpsrcN0YAWmxJ47/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"76irYAZpsrcN0YAWmxJ47","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
|
package/dist/web/404.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/
|
|
1
|
+
<!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><link rel="icon" href="/favicon.ico" sizes="any" data-next-head=""/><link rel="icon" href="/logo.svg" type="image/svg+xml" data-next-head=""/><link rel="apple-touch-icon" href="/apple-touch-icon.png" data-next-head=""/><title data-next-head="">404: This page could not be found</title><link rel="preload" href="/_next/static/css/f8f64877c884206d.css" as="style"/><link rel="stylesheet" href="/_next/static/css/f8f64877c884206d.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-33344f7100190436.js" defer=""></script><script src="/_next/static/chunks/framework-b92a367cf1ad4bf7.js" defer=""></script><script src="/_next/static/chunks/main-ec7fd4c371d06729.js" defer=""></script><script src="/_next/static/chunks/pages/_app-2571a2564f593899.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/76irYAZpsrcN0YAWmxJ47/_buildManifest.js" defer=""></script><script src="/_next/static/76irYAZpsrcN0YAWmxJ47/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"76irYAZpsrcN0YAWmxJ47","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
|
package/dist/web/_next/static/{xoex8UtSV4DX9eSoX3lTZ → 76irYAZpsrcN0YAWmxJ47}/_buildManifest.js
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
self.__BUILD_MANIFEST={__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},__routerFilterStatic:{numItems:0,errorRate:1e-4,numBits:0,numHashes:null,bitArray:[]},__routerFilterDynamic:{numItems:0,errorRate:1e-4,numBits:0,numHashes:null,bitArray:[]},"/_error":["static/chunks/pages/_error-8f65b0f8749f42d2.js"],"/home":["static/chunks/9296-f476f95a89709fef.js","static/chunks/pages/home-
|
|
1
|
+
self.__BUILD_MANIFEST={__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},__routerFilterStatic:{numItems:0,errorRate:1e-4,numBits:0,numHashes:null,bitArray:[]},__routerFilterDynamic:{numItems:0,errorRate:1e-4,numBits:0,numHashes:null,bitArray:[]},"/_error":["static/chunks/pages/_error-8f65b0f8749f42d2.js"],"/home":["static/chunks/9296-f476f95a89709fef.js","static/chunks/pages/home-307d0d7c49e4ee48.js"],sortedPages:["/_app","/_error","/home"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|