proxitor 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/cli.mjs +1 -1
- package/dist/wizard.mjs +35 -5
- package/dist/wizard.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -245,6 +245,7 @@ The wizard asks for:
|
|
|
245
245
|
|
|
246
246
|
- **OpenRouter API key** — stored in config or set as `OPENROUTER_API_KEY` env var
|
|
247
247
|
- **Port** — default `8828` (avoids conflicts with common dev servers on 8080)
|
|
248
|
+
- **API base URL** — default `https://openrouter.ai/api/v1`; change for self-hosted or custom endpoints
|
|
248
249
|
- **Host** — all interfaces (`0.0.0.0`) or localhost only (`127.0.0.1`)
|
|
249
250
|
- **Save location** — project directory, `~/.config/proxitor/`, or `$XDG_CONFIG_HOME/proxitor/`
|
|
250
251
|
|
|
@@ -355,4 +356,4 @@ pnpm check # typecheck + biome + test (full CI)
|
|
|
355
356
|
|
|
356
357
|
## License
|
|
357
358
|
|
|
358
|
-
[MIT](./LICENSE)
|
|
359
|
+
[MIT](./LICENSE)
|
package/dist/cli.mjs
CHANGED
|
@@ -14719,7 +14719,7 @@ function startProxyServer(config, onReady) {
|
|
|
14719
14719
|
}
|
|
14720
14720
|
//#endregion
|
|
14721
14721
|
//#region src/version.ts
|
|
14722
|
-
const version = "0.
|
|
14722
|
+
const version = "0.5.0";
|
|
14723
14723
|
//#endregion
|
|
14724
14724
|
//#region src/cli.ts
|
|
14725
14725
|
const argv = process.argv.slice(2);
|
package/dist/wizard.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
8
8
|
var import_dist = require_dist();
|
|
9
9
|
const DEFAULT_PORT = 8828;
|
|
10
10
|
const DEFAULT_HOST = "0.0.0.0";
|
|
11
|
+
const DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
11
12
|
function maskKey(key) {
|
|
12
13
|
if (key.length <= 11) return "****";
|
|
13
14
|
return `${key.slice(0, 7)}...${key.slice(-4)}`;
|
|
@@ -46,19 +47,23 @@ function detectLocation(path) {
|
|
|
46
47
|
return "user";
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
|
-
function buildYaml(apiKey, port, host, existingRaw) {
|
|
50
|
+
function buildYaml(apiKey, port, host, baseUrl, existingRaw) {
|
|
50
51
|
if (existingRaw) {
|
|
51
52
|
const doc = (0, import_dist.parseDocument)(existingRaw);
|
|
52
53
|
doc.set("openrouterKey", apiKey);
|
|
53
54
|
doc.set("port", port);
|
|
54
55
|
doc.set("host", host);
|
|
56
|
+
if (baseUrl !== DEFAULT_BASE_URL) doc.set("openrouterBaseUrl", baseUrl);
|
|
57
|
+
else doc.delete("openrouterBaseUrl");
|
|
55
58
|
return doc.toString();
|
|
56
59
|
}
|
|
57
|
-
|
|
60
|
+
const config = {
|
|
58
61
|
openrouterKey: apiKey,
|
|
59
62
|
port,
|
|
60
63
|
host
|
|
61
|
-
}
|
|
64
|
+
};
|
|
65
|
+
if (baseUrl !== DEFAULT_BASE_URL) config.openrouterBaseUrl = baseUrl;
|
|
66
|
+
return (0, import_dist.stringify)(config);
|
|
62
67
|
}
|
|
63
68
|
function readExistingConfig(path) {
|
|
64
69
|
const raw = readFileSync(path, "utf-8");
|
|
@@ -67,7 +72,8 @@ function readExistingConfig(path) {
|
|
|
67
72
|
raw,
|
|
68
73
|
port: typeof parsed?.port === "number" ? parsed.port : DEFAULT_PORT,
|
|
69
74
|
host: typeof parsed?.host === "string" ? parsed.host : DEFAULT_HOST,
|
|
70
|
-
apiKey: typeof parsed?.openrouterKey === "string" ? parsed.openrouterKey : ""
|
|
75
|
+
apiKey: typeof parsed?.openrouterKey === "string" ? parsed.openrouterKey : "",
|
|
76
|
+
baseUrl: typeof parsed?.openrouterBaseUrl === "string" ? parsed.openrouterBaseUrl : DEFAULT_BASE_URL
|
|
71
77
|
};
|
|
72
78
|
}
|
|
73
79
|
async function askApiKey(currentKey) {
|
|
@@ -98,6 +104,23 @@ async function askPort(current) {
|
|
|
98
104
|
if (R$1(input)) return null;
|
|
99
105
|
return input.trim() ? Number.parseInt(input, 10) : DEFAULT_PORT;
|
|
100
106
|
}
|
|
107
|
+
async function askBaseUrl(current) {
|
|
108
|
+
const url = await Re({
|
|
109
|
+
message: "OpenRouter API base URL",
|
|
110
|
+
placeholder: DEFAULT_BASE_URL,
|
|
111
|
+
initialValue: current === DEFAULT_BASE_URL ? "" : current,
|
|
112
|
+
validate: (v) => {
|
|
113
|
+
if (!v?.trim()) return void 0;
|
|
114
|
+
try {
|
|
115
|
+
if (!new URL(v.trim()).protocol.startsWith("http")) return "URL must start with http:// or https://";
|
|
116
|
+
} catch {
|
|
117
|
+
return "Invalid URL";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
if (R$1(url)) return null;
|
|
122
|
+
return url.trim() || DEFAULT_BASE_URL;
|
|
123
|
+
}
|
|
101
124
|
async function askHost(current) {
|
|
102
125
|
const host = await Ee({
|
|
103
126
|
message: "Listen address",
|
|
@@ -132,6 +155,7 @@ async function runWizard() {
|
|
|
132
155
|
let currentPort = DEFAULT_PORT;
|
|
133
156
|
let currentHost = DEFAULT_HOST;
|
|
134
157
|
let currentKey = "";
|
|
158
|
+
let currentBaseUrl = DEFAULT_BASE_URL;
|
|
135
159
|
if (existingPath) {
|
|
136
160
|
Ce(existingPath, "Existing config found");
|
|
137
161
|
const reconfigure = await le({
|
|
@@ -148,6 +172,7 @@ async function runWizard() {
|
|
|
148
172
|
currentPort = existing.port;
|
|
149
173
|
currentHost = existing.host;
|
|
150
174
|
currentKey = existing.apiKey;
|
|
175
|
+
currentBaseUrl = existing.baseUrl;
|
|
151
176
|
} catch {}
|
|
152
177
|
}
|
|
153
178
|
const apiKey = await askApiKey(currentKey);
|
|
@@ -160,6 +185,11 @@ async function runWizard() {
|
|
|
160
185
|
fe("Cancelled");
|
|
161
186
|
return;
|
|
162
187
|
}
|
|
188
|
+
const baseUrl = await askBaseUrl(currentBaseUrl);
|
|
189
|
+
if (baseUrl === null) {
|
|
190
|
+
fe("Cancelled");
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
163
193
|
const host = await askHost(currentHost);
|
|
164
194
|
if (host === null) {
|
|
165
195
|
fe("Cancelled");
|
|
@@ -170,7 +200,7 @@ async function runWizard() {
|
|
|
170
200
|
fe("Cancelled");
|
|
171
201
|
return;
|
|
172
202
|
}
|
|
173
|
-
const yaml = buildYaml(apiKey, port, host, existingRaw);
|
|
203
|
+
const yaml = buildYaml(apiKey, port, host, baseUrl, existingRaw);
|
|
174
204
|
Ce(yaml, "Preview");
|
|
175
205
|
const save = await le({
|
|
176
206
|
message: "Save this configuration?",
|
package/dist/wizard.mjs.map
CHANGED
|
@@ -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 { findConfigFile, getXdgConfigDir } from '../../config.js'\n\nconst DEFAULT_PORT = 8828\nconst DEFAULT_HOST = '0.0.0.0'\nconst DEFAULT_BASE_URL = 'https://openrouter.ai/api/v1'\n\ntype SaveLocation = 'local' | 'user' | 'xdg'\n\nfunction maskKey(key: string): string {\n if (key.length <= 11) return '****'\n return `${key.slice(0, 7)}...${key.slice(-4)}`\n}\n\nfunction resolveSavePath(location: SaveLocation): string {\n switch (location) {\n case 'local':\n return resolve('proxitor.config.yaml')\n case 'user':\n return join(homedir(), '.config', 'proxitor', 'config.yaml')\n case 'xdg':\n return join(getXdgConfigDir(), 'config.yaml')\n }\n}\n\nfunction getSaveLocationOptions(_existingPath?: string) {\n const opts: { value: SaveLocation; label: string; hint: string }[] = [\n { value: 'local', label: './proxitor.config.yaml', hint: 'Project directory' },\n { value: 'user', label: '~/.config/proxitor/config.yaml', hint: 'User config' },\n ]\n\n if (process.env.XDG_CONFIG_HOME) {\n opts.push({\n value: 'xdg',\n label: '$XDG_CONFIG_HOME/proxitor/config.yaml',\n hint: 'XDG config directory',\n })\n }\n\n return opts\n}\n\nfunction detectLocation(path: string): SaveLocation | undefined {\n const cwd = resolve('.')\n if (path.startsWith(cwd)) return 'local'\n const userDir = join(homedir(), '.config', 'proxitor')\n if (path.startsWith(userDir)) {\n const xdgDir = process.env.XDG_CONFIG_HOME\n ? join(process.env.XDG_CONFIG_HOME, 'proxitor')\n : null\n if (xdgDir && path.startsWith(xdgDir)) return 'xdg'\n return 'user'\n }\n return undefined\n}\n\nfunction buildYaml(\n apiKey: string,\n port: number,\n host: string,\n baseUrl: string,\n existingRaw?: string,\n): string {\n if (existingRaw) {\n const doc = parseDocument(existingRaw)\n doc.set('openrouterKey', apiKey)\n doc.set('port', port)\n doc.set('host', host)\n if (baseUrl !== DEFAULT_BASE_URL) {\n doc.set('openrouterBaseUrl', baseUrl)\n } else {\n doc.delete('openrouterBaseUrl')\n }\n return doc.toString()\n }\n\n const config: Record<string, unknown> = { openrouterKey: apiKey, port, host }\n if (baseUrl !== DEFAULT_BASE_URL) {\n config.openrouterBaseUrl = baseUrl\n }\n return stringify(config)\n}\n\nfunction readExistingConfig(path: string): {\n raw: string\n port: number\n host: string\n apiKey: string\n baseUrl: string\n} {\n const raw = readFileSync(path, 'utf-8')\n const parsed = parseDocument(raw).toJSON() as Record<string, unknown>\n return {\n raw,\n port: typeof parsed?.port === 'number' ? parsed.port : DEFAULT_PORT,\n host: typeof parsed?.host === 'string' ? parsed.host : DEFAULT_HOST,\n apiKey: typeof parsed?.openrouterKey === 'string' ? parsed.openrouterKey : '',\n baseUrl:\n typeof parsed?.openrouterBaseUrl === 'string'\n ? parsed.openrouterBaseUrl\n : DEFAULT_BASE_URL,\n }\n}\n\nasync function askApiKey(currentKey: string): Promise<string | null> {\n if (currentKey) {\n clack.log.info(`Current key: ${maskKey(currentKey)}`)\n }\n const apiKey = await clack.text({\n message: 'OpenRouter API key',\n placeholder: 'sk-or-v1-...',\n initialValue: currentKey,\n validate: v => {\n if (!v?.trim()) return 'API key is required'\n return undefined\n },\n })\n if (isCancel(apiKey)) return null\n\n clack.note(\n 'You can also set the OPENROUTER_API_KEY environment variable\\nto avoid storing the key in the config file.',\n 'Tip',\n )\n return apiKey as string\n}\n\nasync function askPort(current: number): Promise<number | null> {\n const input = await clack.text({\n message: 'Proxy port',\n initialValue: String(current),\n placeholder: String(DEFAULT_PORT),\n validate: v => {\n if (!v?.trim()) return undefined\n const n = Number.parseInt(v, 10)\n if (Number.isNaN(n) || n < 1 || n > 65535) return 'Port must be 1–65535'\n return undefined\n },\n })\n if (isCancel(input)) return null\n return (input as string).trim() ? Number.parseInt(input as string, 10) : DEFAULT_PORT\n}\n\nasync function askBaseUrl(current: string): Promise<string | null> {\n const url = await clack.text({\n message: 'OpenRouter API base URL',\n placeholder: DEFAULT_BASE_URL,\n initialValue: current === DEFAULT_BASE_URL ? '' : current,\n validate: v => {\n if (!v?.trim()) return undefined\n try {\n const parsed = new URL(v.trim())\n if (!parsed.protocol.startsWith('http'))\n return 'URL must start with http:// or https://'\n } catch {\n return 'Invalid URL'\n }\n return undefined\n },\n })\n if (isCancel(url)) return null\n return (url as string).trim() || DEFAULT_BASE_URL\n}\n\nasync function askHost(current: string): Promise<string | null> {\n const host = await clack.select({\n message: 'Listen address',\n initialValue: current as '0.0.0.0' | '127.0.0.1',\n options: [\n { value: '0.0.0.0', label: 'All interfaces (0.0.0.0)', hint: 'Default' },\n { value: '127.0.0.1', label: 'Localhost only (127.0.0.1)', hint: 'More secure' },\n ],\n })\n if (isCancel(host)) return null\n return host as string\n}\n\nasync function askSaveLocation(existingPath?: string): Promise<SaveLocation | null> {\n const options = getSaveLocationOptions(existingPath)\n const detected = existingPath ? detectLocation(existingPath) : undefined\n\n const location = await clack.select({\n message: 'Save config to',\n initialValue: detected ?? 'local',\n options,\n })\n if (isCancel(location)) return null\n return location as SaveLocation\n}\n\nexport async function runWizard(): Promise<void> {\n clack.intro('Proxitor Setup Wizard')\n\n const existingPath = findConfigFile()\n let existingRaw: string | undefined\n let currentPort = DEFAULT_PORT\n let currentHost = DEFAULT_HOST\n let currentKey = ''\n let currentBaseUrl = DEFAULT_BASE_URL\n\n if (existingPath) {\n clack.note(existingPath, 'Existing config found')\n\n const reconfigure = await clack.confirm({\n message: 'Reconfigure?',\n initialValue: true,\n })\n if (isCancel(reconfigure) || !reconfigure) {\n clack.outro('Using existing configuration')\n return\n }\n\n try {\n const existing = readExistingConfig(existingPath)\n existingRaw = existing.raw\n currentPort = existing.port\n currentHost = existing.host\n currentKey = existing.apiKey\n currentBaseUrl = existing.baseUrl\n } catch {\n // use defaults\n }\n }\n\n const apiKey = await askApiKey(currentKey)\n if (apiKey === null) {\n clack.outro('Cancelled')\n return\n }\n\n const port = await askPort(currentPort)\n if (port === null) {\n clack.outro('Cancelled')\n return\n }\n\n const baseUrl = await askBaseUrl(currentBaseUrl)\n if (baseUrl === null) {\n clack.outro('Cancelled')\n return\n }\n\n const host = await askHost(currentHost)\n if (host === null) {\n clack.outro('Cancelled')\n return\n }\n\n const location = await askSaveLocation(existingPath ?? undefined)\n if (location === null) {\n clack.outro('Cancelled')\n return\n }\n\n const yaml = buildYaml(apiKey, port, host, baseUrl, existingRaw)\n clack.note(yaml, 'Preview')\n\n const save = await clack.confirm({\n message: 'Save this configuration?',\n initialValue: true,\n })\n if (isCancel(save) || !save) {\n clack.outro('Cancelled — no files written')\n return\n }\n\n const savePath = resolveSavePath(location)\n const dir = dirname(savePath)\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n writeFileSync(savePath, yaml, 'utf-8')\n\n clack.outro(`Config saved to ${savePath}`)\n}\n"],"mappings":"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"}
|