proxitor 0.2.0 → 0.3.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 (52) hide show
  1. package/README.md +221 -81
  2. package/dist/add.cjs +139 -0
  3. package/dist/add.cjs.map +1 -0
  4. package/dist/add.mjs +138 -0
  5. package/dist/add.mjs.map +1 -0
  6. package/dist/browse.cjs +88 -0
  7. package/dist/browse.cjs.map +1 -0
  8. package/dist/browse.mjs +87 -0
  9. package/dist/browse.mjs.map +1 -0
  10. package/dist/cli.cjs +148 -25
  11. package/dist/cli.cjs.map +1 -1
  12. package/dist/cli.mjs +149 -26
  13. package/dist/cli.mjs.map +1 -1
  14. package/dist/config.cjs +68 -0
  15. package/dist/config.cjs.map +1 -0
  16. package/dist/config.mjs +45 -0
  17. package/dist/config.mjs.map +1 -0
  18. package/dist/config2.cjs +75 -0
  19. package/dist/config2.cjs.map +1 -0
  20. package/dist/config2.mjs +74 -0
  21. package/dist/config2.mjs.map +1 -0
  22. package/dist/edit.cjs +82 -0
  23. package/dist/edit.cjs.map +1 -0
  24. package/dist/edit.mjs +81 -0
  25. package/dist/edit.mjs.map +1 -0
  26. package/dist/index.cjs +2 -0
  27. package/dist/index.d.cts +223 -53
  28. package/dist/index.d.cts.map +1 -1
  29. package/dist/index.d.mts +223 -53
  30. package/dist/index.d.mts.map +1 -1
  31. package/dist/index.mjs +2 -2
  32. package/dist/list.cjs +33 -0
  33. package/dist/list.cjs.map +1 -0
  34. package/dist/list.mjs +31 -0
  35. package/dist/list.mjs.map +1 -0
  36. package/dist/providers.cjs +376 -0
  37. package/dist/providers.cjs.map +1 -0
  38. package/dist/providers.mjs +279 -0
  39. package/dist/providers.mjs.map +1 -0
  40. package/dist/proxy.cjs +128 -8
  41. package/dist/proxy.cjs.map +1 -1
  42. package/dist/proxy.mjs +99 -9
  43. package/dist/proxy.mjs.map +1 -1
  44. package/dist/remove.cjs +38 -0
  45. package/dist/remove.cjs.map +1 -0
  46. package/dist/remove.mjs +37 -0
  47. package/dist/remove.mjs.map +1 -0
  48. package/dist/validate.cjs +26 -0
  49. package/dist/validate.cjs.map +1 -0
  50. package/dist/validate.mjs +25 -0
  51. package/dist/validate.mjs.map +1 -0
  52. package/package.json +10 -3
