proxitor 0.3.0 → 0.5.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 (62) hide show
  1. package/README.md +27 -8
  2. package/dist/add.mjs +26 -27
  3. package/dist/add.mjs.map +1 -1
  4. package/dist/browse.mjs +20 -21
  5. package/dist/browse.mjs.map +1 -1
  6. package/dist/cli.mjs +14774 -33
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/config.mjs +6 -5
  9. package/dist/config.mjs.map +1 -1
  10. package/dist/config2.mjs +5 -6
  11. package/dist/config2.mjs.map +1 -1
  12. package/dist/dist.mjs +1325 -0
  13. package/dist/dist.mjs.map +1 -0
  14. package/dist/dist2.mjs +6617 -0
  15. package/dist/dist2.mjs.map +1 -0
  16. package/dist/edit.mjs +17 -18
  17. package/dist/edit.mjs.map +1 -1
  18. package/dist/list.mjs +4 -4
  19. package/dist/list.mjs.map +1 -1
  20. package/dist/prompt.mjs +849 -0
  21. package/dist/prompt.mjs.map +1 -0
  22. package/dist/providers.mjs +16 -16
  23. package/dist/providers.mjs.map +1 -1
  24. package/dist/remove.mjs +10 -11
  25. package/dist/remove.mjs.map +1 -1
  26. package/dist/validate.mjs +9 -9
  27. package/dist/validate.mjs.map +1 -1
  28. package/dist/wizard.mjs +222 -0
  29. package/dist/wizard.mjs.map +1 -0
  30. package/package.json +1 -16
  31. package/dist/add.cjs +0 -139
  32. package/dist/add.cjs.map +0 -1
  33. package/dist/browse.cjs +0 -88
  34. package/dist/browse.cjs.map +0 -1
  35. package/dist/cli.cjs +0 -159
  36. package/dist/cli.cjs.map +0 -1
  37. package/dist/cli.d.cts +0 -1
  38. package/dist/cli.d.mts +0 -1
  39. package/dist/config.cjs +0 -68
  40. package/dist/config.cjs.map +0 -1
  41. package/dist/config2.cjs +0 -75
  42. package/dist/config2.cjs.map +0 -1
  43. package/dist/edit.cjs +0 -82
  44. package/dist/edit.cjs.map +0 -1
  45. package/dist/index.cjs +0 -12
  46. package/dist/index.d.cts +0 -261
  47. package/dist/index.d.cts.map +0 -1
  48. package/dist/index.d.mts +0 -261
  49. package/dist/index.d.mts.map +0 -1
  50. package/dist/index.mjs +0 -2
  51. package/dist/list.cjs +0 -33
  52. package/dist/list.cjs.map +0 -1
  53. package/dist/providers.cjs +0 -376
  54. package/dist/providers.cjs.map +0 -1
  55. package/dist/proxy.cjs +0 -656
  56. package/dist/proxy.cjs.map +0 -1
  57. package/dist/proxy.mjs +0 -544
  58. package/dist/proxy.mjs.map +0 -1
  59. package/dist/remove.cjs +0 -38
  60. package/dist/remove.cjs.map +0 -1
  61. package/dist/validate.cjs +0 -26
  62. package/dist/validate.cjs.map +0 -1
