proxitor 0.4.0 → 0.5.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.
package/dist/config2.mjs CHANGED
@@ -7,7 +7,7 @@ import { removeOverrideCommand } from "./remove.mjs";
7
7
  import { validateConfigCommand } from "./validate.mjs";
8
8
  //#region src/commands/config.ts
9
9
  /** Run the interactive config manager menu. */
10
- async function runConfigMenu(apiKey) {
10
+ async function runConfigMenu(client) {
11
11
  ye("Proxitor Config Manager");
12
12
  const action = await Ee({
13
13
  message: "What would you like to do?",
@@ -48,10 +48,10 @@ async function runConfigMenu(apiKey) {
48
48
  }
49
49
  switch (action) {
50
50
  case "add":
51
- await addOverrideCommand(apiKey);
51
+ await addOverrideCommand(client);
52
52
  break;
53
53
  case "edit":
54
- await editOverrideCommand(apiKey);
54
+ await editOverrideCommand(client);
55
55
  break;
56
56
  case "remove":
57
57
  await removeOverrideCommand();
@@ -60,7 +60,7 @@ async function runConfigMenu(apiKey) {
60
60
  await listOverridesCommand();
61
61
  break;
62
62
  case "browse":
63
- await browseModelsCommand(apiKey);
63
+ await browseModelsCommand(client);
64
64
  break;
65
65
  case "validate":
66
66
  await validateConfigCommand();
@@ -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 { 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(apiKey: string): 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(apiKey)\n break\n case 'edit':\n await editOverrideCommand(apiKey)\n break\n case 'remove':\n await removeOverrideCommand()\n break\n case 'list':\n await listOverridesCommand()\n break\n case 'browse':\n await browseModelsCommand(apiKey)\n break\n case 'validate':\n await validateConfigCommand()\n break\n }\n}\n"],"mappings":";;;;;;;;;AAUA,eAAsB,cAAc,QAA+B;CACjE,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 { OpenRouterClient } from '../openrouter/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: OpenRouterClient): 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,QAAyC;CAC3E,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/dist.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as __commonJSMin } from "./cli.mjs";
1
+ import { o as __commonJSMin } from "./cli.mjs";
2
2
  import V, { stdin, stdout } from "node:process";
3
3
  import { ReadStream } from "node:tty";
4
4
  import { stripVTControlCharacters, styleText } from "node:util";
package/dist/dist2.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as __commonJSMin, o as __require } from "./cli.mjs";
1
+ import { o as __commonJSMin, s as __require } from "./cli.mjs";
2
2
  //#region node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js
3
3
  var require_identity = /* @__PURE__ */ __commonJSMin(((exports) => {
4
4
  const ALIAS = Symbol.for("yaml.alias");
package/dist/edit.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { confirm as le, intro as ye, isCancel as R$1, log as R, outro as fe, select as Ee } from "./dist.mjs";
2
- import { g as OpenRouterClient, n as selectProvidersByMode, r as selectRoutingMode, t as fetchProvidersForModel } from "./providers.mjs";
2
+ import { n as selectProvidersByMode, r as selectRoutingMode, t as fetchProvidersForModel } from "./providers.mjs";
3
3
  import { i as setModelOverride, r as requireConfigPath, t as getModelOverrides } from "./config.mjs";
4
4
  //#region src/commands/config/edit.ts
5
5
  function formatOverrideHint(override) {
@@ -14,9 +14,8 @@ function showCurrentConfig(modelKey, current) {
14
14
  function withoutProvider(current) {
15
15
  return current.headers ? { headers: current.headers } : {};
16
16
  }
17
- async function updateProviderRouting(configPath, modelKey, current, apiKey) {
17
+ async function updateProviderRouting(configPath, modelKey, current, client) {
18
18
  const isPattern = modelKey.includes("*");
19
- const client = new OpenRouterClient(apiKey);
20
19
  const mode = await selectRoutingMode("Routing mode");
21
20
  if (R$1(mode)) return;
22
21
  if (mode === "skip") {
@@ -39,7 +38,7 @@ async function updateProviderRouting(configPath, modelKey, current, apiKey) {
39
38
  fe("✓ Override updated");
40
39
  }
41
40
  /** Run the interactive "Edit model override" flow. */
42
- async function editOverrideCommand(apiKey) {
41
+ async function editOverrideCommand(client) {
43
42
  ye("Edit Model Override");
44
43
  const configPath = requireConfigPath();
45
44
  const overrides = getModelOverrides(configPath);
@@ -72,7 +71,7 @@ async function editOverrideCommand(apiKey) {
72
71
  }]
73
72
  });
74
73
  if (R$1(target)) return;
75
- if (target === "provider" || target === "replace") await updateProviderRouting(configPath, modelKey, current, apiKey);
74
+ if (target === "provider" || target === "replace") await updateProviderRouting(configPath, modelKey, current, client);
76
75
  }
77
76
  //#endregion
78
77
  export { editOverrideCommand };
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 { OpenRouterClient } from '../../openrouter/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 apiKey: string,\n): Promise<void> {\n const isPattern = modelKey.includes('*')\n const client = new OpenRouterClient(apiKey)\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(apiKey: string): 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, apiKey)\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;CACvC,MAAM,SAAS,IAAI,iBAAiB,MAAM;CAE1C,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,QAA+B;CACvE,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 { OpenRouterClient } from '../../openrouter/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: OpenRouterClient,\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: OpenRouterClient): 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,QAAyC;CACjF,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"}
@@ -2,35 +2,6 @@ import { confirm as le, isCancel as R$1, log as R, multiselect as we, select as
2
2
  import { homedir } from "node:os";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
- //#region src/openrouter/client.ts
6
- const DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
7
- var OpenRouterClientError = class extends Error {
8
- status;
9
- constructor(status, message) {
10
- super(`OpenRouter API error (${status}): ${message}`);
11
- this.name = "OpenRouterClientError";
12
- this.status = status;
13
- }
14
- };
15
- /** HTTP client for OpenRouter REST endpoints. Auth header is only sent when apiKey is non-empty. */
16
- var OpenRouterClient = class {
17
- apiKey;
18
- baseUrl;
19
- constructor(apiKey, baseUrl) {
20
- this.apiKey = apiKey;
21
- this.baseUrl = baseUrl ?? DEFAULT_BASE_URL;
22
- }
23
- async get(path) {
24
- const url = `${this.baseUrl}${path}`;
25
- const res = await fetch(url, { headers: { ...this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {} } });
26
- if (!res.ok) {
27
- const body = await res.text().catch(() => "");
28
- throw new OpenRouterClientError(res.status, body || res.statusText);
29
- }
30
- return res.json();
31
- }
32
- };
33
- //#endregion
34
5
  //#region src/openrouter/cache.ts
35
6
  const CACHE_DIR = join(homedir(), ".proxitor", "cache");
36
7
  /** Read a cached value. Returns `null` when missing, expired (older than `ttlMs`), or unparseable. */
@@ -274,6 +245,6 @@ async function selectIgnoreProviders(providerOptions) {
274
245
  return { provider: { ignore: values.length === 1 ? values[0] : values } };
275
246
  }
276
247
  //#endregion
277
- export { getUniqueProviders as a, formatModelHint as c, formatThroughput as d, fetchModels as f, OpenRouterClient as g, parseModelSlug as h, fetchModelEndpoints as i, formatModelLabel as l, parseModelAuthor as m, selectProvidersByMode as n, formatContextLength as o, formatPrice as p, selectRoutingMode as r, formatLatency as s, fetchProvidersForModel as t, formatPricing as u };
248
+ export { getUniqueProviders as a, formatModelHint as c, formatThroughput as d, fetchModels as f, parseModelSlug as h, fetchModelEndpoints as i, formatModelLabel as l, parseModelAuthor as m, selectProvidersByMode as n, formatContextLength as o, formatPrice as p, selectRoutingMode as r, formatLatency as s, fetchProvidersForModel as t, formatPricing as u };
278
249
 
279
250
  //# sourceMappingURL=providers.mjs.map
@@ -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/client.ts","../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":["const DEFAULT_BASE_URL = 'https://openrouter.ai/api/v1'\n\nexport class OpenRouterClientError extends Error {\n readonly status: number\n\n constructor(status: number, message: string) {\n super(`OpenRouter API error (${status}): ${message}`)\n this.name = 'OpenRouterClientError'\n this.status = status\n }\n}\n\n/** HTTP client for OpenRouter REST endpoints. Auth header is only sent when apiKey is non-empty. */\nexport class OpenRouterClient {\n private readonly apiKey: string\n private readonly baseUrl: string\n\n constructor(apiKey: string, baseUrl?: string) {\n this.apiKey = apiKey\n this.baseUrl = baseUrl ?? DEFAULT_BASE_URL\n }\n\n async get<T>(path: string): Promise<T> {\n const url = `${this.baseUrl}${path}`\n\n const res = await fetch(url, {\n headers: {\n ...(this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}),\n },\n })\n\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new OpenRouterClientError(res.status, body || res.statusText)\n }\n\n return res.json() as Promise<T>\n }\n}\n","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(), '.proxitor', 'cache')\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 { OpenRouterClient } from './client.js'\nimport type { OpenRouterModel, OpenRouterModelsResponse } from './types.js'\n\nconst CACHE_KEY = 'models'\nconst CACHE_TTL = 60 * 60 * 1000 // 1 hour\n\nexport async function fetchModels(client: OpenRouterClient): Promise<OpenRouterModel[]> {\n const cached = readCache<OpenRouterModel[]>(CACHE_KEY, CACHE_TTL)\n if (cached) return cached\n\n const response = await client.get<OpenRouterModelsResponse>('/models')\n writeCache(CACHE_KEY, response.data)\n return response.data\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 { OpenRouterClient } from './client.js'\nimport type { ModelEndpoint, ModelEndpointsResponse } from './types.js'\n\nexport async function fetchModelEndpoints(\n client: OpenRouterClient,\n author: string,\n slug: string,\n): Promise<ModelEndpoint[]> {\n const response = await client.get<ModelEndpointsResponse>(\n `/models/${author}/${slug}/endpoints`,\n )\n return response.data.endpoints ?? []\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 { OpenRouterClient } from './client.js'\nimport type { OpenRouterProvider, OpenRouterProvidersResponse } from './types.js'\n\nconst CACHE_KEY = 'providers'\nconst CACHE_TTL = 24 * 60 * 60 * 1000 // 24 hours\n\nexport async function fetchProviders(\n client: OpenRouterClient,\n): Promise<OpenRouterProvider[]> {\n const cached = readCache<OpenRouterProvider[]>(CACHE_KEY, CACHE_TTL)\n if (cached) return cached\n\n const response = await client.get<OpenRouterProvidersResponse>('/providers')\n writeCache(CACHE_KEY, response.data)\n return response.data\n}\n","import * as clack from '@clack/prompts'\nimport type { OpenRouterClient } from '../../openrouter/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: OpenRouterClient,\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: OpenRouterClient,\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: OpenRouterClient,\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(), '.proxitor', 'cache')\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 { OpenRouterClient } from './client.js'\nimport type { OpenRouterModel, OpenRouterModelsResponse } from './types.js'\n\nconst CACHE_KEY = 'models'\nconst CACHE_TTL = 60 * 60 * 1000 // 1 hour\n\nexport async function fetchModels(client: OpenRouterClient): Promise<OpenRouterModel[]> {\n const cached = readCache<OpenRouterModel[]>(CACHE_KEY, CACHE_TTL)\n if (cached) return cached\n\n const response = await client.get<OpenRouterModelsResponse>('/models')\n writeCache(CACHE_KEY, response.data)\n return response.data\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 { OpenRouterClient } from './client.js'\nimport type { ModelEndpoint, ModelEndpointsResponse } from './types.js'\n\nexport async function fetchModelEndpoints(\n client: OpenRouterClient,\n author: string,\n slug: string,\n): Promise<ModelEndpoint[]> {\n const response = await client.get<ModelEndpointsResponse>(\n `/models/${author}/${slug}/endpoints`,\n )\n return response.data.endpoints ?? []\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 { OpenRouterClient } from './client.js'\nimport type { OpenRouterProvider, OpenRouterProvidersResponse } from './types.js'\n\nconst CACHE_KEY = 'providers'\nconst CACHE_TTL = 24 * 60 * 60 * 1000 // 24 hours\n\nexport async function fetchProviders(\n client: OpenRouterClient,\n): Promise<OpenRouterProvider[]> {\n const cached = readCache<OpenRouterProvider[]>(CACHE_KEY, CACHE_TTL)\n if (cached) return cached\n\n const response = await client.get<OpenRouterProvidersResponse>('/providers')\n writeCache(CACHE_KEY, response.data)\n return response.data\n}\n","import * as clack from '@clack/prompts'\nimport type { OpenRouterClient } from '../../openrouter/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: OpenRouterClient,\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: OpenRouterClient,\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: OpenRouterClient,\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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"}
package/dist/wizard.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as findConfigFile, r as getXdgConfigDir } from "./cli.mjs";
1
+ import { a as DEFAULTS, i as readConfigFile, n as findConfigFile, r as getXdgConfigDir } from "./cli.mjs";
2
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
3
  import { t as require_dist } from "./dist2.mjs";
4
4
  import { homedir } from "node:os";
@@ -6,8 +6,6 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
6
  import { dirname, join, resolve } from "node:path";
7
7
  //#region src/commands/config/wizard.ts
8
8
  var import_dist = require_dist();
9
- const DEFAULT_PORT = 8828;
10
- const DEFAULT_HOST = "0.0.0.0";
11
9
  function maskKey(key) {
12
10
  if (key.length <= 11) return "****";
13
11
  return `${key.slice(0, 7)}...${key.slice(-4)}`;
@@ -46,29 +44,26 @@ function detectLocation(path) {
46
44
  return "user";
47
45
  }
48
46
  }
49
- function buildYaml(apiKey, port, host, existingRaw) {
47
+ function buildYaml(apiKey, port, host, baseUrl, authType, existingRaw) {
50
48
  if (existingRaw) {
51
49
  const doc = (0, import_dist.parseDocument)(existingRaw);
52
50
  doc.set("openrouterKey", apiKey);
53
51
  doc.set("port", port);
54
52
  doc.set("host", host);
53
+ if (baseUrl !== DEFAULTS.openrouterBaseUrl) doc.set("openrouterBaseUrl", baseUrl);
54
+ else doc.delete("openrouterBaseUrl");
55
+ if (authType !== DEFAULTS.authType) doc.set("authType", authType);
56
+ else doc.delete("authType");
55
57
  return doc.toString();
56
58
  }
57
- return (0, import_dist.stringify)({
59
+ const config = {
58
60
  openrouterKey: apiKey,
59
61
  port,
60
62
  host
61
- });
62
- }
63
- function readExistingConfig(path) {
64
- const raw = readFileSync(path, "utf-8");
65
- const parsed = (0, import_dist.parseDocument)(raw).toJSON();
66
- return {
67
- raw,
68
- port: typeof parsed?.port === "number" ? parsed.port : DEFAULT_PORT,
69
- host: typeof parsed?.host === "string" ? parsed.host : DEFAULT_HOST,
70
- apiKey: typeof parsed?.openrouterKey === "string" ? parsed.openrouterKey : ""
71
63
  };
64
+ if (baseUrl !== DEFAULTS.openrouterBaseUrl) config.openrouterBaseUrl = baseUrl;
65
+ if (authType !== DEFAULTS.authType) config.authType = authType;
66
+ return (0, import_dist.stringify)(config);
72
67
  }
73
68
  async function askApiKey(currentKey) {
74
69
  if (currentKey) R.info(`Current key: ${maskKey(currentKey)}`);
@@ -88,7 +83,7 @@ async function askPort(current) {
88
83
  const input = await Re({
89
84
  message: "Proxy port",
90
85
  initialValue: String(current),
91
- placeholder: String(DEFAULT_PORT),
86
+ placeholder: String(DEFAULTS.port),
92
87
  validate: (v) => {
93
88
  if (!v?.trim()) return void 0;
94
89
  const n = Number.parseInt(v, 10);
@@ -96,7 +91,41 @@ async function askPort(current) {
96
91
  }
97
92
  });
98
93
  if (R$1(input)) return null;
99
- return input.trim() ? Number.parseInt(input, 10) : DEFAULT_PORT;
94
+ return input.trim() ? Number.parseInt(input, 10) : DEFAULTS.port;
95
+ }
96
+ async function askBaseUrl(current) {
97
+ const url = await Re({
98
+ message: "OpenRouter API base URL",
99
+ placeholder: DEFAULTS.openrouterBaseUrl,
100
+ initialValue: current === DEFAULTS.openrouterBaseUrl ? "" : current,
101
+ validate: (v) => {
102
+ if (!v?.trim()) return void 0;
103
+ try {
104
+ if (!new URL(v.trim()).protocol.startsWith("http")) return "URL must start with http:// or https://";
105
+ } catch {
106
+ return "Invalid URL";
107
+ }
108
+ }
109
+ });
110
+ if (R$1(url)) return null;
111
+ return url.trim() || DEFAULTS.openrouterBaseUrl;
112
+ }
113
+ async function askAuthType(current) {
114
+ const authType = await Ee({
115
+ message: "Authentication type",
116
+ initialValue: current,
117
+ options: [{
118
+ value: "bearer",
119
+ label: "Bearer token",
120
+ hint: "Standard OpenRouter"
121
+ }, {
122
+ value: "oauth",
123
+ label: "OAuth token",
124
+ hint: "Custom proxy providers"
125
+ }]
126
+ });
127
+ if (R$1(authType)) return null;
128
+ return authType;
100
129
  }
101
130
  async function askHost(current) {
102
131
  const host = await Ee({
@@ -125,13 +154,38 @@ async function askSaveLocation(existingPath) {
125
154
  if (R$1(location)) return null;
126
155
  return location;
127
156
  }
157
+ /** Load existing config using validated readConfigFile, falling back to defaults */
158
+ function loadExistingConfig(path) {
159
+ try {
160
+ const fileConfig = readConfigFile(path);
161
+ return {
162
+ raw: readFileSync(path, "utf-8"),
163
+ port: fileConfig.port ?? DEFAULTS.port,
164
+ host: fileConfig.host ?? DEFAULTS.host,
165
+ apiKey: fileConfig.openrouterKey ?? DEFAULTS.openrouterKey,
166
+ baseUrl: fileConfig.openrouterBaseUrl ?? DEFAULTS.openrouterBaseUrl,
167
+ authType: fileConfig.authType ?? DEFAULTS.authType
168
+ };
169
+ } catch {
170
+ return {
171
+ raw: void 0,
172
+ port: DEFAULTS.port,
173
+ host: DEFAULTS.host,
174
+ apiKey: DEFAULTS.openrouterKey,
175
+ baseUrl: DEFAULTS.openrouterBaseUrl,
176
+ authType: DEFAULTS.authType
177
+ };
178
+ }
179
+ }
128
180
  async function runWizard() {
129
181
  ye("Proxitor Setup Wizard");
130
182
  const existingPath = findConfigFile();
131
183
  let existingRaw;
132
- let currentPort = DEFAULT_PORT;
133
- let currentHost = DEFAULT_HOST;
134
- let currentKey = "";
184
+ let currentPort = DEFAULTS.port;
185
+ let currentHost = DEFAULTS.host;
186
+ let currentKey = DEFAULTS.openrouterKey;
187
+ let currentBaseUrl = DEFAULTS.openrouterBaseUrl;
188
+ let currentAuthType = DEFAULTS.authType;
135
189
  if (existingPath) {
136
190
  Ce(existingPath, "Existing config found");
137
191
  const reconfigure = await le({
@@ -142,13 +196,13 @@ async function runWizard() {
142
196
  fe("Using existing configuration");
143
197
  return;
144
198
  }
145
- try {
146
- const existing = readExistingConfig(existingPath);
147
- existingRaw = existing.raw;
148
- currentPort = existing.port;
149
- currentHost = existing.host;
150
- currentKey = existing.apiKey;
151
- } catch {}
199
+ const existing = loadExistingConfig(existingPath);
200
+ existingRaw = existing.raw;
201
+ currentPort = existing.port;
202
+ currentHost = existing.host;
203
+ currentKey = existing.apiKey;
204
+ currentBaseUrl = existing.baseUrl;
205
+ currentAuthType = existing.authType;
152
206
  }
153
207
  const apiKey = await askApiKey(currentKey);
154
208
  if (apiKey === null) {
@@ -160,6 +214,16 @@ async function runWizard() {
160
214
  fe("Cancelled");
161
215
  return;
162
216
  }
217
+ const baseUrl = await askBaseUrl(currentBaseUrl);
218
+ if (baseUrl === null) {
219
+ fe("Cancelled");
220
+ return;
221
+ }
222
+ const authType = await askAuthType(currentAuthType);
223
+ if (authType === null) {
224
+ fe("Cancelled");
225
+ return;
226
+ }
163
227
  const host = await askHost(currentHost);
164
228
  if (host === null) {
165
229
  fe("Cancelled");
@@ -170,7 +234,7 @@ async function runWizard() {
170
234
  fe("Cancelled");
171
235
  return;
172
236
  }
173
- const yaml = buildYaml(apiKey, port, host, existingRaw);
237
+ const yaml = buildYaml(apiKey, port, host, baseUrl, authType, existingRaw);
174
238
  Ce(yaml, "Preview");
175
239
  const save = await le({
176
240
  message: "Save this configuration?",
@@ -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 { findConfigFile, getXdgConfigDir } from '../../config.js'\n\nconst DEFAULT_PORT = 8828\nconst DEFAULT_HOST = '0.0.0.0'\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 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 return doc.toString()\n }\n\n return stringify({ openrouterKey: apiKey, port, host })\n}\n\nfunction readExistingConfig(path: string): {\n raw: string\n port: number\n host: string\n apiKey: 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 }\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 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\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 } 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 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, 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.4.0",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "description": "Lightweight proxy for routing CLI requests (claude-code, codex) to OpenRouter",
6
6
  "files": [