omnigateway 0.1.8 → 0.2.0

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 (42) hide show
  1. package/README.md +27 -33
  2. package/bin/omni.js +607 -78
  3. package/gateway.js +2877 -150
  4. package/package.json +1 -1
  5. package/public/assets/{Chip-CM9ZMdr9.js → Chip-BB_5C1Zp.js} +1 -1
  6. package/public/assets/Confirm-B6aAiVbT.js +4 -0
  7. package/public/assets/{CopyValue-WrOTcHwq.js → CopyValue-CRQDLo7k.js} +1 -1
  8. package/public/assets/{Field-Br6jKPz8.js → Field-uHZxl4fI.js} +1 -1
  9. package/public/assets/Lamp-B-5SjXbG.js +25 -0
  10. package/public/assets/{Meter-BzMaum0C.js → Meter-DI_BRUKt.js} +1 -1
  11. package/public/assets/Modal-CI6jk2D4.js +82 -0
  12. package/public/assets/{Rack-BFWC50ex.js → Rack-D1WJswv3.js} +8 -4
  13. package/public/assets/{Readout-DMJs4gYD.js → Readout-BocZ2HXP.js} +1 -1
  14. package/public/assets/{States-BNeCLhZn.js → States-Bbiu5cHE.js} +1 -1
  15. package/public/assets/{Table-nOg_lgEJ.js → Table-CdPWxYaz.js} +1 -1
  16. package/public/assets/Toggle-CiLC67Dw.js +39 -0
  17. package/public/assets/TokenBreakdown-B96iPBm9.js +28 -0
  18. package/public/assets/_app-BBOF6A0T.js +1 -0
  19. package/public/assets/{_app.accounts-BTb5vHI_.js → _app.accounts-BNkhpvaB.js} +8 -5
  20. package/public/assets/_app.console-Daz4aKhf.js +44 -0
  21. package/public/assets/_app.index-LO6d38oe.js +62 -0
  22. package/public/assets/{_app.keys-B-cy-2DS.js → _app.keys-kBqLqoFf.js} +3 -3
  23. package/public/assets/_app.logs-_wYNS47N.js +32 -0
  24. package/public/assets/{_app.models-BAmXHlf2.js → _app.models-Cjng8ohC.js} +5 -5
  25. package/public/assets/_app.settings-a0FKyQAi.js +38 -0
  26. package/public/assets/{_app.usage-BLrQ2hB8.js → _app.usage-D3KtgLrC.js} +9 -6
  27. package/public/assets/dist-C-IbPRiV.js +1 -0
  28. package/public/assets/index-PW6EvVh5.js +170 -0
  29. package/public/assets/{login-BpUl6C5b.js → login-CTvH_KAd.js} +1 -1
  30. package/public/assets/queries-D2o-X8Pj.js +144 -0
  31. package/public/assets/{trash-2-DjWP2u19.js → trash-2-BcZb-sCT.js} +1 -1
  32. package/public/index.html +2 -2
  33. package/public/assets/Lamp-DO-sRDg2.js +0 -25
  34. package/public/assets/Modal-lhjr949H.js +0 -82
  35. package/public/assets/Toggle-DYoCVD37.js +0 -42
  36. package/public/assets/_app-lXr06Xli.js +0 -1
  37. package/public/assets/_app.console-DHUClMsI.js +0 -25
  38. package/public/assets/_app.index-DZ--9mKU.js +0 -61
  39. package/public/assets/_app.logs-BQjAIT2P.js +0 -33
  40. package/public/assets/_app.settings-CG8bv_rm.js +0 -34
  41. package/public/assets/index-UAZ0O5y0.js +0 -170
  42. package/public/assets/queries-D_-Jj9vt.js +0 -144
package/bin/omni.js CHANGED
@@ -91,9 +91,8 @@ function formatUsd(value) {
91
91
  }
92
92
 
93
93
  // apps/cli/src/run.ts
94
- import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
94
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
95
95
  import { homedir as homedir3 } from "os";
96
- import { dirname as dirname3 } from "path";
97
96
 
98
97
  // packages/control/src/adminAuth.ts
99
98
  import { hash, verify } from "@node-rs/argon2";
@@ -153,6 +152,11 @@ function createAdminAuth(store, opts) {
153
152
  };
154
153
  }
155
154
  // packages/ir/src/capabilities.ts
