@foxden-app/foxclaw 0.3.7 → 0.3.8

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.
@@ -249,6 +249,8 @@ export declare class BridgeSessionCore {
249
249
  private retryTurnAfterAuthRotation;
250
250
  private selectNextCodexAuthCandidate;
251
251
  private listCodexAuthState;
252
+ private readCodexAuthSwitchLabels;
253
+ private codexAuthSwitchParams;
252
254
  private switchCodexAuthAndRestart;
253
255
  private buildNativeCollaborationMode;
254
256
  private buildCodexUsageStatusLines;
@@ -3693,7 +3693,8 @@ export class BridgeSessionCore {
3693
3693
  await this.sendMessage(scopeId, renderAuthListMessage(locale, state, parseWeixinBridgeScope(scopeId) !== null));
3694
3694
  return;
3695
3695
  }
3696
- await this.sendMessage(scopeId, t(locale, 'auth_switching', { value: candidate.name }));
3696
+ const switchLabels = await this.readCodexAuthSwitchLabels(candidate);
3697
+ await this.sendMessage(scopeId, t(locale, 'auth_switching', this.codexAuthSwitchParams(locale, switchLabels.fromLabel, switchLabels.toLabel)));
3697
3698
  await this.switchCodexAuthAndRestart(scopeId, locale, candidate, false);
3698
3699
  }
3699
3700
  async handleAuthToggleCommand(scopeId, locale, args, disabled) {
@@ -4190,8 +4191,10 @@ export class BridgeSessionCore {
4190
4191
  }
4191
4192
  await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'auth_choice_recorded'));
4192
4193
  this.pendingAuthChoiceLists.delete(localId);
4194
+ const switchLabels = await this.readCodexAuthSwitchLabels(candidate);
4195
+ const switchingMessage = t(locale, 'auth_switching', this.codexAuthSwitchParams(locale, switchLabels.fromLabel, switchLabels.toLabel));
4193
4196
  if (record.messageId !== null) {
4194
- await this.editMessage(event.scopeId, record.messageId, t(locale, 'auth_switching', { value: candidate.name }), []);
4197
+ await this.editMessage(event.scopeId, record.messageId, switchingMessage, []);
4195
4198
  }
4196
4199
  await this.switchCodexAuthAndRestart(event.scopeId, locale, candidate, false);
4197
4200
  }