package/dist/add.mjs ADDED
@@ -0,0 +1,138 @@
1
+ import { c as formatModelHint, f as fetchModels, g as OpenRouterClient, l as formatModelLabel, n as selectProvidersByMode, o as formatContextLength, p as formatPrice, r as selectRoutingMode, t as fetchProvidersForModel, u as formatPricing } from "./providers.mjs";
2
+ import { i as setModelOverride, r as requireConfigPath, t as getModelOverrides } from "./config.mjs";
3
+ import * as clack from "@clack/prompts";
4
+ import { isCancel } from "@clack/prompts";
5
+ //#region src/commands/config/add.ts
6
+ const CUSTOM_PATTERN = "__custom_pattern__";
7
+ /** Run the interactive "Add model override" flow. */
8
+ async function addOverrideCommand(apiKey) {
9
+ clack.intro("Add Model Override");
10
+ const configPath = requireConfigPath();
11
+ const client = new OpenRouterClient(apiKey);
12
+ const models = await loadModelsWithSpinner(client);
13
+ if (!models) return;
14
+ const modelId = await searchModel(models);
15
+ if (!modelId) return;
16
+ if (typeof modelId !== "string") return;
17
+ if (modelId === CUSTOM_PATTERN) {
18
+ const pattern = await enterPattern(models);
19
+ if (!pattern) return;
20
+ if (getModelOverrides(configPath)[pattern]) {
21
+ clack.log.warn(`Override for "${pattern}" already exists. Use Edit instead.`);
22
+ return;
23
+ }
24
+ await configureProviderAndSave(configPath, client, pattern, true);
25
+ return;
26
+ }
27
+ const selected = models.find((m) => m.id === modelId);
28
+ if (selected) displayModelInfo(selected);
29
+ if (getModelOverrides(configPath)[modelId]) {
30
+ clack.log.warn(`Override for "${modelId}" already exists. Use Edit instead.`);
31
+ return;
32
+ }
33
+ await configureProviderAndSave(configPath, client, modelId, false);
34
+ }
35
+ async function loadModelsWithSpinner(client) {
36
+ const s = clack.spinner();
37
+ s.start("Loading models from OpenRouter...");
38
+ try {
39
+ const models = await fetchModels(client);
40
+ s.stop(`${models.length} models available`);
41
+ return models;
42
+ } catch (error) {
43
+ s.stop("Failed to load models");
44
+ clack.log.error(String(error));
45
+ return null;
46
+ }
47
+ }
48
+ async function searchModel(models) {
49
+ const result = await clack.autocomplete({
50
+ message: "Search for a model",
51
+ placeholder: "Type to search (e.g. \"claude\", \"gpt-4o\", \"qwen\")",
52
+ maxItems: 15,
53
+ options() {
54
+ const query = this.userInput.trim().toLowerCase();
55
+ if (!query) return [{
56
+ value: CUSTOM_PATTERN,
57
+ label: "✏️ Enter custom pattern (e.g. \"claude-*\")"
58
+ }];
59
+ return [...models.filter((m) => {
60
+ return `${m.id} ${m.name}`.toLowerCase().includes(query);
61
+ }).slice(0, 14).map((m) => ({
62
+ value: m.id,
63
+ label: formatModelLabel(m),
64
+ hint: formatModelHint(m)
65
+ })), {
66
+ value: CUSTOM_PATTERN,
67
+ label: "✏️ Enter custom pattern (e.g. \"claude-*\")"
68
+ }];
69
+ },
70
+ filter: (_search, _option) => true
71
+ });
72
+ if (isCancel(result)) return null;
73
+ return result;
74
+ }
75
+ async function enterPattern(models) {
76
+ const pattern = await clack.text({
77
+ message: "Enter model pattern",
78
+ placeholder: "e.g. claude-*, gpt-4*, anthropic/*",
79
+ validate: (v) => {
80
+ if (!v?.trim()) return "Pattern cannot be empty";
81
+ }
82
+ });
83
+ if (isCancel(pattern)) return null;
84
+ const pat = pattern.trim();
85
+ const matches = countPatternMatches(pat, models);
86
+ if (matches > 0) clack.log.info(`Pattern "${pat}" matches ${matches} model(s)`);
87
+ else clack.log.warn(`Pattern "${pat}" does not match any current models — it will still be saved`);
88
+ return pat;
89
+ }
90
+ async function configureProviderAndSave(configPath, client, modelKey, isPattern) {
91
+ const mode = await selectRoutingMode("Configure provider routing");
92
+ if (isCancel(mode)) return;
93
+ if (mode === "skip") {
94
+ setModelOverride(configPath, modelKey, {});
95
+ clack.outro("Done — override saved without provider routing");
96
+ return;
97
+ }
98
+ const providerOptions = await fetchProvidersForModel(client, modelKey, isPattern);
99
+ if (!providerOptions) return;
100
+ const override = await selectProvidersByMode(mode, providerOptions);
101
+ if (!override) return;
102
+ clack.log.info(`Proposed override:\n ${modelKey}:\n ${formatOverrideYaml(override)}`);
103
+ const save = await clack.confirm({ message: "Save to config?" });
104
+ if (isCancel(save) || !save) {
105
+ clack.outro("Cancelled");
106
+ return;
107
+ }
108
+ setModelOverride(configPath, modelKey, override);
109
+ clack.outro("✓ Model override saved");
110
+ }
111
+ function displayModelInfo(model) {
112
+ clack.log.info(`${model.name || model.id}`);
113
+ clack.log.info(` Context: ${formatContextLength(model.context_length)} tokens`);
114
+ clack.log.info(` Pricing: ${formatPricing(model.pricing.prompt, model.pricing.completion)}`);
115
+ if (model.pricing.input_cache_read && model.pricing.input_cache_read !== "0") clack.log.info(` Cache read: ${formatPrice(model.pricing.input_cache_read)}`);
116
+ if (model.pricing.input_cache_write && model.pricing.input_cache_write !== "0") clack.log.info(` Cache write: ${formatPrice(model.pricing.input_cache_write)}`);
117
+ if (model.top_provider?.max_completion_tokens) clack.log.info(` Max output: ${formatContextLength(model.top_provider.max_completion_tokens)} tokens`);
118
+ if (model.architecture?.modality) clack.log.info(` Modality: ${model.architecture.modality}`);
119
+ }
120
+ function countPatternMatches(pattern, models) {
121
+ if (pattern.endsWith("*")) {
122
+ const prefix = pattern.slice(0, -1);
123
+ return models.filter((m) => m.id.startsWith(prefix)).length;
124
+ }
125
+ return models.filter((m) => m.id === pattern).length;
126
+ }
127
+ function formatOverrideYaml(override) {
128
+ const parts = [];
129
+ if (override.provider && typeof override.provider === "object") {
130
+ const p = override.provider;
131
+ for (const [key, value] of Object.entries(p)) parts.push(`provider.${key}: ${JSON.stringify(value)}`);
132
+ }
133
+ return parts.join("\n ") || "(empty)";
134
+ }
135
+ //#endregion
136
+ export { addOverrideCommand };
137
+
138
+ //# sourceMappingURL=add.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.mjs","names":[],"sources":["../src/commands/config/add.ts"],"sourcesContent":["import * as clack from '@clack/prompts'\nimport { isCancel } from '@clack/prompts'\nimport { OpenRouterClient } from '../../openrouter/client.js'\nimport { fetchModels, formatPrice } from '../../openrouter/models.js'\nimport type { OpenRouterModel } from '../../openrouter/types.js'\nimport { getModelOverrides, requireConfigPath, setModelOverride } from './config.js'\nimport {\n formatContextLength,\n formatModelHint,\n formatModelLabel,\n formatPricing,\n} from './format.js'\nimport {\n fetchProvidersForModel,\n selectProvidersByMode,\n selectRoutingMode,\n} from './providers.js'\n\nconst CUSTOM_PATTERN = '__custom_pattern__'\n\n/** Run the interactive \"Add model override\" flow. */\nexport async function addOverrideCommand(apiKey: string): Promise<void> {\n clack.intro('Add Model Override')\n\n const configPath = requireConfigPath()\n const client = new OpenRouterClient(apiKey)\n\n const models = await loadModelsWithSpinner(client)\n if (!models) return\n\n const modelId = await searchModel(models)\n if (!modelId) return\n\n if (typeof modelId !== 'string') return\n\n if (modelId === CUSTOM_PATTERN) {\n const pattern = await enterPattern(models)\n if (!pattern) return\n\n const existing = getModelOverrides(configPath)\n if (existing[pattern]) {\n clack.log.warn(`Override for \"${pattern}\" already exists. Use Edit instead.`)\n return\n }\n\n await configureProviderAndSave(configPath, client, pattern, true)\n return\n }\n\n const selected = models.find(m => m.id === modelId)\n if (selected) displayModelInfo(selected)\n\n const existing = getModelOverrides(configPath)\n if (existing[modelId]) {\n clack.log.warn(`Override for \"${modelId}\" already exists. Use Edit instead.`)\n return\n }\n\n await configureProviderAndSave(configPath, client, modelId, false)\n}\n\nasync function loadModelsWithSpinner(\n client: OpenRouterClient,\n): Promise<OpenRouterModel[] | null> {\n const s = clack.spinner()\n s.start('Loading models from OpenRouter...')\n try {\n const models = await fetchModels(client)\n s.stop(`${models.length} models available`)\n return models\n } catch (error) {\n s.stop('Failed to load models')\n clack.log.error(String(error))\n return null\n }\n}\n\nasync function searchModel(models: OpenRouterModel[]): Promise<string | symbol | null> {\n const result = await clack.autocomplete({\n message: 'Search for a model',\n placeholder: 'Type to search (e.g. \"claude\", \"gpt-4o\", \"qwen\")',\n maxItems: 15,\n options(this: { userInput: string }) {\n const query = this.userInput.trim().toLowerCase()\n\n if (!query) {\n return [\n {\n value: CUSTOM_PATTERN,\n label: '✏️ Enter custom pattern (e.g. \"claude-*\")',\n },\n ]\n }\n\n const filtered = models\n .filter(m => {\n const text = `${m.id} ${m.name}`.toLowerCase()\n return text.includes(query)\n })\n .slice(0, 14)\n .map(m => ({\n value: m.id,\n label: formatModelLabel(m),\n hint: formatModelHint(m),\n }))\n\n return [\n ...filtered,\n { value: CUSTOM_PATTERN, label: '✏️ Enter custom pattern (e.g. \"claude-*\")' },\n ]\n },\n filter: (_search: string, _option: { value: string }) => true,\n })\n\n if (isCancel(result)) return null\n return result as string\n}\n\nasync function enterPattern(models: OpenRouterModel[]): Promise<string | null> {\n const pattern = await clack.text({\n message: 'Enter model pattern',\n placeholder: 'e.g. claude-*, gpt-4*, anthropic/*',\n validate: v => {\n if (!v?.trim()) return 'Pattern cannot be empty'\n return undefined\n },\n })\n\n if (isCancel(pattern)) return null\n\n const pat = (pattern as string).trim()\n const matches = countPatternMatches(pat, models)\n if (matches > 0) {\n clack.log.info(`Pattern \"${pat}\" matches ${matches} model(s)`)\n } else {\n clack.log.warn(\n `Pattern \"${pat}\" does not match any current models — it will still be saved`,\n )\n }\n\n return pat\n}\n\nasync function configureProviderAndSave(\n configPath: string,\n client: OpenRouterClient,\n modelKey: string,\n isPattern: boolean,\n): Promise<void> {\n const mode = await selectRoutingMode('Configure provider routing')\n if (isCancel(mode)) return\n\n if (mode === 'skip') {\n setModelOverride(configPath, modelKey, {})\n clack.outro('Done — override saved without provider routing')\n return\n }\n\n const providerOptions = await fetchProvidersForModel(client, modelKey, isPattern)\n if (!providerOptions) return\n\n const override = await selectProvidersByMode(mode as string, providerOptions)\n if (!override) return\n\n clack.log.info(\n `Proposed override:\\n ${modelKey}:\\n ${formatOverrideYaml(override)}`,\n )\n\n const save = await clack.confirm({ message: 'Save to config?' })\n if (isCancel(save) || !save) {\n clack.outro('Cancelled')\n return\n }\n\n setModelOverride(configPath, modelKey, override)\n clack.outro('✓ Model override saved')\n}\n\nfunction displayModelInfo(model: OpenRouterModel): void {\n clack.log.info(`${model.name || model.id}`)\n clack.log.info(` Context: ${formatContextLength(model.context_length)} tokens`)\n clack.log.info(\n ` Pricing: ${formatPricing(model.pricing.prompt, model.pricing.completion)}`,\n )\n if (model.pricing.input_cache_read && model.pricing.input_cache_read !== '0') {\n clack.log.info(` Cache read: ${formatPrice(model.pricing.input_cache_read)}`)\n }\n if (model.pricing.input_cache_write && model.pricing.input_cache_write !== '0') {\n clack.log.info(` Cache write: ${formatPrice(model.pricing.input_cache_write)}`)\n }\n if (model.top_provider?.max_completion_tokens) {\n clack.log.info(\n ` Max output: ${formatContextLength(model.top_provider.max_completion_tokens)} tokens`,\n )\n }\n if (model.architecture?.modality) {\n clack.log.info(` Modality: ${model.architecture.modality}`)\n }\n}\n\nfunction countPatternMatches(pattern: string, models: OpenRouterModel[]): number {\n if (pattern.endsWith('*')) {\n const prefix = pattern.slice(0, -1)\n return models.filter(m => m.id.startsWith(prefix)).length\n }\n return models.filter(m => m.id === pattern).length\n}\n\nfunction formatOverrideYaml(override: Record<string, unknown>): string {\n const parts: string[] = []\n if (override.provider && typeof override.provider === 'object') {\n const p = override.provider as Record<string, unknown>\n for (const [key, value] of Object.entries(p)) {\n parts.push(`provider.${key}: ${JSON.stringify(value)}`)\n }\n }\n return parts.join('\\n ') || '(empty)'\n}\n"],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ const require_proxy = require("./proxy.cjs");
2
+ const require_providers = require("./providers.cjs");
3
+ const require_add = require("./add.cjs");
4
+ let _clack_prompts = require("@clack/prompts");
5
+ _clack_prompts = require_proxy.__toESM(_clack_prompts, 1);
6
+ //#region src/commands/config/browse.ts
7
+ function toOption(m) {
8
+ return {
9
+ value: m.id,
10
+ label: require_providers.formatModelLabel(m),
11
+ hint: require_providers.formatModelHint(m)
12
+ };
13
+ }
14
+ function displayModelDetails(model) {
15
+ _clack_prompts.log.success(`${model.name || model.id}`);
16
+ if (model.description) {
17
+ const desc = model.description.length > 200 ? `${model.description.slice(0, 200)}...` : model.description;
18
+ _clack_prompts.log.info(` ${desc}`);
19
+ }
20
+ _clack_prompts.log.info(` Context: ${require_providers.formatContextLength(model.context_length)} tokens`);
21
+ if (model.top_provider?.max_completion_tokens) _clack_prompts.log.info(` Max output: ${require_providers.formatContextLength(model.top_provider.max_completion_tokens)} tokens`);
22
+ _clack_prompts.log.info(` Pricing: ${require_providers.formatPricing(model.pricing.prompt, model.pricing.completion)}`);
23
+ if (model.pricing.input_cache_read && model.pricing.input_cache_read !== "0") _clack_prompts.log.info(` Cache read: ${require_providers.formatPrice(model.pricing.input_cache_read)}`);
24
+ if (model.pricing.input_cache_write && model.pricing.input_cache_write !== "0") _clack_prompts.log.info(` Cache write: ${require_providers.formatPrice(model.pricing.input_cache_write)}`);
25
+ if (model.architecture?.modality) _clack_prompts.log.info(` Modality: ${model.architecture.modality}`);
26
+ if (model.supported_parameters?.length) _clack_prompts.log.info(` Parameters: ${model.supported_parameters.join(", ")}`);
27
+ }
28
+ async function displayProviders(client, model) {
29
+ const author = require_providers.parseModelAuthor(model.id);
30
+ const slug = require_providers.parseModelSlug(model.id);
31
+ const s = _clack_prompts.spinner();
32
+ s.start("Checking providers...");
33
+ try {
34
+ const endpoints = await require_providers.fetchModelEndpoints(client, author, slug);
35
+ const unique = require_providers.getUniqueProviders(endpoints);
36
+ s.stop(`${unique.length} providers available`);
37
+ for (const p of unique) {
38
+ const ep = endpoints.find((e) => e.tag === p.tag);
39
+ const latency = require_providers.formatLatency(ep?.latency_last_30m?.p50 ?? null);
40
+ const throughput = require_providers.formatThroughput(ep?.throughput_last_30m?.p50 ?? null);
41
+ _clack_prompts.log.info(` ${p.providerName} (${p.tag}) — ${latency} · ${throughput}`);
42
+ }
43
+ } catch {
44
+ s.stop("Could not fetch providers");
45
+ }
46
+ }
47
+ /** Run the interactive "Browse models" flow. */
48
+ async function browseModelsCommand(apiKey) {
49
+ _clack_prompts.intro("Browse Models");
50
+ const client = new require_providers.OpenRouterClient(apiKey);
51
+ const s = _clack_prompts.spinner();
52
+ s.start("Loading models...");
53
+ let models;
54
+ try {
55
+ models = await require_providers.fetchModels(client);
56
+ s.stop(`${models.length} models available`);
57
+ } catch (error) {
58
+ s.stop("Failed to load models");
59
+ _clack_prompts.log.error(String(error));
60
+ return;
61
+ }
62
+ const modelId = await _clack_prompts.autocomplete({
63
+ message: "Search for a model",
64
+ placeholder: "Type to search...",
65
+ maxItems: 15,
66
+ options() {
67
+ const query = this.userInput.trim().toLowerCase();
68
+ if (!query) return models.slice(0, 15).map(toOption);
69
+ return models.filter((m) => `${m.id} ${m.name}`.toLowerCase().includes(query)).slice(0, 15).map(toOption);
70
+ },
71
+ filter: (_search, _option) => true
72
+ });
73
+ if ((0, _clack_prompts.isCancel)(modelId)) return;
74
+ const model = models.find((m) => m.id === modelId);
75
+ if (!model) return;
76
+ displayModelDetails(model);
77
+ await displayProviders(client, model);
78
+ const configure = await _clack_prompts.confirm({ message: `Configure routing for ${model.id}?` });
79
+ if ((0, _clack_prompts.isCancel)(configure) || !configure) {
80
+ _clack_prompts.outro("Bye!");
81
+ return;
82
+ }
83
+ await require_add.addOverrideCommand(apiKey);
84
+ }
85
+ //#endregion
86
+ exports.browseModelsCommand = browseModelsCommand;
87
+
88
+ //# sourceMappingURL=browse.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browse.cjs","names":["formatModelLabel","formatModelHint","formatContextLength","formatPricing","formatPrice","parseModelAuthor","parseModelSlug","clack","fetchModelEndpoints","getUniqueProviders","formatLatency","formatThroughput","OpenRouterClient","fetchModels","addOverrideCommand"],"sources":["../src/commands/config/browse.ts"],"sourcesContent":["import * as clack from '@clack/prompts'\nimport { isCancel } from '@clack/prompts'\nimport { OpenRouterClient } from '../../openrouter/client.js'\nimport { fetchModelEndpoints, getUniqueProviders } from '../../openrouter/endpoints.js'\nimport {\n fetchModels,\n formatPrice,\n parseModelAuthor,\n parseModelSlug,\n} from '../../openrouter/models.js'\nimport type { OpenRouterModel } from '../../openrouter/types.js'\nimport { addOverrideCommand } from './add.js'\nimport {\n formatContextLength,\n formatLatency,\n formatModelHint,\n formatModelLabel,\n formatPricing,\n formatThroughput,\n} from './format.js'\n\nfunction toOption(m: OpenRouterModel) {\n return { value: m.id, label: formatModelLabel(m), hint: formatModelHint(m) }\n}\n\nfunction displayModelDetails(model: OpenRouterModel): void {\n clack.log.success(`${model.name || model.id}`)\n if (model.description) {\n const desc =\n model.description.length > 200\n ? `${model.description.slice(0, 200)}...`\n : model.description\n clack.log.info(` ${desc}`)\n }\n clack.log.info(` Context: ${formatContextLength(model.context_length)} tokens`)\n if (model.top_provider?.max_completion_tokens) {\n clack.log.info(\n ` Max output: ${formatContextLength(model.top_provider.max_completion_tokens)} tokens`,\n )\n }\n clack.log.info(\n ` Pricing: ${formatPricing(model.pricing.prompt, model.pricing.completion)}`,\n )\n if (model.pricing.input_cache_read && model.pricing.input_cache_read !== '0') {\n clack.log.info(` Cache read: ${formatPrice(model.pricing.input_cache_read)}`)\n }\n if (model.pricing.input_cache_write && model.pricing.input_cache_write !== '0') {\n clack.log.info(` Cache write: ${formatPrice(model.pricing.input_cache_write)}`)\n }\n if (model.architecture?.modality) {\n clack.log.info(` Modality: ${model.architecture.modality}`)\n }\n if (model.supported_parameters?.length) {\n clack.log.info(` Parameters: ${model.supported_parameters.join(', ')}`)\n }\n}\n\nasync function displayProviders(\n client: OpenRouterClient,\n model: OpenRouterModel,\n): Promise<void> {\n const author = parseModelAuthor(model.id)\n const slug = parseModelSlug(model.id)\n const s = clack.spinner()\n s.start('Checking providers...')\n try {\n const endpoints = await fetchModelEndpoints(client, author, slug)\n const unique = getUniqueProviders(endpoints)\n s.stop(`${unique.length} providers available`)\n\n for (const p of unique) {\n const ep = endpoints.find(e => e.tag === p.tag)\n const latency = formatLatency(ep?.latency_last_30m?.p50 ?? null)\n const throughput = formatThroughput(ep?.throughput_last_30m?.p50 ?? null)\n clack.log.info(` ${p.providerName} (${p.tag}) — ${latency} · ${throughput}`)\n }\n } catch {\n s.stop('Could not fetch providers')\n }\n}\n\n/** Run the interactive \"Browse models\" flow. */\nexport async function browseModelsCommand(apiKey: string): Promise<void> {\n clack.intro('Browse Models')\n\n const client = new OpenRouterClient(apiKey)\n\n const s = clack.spinner()\n s.start('Loading models...')\n let models: OpenRouterModel[]\n try {\n models = await fetchModels(client)\n s.stop(`${models.length} models available`)\n } catch (error) {\n s.stop('Failed to load models')\n clack.log.error(String(error))\n return\n }\n\n const modelId = await clack.autocomplete({\n message: 'Search for a model',\n placeholder: 'Type to search...',\n maxItems: 15,\n options(this: { userInput: string }) {\n const query = this.userInput.trim().toLowerCase()\n if (!query) return models.slice(0, 15).map(toOption)\n\n return models\n .filter(m => `${m.id} ${m.name}`.toLowerCase().includes(query))\n .slice(0, 15)\n .map(toOption)\n },\n filter: (_search: string, _option: { value: string }) => true,\n })\n\n if (isCancel(modelId)) return\n\n const model = models.find(m => m.id === modelId)\n if (!model) return\n\n displayModelDetails(model)\n await displayProviders(client, model)\n\n const configure = await clack.confirm({\n message: `Configure routing for ${model.id}?`,\n })\n\n if (isCancel(configure) || !configure) {\n clack.outro('Bye!')\n return\n }\n\n await addOverrideCommand(apiKey)\n}\n"],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ import { a as getUniqueProviders, c as formatModelHint, d as formatThroughput, f as fetchModels, g as OpenRouterClient, h as parseModelSlug, i as fetchModelEndpoints, l as formatModelLabel, m as parseModelAuthor, o as formatContextLength, p as formatPrice, s as formatLatency, u as formatPricing } from "./providers.mjs";
2
+ import { addOverrideCommand } from "./add.mjs";
3
+ import * as clack from "@clack/prompts";
4
+ import { isCancel } from "@clack/prompts";
5
+ //#region src/commands/config/browse.ts
6
+ function toOption(m) {
7
+ return {
8
+ value: m.id,
9
+ label: formatModelLabel(m),
10
+ hint: formatModelHint(m)
11
+ };
12
+ }
13
+ function displayModelDetails(model) {
14
+ clack.log.success(`${model.name || model.id}`);
15
+ if (model.description) {
16
+ const desc = model.description.length > 200 ? `${model.description.slice(0, 200)}...` : model.description;
17
+ clack.log.info(` ${desc}`);
18
+ }
19
+ clack.log.info(` Context: ${formatContextLength(model.context_length)} tokens`);
20
+ if (model.top_provider?.max_completion_tokens) clack.log.info(` Max output: ${formatContextLength(model.top_provider.max_completion_tokens)} tokens`);
21
+ clack.log.info(` Pricing: ${formatPricing(model.pricing.prompt, model.pricing.completion)}`);
22
+ if (model.pricing.input_cache_read && model.pricing.input_cache_read !== "0") clack.log.info(` Cache read: ${formatPrice(model.pricing.input_cache_read)}`);
23
+ if (model.pricing.input_cache_write && model.pricing.input_cache_write !== "0") clack.log.info(` Cache write: ${formatPrice(model.pricing.input_cache_write)}`);
24
+ if (model.architecture?.modality) clack.log.info(` Modality: ${model.architecture.modality}`);
25
+ if (model.supported_parameters?.length) clack.log.info(` Parameters: ${model.supported_parameters.join(", ")}`);
26
+ }
27
+ async function displayProviders(client, model) {
28
+ const author = parseModelAuthor(model.id);
29
+ const slug = parseModelSlug(model.id);
30
+ const s = clack.spinner();
31
+ s.start("Checking providers...");
32
+ try {
33
+ const endpoints = await fetchModelEndpoints(client, author, slug);
34
+ const unique = getUniqueProviders(endpoints);
35
+ s.stop(`${unique.length} providers available`);
36
+ for (const p of unique) {
37
+ const ep = endpoints.find((e) => e.tag === p.tag);
38
+ const latency = formatLatency(ep?.latency_last_30m?.p50 ?? null);
39
+ const throughput = formatThroughput(ep?.throughput_last_30m?.p50 ?? null);
40
+ clack.log.info(` ${p.providerName} (${p.tag}) — ${latency} · ${throughput}`);
41
+ }
42
+ } catch {
43
+ s.stop("Could not fetch providers");
44
+ }
45
+ }
46
+ /** Run the interactive "Browse models" flow. */
47
+ async function browseModelsCommand(apiKey) {
48
+ clack.intro("Browse Models");
49
+ const client = new OpenRouterClient(apiKey);
50
+ const s = clack.spinner();
51
+ s.start("Loading models...");
52
+ let models;
53
+ try {
54
+ models = await fetchModels(client);
55
+ s.stop(`${models.length} models available`);
56
+ } catch (error) {
57
+ s.stop("Failed to load models");
58
+ clack.log.error(String(error));
59
+ return;
60
+ }
61
+ const modelId = await clack.autocomplete({
62
+ message: "Search for a model",
63
+ placeholder: "Type to search...",
64
+ maxItems: 15,
65
+ options() {
66
+ const query = this.userInput.trim().toLowerCase();
67
+ if (!query) return models.slice(0, 15).map(toOption);
68
+ return models.filter((m) => `${m.id} ${m.name}`.toLowerCase().includes(query)).slice(0, 15).map(toOption);
69
+ },
70
+ filter: (_search, _option) => true
71
+ });
72
+ if (isCancel(modelId)) return;
73
+ const model = models.find((m) => m.id === modelId);
74
+ if (!model) return;
75
+ displayModelDetails(model);
76
+ await displayProviders(client, model);
77
+ const configure = await clack.confirm({ message: `Configure routing for ${model.id}?` });
78
+ if (isCancel(configure) || !configure) {
79
+ clack.outro("Bye!");
80
+ return;
81
+ }
82
+ await addOverrideCommand(apiKey);
83
+ }
84
+ //#endregion
85
+ export { browseModelsCommand };
86
+
87
+ //# sourceMappingURL=browse.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browse.mjs","names":[],"sources":["../src/commands/config/browse.ts"],"sourcesContent":["import * as clack from '@clack/prompts'\nimport { isCancel } from '@clack/prompts'\nimport { OpenRouterClient } from '../../openrouter/client.js'\nimport { fetchModelEndpoints, getUniqueProviders } from '../../openrouter/endpoints.js'\nimport {\n fetchModels,\n formatPrice,\n parseModelAuthor,\n parseModelSlug,\n} from '../../openrouter/models.js'\nimport type { OpenRouterModel } from '../../openrouter/types.js'\nimport { addOverrideCommand } from './add.js'\nimport {\n formatContextLength,\n formatLatency,\n formatModelHint,\n formatModelLabel,\n formatPricing,\n formatThroughput,\n} from './format.js'\n\nfunction toOption(m: OpenRouterModel) {\n return { value: m.id, label: formatModelLabel(m), hint: formatModelHint(m) }\n}\n\nfunction displayModelDetails(model: OpenRouterModel): void {\n clack.log.success(`${model.name || model.id}`)\n if (model.description) {\n const desc =\n model.description.length > 200\n ? `${model.description.slice(0, 200)}...`\n : model.description\n clack.log.info(` ${desc}`)\n }\n clack.log.info(` Context: ${formatContextLength(model.context_length)} tokens`)\n if (model.top_provider?.max_completion_tokens) {\n clack.log.info(\n ` Max output: ${formatContextLength(model.top_provider.max_completion_tokens)} tokens`,\n )\n }\n clack.log.info(\n ` Pricing: ${formatPricing(model.pricing.prompt, model.pricing.completion)}`,\n )\n if (model.pricing.input_cache_read && model.pricing.input_cache_read !== '0') {\n clack.log.info(` Cache read: ${formatPrice(model.pricing.input_cache_read)}`)\n }\n if (model.pricing.input_cache_write && model.pricing.input_cache_write !== '0') {\n clack.log.info(` Cache write: ${formatPrice(model.pricing.input_cache_write)}`)\n }\n if (model.architecture?.modality) {\n clack.log.info(` Modality: ${model.architecture.modality}`)\n }\n if (model.supported_parameters?.length) {\n clack.log.info(` Parameters: ${model.supported_parameters.join(', ')}`)\n }\n}\n\nasync function displayProviders(\n client: OpenRouterClient,\n model: OpenRouterModel,\n): Promise<void> {\n const author = parseModelAuthor(model.id)\n const slug = parseModelSlug(model.id)\n const s = clack.spinner()\n s.start('Checking providers...')\n try {\n const endpoints = await fetchModelEndpoints(client, author, slug)\n const unique = getUniqueProviders(endpoints)\n s.stop(`${unique.length} providers available`)\n\n for (const p of unique) {\n const ep = endpoints.find(e => e.tag === p.tag)\n const latency = formatLatency(ep?.latency_last_30m?.p50 ?? null)\n const throughput = formatThroughput(ep?.throughput_last_30m?.p50 ?? null)\n clack.log.info(` ${p.providerName} (${p.tag}) — ${latency} · ${throughput}`)\n }\n } catch {\n s.stop('Could not fetch providers')\n }\n}\n\n/** Run the interactive \"Browse models\" flow. */\nexport async function browseModelsCommand(apiKey: string): Promise<void> {\n clack.intro('Browse Models')\n\n const client = new OpenRouterClient(apiKey)\n\n const s = clack.spinner()\n s.start('Loading models...')\n let models: OpenRouterModel[]\n try {\n models = await fetchModels(client)\n s.stop(`${models.length} models available`)\n } catch (error) {\n s.stop('Failed to load models')\n clack.log.error(String(error))\n return\n }\n\n const modelId = await clack.autocomplete({\n message: 'Search for a model',\n placeholder: 'Type to search...',\n maxItems: 15,\n options(this: { userInput: string }) {\n const query = this.userInput.trim().toLowerCase()\n if (!query) return models.slice(0, 15).map(toOption)\n\n return models\n .filter(m => `${m.id} ${m.name}`.toLowerCase().includes(query))\n .slice(0, 15)\n .map(toOption)\n },\n filter: (_search: string, _option: { value: string }) => true,\n })\n\n if (isCancel(modelId)) return\n\n const model = models.find(m => m.id === modelId)\n if (!model) return\n\n displayModelDetails(model)\n await displayProviders(client, model)\n\n const configure = await clack.confirm({\n message: `Configure routing for ${model.id}?`,\n })\n\n if (isCancel(configure) || !configure) {\n clack.outro('Bye!')\n return\n }\n\n await addOverrideCommand(apiKey)\n}\n"],"mappings":"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"}
package/dist/cli.cjs CHANGED
@@ -1,36 +1,159 @@
1
1
  #!/usr/bin/env node
