auriga-cli 1.20.5 → 1.20.6

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/README.md CHANGED
@@ -119,7 +119,7 @@ Supports both project and global installation scopes.
119
119
 
120
120
  ### Plugins
121
121
 
122
- Installs selected plugins for Claude Code, Codex, or both. Claude Code uses `claude plugins install` and honors `--scope project|user`; Codex uses `codex plugin marketplace add` and enables selected plugins in `~/.codex/config.toml`.
122
+ Installs selected plugins for Claude Code, Codex, or both. Claude Code uses `claude plugins install` and honors `--scope project|user`; Codex uses `codex plugin marketplace add/upgrade` (the right one is picked by reading `~/.codex/config.toml`) and enables selected plugins in `~/.codex/config.toml`.
123
123
 
124
124
  Examples:
125
125
 
package/README.zh-CN.md CHANGED
@@ -119,7 +119,7 @@ npx auriga-cli
119
119
 
120
120
  ### Plugins
121
121
 
122
- 可以把选中的插件安装到 Claude Code、Codex 或两者都装。Claude Code 路径使用 `claude plugins install`,并遵守 `--scope project|user`;Codex 路径使用 `codex plugin marketplace add`,并在 `~/.codex/config.toml` 里启用选中的插件。
122
+ 可以把选中的插件安装到 Claude Code、Codex 或两者都装。Claude Code 路径使用 `claude plugins install`,并遵守 `--scope project|user`;Codex 路径根据 `~/.codex/config.toml` 中是否已注册同名 marketplace 自动选择 `codex plugin marketplace add` 或 `upgrade`,并在 `~/.codex/config.toml` 里启用选中的插件。
123
123
 
124
124
  示例:
125
125
 
