@modelzen/feishu-codex-bridge 0.3.12-test.0 → 0.3.12-test.2

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/cli.js CHANGED
@@ -91,7 +91,10 @@ var paths = {
91
91
  inboundDir: join(appDir, "inbound"),
92
92
  /** daemon 内嵌 Web 控制台的发现文件 {port, token, pid}(0600,daemon 退出
93
93
  * 清理)——`web` 子命令据此直接打开 daemon 控制台而不是再起只读副本。 */
94
- webConsoleFile: join(appDir, "web-console.json")
94
+ webConsoleFile: join(appDir, "web-console.json"),
95
+ /** 稳定的 Web 控制台 token(0600,**不随进程退出清理**)——让重启 / 预览→daemon
96
+ * 切换后浏览器里那条带 token 的 URL 始终有效,不再 401。删此文件即轮换 token。 */
97
+ webTokenFile: join(appDir, "web-token")
95
98
  };
96
99
 
97
100
  // src/config/bots.ts
@@ -11886,6 +11889,7 @@ function createAdminService(deps = {}) {
11886
11889
  return readRecentLogs({ maxBytes: opts?.maxBytes ?? 64 * 1024 });
11887
11890
  },
11888
11891
  registerBot(input2) {
11892
+ if (deps.readonlyPreview) throw new NotWiredYetError("\u2795 \u6DFB\u52A0\u673A\u5668\u4EBA");
11889
11893
  return registerBotFromCredentials({
11890
11894
  appId: input2.appId,
11891
11895
  appSecret: input2.appSecret,
@@ -11954,6 +11958,7 @@ function createAdminService(deps = {}) {
11954
11958
  };
11955
11959
  },
11956
11960
  async registerBotByQr(opts) {
11961
+ if (deps.readonlyPreview) throw new NotWiredYetError("\u2795 \u6DFB\u52A0\u673A\u5668\u4EBA");
11957
11962
  let creds;
11958
11963
  try {
11959
11964
  creds = await startRegistration({ signal: opts.signal, onQr: opts.onQr, onStatus: opts.onStatus });
@@ -12167,7 +12172,7 @@ async function catalogEntryStatus(entry, defaultBackend) {
12167
12172
  }
12168
12173
  async function probeAllBackends() {
12169
12174
  return Promise.all(
12170
- backendIds().map(async (id) => {
12175
+ visibleCatalog().map((e) => e.id).map(async (id) => {
12171
12176
  const backend = createBackend(id);
12172
12177
  const probe = await backend.doctor({ force: true }).catch(() => void 0);
12173
12178
  return {
@@ -12183,7 +12188,7 @@ async function probeAllBackends() {
12183
12188
  );
12184
12189
  }
12185
12190
  function createReadonlyAdminService(deps = {}) {
12186
- return createAdminService({ startDaemon: deps.startDaemon });
12191
+ return createAdminService({ startDaemon: deps.startDaemon, applyUpdate: deps.applyUpdate, readonlyPreview: true });
12187
12192
  }
12188
12193
  function isAlive(pid) {
12189
12194
  try {
@@ -12195,6 +12200,7 @@ function isAlive(pid) {
12195
12200
  }
12196
12201
 
12197
12202
  // src/web/discovery.ts
12203
+ import { randomUUID as randomUUID6 } from "crypto";
12198
12204
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
12199
12205
  import { dirname as dirname12 } from "path";
12200
12206
  function publishWebConsole(rec, file = paths.webConsoleFile) {
@@ -12225,6 +12231,25 @@ function clearWebConsole(file = paths.webConsoleFile) {
12225
12231
  } catch {
12226
12232
  }
12227
12233
  }
12234
+ function stableWebConsoleToken(file = paths.webTokenFile) {
12235
+ try {
12236
+ const t = readFileSync5(file, "utf8").trim();
12237
+ if (t) return t;
12238
+ } catch {
12239
+ }
12240
+ const token = randomUUID6();
12241
+ try {
12242
+ mkdirSync2(dirname12(file), { recursive: true });
12243
+ try {
12244
+ unlinkSync(file);
12245
+ } catch {
12246
+ }
12247
+ writeFileSync2(file, `${token}
12248
+ `, { mode: 384 });
12249
+ } catch {
12250
+ }
12251
+ return token;
12252
+ }
12228
12253
  function isAlive2(pid) {
12229
12254
  try {
12230
12255
  process.kill(pid, 0);
@@ -12347,7 +12372,7 @@ async function doRestart(phase) {
12347
12372
 
12348
12373
  // src/web/server.ts
12349
12374
  import { createServer } from "http";
12350
- import { randomUUID as randomUUID6, timingSafeEqual } from "crypto";
12375
+ import { randomUUID as randomUUID7, timingSafeEqual } from "crypto";
12351
12376
  import { mkdirSync as mkdirSync3, watch } from "fs";
12352
12377
  import { open as open2, stat as stat5 } from "fs/promises";
12353
12378
  import { join as join19 } from "path";
@@ -13377,7 +13402,7 @@ ${UI_PURE_JS}
13377
13402
  bots.forEach(function (b) {
13378
13403
  nav.appendChild(navItem({ icon: 'bot', label: botTitle(b), on: r.tab === 'bot' && r.botId === b.appId, dot: !!b.running, onClick: function () { go({ tab: 'bot', botId: b.appId }); } }));
13379
13404
  });
13380
- nav.appendChild(navItem({ icon: 'add', label: '\u6DFB\u52A0\u673A\u5668\u4EBA', add: true, onClick: function () { openWizard(); } }));
13405
+ nav.appendChild(navItem({ icon: 'add', label: '\u6DFB\u52A0\u673A\u5668\u4EBA', add: true, onClick: function () { tryAddBot(); } }));
13381
13406
  nav.appendChild(el('div', 'nav-sec', '\u914D\u7F6E'));
13382
13407
  nav.appendChild(navItem({ icon: 'backend', label: '\u540E\u7AEF Agent', on: r.tab === 'backends', onClick: function () { go({ tab: 'backends' }); } }));
13383
13408
  nav.appendChild(navItem({ icon: 'doctor', label: '\u5BBF\u4E3B\u673A\u4F53\u68C0', on: r.tab === 'doctor', onClick: function () { go({ tab: 'doctor' }); } }));
@@ -13405,7 +13430,7 @@ ${UI_PURE_JS}
13405
13430
  act.textContent = '';
13406
13431
  if (r.tab === 'overview' || r.tab === 'bot') {
13407
13432
  var add = el('button', 'btn primary sm', '\u2795 \u6DFB\u52A0\u673A\u5668\u4EBA');
13408
- add.onclick = function () { openWizard(); };
13433
+ add.onclick = function () { tryAddBot(); };
13409
13434
  act.appendChild(add);
13410
13435
  }
13411
13436
  }
@@ -13479,7 +13504,7 @@ ${UI_PURE_JS}
13479
13504
  hero.appendChild(el('div', 'home-sub', '\u628A\u98DE\u4E66\u63A5\u5230\u672C\u673A\u7684 Codex / Claude \u2014\u2014 \u4E00\u4E2A\u7FA4\u4E00\u4E2A\u9879\u76EE\uFF0C@\u4E00\u53E5\u8BDD\u5C31\u5F00\u5E72\u3002\u591A\u673A\u5668\u4EBA\u3001\u591A\u540E\u7AEF\uFF0C\u5168\u7A0B\u8DD1\u5728\u4F60\u8FD9\u53F0\u673A\u5668\u4E0A\uFF0C\u79C1\u6709\u53EF\u63A7\u3002'));
13480
13505
  var ctaRow = el('div', 'home-cta-row');
13481
13506
  var primary = el('button', 'btn primary home-cta', hasBots ? '\u8FDB\u5165\u63A7\u5236\u53F0' : '\u2795 \u626B\u7801\u521B\u5EFA\u7B2C\u4E00\u4E2A\u673A\u5668\u4EBA');
13482
- primary.onclick = function () { if (hasBots) go({ tab: 'overview' }); else openWizard(); };
13507
+ primary.onclick = function () { if (hasBots) go({ tab: 'overview' }); else tryAddBot(); };
13483
13508
  ctaRow.appendChild(primary);
13484
13509
  if (hasBots) {
13485
13510
  var sec = el('button', 'btn home-cta', '\u540E\u7AEF\u7BA1\u7406');
@@ -13930,6 +13955,10 @@ ${UI_PURE_JS}
13930
13955
  } else {
13931
13956
  var pb = el('button', 'btn sm primary', '\u25B6\uFE0F \u542F\u52A8'); pb.onclick = askStart; acts.appendChild(pb);
13932
13957
  }
13958
+ // \u68C0\u67E5\u66F4\u65B0\u5165\u53E3\uFF1A\u59CB\u7EC8\u5728\uFF08\u4E0D\u7BA1\u8DD1\u6CA1\u8DD1\uFF09\u3002\u6CA1\u542F\u52A8\u65F6\u80FD\u505A\u7684\u5C31\u662F\u300C\u542F\u52A8\u300D+\u300C\u66F4\u65B0\u300D\u3002
13959
+ var ub = el('button', 'btn sm', '\u{1F504} \u68C0\u67E5\u66F4\u65B0'); ub.style.marginLeft = '6px';
13960
+ ub.onclick = function () { var bx = $('updateBody'); if (bx) { bx.textContent = '\u7248\u672C\u68C0\u67E5\u4E2D\u2026'; loadUpdate(bx); } toast('\u{1F504} \u6B63\u5728\u68C0\u67E5\u66F4\u65B0\u2026'); };
13961
+ acts.appendChild(ub);
13933
13962
  }
13934
13963
  var line = el('div', 'statline');
13935
13964
  if (d.running) line.appendChild(el('span', 'tag green', '\u2705 \u8FD0\u884C\u4E2D' + (d.pid ? ' \xB7 pid ' + d.pid : '')));
@@ -14043,7 +14072,7 @@ ${UI_PURE_JS}
14043
14072
  hero.appendChild(el('div', 'fr-title', '\u6B22\u8FCE\uFF01\u8FD8\u5DEE\u6700\u540E\u4E00\u6B65\uFF1A\u521B\u5EFA\u4F60\u7684\u7B2C\u4E00\u4E2A\u98DE\u4E66\u673A\u5668\u4EBA'));
14044
14073
  hero.appendChild(el('div', 'fr-sub', '\u70B9\u4E0B\u9762\u7684\u6309\u94AE\uFF0C\u7528\u98DE\u4E66\u626B\u7801\u5373\u53EF\u521B\u5EFA\u5E76\u6388\u6743\u2014\u2014\u5168\u7A0B\u5728\u8FD9\u4E2A\u7F51\u9875\u91CC\u5B8C\u6210\uFF0C\u4E0D\u7528\u78B0\u7EC8\u7AEF\u3002'));
14045
14074
  var cta = el('button', 'btn primary fr-cta', '\u2795 \u626B\u7801\u521B\u5EFA\u7B2C\u4E00\u4E2A\u673A\u5668\u4EBA');
14046
- cta.onclick = function () { openWizard(); };
14075
+ cta.onclick = function () { tryAddBot(); };
14047
14076
  hero.appendChild(cta);
14048
14077
  box.appendChild(hero);
14049
14078
  if (countEl) countEl.textContent = '';
@@ -14368,7 +14397,7 @@ ${UI_PURE_JS}
14368
14397
  box.textContent = '';
14369
14398
  box.className = 'note';
14370
14399
  if (!diag) return;
14371
- box.appendChild(el('div', null, '\u4E8B\u4EF6\u8BA2\u9605\uFF1A' + eventDiagText(diag.event)));
14400
+ box.appendChild(el('div', null, '\u2139\uFE0F \u4E8B\u4EF6\u8BA2\u9605\uFF1A\u8BF7\u81EA\u884C\u5230\u300C\u4E8B\u4EF6\u4E0E\u56DE\u8C03\u300D\u786E\u8BA4\u5DF2\u8BA2\u9605 im.message.receive_v1\uFF08\u957F\u8FDE\u63A5\uFF09\uFF1B\u6B64\u9879\u7CFB\u7EDF\u65E0\u6CD5\u53EF\u9760\u68C0\u6D4B\uFF0C\u4EC5\u4F5C\u63D0\u9192\u3002'));
14372
14401
  var title = el('div', null, '\u{1F9E0} \u540E\u7AEF\u73AF\u5883\uFF1A');
14373
14402
  title.style.marginTop = '6px';
14374
14403
  box.appendChild(title);
@@ -14553,9 +14582,27 @@ ${UI_PURE_JS}
14553
14582
  var wizEs = null; // \u626B\u7801 SSE EventSource
14554
14583
  var wizQrSessionId = null;
14555
14584
  var wizCountdown = null; // \u4E8C\u7EF4\u7801\u8FC7\u671F\u5012\u8BA1\u65F6
14585
+ var wizAutoEnabled = false; // \u65B0 bot \u662F\u5426\u5DF2\u81EA\u52A8\u52A0\u5165\u6D3B\u8DC3\u96C6\uFF08\u4E00\u6B21\u6027\uFF09
14586
+ var wizRestartPrompted = false; // \u5B8C\u6210\u6B65\u662F\u5426\u5DF2\u5F39\u8FC7\u300C\u91CD\u542F\u62C9\u8D77\u300D\u786E\u8BA4\uFF08\u4E00\u6B21\u6027\uFF09
14587
+
14588
+ // \u6DFB\u52A0\u673A\u5668\u4EBA\u662F\u5199\u64CD\u4F5C\uFF1AFeishu Bridge \u6CA1\u5728\u8DD1\u65F6\u63A7\u5236\u53F0\u4E3A\u53EA\u8BFB\uFF0C\u4E0D\u80FD\u52A0\uFF08\u4E0E\u300C\u6CA1\u542F\u52A8\u53EA\u8BFB\u300D\u4E00\u81F4\uFF09\u3002
14589
+ // \u6CA1\u5728\u8DD1\u5C31\u5F15\u5BFC\u5148\u542F\u52A8\uFF0C\u800C\u4E0D\u662F\u6253\u5F00\u5411\u5BFC\u8BA9\u7528\u6237\u767D\u586B\u4E00\u901A\u518D\u88AB 501 \u6321\u56DE\u3002
14590
+ function tryAddBot() {
14591
+ if (daemon && daemon.running) { openWizard(); return; }
14592
+ confirmDialog({
14593
+ title: '\u9700\u8981\u5148\u542F\u52A8 Feishu Bridge',
14594
+ lines: [
14595
+ 'Feishu Bridge \u672A\u5728\u8FD0\u884C\u65F6\u63A7\u5236\u53F0\u4E3A\u53EA\u8BFB\uFF0C\u65E0\u6CD5\u6DFB\u52A0\u673A\u5668\u4EBA\u3002',
14596
+ '\u5148\u542F\u52A8 Feishu Bridge\uFF0C\u518D\u56DE\u6765\u6DFB\u52A0\u3002',
14597
+ ],
14598
+ confirmLabel: '\u7ACB\u5373\u542F\u52A8',
14599
+ onConfirm: function () { postAction('/api/daemon/start', '\u542F\u52A8'); },
14600
+ });
14601
+ }
14556
14602
 
14557
14603
  function openWizard() {
14558
14604
  wizStep = 1; wizBotId = null; wizSetup = null; wizManualOpen = false;
14605
+ wizAutoEnabled = false; wizRestartPrompted = false;
14559
14606
  stopWizPoll(); stopWizQr();
14560
14607
  $('wizMask').classList.add('open');
14561
14608
  renderWizard();
@@ -14605,6 +14652,11 @@ ${UI_PURE_JS}
14605
14652
  qrWrap.appendChild(el('div', 'note', '\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026'));
14606
14653
  w.appendChild(qrWrap);
14607
14654
 
14655
+ var steps = el('div', 'note');
14656
+ steps.style.cssText = 'margin-top:6px;line-height:1.7';
14657
+ steps.textContent = '\u2460 \u7528\u98DE\u4E66 App \u626B\u7801 \u2192 \u2461 \u5728\u98DE\u4E66\u91CC\u70B9\u300C\u521B\u5EFA\u5E76\u6388\u6743\u300D \u2192 \u2462 \u56DE\u5230\u672C\u9875\uFF08\u65E0\u9700\u624B\u52A8\u5237\u65B0\uFF09\uFF0C\u521B\u5EFA\u6210\u529F\u4F1A\u81EA\u52A8\u8FDB\u5165\u7B2C\u2461\u6B65\u300C\u63A5\u5165\u68C0\u6D4B\u300D\u3002';
14658
+ w.appendChild(steps);
14659
+
14608
14660
  var statusLine = el('div', 'note'); statusLine.id = 'wizScanStatus';
14609
14661
  statusLine.style.textAlign = 'center';
14610
14662
  w.appendChild(statusLine);
@@ -14773,12 +14825,21 @@ ${UI_PURE_JS}
14773
14825
  function startWizPoll() { stopWizPoll(); pollWizSetup(); wizPoll = setInterval(pollWizSetup, 5000); }
14774
14826
  function pollWizSetup() {
14775
14827
  if (!wizBotId) return;
14828
+ // \u65B0 bot \u9ED8\u8BA4\u5C31\u52A0\u5165\u6D3B\u8DC3\u96C6\u2014\u2014\u7528\u6237\u4E0D\u5FC5\u624B\u52A8\u70B9\u6309\u94AE / \u8DD1 bot use\u3002\u4E00\u6B21\u6027\uFF1B\u5931\u8D25\u4E0B\u5468\u671F\u91CD\u8BD5\u3002
14829
+ // \u771F\u6B63\u300C\u62C9\u8D77\u4E0A\u7EBF\u300D\u8981\u91CD\u542F Feishu Bridge\uFF0C\u90A3\u4E00\u6B65\u7559\u5230\u300C\u5B8C\u6210\u300D\u65F6\u5F39\u7A97\u786E\u8BA4\uFF08\u91CD\u542F\u4F1A\u77ED\u6682
14830
+ // \u5F71\u54CD\u5176\u5B83\u5728\u7EBF bot\uFF0C\u6240\u4EE5\u4E0D\u9759\u9ED8\u91CD\u542F\uFF09\u3002
14831
+ if (!wizAutoEnabled) {
14832
+ wizAutoEnabled = true;
14833
+ fetch('/api/bots/' + encodeURIComponent(wizBotId), {
14834
+ method: 'PATCH', headers: { 'Content-Type': 'application/json' },
14835
+ body: JSON.stringify({ enabled: true }),
14836
+ }).catch(function () { wizAutoEnabled = false; });
14837
+ }
14776
14838
  fetch('/api/bots/' + encodeURIComponent(wizBotId) + '/setup-status')
14777
14839
  .then(function (r) { return r.json(); })
14778
14840
  .then(function (s) {
14779
14841
  wizSetup = s;
14780
14842
  if (wizStep === 2) renderWizChecklist();
14781
- if (s.event && s.event.state === 'ok') stopWizPoll();
14782
14843
  })
14783
14844
  .catch(function () { /* \u4E0B\u4E2A\u5468\u671F\u91CD\u8BD5 */ });
14784
14845
  }
@@ -14805,7 +14866,7 @@ ${UI_PURE_JS}
14805
14866
  var s = wizSetup;
14806
14867
  if (!s || !s.credentials) {
14807
14868
  w.appendChild(checkItem('spin', '\u6B63\u5728\u68C0\u6D4B\u2026', '\u9996\u6B21\u62C9\u53D6\u7EA6 2~3 \u79D2'));
14808
- w.appendChild(wizChecklistActions(false));
14869
+ w.appendChild(wizChecklistActions());
14809
14870
  return;
14810
14871
  }
14811
14872
  w.appendChild(checkItem(
@@ -14827,89 +14888,31 @@ ${UI_PURE_JS}
14827
14888
  w.appendChild(checkItem('\u26A0\uFE0F', '\u7F3A ' + miss.length + ' \u9879\u5FC5\u9700\u6743\u9650', miss.join('\u3001'), grant));
14828
14889
  }
14829
14890
  }
14830
- var conn = s.connection || {};
14831
- if (conn.running && conn.connection === 'connected') {
14832
- w.appendChild(checkItem('\u2705', '\u957F\u8FDE\u63A5\u5728\u7EBF', 'bridge \u5DF2\u8FDE\u4E0A\u98DE\u4E66\uFF0C\u53EF\u5B9E\u65F6\u6536\u53D1'));
14833
- } else if (conn.running) {
14834
- w.appendChild(checkItem('spin', 'bridge \u8FD0\u884C\u4E2D', '\u957F\u8FDE\u63A5' + (conn.connection ? '\uFF08' + conn.connection + '\uFF09' : '\u5EFA\u7ACB\u4E2D\u2026')));
14835
- } else {
14836
- var cmd = el('div');
14837
- var act = el('div', 'actions');
14838
- var go1 = el('button', 'btn primary', '\u26A1 \u4E00\u952E\u542F\u7528\u5E76\u62C9\u8D77');
14839
- go1.onclick = function () { wizEnableAndLaunch(); };
14840
- act.appendChild(go1);
14841
- cmd.appendChild(act);
14842
- cmd.appendChild(el('div', 'note', '\u4E00\u952E\uFF1A\u628A\u5B83\u52A0\u5165\u6D3B\u8DC3\u96C6\u5E76\u81EA\u52A8'
14843
- + ((daemon && daemon.running) ? '\u91CD\u542F' : '\u542F\u52A8') + ' Feishu Bridge'
14844
- + ((daemon && daemon.running) ? '\uFF08\u7EA6\u6570\u79D2\uFF0C\u5176\u5B83\u5728\u7EBF\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u65AD\u8FDE\u540E\u81EA\u52A8\u91CD\u8FDE\uFF09' : '') + '\u3002'));
14845
- cmd.appendChild(el('div', 'note', '\u6216\u624B\u52A8\u5728\u7EC8\u7AEF\u6267\u884C\uFF1A'));
14846
- cmd.appendChild(copyRow('feishu-codex-bridge bot use ' + (wizBotId || '<appId>')));
14847
- cmd.appendChild(copyRow('feishu-codex-bridge start'));
14848
- w.appendChild(checkItem('\u{1F7E0}', '\u957F\u8FDE\u63A5\u672A\u5EFA\u7ACB', '\u9700\u8981 Feishu Bridge \u62C9\u8D77\u8FD9\u4E2A bot', cmd));
14849
- }
14850
- var ev = s.event || { state: 'unchecked' };
14851
- if (ev.state === 'ok') {
14852
- w.appendChild(checkItem('\u2705', '\u4E8B\u4EF6\u8BA2\u9605\u5DF2\u751F\u6548', eventDiagText(ev)));
14853
- } else {
14854
- var evExtra = el('div');
14855
- evExtra.appendChild(el('div', 'note', ev.state === 'unchecked'
14856
- ? '\uFF08\u7F3A application:app_version \u53EA\u8BFB scope \u6216\u7F51\u7EDC\u4E0D\u901A\u65F6\u65E0\u6CD5\u81EA\u52A8\u68C0\u6D4B\uFF1B\u6309\u4E0B\u65B9\u6DF1\u94FE\u624B\u52A8\u6838\u5BF9\u300C\u4E8B\u4EF6\u914D\u7F6E\u300D\u3002\uFF09'
14857
- : '\u53BB\u5F00\u53D1\u8005\u540E\u53F0\u300C\u4E8B\u4EF6\u4E0E\u56DE\u8C03\u300D\uFF1A\u4E8B\u4EF6\u914D\u7F6E\u6539\u300C\u957F\u8FDE\u63A5\u300D\u2192 \u6DFB\u52A0 im.message.receive_v1 \u2192 \u5E94\u7528\u53D1\u5E03\u91CC\u521B\u5EFA\u5E76\u53D1\u5E03\u7248\u672C\u3002'));
14858
- var ea = el('a', null, '\u6253\u5F00\u300C\u4E8B\u4EF6\u4E0E\u56DE\u8C03\u300D\u914D\u7F6E\u9875 \u2197');
14859
- ea.href = s.eventConfigUrl; ea.target = '_blank'; ea.rel = 'noopener';
14860
- evExtra.appendChild(ea);
14861
- evExtra.appendChild(el('div', 'note', '\u914D\u7F6E\u597D\u540E\u65E0\u9700\u624B\u52A8\u5237\u65B0\u2014\u2014\u672C\u9875\u6BCF 5 \u79D2\u81EA\u52A8\u590D\u68C0\uFF0C\u751F\u6548\u4F1A\u53D8 \u2705\u3002'));
14862
- w.appendChild(checkItem(ev.state === 'unchecked' ? '\u26A0\uFE0F' : 'spin',
14863
- ev.state === 'unchecked' ? '\u4E8B\u4EF6\u8BA2\u9605\u672A\u80FD\u81EA\u52A8\u68C0\u6D4B' : '\u4E8B\u4EF6\u8BA2\u9605\u5C1A\u672A\u751F\u6548',
14864
- eventDiagText(ev), evExtra));
14865
- }
14866
- w.appendChild(wizChecklistActions(ev.state === 'ok'));
14891
+ // \u300C\u52A0\u5165\u6D3B\u8DC3\u96C6 + \u62C9\u8D77\u4E0A\u7EBF\u300D\u5168\u7A0B\u9759\u9ED8\uFF1A\u65B0 bot \u5DF2\u5728 pollWizSetup \u81EA\u52A8\u52A0\u5165\u6D3B\u8DC3\u96C6\uFF0C\u771F\u6B63\u62C9\u8D77
14892
+ // \u9760\u6700\u540E\u4E00\u6B65\u300C\u5B8C\u6210\u300D\u5F39\u7A97\u786E\u8BA4\u91CD\u542F\u2014\u2014\u8FD9\u91CC\u4E0D\u518D\u663E\u793A\u300C\u5F85\u62C9\u8D77\u4E0A\u7EBF\u300D\u4E4B\u7C7B\u7684\u4E2D\u95F4\u6001\u9879\u3002
14893
+ // \u4E8B\u4EF6\u8BA2\u9605\uFF1A\u7CFB\u7EDF\u65E0\u6CD5\u53EF\u9760\u68C0\u6D4B\uFF08\u957F\u8FDE\u63A5\u8BA2\u9605\u5728\u5DF2\u53D1\u5E03\u7248\u672C\u91CC\u7684\u4F53\u73B0\u4E0D\u7A33\u5B9A\uFF09\uFF0C\u6545\u53EA\u505A\u63D0\u9192\u3001\u4E0D\u4E0B\u7ED3\u8BBA\u3001
14894
+ // \u4E0D\u963B\u585E\u300C\u4E0B\u4E00\u6B65\u300D\u3002\u7528\u6237\u81EA\u884C\u53BB\u540E\u53F0\u6838\u5BF9\u3002
14895
+ var evHint = el('div');
14896
+ var ea = el('a', null, '\u6253\u5F00\u300C\u4E8B\u4EF6\u4E0E\u56DE\u8C03\u300D\u914D\u7F6E\u9875 \u2197');
14897
+ ea.href = (s.eventConfigUrl || '#'); ea.target = '_blank'; ea.rel = 'noopener';
14898
+ evHint.appendChild(ea);
14899
+ w.appendChild(checkItem('\u2139\uFE0F', '\u4E8B\u4EF6\u8BA2\u9605\uFF08\u8BF7\u81EA\u884C\u786E\u8BA4\uFF09',
14900
+ '\u8BF7\u5230\u300C\u4E8B\u4EF6\u4E0E\u56DE\u8C03\u300D\u786E\u8BA4\u5DF2\u8BA2\u9605 im.message.receive_v1\uFF08\u957F\u8FDE\u63A5\u6A21\u5F0F\uFF09\u5E76\u5DF2\u53D1\u5E03\u7248\u672C\uFF0C\u5426\u5219 @\u673A\u5668\u4EBA\u4E0D\u4F1A\u6709\u53CD\u5E94\u3002\u6B64\u9879\u7CFB\u7EDF\u65E0\u6CD5\u53EF\u9760\u68C0\u6D4B\uFF0C\u4EC5\u4F5C\u63D0\u9192\u3002', evHint));
14901
+ w.appendChild(wizChecklistActions());
14867
14902
  }
14868
14903
 
14869
- function wizChecklistActions(eventOk) {
14904
+ function wizChecklistActions() {
14870
14905
  var actions = el('div', 'actions');
14871
14906
  var back = el('button', 'btn', '\u7A0D\u540E\u518D\u8BF4');
14872
14907
  back.onclick = closeWizard;
14873
14908
  actions.appendChild(back);
14874
14909
  actions.appendChild(el('div', 'grow'));
14875
- var next = el('button', 'btn' + (eventOk ? ' primary' : ''), eventOk ? '\u4E0B\u4E00\u6B65' : '\u4E8B\u4EF6\u751F\u6548\u540E\u518D\u7EE7\u7EED');
14876
- if (eventOk) { next.onclick = function () { wizStep = 3; stopWizPoll(); renderWizard(); }; }
14877
- else { next.className = 'btn disabled'; next.disabled = true; }
14910
+ var next = el('button', 'btn primary', '\u4E0B\u4E00\u6B65');
14911
+ next.onclick = function () { wizStep = 3; stopWizPoll(); renderWizard(); };
14878
14912
  actions.appendChild(next);
14879
14913
  return actions;
14880
14914
  }
14881
14915
 
14882
- // \u300C\u957F\u8FDE\u63A5\u672A\u5EFA\u7ACB\u300D\u4E00\u952E\uFF1A\u542F\u7528\uFF08\u52A0\u5165\u6D3B\u8DC3\u96C6\uFF09\u2192 daemon \u5728\u8DD1\u5C31\u786E\u8BA4\u91CD\u542F\u3001\u6CA1\u8DD1\u5C31\u76F4\u63A5\u542F\u52A8\uFF0C
14883
- // \u7701\u53BB\u7EC8\u7AEF bot use + start\u3002\u590D\u7528 confirmDialog / postAction\uFF0C\u4E0E\u4EEA\u8868\u76D8\u91CD\u542F\u540C\u4E00\u5957\u3002
14884
- function wizEnableAndLaunch() {
14885
- if (!wizBotId) { toast('\u274C \u7F3A\u5C11\u673A\u5668\u4EBA ID'); return; }
14886
- var who = (wizSetup && wizSetup.botName) || wizBotId;
14887
- fetch('/api/bots/' + encodeURIComponent(wizBotId), {
14888
- method: 'PATCH', headers: { 'Content-Type': 'application/json' },
14889
- body: JSON.stringify({ enabled: true }),
14890
- }).then(function (r) { return r.json().then(function (j) { return { status: r.status, body: j }; }); })
14891
- .then(function (resp) {
14892
- if (resp.status !== 200) { toast('\u274C ' + (resp.body.message || ('HTTP ' + resp.status))); return; }
14893
- toast('\u2705 \u5DF2\u52A0\u5165\u6D3B\u8DC3\u96C6');
14894
- if (daemon && daemon.running) {
14895
- confirmDialog({
14896
- title: '\u{1F504} \u7ACB\u5373\u91CD\u542F Feishu Bridge \u62C9\u8D77\u300C' + who + '\u300D\uFF1F',
14897
- lines: [
14898
- '\u5DF2\u52A0\u5165\u6D3B\u8DC3\u96C6\uFF0C\u91CD\u542F\u540E\u5373\u53EF\u4E0A\u7EBF\u3002',
14899
- '\u91CD\u542F\u7EA6\u6570\u79D2\uFF0C\u671F\u95F4\u6240\u6709\u5728\u7EBF\u673A\u5668\u4EBA\u7684\u957F\u8FDE\u63A5\u4F1A\u77ED\u6682\u65AD\u5F00\u5E76\u81EA\u52A8\u91CD\u8FDE\u3002',
14900
- ],
14901
- confirmLabel: '\u7ACB\u5373\u91CD\u542F',
14902
- onConfirm: function () { postAction('/api/daemon/restart', '\u91CD\u542F'); },
14903
- });
14904
- } else {
14905
- // daemon \u6CA1\u5728\u8DD1\uFF08\u53EA\u8BFB\u9884\u89C8\uFF09\u2192 \u542F\u52A8\u5B83\uFF08read-only preview \u6CE8\u5165\u4E86 startDaemon\uFF09\uFF1B
14906
- // \u8D77\u6765\u540E /api/console/live \u8F6E\u8BE2\u4F1A\u628A\u9875\u9762\u81EA\u52A8\u5E26\u53BB\u53EF\u5199\u63A7\u5236\u53F0\u3002
14907
- postAction('/api/daemon/start', '\u542F\u52A8');
14908
- }
14909
- })
14910
- .catch(function () { toast('\u274C \u8BF7\u6C42\u5931\u8D25'); });
14911
- }
14912
-
14913
14916
  function copyRow(text) {
14914
14917
  var row = el('div', 'copybox');
14915
14918
  row.appendChild(el('code', null, text));
@@ -14930,33 +14933,20 @@ ${UI_PURE_JS}
14930
14933
  w.textContent = '';
14931
14934
  w.appendChild(el('h3', null, '\u{1F389} \u63A5\u5165\u5B8C\u6210'));
14932
14935
  w.appendChild(wizStepBar(3));
14933
- w.appendChild(el('div', 'note', '\u673A\u5668\u4EBA\u300C' + ((wizSetup && wizSetup.botName) || wizBotId || '') + '\u300D\u5DF2\u5C31\u7EEA\uFF0C\u4E8B\u4EF6\u8BA2\u9605\u5DF2\u751F\u6548\u3002'));
14934
- var ul = el('div'); ul.style.margin = '12px 0';
14935
- [
14936
- '\u2460 \u5728\u98DE\u4E66\u91CC\u79C1\u804A\u8FD9\u4E2A\u673A\u5668\u4EBA\uFF0C\u70B9\u300C\u2795 \u65B0\u5EFA\u9879\u76EE\u300D\u628A\u4E00\u4E2A\u76EE\u5F55\u7ED1\u6210\u9879\u76EE\u7FA4\uFF1B',
14937
- '\u2461 \u6216\u628A\u673A\u5668\u4EBA\u62C9\u8FDB\u4E00\u4E2A\u5DF2\u6709\u7FA4\uFF08\u9700\u5F00\u300C\u52A0\u5165\u5B58\u91CF\u7FA4\u300D\u76F8\u5173\u6743\u9650\uFF09\u81EA\u52A8\u7ED1\u5B9A\uFF1B',
14938
- '\u2462 \u7136\u540E\u5728\u7FA4\u91CC @\u673A\u5668\u4EBA \u63D0\u9700\u6C42\uFF0C\u5B83\u5C31\u5728\u7ED1\u5B9A\u7684\u76EE\u5F55\u91CC\u5E72\u6D3B\u3002',
14939
- ].forEach(function (line) {
14940
- var d = el('div'); d.style.padding = '4px 0'; d.textContent = line;
14941
- ul.appendChild(d);
14942
- });
14943
- w.appendChild(ul);
14944
- w.appendChild(el('div', 'note', '\u63D0\u793A\uFF1A\u79C1\u804A\u673A\u5668\u4EBA\u53D1\u4EFB\u610F\u6D88\u606F\u5373\u53EF\u5524\u51FA\u79C1\u804A\u7BA1\u7406\u53F0\uFF1BWeb \u63A7\u5236\u53F0\u4E0E\u79C1\u804A\u5361\u7247\u5171\u4EAB\u540C\u4E00\u5957\u8BBE\u7F6E\uFF0C\u53CC\u7AEF\u5B9E\u65F6\u4E00\u81F4\u3002'));
14945
- // \u65B0\u5EFA\u7684\u673A\u5668\u4EBA\u8981\u7B49 daemon \u91CD\u542F\u3001\u88AB supervisor \u63A5\u7BA1\u540E\u624D\u771F\u6B63\u4E0A\u7EBF\uFF08\u5C24\u5176\u300C\u5F15\u5BFC\u63A7\u5236\u53F0\u300D\u662F
14946
- // \u96F6 bot \u8D77\u7684\uFF0C\u5F97\u91CD\u542F\u624D\u4F1A\u8FDE\u4E0A\u8FD9\u4E2A\u65B0 bot\uFF09\u3002\u7ED9\u4E00\u6761\u9192\u76EE\u63D0\u793A + \u4E00\u952E\u91CD\u542F\uFF08\u786E\u8BA4\u5F39\u7A97\u91CC\u4F1A\u8BF4\u660E
14947
- // \u91CD\u542F\u4F1A\u77ED\u6682\u6253\u65AD\u5176\u5B83\u5728\u8DD1\u7684\u673A\u5668\u4EBA\uFF09\u3002
14948
- var liveTip = el('div', 'note');
14949
- liveTip.style.cssText = 'margin-top:10px;padding:10px 12px;background:var(--blue-tint);border-radius:8px;color:var(--text-2)';
14950
- liveTip.textContent = '\u26A1 \u8BA9\u5B83\u4E0A\u7EBF\uFF1A\u65B0\u673A\u5668\u4EBA\u9700\u91CD\u542F Feishu Bridge \u540E\u7531\u540E\u53F0\u63A5\u7BA1\u3002\u70B9\u4E0B\u9762\u300C\u91CD\u542F\u4F7F\u5176\u4E0A\u7EBF\u300D\u5373\u53EF\uFF08\u9996\u6B21\u521B\u5EFA\u65F6\u91CD\u542F\u5F88\u5B89\u5168\uFF0C\u4E0D\u5F71\u54CD\u522B\u7684\u673A\u5668\u4EBA\uFF09\u3002';
14951
- w.appendChild(liveTip);
14936
+ var who = (wizSetup && wizSetup.botName) || wizBotId || '';
14937
+ // \u5230\u8FD9\u4E00\u6B65\u53EA\u5269\u4E00\u4EF6\u4E8B\uFF1A\u91CD\u542F Feishu Bridge \u8BA9\u65B0 bot \u4E0A\u7EBF\u3002\u4E0D\u518D\u5806\u300C\u65B0\u5EFA\u9879\u76EE/\u62C9\u7FA4/@\u300D\u90A3\u5957
14938
+ // \u5F15\u5BFC\uFF08\u90A3\u4F1A\u8BA9\u4EBA\u4EE5\u4E3A\u5F97\u5148\u505A\u5B8C\u624D\u80FD\u7EE7\u7EED\uFF09\u3002\u91CD\u542F\u5165\u53E3**\u53EA\u6B64\u4E00\u5904**\uFF1A\u4E3B\u6309\u94AE\u300C\u91CD\u542F\u4F7F\u5176\u4E0A\u7EBF\u300D\uFF1B
14939
+ // \u60F3\u665A\u70B9\u518D\u8BF4\u5C31\u70B9\u300C\u7A0D\u540E\u518D\u91CD\u542F\u300D\u3002
14940
+ w.appendChild(el('div', 'note', '\u673A\u5668\u4EBA\u300C' + who + '\u300D\u5DF2\u52A0\u5165\u6D3B\u8DC3\u96C6\u3002\u6700\u540E\u4E00\u6B65\uFF1A\u91CD\u542F Feishu Bridge \u8BA9\u5B83\u4E0A\u7EBF\uFF08\u7EA6\u6570\u79D2\uFF0C\u5176\u5B83\u5728\u7EBF\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u65AD\u8FDE\u540E\u81EA\u52A8\u91CD\u8FDE\uFF09\u3002'));
14941
+ var gotoBot = function () { var id = wizBotId; closeWizard(); if (id) go({ tab: 'bot', botId: id }); };
14952
14942
  var actions = el('div', 'actions');
14953
- var restart = el('button', 'btn', '\u{1F501} \u91CD\u542F\u4F7F\u5176\u4E0A\u7EBF');
14954
- restart.onclick = askRestart;
14955
- actions.appendChild(restart);
14943
+ var later = el('button', 'btn', '\u7A0D\u540E\u518D\u91CD\u542F');
14944
+ later.onclick = gotoBot;
14945
+ actions.appendChild(later);
14956
14946
  actions.appendChild(el('div', 'grow'));
14957
- var done = el('button', 'btn primary', '\u5B8C\u6210\u5E76\u8FDB\u5165\u8BE5\u673A\u5668\u4EBA \u2192');
14958
- done.onclick = function () { var id = wizBotId; closeWizard(); if (id) go({ tab: 'bot', botId: id }); };
14959
- actions.appendChild(done);
14947
+ var restart = el('button', 'btn primary', '\u{1F501} \u91CD\u542F\u4F7F\u5176\u4E0A\u7EBF');
14948
+ restart.onclick = function () { postAction('/api/daemon/restart', '\u91CD\u542F'); gotoBot(); };
14949
+ actions.appendChild(restart);
14960
14950
  w.appendChild(actions);
14961
14951
  }
14962
14952
 
@@ -15037,7 +15027,7 @@ var DEFAULT_WEB_PORT = 51847;
15037
15027
  var COOKIE_NAME = "fcb_console_token";
15038
15028
  var SSE_INITIAL_TAIL_BYTES = 16 * 1024;
15039
15029
  function createWebServer(opts) {
15040
- const token = opts.token ?? randomUUID6();
15030
+ const token = opts.token ?? randomUUID7();
15041
15031
  const html = opts.html ?? UI_HTML;
15042
15032
  const logDir = opts.logDir ?? join19(paths.appDir, "logs");
15043
15033
  const sseCleanups = /* @__PURE__ */ new Set();
@@ -15360,7 +15350,16 @@ function createWebServer(opts) {
15360
15350
  const appSecret = typeof body.appSecret === "string" ? body.appSecret : "";
15361
15351
  const tenant = body.tenant === "lark" ? "lark" : "feishu";
15362
15352
  const desiredName = typeof body.name === "string" && body.name.trim() ? body.name.trim() : void 0;
15363
- const result = await opts.service.registerBot({ appId, appSecret, tenant, desiredName });
15353
+ let result;
15354
+ try {
15355
+ result = await opts.service.registerBot({ appId, appSecret, tenant, desiredName });
15356
+ } catch (err) {
15357
+ if (err instanceof NotWiredYetError) {
15358
+ sendJson(res, 501, { error: "not_wired_yet", message: err.message });
15359
+ return;
15360
+ }
15361
+ throw err;
15362
+ }
15364
15363
  if (result.ok) {
15365
15364
  sendJson(res, 201, {
15366
15365
  ok: true,
@@ -15380,7 +15379,7 @@ function createWebServer(opts) {
15380
15379
  function handleRegisterQrStream(req, res) {
15381
15380
  qrSession?.abort.abort();
15382
15381
  const abort = new AbortController();
15383
- const session = { id: randomUUID6(), abort };
15382
+ const session = { id: randomUUID7(), abort };
15384
15383
  qrSession = session;
15385
15384
  res.writeHead(200, {
15386
15385
  "Content-Type": "text/event-stream",
@@ -15657,23 +15656,27 @@ async function readJsonBody(req) {
15657
15656
  }
15658
15657
 
15659
15658
  // src/web/mount.ts
15659
+ async function listenCanonical(web, attempts = 25, gapMs = 200) {
15660
+ for (let i = 0; i < attempts; i++) {
15661
+ try {
15662
+ return await web.listen(DEFAULT_WEB_PORT);
15663
+ } catch (err) {
15664
+ if (err.code !== "EADDRINUSE") throw err;
15665
+ if (i < attempts - 1) await new Promise((r) => setTimeout(r, gapMs));
15666
+ }
15667
+ }
15668
+ log.warn("web", "console-port-busy-fallback", { preferred: DEFAULT_WEB_PORT });
15669
+ return web.listen(0);
15670
+ }
15660
15671
  async function mountWebConsole(service) {
15661
- const web = createWebServer({ service });
15672
+ const web = createWebServer({ service, token: stableWebConsoleToken() });
15662
15673
  let port;
15663
15674
  let url;
15664
15675
  try {
15665
- ({ port, url } = await web.listen(DEFAULT_WEB_PORT));
15676
+ ({ port, url } = await listenCanonical(web));
15666
15677
  } catch (err) {
15667
- if (err.code !== "EADDRINUSE") {
15668
- log.fail("web", err, { phase: "console-listen" });
15669
- return void 0;
15670
- }
15671
- try {
15672
- ({ port, url } = await web.listen(0));
15673
- } catch (err2) {
15674
- log.fail("web", err2, { phase: "console-listen-fallback" });
15675
- return void 0;
15676
- }
15678
+ log.fail("web", err, { phase: "console-listen" });
15679
+ return void 0;
15677
15680
  }
15678
15681
  publishWebConsole({ port, token: web.token, pid: process.pid, startedAt: Date.now() });
15679
15682
  const exitCleanup = () => clearWebConsole();
@@ -16469,14 +16472,17 @@ async function runWeb(opts = {}) {
16469
16472
  console.log(" \xB7 \u4EC5\u672C\u673A\u53EF\u8BBF\u95EE\uFF08127.0.0.1\uFF09\uFF1BURL \u542B token\uFF0C\u8BF7\u52FF\u5916\u4F20/\u622A\u56FE\u3002");
16470
16473
  return;
16471
16474
  }
16472
- const port = opts.port ?? DEFAULT_WEB_PORT;
16475
+ const port = opts.port ?? 0;
16473
16476
  if (!Number.isInteger(port) || port < 0 || port > 65535) {
16474
16477
  console.error(`\u2717 \u65E0\u6548\u7AEF\u53E3\uFF1A${opts.port}`);
16475
16478
  process.exitCode = 1;
16476
16479
  return;
16477
16480
  }
16478
- const service = createReadonlyAdminService({ startDaemon: () => spawnDaemonControl("start") });
16479
- const web = createWebServer({ service, liveConsole: () => readWebConsole() });
16481
+ const service = createReadonlyAdminService({
16482
+ startDaemon: () => spawnDaemonControl("start"),
16483
+ applyUpdate: () => spawnDaemonControl("update")
16484
+ });
16485
+ const web = createWebServer({ service, token: stableWebConsoleToken(), liveConsole: () => readWebConsole() });
16480
16486
  let url;
16481
16487
  try {
16482
16488
  ({ url } = await web.listen(port));
package/dist/index.d.ts CHANGED
@@ -55,6 +55,9 @@ declare const paths: {
55
55
  /** daemon 内嵌 Web 控制台的发现文件 {port, token, pid}(0600,daemon 退出
56
56
  * 清理)——`web` 子命令据此直接打开 daemon 控制台而不是再起只读副本。 */
57
57
  webConsoleFile: string;
58
+ /** 稳定的 Web 控制台 token(0600,**不随进程退出清理**)——让重启 / 预览→daemon
59
+ * 切换后浏览器里那条带 token 的 URL 始终有效,不再 401。删此文件即轮换 token。 */
60
+ webTokenFile: string;
58
61
  };
59
62
 
60
63
  export { log, newTraceId, paths, withTrace };
package/dist/index.js CHANGED
@@ -61,7 +61,10 @@ var paths = {
61
61
  inboundDir: join(appDir, "inbound"),
62
62
  /** daemon 内嵌 Web 控制台的发现文件 {port, token, pid}(0600,daemon 退出
63
63
  * 清理)——`web` 子命令据此直接打开 daemon 控制台而不是再起只读副本。 */
64
- webConsoleFile: join(appDir, "web-console.json")
64
+ webConsoleFile: join(appDir, "web-console.json"),
65
+ /** 稳定的 Web 控制台 token(0600,**不随进程退出清理**)——让重启 / 预览→daemon
66
+ * 切换后浏览器里那条带 token 的 URL 始终有效,不再 401。删此文件即轮换 token。 */
67
+ webTokenFile: join(appDir, "web-token")
65
68
  };
66
69
 
67
70
  // src/core/logger.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelzen/feishu-codex-bridge",
3
- "version": "0.3.12-test.0",
3
+ "version": "0.3.12-test.2",
4
4
  "description": "Bridge Feishu/Lark messenger with local Codex via app-server (project=group, thread=session)",
5
5
  "type": "module",
6
6
  "bin": {