@modelzen/feishu-codex-bridge 0.3.0 → 0.3.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 +301 -32
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -228,6 +228,21 @@ function findBot(reg, nameOrAppId) {
228
228
  function currentBot(reg) {
229
229
  return reg.current ? reg.bots.find((b) => b.appId === reg.current) : void 0;
230
230
  }
231
+ function activeBots(reg) {
232
+ const configured = reg.bots.some((b) => b.active !== void 0);
233
+ if (configured) return reg.bots.filter((b) => b.active === true);
234
+ const cur = currentBot(reg);
235
+ return cur ? [cur] : [];
236
+ }
237
+ async function setActiveBots(appIds) {
238
+ const reg = await loadBots();
239
+ const want = new Set(appIds);
240
+ for (const b of reg.bots) b.active = want.has(b.appId);
241
+ const firstActive = reg.bots.find((b) => b.active);
242
+ if (firstActive) reg.current = firstActive.appId;
243
+ await saveBots(reg);
244
+ return reg;
245
+ }
231
246
  async function addBot(entry) {
232
247
  const reg = await loadBots();
233
248
  reg.bots = reg.bots.filter((b) => b.appId !== entry.appId);
@@ -236,11 +251,6 @@ async function addBot(entry) {
236
251
  await saveBots(reg);
237
252
  return reg;
238
253
  }
239
- async function setCurrent(appId) {
240
- const reg = await loadBots();
241
- reg.current = appId;
242
- await saveBots(reg);
243
- }
244
254
  async function removeBot(appId) {
245
255
  const reg = await loadBots();
246
256
  reg.bots = reg.bots.filter((b) => b.appId !== appId);
@@ -1026,8 +1036,14 @@ function ensureCodex() {
1026
1036
  async function ensureOnboarded(opts = {}) {
1027
1037
  if (!ensureCodex()) return null;
1028
1038
  const reg = await ensureRegistry();
1029
- const entry = currentBot(reg);
1039
+ const entry = opts.bot ? findBot(reg, opts.bot) : currentBot(reg);
1030
1040
  if (!entry) {
1041
+ if (opts.bot) {
1042
+ console.error(
1043
+ `\u2717 \u627E\u4E0D\u5230\u673A\u5668\u4EBA\u300C${opts.bot}\u300D\u3002\u7528 \`feishu-codex-bridge bot list\` \u67E5\u770B\u5DF2\u6CE8\u518C\u7684\u673A\u5668\u4EBA\u3002`
1044
+ );
1045
+ return null;
1046
+ }
1031
1047
  if (!opts.allowCreate) {
1032
1048
  console.error("\u2717 \u5C1A\u672A\u914D\u7F6E\u4EFB\u4F55\u98DE\u4E66\u673A\u5668\u4EBA\u3002\u8BF7\u5148\u8FD0\u884C `feishu-codex-bridge bot init`\uFF08\u6216\u524D\u53F0 `run`\uFF09\u626B\u7801\u521B\u5EFA\u3002");
1033
1049
  return null;
@@ -5956,6 +5972,94 @@ async function startBridge(opts) {
5956
5972
  return { channel, shutdown };
5957
5973
  }
5958
5974
 
5975
+ // src/bot/supervisor.ts
5976
+ var BACKOFF_MIN_MS = 1e3;
5977
+ var BACKOFF_MAX_MS = 3e4;
5978
+ var HEALTHY_UPTIME_MS = 6e4;
5979
+ var SHUTDOWN_GRACE_MS = 8e3;
5980
+ async function runSupervisor(bots) {
5981
+ const cliEntry = process.argv[1];
5982
+ if (!cliEntry) throw new Error("supervisor: \u65E0\u6CD5\u89E3\u6790 CLI \u5165\u53E3\uFF08process.argv[1] \u4E3A\u7A7A\uFF09");
5983
+ const childEnv = { ...process.env };
5984
+ delete childEnv[SERVICE_ENV_FLAG];
5985
+ let shuttingDown = false;
5986
+ const children = bots.map((bot2) => ({ bot: bot2, backoffMs: BACKOFF_MIN_MS, startedAt: 0 }));
5987
+ console.log(`
5988
+ \u6B63\u5728\u542F\u52A8 ${bots.length} \u4E2A\u673A\u5668\u4EBA\uFF08\u5404\u81EA\u72EC\u7ACB\u8FDB\u7A0B\uFF09\uFF1A`);
5989
+ for (const b of bots) console.log(` \u2022 ${b.name} (${b.appId}) [${b.tenant}]`);
5990
+ console.log("Ctrl+C \u9000\u51FA\uFF08\u5173\u95ED\u5168\u90E8\uFF09\u3002\n");
5991
+ const prefixPipe = (name, src, dst) => {
5992
+ if (!src) return;
5993
+ let buf = "";
5994
+ src.setEncoding("utf8");
5995
+ src.on("data", (chunk) => {
5996
+ buf += chunk;
5997
+ let nl;
5998
+ while ((nl = buf.indexOf("\n")) >= 0) {
5999
+ const line = buf.slice(0, nl);
6000
+ buf = buf.slice(nl + 1);
6001
+ dst.write(`\x1B[2m[${name}]\x1B[0m ${line}
6002
+ `);
6003
+ }
6004
+ });
6005
+ src.on("end", () => {
6006
+ if (buf) dst.write(`\x1B[2m[${name}]\x1B[0m ${buf}
6007
+ `);
6008
+ });
6009
+ };
6010
+ const spawnChild = (c) => {
6011
+ c.startedAt = Date.now();
6012
+ const proc = spawnProcess(process.execPath, [cliEntry, "run", "--bot", c.bot.appId], {
6013
+ stdio: ["ignore", "pipe", "pipe"],
6014
+ env: childEnv
6015
+ });
6016
+ c.proc = proc;
6017
+ log.info("supervisor", "child-start", { bot: c.bot.name, appId: c.bot.appId, pid: proc.pid ?? null });
6018
+ prefixPipe(c.bot.name, proc.stdout, process.stdout);
6019
+ prefixPipe(c.bot.name, proc.stderr, process.stderr);
6020
+ proc.on("exit", (code, signal) => {
6021
+ c.proc = void 0;
6022
+ if (shuttingDown) return;
6023
+ const uptime = Date.now() - c.startedAt;
6024
+ if (uptime >= HEALTHY_UPTIME_MS) c.backoffMs = BACKOFF_MIN_MS;
6025
+ const wait = c.backoffMs;
6026
+ c.backoffMs = Math.min(c.backoffMs * 2, BACKOFF_MAX_MS);
6027
+ log.warn("supervisor", "child-exit", { bot: c.bot.name, code, signal, restartInMs: wait });
6028
+ console.error(
6029
+ `\x1B[2m[${c.bot.name}]\x1B[0m \u8FDB\u7A0B\u9000\u51FA\uFF08code=${code ?? signal ?? "?"}\uFF09\uFF0C${Math.round(wait / 1e3)}s \u540E\u91CD\u542F\u2026`
6030
+ );
6031
+ c.restartTimer = setTimeout(() => spawnChild(c), wait);
6032
+ });
6033
+ proc.on("error", (err) => {
6034
+ log.fail("supervisor", err, { bot: c.bot.name, phase: "spawn" });
6035
+ });
6036
+ };
6037
+ for (const c of children) spawnChild(c);
6038
+ await new Promise((resolve7) => {
6039
+ const stop = (sig) => {
6040
+ if (shuttingDown) return;
6041
+ shuttingDown = true;
6042
+ console.log(`
6043
+ \u6536\u5230 ${sig}\uFF0C\u6B63\u5728\u5173\u95ED\u5168\u90E8\u673A\u5668\u4EBA\u2026`);
6044
+ for (const c of children) {
6045
+ if (c.restartTimer) clearTimeout(c.restartTimer);
6046
+ c.proc?.kill("SIGTERM");
6047
+ }
6048
+ const deadline = Date.now() + SHUTDOWN_GRACE_MS;
6049
+ const poll = setInterval(() => {
6050
+ const alive = children.filter((c) => c.proc && !c.proc.killed);
6051
+ if (alive.length === 0 || Date.now() >= deadline) {
6052
+ clearInterval(poll);
6053
+ for (const c of alive) c.proc?.kill("SIGKILL");
6054
+ resolve7();
6055
+ }
6056
+ }, 200);
6057
+ };
6058
+ for (const sig of ["SIGINT", "SIGTERM"]) process.once(sig, () => stop(sig));
6059
+ });
6060
+ process.exit(0);
6061
+ }
6062
+
5959
6063
  // src/core/single-instance.ts
5960
6064
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
5961
6065
  import { dirname as dirname11 } from "path";
@@ -6003,8 +6107,20 @@ function acquireSingleInstanceLock(appId) {
6003
6107
  }
6004
6108
 
6005
6109
  // src/cli/commands/run.ts
6006
- async function runRun() {
6007
- const ready = await ensureOnboarded({ allowCreate: true });
6110
+ async function runRun(botName) {
6111
+ if (botName) {
6112
+ await runSingle(botName);
6113
+ return;
6114
+ }
6115
+ const active = activeBots(await loadBots());
6116
+ if (active.length > 1) {
6117
+ await runSupervisor(active);
6118
+ return;
6119
+ }
6120
+ await runSingle(active[0]?.name);
6121
+ }
6122
+ async function runSingle(botName) {
6123
+ const ready = await ensureOnboarded({ allowCreate: !botName, bot: botName });
6008
6124
  if (!ready) {
6009
6125
  process.exitCode = 1;
6010
6126
  return;
@@ -6047,14 +6163,37 @@ async function runRun() {
6047
6163
 
6048
6164
  // src/cli/commands/daemon.ts
6049
6165
  async function runStart() {
6050
- const ready = await ensureOnboarded({ allowCreate: true });
6051
- if (!ready) {
6052
- process.exitCode = 1;
6053
- return;
6054
- }
6055
- if (!await confirmReadyForDaemon(ready)) {
6056
- process.exitCode = 1;
6057
- return;
6166
+ const active = activeBots(await loadBots());
6167
+ if (active.length === 0) {
6168
+ const ready = await ensureOnboarded({ allowCreate: true });
6169
+ if (!ready) {
6170
+ process.exitCode = 1;
6171
+ return;
6172
+ }
6173
+ if (!await confirmReadyForDaemon(ready)) {
6174
+ process.exitCode = 1;
6175
+ return;
6176
+ }
6177
+ } else {
6178
+ if (active.length > 1) {
6179
+ console.log(`
6180
+ \u540E\u53F0\u670D\u52A1\u5C06\u6258\u7BA1 ${active.length} \u4E2A\u673A\u5668\u4EBA\uFF08supervisor \u591A\u8FDB\u7A0B\uFF0C\u5404\u81EA\u72EC\u7ACB\u8FDB\u7A0B\uFF09\uFF1A`);
6181
+ for (const b of active) console.log(` \u2022 ${b.name} (${b.appId}) [${b.tenant}]`);
6182
+ console.log("");
6183
+ }
6184
+ for (const bot2 of active) {
6185
+ if (active.length > 1) console.log(`
6186
+ \u2500\u2500\u2500\u2500 \u673A\u5668\u4EBA\u300C${bot2.name}\u300D(${bot2.appId}) \u2500\u2500\u2500\u2500`);
6187
+ const ready = await ensureOnboarded({ bot: bot2.appId });
6188
+ if (!ready) {
6189
+ process.exitCode = 1;
6190
+ return;
6191
+ }
6192
+ if (!await confirmReadyForDaemon(ready)) {
6193
+ process.exitCode = 1;
6194
+ return;
6195
+ }
6196
+ }
6058
6197
  }
6059
6198
  const status = await getServiceAdapter().install();
6060
6199
  console.log(installedNote());
@@ -6151,6 +6290,98 @@ async function runUpdate(opts = {}) {
6151
6290
 
6152
6291
  // src/cli/commands/bot.ts
6153
6292
  import { rm as rm5 } from "fs/promises";
6293
+
6294
+ // src/cli/checkbox.ts
6295
+ import { emitKeypressEvents } from "readline";
6296
+ var ESC = "\x1B";
6297
+ var HIDE_CURSOR = `${ESC}[?25l`;
6298
+ var SHOW_CURSOR = `${ESC}[?25h`;
6299
+ var ALT_SCREEN_ON = `${ESC}[?1049h`;
6300
+ var ALT_SCREEN_OFF = `${ESC}[?1049l`;
6301
+ var HOME_AND_CLEAR = `${ESC}[H${ESC}[2J`;
6302
+ async function checkboxSelect(title, items) {
6303
+ const input2 = process.stdin;
6304
+ const output = process.stdout;
6305
+ if (!input2.isTTY) throw new Error("checkboxSelect requires an interactive TTY");
6306
+ const checked = items.map((it) => Boolean(it.checked));
6307
+ let cursor = 0;
6308
+ const frame = () => {
6309
+ const lines = [title, ""];
6310
+ items.forEach((it, i) => {
6311
+ const box = checked[i] ? "\x1B[32m[x]\x1B[0m" : "[ ]";
6312
+ const pointer = i === cursor ? "\x1B[36m>\x1B[0m" : " ";
6313
+ const label = i === cursor ? `\x1B[1m${it.label}\x1B[0m` : it.label;
6314
+ const hint = it.hint ? ` \x1B[2m${it.hint}\x1B[0m` : "";
6315
+ lines.push(`${pointer} ${box} ${label}${hint}`);
6316
+ });
6317
+ const n = checked.filter(Boolean).length;
6318
+ lines.push("");
6319
+ lines.push(`\x1B[2m${n} \u4E2A\u5DF2\u52FE\u9009 \xB7 \u2191\u2193 \u79FB\u52A8 \xB7 \u7A7A\u683C\u52FE\u9009 \xB7 a \u5168\u9009 \xB7 \u56DE\u8F66\u786E\u8BA4 \xB7 q \u53D6\u6D88\x1B[0m`);
6320
+ return lines.join("\r\n");
6321
+ };
6322
+ const redraw = () => {
6323
+ output.write(HOME_AND_CLEAR + frame());
6324
+ };
6325
+ emitKeypressEvents(input2);
6326
+ const wasRaw = Boolean(input2.isRaw);
6327
+ let restored = false;
6328
+ const restore = () => {
6329
+ if (restored) return;
6330
+ restored = true;
6331
+ output.write(`${SHOW_CURSOR}${ALT_SCREEN_OFF}`);
6332
+ };
6333
+ input2.setRawMode?.(true);
6334
+ input2.resume();
6335
+ output.write(`${ALT_SCREEN_ON}${HIDE_CURSOR}`);
6336
+ process.once("exit", restore);
6337
+ redraw();
6338
+ return await new Promise((resolve7) => {
6339
+ const cleanup = () => {
6340
+ input2.off("keypress", onKey);
6341
+ input2.setRawMode?.(wasRaw);
6342
+ input2.pause();
6343
+ process.off("exit", restore);
6344
+ restore();
6345
+ };
6346
+ const onKey = (_str, key) => {
6347
+ const name = key?.name;
6348
+ if (key?.ctrl && name === "c" || name === "escape" || name === "q") {
6349
+ cleanup();
6350
+ resolve7(null);
6351
+ return;
6352
+ }
6353
+ if (name === "return" || name === "enter") {
6354
+ cleanup();
6355
+ resolve7(checked.flatMap((on, i) => on ? [i] : []));
6356
+ return;
6357
+ }
6358
+ if (name === "up" || name === "k") {
6359
+ cursor = (cursor - 1 + items.length) % items.length;
6360
+ redraw();
6361
+ return;
6362
+ }
6363
+ if (name === "down" || name === "j") {
6364
+ cursor = (cursor + 1) % items.length;
6365
+ redraw();
6366
+ return;
6367
+ }
6368
+ if (name === "space") {
6369
+ checked[cursor] = !checked[cursor];
6370
+ redraw();
6371
+ return;
6372
+ }
6373
+ if (name === "a") {
6374
+ const allOn = checked.every(Boolean);
6375
+ for (let i = 0; i < checked.length; i++) checked[i] = !allOn;
6376
+ redraw();
6377
+ return;
6378
+ }
6379
+ };
6380
+ input2.on("keypress", onKey);
6381
+ });
6382
+ }
6383
+
6384
+ // src/cli/commands/bot.ts
6154
6385
  async function runBotInit(name) {
6155
6386
  if (!ensureCodex()) {
6156
6387
  process.exitCode = 1;
@@ -6165,7 +6396,7 @@ async function runBotInit(name) {
6165
6396
  console.log(" 1) \u4E8B\u4EF6\u4E0E\u56DE\u8C03 \u2192 \u957F\u8FDE\u63A5 \u2192 \u8BA2\u9605\uFF1Aim.message.receive_v1 / card.action.trigger / application.bot.menu_v6");
6166
6397
  console.log(" \uFF08\u53EF\u9009\uFF09\u300C\u52A0\u8FDB\u5DF2\u6709\u7FA4\u300D\u529F\u80FD\u518D\u8BA2\u9605\uFF1Aim.chat.member.bot.added_v1 / im.chat.member.bot.deleted_v1");
6167
6398
  console.log(" 2) \u521B\u5EFA\u5E76\u53D1\u5E03\u5E94\u7528\u7248\u672C");
6168
- console.log("\n`bot list` \u67E5\u770B\u5168\u90E8\uFF1B`bot use <\u540D>` \u5207\u6362\u5F53\u524D\uFF1B`run` \u524D\u53F0\u8DD1 / `start` \u540E\u53F0\u5E38\u9A7B\u3002\n");
6399
+ console.log("\n`bot list` \u67E5\u770B\u5168\u90E8\uFF1B`bot use` \u52FE\u9009\u8981\u540C\u65F6\u8FDE\u63A5\u7684\u673A\u5668\u4EBA\uFF1B`run` \u524D\u53F0\u8DD1 / `start` \u540E\u53F0\u5E38\u9A7B\u3002\n");
6169
6400
  }
6170
6401
  async function runBotList() {
6171
6402
  const reg = await loadBots();
@@ -6173,27 +6404,65 @@ async function runBotList() {
6173
6404
  console.log("\uFF08\u8FD8\u6CA1\u6709\u6CE8\u518C\u4EFB\u4F55\u98DE\u4E66\u673A\u5668\u4EBA\u3002\u8FD0\u884C `feishu-codex-bridge bot init` \u521B\u5EFA\u3002\uFF09");
6174
6405
  return;
6175
6406
  }
6407
+ const active = new Set(activeBots(reg).map((b) => b.appId));
6176
6408
  console.log("\n\u5DF2\u6CE8\u518C\u7684\u98DE\u4E66\u673A\u5668\u4EBA\uFF1A\n");
6177
6409
  for (const b of reg.bots) {
6178
- const cur = b.appId === reg.current ? "\u{1F449}" : " ";
6179
- console.log(`${cur} ${b.name.padEnd(16)} ${b.appId} [${b.tenant}]${b.botName ? ` ${b.botName}` : ""}`);
6410
+ const mark = active.has(b.appId) ? "\u2705" : "\u2B1C";
6411
+ console.log(`${mark} ${b.name.padEnd(16)} ${b.appId} [${b.tenant}]${b.botName ? ` ${b.botName}` : ""}`);
6180
6412
  }
6181
- console.log("\n\u{1F449} = \u5F53\u524D\u9009\u4E2D\uFF08run / start \u542F\u52A8\u7684\u5C31\u662F\u5B83\uFF09\u3002`bot use <\u540D>` \u5207\u6362\u3002\n");
6413
+ console.log("\n\x1B[2m`bot use` \u52FE\u9009\u8981\u540C\u65F6\u8FDE\u63A5\u7684\u673A\u5668\u4EBA\uFF0C\u6216 `bot use <\u540D> [\u540D\u2026]` \u76F4\u63A5\u6307\u5B9A\u3002\x1B[0m\n");
6182
6414
  }
6183
- async function runBotUse(name) {
6415
+ async function runBotUse(names) {
6184
6416
  const reg = await loadBots();
6185
- const bot2 = findBot(reg, name);
6186
- if (!bot2) {
6187
- console.error(`\u2717 \u627E\u4E0D\u5230\u673A\u5668\u4EBA\u300C${name}\u300D\u3002\u5DF2\u6CE8\u518C\uFF1A${botNames(reg.bots)}`);
6417
+ if (reg.bots.length === 0) {
6418
+ console.error("\u2717 \u8FD8\u6CA1\u6709\u6CE8\u518C\u4EFB\u4F55\u98DE\u4E66\u673A\u5668\u4EBA\u3002\u5148 `feishu-codex-bridge bot init` \u521B\u5EFA\u3002");
6188
6419
  process.exitCode = 1;
6189
6420
  return;
6190
6421
  }
6191
- if (reg.current === bot2.appId) {
6192
- console.log(`\u300C${bot2.name}\u300D\u5DF2\u7ECF\u662F\u5F53\u524D\u673A\u5668\u4EBA\u3002`);
6422
+ let appIds;
6423
+ if (names.length > 0) {
6424
+ const resolved = [];
6425
+ const unknown = [];
6426
+ for (const n of names) {
6427
+ const b = findBot(reg, n);
6428
+ if (!b) unknown.push(n);
6429
+ else if (!resolved.includes(b.appId)) resolved.push(b.appId);
6430
+ }
6431
+ if (unknown.length) {
6432
+ console.error(`\u2717 \u627E\u4E0D\u5230\u673A\u5668\u4EBA\uFF1A${unknown.join(", ")}\u3002\u5DF2\u6CE8\u518C\uFF1A${botNames(reg.bots)}`);
6433
+ process.exitCode = 1;
6434
+ return;
6435
+ }
6436
+ appIds = resolved;
6437
+ } else {
6438
+ if (!process.stdin.isTTY) {
6439
+ const cur = activeBots(reg).map((b) => b.name).join(", ") || "\uFF08\u7A7A\uFF09";
6440
+ console.log(`\u5F53\u524D\u6D3B\u8DC3\u673A\u5668\u4EBA\uFF1A${cur}`);
6441
+ console.log("\uFF08\u975E\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\uFF0C\u65E0\u6CD5\u5F39\u52FE\u9009\u6846\u3002\u7528 `bot use <\u540D> [\u540D\u2026]` \u76F4\u63A5\u6307\u5B9A\u8981\u540C\u65F6\u8FDE\u63A5\u7684\u673A\u5668\u4EBA\u3002\uFF09");
6442
+ return;
6443
+ }
6444
+ const activeSet = new Set(activeBots(reg).map((b) => b.appId));
6445
+ const items = reg.bots.map((b) => ({
6446
+ label: b.name,
6447
+ hint: `${b.appId} [${b.tenant}]${b.botName ? ` ${b.botName}` : ""}`,
6448
+ checked: activeSet.has(b.appId)
6449
+ }));
6450
+ const picked = await checkboxSelect("\u9009\u62E9\u8981\u540C\u65F6\u8FDE\u63A5\u7684\u673A\u5668\u4EBA\uFF08\u7A7A\u683C\u52FE\u9009\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09\uFF1A", items);
6451
+ if (picked === null) {
6452
+ console.log("\u5DF2\u53D6\u6D88\uFF0C\u672A\u6539\u52A8\u3002");
6453
+ return;
6454
+ }
6455
+ appIds = picked.map((i) => reg.bots[i]?.appId).filter((id) => Boolean(id));
6456
+ }
6457
+ await setActiveBots(appIds);
6458
+ const chosen = appIds.map((id) => reg.bots.find((b) => b.appId === id)?.name ?? id);
6459
+ if (chosen.length === 0) {
6460
+ console.log("\u2713 \u5DF2\u6E05\u7A7A\u6D3B\u8DC3\u673A\u5668\u4EBA\u2014\u2014`run` / `start` \u6682\u4E0D\u8FDE\u63A5\u4EFB\u4F55 bot\u3002`bot use` \u91CD\u65B0\u52FE\u9009\u3002");
6193
6461
  return;
6194
6462
  }
6195
- await setCurrent(bot2.appId);
6196
- console.log(`\u2713 \u5F53\u524D\u673A\u5668\u4EBA \u2192 \u300C${bot2.name}\u300D(${bot2.appId})\u3002\u524D\u53F0 \`run\` \u76F4\u63A5\u751F\u6548\uFF1B\u540E\u53F0\u8BF7 \`restart\`\u3002`);
6463
+ console.log(
6464
+ `\u2713 \u6D3B\u8DC3\u673A\u5668\u4EBA\uFF08${chosen.length} \u4E2A\uFF09\u2192 ${chosen.join(", ")}\u3002\u524D\u53F0\u91CD\u8DD1 \`run\` \u751F\u6548${chosen.length > 1 ? "\uFF08\u591A\u8FDB\u7A0B\u6258\u7BA1\uFF09" : ""}\uFF1B\u540E\u53F0\u8BF7 \`restart\`\u3002`
6465
+ );
6197
6466
  }
6198
6467
  async function runBotRm(name) {
6199
6468
  const reg = await loadBots();
@@ -6278,8 +6547,8 @@ function readStdin() {
6278
6547
  // src/cli/index.ts
6279
6548
  var program = new Command();
6280
6549
  program.name("feishu-codex-bridge").description("\u628A\u98DE\u4E66/Lark \u6865\u63A5\u5230\u672C\u673A Codex\uFF08\u9879\u76EE=\u7FA4, \u8BDD\u9898=\u4F1A\u8BDD\uFF09").version(bridgeVersion());
6281
- program.command("run").description("\u524D\u53F0\u542F\u52A8 bot\uFF08\u6CA1\u914D\u7F6E\u5219\u5148\u626B\u7801 init\uFF1BCtrl+C \u4F18\u96C5\u9000\u51FA\uFF09").action(async () => {
6282
- await runRun();
6550
+ program.command("run").description("\u524D\u53F0\u542F\u52A8\u6D3B\u8DC3\u673A\u5668\u4EBA\uFF08\u591A\u4E2A\u5219\u5404\u81EA\u72EC\u7ACB\u8FDB\u7A0B\uFF1B\u6CA1\u914D\u7F6E\u5219\u5148\u626B\u7801 init\uFF1BCtrl+C \u4F18\u96C5\u9000\u51FA\uFF09").option("--bot <name>", "\u53EA\u542F\u52A8\u6307\u5B9A\u7684\u4E00\u4E2A\u673A\u5668\u4EBA\uFF08\u540D\u5B57\u6216 appId\uFF09").action(async (options) => {
6551
+ await runRun(options.bot);
6283
6552
  });
6284
6553
  program.command("start").description("\u540E\u53F0 daemon \u542F\u52A8\uFF08\u88C5 launchd \u5F00\u673A\u81EA\u542F\uFF1B\u6CA1\u914D\u7F6E\u5219\u5148\u626B\u7801 init\uFF09").action(async () => {
6285
6554
  await runStart();
@@ -6306,8 +6575,8 @@ bot.command("init [name]").description("\u6CE8\u518C\u4E00\u4E2A\u98DE\u4E66\u67
6306
6575
  bot.command("list").description("\u5217\u51FA\u5DF2\u6CE8\u518C\u7684\u98DE\u4E66\u673A\u5668\u4EBA").action(async () => {
6307
6576
  await runBotList();
6308
6577
  });
6309
- bot.command("use <name>").description("\u9009\u62E9 run / start \u542F\u52A8\u65F6\u4F7F\u7528\u7684\u673A\u5668\u4EBA").action(async (name) => {
6310
- await runBotUse(name);
6578
+ bot.command("use [names...]").description("\u52FE\u9009/\u6307\u5B9A\u8981\u540C\u65F6\u8FDE\u63A5\u7684\u673A\u5668\u4EBA\uFF08\u591A\u9009\uFF09\uFF1B\u65E0\u53C2\u6570\u5F39\u4EA4\u4E92\u5F0F\u52FE\u9009\u6846").action(async (names) => {
6579
+ await runBotUse(names ?? []);
6311
6580
  });
6312
6581
  bot.command("rm <name>").description("\u79FB\u9664\u4E00\u4E2A\u673A\u5668\u4EBA\u914D\u7F6E").action(async (name) => {
6313
6582
  await runBotRm(name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelzen/feishu-codex-bridge",
3
- "version": "0.3.0",
3
+ "version": "0.3.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": {