package/dist/catalog.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-05-15T04:24:39.164Z",
2
+ "generatedAt": "2026-05-15T08:06:41.771Z",
3
3
  "workflowSkills": [
4
4
  {
5
5
  "name": "planning-with-files",
package/dist/plugins.js CHANGED
@@ -422,18 +422,30 @@ function runPostInstallMigration(pluginName, opts, runtimes) {
422
422
  migrateLegacyNotifyConfig(opts);
423
423
  }
424
424
  }
425
- function codexMarketplaceAddCommand(packageRoot) {
426
- if (process.env.DEV === "1") {
427
- return `codex plugin marketplace add ${shellQuote(packageRoot)}`;
428
- }
429
- return "codex plugin marketplace add https://github.com/Ben2pc/auriga-cli.git";
425
+ // Source string Codex CLI records in `~/.codex/config.toml`
426
+ // `[marketplaces.<name>].source` after `marketplace add` succeeds. Used for
427
+ // exact-string compare against the registered marketplace's source to detect
428
+ // same-name-different-source hijack attempts before running `upgrade`.
429
+ function codexLocalMarketplaceSource(packageRoot) {
430
+ if (process.env.DEV === "1")
431
+ return packageRoot;
432
+ return "https://github.com/Ben2pc/auriga-cli.git";
430
433
  }
431
- function codexExternalMarketplaceAddCommand(source) {
434
+ function codexExternalMarketplaceSource(source) {
432
435
  // `source` is validated by validateExtraPluginConfigs against
433
436
  // MARKETPLACE_SOURCE_RE (alphanumerics + `._/-`) — no shell metachars
434
- // can reach this string. URL form deliberately mirrors
435
- // codexMarketplaceAddCommand's hardcoded production branch.
436
- return `codex plugin marketplace add https://github.com/${source}.git`;
437
+ // can reach this string.
438
+ return `https://github.com/${source}.git`;
439
+ }
440
+ function codexMarketplaceAddCommand(packageRoot) {
441
+ const source = codexLocalMarketplaceSource(packageRoot);
442
+ // DEV-mode local paths may contain spaces; URLs need no shell quoting.
443
+ return process.env.DEV === "1"
444
+ ? `codex plugin marketplace add ${shellQuote(source)}`
445
+ : `codex plugin marketplace add ${source}`;
446
+ }
447
+ function codexExternalMarketplaceAddCommand(source) {
448
+ return `codex plugin marketplace add ${codexExternalMarketplaceSource(source)}`;
437
449
  }
438
450
  function codexMarketplaceUpgradeCommand(marketplaceName) {
439
451
  return `codex plugin marketplace upgrade ${shellQuote(marketplaceName)}`;
@@ -449,13 +461,36 @@ function commandErrorText(error) {
449
461
  parts.push(String(withOutput.stderr));
450
462
  return parts.join("\n");
451
463
  }
452
- function escapeRegex(value) {
453
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
454
- }
455
- function isCodexMarketplaceAlreadyAdded(error, marketplaceName) {
456
- const text = commandErrorText(error);
457
- const marketplacePattern = new RegExp(`marketplace ['"]?${escapeRegex(marketplaceName)}['"]? is already added`, "i");
458
- return marketplacePattern.test(text);
464
+ // Codex CLI records every added marketplace as `[marketplaces.<name>]` in
465
+ // `~/.codex/config.toml` with a `source` field. Returns that stored source
466
+ // for an exact-string compare against the source we would `add`, or null
467
+ // when the marketplace isn't registered or the entry is malformed.
468
+ //
469
+ // Why config.toml is the authoritative signal: Codex's `add` command is
470
+ // silently idempotent for an already-added marketplace (exit 0, prints to
471
+ // stdout, no error), so we can't detect already-added by catching an
472
+ // `add` failure. The stored source also lets us guard against same-name
473
+ // hijack — a fork registered under our marketplace name would be invisible
474
+ // to a presence-only check.
475
+ function readCodexMarketplaceSource(marketplaceName) {
476
+ const configPath = path.join(codexHome(), "config.toml");
477
+ if (!fs.existsSync(configPath))
478
+ return null;
479
+ try {
480
+ const parsed = parseToml(fs.readFileSync(configPath, "utf-8"));
481
+ const marketplaces = parsed.marketplaces;
482
+ if (typeof marketplaces !== "object" || marketplaces === null || Array.isArray(marketplaces)) {
483
+ return null;
484
+ }
485
+ const entry = marketplaces[marketplaceName];
486
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
487
+ return null;
488
+ const source = entry.source;
489
+ return typeof source === "string" ? source : null;
490
+ }
491
+ catch {
492
+ return null;
493
+ }
459
494
  }
460
495
  function isCodexMarketplaceDifferentSource(error) {
461
496
  return /already added from a different source/i.test(commandErrorText(error));
@@ -616,34 +651,45 @@ function enableCodexPluginConfig(configPath, pluginKeys, needsPluginHooks) {
616
651
  const content = minimalContent ?? buildCodexPluginConfigToml(originalContent, configPath, pluginKeys, needsPluginHooks);
617
652
  atomicWriteFile(configPath, content.endsWith("\n") ? content : `${content}\n`);
618
653
  }
619
- async function addCodexMarketplaceWithRetry(marketplaceName, addCommand, opts, marketplaceExecOpts, failures) {
654
+ async function addCodexMarketplaceWithRetry(marketplaceName, addCommand, expectedSource, opts, marketplaceExecOpts, failures) {
655
+ const registeredSource = readCodexMarketplaceSource(marketplaceName);
656
+ if (registeredSource !== null) {
657
+ if (registeredSource !== expectedSource) {
658
+ // Supply-chain guard: a same-name marketplace pointing at a different
659
+ // source (e.g. a fork URL) must not be silently `upgrade`d — its
660
+ // content would be cached under our name and consumed downstream.
661
+ const msg = `Codex marketplace ${marketplaceName} is already added from a different source`;
662
+ log.error(`${msg}\n registered: ${registeredSource}\n expected: ${expectedSource}`);
663
+ failures.push(msg);
664
+ return;
665
+ }
666
+ try {
667
+ exec(codexMarketplaceUpgradeCommand(marketplaceName), marketplaceExecOpts);
668
+ log.ok(`Codex marketplace ${marketplaceName} upgraded`);
669
+ }
670
+ catch (e) {
671
+ // Surface the underlying upgrade error so a 6-month-out reader
672
+ // can tell apart ENOENT / network / auth / git failures.
673
+ log.error(`Failed to upgrade Codex marketplace: ${marketplaceName}\n${commandErrorText(e)}`);
674
+ failures.push(`codex marketplace ${marketplaceName}`);
675
+ }
676
+ return;
677
+ }
620
678
  try {
621
679
  exec(addCommand, marketplaceExecOpts);
622
680
  log.ok(`Codex marketplace ${marketplaceName} added`);
623
- return;
624
681
  }
625
682
  catch (e) {
626
683
  if (isCodexMarketplaceDifferentSource(e)) {
684
+ // Defense-in-depth: config.toml didn't claim the name but Codex CLI
685
+ // refused with "different source" — rare divergence between Codex
686
+ // internal state and config.toml.
627
687
  const msg = `Codex marketplace ${marketplaceName} is already added from a different source`;
628
688
  log.error(`${msg}\n${commandErrorText(e)}`);
629
689
  failures.push(msg);
630
690
  return;
631
691
  }
632
- if (isCodexMarketplaceAlreadyAdded(e, marketplaceName)) {
633
- try {
634
- exec(codexMarketplaceUpgradeCommand(marketplaceName), marketplaceExecOpts);
635
- log.ok(`Codex marketplace ${marketplaceName} upgraded`);
636
- return;
637
- }
638
- catch (upgradeErr) {
639
- // Surface the underlying upgrade error so a 6-month-out reader
640
- // can tell apart ENOENT / network / auth / git failures.
641
- log.error(`Failed to upgrade Codex marketplace: ${marketplaceName}\n${commandErrorText(upgradeErr)}`);
642
- failures.push(`codex marketplace ${marketplaceName}`);
643
- return;
644
- }
645
- }
646
- // Same: surface the add error rather than masking ENOENT / network / auth.
692
+ // Surface the add error rather than masking ENOENT / network / auth.
647
693
  log.error(`Failed to add Codex marketplace: ${marketplaceName}\n${commandErrorText(e)}`);
648
694
  failures.push(`codex marketplace ${marketplaceName}`);
649
695
  }
@@ -726,7 +772,7 @@ async function installCodexPlugins(packageRoot, opts) {
726
772
  ? { inherit: true }
727
773
  : undefined;
728
774
  if (localMarketplace) {
729
- await addCodexMarketplaceWithRetry(localMarketplace.name, codexMarketplaceAddCommand(packageRoot), opts, marketplaceExecOpts, failures);
775
+ await addCodexMarketplaceWithRetry(localMarketplace.name, codexMarketplaceAddCommand(packageRoot), codexLocalMarketplaceSource(packageRoot), opts, marketplaceExecOpts, failures);
730
776
  }
731
777
  // Dedupe external marketplaces by name — multiple plugins from the same
732
778
  // upstream share a single `marketplace add` call.
@@ -735,7 +781,7 @@ async function installCodexPlugins(packageRoot, opts) {
735
781
  uniqueExternalMarketplaces.set(p.marketplace.name, p.marketplace);
736
782
  }
737
783
  for (const mp of uniqueExternalMarketplaces.values()) {
738
- await addCodexMarketplaceWithRetry(mp.name, codexExternalMarketplaceAddCommand(mp.source), opts, marketplaceExecOpts, failures);
784
+ await addCodexMarketplaceWithRetry(mp.name, codexExternalMarketplaceAddCommand(mp.source), codexExternalMarketplaceSource(mp.source), opts, marketplaceExecOpts, failures);
739
785
  }
740
786
  if (failures.length === 0) {
741
787
  const localMarketplaceContentRoot = localMarketplace
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auriga-cli",
3
- "version": "1.20.5",
3
+ "version": "1.20.6",
4
4
  "description": "Interactive CLI to install Claude Code harness modules (Workflow, Skills, Recommended Skills, Plugins, Hooks)",
5
5
  "license": "MIT",
6
6
  "repository": {