@kw-yeh/vibeflow 4.11.17 → 4.11.18

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 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 fs24 from "fs";
1093
+ import fs25 from "fs";
1094
1094
  import os8 from "os";
1095
- import path28 from "path";
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 = fs24.mkdtempSync(path28.join(os8.tmpdir(), "vibeflow-"));
1222
- const file = path28.join(dir, "description.md");
1221
+ const dir = fs25.mkdtempSync(path29.join(os8.tmpdir(), "vibeflow-"));
1222
+ const file = path29.join(dir, "description.md");
1223
1223
  try {
1224
- fs24.writeFileSync(file, text);
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: fs24.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
1229
+ return { text: fs25.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
1230
1230
  } finally {
1231
- fs24.rmSync(dir, { recursive: true, force: true });
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(path28.resolve(value2.replace(/^~(?=$|\/)/, os8.homedir())));
1302
+ if (value2) setProject(path29.resolve(value2.replace(/^~(?=$|\/)/, os8.homedir())));
1303
1303
  } else if (editing === "attachments" && value2) {
1304
- const file = path28.resolve(value2.replace(/^~(?=$|\/)/, os8.homedir()));
1305
- if (!fs24.existsSync(file) || !fs24.statSync(file).isFile()) {
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) => path28.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
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"}/${path28.basename(s.projectPath)}`;
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 path29 from "path";
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 = path29.join(opts.userDataDir, "tmux.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 fs25 from "fs";
1980
- import path30 from "path";
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 fs22 from "fs";
4697
- import path26 from "path";
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
@@ -7917,7 +7918,7 @@ function runUpdate(options) {
7917
7918
  error(`\u66F4\u65B0\u5931\u6557\uFF1A${result.error?.message ?? `npm \u7D50\u675F\u78BC ${result.status ?? "unknown"}`}`);
7918
7919
  return result.status && result.status > 0 ? result.status : 1;
7919
7920
  }
7920
- log("\u66F4\u65B0\u5B8C\u6210\u3002\u82E5 VibeFlow host \u6B63\u5728\u57F7\u884C\uFF0C\u8ACB\u81EA\u884C\u91CD\u65B0\u555F\u52D5\u4EE5\u8F09\u5165\u65B0\u7248\u672C\u3002");
7921
+ 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
7922
  return 0;
7922
7923
  }
7923
7924
 
@@ -7971,6 +7972,17 @@ function createUpdateMonitor(options) {
7971
7972
  }
7972
7973
  return next ? { status: "available", ...next } : { status: "current", currentVersion: options.currentVersion };
7973
7974
  },
7975
+ /** A newer version is on disk but the host must be restarted by hand to run it. */
7976
+ markInstalled(version, reason) {
7977
+ if (stopped || available?.installedVersion === version && available.restartBlocked === reason) return;
7978
+ available = {
7979
+ currentVersion: options.currentVersion,
7980
+ latestVersion: available && isNewerVersion(available.latestVersion, version) ? available.latestVersion : version,
7981
+ installedVersion: version,
7982
+ restartBlocked: reason
7983
+ };
7984
+ options.onChange(available);
7985
+ },
7974
7986
  stop() {
7975
7987
  stopped = true;
7976
7988
  }
@@ -8037,6 +8049,70 @@ async function openUpdateTerminal(platform = process.platform, run3 = launch) {
8037
8049
  throw new Error("\u627E\u4E0D\u5230\u53EF\u7528\u7684\u7D42\u7AEF\u6A5F\uFF0C\u8ACB\u624B\u52D5\u57F7\u884C\u66F4\u65B0\u6307\u4EE4\u3002");
8038
8050
  }
8039
8051
 
8052
+ // packages/cli/src/self-update.ts
8053
+ import fs22 from "node:fs";
8054
+ import path26 from "node:path";
8055
+ import { execFile as execFile10 } from "node:child_process";
8056
+ import { promisify as promisify7 } from "node:util";
8057
+ var execFileAsync2 = promisify7(execFile10);
8058
+ var INSTALL_POLL_INTERVAL_MS = 3e3;
8059
+ var PROBE_RETRY_MS = 3e4;
8060
+ var HOST_TOKEN_ENV = "VIBEFLOW_HOST_TOKEN";
8061
+ function readInstalledVersion(packageRoot) {
8062
+ try {
8063
+ const pkg = JSON.parse(fs22.readFileSync(path26.join(packageRoot, "package.json"), "utf8"));
8064
+ return typeof pkg.version === "string" ? pkg.version : null;
8065
+ } catch {
8066
+ return null;
8067
+ }
8068
+ }
8069
+ async function probeCli(cliEntry) {
8070
+ try {
8071
+ const { stdout } = await execFileAsync2(process.execPath, [cliEntry, "--version"], { timeout: 15e3, windowsHide: true });
8072
+ return stdout.trim();
8073
+ } catch {
8074
+ return null;
8075
+ }
8076
+ }
8077
+ function watchInstalledVersion(options) {
8078
+ const readVersion2 = options.readVersion ?? readInstalledVersion;
8079
+ const probe = options.probe ?? probeCli;
8080
+ const now = options.now ?? Date.now;
8081
+ let previous = null;
8082
+ let installed = null;
8083
+ const probedAt = /* @__PURE__ */ new Map();
8084
+ let busy = false;
8085
+ const tick = async () => {
8086
+ if (busy) return;
8087
+ busy = true;
8088
+ try {
8089
+ const version = readVersion2(options.packageRoot);
8090
+ const seenTwice = version !== null && version === previous;
8091
+ previous = version;
8092
+ if (!version || !isNewerVersion(version, options.currentVersion)) return;
8093
+ if (installed !== version) {
8094
+ const last = probedAt.get(version);
8095
+ if (!seenTwice || last !== void 0 && now() - last < PROBE_RETRY_MS) return;
8096
+ probedAt.set(version, now());
8097
+ if (await probe(options.cliEntry) !== version) return;
8098
+ installed = version;
8099
+ }
8100
+ options.onInstalled(version);
8101
+ } finally {
8102
+ busy = false;
8103
+ }
8104
+ };
8105
+ return { tick };
8106
+ }
8107
+ function relaunchArgs(cliEntry, options) {
8108
+ return [cliEntry, "web", "--no-open", "--store-path", options.userDataDir, "--port", String(options.port), "--backend", options.backend];
8109
+ }
8110
+ function takeHandedOverToken(env = process.env) {
8111
+ const token = env[HOST_TOKEN_ENV];
8112
+ delete env[HOST_TOKEN_ENV];
8113
+ return token && /^[A-Za-z0-9_-]{43}$/.test(token) ? token : void 0;
8114
+ }
8115
+
8040
8116
  // packages/cli/src/host.ts
8041
8117
  var DEFAULT_HOST_PORT = 47820;
8042
8118
  async function waitForPort(userDataDir, timeoutMs = 1e4) {
@@ -8060,7 +8136,7 @@ async function startHost(options) {
8060
8136
  const lock = {
8061
8137
  pid: process.pid,
8062
8138
  port: 0,
8063
- token: randomBytes2(32).toString("base64url"),
8139
+ token: options.token ?? randomBytes2(32).toString("base64url"),
8064
8140
  version: options.version,
8065
8141
  startedAt: Date.now()
8066
8142
  };
@@ -8079,8 +8155,10 @@ async function startHost(options) {
8079
8155
  });
8080
8156
  const core = createCore({ sessions, bus, version: options.version });
8081
8157
  let updateTimer = null;
8158
+ let installTimer = null;
8159
+ const packageRoot = options.cliEntry ? path27.dirname(path27.dirname(options.cliEntry)) : null;
8082
8160
  const updates = createUpdateMonitor({
8083
- packageName: options.sourceRoot || !options.cliEntry ? null : packageName(path26.dirname(path26.dirname(options.cliEntry))),
8161
+ packageName: options.sourceRoot || !packageRoot ? null : packageName(packageRoot),
8084
8162
  currentVersion: options.version,
8085
8163
  onChange: (update) => bus.emit("update:available", update)
8086
8164
  });
@@ -8118,19 +8196,45 @@ async function startHost(options) {
8118
8196
  updateLock(options.userDataDir, lock);
8119
8197
  const stopWatch = core.watchStore();
8120
8198
  core.warmModelCatalog();
8121
- if (updates.supported) {
8199
+ let requestRestart = () => {
8200
+ };
8201
+ const restartRequested = new Promise((resolve) => {
8202
+ requestRestart = resolve;
8203
+ });
8204
+ if (updates.supported && options.cliEntry && packageRoot) {
8122
8205
  checkUpdates();
8123
8206
  updateTimer = setInterval(checkUpdates, UPDATE_CHECK_INTERVAL_MS);
8207
+ const installs = watchInstalledVersion({
8208
+ packageRoot,
8209
+ cliEntry: options.cliEntry,
8210
+ currentVersion: options.version,
8211
+ onInstalled: (version) => {
8212
+ if (!options.autoRestart) {
8213
+ updates.markInstalled(version, "\u7D42\u7AEF\u6A5F\u4ECB\u9762\uFF08TUI\uFF09host \u4E0D\u6703\u81EA\u52D5\u91CD\u65B0\u555F\u52D5");
8214
+ return;
8215
+ }
8216
+ void (sessions.kind === "pty" ? sessions.list() : Promise.resolve([])).then((running) => {
8217
+ if (running.length > 0) {
8218
+ 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`);
8219
+ } else {
8220
+ requestRestart(version);
8221
+ }
8222
+ }).catch(() => {
8223
+ });
8224
+ }
8225
+ });
8226
+ installTimer = setInterval(() => void installs.tick(), INSTALL_POLL_INTERVAL_MS);
8124
8227
  }
8125
8228
  const stop = () => stopping ??= (async () => {
8126
8229
  updates.stop();
8127
8230
  if (updateTimer) clearInterval(updateTimer);
8231
+ if (installTimer) clearInterval(installTimer);
8128
8232
  stopWatch();
8129
8233
  core.shutdown();
8130
8234
  await server.close();
8131
8235
  releaseLock(options.userDataDir);
8132
8236
  })();
8133
- return { kind: "started", host: { core, server, lock, stop } };
8237
+ return { kind: "started", host: { core, server, lock, restartRequested, stop } };
8134
8238
  } catch (err) {
8135
8239
  releaseLock(options.userDataDir);
8136
8240
  throw err;
@@ -8140,7 +8244,7 @@ function loginUrl(lock, pathname = "/home/") {
8140
8244
  return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
8141
8245
  }
8142
8246
  function isWebDir(dir) {
8143
- return fs22.existsSync(path26.join(dir, "home", "index.html"));
8247
+ return fs23.existsSync(path27.join(dir, "home", "index.html"));
8144
8248
  }
8145
8249
 
8146
8250
  // packages/cli/src/remote.ts
@@ -8257,14 +8361,14 @@ function connectToHost(lock) {
8257
8361
  }
8258
8362
 
8259
8363
  // packages/cli/src/doctor.ts
8260
- import fs23 from "fs";
8261
- import path27 from "path";
8262
- import { execFile as execFile10 } from "child_process";
8263
- import { promisify as promisify7 } from "util";
8364
+ import fs24 from "fs";
8365
+ import path28 from "path";
8366
+ import { execFile as execFile11 } from "child_process";
8367
+ import { promisify as promisify8 } from "util";
8264
8368
  init_env();
8265
8369
  init_git_bash();
8266
8370
  init_tmux_backend();
8267
- var pexec6 = promisify7(execFile10);
8371
+ var pexec6 = promisify8(execFile11);
8268
8372
  function parseVersion(text) {
8269
8373
  const m = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(text);
8270
8374
  return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : [];
@@ -8398,10 +8502,10 @@ async function runDoctor(options) {
8398
8502
  });
8399
8503
  }
8400
8504
  try {
8401
- fs23.mkdirSync(options.userDataDir, { recursive: true });
8402
- const probe = path27.join(options.userDataDir, `.doctor-${process.pid}`);
8403
- fs23.writeFileSync(probe, "");
8404
- fs23.rmSync(probe);
8505
+ fs24.mkdirSync(options.userDataDir, { recursive: true });
8506
+ const probe = path28.join(options.userDataDir, `.doctor-${process.pid}`);
8507
+ fs24.writeFileSync(probe, "");
8508
+ fs24.rmSync(probe);
8405
8509
  checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
8406
8510
  } catch (err) {
8407
8511
  checks.push({
@@ -8441,7 +8545,7 @@ Usage:
8441
8545
  vibeflow open <path> Add a project folder to the recent list, then open the Web UI
8442
8546
  vibeflow doctor Check Node, git, tmux, node-pty and the agent CLIs
8443
8547
  (--skip agents,web,\u2026 leaves checks out, e.g. on CI)
8444
- vibeflow update Install the latest global npm release (restart the host afterward)
8548
+ vibeflow update Install the latest global npm release (a running Web UI host restarts into it)
8445
8549
  vibeflow task \u2026 Create / update cards; add artifacts (vibeflow task --help)
8446
8550
 
8447
8551
  Options:
@@ -8456,24 +8560,24 @@ Options:
8456
8560
  `.trim();
8457
8561
  function detectLayout() {
8458
8562
  const file = fileURLToPath(import.meta.url);
8459
- const here = path30.dirname(file);
8460
- const bundledWeb = path30.join(here, "web");
8461
- if (isWebDir(bundledWeb) || path30.basename(here) === "dist") {
8563
+ const here = path31.dirname(file);
8564
+ const bundledWeb = path31.join(here, "web");
8565
+ if (isWebDir(bundledWeb) || path31.basename(here) === "dist") {
8462
8566
  return {
8463
- version: readVersion(path30.join(here, "..", "package.json")),
8567
+ version: readVersion(path31.join(here, "..", "package.json")),
8464
8568
  webDir: isWebDir(bundledWeb) ? bundledWeb : null,
8465
8569
  sourceRoot: null,
8466
8570
  cliEntry: file
8467
8571
  };
8468
8572
  }
8469
- const root = path30.resolve(here, "..", "..", "..");
8573
+ const root = path31.resolve(here, "..", "..", "..");
8470
8574
  const override = process.env.VIBEFLOW_WEB_DIR;
8471
- const webDir = override && isWebDir(override) ? override : isWebDir(path30.join(root, "app")) ? path30.join(root, "app") : null;
8472
- return { version: readVersion(path30.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
8575
+ const webDir = override && isWebDir(override) ? override : isWebDir(path31.join(root, "app")) ? path31.join(root, "app") : null;
8576
+ return { version: readVersion(path31.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
8473
8577
  }
8474
8578
  function readVersion(pkgPath) {
8475
8579
  try {
8476
- return JSON.parse(fs25.readFileSync(pkgPath, "utf8")).version;
8580
+ return JSON.parse(fs26.readFileSync(pkgPath, "utf8")).version;
8477
8581
  } catch {
8478
8582
  return "0.0.0";
8479
8583
  }
@@ -8497,6 +8601,7 @@ function parse(argv) {
8497
8601
  strict: true
8498
8602
  });
8499
8603
  }
8604
+ var handedOverToken = takeHandedOverToken();
8500
8605
  function hostOptions(values, layout, userDataDir, withWeb = true) {
8501
8606
  const backend = values.backend ?? "auto";
8502
8607
  if (!["auto", "tmux", "pty"].includes(backend)) throw new Error("--backend must be auto | tmux | pty");
@@ -8509,7 +8614,8 @@ function hostOptions(values, layout, userDataDir, withWeb = true) {
8509
8614
  sourceRoot: layout.sourceRoot,
8510
8615
  cliEntry: layout.cliEntry,
8511
8616
  backend,
8512
- port
8617
+ port,
8618
+ token: handedOverToken
8513
8619
  };
8514
8620
  }
8515
8621
  async function openBrowser(url) {
@@ -8518,6 +8624,33 @@ async function openBrowser(url) {
8518
8624
  } catch {
8519
8625
  }
8520
8626
  }
8627
+ async function relaunchHost(host, cliEntry, userDataDir, version) {
8628
+ const port = host.lock.port;
8629
+ const token = host.lock.token;
8630
+ const backend = host.core.sessions.kind;
8631
+ console.log(`
8632
+ \u5DF2\u5B89\u88DD VibeFlow ${version}\uFF0C\u6B63\u5728\u91CD\u65B0\u555F\u52D5 host\u2026`);
8633
+ await host.stop();
8634
+ const child = spawn9(process.execPath, relaunchArgs(cliEntry, { userDataDir, port, backend }), {
8635
+ stdio: "inherit",
8636
+ env: { ...process.env, [HOST_TOKEN_ENV]: token }
8637
+ });
8638
+ const forward = () => {
8639
+ child.kill("SIGTERM");
8640
+ };
8641
+ process.on("SIGINT", () => {
8642
+ });
8643
+ process.on("SIGHUP", () => {
8644
+ });
8645
+ process.on("SIGTERM", forward);
8646
+ return new Promise((resolve) => {
8647
+ child.once("error", (err) => {
8648
+ console.error(`\u7121\u6CD5\u555F\u52D5\u65B0\u7248 host\uFF1A${err.message}\u3002\u8ACB\u57F7\u884C vibeflow \u91CD\u65B0\u555F\u52D5\u3002`);
8649
+ resolve(1);
8650
+ });
8651
+ child.once("exit", (code2, signal) => resolve(code2 ?? (signal ? 1 : 0)));
8652
+ });
8653
+ }
8521
8654
  function stopOnSignals(host) {
8522
8655
  return new Promise((resolve) => {
8523
8656
  const stop = () => {
@@ -8540,7 +8673,7 @@ async function cmdWeb(values, layout, userDataDir, pathname = "/home/") {
8540
8673
  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
8674
  return 1;
8542
8675
  }
8543
- const started = await startHost(hostOptions(values, layout, userDataDir));
8676
+ const started = await startHost({ ...hostOptions(values, layout, userDataDir), autoRestart: true });
8544
8677
  if (started.kind === "running") return cmdWeb(values, layout, userDataDir, pathname);
8545
8678
  const { host } = started;
8546
8679
  const url = loginUrl(host.lock, pathname);
@@ -8553,8 +8686,11 @@ async function cmdWeb(values, layout, userDataDir, pathname = "/home/") {
8553
8686
  ${formatChecks(checks)}
8554
8687
  `);
8555
8688
  if (!values["no-open"]) await openBrowser(url);
8556
- await stopOnSignals(host);
8557
- return 0;
8689
+ const ended = await Promise.race([
8690
+ stopOnSignals(host).then(() => null),
8691
+ host.restartRequested
8692
+ ]);
8693
+ return ended === null || !layout.cliEntry ? 0 : relaunchHost(host, layout.cliEntry, userDataDir, ended);
8558
8694
  }
8559
8695
  async function cmdTui(values, layout, userDataDir) {
8560
8696
  const { runTui: runTui2 } = await Promise.resolve().then(() => (init_src(), src_exports));
@@ -8679,8 +8815,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
8679
8815
  console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
8680
8816
  return 1;
8681
8817
  }
8682
- const dir = path30.resolve(target);
8683
- if (!fs25.existsSync(dir) || !fs25.statSync(dir).isDirectory()) {
8818
+ const dir = path31.resolve(target);
8819
+ if (!fs26.existsSync(dir) || !fs26.statSync(dir).isDirectory()) {
8684
8820
  console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
8685
8821
  return 1;
8686
8822
  }
@@ -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/3cafb8bddc730b60.css" as="style"/><link rel="stylesheet" href="/_next/static/css/3cafb8bddc730b60.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-83dfa6752c321c56.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/xoex8UtSV4DX9eSoX3lTZ/_buildManifest.js" defer=""></script><script src="/_next/static/xoex8UtSV4DX9eSoX3lTZ/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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":"xoex8UtSV4DX9eSoX3lTZ","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
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/3cafb8bddc730b60.css" as="style"/><link rel="stylesheet" href="/_next/static/css/3cafb8bddc730b60.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/wiyRNf4HeW4AW-LhhYSsf/_buildManifest.js" defer=""></script><script src="/_next/static/wiyRNf4HeW4AW-LhhYSsf/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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":"wiyRNf4HeW4AW-LhhYSsf","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/3cafb8bddc730b60.css" as="style"/><link rel="stylesheet" href="/_next/static/css/3cafb8bddc730b60.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-83dfa6752c321c56.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8f65b0f8749f42d2.js" defer=""></script><script src="/_next/static/xoex8UtSV4DX9eSoX3lTZ/_buildManifest.js" defer=""></script><script src="/_next/static/xoex8UtSV4DX9eSoX3lTZ/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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":"xoex8UtSV4DX9eSoX3lTZ","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
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/3cafb8bddc730b60.css" as="style"/><link rel="stylesheet" href="/_next/static/css/3cafb8bddc730b60.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/wiyRNf4HeW4AW-LhhYSsf/_buildManifest.js" defer=""></script><script src="/_next/static/wiyRNf4HeW4AW-LhhYSsf/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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":"wiyRNf4HeW4AW-LhhYSsf","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>