155
+ var ANTHROPIC_NATIVE_TOOLS = {
156
+ anthropic: true,
157
+ openai: false,
158
+ kimi: false
159
+ };
156
160
  var PROVIDER_CAPABILITIES = {
157
161
  anthropic: { tools: true, images: true, reasoning: true },
158
162
  openai: { tools: true, images: true, reasoning: true },
@@ -759,14 +763,232 @@ function parseRecord(record) {
759
763
  `) };
760
764
  }
761
765
 
766
+ // packages/providers/src/anthropic/tools.ts
767
+ var COMMON = ["cache_control", "strict", "defer_loading", "allowed_callers"];
768
+ var COMMON_WITH_EXAMPLES = [...COMMON, "input_examples"];
769
+ var searchDomains = ["max_uses", "allowed_domains", "blocked_domains"];
770
+ var ANTHROPIC_TOOL_SPECS = {
771
+ web_search_20250305: {
772
+ family: "webSearch",
773
+ name: "web_search",
774
+ required: [],
775
+ optional: [...COMMON, ...searchDomains, "user_location"]
776
+ },
777
+ web_search_20260209: {
778
+ family: "webSearch",
779
+ name: "web_search",
780
+ required: [],
781
+ optional: [...COMMON, ...searchDomains, "user_location"]
782
+ },
783
+ web_search_20260318: {
784
+ family: "webSearch",
785
+ name: "web_search",
786
+ required: [],
787
+ optional: [...COMMON, ...searchDomains, "user_location", "response_inclusion"]
788
+ },
789
+ web_fetch_20250910: {
790
+ family: "webFetch",
791
+ name: "web_fetch",
792
+ required: [],
793
+ optional: [...COMMON, ...searchDomains, "citations", "max_content_tokens"]
794
+ },
795
+ web_fetch_20260209: {
796
+ family: "webFetch",
797
+ name: "web_fetch",
798
+ required: [],
799
+ optional: [...COMMON, ...searchDomains, "citations", "max_content_tokens"]
800
+ },
801
+ web_fetch_20260309: {
802
+ family: "webFetch",
803
+ name: "web_fetch",
804
+ required: [],
805
+ optional: [...COMMON, ...searchDomains, "citations", "max_content_tokens", "use_cache"]
806
+ },
807
+ web_fetch_20260318: {
808
+ family: "webFetch",
809
+ name: "web_fetch",
810
+ required: [],
811
+ optional: [
812
+ ...COMMON,
813
+ ...searchDomains,
814
+ "citations",
815
+ "max_content_tokens",
816
+ "use_cache",
817
+ "response_inclusion"
818
+ ]
819
+ },
820
+ code_execution_20250522: {
821
+ family: "codeExecution",
822
+ name: "code_execution",
823
+ required: [],
824
+ optional: [...COMMON]
825
+ },
826
+ code_execution_20250825: {
827
+ family: "codeExecution",
828
+ name: "code_execution",
829
+ required: [],
830
+ optional: [...COMMON]
831
+ },
832
+ code_execution_20260120: {
833
+ family: "codeExecution",
834
+ name: "code_execution",
835
+ required: [],
836
+ optional: [...COMMON]
837
+ },
838
+ code_execution_20260521: {
839
+ family: "codeExecution",
840
+ name: "code_execution",
841
+ required: [],
842
+ optional: [...COMMON]
843
+ },
844
+ bash_20241022: {
845
+ family: "bash",
846
+ name: "bash",
847
+ required: [],
848
+ optional: [...COMMON_WITH_EXAMPLES]
849
+ },
850
+ bash_20250124: {
851
+ family: "bash",
852
+ name: "bash",
853
+ required: [],
854
+ optional: [...COMMON_WITH_EXAMPLES]
855
+ },
856
+ text_editor_20241022: {
857
+ family: "textEditor",
858
+ name: "str_replace_editor",
859
+ required: [],
860
+ optional: [...COMMON_WITH_EXAMPLES]
861
+ },
862
+ text_editor_20250124: {
863
+ family: "textEditor",
864
+ name: "str_replace_editor",
865
+ required: [],
866
+ optional: [...COMMON_WITH_EXAMPLES]
867
+ },
868
+ text_editor_20250429: {
869
+ family: "textEditor",
870
+ name: "str_replace_based_edit_tool",
871
+ required: [],
872
+ optional: [...COMMON_WITH_EXAMPLES]
873
+ },
874
+ text_editor_20250728: {
875
+ family: "textEditor",
876
+ name: "str_replace_based_edit_tool",
877
+ required: [],
878
+ optional: [...COMMON_WITH_EXAMPLES, "max_characters"]
879
+ },
880
+ computer_20241022: {
881
+ family: "computer",
882
+ name: "computer",
883
+ required: ["display_width_px", "display_height_px"],
884
+ optional: [...COMMON_WITH_EXAMPLES, "display_number"]
885
+ },
886
+ computer_20250124: {
887
+ family: "computer",
888
+ name: "computer",
889
+ required: ["display_width_px", "display_height_px"],
890
+ optional: [...COMMON_WITH_EXAMPLES, "display_number"]
891
+ },
892
+ computer_20251124: {
893
+ family: "computer",
894
+ name: "computer",
895
+ required: ["display_width_px", "display_height_px"],
896
+ optional: [...COMMON_WITH_EXAMPLES, "display_number", "enable_zoom"]
897
+ },
898
+ memory_20250818: {
899
+ family: "memory",
900
+ name: "memory",
901
+ required: [],
902
+ optional: [...COMMON_WITH_EXAMPLES]
903
+ },
904
+ tool_search_tool_regex_20251119: {
905
+ family: "toolSearchRegex",
906
+ name: "tool_search_tool_regex",
907
+ required: [],
908
+ optional: [...COMMON]
909
+ },
910
+ tool_search_tool_regex: {
911
+ family: "toolSearchRegex",
912
+ name: "tool_search_tool_regex",
913
+ required: [],
914
+ optional: [...COMMON]
915
+ },
916
+ tool_search_tool_bm25_20251119: {
917
+ family: "toolSearchBm25",
918
+ name: "tool_search_tool_bm25",
919
+ required: [],
920
+ optional: [...COMMON]
921
+ },
922
+ tool_search_tool_bm25: {
923
+ family: "toolSearchBm25",
924
+ name: "tool_search_tool_bm25",
925
+ required: [],
926
+ optional: [...COMMON]
927
+ },
928
+ advisor_20260301: {
929
+ family: "advisor",
930
+ name: "advisor",
931
+ required: ["model"],
932
+ optional: [...COMMON, "caching", "max_uses", "max_tokens"]
933
+ },
934
+ mcp_toolset: {
935
+ family: "mcpToolset",
936
+ required: ["mcp_server_name"],
937
+ optional: ["cache_control", "configs", "default_config"]
938
+ }
939
+ };
940
+ var ANTHROPIC_NATIVE_BLOCK_TYPES = new Set([
941
+ "server_tool_use",
942
+ "web_search_tool_result",
943
+ "web_fetch_tool_result",
944
+ "code_execution_tool_result",
945
+ "bash_code_execution_tool_result",
946
+ "text_editor_code_execution_tool_result",
947
+ "mcp_tool_use",
948
+ "mcp_tool_result",
949
+ "tool_search_tool_result",
950
+ "tool_reference",
951
+ "advisor_tool_result",
952
+ "advisor_result",
953
+ "advisor_redacted_result",
954
+ "container_upload",
955
+ "compaction",
956
+ "search_result",
957
+ "document",
958
+ "mid_conv_system",
959
+ "tool_addition",
960
+ "tool_removal",
961
+ "fallback",
962
+ "redacted_thinking"
963
+ ]);
964
+
762
965
  // packages/providers/src/anthropic/decode.ts
763
966
  var STOP_REASON = {
764
967
  end_turn: "endTurn",
765
968
  max_tokens: "maxTokens",
766
969
  stop_sequence: "stopSequence",
767
970
  tool_use: "toolUse",
768
- refusal: "contentFilter"
971
+ refusal: "contentFilter",
972
+ pause_turn: "pauseTurn"
769
973
  };
974
+ var KNOWN_EVENTS = new Set([
975
+ "message_start",
976
+ "content_block_start",
977
+ "content_block_delta",
978
+ "content_block_stop",
979
+ "message_delta",
980
+ "message_stop",
981
+ "error",
982
+ "ping"
983
+ ]);
984
+ var KNOWN_DELTAS = new Set([
985
+ "text_delta",
986
+ "thinking_delta",
987
+ "signature_delta",
988
+ "input_json_delta",
989
+ "citations_delta",
990
+ "compaction_delta"
991
+ ]);
770
992
  var ERROR_TYPE = {
771
993
  overloaded_error: "OVERLOADED",
772
994
  rate_limit_error: "RATE_LIMIT",
@@ -792,10 +1014,21 @@ async function* decodeAnthropic(messages) {
792
1014
  let outputTokens = 0;
793
1015
  let stopReason = "endTurn";
794
1016
  let terminal = false;
1017
+ const nativeBlocks = new Set;
1018
+ const protocolError = (message) => ({
1019
+ type: "error",
1020
+ code: "UPSTREAM",
1021
+ message,
1022
+ retryable: false
1023
+ });
795
1024
  for await (const msg of messages) {
796
1025
  const d = json(msg.data);
797
1026
  if (d === null)
798
1027
  continue;
1028
+ if (!KNOWN_EVENTS.has(msg.event)) {
1029
+ yield protocolError(`unrecognized Anthropic stream event "${msg.event}"`);
1030
+ return;
1031
+ }
799
1032
  switch (msg.event) {
800
1033
  case "message_start": {
801
1034
  const m = d.message ?? {};
@@ -820,6 +1053,18 @@ async function* decodeAnthropic(messages) {
820
1053
  index,
821
1054
  block: { type: "toolUse", id: String(cb.id), name: String(cb.name) }
822
1055
  };
1056
+ else if (cb.type !== undefined && ANTHROPIC_NATIVE_BLOCK_TYPES.has(cb.type)) {
1057
+ nativeBlocks.add(index);
1058
+ const { type: _blockType, ...data } = cb;
1059
+ yield {
1060
+ type: "blockStart",
1061
+ index,
1062
+ block: { type: "anthropicNative", blockType: cb.type, data }
1063
+ };
1064
+ } else {
1065
+ yield protocolError(`unrecognized Anthropic content block type "${String(cb.type)}"`);
1066
+ return;
1067
+ }
823
1068
  break;
824
1069
  }
825
1070
  case "content_block_delta": {
@@ -843,17 +1088,51 @@ async function* decodeAnthropic(messages) {
843
1088
  yield {
844
1089
  type: "blockDelta",
845
1090
  index,
846
- delta: { type: "toolJson", partial: delta.partial_json ?? "" }
1091
+ delta: nativeBlocks.has(index) ? { type: "anthropicNativeJson", partial: delta.partial_json ?? "" } : { type: "toolJson", partial: delta.partial_json ?? "" }
1092
+ };
1093
+ else if (delta.type === "citations_delta")
1094
+ yield {
1095
+ type: "blockDelta",
1096
+ index,
1097
+ delta: {
1098
+ type: "anthropicNative",
1099
+ deltaType: delta.type,
1100
+ data: { citation: delta.citation }
1101
+ }
847
1102
  };
1103
+ else if (delta.type === "compaction_delta")
1104
+ yield {
1105
+ type: "blockDelta",
1106
+ index,
1107
+ delta: {
1108
+ type: "anthropicNative",
1109
+ deltaType: delta.type,
1110
+ data: {
1111
+ ...delta.content === undefined ? {} : { content: delta.content },
1112
+ ...delta.encrypted_content === undefined ? {} : { encrypted_content: delta.encrypted_content }
1113
+ }
1114
+ }
1115
+ };
1116
+ else if (delta.type === undefined || !KNOWN_DELTAS.has(delta.type)) {
1117
+ yield protocolError(`unrecognized Anthropic content block delta "${String(delta.type)}"`);
1118
+ return;
1119
+ }
848
1120
  break;
849
1121
  }
850
1122
  case "content_block_stop":
1123
+ nativeBlocks.delete(d.index ?? 0);
851
1124
  yield { type: "blockEnd", index: d.index ?? 0 };
852
1125
  break;
853
1126
  case "message_delta": {
854
1127
  const reason = d.delta?.stop_reason;
855
- if (typeof reason === "string")
856
- stopReason = STOP_REASON[reason] ?? "endTurn";
1128
+ if (typeof reason === "string") {
1129
+ const mapped = STOP_REASON[reason];
1130
+ if (mapped === undefined) {
1131
+ yield protocolError(`unrecognized Anthropic stop reason "${reason}"`);
1132
+ return;
1133
+ }
1134
+ stopReason = mapped;
1135
+ }
857
1136
  inputTokens = d.usage?.input_tokens ?? inputTokens;
858
1137
  outputTokens = d.usage?.output_tokens ?? outputTokens;
859
1138
  break;
@@ -912,7 +1191,12 @@ function encodeBlock(b) {
912
1191
  const cache = wireCacheControl(cacheControlOf(b));
913
1192
  switch (b.type) {
914
1193
  case "text":
915
- return { type: "text", text: b.text, ...cache };
1194
+ return {
1195
+ type: "text",
1196
+ text: b.text,
1197
+ ...b.citations === undefined ? {} : { citations: b.citations },
1198
+ ...cache
1199
+ };
916
1200
  case "image":
917
1201
  return {
918
1202
  type: "image",
@@ -931,11 +1215,16 @@ function encodeBlock(b) {
931
1215
  is_error: b.isError,
932
1216
  ...cache
933
1217
  };
1218
+ case "anthropicNative":
1219
+ return { ...b.data, type: b.blockType, ...cache };
934
1220
  }
935
1221
  }
936
1222
  function encodeSystemTurn(content) {
937
- return content.flatMap((b) => b.type === "text" ? [b.text] : []).join(`
1223
+ if (content.every((block) => block.type === "text")) {
1224
+ return content.map((block) => block.text).join(`
938
1225
  `);
1226
+ }
1227
+ return content.map(encodeBlock);
939
1228
  }
940
1229
  function systemCacheControl(req) {
941
1230
  const cacheable = req.messages.flatMap((message) => message.content.flatMap((block) => block.type === "thinking" ? [] : [{ role: message.role, block }]));
@@ -947,6 +1236,23 @@ function systemCacheControl(req) {
947
1236
  lost: markedSystemBlocks.length > (promoted === undefined ? 0 : 1)
948
1237
  };
949
1238
  }
1239
+ function encodeTool(t) {
1240
+ if (t.provider === "anthropic") {
1241
+ return {
1242
+ type: t.type,
1243
+ ...t.name === "" ? {} : { name: t.name },
1244
+ ...t.wire,
1245
+ ...wireCacheControl(t.cacheControl)
1246
+ };
1247
+ }
1248
+ return {
1249
+ name: t.name,
1250
+ ...t.description === undefined ? {} : { description: t.description },
1251
+ input_schema: t.inputSchema,
1252
+ ...t.options ?? {},
1253
+ ...wireCacheControl(t.cacheControl)
1254
+ };
1255
+ }
950
1256
  function encodeToolChoice(c) {
951
1257
  switch (c.type) {
952
1258
  case "auto":
@@ -1001,14 +1307,8 @@ function toWire(req, model, opts) {
1001
1307
  body.temperature = req.temperature;
1002
1308
  if (req.stopSequences !== undefined)
1003
1309
  body.stop_sequences = req.stopSequences;
1004
- if (req.tools !== undefined) {
1005
- body.tools = req.tools.map((t) => ({
1006
- name: t.name,
1007
- ...t.description === undefined ? {} : { description: t.description },
1008
- input_schema: t.inputSchema,
1009
- ...wireCacheControl(t.cacheControl)
1010
- }));
1011
- }
1310
+ if (req.tools !== undefined)
1311
+ body.tools = req.tools.map(encodeTool);
1012
1312
  if (req.toolChoice !== undefined)
1013
1313
  body.tool_choice = encodeToolChoice(req.toolChoice);
1014
1314
  if (req.reasoning !== undefined) {
@@ -1355,6 +1655,9 @@ function toChatWire(req, model) {
1355
1655
  content: block.content
1356
1656
  });
1357
1657
  break;
1658
+ case "anthropicNative":
1659
+ note2("kimi:anthropic-native-block-dropped");
1660
+ break;
1358
1661
  }
1359
1662
  }
1360
1663
  if (toolCalls.length > 0) {
@@ -1382,7 +1685,10 @@ function toChatWire(req, model) {
1382
1685
  if (req.stopSequences !== undefined)
1383
1686
  body.stop = req.stopSequences;
1384
1687
  if (req.tools !== undefined) {
1385
- body.tools = req.tools.map((t) => ({
1688
+ const custom = req.tools.filter((t) => t.provider === "custom");
1689
+ if (custom.length !== req.tools.length)
1690
+ note2("kimi:anthropic-tool-dropped");
1691
+ body.tools = custom.map((t) => ({
1386
1692
  type: "function",
1387
1693
  function: { name: t.name, description: t.description, parameters: t.inputSchema }
1388
1694
  }));
@@ -1649,6 +1955,9 @@ ${block.text}
1649
1955
  output: block.content
1650
1956
  });
1651
1957
  break;
1958
+ case "anthropicNative":
1959
+ note2("openai:anthropic-native-block-dropped");
1960
+ break;
1652
1961
  }
1653
1962
  }
1654
1963
  flush();
@@ -1672,7 +1981,10 @@ ${block.text}
1672
1981
  body.temperature = req.temperature;
1673
1982
  }
1674
1983
  if (req.tools !== undefined) {
1675
- body.tools = req.tools.map((t) => ({
1984
+ const custom = req.tools.filter((t) => t.provider === "custom");
1985
+ if (custom.length !== req.tools.length)
1986
+ note2("openai:anthropic-tool-dropped");
1987
+ body.tools = custom.map((t) => ({
1676
1988
  type: "function",
1677
1989
  name: t.name,
1678
1990
  description: t.description,
@@ -16638,11 +16950,12 @@ var settingsSchema = exports_external.object({
16638
16950
  recency: exports_external.number()
16639
16951
  }).strict(),
16640
16952
  maxAttempts: exports_external.number().int().min(1).max(10),
16641
- requestDeadlineMs: exports_external.number().int().positive(),
16953
+ requestDeadlineMs: exports_external.number().int().min(0),
16642
16954
  breakerThreshold: exports_external.number().int().min(1),
16643
16955
  breakerCooldownMs: exports_external.number().int().positive(),
16644
16956
  logRetentionDays: exports_external.number().int().min(1),
16645
- quotaPollIntervalMs: exports_external.number().int().min(0)
16957
+ quotaPollIntervalMs: exports_external.number().int().min(0),
16958
+ rtkEnabled: exports_external.boolean()
16646
16959
  });
16647
16960
  var credentialPatchSchema = exports_external.object({
16648
16961
  label: exports_external.string().min(1).optional(),
@@ -16829,6 +17142,11 @@ async function buildSnapshot(store, now) {
16829
17142
  }
16830
17143
 
16831
17144
  // packages/router/src/filters.ts
17145
+ function needsAnthropicNative(request2) {
17146
+ if (request2.tools?.some((t) => t.provider === "anthropic") === true)
17147
+ return true;
17148
+ return request2.messages.some((m) => m.content.some((b) => b.type === "anthropicNative"));
17149
+ }
16832
17150
  function requiredCapabilities(request2) {
16833
17151
  const images = request2.messages.some((m) => m.content.some((b) => b.type === "image"));
16834
17152
  return {
@@ -16845,10 +17163,11 @@ function eligible(input) {
16845
17163
  const { request: request2, model, snapshot, now } = input;
16846
17164
  const { breakerThreshold, breakerCooldownMs } = snapshot.settings;
16847
17165
  const need = requiredCapabilities(request2);
17166
+ const needNative = needsAnthropicNative(request2);
16848
17167
  const pairs = [];
16849
17168
  const excluded = [];
16850
17169
  for (const target of model.targets) {
16851
- const missing = ["tools", "images", "reasoning"].find((cap) => need[cap] && !target.capabilities[cap]);
17170
+ const missing = needNative && !ANTHROPIC_NATIVE_TOOLS[target.provider] ? "anthropicTools" : ["tools", "images", "reasoning"].find((cap) => need[cap] && !target.capabilities[cap]);
16852
17171
  for (const credential of snapshot.credentials) {
16853
17172
  if (credential.provider !== target.provider)
16854
17173
  continue;
@@ -17043,7 +17362,11 @@ async function dryRun(deps, modelId, input) {
17043
17362
  }
17044
17363
  ],
17045
17364
  stream: false,
17046
- ...need.tools ? { tools: [{ name: "probe", description: "", inputSchema: { type: "object" } }] } : {},
17365
+ ...need.tools ? {
17366
+ tools: [
17367
+ { provider: "custom", name: "probe", description: "", inputSchema: { type: "object" } }
17368
+ ]
17369
+ } : {},
17047
17370
  ...need.reasoning ? { reasoning: { mode: "adaptive" } } : {}
17048
17371
  };
17049
17372
  const result = rank({ request: probe, model, snapshot, now, rand: 0 });
@@ -17111,7 +17434,8 @@ var DEFAULT_SETTINGS = {
17111
17434
  breakerThreshold: 3,
17112
17435
  breakerCooldownMs: 30000,
17113
17436
  logRetentionDays: 30,
17114
- quotaPollIntervalMs: 300000
17437
+ quotaPollIntervalMs: 300000,
17438
+ rtkEnabled: false
17115
17439
  };
17116
17440
 
17117
17441
  // packages/store/src/sqlite/config.ts
@@ -17147,12 +17471,18 @@ function createConfigRepo(db, emit2 = () => {}) {
17147
17471
  const raw = readRaw(SETTINGS_KEY);
17148
17472
  if (raw === null)
17149
17473
  return DEFAULT_SETTINGS;
17150
- const stored = JSON.parse(raw);
17151
- return {
17152
- ...DEFAULT_SETTINGS,
17153
- ...stored,
17154
- weights: { ...DEFAULT_SETTINGS.weights, ...stored.weights }
17155
- };
17474
+ try {
17475
+ const parsed = JSON.parse(raw);
17476
+ const stored = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
17477
+ return {
17478
+ ...DEFAULT_SETTINGS,
17479
+ ...stored,
17480
+ rtkEnabled: stored.rtkEnabled === true,
17481
+ weights: { ...DEFAULT_SETTINGS.weights, ...stored.weights }
17482
+ };
17483
+ } catch {
17484
+ return DEFAULT_SETTINGS;
17485
+ }
17156
17486
  },
17157
17487
  async putSettings(patch) {
17158
17488
  const current = await this.getSettings();
@@ -17594,6 +17924,20 @@ ALTER TABLE request_logs ADD COLUMN state TEXT NOT NULL DEFAULT 'done';
17594
17924
  CREATE INDEX idx_request_logs_pending ON request_logs(state) WHERE state = 'pending';
17595
17925
  `;
17596
17926
 
17927
+ // packages/store/src/sqlite/migrations/005_rtk_metrics.sql
17928
+ var _005_rtk_metrics_default = `ALTER TABLE request_logs ADD COLUMN rtk_applied INTEGER NOT NULL DEFAULT 0;
17929
+ ALTER TABLE request_logs ADD COLUMN rtk_filter_hits INTEGER NOT NULL DEFAULT 0;
17930
+ ALTER TABLE request_logs ADD COLUMN rtk_original_code_units INTEGER NOT NULL DEFAULT 0;
17931
+ ALTER TABLE request_logs ADD COLUMN rtk_compressed_code_units INTEGER NOT NULL DEFAULT 0;
17932
+ ALTER TABLE request_logs ADD COLUMN rtk_estimated_tokens_saved INTEGER NOT NULL DEFAULT 0;
17933
+ ALTER TABLE request_logs ADD COLUMN rtk_filters TEXT NOT NULL DEFAULT '[]';
17934
+ `;
17935
+
17936
+ // packages/store/src/sqlite/migrations/006_rtk_usage.sql
17937
+ var _006_rtk_usage_default = `ALTER TABLE usage_daily ADD COLUMN rtk_saved_tokens INTEGER NOT NULL DEFAULT 0;
17938
+ ALTER TABLE usage_daily ADD COLUMN rtk_applied_requests INTEGER NOT NULL DEFAULT 0;
17939
+ `;
17940
+
17597
17941
  // packages/store/src/sqlite/rollup.ts
17598
17942
  function startOfLocalDay(at) {
17599
17943
  const day = new Date(at);
@@ -17604,8 +17948,8 @@ var UPSERT = `
17604
17948
  INSERT INTO usage_daily
17605
17949
  (day, provider, credential_id, requested_model, resolved_model, api_key_id,
17606
17950
  requests, errors, input_tokens, output_tokens, cache_read_tokens,
17607
- cache_write_tokens, cost_usd, duration_ms_sum)
17608
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
17951
+ cache_write_tokens, rtk_saved_tokens, rtk_applied_requests, cost_usd, duration_ms_sum)
17952
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
17609
17953
  ON CONFLICT (day, provider, credential_id, requested_model, resolved_model, api_key_id)
17610
17954
  DO UPDATE SET
17611
17955
  requests = requests + excluded.requests,
@@ -17613,8 +17957,10 @@ var UPSERT = `
17613
17957
  input_tokens = input_tokens + excluded.input_tokens,
17614
17958
  output_tokens = output_tokens + excluded.output_tokens,
17615
17959
  cache_read_tokens = cache_read_tokens + excluded.cache_read_tokens,
17616
- cache_write_tokens = cache_write_tokens + excluded.cache_write_tokens,
17617
- cost_usd = cost_usd + excluded.cost_usd,
17960
+ cache_write_tokens = cache_write_tokens + excluded.cache_write_tokens,
17961
+ rtk_saved_tokens = rtk_saved_tokens + excluded.rtk_saved_tokens,
17962
+ rtk_applied_requests = rtk_applied_requests + excluded.rtk_applied_requests,
17963
+ cost_usd = cost_usd + excluded.cost_usd,
17618
17964
  duration_ms_sum = duration_ms_sum + excluded.duration_ms_sum`;
17619
17965
  function keyOf(log) {
17620
17966
  return [
@@ -17634,6 +17980,8 @@ function countersOf(log) {
17634
17980
  outputTokens: log.outputTokens,
17635
17981
  cacheReadTokens: log.cacheReadTokens,
17636
17982
  cacheWriteTokens: log.cacheWriteTokens,
17983
+ rtkSavedTokens: log.rtkEstimatedTokensSaved,
17984
+ rtkAppliedRequests: log.rtkApplied ? 1 : 0,
17637
17985
  costUsd: log.costUsd,
17638
17986
  durationMsSum: log.durationMs
17639
17987
  };
@@ -17647,6 +17995,8 @@ function upsert(db, key, c) {
17647
17995
  c.outputTokens,
17648
17996
  c.cacheReadTokens,
17649
17997
  c.cacheWriteTokens,
17998
+ c.rtkSavedTokens,
17999
+ c.rtkAppliedRequests,
17650
18000
  c.costUsd,
17651
18001
  c.durationMsSum
17652
18002
  ]);
@@ -17677,6 +18027,8 @@ function backfillDaily(db) {
17677
18027
  outputTokens: 0,
17678
18028
  cacheReadTokens: 0,
17679
18029
  cacheWriteTokens: 0,
18030
+ rtkSavedTokens: 0,
18031
+ rtkAppliedRequests: 0,
17680
18032
  costUsd: 0,
17681
18033
  durationMsSum: 0
17682
18034
  };
@@ -17695,13 +18047,42 @@ function backfillDaily(db) {
17695
18047
  upsert(db, group.key, group.counters);
17696
18048
  return groups.size;
17697
18049
  }
18050
+ function backfillRtkUsage(db) {
18051
+ const groups = new Map;
18052
+ for (const row of db.query(`SELECT at, api_key_id, requested_model, resolved_provider, resolved_model, credential_id,
18053
+ rtk_applied, rtk_estimated_tokens_saved
18054
+ FROM request_logs
18055
+ WHERE state = 'done'`).all()) {
18056
+ const key = [
18057
+ startOfLocalDay(row.at),
18058
+ row.resolved_provider ?? "",
18059
+ row.credential_id ?? "",
18060
+ row.requested_model,
18061
+ row.resolved_model ?? "",
18062
+ row.api_key_id ?? ""
18063
+ ];
18064
+ const id = key.join("\x00");
18065
+ const group = groups.get(id) ?? { key, saved: 0, applied: 0 };
18066
+ group.saved += row.rtk_estimated_tokens_saved;
18067
+ group.applied += row.rtk_applied === 1 ? 1 : 0;
18068
+ groups.set(id, group);
18069
+ }
18070
+ for (const { key, saved, applied } of groups.values()) {
18071
+ db.run(`UPDATE usage_daily
18072
+ SET rtk_saved_tokens = ?, rtk_applied_requests = ?
18073
+ WHERE day = ? AND provider = ? AND credential_id = ? AND requested_model = ?
18074
+ AND resolved_model = ? AND api_key_id = ?`, [saved, applied, ...key]);
18075
+ }
18076
+ }
17698
18077
 
17699
18078
  // packages/store/src/sqlite/db.ts
17700
18079
  var MIGRATIONS = [
17701
18080
  { id: 1, sql: _001_init_default },
17702
18081
  { id: 2, sql: _002_usage_daily_default, after: backfillDaily },
17703
18082
  { id: 3, sql: _003_quota_snapshot_default },
17704
- { id: 4, sql: _004_request_state_default }
18083
+ { id: 4, sql: _004_request_state_default },
18084
+ { id: 5, sql: _005_rtk_metrics_default },
18085
+ { id: 6, sql: _006_rtk_usage_default, after: backfillRtkUsage }
17705
18086
  ];
17706
18087
  function openDb(path) {
17707
18088
  const db = new Database(path, { create: true });
@@ -17770,7 +18151,38 @@ function createKeyRepo(db) {
17770
18151
  }
17771
18152
  };
17772
18153
  }
18154
+ // packages/rtk/src/catalog.ts
18155
+ var RTK_FILTER_IDS = [
18156
+ "git-diff",
18157
+ "git-status",
18158
+ "git-log",
18159
+ "grep",
18160
+ "path-list",
18161
+ "numbered-read",
18162
+ "build-output",
18163
+ "test-output",
18164
+ "deduplicate-log",
18165
+ "smart-truncate",
18166
+ "lint-output",
18167
+ "package-output",
18168
+ "tree-output",
18169
+ "git-operation",
18170
+ "docker-build"
18171
+ ];
18172
+ var FILTER_IDS = new Set(RTK_FILTER_IDS);
18173
+ function isRtkFilterId(value) {
18174
+ return FILTER_IDS.has(value);
18175
+ }
18176
+
17773
18177
  // packages/store/src/sqlite/usage.ts
18178
+ function parseRtkFilters(raw) {
18179
+ try {
18180
+ const parsed = JSON.parse(raw);
18181
+ return Array.isArray(parsed) ? parsed.filter(isRtkFilterId) : [];
18182
+ } catch {
18183
+ return [];
18184
+ }
18185
+ }
17774
18186
  var toLog = (r) => ({
17775
18187
  id: r.id,
17776
18188
  state: r.state === "pending" ? "pending" : "done",
@@ -17790,7 +18202,13 @@ var toLog = (r) => ({
17790
18202
  ttftMs: r.ttft_ms,
17791
18203
  durationMs: r.duration_ms,
17792
18204
  costUsd: r.cost_usd,
17793
- degradations: JSON.parse(r.degradations)
18205
+ degradations: JSON.parse(r.degradations),
18206
+ rtkApplied: r.rtk_applied === 1,
18207
+ rtkFilterHits: r.rtk_filter_hits,
18208
+ rtkOriginalCodeUnits: r.rtk_original_code_units,
18209
+ rtkCompressedCodeUnits: r.rtk_compressed_code_units,
18210
+ rtkEstimatedTokensSaved: r.rtk_estimated_tokens_saved,
18211
+ rtkFilters: parseRtkFilters(r.rtk_filters)
17794
18212
  });
17795
18213
  var GROUP_COLUMN = {
17796
18214
  raw: {
@@ -17835,8 +18253,9 @@ function label(value) {
17835
18253
  var COLUMNS = `(id, state, at, api_key_id, requested_model, resolved_provider, resolved_model,
17836
18254
  credential_id, attempts, status, error_code, input_tokens, output_tokens,
17837
18255
  cache_read_tokens, cache_write_tokens, ttft_ms, duration_ms, cost_usd,
17838
- degradations)`;
17839
- var PLACEHOLDERS = "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
18256
+ degradations, rtk_applied, rtk_filter_hits, rtk_original_code_units,
18257
+ rtk_compressed_code_units, rtk_estimated_tokens_saved, rtk_filters)`;
18258
+ var PLACEHOLDERS = "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
17840
18259
  function values(log, state) {
17841
18260
  return [
17842
18261
  log.id,
@@ -17857,7 +18276,13 @@ function values(log, state) {
17857
18276
  log.ttftMs,
17858
18277
  log.durationMs,
17859
18278
  log.costUsd,
17860
- JSON.stringify(log.degradations)
18279
+ JSON.stringify(log.degradations),
18280
+ log.rtkApplied === true ? 1 : 0,
18281
+ log.rtkFilterHits ?? 0,
18282
+ log.rtkOriginalCodeUnits ?? 0,
18283
+ log.rtkCompressedCodeUnits ?? 0,
18284
+ log.rtkEstimatedTokensSaved ?? 0,
18285
+ JSON.stringify(log.rtkFilters ?? [])
17861
18286
  ];
17862
18287
  }
17863
18288
  var COMPLETE = `INSERT INTO request_logs ${COLUMNS} VALUES ${PLACEHOLDERS}
@@ -17878,7 +18303,13 @@ var COMPLETE = `INSERT INTO request_logs ${COLUMNS} VALUES ${PLACEHOLDERS}
17878
18303
  ttft_ms = excluded.ttft_ms,
17879
18304
  duration_ms = excluded.duration_ms,
17880
18305
  cost_usd = excluded.cost_usd,
17881
- degradations = excluded.degradations`;
18306
+ degradations = excluded.degradations,
18307
+ rtk_applied = excluded.rtk_applied,
18308
+ rtk_filter_hits = excluded.rtk_filter_hits,
18309
+ rtk_original_code_units = excluded.rtk_original_code_units,
18310
+ rtk_compressed_code_units = excluded.rtk_compressed_code_units,
18311
+ rtk_estimated_tokens_saved = excluded.rtk_estimated_tokens_saved,
18312
+ rtk_filters = excluded.rtk_filters`;
17882
18313
  function createUsageRepo(db) {
17883
18314
  const complete = db.transaction((log) => {
17884
18315
  db.run(COMPLETE, values(log, "done"));
@@ -17923,6 +18354,8 @@ function createUsageRepo(db) {
17923
18354
  COALESCE(SUM(output_tokens), 0) AS output_tokens,
17924
18355
  COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens,
17925
18356
  COALESCE(SUM(cache_write_tokens), 0) AS cache_write_tokens,
18357
+ COALESCE(SUM(${daily ? "rtk_saved_tokens" : "rtk_estimated_tokens_saved"}), 0) AS rtk_saved_tokens,
18358
+ COALESCE(SUM(${daily ? "rtk_applied_requests" : "CASE WHEN rtk_applied = 1 THEN 1 ELSE 0 END"}), 0) AS rtk_applied_requests,
17926
18359
  COALESCE(SUM(cost_usd), 0) AS cost_usd
17927
18360
  FROM ${daily ? "usage_daily" : "request_logs"}
17928
18361
  WHERE ${daily ? "" : "state = 'done' AND "}${timeColumn} >= ? AND ${timeColumn} <= ?
@@ -17936,6 +18369,8 @@ function createUsageRepo(db) {
17936
18369
  outputTokens: r.output_tokens,
17937
18370
  cacheReadTokens: r.cache_read_tokens,
17938
18371
  cacheWriteTokens: r.cache_write_tokens,
18372
+ rtkSavedTokens: r.rtk_saved_tokens,
18373
+ rtkAppliedRequests: r.rtk_applied_requests,
17939
18374
  costUsd: r.cost_usd,
17940
18375
  errors: r.errors,
17941
18376
  durationMsSum: r.duration_ms_sum
@@ -18436,33 +18871,77 @@ async function describeModelsForSetup(store) {
18436
18871
  label: modelDisplayName(model)
18437
18872
  }));
18438
18873
  }
18439
- function slug(id) {
18440
- return encodeURIComponent(id).replace(/\./g, "%2E");
18441
- }
18442
- function claudeProfiles(described, input) {
18443
- const apiKey = input.apiKey ?? KEY_PLACEHOLDER;
18444
- return described.map(({ model, limits }) => {
18445
- const useMirror = input.discoveryMirrors === true && !/^(?:claude|anthropic)/i.test(model.id);
18446
- const modelId = useMirror ? `claude/${model.id}` : model.id;
18447
- const env2 = {
18448
- ANTHROPIC_BASE_URL: input.baseUrl,
18449
- ANTHROPIC_AUTH_TOKEN: apiKey,
18450
- ANTHROPIC_MODEL: modelId,
18451
- CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1"
18452
- };
18453
- if (limits.contextWindow !== undefined && !/^claude-/i.test(modelId)) {
18454
- env2.CLAUDE_CODE_MAX_CONTEXT_TOKENS = String(limits.contextWindow);
18874
+ var CLAUDE_MAPPING_KEYS = {
18875
+ defaultModel: "ANTHROPIC_MODEL",
18876
+ fableModel: "ANTHROPIC_DEFAULT_FABLE_MODEL",
18877
+ opusModel: "ANTHROPIC_DEFAULT_OPUS_MODEL",
18878
+ sonnetModel: "ANTHROPIC_DEFAULT_SONNET_MODEL",
18879
+ haikuModel: "ANTHROPIC_DEFAULT_HAIKU_MODEL"
18880
+ };
18881
+ function settingsObject(existing) {
18882
+ if (existing === undefined)
18883
+ return {};
18884
+ try {
18885
+ const parsed = JSON.parse(existing);
18886
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
18887
+ throw new Error("settings root is not an object");
18455
18888
  }
18456
- return {
18457
- path: `${slug(model.id)}/settings.json`,
18458
- contents: `${JSON.stringify({ env: env2 }, null, 2)}
18889
+ return parsed;
18890
+ } catch (error51) {
18891
+ const reason = error51 instanceof Error ? error51.message : "invalid JSON";
18892
+ throw new Error(`cannot parse existing settings.json: ${reason}`);
18893
+ }
18894
+ }
18895
+ function claudeSettings(described, input, mapping, existing) {
18896
+ if (mapping.defaultModel === "")
18897
+ throw new Error("default model is required");
18898
+ const ids = new Set(described.map(({ model }) => model.id));
18899
+ const visibleId = (slot, id) => {
18900
+ if (!ids.has(id))
18901
+ throw new Error(`${slot} names unknown virtual model "${id}"`);
18902
+ const useMirror = input.discoveryMirrors === true && !/^(?:claude|anthropic)/i.test(id);
18903
+ return useMirror ? `claude/${id}` : id;
18904
+ };
18905
+ const settings = settingsObject(existing);
18906
+ const currentEnv = settings.env;
18907
+ const env2 = typeof currentEnv === "object" && currentEnv !== null && !Array.isArray(currentEnv) ? { ...currentEnv } : {};
18908
+ for (const key of Object.values(CLAUDE_MAPPING_KEYS))
18909
+ delete env2[key];
18910
+ delete env2.CLAUDE_CODE_MAX_CONTEXT_TOKENS;
18911
+ env2.ANTHROPIC_BASE_URL = input.baseUrl;
18912
+ env2.ANTHROPIC_AUTH_TOKEN = input.apiKey ?? KEY_PLACEHOLDER;
18913
+ env2.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1";
18914
+ for (const [slot, key] of Object.entries(CLAUDE_MAPPING_KEYS)) {
18915
+ const id = mapping[slot];
18916
+ if (id !== undefined && id !== "")
18917
+ env2[key] = visibleId(slot, id);
18918
+ }
18919
+ return {
18920
+ path: "settings.json",
18921
+ contents: `${JSON.stringify({ ...settings, env: env2 }, null, 2)}
18459
18922
  `
18460
- };
18461
- });
18923
+ };
18462
18924
  }
18463
- function opencodeConfig(described, input) {
18925
+ function opencodeConfig(described, input, mapping) {
18926
+ if (mapping.defaultModel === "")
18927
+ throw new Error("default model is required");
18928
+ const byId = new Map(described.map((entry) => [entry.model.id, entry]));
18929
+ const selected = [];
18930
+ const seen = new Set;
18931
+ for (const slot of Object.keys(CLAUDE_MAPPING_KEYS)) {
18932
+ const id = mapping[slot];
18933
+ if (id === undefined || id === "")
18934
+ continue;
18935
+ const entry = byId.get(id);
18936
+ if (entry === undefined)
18937
+ throw new Error(`${slot} names unknown virtual model "${id}"`);
18938
+ if (!seen.has(id)) {
18939
+ seen.add(id);
18940
+ selected.push(entry);
18941
+ }
18942
+ }
18464
18943
  const models = {};
18465
- for (const { model, limits, label: label2 } of described) {
18944
+ for (const { model, limits, label: label2 } of selected) {
18466
18945
  const limit = limits.contextWindow === undefined ? undefined : {
18467
18946
  context: limits.contextWindow,
18468
18947
  ...limits.maxOutputTokens === undefined ? {} : { output: limits.maxOutputTokens }
@@ -18471,6 +18950,7 @@ function opencodeConfig(described, input) {
18471
18950
  }
18472
18951
  const contents = `${JSON.stringify({
18473
18952
  $schema: "https://opencode.ai/config.json",
18953
+ model: `omnigateway/${mapping.defaultModel}`,
18474
18954
  provider: {
18475
18955
  omnigateway: {
18476
18956
  npm: "@ai-sdk/openai-compatible",
@@ -19867,7 +20347,7 @@ var settingsSet = {
19867
20347
  };
19868
20348
 
19869
20349
  // apps/cli/src/commands/setup.ts
19870
- import { dirname as dirname2, join as join4 } from "path";
20350
+ import { join as join4 } from "path";
19871
20351
  var OPTIONS = {
19872
20352
  dir: { type: "string" },
19873
20353
  key: { type: "string" },
@@ -19901,39 +20381,65 @@ ${f.contents}`).join(`
19901
20381
  function at(dir, file2) {
19902
20382
  return { path: join4(dir, file2.path), contents: file2.contents };
19903
20383
  }
20384
+ async function promptMapping(models, prompt) {
20385
+ const choices = models.map(({ model }) => model.id).join(", ");
20386
+ if (prompt.input === undefined) {
20387
+ throw new CliError("model mapping requires an interactive terminal");
20388
+ }
20389
+ const ask = async (label2, required2) => {
20390
+ const answer = await prompt.input?.(`${label2} model${required2 ? "" : " (blank to omit)"} [${choices}]: `);
20391
+ if (required2 && answer === "")
20392
+ throw new CliError("default model is required");
20393
+ return answer === "" ? undefined : answer;
20394
+ };
20395
+ const defaultModel = await ask("Default", true);
20396
+ if (defaultModel === undefined)
20397
+ throw new CliError("default model is required");
20398
+ const fableModel = await ask("Fable", false);
20399
+ const opusModel = await ask("Opus", false);
20400
+ const sonnetModel = await ask("Sonnet", false);
20401
+ const haikuModel = await ask("Haiku", false);
20402
+ return {
20403
+ defaultModel,
20404
+ ...fableModel === undefined ? {} : { fableModel },
20405
+ ...opusModel === undefined ? {} : { opusModel },
20406
+ ...sonnetModel === undefined ? {} : { sonnetModel },
20407
+ ...haikuModel === undefined ? {} : { haikuModel }
20408
+ };
20409
+ }
19904
20410
  var setupClaude = {
19905
20411
  usage: "setup claude [--dir <path>] [--key <key>] [--dry-run]",
19906
- summary: "Write a Claude Code profile per virtual model",
20412
+ summary: "Write Claude Code settings for this gateway",
19907
20413
  options: OPTIONS,
19908
- async run(args, { ctx, writer, setupFs }) {
20414
+ async run(args, { ctx, writer, prompt, setupFs }) {
19909
20415
  const models = await described(ctx);
19910
- const dir = stringFlag(args.values, "dir") ?? join4(setupFs.homeDir, ".claude", "profiles");
20416
+ const dir = stringFlag(args.values, "dir") ?? join4(setupFs.homeDir, ".claude");
19911
20417
  const key = stringFlag(args.values, "key");
19912
20418
  const dryRun2 = boolFlag(args.values, "dry-run");
19913
- const files = claudeProfiles(models, {
20419
+ const mapping = await promptMapping(models, prompt);
20420
+ const path = join4(dir, "settings.json");
20421
+ const file2 = claudeSettings(models, {
19914
20422
  baseUrl: baseUrl(ctx),
19915
20423
  discoveryMirrors: ctx.config().exposeClaudeCodeAliases,
19916
20424
  ...key === undefined ? {} : { apiKey: key }
19917
- }).map((file2) => at(dir, file2));
19918
- finish(ctx, writer, files, dryRun2, key, setupFs.write);
19919
- if (!dryRun2) {
19920
- note(ctx, writer, `run one with: CLAUDE_CONFIG_DIR=${dirname2(files[0]?.path ?? dir)} claude`);
19921
- }
20425
+ }, mapping, setupFs.read(path) ?? undefined);
20426
+ finish(ctx, writer, [at(dir, file2)], dryRun2, key, setupFs.write);
19922
20427
  }
19923
20428
  };
19924
20429
  var setupOpencode = {
19925
20430
  usage: "setup opencode [--dir <path>] [--key <key>] [--dry-run]",
19926
20431
  summary: "Write an opencode.json provider entry for this gateway",
19927
20432
  options: OPTIONS,
19928
- async run(args, { ctx, writer, setupFs }) {
20433
+ async run(args, { ctx, writer, prompt, setupFs }) {
19929
20434
  const models = await described(ctx);
19930
20435
  const dir = stringFlag(args.values, "dir") ?? setupFs.cwd;
19931
20436
  const key = stringFlag(args.values, "key");
19932
20437
  const dryRun2 = boolFlag(args.values, "dry-run");
20438
+ const mapping = await promptMapping(models, prompt);
19933
20439
  const file2 = opencodeConfig(models, {
19934
20440
  baseUrl: baseUrl(ctx),
19935
20441
  ...key === undefined ? {} : { apiKey: key }
19936
- });
20442
+ }, mapping);
19937
20443
  finish(ctx, writer, [at(dir, file2)], dryRun2, key, setupFs.write);
19938
20444
  }
19939
20445
  };
@@ -20316,6 +20822,12 @@ function createPrompt(ctx, writer) {
20316
20822
  const isTty = process.stdin.isTTY === true;
20317
20823
  return {
20318
20824
  isTty,
20825
+ async input(question) {
20826
+ if (!isTty)
20827
+ throw new CliError(`${question} \u2014 refusing without a terminal`);
20828
+ writer.err(question);
20829
+ return (await readLine()).trim();
20830
+ },
20319
20831
  async secret(question) {
20320
20832
  if (!isTty)
20321
20833
  return (await readAllStdin()).trim();
@@ -20336,6 +20848,20 @@ function createPrompt(ctx, writer) {
20336
20848
  };
20337
20849
  }
20338
20850
 
20851
+ // apps/cli/src/setupFs.ts
20852
+ import { dirname as dirname2 } from "path";
20853
+ function atomicWriteFile(path, contents, ops) {
20854
+ const temporary = `${path}.${process.pid}.tmp`;
20855
+ ops.mkdir(dirname2(path));
20856
+ try {
20857
+ ops.write(temporary, contents);
20858
+ ops.rename(temporary, path);
20859
+ } catch (error51) {
20860
+ ops.remove(temporary);
20861
+ throw error51;
20862
+ }
20863
+ }
20864
+
20339
20865
  // apps/cli/src/run.ts
20340
20866
  var VERSION = "0.0.0";
20341
20867
  async function run(argv, writer, options = {}) {
@@ -20377,10 +20903,13 @@ async function run(argv, writer, options = {}) {
20377
20903
  setupFs: options.setupFs ?? {
20378
20904
  homeDir: homedir3(),
20379
20905
  cwd: process.cwd(),
20380
- write: (path, contents) => {
20381
- mkdirSync2(dirname3(path), { recursive: true });
20382
- writeFileSync2(path, contents);
20383
- }
20906
+ read: (path) => existsSync6(path) ? readFileSync3(path, "utf8") : null,
20907
+ write: (path, contents) => atomicWriteFile(path, contents, {
20908
+ mkdir: (directory) => mkdirSync2(directory, { recursive: true }),
20909
+ write: (temporary, data) => writeFileSync2(temporary, data),
20910
+ rename: renameSync,
20911
+ remove: (temporary) => rmSync2(temporary, { force: true })
20912
+ })
20384
20913
  },
20385
20914
  service: () => options.service?.({ root: ctx.root.root, env: ctx.env }) ?? createServiceDeps({ root: ctx.root.root, env: ctx.env, scope, now: ctx.now }),
20386
20915
  foreground: options.foreground ?? runForeground,