promptimizer-cli 0.1.17 → 0.1.19

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 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
- npx promptimizer-cli --help
6
- npx promptimizer-cli login --help
7
+ promptimizer
7
8
  ```
8
9
 
9
- ```bash
10
- promptimizer login --key pmz_live_…
11
- promptimizer connect baseten --key "$BASETEN_API_KEY"
12
- promptimizer chat "What is 17 * 24?"
13
- promptimizer savings
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`.
@@ -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 a completion"],
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 "<prompt>"',
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",
70
- "",
71
- "Print routed spend versus the frontier baseline.",
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.",
56
+ ' promptimizer chat "What is 17 * 24?"',
82
57
  "",
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,56 @@ 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(` ${color(ANSI.dim, "Cache keys on full history — /clear then repeat a prompt to see cache hit")}`);
156
+ out();
157
+ }
158
+
152
159
  function help() {
153
- const width = Math.max(...COMMANDS.map(([name]) => name.length));
154
- out("Usage: promptimizer [--url <gateway>] <command> [options]");
160
+ out("Usage: promptimizer [--url <gateway>] [command] [options]");
155
161
  out();
156
- out("Route prompts through Promptimizer. Create a key at /account.");
162
+ out(" promptimizer Interactive session (REPL)");
163
+ out(' promptimizer chat "…" One-shot completion');
157
164
  out();
158
165
  out("Commands");
159
- for (const [name, desc] of COMMANDS) out(` ${name.padEnd(width + 2)}${desc}`);
166
+ const width = Math.max(...COMMANDS.map(([name]) => name.length || 1));
167
+ for (const [name, desc] of COMMANDS) {
168
+ const label = name || "(default)";
169
+ out(` ${label.padEnd(width + 4)}${desc}`);
170
+ }
160
171
  out();
161
172
  out("Global options");
162
173
  out(" --url, -u Gateway URL (default: hosted app, or $PROMPTIMIZER_URL)");
163
174
  out(" --help, -h Show help");
164
175
  out(" --version, -v Print version");
165
176
  out();
166
- out("Run `promptimizer <command> --help` for command flags.");
167
- out();
168
177
  out("Examples");
178
+ out(" promptimizer");
169
179
  out(" promptimizer login --key pmz_live_…");
170
180
  out(" promptimizer connect baseten --key $BASETEN_API_KEY");
171
181
  out(' promptimizer chat "What is 17 * 24?"');
@@ -197,7 +207,7 @@ async function request(path, { method = "GET", body, apiKey, sessionId, gatewayU
197
207
  if (!response.ok) {
198
208
  const detail =
199
209
  typeof data === "object" && data && "detail" in data ? String(data.detail) : response.statusText;
200
- die(detail);
210
+ throw Object.assign(new Error(detail), { status: response.status });
201
211
  }
202
212
  return data;
203
213
  }
@@ -213,6 +223,46 @@ function requireKey(flags, config) {
213
223
  return String(apiKey);
214
224
  }
215
225
 
226
+ function authFromConfig(flags, config) {
227
+ const apiKey = flags.pmz || process.env.PROMPTIMIZER_API_KEY || config.apiKey;
228
+ const sessionId = apiKey ? undefined : config.sessionId;
229
+ if (!apiKey && !sessionId) {
230
+ throw new Error("Not signed in. Run promptimizer login --key pmz_live_…");
231
+ }
232
+ return { apiKey, sessionId };
233
+ }
234
+
235
+ async function loadSession(flags, config) {
236
+ const gatewayURL = gateway(flags, config);
237
+ const { apiKey, sessionId } = authFromConfig(flags, config);
238
+ return request("/v1/session", { gatewayURL, apiKey, sessionId });
239
+ }
240
+
241
+ function printMeta(result) {
242
+ const meta = result.promptimizer ?? {};
243
+ const saved = result.usage?.cost?.saved_usd;
244
+ const bits = [meta.model || result.model, meta.tier].filter(Boolean);
245
+ if (saved != null) bits.push(`saved ${usd(saved)}`);
246
+ if (meta.exact_cache_hit) bits.push(color(ANSI.green, "cache hit"));
247
+ else if (meta.prefix_cache_hit) bits.push(color(ANSI.green, "prefix cache"));
248
+ else if ("cache_hit" in meta) bits.push(color(ANSI.gray, "miss"));
249
+ if (meta.escalated) bits.push("escalated");
250
+ if (meta.latency_ms != null) bits.push(`${Math.round(Number(meta.latency_ms))}ms`);
251
+ out(color(ANSI.dim, ` ↳ ${bits.join(" · ")}`));
252
+ }
253
+
254
+ async function complete(flags, config, messages) {
255
+ const gatewayURL = gateway(flags, config);
256
+ const { apiKey, sessionId } = authFromConfig(flags, config);
257
+ return request("/v1/chat/completions", {
258
+ method: "POST",
259
+ gatewayURL,
260
+ apiKey,
261
+ sessionId,
262
+ body: { messages },
263
+ });
264
+ }
265
+
216
266
  async function cmdLogin(flags) {
217
267
  const apiKey = flags.key || flags.k || process.env.PROMPTIMIZER_API_KEY;
218
268
  if (!apiKey) die("Missing --key. Create one at /account.");
@@ -220,12 +270,12 @@ async function cmdLogin(flags) {
220
270
  const gatewayURL = gateway(flags, config);
221
271
  await request("/v1/session", { apiKey, gatewayURL });
222
272
  writeConfig({ ...config, gatewayURL, apiKey });
223
- out(`Saved ${gatewayURL}`);
273
+ out(`${color(ANSI.green, "✓")} Saved ${gatewayURL}`);
224
274
  }
225
275
 
226
276
  function cmdLogout() {
227
277
  rmSync(CONFIG_PATH, { force: true });
228
- out("Forgot saved key.");
278
+ out(`${color(ANSI.green, "✓")} Forgot saved key.`);
229
279
  }
230
280
 
231
281
  async function cmdProviders(flags) {
@@ -245,7 +295,9 @@ async function cmdConnect(flags, positional) {
245
295
  const gatewayURL = gateway(flags, config);
246
296
  const provider = String(flags.provider || positional[0] || "").trim();
247
297
  const baseURL = flags["base-url"] || flags.baseUrl;
248
- if (!provider && !baseURL) die("Usage: promptimizer connect <provider>\n promptimizer connect custom --base-url https://…");
298
+ if (!provider && !baseURL) {
299
+ die("Usage: promptimizer connect <provider>\n promptimizer connect custom --base-url https://…");
300
+ }
249
301
 
250
302
  const mock = provider === "simulator" || provider === "mock";
251
303
  let vendorKey = flags.key || flags.k;
@@ -279,53 +331,34 @@ async function cmdConnect(flags, positional) {
279
331
  });
280
332
 
281
333
  writeConfig({ ...config, gatewayURL, apiKey, sessionId: session.session_id });
282
- out(`${session.label} ${session.base_url}`);
283
- out(`${session.models.length} models · baseline ${session.baseline_model}`);
334
+ out(`${color(ANSI.green, "✓")} ${session.label} ${session.base_url}`);
335
+ out(` ${session.models.length} models · baseline ${session.baseline_model}`);
284
336
  }
285
337
 
286
338
  async function cmdChat(flags, positional) {
287
339
  const config = readConfig();
288
340
  const prompt = String(flags.prompt || positional.join(" ")).trim();
289
341
  if (!prompt) die('Usage: promptimizer chat "What is 17 * 24?"');
290
- const gatewayURL = gateway(flags, config);
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
- });
342
+ const result = await complete(flags, config, [{ role: "user", content: prompt }]);
302
343
  const text = result.choices?.[0]?.message?.content?.trim() ?? "";
303
- const meta = result.promptimizer ?? {};
304
- const saved = result.usage?.cost?.saved_usd;
305
344
  out();
306
345
  out(text);
307
346
  out();
308
- const bits = [meta.model || result.model, meta.tier].filter(Boolean);
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(" · "));
347
+ printMeta(result);
313
348
  out();
314
349
  }
315
350
 
316
351
  async function cmdModels(flags) {
317
352
  const config = readConfig();
318
353
  const gatewayURL = gateway(flags, config);
319
- const apiKey = process.env.PROMPTIMIZER_API_KEY || config.apiKey;
320
- const sessionId = apiKey ? undefined : config.sessionId;
321
- if (!apiKey && !sessionId) die("Run promptimizer login or promptimizer connect first.");
354
+ const { apiKey, sessionId } = authFromConfig(flags, config);
322
355
  const data = await request("/v1/models", { gatewayURL, apiKey, sessionId });
323
356
  const models = data.data ?? [];
324
357
  const width = Math.max(8, ...models.map((model) => String(model.tier).length));
325
358
  out();
326
359
  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}`);