2
2
  const require_proxy = require("./proxy.cjs");
3
- let cac = require("cac");
3
+ let cmd_ts = require("cmd-ts");
4
4
  let dotenv = require("dotenv");
5
5
  //#region src/version.ts
6
- const version = "0.1.0";
6
+ const version = "0.2.1";
7
7
  //#endregion
8
8
  //#region src/cli.ts
9
- (0, dotenv.config)();
10
- const cli = (0, cac.cac)("proxitor");
11
- cli.version(version).usage("[options]").help();
12
- cli.option("-p, --port <port>", "Proxy server port", { default: 8080 }).option("-h, --host <host>", "Proxy server host", { default: "0.0.0.0" }).option("-c, --config <path>", "Path to config file").option("--no-config", "Skip config file discovery").option("--openrouter-key <key>", "OpenRouter API key").option("--verbose", "Enable verbose logging");
13
- const parsed = cli.parse();
14
- async function main() {
9
+ const argv = process.argv.slice(2);
10
+ const isInfo = argv.includes("--help") || argv.includes("-h") || argv.includes("--version") || argv.includes("-v");
11
+ if (!isInfo) (0, dotenv.config)();
12
+ async function resolveApiKey(configPath, openrouterKey) {
13
+ if (openrouterKey) return openrouterKey;
14
+ const envKey = process.env.OPENROUTER_API_KEY;
15
+ if (envKey) return envKey;
15
16
  try {
16
- const config = await require_proxy.loadConfig({
17
- configPath: typeof parsed.options.config === "string" ? parsed.options.config : void 0,
18
- noConfig: parsed.options.config === false,
19
- port: parsed.options.port,
20
- host: parsed.options.host,
21
- openrouterKey: parsed.options.openrouterKey,
22
- verbose: parsed.options.verbose
23
- });
24
- require_proxy.startProxyServer(config, () => {
25
- require_proxy.logger.ready(`Proxitor proxy listening on ${config.host}:${config.port}`);
26
- require_proxy.logger.info(`Routing requests to OpenRouter`);
27
- });
28
- } catch (error) {
29
- require_proxy.logger.error("Failed to start proxy:", error);
30
- process.exit(1);
31
- }
17
+ const cfg = await require_proxy.loadConfig({ configPath });
18
+ if (cfg.openrouterKey) return cfg.openrouterKey;
19
+ } catch {}
20
+ require_proxy.logger.error("OpenRouter API key required. Set OPENROUTER_API_KEY, pass --openrouter-key, or add it to config.");
21
+ return null;
32
22
  }
