codex-slot 0.1.27 → 0.1.28

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
@@ -81,6 +81,7 @@ function registerAccountCommands(program) {
81
81
  function registerRuntimeCommands(program) {
82
82
  program
83
83
  .command("status")
84
+ .alias("usage")
84
85
  .description((0, text_1.bi)("刷新并查看所有已录入账号或工作空间的最新额度", "Refresh usage for all managed slots"))
85
86
  .option("--no-interactive", (0, text_1.bi)("仅输出状态表,不进入交互式切换", "Print only"))
86
87
  .action(async (options) => {
@@ -325,53 +325,6 @@ function findTableHeaderOffset(content, header) {
325
325
  }
326
326
  return null;
327
327
  }
328
- /**
329
- * 查找指定偏移之前最近的表头,供恢复原有表块位置时作为后备锚点。
330
- *
331
- * @param content 当前 `config.toml` 内容。
332
- * @param offset 截止偏移。
333
- * @returns 最近的表头文本;未命中返回 `null`。
334
- */
335
- function findPreviousTableHeaderBeforeOffset(content, offset) {
336
- const lines = content.split(/\r?\n/);
337
- let currentOffset = 0;
338
- let previousHeader = null;
339
- for (const line of lines) {
340
- const lineEnd = currentOffset + line.length;
341
- const trimmed = line.trim();
342
- if (currentOffset >= offset) {
343
- break;
344
- }
345
- if (trimmed.startsWith("[") && !trimmed.startsWith("[[") && !trimmed.startsWith("#")) {
346
- previousHeader = trimmed;
347
- }
348
- currentOffset = lineEnd + (content.slice(lineEnd, lineEnd + 2) === "\r\n" ? 2 : 1);
349
- }
350
- return previousHeader;
351
- }
352
- /**
353
- * 查找指定偏移之后的首个表头,供恢复原有表块位置时作为优先锚点。
354
- *
355
- * @param content 当前 `config.toml` 内容。
356
- * @param offset 起始偏移。
357
- * @returns 首个后续表头文本;未命中返回 `null`。
358
- */
359
- function findNextTableHeaderAfterOffset(content, offset) {
360
- const lines = content.split(/\r?\n/);
361
- let currentOffset = 0;
362
- for (const line of lines) {
363
- const lineEnd = currentOffset + line.length;
364
- const trimmed = line.trim();
365
- if (currentOffset >= offset &&
366
- trimmed.startsWith("[") &&
367
- !trimmed.startsWith("[[") &&
368
- !trimmed.startsWith("#")) {
369
- return trimmed;
370
- }
371
- currentOffset = lineEnd + (content.slice(lineEnd, lineEnd + 2) === "\r\n" ? 2 : 1);
372
- }
373
- return null;
374
- }
375
328
  /**
376
329
  * 清理文本中的所有 `model_provider` 配置块,确保每次接管都以单一稳定块重新写入。
377
330
  *
@@ -414,6 +367,35 @@ function stripAllManagedBlocks(content) {
414
367
  const withoutProviderBlock = stripMarkedBlocks(content, PROVIDER_BLOCK_START_MARKER, PROVIDER_BLOCK_END_MARKER);
415
368
  return stripMarkedBlocks(withoutProviderBlock, MODEL_PROVIDER_START_MARKER, MODEL_PROVIDER_END_MARKER);
416
369
  }
370
+ /**
371
+ * 清理文本中的所有 `model_provider = "cslot"` 根级配置,避免 stop 后残留 cslot 入口。
372
+ *
373
+ * @param content 当前 `config.toml` 内容。
374
+ * @returns 移除后的文本内容。
375
+ */
376
+ function removeAllCslotModelProviderLines(content) {
377
+ let nextContent = content;
378
+ while (true) {
379
+ const range = findModelProviderLine(nextContent);
380
+ if (!range) {
381
+ return nextContent;
382
+ }
383
+ if (range.value.includes('model_provider = "cslot"')) {
384
+ nextContent = nextContent.slice(0, range.start) + nextContent.slice(range.end);
385
+ continue;
386
+ }
387
+ return nextContent;
388
+ }
389
+ }
390
+ /**
391
+ * 在 stop 阶段只做去 cslot 化,移除所有受管块与残留的 cslot provider 配置。
392
+ *
393
+ * @param content 当前 `config.toml` 内容。
394
+ * @returns 已去除 cslot 接管痕迹的文本。
395
+ */
396
+ function removeAllCslotManagedConfig(content) {
397
+ return removeAllProviderSections(removeAllCslotModelProviderLines(stripAllManagedBlocks(content)));
398
+ }
417
399
  /**
418
400
  * 查找根级配置区的尾部插入点。
419
401
  *
@@ -487,86 +469,6 @@ function appendBlockToEnd(content, block, eol) {
487
469
  }
488
470
  return `${trimmed}${eol}${eol}${block}${eol}`;
489
471
  }
490
- /**
491
- * 将表块尽量插回原有相邻表头附近;若锚点已不存在,则退回文件尾部追加。
492
- *
493
- * @param content 当前 `config.toml` 内容。
494
- * @param block 待插入的表块。
495
- * @param eol 目标换行符。
496
- * @param preferredNextTableHeader 原始后续表头锚点,命中时优先插到该表之前。
497
- * @param preferredPreviousTableHeader 原始前驱表头锚点,当前者失效时插到该表之后。
498
- * @returns 插入后的完整文本。
499
- */
500
- function insertTableBlock(content, block, eol, preferredNextTableHeader, preferredPreviousTableHeader) {
501
- if (preferredNextTableHeader) {
502
- const nextOffset = findTableHeaderOffset(content, preferredNextTableHeader);
503
- if (nextOffset !== null) {
504
- return insertBlockBetween(content.slice(0, nextOffset), block, content.slice(nextOffset), eol);
505
- }
506
- }
507
- if (preferredPreviousTableHeader) {
508
- const previousRange = findTableSectionRange(content, preferredPreviousTableHeader);
509
- if (previousRange) {
510
- return insertBlockBetween(content.slice(0, previousRange.end), block, content.slice(previousRange.end), eol);
511
- }
512
- }
513
- return appendBlockToEnd(content, block, eol);
514
- }
515
- /**
516
- * 解析当前目标文件对应的上一轮接管快照。
517
- *
518
- * @param targetFile 当前准备接管或恢复的 `config.toml` 路径。
519
- * @returns 命中同一目标文件时返回上一轮快照;否则返回 `null`。
520
- */
521
- function resolveManagedStateForTarget(targetFile) {
522
- const managedState = (0, state_1.getManagedCodexConfigState)();
523
- if (!managedState || managedState.target_file !== targetFile) {
524
- return null;
525
- }
526
- return managedState;
527
- }
528
- /**
529
- * 基于当前未受管的配置文本与上一轮快照,生成本轮接管所需的最小恢复快照。
530
- *
531
- * 业务规则:
532
- * 1. 优先记录当前文件里实际存在的原始 `model_provider` 与 `[model_providers.cslot]`。
533
- * 2. 若当前文件只剩残留受管块,允许继承上一轮快照中的原始片段。
534
- * 3. 仅保存 cslot 自己声明所有权的两块配置及其锚点,不保存整文件内容。
535
- *
536
- * @param targetFile 当前准备接管的 `config.toml` 路径。
537
- * @param strippedCurrent 已移除受管标记块后的配置文本。
538
- * @param previousManagedState 同一目标文件的上一轮快照;不存在时传 `null`。
539
- * @returns 本轮接管后用于 stop 恢复的快照。
540
- */
541
- function buildManagedSnapshot(targetFile, strippedCurrent, previousManagedState) {
542
- const originalModelProviderLine = findModelProviderLine(strippedCurrent);
543
- const originalProviderSection = findProviderSectionRange(strippedCurrent);
544
- return {
545
- target_file: targetFile,
546
- original_model_provider_block: originalModelProviderLine?.value ??
547
- previousManagedState?.original_model_provider_block ??
548
- null,
549
- original_model_provider_next_table_header: (originalModelProviderLine
550
- ? findNextTableHeaderAfterOffset(strippedCurrent, originalModelProviderLine.end)
551
- : null) ??
552
- previousManagedState?.original_model_provider_next_table_header ??
553
- null,
554
- original_cslot_provider_block: (originalProviderSection ? sanitizeLegacyCslotProviderBlock(originalProviderSection.value) : null) ??
555
- (previousManagedState?.original_cslot_provider_block
556
- ? sanitizeLegacyCslotProviderBlock(previousManagedState.original_cslot_provider_block)
557
- : null),
558
- original_cslot_provider_previous_table_header: (originalProviderSection
559
- ? findPreviousTableHeaderBeforeOffset(strippedCurrent, originalProviderSection.start)
560
- : null) ??
561
- previousManagedState?.original_cslot_provider_previous_table_header ??
562
- null,
563
- original_cslot_provider_next_table_header: (originalProviderSection
564
- ? findNextTableHeaderAfterOffset(strippedCurrent, originalProviderSection.end)
565
- : null) ??
566
- previousManagedState?.original_cslot_provider_next_table_header ??
567
- null
568
- };
569
- }
570
472
  /**
571
473
  * 将 cslot 需要的 provider 配置写入指定 `config.toml`,并保存恢复快照。
572
474
  *
@@ -579,21 +481,19 @@ function applyManagedCodexConfig(targetPathOrDir, options) {
579
481
  const rawTarget = targetPathOrDir ? (0, config_1.expandHome)(targetPathOrDir) : getDefaultCodexConfigPath();
580
482
  const targetFile = rawTarget.endsWith(".toml") ? rawTarget : node_path_1.default.join(rawTarget, "config.toml");
581
483
  const current = node_fs_1.default.existsSync(targetFile) ? node_fs_1.default.readFileSync(targetFile, "utf8") : "";
582
- const previousManagedState = resolveManagedStateForTarget(targetFile);
583
- const strippedCurrent = stripAllManagedBlocks(current);
584
- const eol = detectEol(strippedCurrent);
585
- const snapshot = buildManagedSnapshot(targetFile, strippedCurrent, previousManagedState);
484
+ const cleanedCurrent = removeAllCslotManagedConfig(current);
485
+ const eol = detectEol(cleanedCurrent);
586
486
  const config = options?.config ?? (0, config_1.loadConfig)();
587
487
  const managedModelProviderBlock = buildManagedModelProviderBlock(eol);
588
488
  const managedProviderBlock = buildManagedProviderBlock(eol, config);
589
- const cleanedBaseContent = removeAllProviderSections(removeAllModelProviderLines(strippedCurrent));
590
- let nextContent = insertRootBlock(cleanedBaseContent, managedModelProviderBlock, eol, snapshot.original_model_provider_next_table_header);
489
+ const cleanedBaseContent = removeAllProviderSections(removeAllModelProviderLines(cleanedCurrent));
490
+ let nextContent = insertRootBlock(cleanedBaseContent, managedModelProviderBlock, eol);
591
491
  nextContent = appendBlockToEnd(nextContent, managedProviderBlock, eol);
592
492
  if (!nextContent.endsWith(eol)) {
593
493
  nextContent = `${nextContent}${eol}`;
594
494
  }
595
495
  writeFileAtomic(targetFile, nextContent);
596
- (0, state_1.setManagedCodexConfigState)(snapshot);
496
+ (0, state_1.setManagedCodexConfigState)({ target_file: targetFile });
597
497
  if (!options?.silent) {
598
498
  console.log((0, text_1.bi)(`已写入: ${targetFile}`, `Written to: ${targetFile}`));
599
499
  console.log(`base_url=http://${config.server.host}:${config.server.port}/v1`);
@@ -609,25 +509,13 @@ function applyManagedCodexConfig(targetPathOrDir, options) {
609
509
  */
610
510
  function deactivateManagedCodexConfig() {
611
511
  const managedState = (0, state_1.getManagedCodexConfigState)();
612
- if (!managedState) {
613
- return null;
614
- }
615
- const targetFile = managedState.target_file;
512
+ const targetFile = managedState?.target_file ?? getDefaultCodexConfigPath();
616
513
  if (!node_fs_1.default.existsSync(targetFile)) {
617
514
  (0, state_1.clearManagedCodexConfigState)();
618
515
  return null;
619
516
  }
620
517
  const current = node_fs_1.default.readFileSync(targetFile, "utf8");
621
- const eol = detectEol(current);
622
- let restored = sanitizeExistingCslotProviderSection(stripAllManagedBlocks(current));
623
- const existingModelProviderLine = findModelProviderLine(restored);
624
- if (!existingModelProviderLine && managedState.original_model_provider_block) {
625
- restored = insertRootBlock(restored, managedState.original_model_provider_block, eol, managedState.original_model_provider_next_table_header);
626
- }
627
- const existingProviderSection = findProviderSectionRange(restored);
628
- if (!existingProviderSection && managedState.original_cslot_provider_block) {
629
- restored = insertTableBlock(restored, managedState.original_cslot_provider_block, eol, managedState.original_cslot_provider_next_table_header, managedState.original_cslot_provider_previous_table_header);
630
- }
518
+ const restored = removeAllCslotManagedConfig(sanitizeExistingCslotProviderSection(current));
631
519
  writeFileAtomic(targetFile, restored);
632
520
  (0, state_1.clearManagedCodexConfigState)();
633
521
  return targetFile;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-slot",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "本地 Codex 多账号切换与状态管理工具",
5
5
  "type": "commonjs",
6
6
  "main": "dist/cli.js",