@foxden-app/foxclaw 0.5.20 → 0.5.21

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.
@@ -19,6 +19,7 @@ import { diffObservedTurn, findLatestTurn, findLiveTurn } from './observer.js';
19
19
  import { applySessionLog, bootstrapSessionLog, splitJsonlChunk, } from './session_observer.js';
20
20
  import { renderActiveTurnStatus } from './status.js';
21
21
  import { writeRuntimeStatus } from '../runtime.js';
22
+ const AUTH_DELETE_REASON_NEEDS_REPAIR = 'needs_repair';
22
23
  class UserFacingError extends Error {
23
24
  }
24
25
  const OBSERVED_THREAD_POLL_MS = 1500;
@@ -432,6 +433,7 @@ export class BridgeSessionCore {
432
433
  t(locale, 'status_pending_user_inputs', { value: this.store.countPendingUserInputs() }),
433
434
  t(locale, 'status_queued_turns', { value: this.store.countQueuedTurnInputs(scopeId) }),
434
435
  t(locale, 'status_active_turns', { value: this.activeTurns.size }),
436
+ formatCodexAuthPoolSummary(locale, this.store.getCodexAuthPoolStats()),
435
437
  ];
436
438
  if (serviceStatus) {
437
439
  lines.push('', t(locale, 'status_runtime_overview'));
@@ -755,7 +757,7 @@ export class BridgeSessionCore {
755
757
  return;
756
758
  }
757
759
  case 'config': {
758
- await this.handleConfigCommand(scopeId, locale);
760
+ await this.handleConfigCommand(scopeId, locale, args);
759
761
  return;
760
762
  }
761
763
  case 'requirements': {
@@ -1053,6 +1055,11 @@ export class BridgeSessionCore {
1053
1055
  await this.handleSetupCallback(event, setupMatch[1], setupMatch[2], locale);
1054
1056
  return;
1055
1057
  }
1058
+ const configMatch = /^config:auth_auto_delete:(on|off)$/.exec(event.data);
1059
+ if (configMatch) {
1060
+ await this.handleConfigToggleCallback(event, configMatch[1] === 'on', locale);
1061
+ return;
1062
+ }
1056
1063
  const settingsMatch = /^settings:(model|effort|access):(.+)$/.exec(event.data);
1057
1064
  if (settingsMatch) {
1058
1065
  await this.handleSettingsCallback(event, settingsMatch[1], settingsMatch[2], locale);
@@ -2931,6 +2938,19 @@ export class BridgeSessionCore {
2931
2938
  async getCurrentAuthLabel() {
2932
2939
  return (await this.listCodexAuthState()).currentLabel;
2933
2940
  }
2941
+ async handleExternalCodexAuthCandidateDeleted(candidateName, reason = null) {
2942
+ this.store.deleteCodexAuthCandidate(candidateName);
2943
+ if (isInvalidCodexAuthDeleteReason(reason)) {
2944
+ this.store.recordCodexAuthCandidateInvalidDelete(candidateName, reason);
2945
+ }
2946
+ else {
2947
+ this.store.recordCodexAuthCandidateRemoved(candidateName, reason);
2948
+ }
2949
+ this.authRotationFailedTargets.delete(path.join(this.resolveAuthDir(), candidateName));
2950
+ this.pendingTurnErrors.clear();
2951
+ this.attachedThreads.clear();
2952
+ await this.app.restart();
2953
+ }
2934
2954
  async validateExternalCodexAuthCandidate(candidateName, rawAuth, expectedAccountId) {
2935
2955
  if (this.externalAuthValidationInProgress) {
2936
2956
  return { ok: false, reason: 'runtime is not idle' };
@@ -4761,13 +4781,13 @@ export class BridgeSessionCore {
4761
4781
  throw error;
4762
4782
  }
4763
4783
  }
4764
- async deleteCodexAuthCandidate(candidate) {
4784
+ async deleteCodexAuthCandidate(candidate, reason = null) {
4765
4785
  const wasCurrent = candidate.isCurrent;
4766
4786
  const authDir = this.resolveAuthDir();
4767
4787
  const authPath = path.join(authDir, 'auth.json');
4768
4788
  let deletedByCoordinator = false;
4769
4789
  try {
4770
- await this.coordinator?.authCandidateDeleted?.(this.authRuntimeId(), candidate.name);
4790
+ await this.coordinator?.authCandidateDeleted?.(this.authRuntimeId(), candidate.name, reason);
4771
4791
  deletedByCoordinator = Boolean(this.coordinator?.authCandidateDeleted);
4772
4792
  }
4773
4793
  catch (error) {
@@ -4784,6 +4804,12 @@ export class BridgeSessionCore {
4784
4804
  }
4785
4805
  }
4786
4806
  this.store.deleteCodexAuthCandidate(candidate.name);
4807
+ if (isInvalidCodexAuthDeleteReason(reason)) {
4808
+ this.store.recordCodexAuthCandidateInvalidDelete(candidate.name, reason);
4809
+ }
4810
+ else {
4811
+ this.store.recordCodexAuthCandidateRemoved(candidate.name, reason);
4812
+ }
4787
4813
  this.authRotationFailedTargets.delete(candidate.path);
4788
4814
  const snapshots = await this.readCodexAuthQuotaSnapshots();
4789
4815
  if (Object.prototype.hasOwnProperty.call(snapshots, candidate.name)) {
@@ -5092,10 +5118,58 @@ export class BridgeSessionCore {
5092
5118
  const features = await this.app.listExperimentalFeatures();
5093
5119
  await this.sendMessage(scopeId, formatFeaturesMessage(locale, features));
5094
5120
  }
5095
- async handleConfigCommand(scopeId, locale) {
5121
+ async handleConfigCommand(scopeId, locale, args = []) {
5122
+ const action = args[0]?.toLowerCase() ?? '';
5123
+ if (['auth_auto_delete', 'auth-auto-delete', 'auto_delete_needs_repair', 'auto-delete-needs-repair'].includes(action)) {
5124
+ const enabled = parseConfigBooleanArg(args[1]);
5125
+ if (enabled === null) {
5126
+ await this.sendMessage(scopeId, t(locale, 'config_auth_auto_delete_usage'));
5127
+ return;
5128
+ }
5129
+ const update = await this.setAuthAutoDeleteNeedsRepair(enabled);
5130
+ const binding = this.store.getBinding(scopeId);
5131
+ const result = await this.app.readConfig(binding?.cwd ?? this.config.defaultCwd, true);
5132
+ await this.sendMessage(scopeId, `${this.formatConfigToggleUpdate(locale, update)}\n\n${formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats())}`, configKeyboard(locale, this.config));
5133
+ return;
5134
+ }
5096
5135
  const binding = this.store.getBinding(scopeId);
5097
5136
  const result = await this.app.readConfig(binding?.cwd ?? this.config.defaultCwd, true);
5098
- await this.sendMessage(scopeId, formatConfigMessage(locale, result));
5137
+ await this.sendMessage(scopeId, formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats()), configKeyboard(locale, this.config));
5138
+ }
5139
+ async handleConfigToggleCallback(event, enabled, locale) {
5140
+ const update = await this.setAuthAutoDeleteNeedsRepair(enabled);
5141
+ await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'decision_recorded'));
5142
+ const binding = this.store.getBinding(event.scopeId);
5143
+ const result = await this.app.readConfig(binding?.cwd ?? this.config.defaultCwd, true);
5144
+ const message = `${this.formatConfigToggleUpdate(locale, update)}\n\n${formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats())}`;
5145
+ if (event.messageId !== null) {
5146
+ await this.editMessage(event.scopeId, event.messageId, message, configKeyboard(locale, this.config));
5147
+ }
5148
+ else {
5149
+ await this.sendMessage(event.scopeId, message, configKeyboard(locale, this.config));
5150
+ }
5151
+ }
5152
+ async setAuthAutoDeleteNeedsRepair(enabled) {
5153
+ this.config.authAutoDeleteNeedsRepair = enabled;
5154
+ const envPath = this.config.envPath;
5155
+ if (!envPath) {
5156
+ return { enabled, envPath: null, envUpdated: false, envError: null };
5157
+ }
5158
+ try {
5159
+ await writeEnvBoolean(envPath, 'AUTH_AUTO_DELETE_NEEDS_REPAIR', enabled);
5160
+ return { enabled, envPath, envUpdated: true, envError: null };
5161
+ }
5162
+ catch (error) {
5163
+ this.logger.warn('config.env_update_failed', { key: 'AUTH_AUTO_DELETE_NEEDS_REPAIR', envPath, error: toErrorMeta(error) });
5164
+ return { enabled, envPath, envUpdated: false, envError: formatUserError(error) };
5165
+ }
5166
+ }
5167
+ formatConfigToggleUpdate(locale, update) {
5168
+ const lines = [t(locale, 'config_auth_auto_delete_updated', { value: t(locale, update.enabled ? 'yes' : 'no') })];
5169
+ if (update.envError) {
5170
+ lines.push(t(locale, 'config_env_update_failed', { value: update.envPath ?? t(locale, 'unknown'), error: update.envError }));
5171
+ }
5172
+ return lines.join('\n');
5099
5173
  }
5100
5174
  async handleRequirementsCommand(scopeId, locale) {
5101
5175
  const requirements = await this.app.readConfigRequirements();
@@ -5447,7 +5521,10 @@ export class BridgeSessionCore {
5447
5521
  }
5448
5522
  return false;
5449
5523
  }
5450
- this.markCodexAuthCandidateNeedsRepair(current.name);
5524
+ const disposition = await this.markCodexAuthCandidateNeedsRepair(current.name);
5525
+ if (disposition.deleted) {
5526
+ await this.sendMessage(rotation.scopeId, formatCodexAuthPoolSummary(locale, this.store.getCodexAuthPoolStats()));
5527
+ }
5451
5528
  }
5452
5529
  const selection = await this.selectNextCodexAuthCandidate(failedTargets);
5453
5530
  if (!selection) {
@@ -5457,10 +5534,12 @@ export class BridgeSessionCore {
5457
5534
  return false;
5458
5535
  }
5459
5536
  const { candidate, fromLabel, toLabel } = selection;
5460
- await this.sendMessage(rotation.scopeId, t(locale, 'auth_auto_switching', {
5461
- ...this.codexAuthSwitchParams(locale, fromLabel, toLabel),
5462
- error: formatShortStatusError(rotation.reason),
5463
- }));
5537
+ await this.sendMessage(rotation.scopeId, this.config.authAutoDeleteNeedsRepair
5538
+ ? t(locale, 'auth_auto_switching_quiet', { error: formatShortStatusError(rotation.reason) })
5539
+ : t(locale, 'auth_auto_switching', {
5540
+ ...this.codexAuthSwitchParams(locale, fromLabel, toLabel),
5541
+ error: formatShortStatusError(rotation.reason),
5542
+ }));
5464
5543
  const outcome = await this.switchCodexAuthAndRestart(rotation.scopeId, locale, candidate, true);
5465
5544
  if (!outcome.ok) {
5466
5545
  return false;
@@ -5548,6 +5627,7 @@ export class BridgeSessionCore {
5548
5627
  }
5549
5628
  async listCodexAuthState() {
5550
5629
  const state = await listCodexAuthState(this.store.listDisabledCodexAuthCandidateNames(this.authRuntimeId()), this.store.listCodexAuthCandidateStates(this.authRuntimeId()), this.resolveAuthDir());
5630
+ this.store.recordCodexAuthPoolInventory(state.candidates.map(candidate => candidate.name));
5551
5631
  const snapshots = await this.readCodexAuthQuotaSnapshots();
5552
5632
  const candidateQuotaIdentities = await this.readCodexAuthCandidateQuotaIdentities(state.candidates);
5553
5633
  state.candidates.forEach((candidate) => {
@@ -5594,7 +5674,6 @@ export class BridgeSessionCore {
5594
5674
  await this.app.restart();
5595
5675
  const validation = await this.validateCurrentCodexAuthCandidate(candidate);
5596
5676
  if (!validation.ok) {
5597
- this.markCodexAuthCandidateNeedsRepair(candidate.name);
5598
5677
  let restoredPrevious = false;
5599
5678
  try {
5600
5679
  await restoreCodexAuthTarget(initialState.authDir, initialState.authPath, initialState.currentTargetPath, originalRegularAuth);
@@ -5609,6 +5688,7 @@ export class BridgeSessionCore {
5609
5688
  error: toErrorMeta(error),
5610
5689
  });
5611
5690
  }
5691
+ const repairDisposition = await this.markCodexAuthCandidateNeedsRepair(candidate.name);
5612
5692
  const outcome = {
5613
5693
  ...result,
5614
5694
  ok: false,
@@ -5616,6 +5696,8 @@ export class BridgeSessionCore {
5616
5696
  recovered,
5617
5697
  error: validation.error,
5618
5698
  restoredPrevious,
5699
+ autoDeleted: repairDisposition.deleted,
5700
+ deleteRestarted: repairDisposition.restarted,
5619
5701
  };
5620
5702
  if (sendResult) {
5621
5703
  const lines = this.formatAuthSwitchValidationLines(locale, outcome);
@@ -5632,11 +5714,15 @@ export class BridgeSessionCore {
5632
5714
  recovered,
5633
5715
  error: null,
5634
5716
  restoredPrevious: false,
5717
+ autoDeleted: false,
5718
+ deleteRestarted: false,
5635
5719
  };
5636
5720
  if (!sendResult) {
5637
5721
  return outcome;
5638
5722
  }
5639
- const lines = [t(locale, automatic ? 'auth_auto_done' : 'auth_switch_done', this.codexAuthSwitchParams(locale, result.fromLabel, result.toLabel))];
5723
+ const lines = [automatic && this.config.authAutoDeleteNeedsRepair
5724
+ ? t(locale, 'auth_auto_done_quiet')
5725
+ : t(locale, automatic ? 'auth_auto_done' : 'auth_switch_done', this.codexAuthSwitchParams(locale, result.fromLabel, result.toLabel))];
5640
5726
  if (recovered) {
5641
5727
  lines.push(t(locale, 'auth_recovered_newer_candidate', { value: candidate.name }));
5642
5728
  }
@@ -5685,12 +5771,19 @@ export class BridgeSessionCore {
5685
5771
  if (outcome.ok) {
5686
5772
  return [];
5687
5773
  }
5774
+ const validationKey = outcome.autoDeleted && this.config.authAutoDeleteNeedsRepair
5775
+ ? 'auth_switch_validation_auto_deleted_quiet'
5776
+ : outcome.autoDeleted
5777
+ ? 'auth_switch_validation_auto_deleted'
5778
+ : 'auth_switch_validation_failed';
5688
5779
  return [
5689
- t(locale, 'auth_switch_validation_failed', {
5780
+ t(locale, validationKey, {
5690
5781
  value: outcome.candidateName,
5691
5782
  error: outcome.error ?? t(locale, 'unknown'),
5692
5783
  }),
5693
5784
  outcome.restoredPrevious ? t(locale, 'auth_switch_validation_reverted') : '',
5785
+ outcome.deleteRestarted ? t(locale, 'auth_delete_current_restarted') : '',
5786
+ outcome.autoDeleted ? formatCodexAuthPoolSummary(locale, this.store.getCodexAuthPoolStats()) : '',
5694
5787
  ].filter(Boolean);
5695
5788
  }
5696
5789
  async recoverCodexAuthCandidate(candidateName, options = { crossNode: true }) {
@@ -5709,8 +5802,23 @@ export class BridgeSessionCore {
5709
5802
  return false;
5710
5803
  }
5711
5804
  }
5712
- markCodexAuthCandidateNeedsRepair(candidateName) {
5805
+ async markCodexAuthCandidateNeedsRepair(candidateName) {
5806
+ if (this.config.authAutoDeleteNeedsRepair) {
5807
+ const candidate = (await this.listCodexAuthState()).candidates.find(entry => entry.name === candidateName) ?? null;
5808
+ if (!candidate) {
5809
+ this.store.deleteCodexAuthCandidate(candidateName);
5810
+ this.store.recordCodexAuthCandidateInvalidDelete(candidateName, AUTH_DELETE_REASON_NEEDS_REPAIR);
5811
+ return { deleted: true, restarted: false };
5812
+ }
5813
+ const restarted = await this.deleteCodexAuthCandidate(candidate, AUTH_DELETE_REASON_NEEDS_REPAIR);
5814
+ this.logger.warn('codex.auth_candidate_auto_deleted', {
5815
+ candidate: candidateName,
5816
+ runtimeId: this.authRuntimeId(),
5817
+ });
5818
+ return { deleted: true, restarted };
5819
+ }
5713
5820
  this.store.setCodexAuthCandidateState(candidateName, 'needs_repair');
5821
+ return { deleted: false, restarted: false };
5714
5822
  }
5715
5823
  markCodexAuthCandidateActive(candidateName) {
5716
5824
  this.store.setCodexAuthCandidateState(candidateName, 'active');
@@ -8078,7 +8186,7 @@ function formatFeaturesMessage(locale, features) {
8078
8186
  }
8079
8187
  return lines.join('\n');
8080
8188
  }
8081
- function formatConfigMessage(locale, result) {
8189
+ function formatConfigMessage(locale, result, appConfig, authPoolStats) {
8082
8190
  const config = result.config && typeof result.config === 'object' ? result.config : {};
8083
8191
  const layers = Array.isArray(result.layers) ? result.layers : [];
8084
8192
  const keys = ['model', 'model_provider', 'approval_policy', 'sandbox_mode', 'web_search', 'service_tier', 'profile', 'review_model'];
@@ -8088,8 +8196,62 @@ function formatConfigMessage(locale, result) {
8088
8196
  lines.push(`${key}: ${value === null || value === undefined ? '-' : formatConfigValue(value)}`);
8089
8197
  }
8090
8198
  lines.push(t(locale, 'config_layers', { count: layers.length }));
8199
+ lines.push('');
8200
+ lines.push(t(locale, 'config_foxclaw_title'));
8201
+ lines.push(t(locale, 'config_auth_auto_delete_needs_repair', {
8202
+ value: t(locale, appConfig.authAutoDeleteNeedsRepair ? 'yes' : 'no'),
8203
+ }));
8204
+ lines.push(`AUTH_AUTO_DELETE_NEEDS_REPAIR=${appConfig.authAutoDeleteNeedsRepair ? 'true' : 'false'}`);
8205
+ lines.push(formatCodexAuthPoolSummary(locale, authPoolStats));
8091
8206
  return lines.join('\n');
8092
8207
  }
8208
+ function formatCodexAuthPoolSummary(locale, stats) {
8209
+ return t(locale, 'auth_pool_summary', {
8210
+ total: stats.totalSeen,
8211
+ alive: stats.alive,
8212
+ deleted: stats.deletedInvalid,
8213
+ });
8214
+ }
8215
+ function isInvalidCodexAuthDeleteReason(reason) {
8216
+ return reason === AUTH_DELETE_REASON_NEEDS_REPAIR;
8217
+ }
8218
+ function configKeyboard(locale, appConfig) {
8219
+ const enabled = appConfig.authAutoDeleteNeedsRepair;
8220
+ return [[{
8221
+ text: t(locale, enabled ? 'button_config_auth_auto_delete_off' : 'button_config_auth_auto_delete_on'),
8222
+ callback_data: `config:auth_auto_delete:${enabled ? 'off' : 'on'}`,
8223
+ }]];
8224
+ }
8225
+ function parseConfigBooleanArg(value) {
8226
+ const normalized = value?.trim().toLowerCase();
8227
+ if (!normalized)
8228
+ return null;
8229
+ if (['1', 'true', 'yes', 'on', 'enable', 'enabled'].includes(normalized))
8230
+ return true;
8231
+ if (['0', 'false', 'no', 'off', 'disable', 'disabled'].includes(normalized))
8232
+ return false;
8233
+ return null;
8234
+ }
8235
+ async function writeEnvBoolean(envPath, key, enabled) {
8236
+ await fs.mkdir(path.dirname(envPath), { recursive: true });
8237
+ const nextLine = `${key}=${enabled ? 'true' : 'false'}`;
8238
+ let contents = '';
8239
+ try {
8240
+ contents = await fs.readFile(envPath, 'utf8');
8241
+ }
8242
+ catch {
8243
+ await fs.writeFile(envPath, `${nextLine}\n`, { encoding: 'utf8', mode: 0o600 });
8244
+ return;
8245
+ }
8246
+ const pattern = new RegExp(`(^|\\n)[ \\t#]*${escapeRegExp(key)}\\s*=.*(?=\\r?\\n|$)`);
8247
+ const nextContents = pattern.test(contents)
8248
+ ? contents.replace(pattern, (_match, prefix) => `${prefix}${nextLine}`)
8249
+ : `${contents}${contents.endsWith('\n') || contents.length === 0 ? '' : '\n'}${nextLine}\n`;
8250
+ await fs.writeFile(envPath, nextContents, { encoding: 'utf8', mode: 0o600 });
8251
+ }
8252
+ function escapeRegExp(value) {
8253
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
8254
+ }
8093
8255
  function formatRequirementsMessage(locale, requirements) {
8094
8256
  const lines = [t(locale, 'requirements_title')];
8095
8257
  if (!requirements) {
package/dist/i18n.d.ts CHANGED
@@ -187,6 +187,8 @@ declare const MESSAGES: {
187
187
  readonly auth_repair_reverted: "Restored previous auth. The candidate still needs repair.";
188
188
  readonly auth_candidate_deleted: "Deleted auth candidate: {value}";
189
189
  readonly auth_candidate_deleted_short: "Auth deleted";
190
+ readonly auth_candidate_auto_deleted: "Auto-deleted unrecoverable auth candidate: {value}";
191
+ readonly auth_pool_summary: "Auth pool: total seen {total}, alive {alive}, invalid-deleted {deleted}.";
190
192
  readonly auth_delete_current_restarted: "The deleted candidate was current, so Codex app-server was restarted without it.";
191
193
  readonly auth_no_candidates: "No auth candidates found. Expected files like auth.json_personal in the auth dir.";
192
194
  readonly auth_choice_expired: "This auth list is no longer active";
@@ -195,6 +197,8 @@ declare const MESSAGES: {
195
197
  readonly auth_switching: "Switching Codex auth: {from} -> {to}...";
196
198
  readonly auth_switch_done: "Codex auth switched: {from} -> {to}.";
197
199
  readonly auth_switch_validation_failed: "Selected auth failed validation: {error}. Marked {value} for login repair.";
200
+ readonly auth_switch_validation_auto_deleted: "Selected auth failed validation: {error}. Auto-deleted {value}.";
201
+ readonly auth_switch_validation_auto_deleted_quiet: "Selected auth failed validation and was auto-deleted.";
198
202
  readonly auth_switch_validation_reverted: "Restored the previous auth after the failed switch.";
199
203
  readonly auth_recovered_newer_candidate: "Recovered a newer same-account credential for {value} from another Codex home before reload.";
200
204
  readonly auth_refresh_all_confirm_short: "Review refresh all risk first.";
@@ -211,8 +215,10 @@ declare const MESSAGES: {
211
215
  readonly auth_refresh_all_skipped: "Skipped non-ChatGPT/invalid candidates: {value}";
212
216
  readonly auth_refresh_all_failed: "Failed: {value}";
213
217
  readonly auth_auto_switching: "Codex auth problem detected ({error}). Switching: {from} -> {to}...";
218
+ readonly auth_auto_switching_quiet: "Codex auth problem detected ({error}). Selecting another maintained candidate...";
214
219
  readonly auth_auto_recovered_current: "Codex auth problem detected ({error}). Recovered a newer same-account credential for {value} and restarted Codex app-server.";
215
220
  readonly auth_auto_done: "Auto-switched Codex auth: {from} -> {to}.";
221
+ readonly auth_auto_done_quiet: "Codex auth problem handled with another maintained candidate.";
216
222
  readonly auth_auto_retrying: "Retrying the same request with the new auth...";
217
223
  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.";
218
224
  readonly auth_auto_no_candidate: "Codex auth problem detected ({error}), but no unused auth candidate is available.";
@@ -263,6 +269,8 @@ declare const MESSAGES: {
263
269
  readonly button_auth_filter_all: "All";
264
270
  readonly button_auth_filter_enabled: "Enabled";
265
271
  readonly button_auth_filter_attention: "Attention";
272
+ readonly button_config_auth_auto_delete_on: "Auto-delete on";
273
+ readonly button_config_auth_auto_delete_off: "Auto-delete off";
266
274
  readonly another_turn_running: "Another turn is already running. Use /interrupt, /takeover, /queue, or wait.";
267
275
  readonly working: "Working...";
268
276
  readonly usage_open: "Usage: /open <n>";
@@ -593,6 +601,11 @@ declare const MESSAGES: {
593
601
  readonly features_empty: "No features found.";
594
602
  readonly config_title: "Config summary";
595
603
  readonly config_layers: "Config layers: {count}";
604
+ readonly config_foxclaw_title: "FoxClaw runtime config";
605
+ readonly config_auth_auto_delete_needs_repair: "Auto-delete unrecoverable auth candidates: {value}";
606
+ readonly config_auth_auto_delete_updated: "Auto-delete unrecoverable auth candidates set to: {value}";
607
+ readonly config_auth_auto_delete_usage: "Usage: /config auth_auto_delete <on|off>";
608
+ readonly config_env_update_failed: "Runtime setting changed, but updating {value} failed: {error}";
596
609
  readonly requirements_title: "Config requirements";
597
610
  readonly requirements_empty: "No config requirements are configured.";
598
611
  readonly provider_title: "Model provider capabilities";
@@ -862,6 +875,8 @@ declare const MESSAGES: {
862
875
  readonly auth_repair_reverted: "已恢复之前的 auth。该候选仍需要修复。";
863
876
  readonly auth_candidate_deleted: "已删除 auth 候选:{value}";
864
877
  readonly auth_candidate_deleted_short: "auth 已删除";
878
+ readonly auth_candidate_auto_deleted: "已自动剔除无法恢复的 auth 候选:{value}";
879
+ readonly auth_pool_summary: "auth 池:历史 {total},存活 {alive},因失效剔除 {deleted}。";
865
880
  readonly auth_delete_current_restarted: "被删除的候选是当前 auth,已在删除后重启 Codex app-server。";
866
881
  readonly auth_no_candidates: "没有找到 auth 候选文件。请在 auth 目录中放置类似 auth.json_personal 的文件。";
867
882
  readonly auth_choice_expired: "这个 auth 列表已经不再有效";
@@ -870,6 +885,8 @@ declare const MESSAGES: {
870
885
  readonly auth_switching: "正在切换 Codex auth:{from} -> {to}...";
871
886
  readonly auth_switch_done: "Codex auth 已切换:{from} -> {to}。";
872
887
  readonly auth_switch_validation_failed: "选中的 auth 验证失败:{error}。已将 {value} 标记为需要登录修复。";
888
+ readonly auth_switch_validation_auto_deleted: "选中的 auth 验证失败:{error}。已自动剔除 {value}。";
889
+ readonly auth_switch_validation_auto_deleted_quiet: "选中的 auth 验证失败,已自动剔除。";
873
890
  readonly auth_switch_validation_reverted: "已在切换失败后恢复到之前的 auth。";
874
891
  readonly auth_recovered_newer_candidate: "重载前已从其他 Codex home 恢复 {value} 的同账号较新凭据。";
875
892
  readonly auth_refresh_all_confirm_short: "请先确认刷新全部风险。";
@@ -886,8 +903,10 @@ declare const MESSAGES: {
886
903
  readonly auth_refresh_all_skipped: "已跳过非 ChatGPT/无效候选:{value}";
887
904
  readonly auth_refresh_all_failed: "失败:{value}";
888
905
  readonly auth_auto_switching: "检测到 Codex auth 问题({error}),正在切换:{from} -> {to}...";
906
+ readonly auth_auto_switching_quiet: "检测到 Codex auth 问题({error}),正在选择另一个维护中的候选...";
889
907
  readonly auth_auto_recovered_current: "检测到 Codex auth 问题({error}),已为 {value} 恢复同账号较新凭据并重启 Codex app-server。";
890
908
  readonly auth_auto_done: "已自动切换 Codex auth:{from} -> {to}。";
909
+ readonly auth_auto_done_quiet: "Codex auth 问题已用另一个维护中的候选处理。";
891
910
  readonly auth_auto_retrying: "正在用新的 auth 重试同一条请求...";
892
911
  readonly auth_auto_retry_thread_missing: "Auth 已切换,但原线程已经不可用({threadId})。已停止重试,避免创建重复 session。";
893
912
  readonly auth_auto_no_candidate: "检测到 Codex auth 问题({error}),但没有可用的未失败候选 auth。";
@@ -938,6 +957,8 @@ declare const MESSAGES: {
938
957
  readonly button_auth_filter_all: "全部";
939
958
  readonly button_auth_filter_enabled: "已启用";
940
959
  readonly button_auth_filter_attention: "需关注";
960
+ readonly button_config_auth_auto_delete_on: "开启自动剔除";
961
+ readonly button_config_auth_auto_delete_off: "关闭自动剔除";
941
962
  readonly another_turn_running: "已经有一个回复在进行中。请先等待,或使用 /interrupt、/takeover、/queue。";
942
963
  readonly working: "处理中...";
943
964
  readonly usage_open: "用法:/open <编号>";
@@ -1268,6 +1289,11 @@ declare const MESSAGES: {
1268
1289
  readonly features_empty: "没有找到功能开关。";
1269
1290
  readonly config_title: "配置摘要";
1270
1291
  readonly config_layers: "配置层数:{count}";
1292
+ readonly config_foxclaw_title: "FoxClaw 运行时配置";
1293
+ readonly config_auth_auto_delete_needs_repair: "自动剔除无法恢复的 auth 候选:{value}";
1294
+ readonly config_auth_auto_delete_updated: "自动剔除无法恢复的 auth 候选已设置为:{value}";
1295
+ readonly config_auth_auto_delete_usage: "用法:/config auth_auto_delete <on|off>";
1296
+ readonly config_env_update_failed: "运行时设置已改变,但更新 {value} 失败:{error}";
1271
1297
  readonly requirements_title: "配置要求";
1272
1298
  readonly requirements_empty: "当前没有配置要求。";
1273
1299
  readonly provider_title: "模型供应商能力";
package/dist/i18n.js CHANGED
@@ -185,6 +185,8 @@ const MESSAGES = {
185
185
  auth_repair_reverted: 'Restored previous auth. The candidate still needs repair.',
186
186
  auth_candidate_deleted: 'Deleted auth candidate: {value}',
187
187
  auth_candidate_deleted_short: 'Auth deleted',
188
+ auth_candidate_auto_deleted: 'Auto-deleted unrecoverable auth candidate: {value}',
189
+ auth_pool_summary: 'Auth pool: total seen {total}, alive {alive}, invalid-deleted {deleted}.',
188
190
  auth_delete_current_restarted: 'The deleted candidate was current, so Codex app-server was restarted without it.',
189
191
  auth_no_candidates: 'No auth candidates found. Expected files like auth.json_personal in the auth dir.',
190
192
  auth_choice_expired: 'This auth list is no longer active',
@@ -193,6 +195,8 @@ const MESSAGES = {
193
195
  auth_switching: 'Switching Codex auth: {from} -> {to}...',
194
196
  auth_switch_done: 'Codex auth switched: {from} -> {to}.',
195
197
  auth_switch_validation_failed: 'Selected auth failed validation: {error}. Marked {value} for login repair.',
198
+ auth_switch_validation_auto_deleted: 'Selected auth failed validation: {error}. Auto-deleted {value}.',
199
+ auth_switch_validation_auto_deleted_quiet: 'Selected auth failed validation and was auto-deleted.',
196
200
  auth_switch_validation_reverted: 'Restored the previous auth after the failed switch.',
197
201
  auth_recovered_newer_candidate: 'Recovered a newer same-account credential for {value} from another Codex home before reload.',
198
202
  auth_refresh_all_confirm_short: 'Review refresh all risk first.',
@@ -209,8 +213,10 @@ const MESSAGES = {
209
213
  auth_refresh_all_skipped: 'Skipped non-ChatGPT/invalid candidates: {value}',
210
214
  auth_refresh_all_failed: 'Failed: {value}',
211
215
  auth_auto_switching: 'Codex auth problem detected ({error}). Switching: {from} -> {to}...',
216
+ auth_auto_switching_quiet: 'Codex auth problem detected ({error}). Selecting another maintained candidate...',
212
217
  auth_auto_recovered_current: 'Codex auth problem detected ({error}). Recovered a newer same-account credential for {value} and restarted Codex app-server.',
213
218
  auth_auto_done: 'Auto-switched Codex auth: {from} -> {to}.',
219
+ auth_auto_done_quiet: 'Codex auth problem handled with another maintained candidate.',
214
220
  auth_auto_retrying: 'Retrying the same request with the new auth...',
215
221
  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.',
216
222
  auth_auto_no_candidate: 'Codex auth problem detected ({error}), but no unused auth candidate is available.',
@@ -261,6 +267,8 @@ const MESSAGES = {
261
267
  button_auth_filter_all: 'All',
262
268
  button_auth_filter_enabled: 'Enabled',
263
269
  button_auth_filter_attention: 'Attention',
270
+ button_config_auth_auto_delete_on: 'Auto-delete on',
271
+ button_config_auth_auto_delete_off: 'Auto-delete off',
264
272
  another_turn_running: 'Another turn is already running. Use /interrupt, /takeover, /queue, or wait.',
265
273
  working: 'Working...',
266
274
  usage_open: 'Usage: /open <n>',
@@ -591,6 +599,11 @@ const MESSAGES = {
591
599
  features_empty: 'No features found.',
592
600
  config_title: 'Config summary',
593
601
  config_layers: 'Config layers: {count}',
602
+ config_foxclaw_title: 'FoxClaw runtime config',
603
+ config_auth_auto_delete_needs_repair: 'Auto-delete unrecoverable auth candidates: {value}',
604
+ config_auth_auto_delete_updated: 'Auto-delete unrecoverable auth candidates set to: {value}',
605
+ config_auth_auto_delete_usage: 'Usage: /config auth_auto_delete <on|off>',
606
+ config_env_update_failed: 'Runtime setting changed, but updating {value} failed: {error}',
594
607
  requirements_title: 'Config requirements',
595
608
  requirements_empty: 'No config requirements are configured.',
596
609
  provider_title: 'Model provider capabilities',
@@ -860,6 +873,8 @@ const MESSAGES = {
860
873
  auth_repair_reverted: '已恢复之前的 auth。该候选仍需要修复。',
861
874
  auth_candidate_deleted: '已删除 auth 候选:{value}',
862
875
  auth_candidate_deleted_short: 'auth 已删除',
876
+ auth_candidate_auto_deleted: '已自动剔除无法恢复的 auth 候选:{value}',
877
+ auth_pool_summary: 'auth 池:历史 {total},存活 {alive},因失效剔除 {deleted}。',
863
878
  auth_delete_current_restarted: '被删除的候选是当前 auth,已在删除后重启 Codex app-server。',
864
879
  auth_no_candidates: '没有找到 auth 候选文件。请在 auth 目录中放置类似 auth.json_personal 的文件。',
865
880
  auth_choice_expired: '这个 auth 列表已经不再有效',
@@ -868,6 +883,8 @@ const MESSAGES = {
868
883
  auth_switching: '正在切换 Codex auth:{from} -> {to}...',
869
884
  auth_switch_done: 'Codex auth 已切换:{from} -> {to}。',
870
885
  auth_switch_validation_failed: '选中的 auth 验证失败:{error}。已将 {value} 标记为需要登录修复。',
886
+ auth_switch_validation_auto_deleted: '选中的 auth 验证失败:{error}。已自动剔除 {value}。',
887
+ auth_switch_validation_auto_deleted_quiet: '选中的 auth 验证失败,已自动剔除。',
871
888
  auth_switch_validation_reverted: '已在切换失败后恢复到之前的 auth。',
872
889
  auth_recovered_newer_candidate: '重载前已从其他 Codex home 恢复 {value} 的同账号较新凭据。',
873
890
  auth_refresh_all_confirm_short: '请先确认刷新全部风险。',
@@ -884,8 +901,10 @@ const MESSAGES = {
884
901
  auth_refresh_all_skipped: '已跳过非 ChatGPT/无效候选:{value}',
885
902
  auth_refresh_all_failed: '失败:{value}',
886
903
  auth_auto_switching: '检测到 Codex auth 问题({error}),正在切换:{from} -> {to}...',
904
+ auth_auto_switching_quiet: '检测到 Codex auth 问题({error}),正在选择另一个维护中的候选...',
887
905
  auth_auto_recovered_current: '检测到 Codex auth 问题({error}),已为 {value} 恢复同账号较新凭据并重启 Codex app-server。',
888
906
  auth_auto_done: '已自动切换 Codex auth:{from} -> {to}。',
907
+ auth_auto_done_quiet: 'Codex auth 问题已用另一个维护中的候选处理。',
889
908
  auth_auto_retrying: '正在用新的 auth 重试同一条请求...',
890
909
  auth_auto_retry_thread_missing: 'Auth 已切换,但原线程已经不可用({threadId})。已停止重试,避免创建重复 session。',
891
910
  auth_auto_no_candidate: '检测到 Codex auth 问题({error}),但没有可用的未失败候选 auth。',
@@ -936,6 +955,8 @@ const MESSAGES = {
936
955
  button_auth_filter_all: '全部',
937
956
  button_auth_filter_enabled: '已启用',
938
957
  button_auth_filter_attention: '需关注',
958
+ button_config_auth_auto_delete_on: '开启自动剔除',
959
+ button_config_auth_auto_delete_off: '关闭自动剔除',
939
960
  another_turn_running: '已经有一个回复在进行中。请先等待,或使用 /interrupt、/takeover、/queue。',
940
961
  working: '处理中...',
941
962
  usage_open: '用法:/open <编号>',
@@ -1266,6 +1287,11 @@ const MESSAGES = {
1266
1287
  features_empty: '没有找到功能开关。',
1267
1288
  config_title: '配置摘要',
1268
1289
  config_layers: '配置层数:{count}',
1290
+ config_foxclaw_title: 'FoxClaw 运行时配置',
1291
+ config_auth_auto_delete_needs_repair: '自动剔除无法恢复的 auth 候选:{value}',
1292
+ config_auth_auto_delete_updated: '自动剔除无法恢复的 auth 候选已设置为:{value}',
1293
+ config_auth_auto_delete_usage: '用法:/config auth_auto_delete <on|off>',
1294
+ config_env_update_failed: '运行时设置已改变,但更新 {value} 失败:{error}',
1269
1295
  requirements_title: '配置要求',
1270
1296
  requirements_empty: '当前没有配置要求。',
1271
1297
  provider_title: '模型供应商能力',