@@ -4207,16 +4210,17 @@ export class BridgeSessionCore {
4207
4210
  this.authRotationInProgress = true;
4208
4211
  try {
4209
4212
  const failedTargets = rotation.retry?.failedAuthTargets ?? this.authRotationFailedTargets;
4210
- const candidate = await this.selectNextCodexAuthCandidate(failedTargets);
4213
+ const selection = await this.selectNextCodexAuthCandidate(failedTargets);
4211
4214
  const locale = this.localeForChat(rotation.scopeId);
4212
- if (!candidate) {
4215
+ if (!selection) {
4213
4216
  await this.sendMessage(rotation.scopeId, t(locale, 'auth_auto_no_candidate', {
4214
4217
  error: formatShortStatusError(rotation.reason),
4215
4218
  }));
4216
4219
  return false;
4217
4220
  }
4221
+ const { candidate, fromLabel, toLabel } = selection;
4218
4222
  await this.sendMessage(rotation.scopeId, t(locale, 'auth_auto_switching', {
4219
- value: candidate.name,
4223
+ ...this.codexAuthSwitchParams(locale, fromLabel, toLabel),
4220
4224
  error: formatShortStatusError(rotation.reason),
4221
4225
  }));
4222
4226
  await this.switchCodexAuthAndRestart(rotation.scopeId, locale, candidate, true);
@@ -4290,21 +4294,35 @@ export class BridgeSessionCore {
4290
4294
  for (let offset = 1; offset <= state.candidates.length; offset += 1) {
4291
4295
  const candidate = state.candidates[(currentIndex + offset + state.candidates.length) % state.candidates.length];
4292
4296
  if (candidate && !candidate.disabled && !failedTargets.has(candidate.path)) {
4293
- return candidate;
4297
+ return { candidate, fromLabel: state.currentLabel, toLabel: await authPathDisplayLabel(candidate.path) };
4294
4298
  }
4295
4299
  }
4296
- return candidates[0] ?? null;
4300
+ const candidate = candidates[0] ?? null;
4301
+ return candidate ? { candidate, fromLabel: state.currentLabel, toLabel: await authPathDisplayLabel(candidate.path) } : null;
4297
4302
  }
4298
4303
  async listCodexAuthState() {
4299
4304
  return listCodexAuthState(this.store.listDisabledCodexAuthCandidateNames());
4300
4305
  }
4306
+ async readCodexAuthSwitchLabels(candidate) {
4307
+ const state = await this.listCodexAuthState();
4308
+ return {
4309
+ fromLabel: state.currentLabel,
4310
+ toLabel: await authPathDisplayLabel(candidate.path),
4311
+ };
4312
+ }
4313
+ codexAuthSwitchParams(locale, fromLabel, toLabel) {
4314
+ return {
4315
+ from: fromLabel ?? t(locale, 'none'),
4316
+ to: toLabel,
4317
+ };
4318
+ }
4301
4319
  async switchCodexAuthAndRestart(scopeId, locale, candidate, automatic) {
4302
- await switchCodexAuth(candidate.path);
4320
+ const result = await switchCodexAuth(candidate.path);
4303
4321
  this.authRotationFailedTargets.delete(candidate.path);
4304
4322
  this.pendingTurnErrors.clear();
4305
4323
  this.attachedThreads.clear();
4306
4324
  await this.app.restart();
4307
- const lines = [t(locale, automatic ? 'auth_auto_done' : 'auth_switch_done', { value: candidate.name })];
4325
+ const lines = [t(locale, automatic ? 'auth_auto_done' : 'auth_switch_done', this.codexAuthSwitchParams(locale, result.fromLabel, result.toLabel))];
4308
4326
  lines.push(...await this.buildCodexUsageStatusLines(locale));
4309
4327
  await this.sendMessage(scopeId, lines.join('\n'));
4310
4328
  }
@@ -6913,7 +6931,7 @@ async function listCodexAuthState(disabledNames = new Set()) {
6913
6931
  authDir,
6914
6932
  authPath,
6915
6933
  currentTargetPath,
6916
- currentLabel: currentTargetPath ? path.basename(currentTargetPath) : null,
6934
+ currentLabel: currentTargetPath ? await authPathDisplayLabel(currentTargetPath) : null,
6917
6935
  candidates,
6918
6936
  };
6919
6937
  }
@@ -6946,6 +6964,27 @@ async function resolveCurrentAuthTarget(authDir, authPath) {
6946
6964
  }
6947
6965
  return authPath;
6948
6966
  }
6967
+ async function resolveAuthFinalTargetPath(startPath) {
6968
+ let currentPath = startPath;
6969
+ const seen = new Set();
6970
+ for (let depth = 0; depth < 20; depth += 1) {
6971
+ const absolutePath = path.resolve(currentPath);
6972
+ if (seen.has(absolutePath)) {
6973
+ return currentPath;
6974
+ }
6975
+ seen.add(absolutePath);
6976
+ const stat = await fs.lstat(currentPath).catch(() => null);
6977
+ if (!stat?.isSymbolicLink()) {
6978
+ return currentPath;
6979
+ }
6980
+ const target = await fs.readlink(currentPath);
6981
+ currentPath = path.resolve(path.dirname(currentPath), target);
6982
+ }
6983
+ return currentPath;
6984
+ }
6985
+ async function authPathDisplayLabel(authPath) {
6986
+ return path.basename(await resolveAuthFinalTargetPath(authPath));
6987
+ }
6949
6988
  async function pointCodexAuthAtTarget(authDir, authPath, targetPath) {
6950
6989
  await fs.mkdir(authDir, { recursive: true });
6951
6990
  const tempLink = path.join(authDir, `.auth.json.${process.pid}.${Date.now()}.tmp`);
@@ -6965,6 +7004,10 @@ async function switchCodexAuth(targetPath) {
6965
7004
  throw new Error(`Auth candidate is no longer available: ${path.basename(targetPath)}`);
6966
7005
  }
6967
7006
  await pointCodexAuthAtTarget(state.authDir, state.authPath, candidate.path);
7007
+ return {
7008
+ fromLabel: state.currentLabel,
7009
+ toLabel: await authPathDisplayLabel(candidate.path),
7010
+ };
6968
7011
  }
6969
7012
  function renderAuthListMessage(locale, state, includeWeixinCopyPaste = false) {
6970
7013
  const lines = [
package/dist/i18n.d.ts CHANGED
@@ -133,10 +133,10 @@ declare const MESSAGES: {
133
133
  readonly auth_choice_expired: "This auth list is no longer active";
134
134
  readonly auth_choice_mismatch: "Auth choice does not match this message";
135
135
  readonly auth_choice_recorded: "Auth selected";
136
- readonly auth_switching: "Switching Codex auth to {value}...";
137
- readonly auth_switch_done: "Codex auth switched to {value}.";
138
- readonly auth_auto_switching: "Codex auth problem detected ({error}). Switching to {value}...";
139
- readonly auth_auto_done: "Auto-switched Codex auth to {value}.";
136
+ readonly auth_switching: "Switching Codex auth: {from} -> {to}...";
137
+ readonly auth_switch_done: "Codex auth switched: {from} -> {to}.";
138
+ readonly auth_auto_switching: "Codex auth problem detected ({error}). Switching: {from} -> {to}...";
139
+ readonly auth_auto_done: "Auto-switched Codex auth: {from} -> {to}.";
140
140
  readonly auth_auto_retrying: "Retrying the same request with the new auth...";
141
141
  readonly auth_auto_retry_thread_missing: "Auth was switched, but the original thread is no longer available ({threadId}). Retry was stopped to avoid creating duplicate sessions.";
142
142
  readonly auth_auto_no_candidate: "Codex auth problem detected ({error}), but no unused auth candidate is available.";
@@ -683,10 +683,10 @@ declare const MESSAGES: {
683
683
  readonly auth_choice_expired: "这个 auth 列表已经不再有效";
684
684
  readonly auth_choice_mismatch: "这个 auth 选择按钮不属于当前消息";
685
685
  readonly auth_choice_recorded: "已选择 auth";
686
- readonly auth_switching: "正在切换 Codex auth {value}...";
687
- readonly auth_switch_done: "Codex auth 已切换到 {value}。";
688
- readonly auth_auto_switching: "检测到 Codex auth 问题({error}),正在切换到 {value}...";
689
- readonly auth_auto_done: "已自动切换 Codex auth {value}。";
686
+ readonly auth_switching: "正在切换 Codex auth:{from} -> {to}...";
687
+ readonly auth_switch_done: "Codex auth 已切换:{from} -> {to}。";
688
+ readonly auth_auto_switching: "检测到 Codex auth 问题({error}),正在切换:{from} -> {to}...";
689
+ readonly auth_auto_done: "已自动切换 Codex auth:{from} -> {to}。";
690
690
  readonly auth_auto_retrying: "正在用新的 auth 重试同一条请求...";
691
691
  readonly auth_auto_retry_thread_missing: "Auth 已切换,但原线程已经不可用({threadId})。已停止重试,避免创建重复 session。";
692
692
  readonly auth_auto_no_candidate: "检测到 Codex auth 问题({error}),但没有可用的未失败候选 auth。";
package/dist/i18n.js CHANGED
@@ -131,10 +131,10 @@ const MESSAGES = {
131
131
  auth_choice_expired: 'This auth list is no longer active',
132
132
  auth_choice_mismatch: 'Auth choice does not match this message',
133
133
  auth_choice_recorded: 'Auth selected',
134
- auth_switching: 'Switching Codex auth to {value}...',
135
- auth_switch_done: 'Codex auth switched to {value}.',
136
- auth_auto_switching: 'Codex auth problem detected ({error}). Switching to {value}...',
137
- auth_auto_done: 'Auto-switched Codex auth to {value}.',
134
+ auth_switching: 'Switching Codex auth: {from} -> {to}...',
135
+ auth_switch_done: 'Codex auth switched: {from} -> {to}.',
136
+ auth_auto_switching: 'Codex auth problem detected ({error}). Switching: {from} -> {to}...',
137
+ auth_auto_done: 'Auto-switched Codex auth: {from} -> {to}.',
138
138
  auth_auto_retrying: 'Retrying the same request with the new auth...',
139
139
  auth_auto_retry_thread_missing: 'Auth was switched, but the original thread is no longer available ({threadId}). Retry was stopped to avoid creating duplicate sessions.',
140
140
  auth_auto_no_candidate: 'Codex auth problem detected ({error}), but no unused auth candidate is available.',
@@ -681,10 +681,10 @@ const MESSAGES = {
681
681
  auth_choice_expired: '这个 auth 列表已经不再有效',
682
682
  auth_choice_mismatch: '这个 auth 选择按钮不属于当前消息',
683
683
  auth_choice_recorded: '已选择 auth',
684
- auth_switching: '正在切换 Codex auth {value}...',
685
- auth_switch_done: 'Codex auth 已切换到 {value}。',
686
- auth_auto_switching: '检测到 Codex auth 问题({error}),正在切换到 {value}...',
687
- auth_auto_done: '已自动切换 Codex auth {value}。',
684
+ auth_switching: '正在切换 Codex auth:{from} -> {to}...',
685
+ auth_switch_done: 'Codex auth 已切换:{from} -> {to}。',
686
+ auth_auto_switching: '检测到 Codex auth 问题({error}),正在切换:{from} -> {to}...',
687
+ auth_auto_done: '已自动切换 Codex auth:{from} -> {to}。',
688
688
  auth_auto_retrying: '正在用新的 auth 重试同一条请求...',
689
689
  auth_auto_retry_thread_missing: 'Auth 已切换,但原线程已经不可用({threadId})。已停止重试,避免创建重复 session。',
690
690
  auth_auto_no_candidate: '检测到 Codex auth 问题({error}),但没有可用的未失败候选 auth。',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",