33
- main();
23
+ const configOptionArgs = {
24
+ config: (0, cmd_ts.option)({
25
+ long: "config",
26
+ short: "c",
27
+ type: (0, cmd_ts.optional)(cmd_ts.string),
28
+ description: "Path to config file"
29
+ }),
30
+ openrouterKey: (0, cmd_ts.option)({
31
+ long: "openrouter-key",
32
+ type: (0, cmd_ts.optional)(cmd_ts.string),
33
+ description: "OpenRouter API key"
34
+ })
35
+ };
36
+ const startCommand = (0, cmd_ts.command)({
37
+ name: "start",
38
+ description: "Start proxy server",
39
+ args: {
40
+ port: (0, cmd_ts.option)({
41
+ long: "port",
42
+ short: "p",
43
+ type: cmd_ts.number,
44
+ description: "Proxy server port",
45
+ defaultValue: () => 8080,
46
+ defaultValueIsSerializable: true
47
+ }),
48
+ host: (0, cmd_ts.option)({
49
+ long: "host",
50
+ type: cmd_ts.string,
51
+ description: "Proxy server host",
52
+ defaultValue: () => "0.0.0.0",
53
+ defaultValueIsSerializable: true
54
+ }),
55
+ config: (0, cmd_ts.option)({
56
+ long: "config",
57
+ short: "c",
58
+ type: (0, cmd_ts.optional)(cmd_ts.string),
59
+ description: "Path to config file"
60
+ }),
61
+ noConfig: (0, cmd_ts.flag)({
62
+ long: "no-config",
63
+ description: "Skip config file discovery"
64
+ }),
65
+ openrouterKey: (0, cmd_ts.option)({
66
+ long: "openrouter-key",
67
+ type: (0, cmd_ts.optional)(cmd_ts.string),
68
+ description: "OpenRouter API key"
69
+ }),
70
+ verbose: (0, cmd_ts.flag)({
71
+ long: "verbose",
72
+ description: "Enable verbose logging"
73
+ })
74
+ },
75
+ handler: async (args) => {
76
+ try {
77
+ const cfg = await require_proxy.loadConfig({
78
+ configPath: args.config ?? void 0,
79
+ noConfig: args.noConfig,
80
+ port: args.port,
81
+ host: args.host,
82
+ openrouterKey: args.openrouterKey ?? void 0,
83
+ verbose: args.verbose
84
+ });
85
+ require_proxy.startProxyServer(cfg, () => {
86
+ require_proxy.logger.ready(`Proxitor proxy listening on ${cfg.host}:${cfg.port}`);
87
+ require_proxy.logger.info("Routing requests to OpenRouter");
88
+ });
89
+ } catch (error) {
90
+ require_proxy.logger.error("Failed to start proxy:", error);
91
+ process.exit(1);
92
+ }
93
+ }
94
+ });
95
+ const withApiKey = (fn) => async (args) => {
96
+ const apiKey = await resolveApiKey(args.config ?? void 0, args.openrouterKey ?? void 0);
97
+ if (apiKey) await fn(apiKey);
98
+ };
99
+ const rootCli = (0, cmd_ts.subcommands)({
100
+ name: "proxitor",
101
+ version,
102
+ description: "Lightweight proxy for routing CLI requests to OpenRouter",
103
+ cmds: {
104
+ start: startCommand,
105
+ config: (0, cmd_ts.subcommands)({
106
+ name: "config",
107
+ description: "Manage proxy configuration",
108
+ cmds: {
109
+ add: (0, cmd_ts.command)({
110
+ name: "add",
111
+ description: "Add model override",
112
+ args: configOptionArgs,
113
+ handler: withApiKey(async (k) => (await Promise.resolve().then(() => require("./add.cjs"))).addOverrideCommand(k))
114
+ }),
115
+ edit: (0, cmd_ts.command)({
116
+ name: "edit",
117
+ description: "Edit model override",
118
+ args: configOptionArgs,
119
+ handler: withApiKey(async (k) => (await Promise.resolve().then(() => require("./edit.cjs"))).editOverrideCommand(k))
120
+ }),
121
+ remove: (0, cmd_ts.command)({
122
+ name: "remove",
123
+ description: "Remove model override",
124
+ args: {},
125
+ handler: async () => (await Promise.resolve().then(() => require("./remove.cjs"))).removeOverrideCommand()
126
+ }),
127
+ list: (0, cmd_ts.command)({
128
+ name: "list",
129
+ description: "List current overrides",
130
+ args: {},
131
+ handler: async () => (await Promise.resolve().then(() => require("./list.cjs"))).listOverridesCommand()
132
+ }),
133
+ browse: (0, cmd_ts.command)({
134
+ name: "browse",
135
+ description: "Browse models",
136
+ args: configOptionArgs,
137
+ handler: withApiKey(async (k) => (await Promise.resolve().then(() => require("./browse.cjs"))).browseModelsCommand(k))
138
+ }),
139
+ validate: (0, cmd_ts.command)({
140
+ name: "validate",
141
+ description: "Validate config",
142
+ args: {},
143
+ handler: async () => (await Promise.resolve().then(() => require("./validate.cjs"))).validateConfigCommand()
144
+ }),
145
+ menu: (0, cmd_ts.command)({
146
+ name: "menu",
147
+ description: "Interactive configuration menu",
148
+ args: configOptionArgs,
149
+ handler: withApiKey(async (k) => (await Promise.resolve().then(() => require("./config2.cjs"))).runConfigMenu(k))
150
+ })
151
+ }
152
+ })
153
+ }
154
+ });
155
+ if (argv.some((a) => !a.startsWith("-")) || isInfo) (0, cmd_ts.run)(rootCli, argv);
156
+ else (0, cmd_ts.run)(startCommand, argv);
34
157
  //#endregion
