promptimizer-cli 0.1.17 → 0.1.18
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 +23 -7
- package/bin/promptimizer.mjs +264 -101
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
# promptimizer-cli
|
|
2
2
|
|
|
3
|
+
Gemini-style interactive routing CLI for Promptimizer.
|
|
4
|
+
|
|
3
5
|
```bash
|
|
4
6
|
npm install -g promptimizer-cli
|
|
5
|
-
|
|
6
|
-
npx promptimizer-cli login --help
|
|
7
|
+
promptimizer
|
|
7
8
|
```
|
|
8
9
|
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
```text
|
|
11
|
+
██████╗ ███╗ ███╗███████╗
|
|
12
|
+
…
|
|
13
|
+
Promptimizer v0.1.17
|
|
14
|
+
Type a prompt, or /help /models /savings /clear /quit
|
|
15
|
+
› What is 17 * 24?
|
|
16
|
+
✦
|
|
17
|
+
408
|
|
18
|
+
↳ thinkingmachines/inkling-small · economy · saved $0.0001
|
|
14
19
|
```
|
|
15
20
|
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
| Command | What it does |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `promptimizer` | Interactive multi-turn session |
|
|
26
|
+
| `promptimizer login --key pmz_live_…` | Save API key |
|
|
27
|
+
| `promptimizer connect baseten --key $BASETEN_API_KEY` | Attach provider |
|
|
28
|
+
| `promptimizer chat "…"` | One-shot completion |
|
|
29
|
+
| `promptimizer models` | List fleet |
|
|
30
|
+
| `promptimizer savings` | Account ledger |
|
|
31
|
+
|
|
16
32
|
Defaults to the hosted gateway. Override with `--url` or `PROMPTIMIZER_URL`.
|
package/bin/promptimizer.mjs
CHANGED
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { createInterface } from "node:readline/promises";
|
|
3
4
|
import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
5
|
import { homedir } from "node:os";
|
|
5
6
|
import { dirname, join } from "node:path";
|
|
6
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
7
9
|
|
|
8
10
|
const DEFAULT_URL = process.env.PROMPTIMIZER_URL || "https://hackathon-omega-liart.vercel.app/api";
|
|
9
11
|
const CONFIG_PATH = join(homedir(), ".promptimizer", "config.json");
|
|
10
12
|
|
|
13
|
+
const ANSI = {
|
|
14
|
+
reset: "\x1b[0m",
|
|
15
|
+
bold: "\x1b[1m",
|
|
16
|
+
dim: "\x1b[2m",
|
|
17
|
+
cyan: "\x1b[36m",
|
|
18
|
+
green: "\x1b[32m",
|
|
19
|
+
yellow: "\x1b[33m",
|
|
20
|
+
magenta: "\x1b[35m",
|
|
21
|
+
gray: "\x1b[90m",
|
|
22
|
+
blue: "\x1b[34m",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const color = process.stdout.isTTY
|
|
26
|
+
? (code, text) => `${code}${text}${ANSI.reset}`
|
|
27
|
+
: (_code, text) => text;
|
|
28
|
+
|
|
11
29
|
const COMMANDS = [
|
|
30
|
+
["", "Start interactive session (Gemini-style REPL)"],
|
|
12
31
|
["login", "Save a Promptimizer API key"],
|
|
13
32
|
["logout", "Remove the saved key"],
|
|
14
33
|
["connect", "Attach a model provider"],
|
|
15
|
-
["chat", "Route
|
|
34
|
+
["chat", "Route one completion"],
|
|
16
35
|
["models", "List the connected fleet"],
|
|
17
36
|
["savings", "Show account savings"],
|
|
18
37
|
["providers", "List known provider URLs"],
|
|
@@ -24,10 +43,6 @@ const COMMAND_HELP = {
|
|
|
24
43
|
" promptimizer login --key <pmz_live_...>",
|
|
25
44
|
"",
|
|
26
45
|
"Save a key from /account. Stored in ~/.promptimizer/config.json.",
|
|
27
|
-
"",
|
|
28
|
-
"Options",
|
|
29
|
-
" --key, -k Promptimizer API key",
|
|
30
|
-
" --url, -u Gateway URL",
|
|
31
46
|
],
|
|
32
47
|
logout: ["Usage", " promptimizer logout", "", "Deletes ~/.promptimizer/config.json."],
|
|
33
48
|
connect: [
|
|
@@ -35,54 +50,16 @@ const COMMAND_HELP = {
|
|
|
35
50
|
" promptimizer connect <provider> --key <vendor-key>",
|
|
36
51
|
" promptimizer connect custom --base-url <url> --key <vendor-key>",
|
|
37
52
|
" promptimizer connect simulator",
|
|
38
|
-
"",
|
|
39
|
-
"Attach a provider to the signed-in account. Known hosts do not need --base-url.",
|
|
40
|
-
"",
|
|
41
|
-
"Options",
|
|
42
|
-
" --key, -k Provider API key (or $BASETEN_API_KEY, $GROQ_API_KEY, …)",
|
|
43
|
-
" --base-url Required for custom",
|
|
44
|
-
" --pmz Promptimizer key (else the saved login)",
|
|
45
|
-
" --url, -u Gateway URL",
|
|
46
53
|
],
|
|
47
54
|
chat: [
|
|
48
55
|
"Usage",
|
|
49
|
-
' promptimizer chat "
|
|
50
|
-
"",
|
|
51
|
-
"Route one completion through the connected provider.",
|
|
52
|
-
"",
|
|
53
|
-
"Options",
|
|
54
|
-
" --prompt Prompt text",
|
|
55
|
-
" --pmz Promptimizer key (else the saved login)",
|
|
56
|
-
" --url, -u Gateway URL",
|
|
57
|
-
],
|
|
58
|
-
models: [
|
|
59
|
-
"Usage",
|
|
60
|
-
" promptimizer models",
|
|
61
|
-
"",
|
|
62
|
-
"List chat models on the current session.",
|
|
63
|
-
"",
|
|
64
|
-
"Options",
|
|
65
|
-
" --url, -u Gateway URL",
|
|
66
|
-
],
|
|
67
|
-
savings: [
|
|
68
|
-
"Usage",
|
|
69
|
-
" promptimizer savings",
|
|
56
|
+
' promptimizer chat "What is 17 * 24?"',
|
|
70
57
|
"",
|
|
71
|
-
"
|
|
72
|
-
"",
|
|
73
|
-
"Options",
|
|
74
|
-
" --key, -k Promptimizer API key",
|
|
75
|
-
" --url, -u Gateway URL",
|
|
76
|
-
],
|
|
77
|
-
providers: [
|
|
78
|
-
"Usage",
|
|
79
|
-
" promptimizer providers",
|
|
80
|
-
"",
|
|
81
|
-
"Print known provider ids and base URLs.",
|
|
82
|
-
"",
|
|
83
|
-
"Options",
|
|
84
|
-
" --url, -u Gateway URL",
|
|
58
|
+
"One-shot. Prefer bare `promptimizer` for a multi-turn session.",
|
|
85
59
|
],
|
|
60
|
+
models: ["Usage", " promptimizer models"],
|
|
61
|
+
savings: ["Usage", " promptimizer savings"],
|
|
62
|
+
providers: ["Usage", " promptimizer providers"],
|
|
86
63
|
};
|
|
87
64
|
|
|
88
65
|
function die(message, code = 1) {
|
|
@@ -149,23 +126,55 @@ function usd(value) {
|
|
|
149
126
|
return Math.abs(n) >= 1 ? `$${n.toFixed(2)}` : `$${n.toFixed(4)}`;
|
|
150
127
|
}
|
|
151
128
|
|
|
129
|
+
function printVersion() {
|
|
130
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
131
|
+
const pkg = JSON.parse(readFileSync(join(here, "../package.json"), "utf8"));
|
|
132
|
+
out(pkg.version);
|
|
133
|
+
return pkg.version;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function banner(session, version) {
|
|
137
|
+
const label = session?.label || "not connected";
|
|
138
|
+
const models = session?.models?.length ?? 0;
|
|
139
|
+
const baseline = session?.baseline_model || "—";
|
|
140
|
+
out();
|
|
141
|
+
out(color(ANSI.cyan, " ██████╗ ███╗ ███╗███████╗"));
|
|
142
|
+
out(color(ANSI.cyan, " ██╔══██╗████╗ ████║╚══███╔╝"));
|
|
143
|
+
out(color(ANSI.cyan, " ██████╔╝██╔████╔██║ ███╔╝ "));
|
|
144
|
+
out(color(ANSI.cyan, " ██╔═══╝ ██║╚██╔╝██║ ███╔╝ "));
|
|
145
|
+
out(color(ANSI.cyan, " ██║ ██║ ╚═╝ ██║███████╗"));
|
|
146
|
+
out(color(ANSI.cyan, " ╚═╝ ╚═╝ ╚═╝╚══════╝"));
|
|
147
|
+
out();
|
|
148
|
+
out(` ${color(ANSI.bold, "Promptimizer")} ${color(ANSI.dim, `v${version}`)}`);
|
|
149
|
+
out(` ${color(ANSI.dim, "Quality-aware routing · OpenAI-compatible")}`);
|
|
150
|
+
out();
|
|
151
|
+
out(` ${color(ANSI.green, "●")} ${label}${models ? ` · ${models} models` : ""}`);
|
|
152
|
+
out(` ${color(ANSI.dim, `baseline ${baseline}`)}`);
|
|
153
|
+
out();
|
|
154
|
+
out(` ${color(ANSI.dim, "Type a prompt, or /help /models /savings /clear /quit")}`);
|
|
155
|
+
out();
|
|
156
|
+
}
|
|
157
|
+
|
|
152
158
|
function help() {
|
|
153
|
-
|
|
154
|
-
out("Usage: promptimizer [--url <gateway>] <command> [options]");
|
|
159
|
+
out("Usage: promptimizer [--url <gateway>] [command] [options]");
|
|
155
160
|
out();
|
|
156
|
-
out("
|
|
161
|
+
out(" promptimizer Interactive session (REPL)");
|
|
162
|
+
out(' promptimizer chat "…" One-shot completion');
|
|
157
163
|
out();
|
|
158
164
|
out("Commands");
|
|
159
|
-
|
|
165
|
+
const width = Math.max(...COMMANDS.map(([name]) => name.length || 1));
|
|
166
|
+
for (const [name, desc] of COMMANDS) {
|
|
167
|
+
const label = name || "(default)";
|
|
168
|
+
out(` ${label.padEnd(width + 4)}${desc}`);
|
|
169
|
+
}
|
|
160
170
|
out();
|
|
161
171
|
out("Global options");
|
|
162
172
|
out(" --url, -u Gateway URL (default: hosted app, or $PROMPTIMIZER_URL)");
|
|
163
173
|
out(" --help, -h Show help");
|
|
164
174
|
out(" --version, -v Print version");
|
|
165
175
|
out();
|
|
166
|
-
out("Run `promptimizer <command> --help` for command flags.");
|
|
167
|
-
out();
|
|
168
176
|
out("Examples");
|
|
177
|
+
out(" promptimizer");
|
|
169
178
|
out(" promptimizer login --key pmz_live_…");
|
|
170
179
|
out(" promptimizer connect baseten --key $BASETEN_API_KEY");
|
|
171
180
|
out(' promptimizer chat "What is 17 * 24?"');
|
|
@@ -197,7 +206,7 @@ async function request(path, { method = "GET", body, apiKey, sessionId, gatewayU
|
|
|
197
206
|
if (!response.ok) {
|
|
198
207
|
const detail =
|
|
199
208
|
typeof data === "object" && data && "detail" in data ? String(data.detail) : response.statusText;
|
|
200
|
-
|
|
209
|
+
throw Object.assign(new Error(detail), { status: response.status });
|
|
201
210
|
}
|
|
202
211
|
return data;
|
|
203
212
|
}
|
|
@@ -213,6 +222,44 @@ function requireKey(flags, config) {
|
|
|
213
222
|
return String(apiKey);
|
|
214
223
|
}
|
|
215
224
|
|
|
225
|
+
function authFromConfig(flags, config) {
|
|
226
|
+
const apiKey = flags.pmz || process.env.PROMPTIMIZER_API_KEY || config.apiKey;
|
|
227
|
+
const sessionId = apiKey ? undefined : config.sessionId;
|
|
228
|
+
if (!apiKey && !sessionId) {
|
|
229
|
+
throw new Error("Not signed in. Run promptimizer login --key pmz_live_…");
|
|
230
|
+
}
|
|
231
|
+
return { apiKey, sessionId };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function loadSession(flags, config) {
|
|
235
|
+
const gatewayURL = gateway(flags, config);
|
|
236
|
+
const { apiKey, sessionId } = authFromConfig(flags, config);
|
|
237
|
+
return request("/v1/session", { gatewayURL, apiKey, sessionId });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function printMeta(result) {
|
|
241
|
+
const meta = result.promptimizer ?? {};
|
|
242
|
+
const saved = result.usage?.cost?.saved_usd;
|
|
243
|
+
const bits = [meta.model || result.model, meta.tier].filter(Boolean);
|
|
244
|
+
if (saved != null) bits.push(`saved ${usd(saved)}`);
|
|
245
|
+
if (meta.cache_hit) bits.push("cache");
|
|
246
|
+
if (meta.escalated) bits.push("escalated");
|
|
247
|
+
if (meta.latency_ms != null) bits.push(`${Math.round(Number(meta.latency_ms))}ms`);
|
|
248
|
+
out(color(ANSI.dim, ` ↳ ${bits.join(" · ")}`));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function complete(flags, config, messages) {
|
|
252
|
+
const gatewayURL = gateway(flags, config);
|
|
253
|
+
const { apiKey, sessionId } = authFromConfig(flags, config);
|
|
254
|
+
return request("/v1/chat/completions", {
|
|
255
|
+
method: "POST",
|
|
256
|
+
gatewayURL,
|
|
257
|
+
apiKey,
|
|
258
|
+
sessionId,
|
|
259
|
+
body: { messages },
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
216
263
|
async function cmdLogin(flags) {
|
|
217
264
|
const apiKey = flags.key || flags.k || process.env.PROMPTIMIZER_API_KEY;
|
|
218
265
|
if (!apiKey) die("Missing --key. Create one at /account.");
|
|
@@ -220,12 +267,12 @@ async function cmdLogin(flags) {
|
|
|
220
267
|
const gatewayURL = gateway(flags, config);
|
|
221
268
|
await request("/v1/session", { apiKey, gatewayURL });
|
|
222
269
|
writeConfig({ ...config, gatewayURL, apiKey });
|
|
223
|
-
out(
|
|
270
|
+
out(`${color(ANSI.green, "✓")} Saved ${gatewayURL}`);
|
|
224
271
|
}
|
|
225
272
|
|
|
226
273
|
function cmdLogout() {
|
|
227
274
|
rmSync(CONFIG_PATH, { force: true });
|
|
228
|
-
out("Forgot saved key
|
|
275
|
+
out(`${color(ANSI.green, "✓")} Forgot saved key.`);
|
|
229
276
|
}
|
|
230
277
|
|
|
231
278
|
async function cmdProviders(flags) {
|
|
@@ -245,7 +292,9 @@ async function cmdConnect(flags, positional) {
|
|
|
245
292
|
const gatewayURL = gateway(flags, config);
|
|
246
293
|
const provider = String(flags.provider || positional[0] || "").trim();
|
|
247
294
|
const baseURL = flags["base-url"] || flags.baseUrl;
|
|
248
|
-
if (!provider && !baseURL)
|
|
295
|
+
if (!provider && !baseURL) {
|
|
296
|
+
die("Usage: promptimizer connect <provider>\n promptimizer connect custom --base-url https://…");
|
|
297
|
+
}
|
|
249
298
|
|
|
250
299
|
const mock = provider === "simulator" || provider === "mock";
|
|
251
300
|
let vendorKey = flags.key || flags.k;
|
|
@@ -279,53 +328,34 @@ async function cmdConnect(flags, positional) {
|
|
|
279
328
|
});
|
|
280
329
|
|
|
281
330
|
writeConfig({ ...config, gatewayURL, apiKey, sessionId: session.session_id });
|
|
282
|
-
out(`${session.label} ${session.base_url}`);
|
|
283
|
-
out(
|
|
331
|
+
out(`${color(ANSI.green, "✓")} ${session.label} ${session.base_url}`);
|
|
332
|
+
out(` ${session.models.length} models · baseline ${session.baseline_model}`);
|
|
284
333
|
}
|
|
285
334
|
|
|
286
335
|
async function cmdChat(flags, positional) {
|
|
287
336
|
const config = readConfig();
|
|
288
337
|
const prompt = String(flags.prompt || positional.join(" ")).trim();
|
|
289
338
|
if (!prompt) die('Usage: promptimizer chat "What is 17 * 24?"');
|
|
290
|
-
const
|
|
291
|
-
const apiKey = flags.pmz || process.env.PROMPTIMIZER_API_KEY || config.apiKey;
|
|
292
|
-
const sessionId = apiKey ? undefined : config.sessionId;
|
|
293
|
-
if (!apiKey && !sessionId) die("Run promptimizer login or promptimizer connect first.");
|
|
294
|
-
|
|
295
|
-
const result = await request("/v1/chat/completions", {
|
|
296
|
-
method: "POST",
|
|
297
|
-
gatewayURL,
|
|
298
|
-
apiKey,
|
|
299
|
-
sessionId,
|
|
300
|
-
body: { messages: [{ role: "user", content: prompt }] },
|
|
301
|
-
});
|
|
339
|
+
const result = await complete(flags, config, [{ role: "user", content: prompt }]);
|
|
302
340
|
const text = result.choices?.[0]?.message?.content?.trim() ?? "";
|
|
303
|
-
const meta = result.promptimizer ?? {};
|
|
304
|
-
const saved = result.usage?.cost?.saved_usd;
|
|
305
341
|
out();
|
|
306
342
|
out(text);
|
|
307
343
|
out();
|
|
308
|
-
|
|
309
|
-
if (saved != null) bits.push(`saved ${usd(saved)}`);
|
|
310
|
-
if (meta.cache_hit) bits.push("cache");
|
|
311
|
-
if (meta.escalated) bits.push("escalated");
|
|
312
|
-
out(bits.join(" · "));
|
|
344
|
+
printMeta(result);
|
|
313
345
|
out();
|
|
314
346
|
}
|
|
315
347
|
|
|
316
348
|
async function cmdModels(flags) {
|
|
317
349
|
const config = readConfig();
|
|
318
350
|
const gatewayURL = gateway(flags, config);
|
|
319
|
-
const apiKey =
|
|
320
|
-
const sessionId = apiKey ? undefined : config.sessionId;
|
|
321
|
-
if (!apiKey && !sessionId) die("Run promptimizer login or promptimizer connect first.");
|
|
351
|
+
const { apiKey, sessionId } = authFromConfig(flags, config);
|
|
322
352
|
const data = await request("/v1/models", { gatewayURL, apiKey, sessionId });
|
|
323
353
|
const models = data.data ?? [];
|
|
324
354
|
const width = Math.max(8, ...models.map((model) => String(model.tier).length));
|
|
325
355
|
out();
|
|
326
356
|
for (const model of models) {
|
|
327
|
-
const mark = model.id === data.baseline_model ? " baseline" : "";
|
|
328
|
-
out(` ${String(model.tier).padEnd(width + 2)}${model.id}${mark}`);
|
|
357
|
+
const mark = model.id === data.baseline_model ? color(ANSI.yellow, " baseline") : "";
|
|
358
|
+
out(` ${color(ANSI.dim, String(model.tier).padEnd(width + 2))}${model.id}${mark}`);
|
|
329
359
|
}
|
|
330
360
|
out();
|
|
331
361
|
}
|
|
@@ -335,7 +365,7 @@ async function cmdSavings(flags) {
|
|
|
335
365
|
const apiKey = requireKey(flags, config);
|
|
336
366
|
const data = await request("/v1/savings", { gatewayURL: gateway(flags, config), apiKey });
|
|
337
367
|
out();
|
|
338
|
-
out(`${usd(data.saved_usd)} saved`);
|
|
368
|
+
out(`${color(ANSI.bold, usd(data.saved_usd))} saved ${color(ANSI.dim, `(${Number(data.saved_pct || 0).toFixed(1)}%)`)}`);
|
|
339
369
|
out();
|
|
340
370
|
out(` routed ${usd(data.actual_usd)}`);
|
|
341
371
|
out(` baseline ${usd(data.baseline_usd)}`);
|
|
@@ -345,10 +375,137 @@ async function cmdSavings(flags) {
|
|
|
345
375
|
out();
|
|
346
376
|
}
|
|
347
377
|
|
|
348
|
-
function
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
378
|
+
async function interactive(flags) {
|
|
379
|
+
if (!input.isTTY || !output.isTTY) {
|
|
380
|
+
help();
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const config = readConfig();
|
|
385
|
+
const version = (() => {
|
|
386
|
+
try {
|
|
387
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
388
|
+
return JSON.parse(readFileSync(join(here, "../package.json"), "utf8")).version;
|
|
389
|
+
} catch {
|
|
390
|
+
return "0.0.0";
|
|
391
|
+
}
|
|
392
|
+
})();
|
|
393
|
+
|
|
394
|
+
let session = null;
|
|
395
|
+
try {
|
|
396
|
+
session = await loadSession(flags, config);
|
|
397
|
+
} catch (error) {
|
|
398
|
+
banner(null, version);
|
|
399
|
+
out(color(ANSI.yellow, ` ${error instanceof Error ? error.message : String(error)}`));
|
|
400
|
+
out(color(ANSI.dim, " Run: promptimizer login --key pmz_live_…"));
|
|
401
|
+
out();
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
banner(session, version);
|
|
406
|
+
|
|
407
|
+
const rl = createInterface({ input, output, terminal: true });
|
|
408
|
+
const history = [];
|
|
409
|
+
|
|
410
|
+
const slashHelp = () => {
|
|
411
|
+
out();
|
|
412
|
+
out(` ${color(ANSI.bold, "/help")} this list`);
|
|
413
|
+
out(` ${color(ANSI.bold, "/models")} fleet + tiers`);
|
|
414
|
+
out(` ${color(ANSI.bold, "/savings")} account ledger`);
|
|
415
|
+
out(` ${color(ANSI.bold, "/session")} provider status`);
|
|
416
|
+
out(` ${color(ANSI.bold, "/clear")} clear chat history`);
|
|
417
|
+
out(` ${color(ANSI.bold, "/quit")} exit`);
|
|
418
|
+
out();
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
while (true) {
|
|
423
|
+
let line;
|
|
424
|
+
try {
|
|
425
|
+
line = await rl.question(color(ANSI.cyan, "› "));
|
|
426
|
+
} catch {
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
const trimmed = line.trim();
|
|
430
|
+
if (!trimmed) continue;
|
|
431
|
+
|
|
432
|
+
if (trimmed === "/quit" || trimmed === "/exit" || trimmed === "/q") break;
|
|
433
|
+
|
|
434
|
+
if (trimmed === "/help" || trimmed === "/?") {
|
|
435
|
+
slashHelp();
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (trimmed === "/clear") {
|
|
440
|
+
history.length = 0;
|
|
441
|
+
out(color(ANSI.dim, " History cleared."));
|
|
442
|
+
out();
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (trimmed === "/session") {
|
|
447
|
+
try {
|
|
448
|
+
session = await loadSession(flags, readConfig());
|
|
449
|
+
out();
|
|
450
|
+
out(` ${session.label} · ${session.mode}`);
|
|
451
|
+
out(` ${session.base_url}`);
|
|
452
|
+
out(` ${session.models.length} models · baseline ${session.baseline_model}`);
|
|
453
|
+
out();
|
|
454
|
+
} catch (error) {
|
|
455
|
+
out(color(ANSI.yellow, ` ${error instanceof Error ? error.message : String(error)}`));
|
|
456
|
+
}
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (trimmed === "/models") {
|
|
461
|
+
try {
|
|
462
|
+
await cmdModels(flags);
|
|
463
|
+
} catch (error) {
|
|
464
|
+
out(color(ANSI.yellow, ` ${error instanceof Error ? error.message : String(error)}`));
|
|
465
|
+
}
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (trimmed === "/savings") {
|
|
470
|
+
try {
|
|
471
|
+
await cmdSavings(flags);
|
|
472
|
+
} catch (error) {
|
|
473
|
+
out(color(ANSI.yellow, ` ${error instanceof Error ? error.message : String(error)}`));
|
|
474
|
+
}
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (trimmed.startsWith("/")) {
|
|
479
|
+
out(color(ANSI.dim, " Unknown command. Try /help"));
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
history.push({ role: "user", content: trimmed });
|
|
484
|
+
process.stdout.write(color(ANSI.dim, " … routing\r"));
|
|
485
|
+
try {
|
|
486
|
+
const result = await complete(flags, readConfig(), history);
|
|
487
|
+
process.stdout.write(" \r");
|
|
488
|
+
const text = result.choices?.[0]?.message?.content?.trim() ?? "";
|
|
489
|
+
history.push({ role: "assistant", content: text });
|
|
490
|
+
out();
|
|
491
|
+
out(color(ANSI.magenta, "✦"));
|
|
492
|
+
out(text);
|
|
493
|
+
out();
|
|
494
|
+
printMeta(result);
|
|
495
|
+
out();
|
|
496
|
+
} catch (error) {
|
|
497
|
+
process.stdout.write(" \r");
|
|
498
|
+
history.pop();
|
|
499
|
+
out(color(ANSI.yellow, ` ${error instanceof Error ? error.message : String(error)}`));
|
|
500
|
+
out();
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
} finally {
|
|
504
|
+
rl.close();
|
|
505
|
+
out();
|
|
506
|
+
out(color(ANSI.dim, " bye"));
|
|
507
|
+
out();
|
|
508
|
+
}
|
|
352
509
|
}
|
|
353
510
|
|
|
354
511
|
async function main() {
|
|
@@ -362,23 +519,29 @@ async function main() {
|
|
|
362
519
|
else help();
|
|
363
520
|
return;
|
|
364
521
|
}
|
|
365
|
-
if (
|
|
366
|
-
|
|
522
|
+
if (flags.help || flags.h) {
|
|
523
|
+
if (positional[0]) commandHelp(positional[0]);
|
|
524
|
+
else help();
|
|
367
525
|
return;
|
|
368
526
|
}
|
|
369
527
|
|
|
528
|
+
if (positional.length === 0) {
|
|
529
|
+
return interactive(flags);
|
|
530
|
+
}
|
|
531
|
+
|
|
370
532
|
const [command, ...rest] = positional;
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
return;
|
|
533
|
+
try {
|
|
534
|
+
if (command === "login") return await cmdLogin(flags);
|
|
535
|
+
if (command === "logout") return cmdLogout();
|
|
536
|
+
if (command === "providers") return await cmdProviders(flags);
|
|
537
|
+
if (command === "connect") return await cmdConnect(flags, rest);
|
|
538
|
+
if (command === "chat") return await cmdChat(flags, rest);
|
|
539
|
+
if (command === "models") return await cmdModels(flags);
|
|
540
|
+
if (command === "savings") return await cmdSavings(flags);
|
|
541
|
+
if (command === "repl" || command === "i") return await interactive(flags);
|
|
542
|
+
} catch (error) {
|
|
543
|
+
die(error instanceof Error ? error.message : String(error));
|
|
374
544
|
}
|
|
375
|
-
if (command === "login") return cmdLogin(flags);
|
|
376
|
-
if (command === "logout") return cmdLogout();
|
|
377
|
-
if (command === "providers") return cmdProviders(flags);
|
|
378
|
-
if (command === "connect") return cmdConnect(flags, rest);
|
|
379
|
-
if (command === "chat") return cmdChat(flags, rest);
|
|
380
|
-
if (command === "models") return cmdModels(flags);
|
|
381
|
-
if (command === "savings") return cmdSavings(flags);
|
|
382
545
|
die(`Unknown command "${command}". Run promptimizer --help.`);
|
|
383
546
|
}
|
|
384
547
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "promptimizer-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.18",
|
|
4
|
+
"description": "Interactive Promptimizer CLI — Gemini-style REPL for quality-aware routing.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"promptimizer": "./bin/promptimizer.mjs"
|