proxitor 0.5.2 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"config.mjs","names":[],"sources":["../src/commands/config/config.ts"],"sourcesContent":["import { readFileSync, writeFileSync } from 'node:fs'\nimport type { YAMLMap } from 'yaml'\nimport { parseDocument } from 'yaml'\nimport { findConfigFile } from '../../config.js'\nimport type { ModelOverride } from '../../config-schema.js'\nimport { logger } from '../../logger.js'\n\nexport function requireConfigPath(): string {\n const path = findConfigFile()\n if (!path) {\n throw new Error(\n 'No config file found. Create proxitor.config.yaml first, or pass -c <path>.',\n )\n }\n return path\n}\n\nexport function readConfigRaw(path: string): string {\n return readFileSync(path, 'utf-8')\n}\n\nexport function writeConfigRaw(path: string, content: string): void {\n writeFileSync(path, content, 'utf-8')\n}\n\nexport function setModelOverride(\n configPath: string,\n modelKey: string,\n override: ModelOverride,\n): void {\n const raw = readConfigRaw(configPath)\n const doc = parseDocument(raw)\n\n let overrides = doc.get('modelOverrides') as YAMLMap | undefined\n if (!overrides) {\n overrides = doc.createNode({}) as YAMLMap\n doc.set('modelOverrides', overrides)\n }\n\n overrides.set(modelKey, override)\n\n writeConfigRaw(configPath, doc.toString())\n logger.success(`Saved override for \"${modelKey}\"`)\n}\n\nexport function removeModelOverride(configPath: string, modelKey: string): void {\n const raw = readConfigRaw(configPath)\n const doc = parseDocument(raw)\n\n const overrides = doc.get('modelOverrides') as YAMLMap | undefined\n if (!overrides?.has(modelKey)) {\n throw new Error(`No override found for \"${modelKey}\"`)\n }\n\n overrides.delete(modelKey)\n if (overrides.items.length === 0) {\n doc.delete('modelOverrides')\n }\n\n writeConfigRaw(configPath, doc.toString())\n logger.success(`Removed override for \"${modelKey}\"`)\n}\n\nexport function getModelOverrides(configPath: string): Record<string, ModelOverride> {\n const raw = readConfigRaw(configPath)\n const doc = parseDocument(raw)\n const overrides = doc.get('modelOverrides')\n if (!overrides) return {}\n if (typeof overrides === 'object' && overrides !== null && 'toJSON' in overrides) {\n return (overrides as { toJSON: () => unknown }).toJSON() as Record<\n string,\n ModelOverride\n >\n }\n return overrides as unknown as Record<string, ModelOverride>\n}\n"],"mappings":";;;;;AAOA,SAAgB,oBAA4B;CAC1C,MAAM,OAAO,eAAe;CAC5B,IAAI,CAAC,MACH,MAAM,IAAI,MACR,6EACF;CAEF,OAAO;AACT;AAEA,SAAgB,cAAc,MAAsB;CAClD,OAAO,aAAa,MAAM,OAAO;AACnC;AAEA,SAAgB,eAAe,MAAc,SAAuB;CAClE,cAAc,MAAM,SAAS,OAAO;AACtC;AAEA,SAAgB,iBACd,YACA,UACA,UACM;CAEN,MAAM,OAAA,GAAA,YAAA,eADM,cAAc,UACE,CAAC;CAE7B,IAAI,YAAY,IAAI,IAAI,gBAAgB;CACxC,IAAI,CAAC,WAAW;EACd,YAAY,IAAI,WAAW,CAAC,CAAC;EAC7B,IAAI,IAAI,kBAAkB,SAAS;CACrC;CAEA,UAAU,IAAI,UAAU,QAAQ;CAEhC,eAAe,YAAY,IAAI,SAAS,CAAC;CACzC,OAAO,QAAQ,uBAAuB,SAAS,EAAE;AACnD;AAEA,SAAgB,oBAAoB,YAAoB,UAAwB;CAE9E,MAAM,OAAA,GAAA,YAAA,eADM,cAAc,UACE,CAAC;CAE7B,MAAM,YAAY,IAAI,IAAI,gBAAgB;CAC1C,IAAI,CAAC,WAAW,IAAI,QAAQ,GAC1B,MAAM,IAAI,MAAM,0BAA0B,SAAS,EAAE;CAGvD,UAAU,OAAO,QAAQ;CACzB,IAAI,UAAU,MAAM,WAAW,GAC7B,IAAI,OAAO,gBAAgB;CAG7B,eAAe,YAAY,IAAI,SAAS,CAAC;CACzC,OAAO,QAAQ,yBAAyB,SAAS,EAAE;AACrD;AAEA,SAAgB,kBAAkB,YAAmD;CAGnF,MAAM,aAAA,GAAA,YAAA,eAFM,cAAc,UACE,CACR,EAAE,IAAI,gBAAgB;CAC1C,IAAI,CAAC,WAAW,OAAO,CAAC;CACxB,IAAI,OAAO,cAAc,YAAY,cAAc,QAAQ,YAAY,WACrE,OAAQ,UAAwC,OAAO;CAKzD,OAAO;AACT"}
1
+ {"version":3,"file":"config.mjs","names":[],"sources":["../src/commands/config/config.ts"],"sourcesContent":["import { readFileSync, writeFileSync } from 'node:fs';\nimport type { YAMLMap } from 'yaml';\nimport { parseDocument } from 'yaml';\nimport { findConfigFile } from '../../config.js';\nimport type { ModelOverride } from '../../config-schema.js';\nimport { logger } from '../../logger.js';\n\nexport function requireConfigPath(): string {\n const path = findConfigFile();\n if (!path) {\n throw new Error(\n 'No config file found. Create proxitor.config.yaml first, or pass -c <path>.',\n );\n }\n return path;\n}\n\nexport function readConfigRaw(path: string): string {\n return readFileSync(path, 'utf-8');\n}\n\nexport function writeConfigRaw(path: string, content: string): void {\n writeFileSync(path, content, 'utf-8');\n}\n\nexport function setModelOverride(\n configPath: string,\n modelKey: string,\n override: ModelOverride,\n): void {\n const raw = readConfigRaw(configPath);\n const doc = parseDocument(raw);\n\n let overrides = doc.get('modelOverrides') as YAMLMap | undefined;\n if (!overrides) {\n overrides = doc.createNode({}) as YAMLMap;\n doc.set('modelOverrides', overrides);\n }\n\n overrides.set(modelKey, override);\n\n writeConfigRaw(configPath, doc.toString());\n logger.success(`Saved override for \"${modelKey}\"`);\n}\n\nexport function removeModelOverride(configPath: string, modelKey: string): void {\n const raw = readConfigRaw(configPath);\n const doc = parseDocument(raw);\n\n const overrides = doc.get('modelOverrides') as YAMLMap | undefined;\n if (!overrides?.has(modelKey)) {\n throw new Error(`No override found for \"${modelKey}\"`);\n }\n\n overrides.delete(modelKey);\n if (overrides.items.length === 0) {\n doc.delete('modelOverrides');\n }\n\n writeConfigRaw(configPath, doc.toString());\n logger.success(`Removed override for \"${modelKey}\"`);\n}\n\nexport function getModelOverrides(configPath: string): Record<string, ModelOverride> {\n const raw = readConfigRaw(configPath);\n const doc = parseDocument(raw);\n const overrides = doc.get('modelOverrides');\n if (!overrides) return {};\n if (typeof overrides === 'object' && overrides !== null && 'toJSON' in overrides) {\n return (overrides as { toJSON: () => unknown }).toJSON() as Record<\n string,\n ModelOverride\n >;\n }\n return overrides as unknown as Record<string, ModelOverride>;\n}\n"],"mappings":";;;;;AAOA,SAAgB,oBAA4B;CAC1C,MAAM,OAAO,eAAe;CAC5B,IAAI,CAAC,MACH,MAAM,IAAI,MACR,6EACF;CAEF,OAAO;AACT;AAEA,SAAgB,cAAc,MAAsB;CAClD,OAAO,aAAa,MAAM,OAAO;AACnC;AAEA,SAAgB,eAAe,MAAc,SAAuB;CAClE,cAAc,MAAM,SAAS,OAAO;AACtC;AAEA,SAAgB,iBACd,YACA,UACA,UACM;CAEN,MAAM,OAAA,GAAA,YAAA,eADM,cAAc,UACE,CAAC;CAE7B,IAAI,YAAY,IAAI,IAAI,gBAAgB;CACxC,IAAI,CAAC,WAAW;EACd,YAAY,IAAI,WAAW,CAAC,CAAC;EAC7B,IAAI,IAAI,kBAAkB,SAAS;CACrC;CAEA,UAAU,IAAI,UAAU,QAAQ;CAEhC,eAAe,YAAY,IAAI,SAAS,CAAC;CACzC,OAAO,QAAQ,uBAAuB,SAAS,EAAE;AACnD;AAEA,SAAgB,oBAAoB,YAAoB,UAAwB;CAE9E,MAAM,OAAA,GAAA,YAAA,eADM,cAAc,UACE,CAAC;CAE7B,MAAM,YAAY,IAAI,IAAI,gBAAgB;CAC1C,IAAI,CAAC,WAAW,IAAI,QAAQ,GAC1B,MAAM,IAAI,MAAM,0BAA0B,SAAS,EAAE;CAGvD,UAAU,OAAO,QAAQ;CACzB,IAAI,UAAU,MAAM,WAAW,GAC7B,IAAI,OAAO,gBAAgB;CAG7B,eAAe,YAAY,IAAI,SAAS,CAAC;CACzC,OAAO,QAAQ,yBAAyB,SAAS,EAAE;AACrD;AAEA,SAAgB,kBAAkB,YAAmD;CAGnF,MAAM,aAAA,GAAA,YAAA,eAFM,cAAc,UACE,CACR,EAAE,IAAI,gBAAgB;CAC1C,IAAI,CAAC,WAAW,OAAO,CAAC;CACxB,IAAI,OAAO,cAAc,YAAY,cAAc,QAAQ,YAAY,WACrE,OAAQ,UAAwC,OAAO;CAKzD,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"config2.mjs","names":["clack.select","isCancel"],"sources":["../src/commands/config.ts"],"sourcesContent":["import * as clack from '@clack/prompts'\nimport { isCancel } from '@clack/prompts'\nimport type { OpenRouterDataClient } from '../openrouter/data-client.js'\nimport { addOverrideCommand } from './config/add.js'\nimport { browseModelsCommand } from './config/browse.js'\nimport { editOverrideCommand } from './config/edit.js'\nimport { listOverridesCommand } from './config/list.js'\nimport { removeOverrideCommand } from './config/remove.js'\nimport { validateConfigCommand } from './config/validate.js'\n\n/** Run the interactive config manager menu. */\nexport async function runConfigMenu(client: OpenRouterDataClient): Promise<void> {\n clack.intro('Proxitor Config Manager')\n\n const action = await clack.select({\n message: 'What would you like to do?',\n options: [\n { value: 'add', label: '➕ Add model override' },\n { value: 'edit', label: '✏️ Edit model override' },\n { value: 'remove', label: '🗑 Remove model override' },\n { value: 'list', label: '📋 List current overrides' },\n { value: 'browse', label: '🔍 Browse models' },\n { value: 'validate', label: '✅ Validate config' },\n { value: 'exit', label: '❌ Exit' },\n ],\n })\n\n if (isCancel(action) || action === 'exit') {\n clack.outro('Bye!')\n return\n }\n\n switch (action) {\n case 'add':\n await addOverrideCommand(client)\n break\n case 'edit':\n await editOverrideCommand(client)\n break\n case 'remove':\n await removeOverrideCommand()\n break\n case 'list':\n await listOverridesCommand()\n break\n case 'browse':\n await browseModelsCommand(client)\n break\n case 'validate':\n await validateConfigCommand()\n break\n }\n}\n"],"mappings":";;;;;;;;;AAWA,eAAsB,cAAc,QAA6C;CAC/E,GAAY,yBAAyB;CAErC,MAAM,SAAS,MAAMA,GAAa;EAChC,SAAS;EACT,SAAS;GACP;IAAE,OAAO;IAAO,OAAO;GAAwB;GAC/C;IAAE,OAAO;IAAQ,OAAO;GAA2B;GACnD;IAAE,OAAO;IAAU,OAAO;GAA6B;GACvD;IAAE,OAAO;IAAQ,OAAO;GAA6B;GACrD;IAAE,OAAO;IAAU,OAAO;GAAoB;GAC9C;IAAE,OAAO;IAAY,OAAO;GAAqB;GACjD;IAAE,OAAO;IAAQ,OAAO;GAAU;EACpC;CACF,CAAC;CAED,IAAIC,EAAS,MAAM,KAAK,WAAW,QAAQ;EACzC,GAAY,MAAM;EAClB;CACF;CAEA,QAAQ,QAAR;EACE,KAAK;GACH,MAAM,mBAAmB,MAAM;GAC/B;EACF,KAAK;GACH,MAAM,oBAAoB,MAAM;GAChC;EACF,KAAK;GACH,MAAM,sBAAsB;GAC5B;EACF,KAAK;GACH,MAAM,qBAAqB;GAC3B;EACF,KAAK;GACH,MAAM,oBAAoB,MAAM;GAChC;EACF,KAAK;GACH,MAAM,sBAAsB;GAC5B;CACJ;AACF"}
1
+ {"version":3,"file":"config2.mjs","names":["clack.select","isCancel"],"sources":["../src/commands/config.ts"],"sourcesContent":["import * as clack from '@clack/prompts';\nimport { isCancel } from '@clack/prompts';\nimport type { OpenRouterDataClient } from '../openrouter/data-client.js';\nimport { addOverrideCommand } from './config/add.js';\nimport { browseModelsCommand } from './config/browse.js';\nimport { editOverrideCommand } from './config/edit.js';\nimport { listOverridesCommand } from './config/list.js';\nimport { removeOverrideCommand } from './config/remove.js';\nimport { validateConfigCommand } from './config/validate.js';\n\n/** Run the interactive config manager menu. */\nexport async function runConfigMenu(client: OpenRouterDataClient): Promise<void> {\n clack.intro('Proxitor Config Manager');\n\n const action = await clack.select({\n message: 'What would you like to do?',\n options: [\n { value: 'add', label: '➕ Add model override' },\n { value: 'edit', label: '✏️ Edit model override' },\n { value: 'remove', label: '🗑 Remove model override' },\n { value: 'list', label: '📋 List current overrides' },\n { value: 'browse', label: '🔍 Browse models' },\n { value: 'validate', label: '✅ Validate config' },\n { value: 'exit', label: '❌ Exit' },\n ],\n });\n\n if (isCancel(action) || action === 'exit') {\n clack.outro('Bye!');\n return;\n }\n\n switch (action) {\n case 'add':\n await addOverrideCommand(client);\n break;\n case 'edit':\n await editOverrideCommand(client);\n break;\n case 'remove':\n await removeOverrideCommand();\n break;\n case 'list':\n await listOverridesCommand();\n break;\n case 'browse':\n await browseModelsCommand(client);\n break;\n case 'validate':\n await validateConfigCommand();\n break;\n }\n}\n"],"mappings":";;;;;;;;;AAWA,eAAsB,cAAc,QAA6C;CAC/E,GAAY,yBAAyB;CAErC,MAAM,SAAS,MAAMA,GAAa;EAChC,SAAS;EACT,SAAS;GACP;IAAE,OAAO;IAAO,OAAO;GAAwB;GAC/C;IAAE,OAAO;IAAQ,OAAO;GAA2B;GACnD;IAAE,OAAO;IAAU,OAAO;GAA6B;GACvD;IAAE,OAAO;IAAQ,OAAO;GAA6B;GACrD;IAAE,OAAO;IAAU,OAAO;GAAoB;GAC9C;IAAE,OAAO;IAAY,OAAO;GAAqB;GACjD;IAAE,OAAO;IAAQ,OAAO;GAAU;EACpC;CACF,CAAC;CAED,IAAIC,EAAS,MAAM,KAAK,WAAW,QAAQ;EACzC,GAAY,MAAM;EAClB;CACF;CAEA,QAAQ,QAAR;EACE,KAAK;GACH,MAAM,mBAAmB,MAAM;GAC/B;EACF,KAAK;GACH,MAAM,oBAAoB,MAAM;GAChC;EACF,KAAK;GACH,MAAM,sBAAsB;GAC5B;EACF,KAAK;GACH,MAAM,qBAAqB;GAC3B;EACF,KAAK;GACH,MAAM,oBAAoB,MAAM;GAChC;EACF,KAAK;GACH,MAAM,sBAAsB;GAC5B;CACJ;AACF"}
package/dist/edit.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"edit.mjs","names":["isCancel","clack.confirm","clack.select"],"sources":["../src/commands/config/edit.ts"],"sourcesContent":["import * as clack from '@clack/prompts'\nimport { isCancel } from '@clack/prompts'\nimport type { ModelOverride } from '../../config-schema.js'\nimport type { OpenRouterDataClient } from '../../openrouter/data-client.js'\nimport { getModelOverrides, requireConfigPath, setModelOverride } from './config.js'\nimport {\n fetchProvidersForModel,\n selectProvidersByMode,\n selectRoutingMode,\n} from './providers.js'\n\nfunction formatOverrideHint(override: ModelOverride | undefined): string {\n if (!override?.provider) return '(no provider routing)'\n const keys = Object.keys(override.provider)\n return keys\n .map(\n k => `${k}: ${JSON.stringify((override.provider as Record<string, unknown>)?.[k])}`,\n )\n .join(', ')\n}\n\nfunction showCurrentConfig(modelKey: string, current: ModelOverride): void {\n clack.log.info(`Current config for \"${modelKey}\":`)\n if (current.provider) {\n for (const [field, value] of Object.entries(current.provider)) {\n clack.log.info(` provider.${field}: ${JSON.stringify(value)}`)\n }\n }\n if (current.headers) {\n for (const [name, value] of Object.entries(current.headers)) {\n clack.log.info(` headers.${name}: ${value}`)\n }\n }\n}\n\nfunction withoutProvider(current: ModelOverride): ModelOverride {\n return current.headers ? { headers: current.headers } : {}\n}\n\nasync function updateProviderRouting(\n configPath: string,\n modelKey: string,\n current: ModelOverride,\n client: OpenRouterDataClient,\n): Promise<void> {\n const isPattern = modelKey.includes('*')\n\n const mode = await selectRoutingMode('Routing mode')\n if (isCancel(mode)) return\n\n if (mode === 'skip') {\n setModelOverride(configPath, modelKey, withoutProvider(current))\n clack.outro('✓ Override updated')\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 === null) return\n\n const updated = withoutProvider(current)\n if (override.provider) {\n updated.provider = override.provider as ModelOverride['provider']\n }\n\n const save = await clack.confirm({ message: 'Save changes?' })\n if (isCancel(save) || !save) {\n clack.outro('Cancelled')\n return\n }\n\n setModelOverride(configPath, modelKey, updated)\n clack.outro('✓ Override updated')\n}\n\n/** Run the interactive \"Edit model override\" flow. */\nexport async function editOverrideCommand(client: OpenRouterDataClient): Promise<void> {\n clack.intro('Edit Model Override')\n\n const configPath = requireConfigPath()\n const overrides = getModelOverrides(configPath)\n const keys = Object.keys(overrides)\n\n if (keys.length === 0) {\n clack.log.warn('No model overrides found. Use Add instead.')\n clack.outro('')\n return\n }\n\n const selected = await clack.select({\n message: 'Select override to edit',\n options: keys.map(k => ({\n value: k,\n label: k,\n hint: formatOverrideHint(overrides[k]),\n })),\n })\n if (isCancel(selected)) return\n\n const modelKey = selected as string\n const current: ModelOverride = overrides[modelKey] ?? {}\n\n showCurrentConfig(modelKey, current)\n\n const target = await clack.select({\n message: 'What to change?',\n options: [\n { value: 'provider', label: 'Provider routing' },\n { value: 'replace', label: 'Replace entirely' },\n ],\n })\n if (isCancel(target)) return\n\n if (target === 'provider' || target === 'replace') {\n await updateProviderRouting(configPath, modelKey, current, client)\n }\n}\n"],"mappings":";;;;AAWA,SAAS,mBAAmB,UAA6C;CACvE,IAAI,CAAC,UAAU,UAAU,OAAO;CAEhC,OADa,OAAO,KAAK,SAAS,QACxB,EACP,KACC,MAAK,GAAG,EAAE,IAAI,KAAK,UAAW,SAAS,WAAuC,EAAE,GAClF,EACC,KAAK,IAAI;AACd;AAEA,SAAS,kBAAkB,UAAkB,SAA8B;CACzE,EAAU,KAAK,uBAAuB,SAAS,GAAG;CAClD,IAAI,QAAQ,UACV,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,QAAQ,QAAQ,GAC1D,EAAU,KAAK,cAAc,MAAM,IAAI,KAAK,UAAU,KAAK,GAAG;CAGlE,IAAI,QAAQ,SACV,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,OAAO,GACxD,EAAU,KAAK,aAAa,KAAK,IAAI,OAAO;AAGlD;AAEA,SAAS,gBAAgB,SAAuC;CAC9D,OAAO,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAC3D;AAEA,eAAe,sBACb,YACA,UACA,SACA,QACe;CACf,MAAM,YAAY,SAAS,SAAS,GAAG;CAEvC,MAAM,OAAO,MAAM,kBAAkB,cAAc;CACnD,IAAIA,IAAS,IAAI,GAAG;CAEpB,IAAI,SAAS,QAAQ;EACnB,iBAAiB,YAAY,UAAU,gBAAgB,OAAO,CAAC;EAC/D,GAAY,oBAAoB;EAChC;CACF;CAEA,MAAM,kBAAkB,MAAM,uBAAuB,QAAQ,UAAU,SAAS;CAChF,IAAI,CAAC,iBAAiB;CAEtB,MAAM,WAAW,MAAM,sBAAsB,MAAgB,eAAe;CAC5E,IAAI,aAAa,MAAM;CAEvB,MAAM,UAAU,gBAAgB,OAAO;CACvC,IAAI,SAAS,UACX,QAAQ,WAAW,SAAS;CAG9B,MAAM,OAAO,MAAMC,GAAc,EAAE,SAAS,gBAAgB,CAAC;CAC7D,IAAID,IAAS,IAAI,KAAK,CAAC,MAAM;EAC3B,GAAY,WAAW;EACvB;CACF;CAEA,iBAAiB,YAAY,UAAU,OAAO;CAC9C,GAAY,oBAAoB;AAClC;;AAGA,eAAsB,oBAAoB,QAA6C;CACrF,GAAY,qBAAqB;CAEjC,MAAM,aAAa,kBAAkB;CACrC,MAAM,YAAY,kBAAkB,UAAU;CAC9C,MAAM,OAAO,OAAO,KAAK,SAAS;CAElC,IAAI,KAAK,WAAW,GAAG;EACrB,EAAU,KAAK,4CAA4C;EAC3D,GAAY,EAAE;EACd;CACF;CAEA,MAAM,WAAW,MAAME,GAAa;EAClC,SAAS;EACT,SAAS,KAAK,KAAI,OAAM;GACtB,OAAO;GACP,OAAO;GACP,MAAM,mBAAmB,UAAU,EAAE;EACvC,EAAE;CACJ,CAAC;CACD,IAAIF,IAAS,QAAQ,GAAG;CAExB,MAAM,WAAW;CACjB,MAAM,UAAyB,UAAU,aAAa,CAAC;CAEvD,kBAAkB,UAAU,OAAO;CAEnC,MAAM,SAAS,MAAME,GAAa;EAChC,SAAS;EACT,SAAS,CACP;GAAE,OAAO;GAAY,OAAO;EAAmB,GAC/C;GAAE,OAAO;GAAW,OAAO;EAAmB,CAChD;CACF,CAAC;CACD,IAAIF,IAAS,MAAM,GAAG;CAEtB,IAAI,WAAW,cAAc,WAAW,WACtC,MAAM,sBAAsB,YAAY,UAAU,SAAS,MAAM;AAErE"}
1
+ {"version":3,"file":"edit.mjs","names":["isCancel","clack.confirm","clack.select"],"sources":["../src/commands/config/edit.ts"],"sourcesContent":["import * as clack from '@clack/prompts';\nimport { isCancel } from '@clack/prompts';\nimport type { ModelOverride } from '../../config-schema.js';\nimport type { OpenRouterDataClient } from '../../openrouter/data-client.js';\nimport { getModelOverrides, requireConfigPath, setModelOverride } from './config.js';\nimport {\n fetchProvidersForModel,\n selectProvidersByMode,\n selectRoutingMode,\n} from './providers.js';\n\nfunction formatOverrideHint(override: ModelOverride | undefined): string {\n if (!override?.provider) return '(no provider routing)';\n const keys = Object.keys(override.provider);\n return keys\n .map(\n k => `${k}: ${JSON.stringify((override.provider as Record<string, unknown>)?.[k])}`,\n )\n .join(', ');\n}\n\nfunction showCurrentConfig(modelKey: string, current: ModelOverride): void {\n clack.log.info(`Current config for \"${modelKey}\":`);\n if (current.provider) {\n for (const [field, value] of Object.entries(current.provider)) {\n clack.log.info(` provider.${field}: ${JSON.stringify(value)}`);\n }\n }\n if (current.headers) {\n for (const [name, value] of Object.entries(current.headers)) {\n clack.log.info(` headers.${name}: ${value}`);\n }\n }\n}\n\nfunction withoutProvider(current: ModelOverride): ModelOverride {\n return current.headers ? { headers: current.headers } : {};\n}\n\nasync function updateProviderRouting(\n configPath: string,\n modelKey: string,\n current: ModelOverride,\n client: OpenRouterDataClient,\n): Promise<void> {\n const isPattern = modelKey.includes('*');\n\n const mode = await selectRoutingMode('Routing mode');\n if (isCancel(mode)) return;\n\n if (mode === 'skip') {\n setModelOverride(configPath, modelKey, withoutProvider(current));\n clack.outro('✓ Override updated');\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 === null) return;\n\n const updated = withoutProvider(current);\n if (override.provider) {\n updated.provider = override.provider as ModelOverride['provider'];\n }\n\n const save = await clack.confirm({ message: 'Save changes?' });\n if (isCancel(save) || !save) {\n clack.outro('Cancelled');\n return;\n }\n\n setModelOverride(configPath, modelKey, updated);\n clack.outro('✓ Override updated');\n}\n\n/** Run the interactive \"Edit model override\" flow. */\nexport async function editOverrideCommand(client: OpenRouterDataClient): Promise<void> {\n clack.intro('Edit Model Override');\n\n const configPath = requireConfigPath();\n const overrides = getModelOverrides(configPath);\n const keys = Object.keys(overrides);\n\n if (keys.length === 0) {\n clack.log.warn('No model overrides found. Use Add instead.');\n clack.outro('');\n return;\n }\n\n const selected = await clack.select({\n message: 'Select override to edit',\n options: keys.map(k => ({\n value: k,\n label: k,\n hint: formatOverrideHint(overrides[k]),\n })),\n });\n if (isCancel(selected)) return;\n\n const modelKey = selected as string;\n const current: ModelOverride = overrides[modelKey] ?? {};\n\n showCurrentConfig(modelKey, current);\n\n const target = await clack.select({\n message: 'What to change?',\n options: [\n { value: 'provider', label: 'Provider routing' },\n { value: 'replace', label: 'Replace entirely' },\n ],\n });\n if (isCancel(target)) return;\n\n if (target === 'provider' || target === 'replace') {\n await updateProviderRouting(configPath, modelKey, current, client);\n }\n}\n"],"mappings":";;;;AAWA,SAAS,mBAAmB,UAA6C;CACvE,IAAI,CAAC,UAAU,UAAU,OAAO;CAEhC,OADa,OAAO,KAAK,SAAS,QACxB,EACP,KACC,MAAK,GAAG,EAAE,IAAI,KAAK,UAAW,SAAS,WAAuC,EAAE,GAClF,EACC,KAAK,IAAI;AACd;AAEA,SAAS,kBAAkB,UAAkB,SAA8B;CACzE,EAAU,KAAK,uBAAuB,SAAS,GAAG;CAClD,IAAI,QAAQ,UACV,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,QAAQ,QAAQ,GAC1D,EAAU,KAAK,cAAc,MAAM,IAAI,KAAK,UAAU,KAAK,GAAG;CAGlE,IAAI,QAAQ,SACV,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,OAAO,GACxD,EAAU,KAAK,aAAa,KAAK,IAAI,OAAO;AAGlD;AAEA,SAAS,gBAAgB,SAAuC;CAC9D,OAAO,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAC3D;AAEA,eAAe,sBACb,YACA,UACA,SACA,QACe;CACf,MAAM,YAAY,SAAS,SAAS,GAAG;CAEvC,MAAM,OAAO,MAAM,kBAAkB,cAAc;CACnD,IAAIA,IAAS,IAAI,GAAG;CAEpB,IAAI,SAAS,QAAQ;EACnB,iBAAiB,YAAY,UAAU,gBAAgB,OAAO,CAAC;EAC/D,GAAY,oBAAoB;EAChC;CACF;CAEA,MAAM,kBAAkB,MAAM,uBAAuB,QAAQ,UAAU,SAAS;CAChF,IAAI,CAAC,iBAAiB;CAEtB,MAAM,WAAW,MAAM,sBAAsB,MAAgB,eAAe;CAC5E,IAAI,aAAa,MAAM;CAEvB,MAAM,UAAU,gBAAgB,OAAO;CACvC,IAAI,SAAS,UACX,QAAQ,WAAW,SAAS;CAG9B,MAAM,OAAO,MAAMC,GAAc,EAAE,SAAS,gBAAgB,CAAC;CAC7D,IAAID,IAAS,IAAI,KAAK,CAAC,MAAM;EAC3B,GAAY,WAAW;EACvB;CACF;CAEA,iBAAiB,YAAY,UAAU,OAAO;CAC9C,GAAY,oBAAoB;AAClC;;AAGA,eAAsB,oBAAoB,QAA6C;CACrF,GAAY,qBAAqB;CAEjC,MAAM,aAAa,kBAAkB;CACrC,MAAM,YAAY,kBAAkB,UAAU;CAC9C,MAAM,OAAO,OAAO,KAAK,SAAS;CAElC,IAAI,KAAK,WAAW,GAAG;EACrB,EAAU,KAAK,4CAA4C;EAC3D,GAAY,EAAE;EACd;CACF;CAEA,MAAM,WAAW,MAAME,GAAa;EAClC,SAAS;EACT,SAAS,KAAK,KAAI,OAAM;GACtB,OAAO;GACP,OAAO;GACP,MAAM,mBAAmB,UAAU,EAAE;EACvC,EAAE;CACJ,CAAC;CACD,IAAIF,IAAS,QAAQ,GAAG;CAExB,MAAM,WAAW;CACjB,MAAM,UAAyB,UAAU,aAAa,CAAC;CAEvD,kBAAkB,UAAU,OAAO;CAEnC,MAAM,SAAS,MAAME,GAAa;EAChC,SAAS;EACT,SAAS,CACP;GAAE,OAAO;GAAY,OAAO;EAAmB,GAC/C;GAAE,OAAO;GAAW,OAAO;EAAmB,CAChD;CACF,CAAC;CACD,IAAIF,IAAS,MAAM,GAAG;CAEtB,IAAI,WAAW,cAAc,WAAW,WACtC,MAAM,sBAAsB,YAAY,UAAU,SAAS,MAAM;AAErE"}
package/dist/list.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"list.mjs","names":[],"sources":["../src/commands/config/list.ts"],"sourcesContent":["import * as clack from '@clack/prompts'\nimport type { ModelOverride } from '../../config-schema.js'\nimport { getModelOverrides, requireConfigPath } from './config.js'\n\nfunction formatOverrideSummary(override: ModelOverride): string {\n const parts: string[] = []\n\n if (override.provider) {\n for (const [field, value] of Object.entries(override.provider)) {\n if (value !== undefined) parts.push(`${field}: ${JSON.stringify(value)}`)\n }\n }\n\n if (override.headers) {\n for (const [name, value] of Object.entries(override.headers)) {\n parts.push(`header ${name}: ${value}`)\n }\n }\n\n return parts.join(', ') || '(empty)'\n}\n\n/** Display all current model overrides. */\nexport async function listOverridesCommand(): Promise<void> {\n const configPath = requireConfigPath()\n const overrides = getModelOverrides(configPath)\n const keys = Object.keys(overrides)\n\n if (keys.length === 0) {\n clack.log.info('No model overrides configured.')\n return\n }\n\n clack.log.success(`${keys.length} override(s) in ${configPath}`)\n\n for (const key of keys) {\n const override = overrides[key]\n if (!override) continue\n clack.log.info(` ${key} — ${formatOverrideSummary(override)}`)\n }\n}\n"],"mappings":";;;AAIA,SAAS,sBAAsB,UAAiC;CAC9D,MAAM,QAAkB,CAAC;CAEzB,IAAI,SAAS;OACN,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,SAAS,QAAQ,GAC3D,IAAI,UAAU,KAAA,GAAW,MAAM,KAAK,GAAG,MAAM,IAAI,KAAK,UAAU,KAAK,GAAG;CAAA;CAI5E,IAAI,SAAS,SACX,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,SAAS,OAAO,GACzD,MAAM,KAAK,UAAU,KAAK,IAAI,OAAO;CAIzC,OAAO,MAAM,KAAK,IAAI,KAAK;AAC7B;;AAGA,eAAsB,uBAAsC;CAC1D,MAAM,aAAa,kBAAkB;CACrC,MAAM,YAAY,kBAAkB,UAAU;CAC9C,MAAM,OAAO,OAAO,KAAK,SAAS;CAElC,IAAI,KAAK,WAAW,GAAG;EACrB,EAAU,KAAK,gCAAgC;EAC/C;CACF;CAEA,EAAU,QAAQ,GAAG,KAAK,OAAO,kBAAkB,YAAY;CAE/D,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,WAAW,UAAU;EAC3B,IAAI,CAAC,UAAU;EACf,EAAU,KAAK,KAAK,IAAI,KAAK,sBAAsB,QAAQ,GAAG;CAChE;AACF"}
1
+ {"version":3,"file":"list.mjs","names":[],"sources":["../src/commands/config/list.ts"],"sourcesContent":["import * as clack from '@clack/prompts';\nimport type { ModelOverride } from '../../config-schema.js';\nimport { getModelOverrides, requireConfigPath } from './config.js';\n\nfunction formatOverrideSummary(override: ModelOverride): string {\n const parts: string[] = [];\n\n if (override.provider) {\n for (const [field, value] of Object.entries(override.provider)) {\n if (value !== undefined) parts.push(`${field}: ${JSON.stringify(value)}`);\n }\n }\n\n if (override.headers) {\n for (const [name, value] of Object.entries(override.headers)) {\n parts.push(`header ${name}: ${value}`);\n }\n }\n\n return parts.join(', ') || '(empty)';\n}\n\n/** Display all current model overrides. */\nexport async function listOverridesCommand(): Promise<void> {\n const configPath = requireConfigPath();\n const overrides = getModelOverrides(configPath);\n const keys = Object.keys(overrides);\n\n if (keys.length === 0) {\n clack.log.info('No model overrides configured.');\n return;\n }\n\n clack.log.success(`${keys.length} override(s) in ${configPath}`);\n\n for (const key of keys) {\n const override = overrides[key];\n if (!override) continue;\n clack.log.info(` ${key} — ${formatOverrideSummary(override)}`);\n }\n}\n"],"mappings":";;;AAIA,SAAS,sBAAsB,UAAiC;CAC9D,MAAM,QAAkB,CAAC;CAEzB,IAAI,SAAS;OACN,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,SAAS,QAAQ,GAC3D,IAAI,UAAU,KAAA,GAAW,MAAM,KAAK,GAAG,MAAM,IAAI,KAAK,UAAU,KAAK,GAAG;CAAA;CAI5E,IAAI,SAAS,SACX,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,SAAS,OAAO,GACzD,MAAM,KAAK,UAAU,KAAK,IAAI,OAAO;CAIzC,OAAO,MAAM,KAAK,IAAI,KAAK;AAC7B;;AAGA,eAAsB,uBAAsC;CAC1D,MAAM,aAAa,kBAAkB;CACrC,MAAM,YAAY,kBAAkB,UAAU;CAC9C,MAAM,OAAO,OAAO,KAAK,SAAS;CAElC,IAAI,KAAK,WAAW,GAAG;EACrB,EAAU,KAAK,gCAAgC;EAC/C;CACF;CAEA,EAAU,QAAQ,GAAG,KAAK,OAAO,kBAAkB,YAAY;CAE/D,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,WAAW,UAAU;EAC3B,IAAI,CAAC,UAAU;EACf,EAAU,KAAK,KAAK,IAAI,KAAK,sBAAsB,QAAQ,GAAG;CAChE;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"providers.mjs","names":["CACHE_KEY","CACHE_TTL","clack.spinner","clack.select","clack.multiselect","clack.isCancel","clack.confirm"],"sources":["../src/openrouter/cache.ts","../src/openrouter/models.ts","../src/commands/config/format.ts","../src/openrouter/endpoints.ts","../src/openrouter/providers.ts","../src/commands/config/providers.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nexport type CacheEntry<T> = {\n data: T\n fetchedAt: number\n}\n\nexport const CACHE_DIR = join(homedir(), '.cache', 'proxitor')\n\n/** Read a cached value. Returns `null` when missing, expired (older than `ttlMs`), or unparseable. */\nexport function readCache<T>(key: string, ttlMs: number): T | null {\n const path = join(CACHE_DIR, `${key}.json`)\n if (!existsSync(path)) return null\n\n try {\n const entry: CacheEntry<T> = JSON.parse(readFileSync(path, 'utf-8'))\n if (Date.now() - entry.fetchedAt > ttlMs) return null\n return entry.data\n } catch {\n return null\n }\n}\n\nexport function writeCache<T>(key: string, data: T): void {\n mkdirSync(CACHE_DIR, { recursive: true })\n const entry: CacheEntry<T> = { fetchedAt: Date.now(), data }\n writeFileSync(join(CACHE_DIR, `${key}.json`), JSON.stringify(entry))\n}\n\nexport function clearCache(key: string): void {\n const path = join(CACHE_DIR, `${key}.json`)\n if (existsSync(path)) unlinkSync(path)\n}\n","import { readCache, writeCache } from './cache.js'\nimport type { OpenRouterDataClient } from './data-client.js'\nimport type { OpenRouterModel } from './types.js'\n\nconst CACHE_KEY = 'models'\nconst CACHE_TTL = 60 * 60 * 1000 // 1 hour\n\nexport async function fetchModels(\n client: OpenRouterDataClient,\n): Promise<OpenRouterModel[]> {\n const cached = readCache<OpenRouterModel[]>(CACHE_KEY, CACHE_TTL)\n if (cached) return cached\n\n const models = await client.fetchModels()\n writeCache(CACHE_KEY, models)\n return models\n}\n\n/** `\"anthropic/claude-sonnet-4\"` → `\"anthropic\"` */\nexport function parseModelAuthor(modelId: string): string {\n return modelId.split('/')[0] ?? ''\n}\n\n/** `\"anthropic/claude-sonnet-4\"` → `\"claude-sonnet-4\"` */\nexport function parseModelSlug(modelId: string): string {\n return modelId.split('/').slice(1).join('/')\n}\n\n/** `\"0.000003\"` → `\"$3.00\"`, `\"0\"` → `\"free\"` */\nexport function formatPrice(pricePerToken: string): string {\n const per1M = Number.parseFloat(pricePerToken) * 1_000_000\n if (per1M === 0) return 'free'\n if (per1M < 0.01) return `$${per1M.toFixed(4)}`\n return `$${per1M.toFixed(2)}`\n}\n","import { formatPrice } from '../../openrouter/models.js'\nimport type { OpenRouterModel } from '../../openrouter/types.js'\n\nexport function formatPricing(prompt: string, completion: string): string {\n const fmt = (perToken: string) => {\n const per1M = Number.parseFloat(perToken) * 1_000_000\n if (per1M === 0) return 'free'\n if (per1M < 0.01) return `$${per1M.toFixed(4)}`\n return `$${per1M.toFixed(2)}`\n }\n return `${fmt(prompt)} / ${fmt(completion)}`\n}\n\n/** `200000` → `\"200k\"`, `1000000` → `\"1.0M\"` */\nexport function formatContextLength(tokens: number): string {\n if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M`\n if (tokens >= 1_000) return `${Math.round(tokens / 1_000)}k`\n return `${tokens}`\n}\n\n/** `1137` → `\"1.1s\"`, `null` → `\"N/A\"` */\nexport function formatLatency(ms: number | null): string {\n if (ms === null) return 'N/A'\n if (ms < 1000) return `${Math.round(ms)}ms`\n return `${(ms / 1000).toFixed(1)}s`\n}\n\nexport function formatThroughput(tokensPerSec: number | null): string {\n if (tokensPerSec === null) return 'N/A'\n return `${tokensPerSec.toFixed(0)} t/s`\n}\n\nexport function formatModelLabel(m: OpenRouterModel): string {\n return `${m.name || m.id} — ${formatPrice(m.pricing.prompt)} · ${formatContextLength(m.context_length)}`\n}\n\nexport function formatModelHint(m: OpenRouterModel): string {\n const parts = [`out ${formatPrice(m.pricing.completion)}`]\n if (m.pricing.input_cache_read && m.pricing.input_cache_read !== '0') {\n parts.push(`cache ${formatPrice(m.pricing.input_cache_read)}`)\n }\n return parts.join(' · ')\n}\n","import type { OpenRouterDataClient } from './data-client.js'\nimport type { ModelEndpoint } from './types.js'\n\nexport async function fetchModelEndpoints(\n client: OpenRouterDataClient,\n author: string,\n slug: string,\n): Promise<ModelEndpoint[]> {\n return client.fetchModelEndpoints(author, slug)\n}\n\nexport type ProviderOption = {\n providerName: string\n /** Routing slug for `provider.only/order/ignore` (e.g. \"anthropic\", \"google-vertex/global\"). */\n tag: string\n}\n\nexport function getUniqueProviders(endpoints: ModelEndpoint[]): ProviderOption[] {\n const seen = new Set<string>()\n const result: ProviderOption[] = []\n\n for (const ep of endpoints) {\n if (seen.has(ep.tag)) continue\n seen.add(ep.tag)\n result.push({ tag: ep.tag, providerName: ep.provider_name })\n }\n\n result.sort((a, b) => a.providerName.localeCompare(b.providerName))\n return result\n}\n","import { readCache, writeCache } from './cache.js'\nimport type { OpenRouterDataClient } from './data-client.js'\nimport type { OpenRouterProvider } from './types.js'\n\nconst CACHE_KEY = 'providers'\nconst CACHE_TTL = 24 * 60 * 60 * 1000 // 24 hours\n\nexport async function fetchProviders(\n client: OpenRouterDataClient,\n): Promise<OpenRouterProvider[]> {\n const cached = readCache<OpenRouterProvider[]>(CACHE_KEY, CACHE_TTL)\n if (cached) return cached\n\n const providers = await client.fetchProviders()\n writeCache(CACHE_KEY, providers)\n return providers\n}\n","import * as clack from '@clack/prompts'\nimport type { OpenRouterDataClient } from '../../openrouter/data-client.js'\nimport { fetchModelEndpoints, getUniqueProviders } from '../../openrouter/endpoints.js'\nimport { parseModelAuthor, parseModelSlug } from '../../openrouter/models.js'\nimport { fetchProviders } from '../../openrouter/providers.js'\nimport { formatLatency, formatThroughput } from './format.js'\n\nexport async function fetchProvidersForPattern(\n client: OpenRouterDataClient,\n): Promise<Array<{ value: string; label: string; hint?: string }> | null> {\n const s = clack.spinner()\n s.start('Fetching providers...')\n try {\n const providers = await fetchProviders(client)\n const options = providers\n .map(p => ({ value: p.slug, label: p.name }))\n .sort((a, b) => a.label.localeCompare(b.label))\n s.stop(`${providers.length} providers available`)\n return options\n } catch (error) {\n s.stop('Failed to fetch providers')\n clack.log.error(String(error))\n return null\n }\n}\n\nexport async function fetchEndpointsForModel(\n client: OpenRouterDataClient,\n modelId: string,\n): Promise<Array<{ value: string; label: string; hint?: string }> | null> {\n const author = parseModelAuthor(modelId)\n const slug = parseModelSlug(modelId)\n\n const s = clack.spinner()\n s.start('Fetching providers for this model...')\n try {\n const endpoints = await fetchModelEndpoints(client, author, slug)\n const unique = getUniqueProviders(endpoints)\n\n const options = unique.map(p => {\n const ep = endpoints.find(e => e.tag === p.tag)\n const latency = ep?.latency_last_30m?.p50 ?? null\n const throughput = ep?.throughput_last_30m?.p50 ?? null\n return {\n value: p.tag,\n label: `${p.providerName} (${p.tag})`,\n hint: `${formatLatency(latency)} · ${formatThroughput(throughput)}`,\n }\n })\n\n s.stop(`${unique.length} providers available for this model`)\n return options\n } catch (error) {\n s.stop('Failed to fetch providers')\n clack.log.error(String(error))\n return null\n }\n}\n\nexport async function fetchProvidersForModel(\n client: OpenRouterDataClient,\n modelKey: string,\n isPattern: boolean,\n): Promise<Array<{ value: string; label: string; hint?: string }> | null> {\n if (isPattern) return fetchProvidersForPattern(client)\n return fetchEndpointsForModel(client, modelKey)\n}\n\nconst DONE_OPTION = '__done__'\n\nexport async function selectRoutingMode(message: string): Promise<string | symbol> {\n return clack.select({\n message,\n options: [\n { value: 'only', label: 'Use specific providers only' },\n { value: 'order', label: 'Set provider priority order' },\n { value: 'ignore', label: 'Ignore specific providers' },\n { value: 'skip', label: 'Skip provider routing' },\n ],\n })\n}\n\nexport async function selectProvidersByMode(\n mode: string,\n providerOptions: Array<{ value: string; label: string; hint?: string }>,\n): Promise<Record<string, unknown> | null> {\n if (mode === 'only') return selectOnlyProviders(providerOptions)\n if (mode === 'order') return selectOrderedProviders(providerOptions)\n if (mode === 'ignore') return selectIgnoreProviders(providerOptions)\n return null\n}\n\nasync function selectOnlyProviders(\n providerOptions: Array<{ value: string; label: string; hint?: string }>,\n): Promise<Record<string, unknown> | null> {\n const selected = await clack.multiselect({\n message: 'Select providers',\n options: providerOptions,\n required: false,\n })\n\n if (clack.isCancel(selected)) return null\n\n const values = selected as string[]\n if (values.length === 0) return {}\n\n const only = values.length === 1 ? values[0] : values\n return { provider: { only } }\n}\n\nasync function selectOrderedProviders(\n providerOptions: Array<{ value: string; label: string; hint?: string }>,\n): Promise<Record<string, unknown> | null> {\n const order: string[] = []\n\n for (let i = 1; ; i++) {\n const remaining = providerOptions.filter(p => !order.includes(p.value))\n if (remaining.length === 0) break\n\n const pick = await clack.select({\n message: `Select provider #${i} (or cancel to finish)`,\n options: [...remaining, { value: DONE_OPTION, label: '✓ Done' }],\n })\n\n if (clack.isCancel(pick) || pick === DONE_OPTION) break\n order.push(pick as string)\n }\n\n if (order.length === 0) {\n clack.log.warn('No providers selected')\n return null\n }\n\n const allowFallbacks = await clack.confirm({\n message: 'Allow fallbacks to other providers?',\n initialValue: true,\n })\n\n return {\n provider: {\n order: order.length === 1 ? order[0] : order,\n allowFallbacks: clack.isCancel(allowFallbacks) ? true : (allowFallbacks as boolean),\n },\n }\n}\n\nasync function selectIgnoreProviders(\n providerOptions: Array<{ value: string; label: string; hint?: string }>,\n): Promise<Record<string, unknown> | null> {\n const selected = await clack.multiselect({\n message: 'Select providers to ignore',\n options: providerOptions,\n required: false,\n })\n\n if (clack.isCancel(selected)) return null\n\n const values = selected as string[]\n if (values.length === 0) return {}\n\n const ignore = values.length === 1 ? values[0] : values\n return { provider: { ignore } }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"providers.mjs","names":["CACHE_KEY","CACHE_TTL","clack.spinner","clack.select","clack.multiselect","clack.isCancel","clack.confirm"],"sources":["../src/openrouter/cache.ts","../src/openrouter/models.ts","../src/commands/config/format.ts","../src/openrouter/endpoints.ts","../src/openrouter/providers.ts","../src/commands/config/providers.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport type CacheEntry<T> = {\n data: T;\n fetchedAt: number;\n};\n\nexport const CACHE_DIR = join(homedir(), '.cache', 'proxitor');\n\n/** Read a cached value. Returns `null` when missing, expired (older than `ttlMs`), or unparseable. */\nexport function readCache<T>(key: string, ttlMs: number): T | null {\n const path = join(CACHE_DIR, `${key}.json`);\n if (!existsSync(path)) return null;\n\n try {\n const entry: CacheEntry<T> = JSON.parse(readFileSync(path, 'utf-8'));\n if (Date.now() - entry.fetchedAt > ttlMs) return null;\n return entry.data;\n } catch {\n return null;\n }\n}\n\nexport function writeCache<T>(key: string, data: T): void {\n mkdirSync(CACHE_DIR, { recursive: true });\n const entry: CacheEntry<T> = { fetchedAt: Date.now(), data };\n writeFileSync(join(CACHE_DIR, `${key}.json`), JSON.stringify(entry));\n}\n\nexport function clearCache(key: string): void {\n const path = join(CACHE_DIR, `${key}.json`);\n if (existsSync(path)) unlinkSync(path);\n}\n","import { readCache, writeCache } from './cache.js';\nimport type { OpenRouterDataClient } from './data-client.js';\nimport type { OpenRouterModel } from './types.js';\n\nconst CACHE_KEY = 'models';\nconst CACHE_TTL = 60 * 60 * 1000; // 1 hour\n\nexport async function fetchModels(\n client: OpenRouterDataClient,\n): Promise<OpenRouterModel[]> {\n const cached = readCache<OpenRouterModel[]>(CACHE_KEY, CACHE_TTL);\n if (cached) return cached;\n\n const models = await client.fetchModels();\n writeCache(CACHE_KEY, models);\n return models;\n}\n\n/** `\"anthropic/claude-sonnet-4\"` → `\"anthropic\"` */\nexport function parseModelAuthor(modelId: string): string {\n return modelId.split('/')[0] ?? '';\n}\n\n/** `\"anthropic/claude-sonnet-4\"` → `\"claude-sonnet-4\"` */\nexport function parseModelSlug(modelId: string): string {\n return modelId.split('/').slice(1).join('/');\n}\n\n/** `\"0.000003\"` → `\"$3.00\"`, `\"0\"` → `\"free\"` */\nexport function formatPrice(pricePerToken: string): string {\n const per1M = Number.parseFloat(pricePerToken) * 1_000_000;\n if (per1M === 0) return 'free';\n if (per1M < 0.01) return `$${per1M.toFixed(4)}`;\n return `$${per1M.toFixed(2)}`;\n}\n","import { formatPrice } from '../../openrouter/models.js';\nimport type { OpenRouterModel } from '../../openrouter/types.js';\n\nexport function formatPricing(prompt: string, completion: string): string {\n const fmt = (perToken: string) => {\n const per1M = Number.parseFloat(perToken) * 1_000_000;\n if (per1M === 0) return 'free';\n if (per1M < 0.01) return `$${per1M.toFixed(4)}`;\n return `$${per1M.toFixed(2)}`;\n };\n return `${fmt(prompt)} / ${fmt(completion)}`;\n}\n\n/** `200000` → `\"200k\"`, `1000000` → `\"1.0M\"` */\nexport function formatContextLength(tokens: number): string {\n if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M`;\n if (tokens >= 1_000) return `${Math.round(tokens / 1_000)}k`;\n return `${tokens}`;\n}\n\n/** `1137` → `\"1.1s\"`, `null` → `\"N/A\"` */\nexport function formatLatency(ms: number | null): string {\n if (ms === null) return 'N/A';\n if (ms < 1000) return `${Math.round(ms)}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nexport function formatThroughput(tokensPerSec: number | null): string {\n if (tokensPerSec === null) return 'N/A';\n return `${tokensPerSec.toFixed(0)} t/s`;\n}\n\nexport function formatModelLabel(m: OpenRouterModel): string {\n return `${m.name || m.id} — ${formatPrice(m.pricing.prompt)} · ${formatContextLength(m.context_length)}`;\n}\n\nexport function formatModelHint(m: OpenRouterModel): string {\n const parts = [`out ${formatPrice(m.pricing.completion)}`];\n if (m.pricing.input_cache_read && m.pricing.input_cache_read !== '0') {\n parts.push(`cache ${formatPrice(m.pricing.input_cache_read)}`);\n }\n return parts.join(' · ');\n}\n","import type { OpenRouterDataClient } from './data-client.js';\nimport type { ModelEndpoint } from './types.js';\n\nexport async function fetchModelEndpoints(\n client: OpenRouterDataClient,\n author: string,\n slug: string,\n): Promise<ModelEndpoint[]> {\n return client.fetchModelEndpoints(author, slug);\n}\n\nexport type ProviderOption = {\n providerName: string;\n /** Routing slug for `provider.only/order/ignore` (e.g. \"anthropic\", \"google-vertex/global\"). */\n tag: string;\n};\n\nexport function getUniqueProviders(endpoints: ModelEndpoint[]): ProviderOption[] {\n const seen = new Set<string>();\n const result: ProviderOption[] = [];\n\n for (const ep of endpoints) {\n if (seen.has(ep.tag)) continue;\n seen.add(ep.tag);\n result.push({ tag: ep.tag, providerName: ep.provider_name });\n }\n\n result.sort((a, b) => a.providerName.localeCompare(b.providerName));\n return result;\n}\n","import { readCache, writeCache } from './cache.js';\nimport type { OpenRouterDataClient } from './data-client.js';\nimport type { OpenRouterProvider } from './types.js';\n\nconst CACHE_KEY = 'providers';\nconst CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours\n\nexport async function fetchProviders(\n client: OpenRouterDataClient,\n): Promise<OpenRouterProvider[]> {\n const cached = readCache<OpenRouterProvider[]>(CACHE_KEY, CACHE_TTL);\n if (cached) return cached;\n\n const providers = await client.fetchProviders();\n writeCache(CACHE_KEY, providers);\n return providers;\n}\n","import * as clack from '@clack/prompts';\nimport type { OpenRouterDataClient } from '../../openrouter/data-client.js';\nimport { fetchModelEndpoints, getUniqueProviders } from '../../openrouter/endpoints.js';\nimport { parseModelAuthor, parseModelSlug } from '../../openrouter/models.js';\nimport { fetchProviders } from '../../openrouter/providers.js';\nimport { formatLatency, formatThroughput } from './format.js';\n\nexport async function fetchProvidersForPattern(\n client: OpenRouterDataClient,\n): Promise<Array<{ value: string; label: string; hint?: string }> | null> {\n const s = clack.spinner();\n s.start('Fetching providers...');\n try {\n const providers = await fetchProviders(client);\n const options = providers\n .map(p => ({ value: p.slug, label: p.name }))\n .sort((a, b) => a.label.localeCompare(b.label));\n s.stop(`${providers.length} providers available`);\n return options;\n } catch (error) {\n s.stop('Failed to fetch providers');\n clack.log.error(String(error));\n return null;\n }\n}\n\nexport async function fetchEndpointsForModel(\n client: OpenRouterDataClient,\n modelId: string,\n): Promise<Array<{ value: string; label: string; hint?: string }> | null> {\n const author = parseModelAuthor(modelId);\n const slug = parseModelSlug(modelId);\n\n const s = clack.spinner();\n s.start('Fetching providers for this model...');\n try {\n const endpoints = await fetchModelEndpoints(client, author, slug);\n const unique = getUniqueProviders(endpoints);\n\n const options = unique.map(p => {\n const ep = endpoints.find(e => e.tag === p.tag);\n const latency = ep?.latency_last_30m?.p50 ?? null;\n const throughput = ep?.throughput_last_30m?.p50 ?? null;\n return {\n value: p.tag,\n label: `${p.providerName} (${p.tag})`,\n hint: `${formatLatency(latency)} · ${formatThroughput(throughput)}`,\n };\n });\n\n s.stop(`${unique.length} providers available for this model`);\n return options;\n } catch (error) {\n s.stop('Failed to fetch providers');\n clack.log.error(String(error));\n return null;\n }\n}\n\nexport async function fetchProvidersForModel(\n client: OpenRouterDataClient,\n modelKey: string,\n isPattern: boolean,\n): Promise<Array<{ value: string; label: string; hint?: string }> | null> {\n if (isPattern) return fetchProvidersForPattern(client);\n return fetchEndpointsForModel(client, modelKey);\n}\n\nconst DONE_OPTION = '__done__';\n\nexport async function selectRoutingMode(message: string): Promise<string | symbol> {\n return clack.select({\n message,\n options: [\n { value: 'only', label: 'Use specific providers only' },\n { value: 'order', label: 'Set provider priority order' },\n { value: 'ignore', label: 'Ignore specific providers' },\n { value: 'skip', label: 'Skip provider routing' },\n ],\n });\n}\n\nexport async function selectProvidersByMode(\n mode: string,\n providerOptions: Array<{ value: string; label: string; hint?: string }>,\n): Promise<Record<string, unknown> | null> {\n if (mode === 'only') return selectOnlyProviders(providerOptions);\n if (mode === 'order') return selectOrderedProviders(providerOptions);\n if (mode === 'ignore') return selectIgnoreProviders(providerOptions);\n return null;\n}\n\nasync function selectOnlyProviders(\n providerOptions: Array<{ value: string; label: string; hint?: string }>,\n): Promise<Record<string, unknown> | null> {\n const selected = await clack.multiselect({\n message: 'Select providers',\n options: providerOptions,\n required: false,\n });\n\n if (clack.isCancel(selected)) return null;\n\n const values = selected as string[];\n if (values.length === 0) return {};\n\n const only = values.length === 1 ? values[0] : values;\n return { provider: { only } };\n}\n\nasync function selectOrderedProviders(\n providerOptions: Array<{ value: string; label: string; hint?: string }>,\n): Promise<Record<string, unknown> | null> {\n const order: string[] = [];\n\n for (let i = 1; ; i++) {\n const remaining = providerOptions.filter(p => !order.includes(p.value));\n if (remaining.length === 0) break;\n\n const pick = await clack.select({\n message: `Select provider #${i} (or cancel to finish)`,\n options: [...remaining, { value: DONE_OPTION, label: '✓ Done' }],\n });\n\n if (clack.isCancel(pick) || pick === DONE_OPTION) break;\n order.push(pick as string);\n }\n\n if (order.length === 0) {\n clack.log.warn('No providers selected');\n return null;\n }\n\n const allowFallbacks = await clack.confirm({\n message: 'Allow fallbacks to other providers?',\n initialValue: true,\n });\n\n return {\n provider: {\n order: order.length === 1 ? order[0] : order,\n allowFallbacks: clack.isCancel(allowFallbacks) ? true : (allowFallbacks as boolean),\n },\n };\n}\n\nasync function selectIgnoreProviders(\n providerOptions: Array<{ value: string; label: string; hint?: string }>,\n): Promise<Record<string, unknown> | null> {\n const selected = await clack.multiselect({\n message: 'Select providers to ignore',\n options: providerOptions,\n required: false,\n });\n\n if (clack.isCancel(selected)) return null;\n\n const values = selected as string[];\n if (values.length === 0) return {};\n\n const ignore = values.length === 1 ? values[0] : values;\n return { provider: { ignore } };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"remove.mjs","names":["clack.multiselect","isCancel","clack.confirm"],"sources":["../src/commands/config/remove.ts"],"sourcesContent":["import * as clack from '@clack/prompts'\nimport { isCancel } from '@clack/prompts'\nimport { getModelOverrides, removeModelOverride, requireConfigPath } from './config.js'\n\n/** Run the interactive \"Remove model override\" flow. */\nexport async function removeOverrideCommand(): Promise<void> {\n clack.intro('Remove Model Override')\n\n const configPath = requireConfigPath()\n const overrides = getModelOverrides(configPath)\n const keys = Object.keys(overrides)\n\n if (keys.length === 0) {\n clack.log.warn('No model overrides found.')\n clack.outro('')\n return\n }\n\n const selected = await clack.multiselect({\n message: 'Select overrides to remove',\n options: keys.map(k => ({\n value: k,\n label: k,\n })),\n required: true,\n })\n\n if (isCancel(selected)) return\n\n const toRemove = selected as string[]\n\n const confirmed = await clack.confirm({\n message: `Remove ${toRemove.length} override(s)?`,\n })\n\n if (isCancel(confirmed) || !confirmed) {\n clack.outro('Cancelled')\n return\n }\n\n for (const key of toRemove) {\n removeModelOverride(configPath, key)\n }\n\n clack.outro(`✓ ${toRemove.length} override(s) removed`)\n}\n"],"mappings":";;;;AAKA,eAAsB,wBAAuC;CAC3D,GAAY,uBAAuB;CAEnC,MAAM,aAAa,kBAAkB;CACrC,MAAM,YAAY,kBAAkB,UAAU;CAC9C,MAAM,OAAO,OAAO,KAAK,SAAS;CAElC,IAAI,KAAK,WAAW,GAAG;EACrB,EAAU,KAAK,2BAA2B;EAC1C,GAAY,EAAE;EACd;CACF;CAEA,MAAM,WAAW,MAAMA,GAAkB;EACvC,SAAS;EACT,SAAS,KAAK,KAAI,OAAM;GACtB,OAAO;GACP,OAAO;EACT,EAAE;EACF,UAAU;CACZ,CAAC;CAED,IAAIC,IAAS,QAAQ,GAAG;CAExB,MAAM,WAAW;CAEjB,MAAM,YAAY,MAAMC,GAAc,EACpC,SAAS,UAAU,SAAS,OAAO,eACrC,CAAC;CAED,IAAID,IAAS,SAAS,KAAK,CAAC,WAAW;EACrC,GAAY,WAAW;EACvB;CACF;CAEA,KAAK,MAAM,OAAO,UAChB,oBAAoB,YAAY,GAAG;CAGrC,GAAY,KAAK,SAAS,OAAO,qBAAqB;AACxD"}
1
+ {"version":3,"file":"remove.mjs","names":["clack.multiselect","isCancel","clack.confirm"],"sources":["../src/commands/config/remove.ts"],"sourcesContent":["import * as clack from '@clack/prompts';\nimport { isCancel } from '@clack/prompts';\nimport { getModelOverrides, removeModelOverride, requireConfigPath } from './config.js';\n\n/** Run the interactive \"Remove model override\" flow. */\nexport async function removeOverrideCommand(): Promise<void> {\n clack.intro('Remove Model Override');\n\n const configPath = requireConfigPath();\n const overrides = getModelOverrides(configPath);\n const keys = Object.keys(overrides);\n\n if (keys.length === 0) {\n clack.log.warn('No model overrides found.');\n clack.outro('');\n return;\n }\n\n const selected = await clack.multiselect({\n message: 'Select overrides to remove',\n options: keys.map(k => ({\n value: k,\n label: k,\n })),\n required: true,\n });\n\n if (isCancel(selected)) return;\n\n const toRemove = selected as string[];\n\n const confirmed = await clack.confirm({\n message: `Remove ${toRemove.length} override(s)?`,\n });\n\n if (isCancel(confirmed) || !confirmed) {\n clack.outro('Cancelled');\n return;\n }\n\n for (const key of toRemove) {\n removeModelOverride(configPath, key);\n }\n\n clack.outro(`✓ ${toRemove.length} override(s) removed`);\n}\n"],"mappings":";;;;AAKA,eAAsB,wBAAuC;CAC3D,GAAY,uBAAuB;CAEnC,MAAM,aAAa,kBAAkB;CACrC,MAAM,YAAY,kBAAkB,UAAU;CAC9C,MAAM,OAAO,OAAO,KAAK,SAAS;CAElC,IAAI,KAAK,WAAW,GAAG;EACrB,EAAU,KAAK,2BAA2B;EAC1C,GAAY,EAAE;EACd;CACF;CAEA,MAAM,WAAW,MAAMA,GAAkB;EACvC,SAAS;EACT,SAAS,KAAK,KAAI,OAAM;GACtB,OAAO;GACP,OAAO;EACT,EAAE;EACF,UAAU;CACZ,CAAC;CAED,IAAIC,IAAS,QAAQ,GAAG;CAExB,MAAM,WAAW;CAEjB,MAAM,YAAY,MAAMC,GAAc,EACpC,SAAS,UAAU,SAAS,OAAO,eACrC,CAAC;CAED,IAAID,IAAS,SAAS,KAAK,CAAC,WAAW;EACrC,GAAY,WAAW;EACvB;CACF;CAEA,KAAK,MAAM,OAAO,UAChB,oBAAoB,YAAY,GAAG;CAGrC,GAAY,KAAK,SAAS,OAAO,qBAAqB;AACxD"}
@@ -1 +1 @@
1
- {"version":3,"file":"validate.mjs","names":[],"sources":["../src/commands/config/validate.ts"],"sourcesContent":["import * as clack from '@clack/prompts'\nimport { findConfigFile, readConfigFile } from '../../config.js'\n\n/** Run config validation and display results. */\nexport async function validateConfigCommand(): Promise<void> {\n clack.intro('Validate Config')\n\n const configPath = findConfigFile()\n if (!configPath) {\n clack.log.error('No config file found.')\n clack.outro('')\n return\n }\n\n clack.log.info(`Checking ${configPath}...`)\n\n try {\n readConfigFile(configPath)\n clack.log.success('Config is valid ✓')\n } catch (error) {\n clack.log.error(String(error))\n }\n\n clack.outro('')\n}\n"],"mappings":";;;;AAIA,eAAsB,wBAAuC;CAC3D,GAAY,iBAAiB;CAE7B,MAAM,aAAa,eAAe;CAClC,IAAI,CAAC,YAAY;EACf,EAAU,MAAM,uBAAuB;EACvC,GAAY,EAAE;EACd;CACF;CAEA,EAAU,KAAK,YAAY,WAAW,IAAI;CAE1C,IAAI;EACF,eAAe,UAAU;EACzB,EAAU,QAAQ,mBAAmB;CACvC,SAAS,OAAO;EACd,EAAU,MAAM,OAAO,KAAK,CAAC;CAC/B;CAEA,GAAY,EAAE;AAChB"}
1
+ {"version":3,"file":"validate.mjs","names":[],"sources":["../src/commands/config/validate.ts"],"sourcesContent":["import * as clack from '@clack/prompts';\nimport { findConfigFile, readConfigFile } from '../../config.js';\n\n/** Run config validation and display results. */\nexport async function validateConfigCommand(): Promise<void> {\n clack.intro('Validate Config');\n\n const configPath = findConfigFile();\n if (!configPath) {\n clack.log.error('No config file found.');\n clack.outro('');\n return;\n }\n\n clack.log.info(`Checking ${configPath}...`);\n\n try {\n readConfigFile(configPath);\n clack.log.success('Config is valid ✓');\n } catch (error) {\n clack.log.error(String(error));\n }\n\n clack.outro('');\n}\n"],"mappings":";;;;AAIA,eAAsB,wBAAuC;CAC3D,GAAY,iBAAiB;CAE7B,MAAM,aAAa,eAAe;CAClC,IAAI,CAAC,YAAY;EACf,EAAU,MAAM,uBAAuB;EACvC,GAAY,EAAE;EACd;CACF;CAEA,EAAU,KAAK,YAAY,WAAW,IAAI;CAE1C,IAAI;EACF,eAAe,UAAU;EACzB,EAAU,QAAQ,mBAAmB;CACvC,SAAS,OAAO;EACd,EAAU,MAAM,OAAO,KAAK,CAAC;CAC/B;CAEA,GAAY,EAAE;AAChB"}
@@ -1 +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 {\n type AuthType,\n DEFAULTS,\n findConfigFile,\n getXdgConfigDir,\n readConfigFile,\n} from '../../config.js'\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 authType: 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 !== DEFAULTS.openrouterBaseUrl) {\n doc.set('openrouterBaseUrl', baseUrl)\n } else {\n doc.delete('openrouterBaseUrl')\n }\n if (authType !== DEFAULTS.authType) {\n doc.set('authType', authType)\n } else {\n doc.delete('authType')\n }\n return doc.toString()\n }\n\n const config: Record<string, unknown> = { openrouterKey: apiKey, port, host }\n if (baseUrl !== DEFAULTS.openrouterBaseUrl) {\n config.openrouterBaseUrl = baseUrl\n }\n if (authType !== DEFAULTS.authType) {\n config.authType = authType\n }\n return stringify(config)\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(DEFAULTS.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) : DEFAULTS.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: DEFAULTS.openrouterBaseUrl,\n initialValue: current === DEFAULTS.openrouterBaseUrl ? '' : 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() || DEFAULTS.openrouterBaseUrl\n}\n\nasync function askAuthType(current: string): Promise<string | null> {\n const authType = await clack.select({\n message: 'Authentication type',\n initialValue: current,\n options: [\n { value: 'bearer', label: 'Bearer token', hint: 'Standard OpenRouter' },\n { value: 'oauth', label: 'OAuth token', hint: 'Custom proxy providers' },\n ],\n })\n if (isCancel(authType)) return null\n return authType as string\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\ntype ExistingConfigState = {\n raw?: string\n port: number\n host: string\n apiKey: string\n baseUrl: string\n authType: AuthType\n}\n\n/** Load existing config using validated readConfigFile, falling back to defaults */\nfunction loadExistingConfig(path: string): ExistingConfigState {\n try {\n const fileConfig = readConfigFile(path)\n const raw = readFileSync(path, 'utf-8')\n return {\n raw,\n port: fileConfig.port ?? DEFAULTS.port,\n host: fileConfig.host ?? DEFAULTS.host,\n apiKey: fileConfig.openrouterKey ?? DEFAULTS.openrouterKey,\n baseUrl: fileConfig.openrouterBaseUrl ?? DEFAULTS.openrouterBaseUrl,\n authType: fileConfig.authType ?? DEFAULTS.authType,\n }\n } catch {\n return {\n raw: undefined,\n port: DEFAULTS.port,\n host: DEFAULTS.host,\n apiKey: DEFAULTS.openrouterKey,\n baseUrl: DEFAULTS.openrouterBaseUrl,\n authType: DEFAULTS.authType,\n }\n }\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 = DEFAULTS.port\n let currentHost = DEFAULTS.host\n let currentKey = DEFAULTS.openrouterKey\n let currentBaseUrl = DEFAULTS.openrouterBaseUrl\n let currentAuthType = DEFAULTS.authType\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 const existing = loadExistingConfig(existingPath)\n existingRaw = existing.raw\n currentPort = existing.port\n currentHost = existing.host\n currentKey = existing.apiKey\n currentBaseUrl = existing.baseUrl\n currentAuthType = existing.authType\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 authType = await askAuthType(currentAuthType)\n if (authType === 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, authType, 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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"}
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 {\n type AuthType,\n DEFAULTS,\n findConfigFile,\n getXdgConfigDir,\n readConfigFile,\n} from '../../config.js';\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 authType: 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 !== DEFAULTS.openrouterBaseUrl) {\n doc.set('openrouterBaseUrl', baseUrl);\n } else {\n doc.delete('openrouterBaseUrl');\n }\n if (authType !== DEFAULTS.authType) {\n doc.set('authType', authType);\n } else {\n doc.delete('authType');\n }\n return doc.toString();\n }\n\n const config: Record<string, unknown> = { openrouterKey: apiKey, port, host };\n if (baseUrl !== DEFAULTS.openrouterBaseUrl) {\n config.openrouterBaseUrl = baseUrl;\n }\n if (authType !== DEFAULTS.authType) {\n config.authType = authType;\n }\n return stringify(config);\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(DEFAULTS.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) : DEFAULTS.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: DEFAULTS.openrouterBaseUrl,\n initialValue: current === DEFAULTS.openrouterBaseUrl ? '' : 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() || DEFAULTS.openrouterBaseUrl;\n}\n\nasync function askAuthType(current: string): Promise<string | null> {\n const authType = await clack.select({\n message: 'Authentication type',\n initialValue: current,\n options: [\n { value: 'bearer', label: 'Bearer token', hint: 'Standard OpenRouter' },\n { value: 'oauth', label: 'OAuth token', hint: 'Custom proxy providers' },\n ],\n });\n if (isCancel(authType)) return null;\n return authType as string;\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\ntype ExistingConfigState = {\n raw?: string;\n port: number;\n host: string;\n apiKey: string;\n baseUrl: string;\n authType: AuthType;\n};\n\n/** Load existing config using validated readConfigFile, falling back to defaults */\nfunction loadExistingConfig(path: string): ExistingConfigState {\n try {\n const fileConfig = readConfigFile(path);\n const raw = readFileSync(path, 'utf-8');\n return {\n raw,\n port: fileConfig.port ?? DEFAULTS.port,\n host: fileConfig.host ?? DEFAULTS.host,\n apiKey: fileConfig.openrouterKey ?? DEFAULTS.openrouterKey,\n baseUrl: fileConfig.openrouterBaseUrl ?? DEFAULTS.openrouterBaseUrl,\n authType: fileConfig.authType ?? DEFAULTS.authType,\n };\n } catch {\n return {\n raw: undefined,\n port: DEFAULTS.port,\n host: DEFAULTS.host,\n apiKey: DEFAULTS.openrouterKey,\n baseUrl: DEFAULTS.openrouterBaseUrl,\n authType: DEFAULTS.authType,\n };\n }\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 = DEFAULTS.port;\n let currentHost = DEFAULTS.host;\n let currentKey = DEFAULTS.openrouterKey;\n let currentBaseUrl = DEFAULTS.openrouterBaseUrl;\n let currentAuthType = DEFAULTS.authType;\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 const existing = loadExistingConfig(existingPath);\n existingRaw = existing.raw;\n currentPort = existing.port;\n currentHost = existing.host;\n currentKey = existing.apiKey;\n currentBaseUrl = existing.baseUrl;\n currentAuthType = existing.authType;\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 authType = await askAuthType(currentAuthType);\n if (authType === 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, authType, 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,6 +1,6 @@
1
1
  {
2
2
  "name": "proxitor",
3
- "version": "0.5.2",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "description": "Lightweight proxy for routing CLI requests (claude-code, codex) to OpenRouter",
6
6
  "files": [