@modelzen/feishu-codex-bridge 0.6.6 → 0.6.7-test.1

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.
Files changed (2) hide show
  1. package/dist/cli.js +244 -37
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -8509,8 +8509,8 @@ function withCodexHooksFeature(text) {
8509
8509
  }
8510
8510
 
8511
8511
  // src/service/update.ts
8512
- import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
8513
- import { dirname as dirname9, join as join15, resolve as resolve6 } from "path";
8512
+ import { existsSync as existsSync9, readFileSync as readFileSync4 } from "fs";
8513
+ import { dirname as dirname10, join as join15, resolve as resolve6 } from "path";
8514
8514
  import { fileURLToPath as fileURLToPath4 } from "url";
8515
8515
 
8516
8516
  // src/service/launchd.ts
@@ -8523,7 +8523,7 @@ import { fileURLToPath as fileURLToPath3 } from "url";
8523
8523
 
8524
8524
  // src/service/common.ts
8525
8525
  init_paths();
8526
- import { createReadStream, statSync as statSync2 } from "fs";
8526
+ import { appendFileSync, createReadStream, statSync as statSync2 } from "fs";
8527
8527
  import { appendFile, mkdir as mkdir7, readFile as readFile9 } from "fs/promises";
8528
8528
  import { dirname as dirname6, join as join11, resolve as resolve4 } from "path";
8529
8529
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -8538,6 +8538,13 @@ async function ensureLogFiles() {
8538
8538
  await appendFile(serviceStdoutPath(), "");
8539
8539
  await appendFile(serviceStderrPath(), "");
8540
8540
  }