@@ -0,0 +1,222 @@
1
+ import { n as findConfigFile, r as getXdgConfigDir } from "./cli.mjs";
2
+ import { confirm as le, intro as ye, isCancel as R$1, log as R, note as Ce, outro as fe, select as Ee, text as Re } from "./dist.mjs";
3
+ import { t as require_dist } from "./dist2.mjs";
4
+ import { homedir } from "node:os";
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
+ import { dirname, join, resolve } from "node:path";
7
+ //#region src/commands/config/wizard.ts
8
+ var import_dist = require_dist();
9
+ const DEFAULT_PORT = 8828;
10
+ const DEFAULT_HOST = "0.0.0.0";
11
+ const DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
12
+ function maskKey(key) {
13
+ if (key.length <= 11) return "****";
14
+ return `${key.slice(0, 7)}...${key.slice(-4)}`;
15
+ }
16
+ function resolveSavePath(location) {
17
+ switch (location) {
18
+ case "local": return resolve("proxitor.config.yaml");
19
+ case "user": return join(homedir(), ".config", "proxitor", "config.yaml");
20
+ case "xdg": return join(getXdgConfigDir(), "config.yaml");
21
+ }
22
+ }
23
+ function getSaveLocationOptions(_existingPath) {
24
+ const opts = [{
25
+ value: "local",
26
+ label: "./proxitor.config.yaml",
27
+ hint: "Project directory"
28
+ }, {
29
+ value: "user",
30
+ label: "~/.config/proxitor/config.yaml",
31
+ hint: "User config"
32
+ }];
33
+ if (process.env.XDG_CONFIG_HOME) opts.push({
34
+ value: "xdg",
35
+ label: "$XDG_CONFIG_HOME/proxitor/config.yaml",
36
+ hint: "XDG config directory"
37
+ });
38
+ return opts;
39
+ }
40
+ function detectLocation(path) {
41
+ const cwd = resolve(".");
42
+ if (path.startsWith(cwd)) return "local";
43
+ const userDir = join(homedir(), ".config", "proxitor");
44
+ if (path.startsWith(userDir)) {
45
+ const xdgDir = process.env.XDG_CONFIG_HOME ? join(process.env.XDG_CONFIG_HOME, "proxitor") : null;
46
+ if (xdgDir && path.startsWith(xdgDir)) return "xdg";
47
+ return "user";
48
+ }
49
+ }
50
+ function buildYaml(apiKey, port, host, baseUrl, existingRaw) {
51
+ if (existingRaw) {
52
+ const doc = (0, import_dist.parseDocument)(existingRaw);
53
+ doc.set("openrouterKey", apiKey);
54
+ doc.set("port", port);
55
+ doc.set("host", host);
56
+ if (baseUrl !== DEFAULT_BASE_URL) doc.set("openrouterBaseUrl", baseUrl);
57
+ else doc.delete("openrouterBaseUrl");
58
+ return doc.toString();
59
+ }
60
+ const config = {
61
+ openrouterKey: apiKey,
62
+ port,
63
+ host
64
+ };
65
+ if (baseUrl !== DEFAULT_BASE_URL) config.openrouterBaseUrl = baseUrl;
66
+ return (0, import_dist.stringify)(config);
67
+ }
68
+ function readExistingConfig(path) {
69
+ const raw = readFileSync(path, "utf-8");
70
+ const parsed = (0, import_dist.parseDocument)(raw).toJSON();
71
+ return {
72
+ raw,
73
+ port: typeof parsed?.port === "number" ? parsed.port : DEFAULT_PORT,
74
+ host: typeof parsed?.host === "string" ? parsed.host : DEFAULT_HOST,
75
+ apiKey: typeof parsed?.openrouterKey === "string" ? parsed.openrouterKey : "",
76
+ baseUrl: typeof parsed?.openrouterBaseUrl === "string" ? parsed.openrouterBaseUrl : DEFAULT_BASE_URL
77
+ };
78
+ }
79
+ async function askApiKey(currentKey) {
80
+ if (currentKey) R.info(`Current key: ${maskKey(currentKey)}`);
81
+ const apiKey = await Re({
82
+ message: "OpenRouter API key",
83
+ placeholder: "sk-or-v1-...",
84
+ initialValue: currentKey,
85
+ validate: (v) => {
86
+ if (!v?.trim()) return "API key is required";
87
+ }
88
+ });
89
+ if (R$1(apiKey)) return null;
90
+ Ce("You can also set the OPENROUTER_API_KEY environment variable\nto avoid storing the key in the config file.", "Tip");
91
+ return apiKey;
92
+ }
93
+ async function askPort(current) {
94
+ const input = await Re({
95
+ message: "Proxy port",
96
+ initialValue: String(current),
97
+ placeholder: String(DEFAULT_PORT),
98
+ validate: (v) => {
99
+ if (!v?.trim()) return void 0;
100
+ const n = Number.parseInt(v, 10);
101
+ if (Number.isNaN(n) || n < 1 || n > 65535) return "Port must be 1–65535";
102
+ }
103
+ });
104
+ if (R$1(input)) return null;
105
+ return input.trim() ? Number.parseInt(input, 10) : DEFAULT_PORT;
106
+ }
107
+ async function askBaseUrl(current) {
108
+ const url = await Re({
109
+ message: "OpenRouter API base URL",
110
+ placeholder: DEFAULT_BASE_URL,
111
+ initialValue: current === DEFAULT_BASE_URL ? "" : current,
112
+ validate: (v) => {
113
+ if (!v?.trim()) return void 0;
114
+ try {
115
+ if (!new URL(v.trim()).protocol.startsWith("http")) return "URL must start with http:// or https://";
116
+ } catch {
117
+ return "Invalid URL";
118
+ }
119
+ }
120
+ });
121
+ if (R$1(url)) return null;
122
+ return url.trim() || DEFAULT_BASE_URL;
123
+ }
124
+ async function askHost(current) {
125
+ const host = await Ee({
126
+ message: "Listen address",
127
+ initialValue: current,
128
+ options: [{
129
+ value: "0.0.0.0",
130
+ label: "All interfaces (0.0.0.0)",
131
+ hint: "Default"
132
+ }, {
133
+ value: "127.0.0.1",
134
+ label: "Localhost only (127.0.0.1)",
135
+ hint: "More secure"
136
+ }]
137
+ });
138
+ if (R$1(host)) return null;
139
+ return host;
140
+ }
141
+ async function askSaveLocation(existingPath) {
142
+ const options = getSaveLocationOptions(existingPath);
143
+ const location = await Ee({
144
+ message: "Save config to",
145
+ initialValue: (existingPath ? detectLocation(existingPath) : void 0) ?? "local",
146
+ options
147
+ });
148
+ if (R$1(location)) return null;
149
+ return location;
150
+ }
151
+ async function runWizard() {
152
+ ye("Proxitor Setup Wizard");
153
+ const existingPath = findConfigFile();
154
+ let existingRaw;
155
+ let currentPort = DEFAULT_PORT;
156
+ let currentHost = DEFAULT_HOST;
157
+ let currentKey = "";
158
+ let currentBaseUrl = DEFAULT_BASE_URL;
159
+ if (existingPath) {
160
+ Ce(existingPath, "Existing config found");
161
+ const reconfigure = await le({
162
+ message: "Reconfigure?",
163
+ initialValue: true
164
+ });
165
+ if (R$1(reconfigure) || !reconfigure) {
166
+ fe("Using existing configuration");
167
+ return;
168
+ }
169
+ try {
170
+ const existing = readExistingConfig(existingPath);
171
+ existingRaw = existing.raw;
172
+ currentPort = existing.port;
173
+ currentHost = existing.host;
174
+ currentKey = existing.apiKey;
175
+ currentBaseUrl = existing.baseUrl;
176
+ } catch {}
177
+ }
178
+ const apiKey = await askApiKey(currentKey);
179
+ if (apiKey === null) {
180
+ fe("Cancelled");
181
+ return;
182
+ }
183
+ const port = await askPort(currentPort);
184
+ if (port === null) {
185
+ fe("Cancelled");
186
+ return;
187
+ }
188
+ const baseUrl = await askBaseUrl(currentBaseUrl);
189
+ if (baseUrl === null) {
190
+ fe("Cancelled");
191
+ return;
192
+ }
193
+ const host = await askHost(currentHost);
194
+ if (host === null) {
195
+ fe("Cancelled");
196
+ return;
197
+ }
198
+ const location = await askSaveLocation(existingPath ?? void 0);
199
+ if (location === null) {
200
+ fe("Cancelled");
201
+ return;
202
+ }
203
+ const yaml = buildYaml(apiKey, port, host, baseUrl, existingRaw);
204
+ Ce(yaml, "Preview");
205
+ const save = await le({
206
+ message: "Save this configuration?",
207
+ initialValue: true
208
+ });
209
+ if (R$1(save) || !save) {
210
+ fe("Cancelled — no files written");
211
+ return;
212
+ }
213
+ const savePath = resolveSavePath(location);
214
+ const dir = dirname(savePath);
215
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
216
+ writeFileSync(savePath, yaml, "utf-8");
217
+ fe(`Config saved to ${savePath}`);
218
+ }
219
+ //#endregion
220
+ export { runWizard };
221
+
222
+ //# sourceMappingURL=wizard.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wizard.mjs","names":["clack.text","isCancel","clack.select","clack.confirm"],"sources":["../src/commands/config/wizard.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, join, resolve } from 'node:path'\nimport * as clack from '@clack/prompts'\nimport { isCancel } from '@clack/prompts'\nimport { parseDocument, stringify } from 'yaml'\nimport { findConfigFile, getXdgConfigDir } from '../../config.js'\n\nconst DEFAULT_PORT = 8828\nconst DEFAULT_HOST = '0.0.0.0'\nconst DEFAULT_BASE_URL = 'https://openrouter.ai/api/v1'\n\ntype SaveLocation = 'local' | 'user' | 'xdg'\n\nfunction maskKey(key: string): string {\n if (key.length <= 11) return '****'\n return `${key.slice(0, 7)}...${key.slice(-4)}`\n}\n\nfunction resolveSavePath(location: SaveLocation): string {\n switch (location) {\n case 'local':\n return resolve('proxitor.config.yaml')\n case 'user':\n return join(homedir(), '.config', 'proxitor', 'config.yaml')\n case 'xdg':\n return join(getXdgConfigDir(), 'config.yaml')\n }\n}\n\nfunction getSaveLocationOptions(_existingPath?: string) {\n const opts: { value: SaveLocation; label: string; hint: string }[] = [\n { value: 'local', label: './proxitor.config.yaml', hint: 'Project directory' },\n { value: 'user', label: '~/.config/proxitor/config.yaml', hint: 'User config' },\n ]\n\n if (process.env.XDG_CONFIG_HOME) {\n opts.push({\n value: 'xdg',\n label: '$XDG_CONFIG_HOME/proxitor/config.yaml',\n hint: 'XDG config directory',\n })\n }\n\n return opts\n}\n\nfunction detectLocation(path: string): SaveLocation | undefined {\n const cwd = resolve('.')\n if (path.startsWith(cwd)) return 'local'\n const userDir = join(homedir(), '.config', 'proxitor')\n if (path.startsWith(userDir)) {\n const xdgDir = process.env.XDG_CONFIG_HOME\n ? join(process.env.XDG_CONFIG_HOME, 'proxitor')\n : null\n if (xdgDir && path.startsWith(xdgDir)) return 'xdg'\n return 'user'\n }\n return undefined\n}\n\nfunction buildYaml(\n apiKey: string,\n port: number,\n host: string,\n baseUrl: string,\n existingRaw?: string,\n): string {\n if (existingRaw) {\n const doc = parseDocument(existingRaw)\n doc.set('openrouterKey', apiKey)\n doc.set('port', port)\n doc.set('host', host)\n if (baseUrl !== DEFAULT_BASE_URL) {\n doc.set('openrouterBaseUrl', baseUrl)\n } else {\n doc.delete('openrouterBaseUrl')\n }\n return doc.toString()\n }\n\n const config: Record<string, unknown> = { openrouterKey: apiKey, port, host }\n if (baseUrl !== DEFAULT_BASE_URL) {\n config.openrouterBaseUrl = baseUrl\n }\n return stringify(config)\n}\n\nfunction readExistingConfig(path: string): {\n raw: string\n port: number\n host: string\n apiKey: string\n baseUrl: string\n} {\n const raw = readFileSync(path, 'utf-8')\n const parsed = parseDocument(raw).toJSON() as Record<string, unknown>\n return {\n raw,\n port: typeof parsed?.port === 'number' ? parsed.port : DEFAULT_PORT,\n host: typeof parsed?.host === 'string' ? parsed.host : DEFAULT_HOST,\n apiKey: typeof parsed?.openrouterKey === 'string' ? parsed.openrouterKey : '',\n baseUrl:\n typeof parsed?.openrouterBaseUrl === 'string'\n ? parsed.openrouterBaseUrl\n : DEFAULT_BASE_URL,\n }\n}\n\nasync function askApiKey(currentKey: string): Promise<string | null> {\n if (currentKey) {\n clack.log.info(`Current key: ${maskKey(currentKey)}`)\n }\n const apiKey = await clack.text({\n message: 'OpenRouter API key',\n placeholder: 'sk-or-v1-...',\n initialValue: currentKey,\n validate: v => {\n if (!v?.trim()) return 'API key is required'\n return undefined\n },\n })\n if (isCancel(apiKey)) return null\n\n clack.note(\n 'You can also set the OPENROUTER_API_KEY environment variable\\nto avoid storing the key in the config file.',\n 'Tip',\n )\n return apiKey as string\n}\n\nasync function askPort(current: number): Promise<number | null> {\n const input = await clack.text({\n message: 'Proxy port',\n initialValue: String(current),\n placeholder: String(DEFAULT_PORT),\n validate: v => {\n if (!v?.trim()) return undefined\n const n = Number.parseInt(v, 10)\n if (Number.isNaN(n) || n < 1 || n > 65535) return 'Port must be 1–65535'\n return undefined\n },\n })\n if (isCancel(input)) return null\n return (input as string).trim() ? Number.parseInt(input as string, 10) : DEFAULT_PORT\n}\n\nasync function askBaseUrl(current: string): Promise<string | null> {\n const url = await clack.text({\n message: 'OpenRouter API base URL',\n placeholder: DEFAULT_BASE_URL,\n initialValue: current === DEFAULT_BASE_URL ? '' : current,\n validate: v => {\n if (!v?.trim()) return undefined\n try {\n const parsed = new URL(v.trim())\n if (!parsed.protocol.startsWith('http'))\n return 'URL must start with http:// or https://'\n } catch {\n return 'Invalid URL'\n }\n return undefined\n },\n })\n if (isCancel(url)) return null\n return (url as string).trim() || DEFAULT_BASE_URL\n}\n\nasync function askHost(current: string): Promise<string | null> {\n const host = await clack.select({\n message: 'Listen address',\n initialValue: current as '0.0.0.0' | '127.0.0.1',\n options: [\n { value: '0.0.0.0', label: 'All interfaces (0.0.0.0)', hint: 'Default' },\n { value: '127.0.0.1', label: 'Localhost only (127.0.0.1)', hint: 'More secure' },\n ],\n })\n if (isCancel(host)) return null\n return host as string\n}\n\nasync function askSaveLocation(existingPath?: string): Promise<SaveLocation | null> {\n const options = getSaveLocationOptions(existingPath)\n const detected = existingPath ? detectLocation(existingPath) : undefined\n\n const location = await clack.select({\n message: 'Save config to',\n initialValue: detected ?? 'local',\n options,\n })\n if (isCancel(location)) return null\n return location as SaveLocation\n}\n\nexport async function runWizard(): Promise<void> {\n clack.intro('Proxitor Setup Wizard')\n\n const existingPath = findConfigFile()\n let existingRaw: string | undefined\n let currentPort = DEFAULT_PORT\n let currentHost = DEFAULT_HOST\n let currentKey = ''\n let currentBaseUrl = DEFAULT_BASE_URL\n\n if (existingPath) {\n clack.note(existingPath, 'Existing config found')\n\n const reconfigure = await clack.confirm({\n message: 'Reconfigure?',\n initialValue: true,\n })\n if (isCancel(reconfigure) || !reconfigure) {\n clack.outro('Using existing configuration')\n return\n }\n\n try {\n const existing = readExistingConfig(existingPath)\n existingRaw = existing.raw\n currentPort = existing.port\n currentHost = existing.host\n currentKey = existing.apiKey\n currentBaseUrl = existing.baseUrl\n } catch {\n // use defaults\n }\n }\n\n const apiKey = await askApiKey(currentKey)\n if (apiKey === null) {\n clack.outro('Cancelled')\n return\n }\n\n const port = await askPort(currentPort)\n if (port === null) {\n clack.outro('Cancelled')\n return\n }\n\n const baseUrl = await askBaseUrl(currentBaseUrl)\n if (baseUrl === null) {\n clack.outro('Cancelled')\n return\n }\n\n const host = await askHost(currentHost)\n if (host === null) {\n clack.outro('Cancelled')\n return\n }\n\n const location = await askSaveLocation(existingPath ?? undefined)\n if (location === null) {\n clack.outro('Cancelled')\n return\n }\n\n const yaml = buildYaml(apiKey, port, host, baseUrl, existingRaw)\n clack.note(yaml, 'Preview')\n\n const save = await clack.confirm({\n message: 'Save this configuration?',\n initialValue: true,\n })\n if (isCancel(save) || !save) {\n clack.outro('Cancelled — no files written')\n return\n }\n\n const savePath = resolveSavePath(location)\n const dir = dirname(savePath)\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n writeFileSync(savePath, yaml, 'utf-8')\n\n clack.outro(`Config saved to ${savePath}`)\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,26 +1,11 @@
1
1
  {
2
2
  "name": "proxitor",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Lightweight proxy for routing CLI requests (claude-code, codex) to OpenRouter",
6
6
  "files": [
7
7
  "dist"
8
8
  ],
9
- "exports": {
10
- ".": {
11
- "import": {
12
- "types": "./dist/index.d.mts",
13
- "default": "./dist/index.mjs"
14
- },
15
- "require": {
16
- "types": "./dist/index.d.cts",
17
- "default": "./dist/index.cjs"
18
- }
19
- }
20
- },
21
- "main": "./dist/index.cjs",
22
- "module": "./dist/index.mjs",
23
- "types": "./dist/index.d.mts",
24
9
  "keywords": [
25
10
  "proxy",
26
11
  "openrouter",
package/dist/add.cjs DELETED
@@ -1,139 +0,0 @@
1
- const require_proxy = require("./proxy.cjs");
2
- const require_providers = require("./providers.cjs");
3
- const require_config = require("./config.cjs");
4
- let _clack_prompts = require("@clack/prompts");
5
- _clack_prompts = require_proxy.__toESM(_clack_prompts, 1);
6
- //#region src/commands/config/add.ts
7
- const CUSTOM_PATTERN = "__custom_pattern__";
8
- /** Run the interactive "Add model override" flow. */
9
- async function addOverrideCommand(apiKey) {
10
- _clack_prompts.intro("Add Model Override");
11
- const configPath = require_config.requireConfigPath();
12
- const client = new require_providers.OpenRouterClient(apiKey);
13
- const models = await loadModelsWithSpinner(client);
14
- if (!models) return;
15
- const modelId = await searchModel(models);
16
- if (!modelId) return;
17
- if (typeof modelId !== "string") return;
18
- if (modelId === CUSTOM_PATTERN) {
19
- const pattern = await enterPattern(models);
20
- if (!pattern) return;
21
- if (require_config.getModelOverrides(configPath)[pattern]) {
22
- _clack_prompts.log.warn(`Override for "${pattern}" already exists. Use Edit instead.`);
23
- return;
24
- }
25
- await configureProviderAndSave(configPath, client, pattern, true);
26
- return;
27
- }
28
- const selected = models.find((m) => m.id === modelId);
29
- if (selected) displayModelInfo(selected);
30
- if (require_config.getModelOverrides(configPath)[modelId]) {
31
- _clack_prompts.log.warn(`Override for "${modelId}" already exists. Use Edit instead.`);
32
- return;
33
- }
34
- await configureProviderAndSave(configPath, client, modelId, false);
35
- }
36
- async function loadModelsWithSpinner(client) {
37
- const s = _clack_prompts.spinner();
38
- s.start("Loading models from OpenRouter...");
39
- try {
40
- const models = await require_providers.fetchModels(client);
41
- s.stop(`${models.length} models available`);
42
- return models;
43
- } catch (error) {
44
- s.stop("Failed to load models");
45
- _clack_prompts.log.error(String(error));
46
- return null;
47
- }
48
- }
49
- async function searchModel(models) {
50
- const result = await _clack_prompts.autocomplete({
51
- message: "Search for a model",
52
- placeholder: "Type to search (e.g. \"claude\", \"gpt-4o\", \"qwen\")",
53
- maxItems: 15,
54
- options() {
55
- const query = this.userInput.trim().toLowerCase();
56
- if (!query) return [{
57
- value: CUSTOM_PATTERN,
58
- label: "✏️ Enter custom pattern (e.g. \"claude-*\")"
59
- }];
60
- return [...models.filter((m) => {
61
- return `${m.id} ${m.name}`.toLowerCase().includes(query);
62
- }).slice(0, 14).map((m) => ({
63
- value: m.id,
64
- label: require_providers.formatModelLabel(m),
65
- hint: require_providers.formatModelHint(m)
66
- })), {
67
- value: CUSTOM_PATTERN,
68
- label: "✏️ Enter custom pattern (e.g. \"claude-*\")"
69
- }];
70
- },
71
- filter: (_search, _option) => true
72
- });
73
- if ((0, _clack_prompts.isCancel)(result)) return null;
74
- return result;
75
- }
76
- async function enterPattern(models) {
77
- const pattern = await _clack_prompts.text({
78
- message: "Enter model pattern",
79
- placeholder: "e.g. claude-*, gpt-4*, anthropic/*",
80
- validate: (v) => {
81
- if (!v?.trim()) return "Pattern cannot be empty";
82
- }
83
- });
84
- if ((0, _clack_prompts.isCancel)(pattern)) return null;
85
- const pat = pattern.trim();
86
- const matches = countPatternMatches(pat, models);
87
- if (matches > 0) _clack_prompts.log.info(`Pattern "${pat}" matches ${matches} model(s)`);
88
- else _clack_prompts.log.warn(`Pattern "${pat}" does not match any current models — it will still be saved`);
89
- return pat;
90
- }
91
- async function configureProviderAndSave(configPath, client, modelKey, isPattern) {
92
- const mode = await require_providers.selectRoutingMode("Configure provider routing");
93
- if ((0, _clack_prompts.isCancel)(mode)) return;
94
- if (mode === "skip") {
95
- require_config.setModelOverride(configPath, modelKey, {});
96
- _clack_prompts.outro("Done — override saved without provider routing");
97
- return;
98
- }
99
- const providerOptions = await require_providers.fetchProvidersForModel(client, modelKey, isPattern);
100
- if (!providerOptions) return;
101
- const override = await require_providers.selectProvidersByMode(mode, providerOptions);
102
- if (!override) return;
103
- _clack_prompts.log.info(`Proposed override:\n ${modelKey}:\n ${formatOverrideYaml(override)}`);
104
- const save = await _clack_prompts.confirm({ message: "Save to config?" });
105
- if ((0, _clack_prompts.isCancel)(save) || !save) {
106
- _clack_prompts.outro("Cancelled");
107
- return;
108
- }
109
- require_config.setModelOverride(configPath, modelKey, override);
110
- _clack_prompts.outro("✓ Model override saved");
111
- }
112
- function displayModelInfo(model) {
113
- _clack_prompts.log.info(`${model.name || model.id}`);
114
- _clack_prompts.log.info(` Context: ${require_providers.formatContextLength(model.context_length)} tokens`);
115
- _clack_prompts.log.info(` Pricing: ${require_providers.formatPricing(model.pricing.prompt, model.pricing.completion)}`);
116
- 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)}`);
117
- 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)}`);
118
- if (model.top_provider?.max_completion_tokens) _clack_prompts.log.info(` Max output: ${require_providers.formatContextLength(model.top_provider.max_completion_tokens)} tokens`);
119
- if (model.architecture?.modality) _clack_prompts.log.info(` Modality: ${model.architecture.modality}`);
120
- }
121
- function countPatternMatches(pattern, models) {
122
- if (pattern.endsWith("*")) {
123
- const prefix = pattern.slice(0, -1);
124
- return models.filter((m) => m.id.startsWith(prefix)).length;
125
- }
126
- return models.filter((m) => m.id === pattern).length;
127
- }
128
- function formatOverrideYaml(override) {
129
- const parts = [];
130
- if (override.provider && typeof override.provider === "object") {
131
- const p = override.provider;
132
- for (const [key, value] of Object.entries(p)) parts.push(`provider.${key}: ${JSON.stringify(value)}`);
133
- }
134
- return parts.join("\n ") || "(empty)";
135
- }
136
- //#endregion
137
- exports.addOverrideCommand = addOverrideCommand;
138
-
139
- //# sourceMappingURL=add.cjs.map
package/dist/add.cjs.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"add.cjs","names":["requireConfigPath","OpenRouterClient","getModelOverrides","clack","fetchModels","formatModelLabel","formatModelHint","selectRoutingMode","fetchProvidersForModel","selectProvidersByMode","formatContextLength","formatPricing","formatPrice"],"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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"}
package/dist/browse.cjs DELETED
@@ -1,88 +0,0 @@
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
@@ -1 +0,0 @@
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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"}