@modelzen/feishu-codex-bridge 0.2.2-win → 0.3.0-win.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 +183 -143
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -274,10 +274,10 @@ import { extname, join as join3 } from "path";
274
274
  // src/platform/spawn.ts
275
275
  import crossSpawn from "cross-spawn";
276
276
  function spawnProcess(command, args = [], options = {}) {
277
- return crossSpawn(command, [...args], options);
277
+ return crossSpawn(command, [...args], { windowsHide: true, ...options });
278
278
  }
279
279
  function spawnProcessSync(command, args = [], options = {}) {
280
- return crossSpawn.sync(command, [...args], options);
280
+ return crossSpawn.sync(command, [...args], { windowsHide: true, ...options });
281
281
  }
282
282
  function mergeProcessEnv(base = process.env, overrides = {}) {
283
283
  const out = { ...base };
@@ -491,7 +491,6 @@ async function listSecretIds() {
491
491
  }
492
492
 
493
493
  // src/config/secret-resolver.ts
494
- import { spawn } from "child_process";
495
494
  import { readFile as readFile4 } from "fs/promises";
496
495
  import { join as join5 } from "path";
497
496
  var ENV_TEMPLATE_RE = /^\$\{([A-Z][A-Z0-9_]{0,127})\}$/;
@@ -577,7 +576,10 @@ async function spawnExecProvider(pc, ref) {
577
576
  if (v) env[k] = v;
578
577
  }
579
578
  if (pc.env) Object.assign(env, pc.env);
580
- const child = spawn(pc.command, pc.args ?? [], { env, stdio: ["pipe", "pipe", "pipe"] });
579
+ const child = spawnProcess(pc.command, pc.args ?? [], {
580
+ env,
581
+ stdio: ["pipe", "pipe", "pipe"]
582
+ });
581
583
  let stdout = "", stderr = "", truncated = false, settled = false;
582
584
  const timer = setTimeout(() => {
583
585
  if (settled) return;
@@ -812,7 +814,7 @@ async function fetchGrantedScopes(base, token) {
812
814
  }
813
815
 
814
816
  // src/utils/open-url.ts
815
- import { spawn as spawn2 } from "child_process";
817
+ import { spawn } from "child_process";
816
818
  import { platform } from "os";
817
819
  function openUrl(url) {
818
820
  if (!process.stdout.isTTY) return false;
@@ -832,7 +834,7 @@ function openUrl(url) {
832
834
  args = [url];
833
835
  }
834
836
  try {
835
- const child = spawn2(cmd, args, { stdio: "ignore", detached: true });
837
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true });
836
838
  child.on("error", () => {
837
839
  });
838
840
  child.unref();
@@ -3556,14 +3558,12 @@ function buildGroupSettingsCard(project) {
3556
3558
  }
3557
3559
 
3558
3560
  // src/service/update.ts
3559
- import { execFile, spawn as spawn4 } from "child_process";
3560
- import { existsSync as existsSync7, readFileSync as readFileSync2 } from "fs";
3561
- import { dirname as dirname10, join as join11, resolve as resolve5 } from "path";
3561
+ import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
3562
+ import { dirname as dirname9, join as join11, resolve as resolve5 } from "path";
3562
3563
  import { fileURLToPath as fileURLToPath4 } from "url";
3563
- import { promisify } from "util";
3564
3564
 
3565
3565
  // src/service/launchd.ts
3566
- import { spawn as spawn3, spawnSync } from "child_process";
3566
+ import { spawn as spawn2, spawnSync } from "child_process";
3567
3567
  import { existsSync as existsSync4 } from "fs";
3568
3568
  import { mkdir as mkdir6, rm as rm2, writeFile as writeFile5 } from "fs/promises";
3569
3569
  import { homedir as homedir3, userInfo as userInfo2 } from "os";
@@ -3741,7 +3741,7 @@ async function tailLaunchdLogs(follow) {
3741
3741
  await ensureLogFiles();
3742
3742
  const args = follow ? ["-f", serviceStdoutPath(), serviceStderrPath()] : ["-n", "100", serviceStdoutPath(), serviceStderrPath()];
3743
3743
  await new Promise((resolvePromise, reject) => {
3744
- const child = spawn3("tail", args, { stdio: "inherit" });
3744
+ const child = spawn2("tail", args, { stdio: "inherit" });
3745
3745
  child.on("error", reject);
3746
3746
  child.on("close", (code) => {
3747
3747
  if (code === 0 || follow && code === null) {
@@ -3792,126 +3792,150 @@ function launchctlError(command, result) {
3792
3792
  return new Error(`${command} \u5931\u8D25\uFF08exit ${result.status ?? "unknown"}\uFF09${output ? `\uFF1A${output}` : ""}`);
3793
3793
  }
3794
3794
 
3795
- // src/service/schtasks.ts
3796
- import { spawnSync as spawnSync2 } from "child_process";
3797
- import { existsSync as existsSync5 } from "fs";
3798
- import { mkdir as mkdir7, rm as rm3, writeFile as writeFile6 } from "fs/promises";
3799
- import { dirname as dirname8, join as join9 } from "path";
3800
- var WINDOWS_TASK_NAME = "feishu-codex-bridge";
3795
+ // src/service/win-startup.ts
3796
+ import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
3797
+ import { openSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
3798
+ import { mkdir as mkdir7, writeFile as writeFile6 } from "fs/promises";
3799
+ import { join as join9 } from "path";
3800
+ var RUN_KEY_PATH = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
3801
+ var RUN_KEY_NAME = "feishu-codex-bridge";
3802
+ var SERVICE_ENV_FLAG = "FEISHU_CODEX_BRIDGE_SERVICE";
3801
3803
  function launcherCmdPath() {
3802
3804
  return join9(paths.appDir, "service-launcher.cmd");
3803
3805
  }
3806
+ function launcherVbsPath() {
3807
+ return join9(paths.appDir, "service-launcher.vbs");
3808
+ }
3809
+ function servicePidFile() {
3810
+ return join9(paths.appDir, "service.pid");
3811
+ }
3804
3812
  function buildLauncherCmd() {
3805
- const nodePath = process.execPath;
3806
- const cliBinPath = resolveCliBinPath();
3807
- const pathEnv = process.env.PATH ?? "";
3808
3813
  return [
3809
3814
  "@echo off",
3810
- `set "PATH=${pathEnv}"`,
3811
- `"${nodePath}" "${cliBinPath}" run >> "${serviceStdoutPath()}" 2>> "${serviceStderrPath()}"`,
3815
+ `set "PATH=${process.env.PATH ?? ""}"`,
3816
+ `set "${SERVICE_ENV_FLAG}=1"`,
3817
+ `"${process.execPath}" "${resolveCliBinPath()}" run >> "${serviceStdoutPath()}" 2>> "${serviceStderrPath()}"`,
3812
3818
  ""
3813
3819
  ].join("\r\n");
3814
3820
  }
3815
- function runSchtasks(args) {
3816
- const r = spawnSync2("schtasks", args, { encoding: "utf8" });
3817
- return {
3818
- ok: r.status === 0,
3819
- status: r.status,
3820
- stdout: r.stdout ?? "",
3821
- stderr: r.stderr ?? ""
3822
- };
3821
+ function buildLauncherVbs() {
3822
+ return [
3823
+ "' Auto-generated by feishu-codex-bridge. Do not edit.",
3824
+ 'Set sh = CreateObject("WScript.Shell")',
3825
+ `sh.Run "cmd /c ""${launcherCmdPath()}""", 0, False`,
3826
+ ""
3827
+ ].join("\r\n");
3823
3828
  }
3824
- function schtasksError(command, r) {
3825
- const out = [r.stderr.trim(), r.stdout.trim()].filter(Boolean).join("\n");
3826
- return new Error(`${command} \u5931\u8D25\uFF08exit ${r.status ?? "unknown"}\uFF09${out ? `\uFF1A${out}` : ""}`);
3829
+ function startNow() {
3830
+ const out = openSync(serviceStdoutPath(), "a");
3831
+ const err = openSync(serviceStderrPath(), "a");
3832
+ const child = spawn3(process.execPath, [resolveCliBinPath(), "run"], {
3833
+ detached: true,
3834
+ windowsHide: true,
3835
+ stdio: ["ignore", out, err],
3836
+ env: mergeProcessEnv(process.env, { [SERVICE_ENV_FLAG]: "1" })
3837
+ });
3838
+ if (child.pid) writeFileSync(servicePidFile(), String(child.pid), "utf8");
3839
+ child.unref();
3840
+ }
3841
+ async function installWinStartup() {
3842
+ await mkdir7(paths.appDir, { recursive: true });
3843
+ await ensureLogFiles();
3844
+ await writeFile6(launcherCmdPath(), buildLauncherCmd(), "utf8");
3845
+ await writeFile6(launcherVbsPath(), buildLauncherVbs(), "utf8");
3846
+ const reg = spawnSync2(
3847
+ "reg",
3848
+ ["add", RUN_KEY_PATH, "/v", RUN_KEY_NAME, "/t", "REG_SZ", "/d", `wscript.exe "${launcherVbsPath()}"`, "/f"],
3849
+ { encoding: "utf8" }
3850
+ );
3851
+ if (reg.status !== 0) {
3852
+ throw new Error(`\u5199\u5165\u767B\u5F55\u81EA\u542F\u6CE8\u518C\u8868\u9879\u5931\u8D25\uFF08exit ${reg.status ?? "unknown"}\uFF09\uFF1A${(reg.stderr || reg.stdout || "").trim()}`);
3853
+ }
3854
+ startNow();
3855
+ return statusWinStartup();
3856
+ }
3857
+ async function uninstallWinStartup() {
3858
+ spawnSync2("reg", ["delete", RUN_KEY_PATH, "/v", RUN_KEY_NAME, "/f"], { stdio: "ignore" });
3859
+ killService();
3860
+ rmSync(servicePidFile(), { force: true });
3827
3861
  }
3828
- async function writeLauncherCmd() {
3829
- const cmdPath = launcherCmdPath();
3830
- await mkdir7(dirname8(cmdPath), { recursive: true });
3862
+ async function restartWinStartup() {
3863
+ if (!isInstalled()) {
3864
+ throw new Error("\u767B\u5F55\u81EA\u542F\u672A\u5B89\u88C5\uFF08\u5148\u8FD0\u884C `feishu-codex-bridge start`\uFF09\u3002");
3865
+ }
3831
3866
  await ensureLogFiles();
3832
- await writeFile6(cmdPath, buildLauncherCmd(), "utf8");
3833
- }
3834
- async function installSchtask() {
3835
- await writeLauncherCmd();
3836
- const create = runSchtasks([
3837
- "/Create",
3838
- "/F",
3839
- "/SC",
3840
- "ONLOGON",
3841
- "/RL",
3842
- "LIMITED",
3843
- "/TN",
3844
- WINDOWS_TASK_NAME,
3845
- "/TR",
3846
- `"${launcherCmdPath()}"`
3847
- ]);
3848
- if (!create.ok) throw schtasksError("schtasks /Create", create);
3849
- const run = runSchtasks(["/Run", "/TN", WINDOWS_TASK_NAME]);
3850
- if (!run.ok) throw schtasksError("schtasks /Run", run);
3851
- return statusSchtask();
3852
- }
3853
- async function uninstallSchtask() {
3854
- runSchtasks(["/End", "/TN", WINDOWS_TASK_NAME]);
3855
- const del = runSchtasks(["/Delete", "/F", "/TN", WINDOWS_TASK_NAME]);
3856
- if (!del.ok && isTaskRegistered()) throw schtasksError("schtasks /Delete", del);
3857
- if (existsSync5(launcherCmdPath())) await rm3(launcherCmdPath(), { force: true });
3858
- }
3859
- async function restartSchtask() {
3860
- if (!isTaskRegistered()) {
3861
- throw new Error(`\u8BA1\u5212\u4EFB\u52A1\u672A\u5B89\u88C5\uFF1A${WINDOWS_TASK_NAME}\uFF08\u5148\u8FD0\u884C \`feishu-codex-bridge start\`\uFF09`);
3862
- }
3863
- runSchtasks(["/End", "/TN", WINDOWS_TASK_NAME]);
3864
- await waitUntilStopped();
3865
- const run = runSchtasks(["/Run", "/TN", WINDOWS_TASK_NAME]);
3866
- if (!run.ok) throw schtasksError("schtasks /Run", run);
3867
- return statusSchtask();
3868
- }
3869
- function statusSchtask() {
3870
- const installed = isTaskRegistered();
3871
- const raw = installed ? describeTask() : "";
3867
+ killService();
3868
+ startNow();
3869
+ return statusWinStartup();
3870
+ }
3871
+ function statusWinStartup() {
3872
+ const installed = isInstalled();
3873
+ const pid = readServicePid();
3874
+ const running = pid !== null && pidAlive(pid);
3872
3875
  return {
3873
- platformName: "Task Scheduler (Windows)",
3876
+ platformName: "Windows \u767B\u5F55\u81EA\u542F\uFF08HKCU Run\uFF0C\u514D\u7BA1\u7406\u5458\uFF09",
3874
3877
  installed,
3875
- running: installed && /Status:\s+Running/i.test(raw),
3876
- servicePath: WINDOWS_TASK_NAME,
3878
+ running,
3879
+ servicePath: `${RUN_KEY_PATH}\\${RUN_KEY_NAME}`,
3877
3880
  stdoutPath: serviceStdoutPath(),
3878
3881
  stderrPath: serviceStderrPath(),
3879
- // `Process ID:` only appears in verbose output while the task is running.
3880
- pid: raw.match(/Process ID:\s*(\d+)/i)?.[1],
3881
- // `Last Result: 0` ⇒ last run succeeded. Surface it as the exit code.
3882
- lastExit: raw.match(/Last Result:\s*(-?\d+)/i)?.[1],
3883
- raw
3882
+ pid: running && pid !== null ? String(pid) : void 0,
3883
+ raw: ""
3884
3884
  };
3885
3885
  }
3886
- function isTaskRegistered() {
3887
- const r = spawnSync2("schtasks", ["/Query", "/TN", WINDOWS_TASK_NAME], {
3886
+ function winStartupRunning() {
3887
+ const pid = readServicePid();
3888
+ return pid !== null && pidAlive(pid);
3889
+ }
3890
+ function recordServicePid() {
3891
+ if (process.platform !== "win32" || !process.env[SERVICE_ENV_FLAG]) return;
3892
+ try {
3893
+ writeFileSync(servicePidFile(), String(process.pid), "utf8");
3894
+ } catch {
3895
+ }
3896
+ process.once("exit", clearServicePid);
3897
+ }
3898
+ function clearServicePid() {
3899
+ if (process.platform !== "win32" || !process.env[SERVICE_ENV_FLAG]) return;
3900
+ try {
3901
+ if (readServicePid() === process.pid) rmSync(servicePidFile(), { force: true });
3902
+ } catch {
3903
+ }
3904
+ }
3905
+ function isInstalled() {
3906
+ const r = spawnSync2("reg", ["query", RUN_KEY_PATH, "/v", RUN_KEY_NAME], {
3888
3907
  stdio: ["ignore", "ignore", "ignore"]
3889
3908
  });
3890
3909
  return r.status === 0;
3891
3910
  }
3892
- function schtaskRunning() {
3893
- if (!isTaskRegistered()) return false;
3894
- return /Status:\s+Running/i.test(describeTask());
3895
- }
3896
- function describeTask() {
3897
- const r = runSchtasks(["/Query", "/V", "/FO", "LIST", "/TN", WINDOWS_TASK_NAME]);
3898
- return r.stdout || r.stderr || "";
3911
+ function readServicePid() {
3912
+ try {
3913
+ const pid = Number.parseInt(readFileSync2(servicePidFile(), "utf8").trim(), 10);
3914
+ return Number.isFinite(pid) ? pid : null;
3915
+ } catch {
3916
+ return null;
3917
+ }
3899
3918
  }
3900
- async function waitUntilStopped(timeoutMs = 5e3) {
3901
- const deadline = Date.now() + timeoutMs;
3902
- while (Date.now() < deadline) {
3903
- if (!schtaskRunning()) return true;
3904
- await new Promise((r) => setTimeout(r, 200));
3919
+ function pidAlive(pid) {
3920
+ try {
3921
+ process.kill(pid, 0);
3922
+ return true;
3923
+ } catch (err) {
3924
+ return err.code === "EPERM";
3905
3925
  }
3906
- return false;
3926
+ }
3927
+ function killService() {
3928
+ const pid = readServicePid();
3929
+ if (pid === null || !pidAlive(pid)) return;
3930
+ spawnSync2("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore" });
3907
3931
  }
3908
3932
 
3909
3933
  // src/service/systemd.ts
3910
3934
  import { spawnSync as spawnSync3 } from "child_process";
3911
- import { existsSync as existsSync6 } from "fs";
3912
- import { mkdir as mkdir8, rm as rm4, writeFile as writeFile7 } from "fs/promises";
3935
+ import { existsSync as existsSync5 } from "fs";
3936
+ import { mkdir as mkdir8, rm as rm3, writeFile as writeFile7 } from "fs/promises";
3913
3937
  import { homedir as homedir4 } from "os";
3914
- import { dirname as dirname9, join as join10 } from "path";
3938
+ import { dirname as dirname8, join as join10 } from "path";
3915
3939
  var SYSTEMD_UNIT_NAME = "feishu-codex-bridge.service";
3916
3940
  function systemdUnitPath() {
3917
3941
  const base = process.env.XDG_CONFIG_HOME ?? join10(homedir4(), ".config");
@@ -3969,7 +3993,7 @@ function ensureSystemdOrThrow() {
3969
3993
  async function installSystemd() {
3970
3994
  ensureSystemdOrThrow();
3971
3995
  const unitPath = systemdUnitPath();
3972
- await mkdir8(dirname9(unitPath), { recursive: true });
3996
+ await mkdir8(dirname8(unitPath), { recursive: true });
3973
3997
  await ensureLogFiles();
3974
3998
  await writeFile7(unitPath, buildUnit(), "utf8");
3975
3999
  const reload = runSystemctl(["daemon-reload"]);
@@ -3982,7 +4006,7 @@ async function uninstallSystemd() {
3982
4006
  if (systemdAvailable() && unitExists()) {
3983
4007
  runSystemctl(["disable", "--now", SYSTEMD_UNIT_NAME]);
3984
4008
  }
3985
- await rm4(systemdUnitPath(), { force: true });
4009
+ await rm3(systemdUnitPath(), { force: true });
3986
4010
  if (systemdAvailable()) runSystemctl(["daemon-reload"]);
3987
4011
  }
3988
4012
  async function restartSystemd() {
@@ -4012,7 +4036,7 @@ function statusSystemd() {
4012
4036
  };
4013
4037
  }
4014
4038
  function unitExists() {
4015
- return existsSync6(systemdUnitPath());
4039
+ return existsSync5(systemdUnitPath());
4016
4040
  }
4017
4041
  function systemdActive() {
4018
4042
  const r = spawnSync3("systemctl", ["--user", "is-active", SYSTEMD_UNIT_NAME], {
@@ -4038,10 +4062,10 @@ function getServiceAdapter() {
4038
4062
  }
4039
4063
  if (process.platform === "win32") {
4040
4064
  return {
4041
- install: installSchtask,
4042
- uninstall: uninstallSchtask,
4043
- status: async () => statusSchtask(),
4044
- restart: restartSchtask,
4065
+ install: installWinStartup,
4066
+ uninstall: uninstallWinStartup,
4067
+ status: async () => statusWinStartup(),
4068
+ restart: restartWinStartup,
4045
4069
  logs: tailServiceLogs
4046
4070
  };
4047
4071
  }
@@ -4061,7 +4085,7 @@ function getServiceAdapter() {
4061
4085
  function isServiceRunning() {
4062
4086
  try {
4063
4087
  if (process.platform === "darwin") return isLoaded();
4064
- if (process.platform === "win32") return schtaskRunning();
4088
+ if (process.platform === "win32") return winStartupRunning();
4065
4089
  if (process.platform === "linux") return systemdActive();
4066
4090
  } catch {
4067
4091
  }
@@ -4069,14 +4093,13 @@ function isServiceRunning() {
4069
4093
  }
4070
4094
 
4071
4095
  // src/service/update.ts
4072
- var execFileP = promisify(execFile);
4073
- var NPM = process.platform === "win32" ? "npm.cmd" : "npm";
4096
+ var NPM = "npm";
4074
4097
  function pkgRoot() {
4075
- return resolve5(dirname10(fileURLToPath4(import.meta.url)), "..");
4098
+ return resolve5(dirname9(fileURLToPath4(import.meta.url)), "..");
4076
4099
  }
4077
4100
  function pkgJson() {
4078
4101
  try {
4079
- return JSON.parse(readFileSync2(join11(pkgRoot(), "package.json"), "utf8"));
4102
+ return JSON.parse(readFileSync3(join11(pkgRoot(), "package.json"), "utf8"));
4080
4103
  } catch {
4081
4104
  return {};
4082
4105
  }
@@ -4088,7 +4111,7 @@ function packageName() {
4088
4111
  return pkgJson().name ?? "@modelzen/feishu-codex-bridge";
4089
4112
  }
4090
4113
  function isDevSource() {
4091
- return existsSync7(join11(pkgRoot(), ".git"));
4114
+ return existsSync6(join11(pkgRoot(), ".git"));
4092
4115
  }
4093
4116
  function isNewer(a, b) {
4094
4117
  const pa = a.split(".").map((n) => Number.parseInt(n, 10) || 0);
@@ -4100,18 +4123,31 @@ function isNewer(a, b) {
4100
4123
  return false;
4101
4124
  }
4102
4125
  async function latestVersion() {
4103
- try {
4104
- const { stdout } = await execFileP(NPM, ["view", packageName(), "version"], { timeout: 2e4 });
4105
- const v = stdout.trim();
4106
- return /^\d+\.\d+\.\d+/.test(v) ? v : null;
4107
- } catch {
4108
- return null;
4109
- }
4126
+ const v = await new Promise((resolveP) => {
4127
+ const child = spawnProcess(NPM, ["view", packageName(), "version"], {
4128
+ stdio: ["ignore", "pipe", "ignore"]
4129
+ });
4130
+ let out = "";
4131
+ const timer = setTimeout(() => {
4132
+ child.kill();
4133
+ resolveP(null);
4134
+ }, 2e4);
4135
+ child.stdout?.on("data", (d) => out += d);
4136
+ child.on("error", () => {
4137
+ clearTimeout(timer);
4138
+ resolveP(null);
4139
+ });
4140
+ child.on("close", (code) => {
4141
+ clearTimeout(timer);
4142
+ resolveP(code === 0 ? out.trim() : null);
4143
+ });
4144
+ });
4145
+ return v && /^\d+\.\d+\.\d+/.test(v) ? v : null;
4110
4146
  }
4111
4147
  async function installLatest(opts = {}) {
4112
4148
  const target = `${packageName()}@latest`;
4113
4149
  return await new Promise((resolveP) => {
4114
- const child = spawn4(NPM, ["install", "-g", target], {
4150
+ const child = spawnProcess(NPM, ["install", "-g", target], {
4115
4151
  stdio: opts.inherit ? ["ignore", "inherit", "inherit"] : ["ignore", "pipe", "pipe"]
4116
4152
  });
4117
4153
  let out = "";
@@ -4135,18 +4171,21 @@ async function restartDaemon() {
4135
4171
 
4136
4172
  // src/project/lifecycle.ts
4137
4173
  import { mkdir as mkdir9 } from "fs/promises";
4138
- import { existsSync as existsSync8 } from "fs";
4174
+ import { existsSync as existsSync7 } from "fs";
4139
4175
  import { isAbsolute as isAbsolute2, join as join12, resolve as resolve6 } from "path";
4140
4176
 
4141
4177
  // src/project/git-info.ts
4142
- import { execFile as execFile2 } from "child_process";
4143
- import { promisify as promisify2 } from "util";
4144
- var execFileAsync = promisify2(execFile2);
4178
+ import { execFile } from "child_process";
4179
+ import { promisify } from "util";
4180
+ var execFileAsync = promisify(execFile);
4145
4181
  async function currentBranch(cwd) {
4146
4182
  try {
4147
4183
  const { stdout } = await execFileAsync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
4148
4184
  cwd,
4149
- timeout: 3e3
4185
+ timeout: 3e3,
4186
+ // Hide the console window: this runs on every inbound message, and the
4187
+ // Windows background service has no console of its own to inherit.
4188
+ windowsHide: true
4150
4189
  });
4151
4190
  const b = stdout.trim();
4152
4191
  return b && b !== "HEAD" ? b : null;
@@ -4272,7 +4311,7 @@ async function onboardGroup(channel, project) {
4272
4311
  async function resolveCwd(name, existingPath) {
4273
4312
  if (existingPath) {
4274
4313
  const cwd2 = isAbsolute2(existingPath) ? existingPath : resolve6(existingPath);
4275
- if (!existsSync8(cwd2)) throw new Error(`\u6587\u4EF6\u5939\u4E0D\u5B58\u5728\uFF1A${cwd2}`);
4314
+ if (!existsSync7(cwd2)) throw new Error(`\u6587\u4EF6\u5939\u4E0D\u5B58\u5728\uFF1A${cwd2}`);
4276
4315
  return { cwd: cwd2, blank: false };
4277
4316
  }
4278
4317
  const cwd = join12(paths.projectsRootDir, name);
@@ -4344,7 +4383,7 @@ async function leaveChat(channel, chatId) {
4344
4383
 
4345
4384
  // src/bot/session-store.ts
4346
4385
  import { mkdir as mkdir10, readFile as readFile8, rename as rename5, writeFile as writeFile8 } from "fs/promises";
4347
- import { dirname as dirname11 } from "path";
4386
+ import { dirname as dirname10 } from "path";
4348
4387
  var FILE_VERSION3 = 1;
4349
4388
  async function read2() {
4350
4389
  try {
@@ -4357,7 +4396,7 @@ async function read2() {
4357
4396
  }
4358
4397
  }
4359
4398
  async function write2(sessions) {
4360
- await mkdir10(dirname11(paths.sessionsFile), { recursive: true });
4399
+ await mkdir10(dirname10(paths.sessionsFile), { recursive: true });
4361
4400
  const tmp = `${paths.sessionsFile}.tmp-${process.pid}`;
4362
4401
  const body = { version: FILE_VERSION3, sessions };
4363
4402
  await writeFile8(tmp, `${JSON.stringify(body, null, 2)}
@@ -4404,7 +4443,7 @@ async function handleDmConsole(channel, cfg, msg) {
4404
4443
  }
4405
4444
 
4406
4445
  // src/bot/media.ts
4407
- import { mkdir as mkdir11, readdir as readdir2, rm as rm5, stat as stat3 } from "fs/promises";
4446
+ import { mkdir as mkdir11, readdir as readdir2, rm as rm4, stat as stat3 } from "fs/promises";
4408
4447
  import { join as join13 } from "path";
4409
4448
  var MAX_IMAGES2 = 9;
4410
4449
  var MEDIA_TTL_MS = 60 * 6e4;
@@ -4547,7 +4586,7 @@ async function pruneOldMedia() {
4547
4586
  const file = join13(paths.mediaDir, name);
4548
4587
  try {
4549
4588
  const st = await stat3(file);
4550
- if (st.mtimeMs < cutoff) await rm5(file, { force: true });
4589
+ if (st.mtimeMs < cutoff) await rm4(file, { force: true });
4551
4590
  } catch {
4552
4591
  }
4553
4592
  }
@@ -5918,8 +5957,8 @@ async function startBridge(opts) {
5918
5957
  }
5919
5958
 
5920
5959
  // src/core/single-instance.ts
5921
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, unlinkSync, writeFileSync } from "fs";
5922
- import { dirname as dirname12 } from "path";
5960
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
5961
+ import { dirname as dirname11 } from "path";
5923
5962
  var BridgeAlreadyRunningError = class extends Error {
5924
5963
  constructor(pid) {
5925
5964
  super(
@@ -5941,20 +5980,20 @@ function isAlive(pid) {
5941
5980
  function acquireSingleInstanceLock(appId) {
5942
5981
  const file = paths.processesFile;
5943
5982
  try {
5944
- const rec = JSON.parse(readFileSync3(file, "utf8"));
5983
+ const rec = JSON.parse(readFileSync4(file, "utf8"));
5945
5984
  if (rec.pid && rec.pid !== process.pid && rec.appId === appId && isAlive(rec.pid)) {
5946
5985
  throw new BridgeAlreadyRunningError(rec.pid);
5947
5986
  }
5948
5987
  } catch (err) {
5949
5988
  if (err instanceof BridgeAlreadyRunningError) throw err;
5950
5989
  }
5951
- mkdirSync2(dirname12(file), { recursive: true });
5990
+ mkdirSync2(dirname11(file), { recursive: true });
5952
5991
  const record = { pid: process.pid, appId, startedAt: Date.now() };
5953
- writeFileSync(file, `${JSON.stringify(record)}
5992
+ writeFileSync2(file, `${JSON.stringify(record)}
5954
5993
  `, "utf8");
5955
5994
  const release = () => {
5956
5995
  try {
5957
- const rec = JSON.parse(readFileSync3(file, "utf8"));
5996
+ const rec = JSON.parse(readFileSync4(file, "utf8"));
5958
5997
  if (rec.pid === process.pid) unlinkSync(file);
5959
5998
  } catch {
5960
5999
  }
@@ -5983,6 +6022,7 @@ async function runRun() {
5983
6022
  }
5984
6023
  throw err;
5985
6024
  }
6025
+ recordServicePid();
5986
6026
  const fallbackCwd = process.env.FEISHU_CODEX_CWD || process.cwd();
5987
6027
  console.log("\n\u6B63\u5728\u542F\u52A8\u957F\u8FDE\u63A5 bot\u2026");
5988
6028
  console.log("\u79C1\u804A\u6211 `/new <\u540D>` \u5EFA\u9879\u76EE\uFF1B\u5728\u9879\u76EE\u7FA4\u91CC @\u6211 \u5E72\u6D3B\u3002Ctrl+C \u9000\u51FA\u3002\n");
@@ -6038,7 +6078,7 @@ async function runLogs(follow) {
6038
6078
  function installedNote() {
6039
6079
  switch (process.platform) {
6040
6080
  case "win32":
6041
- return "\u2713 \u540E\u53F0\u670D\u52A1\u5DF2\u5B89\u88C5\u5E76\u542F\u52A8\uFF08\u767B\u5F55\u81EA\u542F\uFF1B\u6CE8\u610F\uFF1AWindows \u8BA1\u5212\u4EFB\u52A1\u65E0\u5D29\u6E83\u81EA\u52A8\u62C9\u8D77\uFF09\u3002";
6081
+ return "\u2713 \u540E\u53F0\u670D\u52A1\u5DF2\u5B89\u88C5\u5E76\u542F\u52A8\uFF08\u767B\u5F55\u81EA\u542F\uFF0C\u514D\u7BA1\u7406\u5458\uFF09\u3002\n \u63D0\u793A\uFF1A\u767B\u5F55\u81EA\u542F\u5728\u4E0B\u6B21\u767B\u5F55\u751F\u6548\uFF1B\u5F53\u524D\u5DF2\u5728\u540E\u53F0\u9690\u85CF\u542F\u52A8\u3002\u6CE8\u610F Windows \u767B\u5F55\u81EA\u542F\u65E0\u5D29\u6E83\u81EA\u52A8\u62C9\u8D77\u3002";
6042
6082
  case "linux":
6043
6083
  return "\u2713 \u540E\u53F0\u670D\u52A1\u5DF2\u5B89\u88C5\u5E76\u542F\u52A8\uFF08\u767B\u5F55\u81EA\u542F\u3001\u5D29\u6E83\u81EA\u52A8\u62C9\u8D77\uFF09\u3002\n \u63D0\u793A\uFF1A\u6CE8\u9500\u540E\u4ECD\u4FDD\u6301\u8FD0\u884C\u9700\u6267\u884C\u4E00\u6B21 `loginctl enable-linger $USER`\u3002";
6044
6084
  default:
@@ -6110,7 +6150,7 @@ async function runUpdate(opts = {}) {
6110
6150
  }
6111
6151
 
6112
6152
  // src/cli/commands/bot.ts
6113
- import { rm as rm6 } from "fs/promises";
6153
+ import { rm as rm5 } from "fs/promises";
6114
6154
  async function runBotInit(name) {
6115
6155
  if (!ensureCodex()) {
6116
6156
  process.exitCode = 1;
@@ -6165,7 +6205,7 @@ async function runBotRm(name) {
6165
6205
  }
6166
6206
  const after = await removeBot(bot2.appId);
6167
6207
  await removeSecret(secretKeyForApp(bot2.appId));
6168
- await rm6(botDir(bot2.appId), { recursive: true, force: true });
6208
+ await rm5(botDir(bot2.appId), { recursive: true, force: true });
6169
6209
  console.log(`\u2713 \u5DF2\u79FB\u9664\u673A\u5668\u4EBA\u300C${bot2.name}\u300D(${bot2.appId})\uFF1A\u6CE8\u518C\u8868 + \u5BC6\u94A5 + \u72B6\u6001\u76EE\u5F55(projects/sessions)\u3002`);
6170
6210
  if (after.bots.length === 0) {
6171
6211
  console.log(" \u5DF2\u65E0\u4EFB\u4F55\u673A\u5668\u4EBA\uFF0C`bot init` \u91CD\u65B0\u521B\u5EFA\u3002");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelzen/feishu-codex-bridge",
3
- "version": "0.2.2-win",
3
+ "version": "0.3.0-win.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": {