8541
+ function appendServiceErr(tag, line, filePath = serviceStderrPath()) {
8542
+ try {
8543
+ appendFileSync(filePath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [${tag}] ${line}
8544
+ `);
8545
+ } catch {
8546
+ }
8547
+ }
8541
8548
  function resolveCliBinPath() {
8542
8549
  const distDir = dirname6(fileURLToPath2(import.meta.url));
8543
8550
  return resolve4(distDir, "..", "bin", "feishu-codex-bridge.mjs");
@@ -8747,9 +8754,10 @@ function launchctlError(command, result) {
8747
8754
  // src/service/win-startup.ts
8748
8755
  init_paths();
8749
8756
  import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
8750
- import { openSync, readFileSync as readFileSync3, rmSync, writeFileSync } from "fs";
8757
+ import { existsSync as existsSync7, openSync, readFileSync as readFileSync3, renameSync, rmSync, writeFileSync } from "fs";
8751
8758
  import { mkdir as mkdir9, writeFile as writeFile8 } from "fs/promises";
8752
- import { join as join13 } from "path";
8759
+ import { dirname as dirname8, join as join13 } from "path";
8760
+ var RELAUNCH_TASK_NAME = "feishu-codex-bridge-relaunch";
8753
8761
  var RUN_KEY_PATH = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
8754
8762
  var RUN_KEY_NAME = "feishu-codex-bridge";
8755
8763
  var SERVICE_ENV_FLAG = "FEISHU_CODEX_BRIDGE_SERVICE";
@@ -8779,16 +8787,20 @@ function buildLauncherVbs() {
8779
8787
  ""
8780
8788
  ].join("\r\n");
8781
8789
  }
8782
- function startNow() {
8783
- const out = openSync(serviceStdoutPath(), "a");
8784
- const err = openSync(serviceStderrPath(), "a");
8790
+ function startNow(opts = {}) {
8791
+ const dir = opts.appDir ?? paths.appDir;
8792
+ const out = openSync(join13(dir, "service.log"), "a");
8793
+ const err = openSync(join13(dir, "service.err.log"), "a");
8794
+ const overrides = { [SERVICE_ENV_FLAG]: "1" };
8795
+ if (opts.envPath) overrides.PATH = opts.envPath;
8796
+ if (opts.userProfile) overrides.USERPROFILE = opts.userProfile;
8785
8797
  const child = spawn3(process.execPath, [resolveCliBinPath(), "run"], {
8786
8798
  detached: true,
8787
8799
  windowsHide: true,
8788
8800
  stdio: ["ignore", out, err],
8789
- env: mergeProcessEnv(process.env, { [SERVICE_ENV_FLAG]: "1" })
8801
+ env: mergeProcessEnv(process.env, overrides)
8790
8802
  });
8791
- if (child.pid) writeFileSync(servicePidFile(), String(child.pid), "utf8");
8803
+ if (child.pid) writeFileSync(join13(dir, "service.pid"), String(child.pid), "utf8");
8792
8804
  child.unref();
8793
8805
  }
8794
8806
  async function installWinStartup() {
@@ -8817,10 +8829,162 @@ async function restartWinStartup() {
8817
8829
  throw new Error("\u767B\u5F55\u81EA\u542F\u672A\u5B89\u88C5\uFF08\u5148\u8FD0\u884C `feishu-codex-bridge start`\uFF09\u3002");
8818
8830
  }
8819
8831
  await ensureLogFiles();
8820
- killService();
8821
- startNow();
8832
+ const oldPid = readServicePid();
8833
+ if (oldPid === null || !pidAlive(oldPid)) {
8834
+ startNow();
8835
+ return statusWinStartup();
8836
+ }
8837
+ const reqPath = relaunchRequestFile();
8838
+ writeRelaunchRequest({
8839
+ oldPid,
8840
+ envPath: process.env.PATH ?? "",
8841
+ userProfile: process.env.USERPROFILE ?? "",
8842
+ nonce: `${process.pid}-${Date.now()}`
8843
+ });
8844
+ if (!spawnTreeFreeRelauncher(reqPath)) {
8845
+ clearRelaunchRequest();
8846
+ appendServiceErr("relaunch", `no tree-free backend (powershell/schtasks); kept old pid=${oldPid} running, did NOT restart`);
8847
+ throw new Error("\u65E0\u6CD5\u62C9\u8D77\u300C\u6811\u5916\u300D\u91CD\u542F\u8FDB\u7A0B\uFF08powershell \u4E0E schtasks \u5747\u4E0D\u53EF\u7528\uFF09\uFF1B\u5DF2\u4FDD\u7559\u65E7\u670D\u52A1\u7EE7\u7EED\u8FD0\u884C\uFF0C\u672A\u91CD\u542F\u3002");
8848
+ }
8849
+ appendServiceErr("relaunch", `submitted async relaunch for oldPid=${oldPid}`);
8822
8850
  return statusWinStartup();
8823
8851
  }
8852
+ function relaunchRequestFile() {
8853
+ return join13(paths.appDir, "relaunch.json");
8854
+ }
8855
+ function writeRelaunchRequest(req) {
8856
+ writeFileSync(relaunchRequestFile(), JSON.stringify(req), "utf8");
8857
+ }
8858
+ function clearRelaunchRequest() {
8859
+ rmSync(relaunchRequestFile(), { force: true });
8860
+ }
8861
+ function claimRelaunchRequest(reqPath) {
8862
+ const claim = `${reqPath}.claim.${process.pid}`;
8863
+ try {
8864
+ renameSync(reqPath, claim);
8865
+ } catch {
8866
+ return null;
8867
+ }
8868
+ try {
8869
+ const r = JSON.parse(readFileSync3(claim, "utf8"));
8870
+ if (typeof r.oldPid === "number" && Number.isFinite(r.oldPid)) {
8871
+ return {
8872
+ oldPid: r.oldPid,
8873
+ envPath: typeof r.envPath === "string" ? r.envPath : "",
8874
+ userProfile: typeof r.userProfile === "string" ? r.userProfile : "",
8875
+ nonce: String(r.nonce ?? "")
8876
+ };
8877
+ }
8878
+ } catch {
8879
+ }
8880
+ return null;
8881
+ }
8882
+ function clearRelaunchClaim(reqPath) {
8883
+ rmSync(`${reqPath}.claim.${process.pid}`, { force: true });
8884
+ }
8885
+ function buildRelauncherPowershell(reqPath, binPath = resolveCliBinPath(), nodePath = process.execPath) {
8886
+ const inner = `"${nodePath}" "${binPath}" __win-relaunch "${reqPath}"`;
8887
+ const lit = `'${inner.replace(/'/g, "''")}'`;
8888
+ return `$c=${lit}; try { Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine=$c } -ErrorAction Stop | Out-Null } catch { schtasks /create /tn ${RELAUNCH_TASK_NAME} /tr $c /sc ONCE /st 00:00 /f | Out-Null; schtasks /run /tn ${RELAUNCH_TASK_NAME} | Out-Null }`;
8889
+ }
8890
+ function encodePowershellCommand(script) {
8891
+ return Buffer.from(script, "utf16le").toString("base64");
8892
+ }
8893
+ function powershellPath() {
8894
+ const root = process.env.SystemRoot || process.env.windir || "C:\\Windows";
8895
+ const p = join13(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
8896
+ return existsSync7(p) ? p : null;
8897
+ }
8898
+ function spawnTreeFreeRelauncher(reqPath) {
8899
+ const ps = powershellPath();
8900
+ if (ps) {
8901
+ const errFd = openSync(serviceStderrPath(), "a");
8902
+ const child = spawn3(
8903
+ ps,
8904
+ ["-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-EncodedCommand", encodePowershellCommand(buildRelauncherPowershell(reqPath))],
8905
+ { detached: true, windowsHide: true, stdio: ["ignore", errFd, errFd] }
8906
+ );
8907
+ child.on("error", (e) => appendServiceErr("relaunch", `powershell spawn error: ${e.message}`));
8908
+ child.unref();
8909
+ return true;
8910
+ }
8911
+ appendServiceErr("relaunch", "powershell.exe not found; trying schtasks fallback");
8912
+ return spawnSchtasksRelauncher(reqPath);
8913
+ }
8914
+ function spawnSchtasksRelauncher(reqPath) {
8915
+ const tr = `"${process.execPath}" "${resolveCliBinPath()}" __win-relaunch "${reqPath}"`;
8916
+ const create = spawnSync2(
8917
+ "schtasks",
8918
+ ["/create", "/tn", RELAUNCH_TASK_NAME, "/tr", tr, "/sc", "ONCE", "/st", "00:00", "/f"],
8919
+ { encoding: "utf8", windowsHide: true }
8920
+ );
8921
+ if (create.status !== 0) {
8922
+ appendServiceErr("relaunch", `schtasks /create failed exit=${create.status ?? "?"} ${(create.stderr || "").trim()}`);
8923
+ return false;
8924
+ }
8925
+ const run = spawnSync2("schtasks", ["/run", "/tn", RELAUNCH_TASK_NAME], { encoding: "utf8", windowsHide: true });
8926
+ if (run.status !== 0) {
8927
+ appendServiceErr("relaunch", `schtasks /run failed exit=${run.status ?? "?"} ${(run.stderr || "").trim()}`);
8928
+ return false;
8929
+ }
8930
+ return true;
8931
+ }
8932
+ function defaultTaskkill(pid) {
8933
+ const r = spawnSync2("taskkill", ["/pid", String(pid), "/T", "/F"], { encoding: "utf8", windowsHide: true });
8934
+ return { status: r.status, stderr: (r.stderr || "").toString() };
8935
+ }
8936
+ async function runWinRelaunch(deps = {}) {
8937
+ const reqPath = deps.requestPath ?? relaunchRequestFile();
8938
+ const appDir2 = dirname8(reqPath);
8939
+ const claim = deps.claimRequest ?? claimRelaunchRequest;
8940
+ const clearClaim = deps.clearRequest ?? clearRelaunchClaim;
8941
+ const alive = deps.pidAlive ?? pidAlive;
8942
+ const kill = deps.taskkill ?? defaultTaskkill;
8943
+ const start = deps.start ?? startNow;
8944
+ const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
8945
+ const graceMs = deps.graceMs ?? 15e3;
8946
+ const pollMs = deps.pollMs ?? 200;
8947
+ const now = deps.now ?? (() => Date.now());
8948
+ const ensureLogs = deps.ensureLogs ?? ensureLogFiles;
8949
+ const logLine = deps.logLine ?? ((line) => appendServiceErr("relaunch", line, join13(appDir2, "service.err.log")));
8950
+ try {
8951
+ await ensureLogs();
8952
+ } catch {
8953
+ }
8954
+ const req = claim(reqPath);
8955
+ if (!req) {
8956
+ logLine("no relaunch request to claim (already handled by another relauncher, or none)");
8957
+ return;
8958
+ }
8959
+ try {
8960
+ logLine(`start oldPid=${req.oldPid} self=${process.pid}`);
8961
+ if (alive(req.oldPid)) {
8962
+ const r = kill(req.oldPid);
8963
+ logLine(`taskkill oldPid=${req.oldPid} exit=${r.status ?? "?"} ${r.stderr.trim()}`.trim());
8964
+ }
8965
+ const deadline = now() + graceMs;
8966
+ while (now() < deadline && alive(req.oldPid)) {
8967
+ await sleep(pollMs);
8968
+ }
8969
+ if (alive(req.oldPid)) {
8970
+ logLine(
8971
+ `ABORT: oldPid=${req.oldPid} still alive after ${graceMs}ms (elevated / access denied?); not starting new to avoid double instance`
8972
+ );
8973
+ return;
8974
+ }
8975
+ await sleep(300);
8976
+ start({ appDir: appDir2, envPath: req.envPath, userProfile: req.userProfile });
8977
+ logLine(`relaunched; new daemon started (appDir=${appDir2})`);
8978
+ } finally {
8979
+ clearClaim(reqPath);
8980
+ if (process.platform === "win32") {
8981
+ try {
8982
+ spawnSync2("schtasks", ["/delete", "/tn", RELAUNCH_TASK_NAME, "/f"], { stdio: "ignore", windowsHide: true });
8983
+ } catch {
8984
+ }
8985
+ }
8986
+ }
8987
+ }
8824
8988
  function statusWinStartup() {
8825
8989
  const installed = isInstalled();
8826
8990
  const pid = readServicePid();
@@ -8880,15 +9044,18 @@ function pidAlive(pid) {
8880
9044
  function killService() {
8881
9045
  const pid = readServicePid();
8882
9046
  if (pid === null || !pidAlive(pid)) return;
8883
- spawnSync2("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore" });
9047
+ const r = spawnSync2("taskkill", ["/pid", String(pid), "/T", "/F"], { encoding: "utf8", windowsHide: true });
9048
+ if (r.status !== 0) {
9049
+ appendServiceErr("kill", `taskkill pid=${pid} exit=${r.status ?? "?"} ${(r.stderr || "").toString().trim()}`);
9050
+ }
8884
9051
  }
8885
9052
 
8886
9053
  // src/service/systemd.ts
8887
9054
  import { spawnSync as spawnSync3 } from "child_process";
8888
- import { existsSync as existsSync7 } from "fs";
9055
+ import { existsSync as existsSync8 } from "fs";
8889
9056
  import { mkdir as mkdir10, rm as rm4, writeFile as writeFile9 } from "fs/promises";
8890
9057
  import { homedir as homedir5 } from "os";
8891
- import { dirname as dirname8, join as join14 } from "path";
9058
+ import { dirname as dirname9, join as join14 } from "path";
8892
9059
  var SYSTEMD_UNIT_NAME = "feishu-codex-bridge.service";
8893
9060
  function systemdUnitPath() {
8894
9061
  const base = process.env.XDG_CONFIG_HOME ?? join14(homedir5(), ".config");
@@ -8946,7 +9113,7 @@ function ensureSystemdOrThrow() {
8946
9113
  async function installSystemd() {
8947
9114
  ensureSystemdOrThrow();
8948
9115
  const unitPath = systemdUnitPath();
8949
- await mkdir10(dirname8(unitPath), { recursive: true });
9116
+ await mkdir10(dirname9(unitPath), { recursive: true });
8950
9117
  await ensureLogFiles();
8951
9118
  await writeFile9(unitPath, buildUnit(), "utf8");
8952
9119
  const reload = runSystemctl(["daemon-reload"]);
@@ -8989,7 +9156,7 @@ function statusSystemd() {
8989
9156
  };
8990
9157
  }
8991
9158
  function unitExists() {
8992
- return existsSync7(systemdUnitPath());
9159
+ return existsSync8(systemdUnitPath());
8993
9160
  }
8994
9161
  function systemdActive() {
8995
9162
  const r = spawnSync3("systemctl", ["--user", "is-active", SYSTEMD_UNIT_NAME], {
@@ -9048,7 +9215,7 @@ function isServiceRunning() {
9048
9215
  // src/service/update.ts
9049
9216
  var NPM2 = "npm";
9050
9217
  function pkgRoot() {
9051
- return resolve6(dirname9(fileURLToPath4(import.meta.url)), "..");
9218
+ return resolve6(dirname10(fileURLToPath4(import.meta.url)), "..");
9052
9219
  }
9053
9220
  function pkgJson() {
9054
9221
  try {
@@ -9064,7 +9231,7 @@ function packageName() {
9064
9231
  return pkgJson().name ?? "@modelzen/feishu-codex-bridge";
9065
9232
  }
9066
9233
  function isDevSource() {
9067
- return existsSync8(join15(pkgRoot(), ".git"));
9234
+ return existsSync9(join15(pkgRoot(), ".git"));
9068
9235
  }
9069
9236
  function isNewer(a, b) {
9070
9237
  const pa = a.split(".").map((n) => Number.parseInt(n, 10) || 0);
@@ -9710,9 +9877,9 @@ function buildUsageShareCard(data, opts = {}) {
9710
9877
  init_paths();
9711
9878
  import { randomUUID as randomUUID4 } from "crypto";
9712
9879
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
9713
- import { dirname as dirname10 } from "path";
9880
+ import { dirname as dirname11 } from "path";
9714
9881
  function publishWebConsole(rec, file = paths.webConsoleFile) {
9715
- mkdirSync2(dirname10(file), { recursive: true });
9882
+ mkdirSync2(dirname11(file), { recursive: true });
9716
9883
  try {
9717
9884
  unlinkSync(file);
9718
9885
  } catch {
@@ -9752,7 +9919,7 @@ function stableWebConsoleToken(file = paths.webTokenFile) {
9752
9919
  }
9753
9920
  const token = randomUUID4();
9754
9921
  try {
9755
- mkdirSync2(dirname10(file), { recursive: true });
9922
+ mkdirSync2(dirname11(file), { recursive: true });
9756
9923
  try {
9757
9924
  unlinkSync(file);
9758
9925
  } catch {
@@ -9779,7 +9946,7 @@ init_paths();
9779
9946
  init_paths();
9780
9947
  init_logger();
9781
9948
  import { mkdir as mkdir11 } from "fs/promises";
9782
- import { existsSync as existsSync9 } from "fs";
9949
+ import { existsSync as existsSync10 } from "fs";
9783
9950
  import { isAbsolute as isAbsolute2, join as join17, resolve as resolve7 } from "path";
9784
9951
 
9785
9952
  // src/project/announcement.ts
@@ -9972,7 +10139,7 @@ function assertBackendUsable(backend, mode) {
9972
10139
  async function resolveCwd(name, existingPath) {
9973
10140
  if (existingPath) {
9974
10141
  const cwd2 = isAbsolute2(existingPath) ? existingPath : resolve7(existingPath);
9975
- if (!existsSync9(cwd2)) throw new Error(`\u6587\u4EF6\u5939\u4E0D\u5B58\u5728\uFF1A${cwd2}`);
10142
+ if (!existsSync10(cwd2)) throw new Error(`\u6587\u4EF6\u5939\u4E0D\u5B58\u5728\uFF1A${cwd2}`);
9976
10143
  return { cwd: cwd2, blank: false };
9977
10144
  }
9978
10145
  const cwd = join17(paths.projectsRootDir, name);
@@ -10063,7 +10230,7 @@ async function leaveChat(channel, chatId) {
10063
10230
  init_paths();
10064
10231
  import { mkdir as mkdir12, readFile as readFile11, rename as rename5, writeFile as writeFile10 } from "fs/promises";
10065
10232
  import { randomUUID as randomUUID5 } from "crypto";
10066
- import { dirname as dirname11 } from "path";
10233
+ import { dirname as dirname12 } from "path";
10067
10234
  var FILE_VERSION3 = 2;
10068
10235
  var LEGACY_V1_SESSION_FIELD = "codexThreadId";
10069
10236
  function migrate(raw) {
@@ -10099,7 +10266,7 @@ function withLock2(fn) {
10099
10266
  return run;
10100
10267
  }
10101
10268
  async function write2(sessions) {
10102
- await mkdir12(dirname11(paths.sessionsFile), { recursive: true });
10269
+ await mkdir12(dirname12(paths.sessionsFile), { recursive: true });
10103
10270
  const tmp = `${paths.sessionsFile}.tmp-${process.pid}-${randomUUID5()}`;
10104
10271
  const body = { version: FILE_VERSION3, sessions };
10105
10272
  await writeFile10(tmp, `${JSON.stringify(body, null, 2)}
@@ -10715,7 +10882,7 @@ function weaveSender(text, sender) {
10715
10882
  // src/bot/comments.ts
10716
10883
  init_logger();
10717
10884
  import { mkdir as mkdir14, readdir as readdir4, readFile as readFile12, writeFile as writeFile11 } from "fs/promises";
10718
- import { dirname as dirname12, join as join19 } from "path";
10885
+ import { dirname as dirname13, join as join19 } from "path";
10719
10886
  var SUPPORTED_FILE_TYPES = /* @__PURE__ */ new Set(["doc", "docx", "sheet", "bitable"]);
10720
10887
  var REPLY_MAX_CHARS = 2e3;
10721
10888
  function apiErrCode(err) {
@@ -10847,7 +11014,7 @@ async function loadCommentInstructions(masterFile) {
10847
11014
  return existing.trim() ? existing : DEFAULT_COMMENT_INSTRUCTIONS;
10848
11015
  } catch {
10849
11016
  }
10850
- await mkdir14(dirname12(masterFile), { recursive: true }).catch(() => void 0);
11017
+ await mkdir14(dirname13(masterFile), { recursive: true }).catch(() => void 0);
10851
11018
  await writeFile11(masterFile, DEFAULT_COMMENT_INSTRUCTIONS, "utf8").catch(() => void 0);
10852
11019
  return DEFAULT_COMMENT_INSTRUCTIONS;
10853
11020
  }
@@ -10859,7 +11026,7 @@ async function syncCommentInstructions(cwd, instructions) {
10859
11026
  ]);
10860
11027
  }
10861
11028
  async function saveCommentInstructions(masterFile, content) {
10862
- await mkdir14(dirname12(masterFile), { recursive: true });
11029
+ await mkdir14(dirname13(masterFile), { recursive: true });
10863
11030
  await writeFile11(masterFile, content, "utf8");
10864
11031
  }
10865
11032
  function renderCommentInstructions(template, tenant, fileType, fileToken) {
@@ -14130,7 +14297,7 @@ init_logger();
14130
14297
  // src/admin/host.ts
14131
14298
  init_paths();
14132
14299
  import { readdir as readdir5, stat as stat4 } from "fs/promises";
14133
- import { dirname as dirname13, join as join21, resolve as resolve8 } from "path";
14300
+ import { dirname as dirname14, join as join21, resolve as resolve8 } from "path";
14134
14301
  import { fileURLToPath as fileURLToPath5 } from "url";
14135
14302
  import { arch, platform as platform2, release } from "os";
14136
14303
  function toDaemonStatus(opts) {
@@ -14182,7 +14349,7 @@ async function collectHostDoctor(logsDir2 = join21(paths.appDir, "logs")) {
14182
14349
  };
14183
14350
  }
14184
14351
  function resolveCliBinPath3() {
14185
- const distDir = dirname13(fileURLToPath5(import.meta.url));
14352
+ const distDir = dirname14(fileURLToPath5(import.meta.url));
14186
14353
  return resolve8(distDir, "..", "bin", "feishu-codex-bridge.mjs");
14187
14354
  }
14188
14355
  function buildDaemonControlCommand(action, binPath = resolveCliBinPath3(), nodePath = process.execPath) {
@@ -14737,16 +14904,22 @@ async function doUpdate() {
14737
14904
  }
14738
14905
  const res = await installLatest();
14739
14906
  log.info("daemon-control", "update-installed", { ok: res.ok });
14740
- if (!res.ok) return;
14907
+ appendServiceErr("daemon-control", `update-installed ok=${res.ok}`);
14908
+ if (!res.ok) {
14909
+ appendServiceErr("daemon-control", "install failed \u2014 keeping old version, not restarting");
14910
+ return;
14911
+ }
14741
14912
  await doRestart("update");
14742
14913
  }
14743
14914
  async function doRestart(phase) {
14744
14915
  if (!isServiceRunning()) {
14745
14916
  log.info("daemon-control", "restart-skipped-no-service", { phase });
14917
+ appendServiceErr("daemon-control", `restart-skipped-no-service (phase=${phase}); service.pid \u672A\u6307\u5411\u5728\u8DD1\u7684 daemon`);
14746
14918
  return;
14747
14919
  }
14748
14920
  await getServiceAdapter().restart();
14749
14921
  log.info("daemon-control", "restart-issued", { phase });
14922
+ appendServiceErr("daemon-control", `restart-issued (phase=${phase})`);
14750
14923
  }
14751
14924
 
14752
14925
  // src/web/mount.ts
@@ -15495,6 +15668,9 @@ ${UI_PURE_JS}
15495
15668
  var daemon = null; // /api/daemon
15496
15669
  var diag = null; // /api/diagnosis \u7ED3\u679C\uFF08\u5F53\u524D bot \u7684\u63A2\u6D4B\uFF1B\u5207 Tab \u5FC5\u6E05\uFF09
15497
15670
  var catalog = null; // /api/backends \u7F13\u5B58\uFF08\u540E\u7AEF\u7BA1\u7406\u5361 + \u9879\u76EE picker \u590D\u7528\uFF09
15671
+ var lastUpdate = null; // /api/update/check \u7ED3\u679C\u7F13\u5B58\uFF1A\u603B\u89C8 5s \u91CD\u6E32\u4E0D\u91CD\u67E5\uFF08\u6BCF\u6B21\u67E5\u90FD spawn npm view\uFF09
15672
+ var updateCheckedAt = 0; // \u4E0A\u6B21\u68C0\u67E5\u65F6\u95F4\u6233\uFF1B\u62FF\u5230\u7ED3\u679C\u8D85 30min\u3001\u8F6F\u5931\u8D25\u8D85 ~2min \u624D\u968F\u91CD\u6E32\u81EA\u52A8\u590D\u67E5
15673
+ var updateChecking = false;// in-flight \u95F8\uFF1Anpm view \u53EF\u80FD\u6570\u79D2~20s\uFF0C\u671F\u95F4 5s \u91CD\u6E32\u4E0D\u53E0\u53D1\u7B2C\u4E8C\u4E2A\u8BF7\u6C42
15498
15674
  var drawerProject = null; // \u62BD\u5C49\u91CC\u6253\u5F00\u7684\u9879\u76EE\u540D
15499
15675
  var diagBotId = null; // diag \u5C5E\u4E8E\u54EA\u4E2A bot\uFF08\u9632\u4E32\u53F0\uFF09
15500
15676
  var bkDetailId = null; // \u540E\u7AEF Agent \u8BE6\u60C5\u89C6\u56FE\u5F53\u524D\u5C55\u5F00\u7684\u540E\u7AEF id\uFF08null=\u5217\u8868\uFF09
@@ -15693,6 +15869,14 @@ ${UI_PURE_JS}
15693
15869
  fetch('/api/daemon').then(function (r) { return r.json(); })
15694
15870
  .then(function (d) {
15695
15871
  daemon = d;
15872
+ // \u8FD0\u884C\u4E2D\u7684\u5B9E\u9645\u7248\u672C\u4E00\u65E6\u8DDF\u7248\u672C\u68C0\u67E5\u7F13\u5B58\u91CC\u7684 current \u5BF9\u4E0D\u4E0A\uFF08\u591A\u534A\u662F\u521A\u66F4\u65B0+\u91CD\u542F\u5B8C\u6210\uFF09\uFF0C
15873
+ // \u5C31\u4F5C\u5E9F\u7F13\u5B58\u5E76\u5F53\u573A\u8865\u67E5\u4E00\u6B21\uFF08\u82E5\u5728\u603B\u89C8\u9875\uFF09\uFF0C\u628A\u300C\u6709\u65B0\u7248\u300D\u7ACB\u523B\u7FFB\u6210\u300C\u5DF2\u662F\u6700\u65B0\u7248\u300D\u2014\u2014\u4E0D\u7B49
15874
+ // \u4E0B\u4E00\u8F6E renderRoute\uFF0C\u6D88\u9664 loadState/loadDaemon \u540C\u5468\u671F\u7ADE\u6001\u5BFC\u81F4\u7684 ~10s \u5EF6\u8FDF\u4E0E\u6A2A\u5E45\u6B8B\u7559\u3002
15875
+ // \u53EA\u5728\u7248\u672C\u771F\u53D8\u65F6\u624D\u67E5\uFF08\u7A33\u6001 d.version===current\uFF09\uFF0C\u65E2\u7559\u4F4F\u66F4\u65B0\u540E\u7684\u5373\u65F6\u53CD\u9988\uFF0C\u53C8\u4E0D\u56DE\u5230 5s \u7A7A\u8F6C\u3002
15876
+ if (lastUpdate && d.version && d.version !== lastUpdate.current) {
15877
+ lastUpdate = null; updateCheckedAt = 0;
15878
+ if (parseRoute(location.hash).tab === 'overview') { var ub = $('updateBody'); if (ub) loadUpdate(ub); }
15879
+ }
15696
15880
  if (parseRoute(location.hash).tab === 'overview') { var b = $('daemonBody'); if (b) renderDaemon(b); }
15697
15881
  renderSidebarFoot();
15698
15882
  renderGlobalSummary();
@@ -16006,7 +16190,14 @@ ${UI_PURE_JS}
16006
16190
  daemonCard.appendChild(updateBody);
16007
16191
  root.appendChild(daemonCard);
16008
16192
  renderDaemon(daemonBody);
16009
- loadUpdate(updateBody);
16193
+ // \u7248\u672C\u68C0\u67E5\u4F1A\u5728\u670D\u52A1\u7AEF spawn\u300Cnpm view\u300D\uFF0C\u4E0D\u80FD\u8DDF\u7740 5s \u91CD\u6E32\u6BCF\u6B21\u90FD\u8DD1\uFF1A\u6709\u7F13\u5B58\u5C31\u76F4\u63A5\u6E32\uFF0C
16194
+ // \u53EA\u5728\u9996\u6B21\u6216\u7F13\u5B58\u8FC7\u671F\uFF0830min\uFF09\u65F6\u624D\u771F\u67E5\u4E00\u6B21\u3002\u624B\u52A8\u300C\u68C0\u67E5\u66F4\u65B0\u300D\u6309\u94AE\u4ECD\u5F3A\u5236\u5237\u65B0\u3002
16195
+ if (lastUpdate) {
16196
+ renderUpdate(updateBody, lastUpdate);
16197
+ if (Date.now() - updateCheckedAt > 30 * 60 * 1000) loadUpdate(updateBody);
16198
+ } else {
16199
+ loadUpdate(updateBody);
16200
+ }
16010
16201
 
16011
16202
  // \u{1F916} \u673A\u5668\u4EBA\uFF08\u5728 Feishu Bridge \u5361\u4E0B\u9762\uFF0C\u5404\u81EA\u5360\u6EE1\u5BBD\u5EA6\uFF09
16012
16203
  var botsCard = el('div', 'card');
@@ -16365,9 +16556,19 @@ ${UI_PURE_JS}
16365
16556
  }
16366
16557
 
16367
16558
  function loadUpdate(box) {
16559
+ if (updateChecking) return; // \u5728\u9014\u5C31\u4E0D\u53E0\u53D1\uFF1A\u6162\u68C0\u67E5\uFF08npm view \u6700\u957F 20s\uFF09\u671F\u95F4 5s \u91CD\u6E32\u4E0D\u91CD\u590D spawn
16560
+ updateChecking = true;
16561
+ updateCheckedAt = Date.now(); // \u4E50\u89C2\u5360\u4F4D\uFF1Ain-flight \u671F\u95F4 30min \u8FB9\u754C\u4E0D\u518D\u88AB\u53CD\u590D\u5224\u771F
16368
16562
  fetch('/api/update/check').then(function (r) { return r.json(); })
16369
- .then(function (u) { renderUpdate(box, u); })
16370
- .catch(function () { box.textContent = '\u26A0\uFE0F \u7248\u672C\u68C0\u67E5\u5931\u8D25\uFF08\u7F51\u7EDC\u6216 npm registry\uFF09'; });
16563
+ .then(function (u) {
16564
+ lastUpdate = u;
16565
+ // \u8F6F\u5931\u8D25\uFF1A\u975E dev \u5374\u6CA1\u62FF\u5230 latest\uFF08\u591A\u534A registry \u4E0D\u53EF\u8FBE/\u8D85\u65F6\uFF0C\u670D\u52A1\u7AEF\u4ECD\u56DE 200\uFF09\uFF0C\u53EA\u7F13\u5B58 ~2min
16566
+ // \u4FBF\u81EA\u52A8\u590D\u67E5\uFF0C\u522B\u628A\u300C\u67E5\u8BE2\u5931\u8D25\u300D\u9489\u4F4F 30min\uFF1B\u62FF\u5230 latest \u6216 dev \u6A21\u5F0F\u624D\u6309\u5B8C\u6574 30min TTL\u3002
16567
+ updateCheckedAt = (u && (u.dev || u.latest)) ? Date.now() : Date.now() - 28 * 60 * 1000;
16568
+ renderUpdate(box, u);
16569
+ })
16570
+ .catch(function () { box.textContent = '\u26A0\uFE0F \u7248\u672C\u68C0\u67E5\u5931\u8D25\uFF08\u7F51\u7EDC\u6216 npm registry\uFF09'; })
16571
+ .then(function () { updateChecking = false; }); // finally\uFF1A\u65E0\u8BBA\u6210\u8D25\u90FD\u677E\u95F8
16371
16572
  }
16372
16573
  function renderUpdate(box, u) {
16373
16574
  box.textContent = '';
@@ -18141,6 +18342,7 @@ async function runSupervisor(bots) {
18141
18342
  const supervisorStartedAt = Date.now();
18142
18343
  const cliEntry = process.argv[1];
18143
18344
  if (!cliEntry) throw new Error("supervisor: \u65E0\u6CD5\u89E3\u6790 CLI \u5165\u53E3\uFF08process.argv[1] \u4E3A\u7A7A\uFF09");
18345
+ recordServicePid();
18144
18346
  const childEnv = { ...process.env };
18145
18347
  delete childEnv[SERVICE_ENV_FLAG];
18146
18348
  let shuttingDown = false;
@@ -18274,7 +18476,7 @@ async function runSupervisor(bots) {
18274
18476
  // src/core/single-instance.ts
18275
18477
  init_paths();
18276
18478
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
18277
- import { dirname as dirname14 } from "path";
18479
+ import { dirname as dirname15 } from "path";
18278
18480
  var CLAIM_ATTEMPTS = 5;
18279
18481
  var PID_REUSE_SLACK_MS = 15e3;
18280
18482
  var EMPTY_LOCK_STALE_MS = 2e3;
@@ -18320,7 +18522,7 @@ function isLiveHolder(rec, appId) {
18320
18522
  return true;
18321
18523
  }
18322
18524
  function acquireSingleInstanceLock(appId, file = paths.processesFile) {
18323
- mkdirSync4(dirname14(file), { recursive: true });
18525
+ mkdirSync4(dirname15(file), { recursive: true });
18324
18526
  const record = { pid: process.pid, appId, startedAt: Date.now() };
18325
18527
  for (let attempt = 0; attempt < CLAIM_ATTEMPTS; attempt++) {
18326
18528
  try {
@@ -18407,6 +18609,7 @@ async function runOnboardingConsole() {
18407
18609
  releaseLock = acquireSingleInstanceLock("__onboarding__");
18408
18610
  } catch (err) {
18409
18611
  if (err instanceof BridgeAlreadyRunningError) {
18612
+ clearServicePid();
18410
18613
  console.error(`\u2717 ${err.message}`);
18411
18614
  process.exitCode = 1;
18412
18615
  return;
@@ -18475,6 +18678,7 @@ async function runSingle(botName) {
18475
18678
  releaseLock = acquireSingleInstanceLock(cfg.accounts.app.id);
18476
18679
  } catch (err) {
18477
18680
  if (err instanceof BridgeAlreadyRunningError) {
18681
+ clearServicePid();
18478
18682
  console.error(`\u2717 ${err.message}`);
18479
18683
  log.info("run", "already-running", { pid: err.pid });
18480
18684
  process.exitCode = 1;
@@ -19030,6 +19234,9 @@ program.command("web").description("\u672C\u673A Web \u63A7\u5236\u53F0\uFF08\u5
19030
19234
  program.command("__daemon-control <action>", { hidden: true }).action(async (action) => {
19031
19235
  await runDaemonControl(action);
19032
19236
  });
19237
+ program.command("__win-relaunch [requestPath]", { hidden: true }).action(async (requestPath) => {
19238
+ await runWinRelaunch({ requestPath });
19239
+ });
19033
19240
  var bot = program.command("bot").description("\u98DE\u4E66\u673A\u5668\u4EBA\u7BA1\u7406\uFF08\u591A\u673A\u5668\u4EBA\uFF09");
19034
19241
  bot.command("init [name]").description("\u6CE8\u518C\u4E00\u4E2A\u98DE\u4E66\u673A\u5668\u4EBA\u5E76\u6388\u6743\uFF08\u53EF\u9009\u77ED\u540D\uFF09").action(async (name) => {
19035
19242
  await runBotInit(name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelzen/feishu-codex-bridge",
3
- "version": "0.6.6",
3
+ "version": "0.6.7-test.1",
4
4
  "description": "Bridge Feishu/Lark messenger with local Codex via app-server (project=group, thread=session)",
5
5
  "type": "module",
6
6
  "bin": {