360
+ const mark = model.id === data.baseline_model ? color(ANSI.yellow, " baseline") : "";
361
+ out(` ${color(ANSI.dim, String(model.tier).padEnd(width + 2))}${model.id}${mark}`);
329
362
  }
330
363
  out();
331
364
  }
@@ -335,7 +368,7 @@ async function cmdSavings(flags) {
335
368
  const apiKey = requireKey(flags, config);
336
369
  const data = await request("/v1/savings", { gatewayURL: gateway(flags, config), apiKey });
337
370
  out();
338
- out(`${usd(data.saved_usd)} saved`);
371
+ out(`${color(ANSI.bold, usd(data.saved_usd))} saved ${color(ANSI.dim, `(${Number(data.saved_pct || 0).toFixed(1)}%)`)}`);
339
372
  out();
340
373
  out(` routed ${usd(data.actual_usd)}`);
341
374
  out(` baseline ${usd(data.baseline_usd)}`);
@@ -345,10 +378,137 @@ async function cmdSavings(flags) {
345
378
  out();
346
379
  }
347
380
 
348
- function printVersion() {
349
- const here = dirname(fileURLToPath(import.meta.url));
350
- const pkg = JSON.parse(readFileSync(join(here, "../package.json"), "utf8"));
351
- out(pkg.version);
381
+ async function interactive(flags) {
382
+ if (!input.isTTY || !output.isTTY) {
383
+ help();
384
+ return;
385
+ }
386
+
387
+ const config = readConfig();
388
+ const version = (() => {
389
+ try {
390
+ const here = dirname(fileURLToPath(import.meta.url));
391
+ return JSON.parse(readFileSync(join(here, "../package.json"), "utf8")).version;
392
+ } catch {
393
+ return "0.0.0";
394
+ }
395
+ })();
396
+
397
+ let session = null;
398
+ try {
399
+ session = await loadSession(flags, config);
400
+ } catch (error) {
401
+ banner(null, version);
402
+ out(color(ANSI.yellow, ` ${error instanceof Error ? error.message : String(error)}`));
403
+ out(color(ANSI.dim, " Run: promptimizer login --key pmz_live_…"));
404
+ out();
405
+ return;
406
+ }
407
+
408
+ banner(session, version);
409
+
410
+ const rl = createInterface({ input, output, terminal: true });
411
+ const history = [];
412
+
413
+ const slashHelp = () => {
414
+ out();
415
+ out(` ${color(ANSI.bold, "/help")} this list`);
416
+ out(` ${color(ANSI.bold, "/models")} fleet + tiers`);
417
+ out(` ${color(ANSI.bold, "/savings")} account ledger`);
418
+ out(` ${color(ANSI.bold, "/session")} provider status`);
419
+ out(` ${color(ANSI.bold, "/clear")} clear chat history`);
420
+ out(` ${color(ANSI.bold, "/quit")} exit`);
421
+ out();
422
+ };
423
+
424
+ try {
425
+ while (true) {
426
+ let line;
427
+ try {
428
+ line = await rl.question(color(ANSI.cyan, "› "));
429
+ } catch {
430
+ break;
431
+ }
432
+ const trimmed = line.trim();
433
+ if (!trimmed) continue;
434
+
435
+ if (trimmed === "/quit" || trimmed === "/exit" || trimmed === "/q") break;
436
+
437
+ if (trimmed === "/help" || trimmed === "/?") {
438
+ slashHelp();
439
+ continue;
440
+ }
441
+
442
+ if (trimmed === "/clear") {
443
+ history.length = 0;
444
+ out(color(ANSI.dim, " History cleared."));
445
+ out();
446
+ continue;
447
+ }
448
+
449
+ if (trimmed === "/session") {
450
+ try {
451
+ session = await loadSession(flags, readConfig());
452
+ out();
453
+ out(` ${session.label} · ${session.mode}`);
454
+ out(` ${session.base_url}`);
455
+ out(` ${session.models.length} models · baseline ${session.baseline_model}`);
456
+ out();
457
+ } catch (error) {
458
+ out(color(ANSI.yellow, ` ${error instanceof Error ? error.message : String(error)}`));
459
+ }
460
+ continue;
461
+ }
462
+
463
+ if (trimmed === "/models") {
464
+ try {
465
+ await cmdModels(flags);
466
+ } catch (error) {
467
+ out(color(ANSI.yellow, ` ${error instanceof Error ? error.message : String(error)}`));
468
+ }
469
+ continue;
470
+ }
471
+
472
+ if (trimmed === "/savings") {
473
+ try {
474
+ await cmdSavings(flags);
475
+ } catch (error) {
476
+ out(color(ANSI.yellow, ` ${error instanceof Error ? error.message : String(error)}`));
477
+ }
478
+ continue;
479
+ }
480
+
481
+ if (trimmed.startsWith("/")) {
482
+ out(color(ANSI.dim, " Unknown command. Try /help"));
483
+ continue;
484
+ }
485
+
486
+ history.push({ role: "user", content: trimmed });
487
+ process.stdout.write(color(ANSI.dim, " … routing\r"));
488
+ try {
489
+ const result = await complete(flags, readConfig(), history);
490
+ process.stdout.write(" \r");
491
+ const text = result.choices?.[0]?.message?.content?.trim() ?? "";
492
+ history.push({ role: "assistant", content: text });
493
+ out();
494
+ out(color(ANSI.magenta, "✦"));
495
+ out(text);
496
+ out();
497
+ printMeta(result);
498
+ out();
499
+ } catch (error) {
500
+ process.stdout.write(" \r");
501
+ history.pop();
502
+ out(color(ANSI.yellow, ` ${error instanceof Error ? error.message : String(error)}`));
503
+ out();
504
+ }
505
+ }
506
+ } finally {
507
+ rl.close();
508
+ out();
509
+ out(color(ANSI.dim, " bye"));
510
+ out();
511
+ }
352
512
  }
353
513
 
354
514
  async function main() {
@@ -362,23 +522,29 @@ async function main() {
362
522
  else help();
363
523
  return;
364
524
  }
365
- if (positional.length === 0) {
366
- help();
525
+ if (flags.help || flags.h) {
526
+ if (positional[0]) commandHelp(positional[0]);
527
+ else help();
367
528
  return;
368
529
  }
369
530
 
531
+ if (positional.length === 0) {
532
+ return interactive(flags);
533
+ }
534
+
370
535
  const [command, ...rest] = positional;
371
- if (flags.help || flags.h) {
372
- commandHelp(command);
373
- return;
536
+ try {
537
+ if (command === "login") return await cmdLogin(flags);
538
+ if (command === "logout") return cmdLogout();
539
+ if (command === "providers") return await cmdProviders(flags);
540
+ if (command === "connect") return await cmdConnect(flags, rest);
541
+ if (command === "chat") return await cmdChat(flags, rest);
542
+ if (command === "models") return await cmdModels(flags);
543
+ if (command === "savings") return await cmdSavings(flags);
544
+ if (command === "repl" || command === "i") return await interactive(flags);
545
+ } catch (error) {
546
+ die(error instanceof Error ? error.message : String(error));
374
547
  }
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
548
  die(`Unknown command "${command}". Run promptimizer --help.`);
383
549
  }
384
550
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "promptimizer-cli",
3
- "version": "0.1.17",
4
- "description": "Login, connect a provider, route prompts, and read savings.",
3
+ "version": "0.1.19",
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"