35
158
 
36
159
  //# sourceMappingURL=cli.cjs.map
package/dist/cli.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.cjs","names":["loadConfig"],"sources":["../src/version.ts","../src/cli.ts"],"sourcesContent":["export const version = '0.1.0'\n","#!/usr/bin/env node\nimport { cac } from 'cac'\nimport { config as loadDotenv } from 'dotenv'\nimport { loadConfig } from './config.js'\nimport { logger } from './logger.js'\nimport { startProxyServer } from './proxy.js'\nimport { version } from './version.js'\n\nloadDotenv()\n\nconst cli = cac('proxitor')\n\ncli.version(version).usage('[options]').help()\n\ncli\n .option('-p, --port <port>', 'Proxy server port', { default: 8080 })\n .option('-h, --host <host>', 'Proxy server host', { default: '0.0.0.0' })\n .option('-c, --config <path>', 'Path to config file')\n .option('--no-config', 'Skip config file discovery')\n .option('--openrouter-key <key>', 'OpenRouter API key')\n .option('--verbose', 'Enable verbose logging')\n\nconst parsed = cli.parse()\n\nasync function main() {\n try {\n const config = await loadConfig({\n configPath:\n typeof parsed.options.config === 'string' ? parsed.options.config : undefined,\n noConfig: parsed.options.config === false,\n port: parsed.options.port,\n host: parsed.options.host,\n openrouterKey: parsed.options.openrouterKey,\n verbose: parsed.options.verbose,\n })\n\n startProxyServer(config, () => {\n logger.ready(`Proxitor proxy listening on ${config.host}:${config.port}`)\n logger.info(`Routing requests to OpenRouter`)\n })\n } catch (error) {\n logger.error('Failed to start proxy:', error)\n process.exit(1)\n }\n}\n\nvoid main()\n"],"mappings":";;;;;AAAA,MAAa,UAAU;;;mBCQZ;AAEX,MAAM,OAAA,GAAA,IAAA,KAAU,UAAU;AAE1B,IAAI,QAAQ,OAAO,EAAE,MAAM,WAAW,EAAE,KAAK;AAE7C,IACG,OAAO,qBAAqB,qBAAqB,EAAE,SAAS,KAAK,CAAC,EAClE,OAAO,qBAAqB,qBAAqB,EAAE,SAAS,UAAU,CAAC,EACvE,OAAO,uBAAuB,qBAAqB,EACnD,OAAO,eAAe,4BAA4B,EAClD,OAAO,0BAA0B,oBAAoB,EACrD,OAAO,aAAa,wBAAwB;AAE/C,MAAM,SAAS,IAAI,MAAM;AAEzB,eAAe,OAAO;CACpB,IAAI;EACF,MAAM,SAAS,MAAMA,cAAAA,WAAW;GAC9B,YACE,OAAO,OAAO,QAAQ,WAAW,WAAW,OAAO,QAAQ,SAAS,KAAA;GACtE,UAAU,OAAO,QAAQ,WAAW;GACpC,MAAM,OAAO,QAAQ;GACrB,MAAM,OAAO,QAAQ;GACrB,eAAe,OAAO,QAAQ;GAC9B,SAAS,OAAO,QAAQ;EAC1B,CAAC;EAED,cAAA,iBAAiB,cAAc;GAC7B,cAAA,OAAO,MAAM,+BAA+B,OAAO,KAAK,GAAG,OAAO,MAAM;GACxE,cAAA,OAAO,KAAK,gCAAgC;EAC9C,CAAC;CACH,SAAS,OAAO;EACd,cAAA,OAAO,MAAM,0BAA0B,KAAK;EAC5C,QAAQ,KAAK,CAAC;CAChB;AACF;AAEK,KAAK"}
1
+ {"version":3,"file":"cli.cjs","names":["loadConfig","string","number"],"sources":["../src/version.ts","../src/cli.ts"],"sourcesContent":["export const version = '0.2.1'\n","#!/usr/bin/env node\nimport { command, flag, number, option, optional, run, string, subcommands } from 'cmd-ts'\nimport { config as loadDotenv } from 'dotenv'\nimport { loadConfig } from './config.js'\nimport { logger } from './logger.js'\nimport { startProxyServer } from './proxy.js'\nimport { version } from './version.js'\n\nconst argv = process.argv.slice(2)\nconst isInfo =\n argv.includes('--help') ||\n argv.includes('-h') ||\n argv.includes('--version') ||\n argv.includes('-v')\nif (!isInfo) loadDotenv()\n\nasync function resolveApiKey(\n configPath?: string,\n openrouterKey?: string,\n): Promise<string | null> {\n if (openrouterKey) return openrouterKey\n const envKey = process.env.OPENROUTER_API_KEY\n if (envKey) return envKey\n\n try {\n const cfg = await loadConfig({ configPath })\n if (cfg.openrouterKey) return cfg.openrouterKey\n } catch {\n // Config not found or invalid — fall through\n }\n\n logger.error(\n 'OpenRouter API key required. Set OPENROUTER_API_KEY, pass --openrouter-key, or add it to config.',\n )\n return null\n}\n\nconst configOptionArgs = {\n config: option({\n long: 'config',\n short: 'c',\n type: optional(string),\n description: 'Path to config file',\n }),\n openrouterKey: option({\n long: 'openrouter-key',\n type: optional(string),\n description: 'OpenRouter API key',\n }),\n}\n\nconst startCommand = command({\n name: 'start',\n description: 'Start proxy server',\n args: {\n port: option({\n long: 'port',\n short: 'p',\n type: number,\n description: 'Proxy server port',\n defaultValue: () => 8080,\n defaultValueIsSerializable: true,\n }),\n host: option({\n long: 'host',\n type: string,\n description: 'Proxy server host',\n defaultValue: () => '0.0.0.0',\n defaultValueIsSerializable: true,\n }),\n config: option({\n long: 'config',\n short: 'c',\n type: optional(string),\n description: 'Path to config file',\n }),\n noConfig: flag({ long: 'no-config', description: 'Skip config file discovery' }),\n openrouterKey: option({\n long: 'openrouter-key',\n type: optional(string),\n description: 'OpenRouter API key',\n }),\n verbose: flag({ long: 'verbose', description: 'Enable verbose logging' }),\n },\n handler: async args => {\n try {\n const cfg = await loadConfig({\n configPath: args.config ?? undefined,\n noConfig: args.noConfig,\n port: args.port,\n host: args.host,\n openrouterKey: args.openrouterKey ?? undefined,\n verbose: args.verbose,\n })\n startProxyServer(cfg, () => {\n logger.ready(`Proxitor proxy listening on ${cfg.host}:${cfg.port}`)\n logger.info('Routing requests to OpenRouter')\n })\n } catch (error) {\n logger.error('Failed to start proxy:', error)\n process.exit(1)\n }\n },\n})\n\nconst withApiKey =\n (fn: (apiKey: string) => Promise<void>) =>\n async (args: { config?: string; openrouterKey?: string }) => {\n const apiKey = await resolveApiKey(\n args.config ?? undefined,\n args.openrouterKey ?? undefined,\n )\n if (apiKey) await fn(apiKey)\n }\n\nconst configCli = subcommands({\n name: 'config',\n description: 'Manage proxy configuration',\n cmds: {\n add: command({\n name: 'add',\n description: 'Add model override',\n args: configOptionArgs,\n handler: withApiKey(async k =>\n (await import('./commands/config/add.js')).addOverrideCommand(k),\n ),\n }),\n edit: command({\n name: 'edit',\n description: 'Edit model override',\n args: configOptionArgs,\n handler: withApiKey(async k =>\n (await import('./commands/config/edit.js')).editOverrideCommand(k),\n ),\n }),\n remove: command({\n name: 'remove',\n description: 'Remove model override',\n args: {},\n handler: async () =>\n (await import('./commands/config/remove.js')).removeOverrideCommand(),\n }),\n list: command({\n name: 'list',\n description: 'List current overrides',\n args: {},\n handler: async () =>\n (await import('./commands/config/list.js')).listOverridesCommand(),\n }),\n browse: command({\n name: 'browse',\n description: 'Browse models',\n args: configOptionArgs,\n handler: withApiKey(async k =>\n (await import('./commands/config/browse.js')).browseModelsCommand(k),\n ),\n }),\n validate: command({\n name: 'validate',\n description: 'Validate config',\n args: {},\n handler: async () =>\n (await import('./commands/config/validate.js')).validateConfigCommand(),\n }),\n menu: command({\n name: 'menu',\n description: 'Interactive configuration menu',\n args: configOptionArgs,\n handler: withApiKey(async k =>\n (await import('./commands/config.js')).runConfigMenu(k),\n ),\n }),\n },\n})\n\nconst rootCli = subcommands({\n name: 'proxitor',\n version,\n description: 'Lightweight proxy for routing CLI requests to OpenRouter',\n cmds: { start: startCommand, config: configCli },\n})\n\nconst hasSubcommand = argv.some(a => !a.startsWith('-'))\nif (hasSubcommand || isInfo) {\n void run(rootCli, argv)\n} else {\n void run(startCommand, argv)\n}\n"],"mappings":"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"}