@biffo/cli 0.266.2 → 0.267.1

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.
Files changed (2) hide show
  1. package/dist/index.js +627 -33
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -10237,8 +10237,8 @@ async function runOwnershipCheck(argv) {
10237
10237
  const { stdout } = await execa8("git", ["diff", "--cached", "--name-status"], { cwd: root });
10238
10238
  ({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
10239
10239
  if (messageFile) {
10240
- const { readFileSync: readFileSync32, existsSync: existsSync42 } = await import("fs");
10241
- if (existsSync42(messageFile)) commitMessage = readFileSync32(messageFile, "utf8");
10240
+ const { readFileSync: readFileSync32, existsSync: existsSync43 } = await import("fs");
10241
+ if (existsSync43(messageFile)) commitMessage = readFileSync32(messageFile, "utf8");
10242
10242
  }
10243
10243
  } else {
10244
10244
  const base = process.env["GITHUB_BASE_REF"] ?? args[0];
@@ -10728,13 +10728,420 @@ async function runPluginTerraformCheck() {
10728
10728
  }
10729
10729
 
10730
10730
  // src/scripts/check-plugin-tool-supply.ts
10731
- import { existsSync as existsSync39 } from "fs";
10731
+ import { existsSync as existsSync40 } from "fs";
10732
10732
  import { join as join43 } from "path";
10733
10733
  import { execa as execa12 } from "execa";
10734
10734
 
10735
10735
  // src/lib/plugin-tool-supply-audit.ts
10736
- import { readFileSync as readFileSync30, readdirSync as readdirSync18, statSync as statSync10 } from "fs";
10736
+ import { existsSync as existsSync39, readFileSync as readFileSync30, readdirSync as readdirSync18, statSync as statSync10 } from "fs";
10737
10737
  import { join as join42 } from "path";
10738
+
10739
+ // src/lib/openrouter-model-snapshot.ts
10740
+ var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
10741
+ var OPENROUTER_MODEL_IDS = [
10742
+ "ai21/jamba-large-1.7",
10743
+ "aion-labs/aion-2.0",
10744
+ "aion-labs/aion-3.0",
10745
+ "aion-labs/aion-3.0-mini",
10746
+ "aion-labs/aion-rp-llama-3.1-8b",
10747
+ "allenai/olmo-3-32b-think",
10748
+ "amazon/nova-2-lite-v1",
10749
+ "amazon/nova-lite-v1",
10750
+ "amazon/nova-micro-v1",
10751
+ "amazon/nova-premier-v1",
10752
+ "amazon/nova-pro-v1",
10753
+ "anthracite-org/magnum-v4-72b",
10754
+ "anthropic/claude-3-haiku",
10755
+ "anthropic/claude-fable-5",
10756
+ "anthropic/claude-fable-5:batch",
10757
+ "anthropic/claude-haiku-4.5",
10758
+ "anthropic/claude-haiku-4.5:batch",
10759
+ "anthropic/claude-opus-4",
10760
+ "anthropic/claude-opus-4.1",
10761
+ "anthropic/claude-opus-4.1:batch",
10762
+ "anthropic/claude-opus-4.5",
10763
+ "anthropic/claude-opus-4.5:batch",
10764
+ "anthropic/claude-opus-4.6",
10765
+ "anthropic/claude-opus-4.6:batch",
10766
+ "anthropic/claude-opus-4.7",
10767
+ "anthropic/claude-opus-4.7-fast",
10768
+ "anthropic/claude-opus-4.7:batch",
10769
+ "anthropic/claude-opus-4.8",
10770
+ "anthropic/claude-opus-4.8-fast",
10771
+ "anthropic/claude-opus-4.8:batch",
10772
+ "anthropic/claude-opus-5",
10773
+ "anthropic/claude-opus-5-fast",
10774
+ "anthropic/claude-opus-5:batch",
10775
+ "anthropic/claude-sonnet-4",
10776
+ "anthropic/claude-sonnet-4.5",
10777
+ "anthropic/claude-sonnet-4.5:batch",
10778
+ "anthropic/claude-sonnet-4.6",
10779
+ "anthropic/claude-sonnet-4.6:batch",
10780
+ "anthropic/claude-sonnet-5",
10781
+ "anthropic/claude-sonnet-5:batch",
10782
+ "arcee-ai/trinity-large-thinking",
10783
+ "arcee-ai/virtuoso-large",
10784
+ "baidu/ernie-4.5-vl-424b-a47b",
10785
+ "bytedance-seed/seed-1.6",
10786
+ "bytedance-seed/seed-1.6-flash",
10787
+ "bytedance-seed/seed-2.0-lite",
10788
+ "bytedance-seed/seed-2.0-mini",
10789
+ "bytedance/ui-tars-1.5-7b",
10790
+ "cognitivecomputations/dolphin-mistral-24b-venice-edition",
10791
+ "cohere/command-a",
10792
+ "cohere/command-r-08-2024",
10793
+ "cohere/command-r-plus-08-2024",
10794
+ "cohere/command-r7b-12-2024",
10795
+ "cohere/north-mini-code:free",
10796
+ "deepcogito/cogito-v2.1-671b",
10797
+ "deepseek/deepseek-chat",
10798
+ "deepseek/deepseek-chat-v3-0324",
10799
+ "deepseek/deepseek-chat-v3.1",
10800
+ "deepseek/deepseek-r1",
10801
+ "deepseek/deepseek-r1-0528",
10802
+ "deepseek/deepseek-r1-distill-llama-70b",
10803
+ "deepseek/deepseek-v3.1-terminus",
10804
+ "deepseek/deepseek-v3.2",
10805
+ "deepseek/deepseek-v3.2-exp",
10806
+ "deepseek/deepseek-v4-flash",
10807
+ "deepseek/deepseek-v4-flash-0731",
10808
+ "deepseek/deepseek-v4-pro",
10809
+ "google/gemini-2.5-flash",
10810
+ "google/gemini-2.5-flash-image",
10811
+ "google/gemini-2.5-flash-lite",
10812
+ "google/gemini-2.5-flash-lite:batch",
10813
+ "google/gemini-2.5-flash:batch",
10814
+ "google/gemini-2.5-pro",
10815
+ "google/gemini-2.5-pro-preview",
10816
+ "google/gemini-2.5-pro-preview-05-06",
10817
+ "google/gemini-2.5-pro:batch",
10818
+ "google/gemini-3-flash-preview",
10819
+ "google/gemini-3-flash-preview:batch",
10820
+ "google/gemini-3-pro-image",
10821
+ "google/gemini-3-pro-image-preview",
10822
+ "google/gemini-3.1-flash-image",
10823
+ "google/gemini-3.1-flash-image-preview",
10824
+ "google/gemini-3.1-flash-lite",
10825
+ "google/gemini-3.1-flash-lite-image",
10826
+ "google/gemini-3.1-flash-lite-preview",
10827
+ "google/gemini-3.1-flash-lite:batch",
10828
+ "google/gemini-3.1-pro-preview",
10829
+ "google/gemini-3.1-pro-preview-customtools",
10830
+ "google/gemini-3.1-pro-preview:batch",
10831
+ "google/gemini-3.5-flash",
10832
+ "google/gemini-3.5-flash-lite",
10833
+ "google/gemini-3.5-flash-lite:batch",
10834
+ "google/gemini-3.5-flash:batch",
10835
+ "google/gemini-3.6-flash",
10836
+ "google/gemini-3.6-flash:batch",
10837
+ "google/gemma-2-27b-it",
10838
+ "google/gemma-3-12b-it",
10839
+ "google/gemma-3-27b-it",
10840
+ "google/gemma-3-4b-it",
10841
+ "google/gemma-3n-e4b-it",
10842
+ "google/gemma-4-26b-a4b-it",
10843
+ "google/gemma-4-26b-a4b-it:free",
10844
+ "google/gemma-4-31b-it",
10845
+ "google/gemma-4-31b-it:free",
10846
+ "google/lyria-3-clip-preview",
10847
+ "google/lyria-3-pro-preview",
10848
+ "gryphe/mythomax-l2-13b",
10849
+ "ibm-granite/granite-4.0-h-micro",
10850
+ "ibm-granite/granite-4.1-8b",
10851
+ "inception/mercury-2",
10852
+ "inclusionai/ling-2.6-1t",
10853
+ "inclusionai/ling-2.6-flash",
10854
+ "inclusionai/ling-3.0-flash",
10855
+ "inclusionai/ling-3.0-tiny:free",
10856
+ "inclusionai/ring-2.6-1t",
10857
+ "kwaipilot/kat-coder-air-v2.5",
10858
+ "kwaipilot/kat-coder-pro-v2",
10859
+ "kwaipilot/kat-coder-pro-v2.5",
10860
+ "mancer/weaver",
10861
+ "meituan/longcat-2.0",
10862
+ "meta-llama/llama-3.1-70b-instruct",
10863
+ "meta-llama/llama-3.1-8b-instruct",
10864
+ "meta-llama/llama-3.2-1b-instruct",
10865
+ "meta-llama/llama-3.2-3b-instruct",
10866
+ "meta-llama/llama-3.3-70b-instruct",
10867
+ "meta-llama/llama-4-maverick",
10868
+ "meta-llama/llama-4-scout",
10869
+ "meta-llama/llama-guard-4-12b",
10870
+ "meta/muse-spark-1.1",
10871
+ "meta/muse-spark-1.2",
10872
+ "microsoft/phi-4",
10873
+ "microsoft/wizardlm-2-8x22b",
10874
+ "minimax/minimax-01",
10875
+ "minimax/minimax-m1",
10876
+ "minimax/minimax-m2",
10877
+ "minimax/minimax-m2-her",
10878
+ "minimax/minimax-m2.1",
10879
+ "minimax/minimax-m2.5",
10880
+ "minimax/minimax-m2.7",
10881
+ "minimax/minimax-m3",
10882
+ "minimax/minimax-m3:batch",
10883
+ "mistralai/codestral-2508",
10884
+ "mistralai/ministral-14b-2512",
10885
+ "mistralai/ministral-3b-2512",
10886
+ "mistralai/ministral-8b-2512",
10887
+ "mistralai/mistral-large",
10888
+ "mistralai/mistral-large-2407",
10889
+ "mistralai/mistral-large-2512",
10890
+ "mistralai/mistral-medium-3",
10891
+ "mistralai/mistral-medium-3-5",
10892
+ "mistralai/mistral-medium-3.1",
10893
+ "mistralai/mistral-nemo",
10894
+ "mistralai/mistral-saba",
10895
+ "mistralai/mistral-small-24b-instruct-2501",
10896
+ "mistralai/mistral-small-2603",
10897
+ "mistralai/mistral-small-3.1-24b-instruct",
10898
+ "mistralai/mistral-small-3.2-24b-instruct",
10899
+ "mistralai/mixtral-8x22b-instruct",
10900
+ "mistralai/voxtral-small-24b-2507",
10901
+ "moonshotai/kimi-k2",
10902
+ "moonshotai/kimi-k2-0905",
10903
+ "moonshotai/kimi-k2-thinking",
10904
+ "moonshotai/kimi-k2.5",
10905
+ "moonshotai/kimi-k2.6",
10906
+ "moonshotai/kimi-k2.7-code",
10907
+ "moonshotai/kimi-k2.7-code:batch",
10908
+ "moonshotai/kimi-k3",
10909
+ "morph/morph-v3-fast",
10910
+ "morph/morph-v3-large",
10911
+ "nex-agi/nex-n2-mini",
10912
+ "nex-agi/nex-n2-pro",
10913
+ "nousresearch/hermes-3-llama-3.1-405b",
10914
+ "nousresearch/hermes-3-llama-3.1-70b",
10915
+ "nousresearch/hermes-4-405b",
10916
+ "nousresearch/hermes-4-70b",
10917
+ "nvidia/nemotron-3-nano-30b-a3b",
10918
+ "nvidia/nemotron-3-nano-30b-a3b:free",
10919
+ "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free",
10920
+ "nvidia/nemotron-3-super-120b-a12b",
10921
+ "nvidia/nemotron-3-super-120b-a12b:free",
10922
+ "nvidia/nemotron-3-ultra-550b-a55b",
10923
+ "nvidia/nemotron-3-ultra-550b-a55b:batch",
10924
+ "nvidia/nemotron-3-ultra-550b-a55b:free",
10925
+ "nvidia/nemotron-3.5-content-safety:free",
10926
+ "nvidia/nemotron-nano-12b-v2-vl:free",
10927
+ "nvidia/nemotron-nano-9b-v2:free",
10928
+ "openai/gpt-3.5-turbo",
10929
+ "openai/gpt-3.5-turbo-0613",
10930
+ "openai/gpt-3.5-turbo-16k",
10931
+ "openai/gpt-3.5-turbo-instruct",
10932
+ "openai/gpt-3.5-turbo:batch",
10933
+ "openai/gpt-4",
10934
+ "openai/gpt-4-turbo",
10935
+ "openai/gpt-4-turbo-preview",
10936
+ "openai/gpt-4-turbo:batch",
10937
+ "openai/gpt-4.1",
10938
+ "openai/gpt-4.1-mini",
10939
+ "openai/gpt-4.1-mini:batch",
10940
+ "openai/gpt-4.1-nano",
10941
+ "openai/gpt-4.1-nano:batch",
10942
+ "openai/gpt-4.1:batch",
10943
+ "openai/gpt-4o",
10944
+ "openai/gpt-4o-2024-05-13",
10945
+ "openai/gpt-4o-2024-08-06",
10946
+ "openai/gpt-4o-2024-11-20",
10947
+ "openai/gpt-4o-mini",
10948
+ "openai/gpt-4o-mini-2024-07-18",
10949
+ "openai/gpt-4o-mini:batch",
10950
+ "openai/gpt-4o:batch",
10951
+ "openai/gpt-5",
10952
+ "openai/gpt-5-codex:batch",
10953
+ "openai/gpt-5-image",
10954
+ "openai/gpt-5-image-mini",
10955
+ "openai/gpt-5-mini",
10956
+ "openai/gpt-5-mini:batch",
10957
+ "openai/gpt-5-nano",
10958
+ "openai/gpt-5-nano:batch",
10959
+ "openai/gpt-5-pro",
10960
+ "openai/gpt-5-pro:batch",
10961
+ "openai/gpt-5.1",
10962
+ "openai/gpt-5.1-codex",
10963
+ "openai/gpt-5.1-codex-max",
10964
+ "openai/gpt-5.1-codex-mini",
10965
+ "openai/gpt-5.1:batch",
10966
+ "openai/gpt-5.2",
10967
+ "openai/gpt-5.2-chat",
10968
+ "openai/gpt-5.2-codex",
10969
+ "openai/gpt-5.2-pro",
10970
+ "openai/gpt-5.2-pro:batch",
10971
+ "openai/gpt-5.2:batch",
10972
+ "openai/gpt-5.3-chat",
10973
+ "openai/gpt-5.3-codex",
10974
+ "openai/gpt-5.4",
10975
+ "openai/gpt-5.4-image-2",
10976
+ "openai/gpt-5.4-mini",
10977
+ "openai/gpt-5.4-mini:batch",
10978
+ "openai/gpt-5.4-nano",
10979
+ "openai/gpt-5.4-nano:batch",
10980
+ "openai/gpt-5.4-pro",
10981
+ "openai/gpt-5.4-pro:batch",
10982
+ "openai/gpt-5.4:batch",
10983
+ "openai/gpt-5.5",
10984
+ "openai/gpt-5.5-pro",
10985
+ "openai/gpt-5.5-pro:batch",
10986
+ "openai/gpt-5.5:batch",
10987
+ "openai/gpt-5.6-luna",
10988
+ "openai/gpt-5.6-luna-pro",
10989
+ "openai/gpt-5.6-luna-pro:batch",
10990
+ "openai/gpt-5.6-luna:batch",
10991
+ "openai/gpt-5.6-sol",
10992
+ "openai/gpt-5.6-sol-pro",
10993
+ "openai/gpt-5.6-sol-pro:batch",
10994
+ "openai/gpt-5.6-sol:batch",
10995
+ "openai/gpt-5.6-terra",
10996
+ "openai/gpt-5.6-terra-pro",
10997
+ "openai/gpt-5.6-terra-pro:batch",
10998
+ "openai/gpt-5.6-terra:batch",
10999
+ "openai/gpt-5:batch",
11000
+ "openai/gpt-audio",
11001
+ "openai/gpt-audio-mini",
11002
+ "openai/gpt-chat-latest",
11003
+ "openai/gpt-oss-120b",
11004
+ "openai/gpt-oss-20b",
11005
+ "openai/gpt-oss-20b:free",
11006
+ "openai/gpt-oss-safeguard-20b",
11007
+ "openai/o1",
11008
+ "openai/o1-pro",
11009
+ "openai/o1-pro:batch",
11010
+ "openai/o1:batch",
11011
+ "openai/o3",
11012
+ "openai/o3-mini",
11013
+ "openai/o3-mini-high",
11014
+ "openai/o3-mini-high:batch",
11015
+ "openai/o3-mini:batch",
11016
+ "openai/o3-pro",
11017
+ "openai/o3-pro:batch",
11018
+ "openai/o3:batch",
11019
+ "openai/o4-mini",
11020
+ "openai/o4-mini-high",
11021
+ "openai/o4-mini-high:batch",
11022
+ "openai/o4-mini:batch",
11023
+ "openrouter/auto",
11024
+ "openrouter/auto-beta",
11025
+ "openrouter/bodybuilder",
11026
+ "openrouter/free",
11027
+ "openrouter/fusion",
11028
+ "openrouter/pareto-code",
11029
+ "perceptron/perceptron-mk1",
11030
+ "perplexity/sonar",
11031
+ "perplexity/sonar-deep-research",
11032
+ "perplexity/sonar-pro",
11033
+ "perplexity/sonar-pro-search",
11034
+ "perplexity/sonar-reasoning-pro",
11035
+ "poolside/laguna-s-2.1",
11036
+ "poolside/laguna-s-2.1:free",
11037
+ "poolside/laguna-xs-2.1",
11038
+ "poolside/laguna-xs-2.1:free",
11039
+ "qwen/qwen-2.5-72b-instruct",
11040
+ "qwen/qwen-2.5-7b-instruct",
11041
+ "qwen/qwen-2.5-coder-32b-instruct",
11042
+ "qwen/qwen-plus",
11043
+ "qwen/qwen-plus-2025-07-28",
11044
+ "qwen/qwen-plus-2025-07-28:thinking",
11045
+ "qwen/qwen2.5-vl-72b-instruct",
11046
+ "qwen/qwen3-14b",
11047
+ "qwen/qwen3-235b-a22b",
11048
+ "qwen/qwen3-235b-a22b-2507",
11049
+ "qwen/qwen3-235b-a22b-thinking-2507",
11050
+ "qwen/qwen3-30b-a3b",
11051
+ "qwen/qwen3-30b-a3b-instruct-2507",
11052
+ "qwen/qwen3-30b-a3b-thinking-2507",
11053
+ "qwen/qwen3-32b",
11054
+ "qwen/qwen3-8b",
11055
+ "qwen/qwen3-coder",
11056
+ "qwen/qwen3-coder-30b-a3b-instruct",
11057
+ "qwen/qwen3-coder-flash",
11058
+ "qwen/qwen3-coder-next",
11059
+ "qwen/qwen3-coder-plus",
11060
+ "qwen/qwen3-max",
11061
+ "qwen/qwen3-max-thinking",
11062
+ "qwen/qwen3-next-80b-a3b-instruct",
11063
+ "qwen/qwen3-next-80b-a3b-thinking",
11064
+ "qwen/qwen3-vl-235b-a22b-instruct",
11065
+ "qwen/qwen3-vl-235b-a22b-thinking",
11066
+ "qwen/qwen3-vl-30b-a3b-instruct",
11067
+ "qwen/qwen3-vl-30b-a3b-thinking",
11068
+ "qwen/qwen3-vl-32b-instruct",
11069
+ "qwen/qwen3-vl-8b-instruct",
11070
+ "qwen/qwen3-vl-8b-thinking",
11071
+ "qwen/qwen3.5-122b-a10b",
11072
+ "qwen/qwen3.5-27b",
11073
+ "qwen/qwen3.5-35b-a3b",
11074
+ "qwen/qwen3.5-397b-a17b",
11075
+ "qwen/qwen3.5-9b",
11076
+ "qwen/qwen3.5-flash-02-23",
11077
+ "qwen/qwen3.5-plus-02-15",
11078
+ "qwen/qwen3.5-plus-20260420",
11079
+ "qwen/qwen3.6-27b",
11080
+ "qwen/qwen3.6-35b-a3b",
11081
+ "qwen/qwen3.6-flash",
11082
+ "qwen/qwen3.6-max-preview",
11083
+ "qwen/qwen3.6-plus",
11084
+ "qwen/qwen3.7-flash",
11085
+ "qwen/qwen3.7-max",
11086
+ "qwen/qwen3.7-plus",
11087
+ "qwen/qwen3.8-max",
11088
+ "rekaai/reka-edge",
11089
+ "rekaai/reka-flash-3",
11090
+ "relace/relace-apply-3",
11091
+ "relace/relace-search",
11092
+ "sakana/fugu-ultra",
11093
+ "sao10k/l3-lunaris-8b",
11094
+ "sao10k/l3.1-euryale-70b",
11095
+ "sao10k/l3.3-euryale-70b",
11096
+ "stepfun/step-3.5-flash",
11097
+ "stepfun/step-3.7-flash",
11098
+ "tencent/hunyuan-a13b-instruct",
11099
+ "tencent/hy3",
11100
+ "tencent/hy3-preview",
11101
+ "thedrummer/cydonia-24b-v4.1",
11102
+ "thedrummer/rocinante-12b",
11103
+ "thedrummer/skyfall-36b-v2",
11104
+ "thedrummer/unslopnemo-12b",
11105
+ "thinkingmachines/inkling",
11106
+ "thinkingmachines/inkling-small",
11107
+ "thinkingmachines/inkling:batch",
11108
+ "undi95/remm-slerp-l2-13b",
11109
+ "upstage/solar-pro-3",
11110
+ "writer/palmyra-x5",
11111
+ "x-ai/grok-4.20",
11112
+ "x-ai/grok-4.20-multi-agent",
11113
+ "x-ai/grok-4.3",
11114
+ "x-ai/grok-4.5",
11115
+ "x-ai/grok-build-0.1",
11116
+ "xiaomi/mimo-v2.5",
11117
+ "xiaomi/mimo-v2.5-pro",
11118
+ "z-ai/glm-4.5",
11119
+ "z-ai/glm-4.5-air",
11120
+ "z-ai/glm-4.5v",
11121
+ "z-ai/glm-4.6",
11122
+ "z-ai/glm-4.6v",
11123
+ "z-ai/glm-4.7",
11124
+ "z-ai/glm-4.7-flash",
11125
+ "z-ai/glm-5",
11126
+ "z-ai/glm-5-turbo",
11127
+ "z-ai/glm-5.1",
11128
+ "z-ai/glm-5.2",
11129
+ "z-ai/glm-5.2:batch",
11130
+ "z-ai/glm-5v-turbo",
11131
+ "~anthropic/claude-fable-latest",
11132
+ "~anthropic/claude-haiku-latest",
11133
+ "~anthropic/claude-opus-latest",
11134
+ "~anthropic/claude-sonnet-latest",
11135
+ "~deepseek/deepseek-v4-flash-latest",
11136
+ "~google/gemini-flash-latest",
11137
+ "~google/gemini-pro-latest",
11138
+ "~moonshotai/kimi-latest",
11139
+ "~openai/gpt-latest",
11140
+ "~openai/gpt-mini-latest",
11141
+ "~x-ai/grok-latest"
11142
+ ];
11143
+
11144
+ // src/lib/plugin-tool-supply-audit.ts
10738
11145
  function listDirs(root) {
10739
11146
  let entries;
10740
11147
  try {
@@ -10987,6 +11394,148 @@ function extractTerraformEnvKeys(tfText) {
10987
11394
  }
10988
11395
  return { keys: [...keys].sort(), rawMarkerCount, resolvedBlockCount };
10989
11396
  }
11397
+ var SETTINGS_STR_FIELD = /^[ \t]*([A-Za-z_][A-Za-z0-9_]*)\s*:\s*str\s*=\s*"([^"]*)"/gm;
11398
+ function extractSettingsModelFields(text) {
11399
+ const out = [];
11400
+ SETTINGS_STR_FIELD.lastIndex = 0;
11401
+ let m;
11402
+ while ((m = SETTINGS_STR_FIELD.exec(text)) !== null) {
11403
+ const [field, value] = [m[1], m[2]];
11404
+ if (field.toLowerCase().includes("model") && value.includes("/")) {
11405
+ out.push({ field, value });
11406
+ }
11407
+ }
11408
+ return out;
11409
+ }
11410
+ var MODEL_FIELD_NAME_MARKER = /"name"\s*:\s*"model"/g;
11411
+ var FIELD_DEFAULT = /"default"\s*:\s*"([^"]*)"/;
11412
+ var FIELD_OPTIONS_START = /"options"\s*:\s*\[/;
11413
+ var OPTION_VALUE = /"value"\s*:\s*"([^"]*)"/g;
11414
+ function extractCuratedModelFields(text) {
11415
+ const fields = [];
11416
+ let rawFieldCount = 0;
11417
+ MODEL_FIELD_NAME_MARKER.lastIndex = 0;
11418
+ let m;
11419
+ while ((m = MODEL_FIELD_NAME_MARKER.exec(text)) !== null) {
11420
+ rawFieldCount += 1;
11421
+ const start = m.index + m[0].length;
11422
+ const nextNameIdx = text.indexOf('"name"', start);
11423
+ const windowEnd = nextNameIdx === -1 ? text.length : nextNameIdx;
11424
+ const window = text.slice(start, windowEnd);
11425
+ const defaultMatch = FIELD_DEFAULT.exec(window);
11426
+ const defaultValue = defaultMatch ? defaultMatch[1] : null;
11427
+ const optionsMatch = FIELD_OPTIONS_START.exec(window);
11428
+ const optionValues = [];
11429
+ if (optionsMatch) {
11430
+ const openIdx = start + optionsMatch.index + optionsMatch[0].length - 1;
11431
+ const closeIdx = balancedSpanFrom(text, openIdx, "[", "]");
11432
+ if (closeIdx !== null) {
11433
+ const inner = text.slice(openIdx, closeIdx + 1);
11434
+ OPTION_VALUE.lastIndex = 0;
11435
+ let om;
11436
+ while ((om = OPTION_VALUE.exec(inner)) !== null) {
11437
+ optionValues.push(om[1]);
11438
+ }
11439
+ }
11440
+ }
11441
+ fields.push({ defaultValue, optionValues });
11442
+ }
11443
+ return { rawFieldCount, fields };
11444
+ }
11445
+ var MODEL_SNAPSHOT_MAX_AGE_DAYS = 45;
11446
+ function isSnapshotStale(fetchedAt, now) {
11447
+ const fetchedMs = new Date(fetchedAt).getTime();
11448
+ if (Number.isNaN(fetchedMs)) return true;
11449
+ const ageMs = now.getTime() - fetchedMs;
11450
+ return ageMs > MODEL_SNAPSHOT_MAX_AGE_DAYS * 24 * 60 * 60 * 1e3;
11451
+ }
11452
+ function normalizeModelId(id) {
11453
+ return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
11454
+ }
11455
+ var CONFIG_PY_PATH = join42("services", "api", "src", "api", "config.py");
11456
+ var ORCHESTRATION_SCHEMA_PATH = join42(
11457
+ "services",
11458
+ "api",
11459
+ "src",
11460
+ "api",
11461
+ "schemas",
11462
+ "orchestration.py"
11463
+ );
11464
+ function auditDeclaredModelIds(repoRoot, options = {}) {
11465
+ const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
11466
+ const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
11467
+ const now = options.now ?? /* @__PURE__ */ new Date();
11468
+ const configPath = join42(repoRoot, CONFIG_PY_PATH);
11469
+ const orchestrationPath = join42(repoRoot, ORCHESTRATION_SCHEMA_PATH);
11470
+ const configMissing = !existsSync39(configPath);
11471
+ const orchestrationSchemaMissing = !existsSync39(orchestrationPath);
11472
+ const knownSet = new Set(knownModelIds);
11473
+ const snapshotEmpty = knownModelIds.length === 0;
11474
+ const snapshotStale = isSnapshotStale(snapshotFetchedAt, now);
11475
+ const findings = [];
11476
+ const record = (source, rawValue) => {
11477
+ const normalized = normalizeModelId(rawValue);
11478
+ const ok2 = knownSet.has(normalized);
11479
+ findings.push({
11480
+ source,
11481
+ modelId: rawValue,
11482
+ normalizedModelId: normalized,
11483
+ status: ok2 ? "ok" : "unknown-model",
11484
+ detail: ok2 ? `"${rawValue}" is in the OpenRouter snapshot (${knownModelIds.length} id(s), fetched ${snapshotFetchedAt})` : `"${rawValue}" (normalized "${normalized}") is NOT in the OpenRouter snapshot (${knownModelIds.length} id(s), fetched ${snapshotFetchedAt}) \u2014 either the slug is wrong or the snapshot needs a refresh`
11485
+ });
11486
+ };
11487
+ let settingsBlind = false;
11488
+ if (!configMissing) {
11489
+ const settingsFields = extractSettingsModelFields(readFileSync30(configPath, "utf8"));
11490
+ if (settingsFields.length === 0) settingsBlind = true;
11491
+ for (const { field, value } of settingsFields) record(`${CONFIG_PY_PATH}#${field}`, value);
11492
+ }
11493
+ let curatedFieldsBlind = false;
11494
+ if (!orchestrationSchemaMissing) {
11495
+ const curated = extractCuratedModelFields(readFileSync30(orchestrationPath, "utf8"));
11496
+ if (curated.rawFieldCount > 0 && curated.fields.every((f) => f.defaultValue === null && f.optionValues.length === 0)) {
11497
+ curatedFieldsBlind = true;
11498
+ }
11499
+ curated.fields.forEach((field, i) => {
11500
+ if (field.defaultValue !== null) {
11501
+ record(`${ORCHESTRATION_SCHEMA_PATH}#model[${i}].default`, field.defaultValue);
11502
+ }
11503
+ field.optionValues.forEach((v, j) => {
11504
+ record(`${ORCHESTRATION_SCHEMA_PATH}#model[${i}].options[${j}]`, v);
11505
+ });
11506
+ });
11507
+ }
11508
+ const badFindings = findings.filter((f) => f.status !== "ok");
11509
+ const ok = !configMissing && !orchestrationSchemaMissing && !settingsBlind && !curatedFieldsBlind && !snapshotEmpty && !snapshotStale && badFindings.length === 0;
11510
+ const summaryParts = [
11511
+ `${findings.length} declared model id(s) checked against ${knownModelIds.length} known OpenRouter id(s) (snapshot fetched ${snapshotFetchedAt})`,
11512
+ `${badFindings.length} not ok`
11513
+ ];
11514
+ if (configMissing) summaryParts.push(`MISSING ${CONFIG_PY_PATH}`);
11515
+ if (orchestrationSchemaMissing) summaryParts.push(`MISSING ${ORCHESTRATION_SCHEMA_PATH}`);
11516
+ if (settingsBlind) summaryParts.push("SETTINGS EXTRACTOR BLIND");
11517
+ if (curatedFieldsBlind) summaryParts.push("CURATED-OPTIONS EXTRACTOR BLIND");
11518
+ if (snapshotEmpty) summaryParts.push("SNAPSHOT EMPTY");
11519
+ if (snapshotStale)
11520
+ summaryParts.push(
11521
+ `SNAPSHOT STALE (older than ${MODEL_SNAPSHOT_MAX_AGE_DAYS}d \u2014 run refresh-openrouter-model-snapshot.ts)`
11522
+ );
11523
+ return {
11524
+ configPath: CONFIG_PY_PATH,
11525
+ orchestrationSchemaPath: ORCHESTRATION_SCHEMA_PATH,
11526
+ configMissing,
11527
+ orchestrationSchemaMissing,
11528
+ settingsBlind,
11529
+ curatedFieldsBlind,
11530
+ snapshotFetchedAt,
11531
+ snapshotModelCount: knownModelIds.length,
11532
+ snapshotEmpty,
11533
+ snapshotStale,
11534
+ findings,
11535
+ ok,
11536
+ summary: summaryParts.join("; ")
11537
+ };
11538
+ }
10990
11539
  function discoverPluginDirs(pluginsRoot) {
10991
11540
  return listDirs(pluginsRoot).filter((name) => {
10992
11541
  try {
@@ -11134,37 +11683,82 @@ function auditPluginToolSupply(pluginsRoot) {
11134
11683
  // src/scripts/check-plugin-tool-supply.ts
11135
11684
  async function runPluginToolSupplyCheck() {
11136
11685
  const root = (await execa12("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11686
+ let allOk = true;
11137
11687
  const pluginsRoot = join43(root, "services", "_plugins");
11138
- if (!existsSync39(pluginsRoot)) {
11688
+ if (!existsSync40(pluginsRoot)) {
11139
11689
  console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
11140
- return;
11141
- }
11142
- const report = auditPluginToolSupply(pluginsRoot);
11143
- console.log(
11144
- `audited ${report.plugins.length} plugin dir(s), ${report.findings.length} declared tool(s) cross-checked, under ${pluginsRoot}`
11145
- );
11146
- if (!report.ok) {
11147
- console.error("\u2717 plugin tool-supply guard: an unsatisfiable tool grant found\n");
11148
- if (report.noPluginsFound) {
11149
- console.error(` NO PLUGINS FOUND under ${pluginsRoot} \u2014 cannot evaluate an empty world.`);
11150
- }
11151
- if (report.registryBlind) {
11152
- console.error(
11153
- " REGISTRY BLIND: raw source contains ToolDefinition( call site(s) but the extractor resolved none \u2014 the extractor broke, this is not evidence a plugin registers nothing."
11154
- );
11155
- }
11156
- if (report.terraformBlind) {
11157
- console.error(
11158
- " TERRAFORM BLIND: raw source contains environment_variables but the extractor resolved no keys \u2014 the extractor broke, this is not evidence of an empty Lambda."
11159
- );
11690
+ } else {
11691
+ const report = auditPluginToolSupply(pluginsRoot);
11692
+ console.log(
11693
+ `audited ${report.plugins.length} plugin dir(s), ${report.findings.length} declared tool(s) cross-checked, under ${pluginsRoot}`
11694
+ );
11695
+ if (!report.ok) {
11696
+ allOk = false;
11697
+ console.error("\u2717 plugin tool-supply guard: an unsatisfiable tool grant found\n");
11698
+ if (report.noPluginsFound) {
11699
+ console.error(` NO PLUGINS FOUND under ${pluginsRoot} \u2014 cannot evaluate an empty world.`);
11700
+ }
11701
+ if (report.registryBlind) {
11702
+ console.error(
11703
+ " REGISTRY BLIND: raw source contains ToolDefinition( call site(s) but the extractor resolved none \u2014 the extractor broke, this is not evidence a plugin registers nothing."
11704
+ );
11705
+ }
11706
+ if (report.terraformBlind) {
11707
+ console.error(
11708
+ " TERRAFORM BLIND: raw source contains environment_variables but the extractor resolved no keys \u2014 the extractor broke, this is not evidence of an empty Lambda."
11709
+ );
11710
+ }
11711
+ for (const f of report.findings.filter((f2) => f2.status !== "ok")) {
11712
+ console.error(` ${f.status.toUpperCase()} ${f.plugin}/${f.tool} ${f.detail}`);
11713
+ }
11714
+ } else {
11715
+ console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
11160
11716
  }
11161
- for (const f of report.findings.filter((f2) => f2.status !== "ok")) {
11162
- console.error(` ${f.status.toUpperCase()} ${f.plugin}/${f.tool} ${f.detail}`);
11717
+ }
11718
+ const servicesApiRoot = join43(root, "services", "api");
11719
+ if (!existsSync40(servicesApiRoot)) {
11720
+ console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
11721
+ } else {
11722
+ const modelReport = auditDeclaredModelIds(root);
11723
+ console.log(`audited ${modelReport.findings.length} declared model id(s)`);
11724
+ if (!modelReport.ok) {
11725
+ allOk = false;
11726
+ console.error("\u2717 plugin model-id guard: a declared model id does not match the provider\n");
11727
+ if (modelReport.configMissing) {
11728
+ console.error(` MISSING ${modelReport.configPath}`);
11729
+ }
11730
+ if (modelReport.orchestrationSchemaMissing) {
11731
+ console.error(` MISSING ${modelReport.orchestrationSchemaPath}`);
11732
+ }
11733
+ if (modelReport.settingsBlind) {
11734
+ console.error(
11735
+ ` SETTINGS EXTRACTOR BLIND: ${modelReport.configPath} exists but no model-named string field resolved \u2014 the extractor broke, this is not evidence there is no default.`
11736
+ );
11737
+ }
11738
+ if (modelReport.curatedFieldsBlind) {
11739
+ console.error(
11740
+ ` CURATED-OPTIONS EXTRACTOR BLIND: ${modelReport.orchestrationSchemaPath} has a "name": "model" field but no default/options resolved from it.`
11741
+ );
11742
+ }
11743
+ if (modelReport.snapshotEmpty) {
11744
+ console.error(" SNAPSHOT EMPTY: the committed OpenRouter snapshot has zero ids.");
11745
+ }
11746
+ if (modelReport.snapshotStale) {
11747
+ console.error(
11748
+ ` SNAPSHOT STALE: fetched ${modelReport.snapshotFetchedAt}, older than the refresh window \u2014 run refresh-openrouter-model-snapshot.ts and commit the result.`
11749
+ );
11750
+ }
11751
+ for (const f of modelReport.findings.filter((f2) => f2.status !== "ok")) {
11752
+ console.error(` ${f.status.toUpperCase()} ${f.source} ${f.detail}`);
11753
+ }
11754
+ } else {
11755
+ console.log(`\u2713 plugin model-id guard: ${modelReport.summary}`);
11163
11756
  }
11757
+ }
11758
+ if (!allOk) {
11164
11759
  console.error("\nSee biffo-template#822.");
11165
11760
  process.exit(1);
11166
11761
  }
11167
- console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
11168
11762
  }
11169
11763
 
11170
11764
  // src/scripts/check-release-subject.ts
@@ -11348,7 +11942,7 @@ function rawArgsAfter(subcommand) {
11348
11942
  }
11349
11943
 
11350
11944
  // src/commands/doctor.ts
11351
- import { existsSync as existsSync40, readFileSync as readFileSync31 } from "fs";
11945
+ import { existsSync as existsSync41, readFileSync as readFileSync31 } from "fs";
11352
11946
  import { join as join44, resolve as resolve18 } from "path";
11353
11947
  import chalk21 from "chalk";
11354
11948
  import { Command as Command24 } from "commander";
@@ -11525,7 +12119,7 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
11525
12119
  }
11526
12120
  function readLocalCoreVersion(cwd) {
11527
12121
  const path = join44(cwd, INSTANCE_CORE_FILE);
11528
- if (!existsSync40(path)) return null;
12122
+ if (!existsSync41(path)) return null;
11529
12123
  try {
11530
12124
  return parseCoreRecord(readFileSync31(path, "utf8"));
11531
12125
  } catch {
@@ -11543,7 +12137,7 @@ function parseCoreRecord(contents) {
11543
12137
  }
11544
12138
  function readFossil(cwd) {
11545
12139
  const path = join44(cwd, CORE_VERSION_FILE);
11546
- if (!existsSync40(path)) return null;
12140
+ if (!existsSync41(path)) return null;
11547
12141
  try {
11548
12142
  const value = readFileSync31(path, "utf8").trim();
11549
12143
  return value === "" ? null : value;
@@ -11994,13 +12588,13 @@ import { fileURLToPath as fileURLToPath6 } from "url";
11994
12588
  import { Command as Command26 } from "commander";
11995
12589
 
11996
12590
  // src/lib/packaged-scripts.ts
11997
- import { existsSync as existsSync41 } from "fs";
12591
+ import { existsSync as existsSync42 } from "fs";
11998
12592
  import { dirname as dirname10, join as join45 } from "path";
11999
12593
  function findPackagedScript(startDir, relativePath) {
12000
12594
  let dir = startDir;
12001
12595
  for (; ; ) {
12002
12596
  const candidate = join45(dir, relativePath);
12003
- if (existsSync41(candidate)) return candidate;
12597
+ if (existsSync42(candidate)) return candidate;
12004
12598
  const parent = dirname10(dir);
12005
12599
  if (parent === dir) return null;
12006
12600
  dir = parent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.266.2",
3
+ "version": "0.267.1",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -50,7 +50,8 @@
50
50
  "check:core-ownership": "tsx src/index.ts check ownership",
51
51
  "check:plugin-terraform": "tsx src/index.ts check plugin-terraform",
52
52
  "sync:core-tag": "tsx src/scripts/sync-core-tag.ts",
53
- "report:publish-failure": "tsx src/scripts/report-publish-failure.ts"
53
+ "report:publish-failure": "tsx src/scripts/report-publish-failure.ts",
54
+ "refresh:openrouter-models": "tsx src/scripts/refresh-openrouter-model-snapshot.ts"
54
55
  },
55
56
  "dependencies": {
56
57
  "@aws-sdk/client-cloudformation": "^3.726.1",