@ordewell/cli 0.4.6 → 0.4.8

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.
Files changed (2) hide show
  1. package/dist/main.js +121 -77
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -205,15 +205,16 @@ var import_fs2 = require("fs");
205
205
  var import_os2 = require("os");
206
206
  var import_path2 = require("path");
207
207
  var import_http = __toESM(require("http"));
208
+ var import_core = require("@ordewell/core");
208
209
  var DEFAULT_PORT = 3742;
209
210
  var CONFIG_DIR = (0, import_path2.join)((0, import_os2.homedir)(), ".config", "ordewell");
210
211
  var LEGACY_PID_FILE = (0, import_path2.join)(CONFIG_DIR, "server.pid");
211
212
  function pidFileFor(port) {
212
213
  return port === DEFAULT_PORT ? LEGACY_PID_FILE : (0, import_path2.join)(CONFIG_DIR, `server-${port}.pid`);
213
214
  }
214
- function httpGet(url, timeoutMs = 2e3) {
215
+ function httpGet(url, timeoutMs = 2e3, headers = {}) {
215
216
  return new Promise((resolve3, reject) => {
216
- const req = import_http.default.get(url, { timeout: timeoutMs }, (res) => {
217
+ const req = import_http.default.get(url, { timeout: timeoutMs, headers }, (res) => {
217
218
  let data = "";
218
219
  res.on("data", (chunk) => {
219
220
  data += chunk.toString();
@@ -242,9 +243,14 @@ function getWebServerPath() {
242
243
  );
243
244
  }
244
245
  async function isDaemonRunning(port = DEFAULT_PORT) {
246
+ const token = (0, import_core.readDaemonToken)(port);
245
247
  try {
246
- const res = await httpGet(`http://127.0.0.1:${port}/api/workspaces`);
247
- return res.status === 200;
248
+ const res = await httpGet(
249
+ `http://127.0.0.1:${port}/api/workspaces`,
250
+ 2e3,
251
+ token ? { Authorization: (0, import_core.bearerHeaderValue)(token) } : {}
252
+ );
253
+ return res.status === 200 || res.status === 401;
248
254
  } catch {
249
255
  return false;
250
256
  }
@@ -292,6 +298,7 @@ async function stopDaemon(port = DEFAULT_PORT) {
292
298
  const pid = parseInt(pidStr, 10);
293
299
  if (!pid || isNaN(pid)) {
294
300
  (0, import_fs2.unlinkSync)(pidFile);
301
+ (0, import_core.clearDaemonToken)(port);
295
302
  return false;
296
303
  }
297
304
  process.kill(pid, "SIGTERM");
@@ -304,6 +311,7 @@ async function stopDaemon(port = DEFAULT_PORT) {
304
311
  (0, import_fs2.unlinkSync)(pidFile);
305
312
  } catch {
306
313
  }
314
+ (0, import_core.clearDaemonToken)(port);
307
315
  console.log("Server was not running (stale PID removed).");
308
316
  return true;
309
317
  }
@@ -344,22 +352,35 @@ function resolvePort(args = []) {
344
352
  // src/apiClient.ts
345
353
  var import_http2 = __toESM(require("http"));
346
354
  var import_ws = __toESM(require("ws"));
355
+ var import_core2 = require("@ordewell/core");
347
356
  var DEFAULT_HTTP_TIMEOUT_MS = 15 * 60 * 1e3;
348
357
  var ApiClient = class {
349
358
  port;
350
359
  constructor(port) {
351
360
  this.port = port || DEFAULT_PORT;
352
361
  }
362
+ /**
363
+ * Read at call time rather than at construction: a daemon restarted mid-run
364
+ * mints a new token, and a client built before that restart must pick it up.
365
+ * A missing file is not an error here — the daemon's own 401 names the path.
366
+ */
367
+ token() {
368
+ return (0, import_core2.readDaemonToken)(this.port);
369
+ }
353
370
  httpRequest(method, urlPath, body) {
354
371
  return new Promise((resolve3, reject) => {
355
372
  const url = new URL(urlPath, `http://127.0.0.1:${this.port}`);
356
373
  const configuredTimeout = Number.parseInt(process.env.ORDEWELL_HTTP_TIMEOUT_MS || "", 10);
374
+ const token = this.token();
357
375
  const options = {
358
376
  hostname: url.hostname,
359
377
  port: url.port,
360
378
  path: url.pathname + url.search,
361
379
  method,
362
- headers: body ? { "Content-Type": "application/json" } : {},
380
+ headers: {
381
+ ...body ? { "Content-Type": "application/json" } : {},
382
+ ...token ? { Authorization: (0, import_core2.bearerHeaderValue)(token) } : {}
383
+ },
363
384
  // Planner research on a real repository regularly exceeds two minutes.
364
385
  // The benchmark harness uses the same 15-minute default explicitly;
365
386
  // keep the env override for callers that need a tighter/looser bound.
@@ -387,7 +408,7 @@ var ApiClient = class {
387
408
  req.end();
388
409
  });
389
410
  }
390
- async generatePlan(goal, runners2, workspace, sessionId = `session-${Date.now()}`) {
411
+ async generatePlan(goal, runners2, workspace, sessionId = (0, import_core2.mintSessionId)()) {
391
412
  const res = await this.httpRequest("POST", `/api/plans/${sessionId}/generate`, {
392
413
  goal,
393
414
  runners: runners2 || void 0,
@@ -579,10 +600,38 @@ var ApiClient = class {
579
600
  }
580
601
  return res.data;
581
602
  }
603
+ /**
604
+ * The one place a session socket is built, so the token travels on both
605
+ * stream paths. It rides as a subprotocol rather than a query parameter, so
606
+ * it never reaches an access log.
607
+ */
608
+ openSessionSocket(sessionId) {
609
+ const token = this.token();
610
+ const socket = new import_ws.default(
611
+ `ws://127.0.0.1:${this.port}/ws/session/${sessionId}`,
612
+ token ? (0, import_core2.tokenSubprotocols)(token) : void 0
613
+ );
614
+ socket.on("unexpected-response", (_req, res) => {
615
+ let body = "";
616
+ res.on("data", (chunk) => {
617
+ body += chunk.toString();
618
+ });
619
+ res.on("end", () => {
620
+ let reason = "";
621
+ try {
622
+ reason = JSON.parse(body).error ?? "";
623
+ } catch {
624
+ reason = body.trim();
625
+ }
626
+ socket.emit("error", new Error(reason || `Unexpected server response: ${res.statusCode}`));
627
+ socket.emit("close", res.statusCode ?? 1006, Buffer.from(""));
628
+ });
629
+ });
630
+ return socket;
631
+ }
582
632
  streamExecution(sessionId, onEvent, onReady) {
583
633
  return new Promise((resolve3, reject) => {
584
- const wsUrl = `ws://127.0.0.1:${this.port}/ws/session/${sessionId}`;
585
- const socket = new import_ws.default(wsUrl);
634
+ const socket = this.openSessionSocket(sessionId);
586
635
  let resolved = false;
587
636
  let opened = false;
588
637
  socket.on("open", () => {
@@ -633,8 +682,7 @@ var ApiClient = class {
633
682
  * completion signal, not a terminal WS message like execution has.
634
683
  */
635
684
  streamPlanning(sessionId, onEvent) {
636
- const wsUrl = `ws://127.0.0.1:${this.port}/ws/session/${sessionId}`;
637
- const socket = new import_ws.default(wsUrl);
685
+ const socket = this.openSessionSocket(sessionId);
638
686
  socket.on("message", (data) => {
639
687
  try {
640
688
  onEvent(JSON.parse(data.toString()));
@@ -866,6 +914,7 @@ function isTransient(event) {
866
914
  }
867
915
 
868
916
  // src/commands/plan.ts
917
+ var import_core3 = require("@ordewell/core");
869
918
  function planTasks(plan) {
870
919
  return plan?.tasks || [];
871
920
  }
@@ -987,7 +1036,7 @@ async function handlePlan(subArgs, deps = {}) {
987
1036
  runners2 = runnerFlags;
988
1037
  }
989
1038
  console.error(`Generating plan for: "${goal}"...`);
990
- const sessionId = `session-${Date.now()}`;
1039
+ const sessionId = (0, import_core3.mintSessionId)();
991
1040
  const isTty = !!process.stderr.isTTY;
992
1041
  let lastLineLength = 0;
993
1042
  function renderStep(event) {
@@ -1318,10 +1367,10 @@ async function handleWeb(subArgs) {
1318
1367
  }
1319
1368
 
1320
1369
  // src/commands/models.ts
1321
- var import_core2 = require("@ordewell/core");
1370
+ var import_core5 = require("@ordewell/core");
1322
1371
 
1323
1372
  // src/commands/model.ts
1324
- var import_core = require("@ordewell/core");
1373
+ var import_core4 = require("@ordewell/core");
1325
1374
 
1326
1375
  // src/catalog.ts
1327
1376
  function normalizeCatalog(result) {
@@ -1425,7 +1474,7 @@ async function handleModel(subArgs, injectedApi) {
1425
1474
  if (!modelId) fail(USAGE);
1426
1475
  const settings = await api.getSettings();
1427
1476
  const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
1428
- const runner = (0, import_core.runnerForProvider)(provider);
1477
+ const runner = (0, import_core4.runnerForProvider)(provider);
1429
1478
  if (runner) {
1430
1479
  const known = (await fetchCatalog(api)).models.filter((m) => m.runners?.includes(runner));
1431
1480
  if (known.length > 0 && !known.some((m) => m.id === modelId)) {
@@ -1444,7 +1493,7 @@ async function showModels(api) {
1444
1493
  const [settings, catalog] = await Promise.all([api.getSettings(), fetchCatalog(api)]);
1445
1494
  const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
1446
1495
  const current = typeof settings.orchestratorModel === "string" ? settings.orchestratorModel : "";
1447
- const runner = (0, import_core.runnerForProvider)(provider);
1496
+ const runner = (0, import_core4.runnerForProvider)(provider);
1448
1497
  const options = runner ? catalog.models.filter((m) => m.runners?.includes(runner)) : catalog.orchestratorModels;
1449
1498
  console.log(`
1450
1499
  Planner model: ${current || "(backend default)"}`);
@@ -1478,8 +1527,8 @@ async function handleModels(subArgs) {
1478
1527
  const rest = subArgs.filter((_, i) => i !== setIdx && i !== setIdx + 1);
1479
1528
  return handleModel(["set", subArgs[setIdx + 1], ...rest]);
1480
1529
  }
1481
- const config = new import_core2.EnvConfig();
1482
- const providers = (0, import_core2.configuredProviders)(config);
1530
+ const config = new import_core5.EnvConfig();
1531
+ const providers = (0, import_core5.configuredProviders)(config);
1483
1532
  if (providers.length === 0) {
1484
1533
  console.error("No providers configured. Set an API key for a provider, e.g.:");
1485
1534
  console.error(" OPENROUTER_API_KEY=... (OpenRouter)");
@@ -1488,19 +1537,19 @@ async function handleModels(subArgs) {
1488
1537
  console.error(" OPENAI_COMPATIBLE_BASE_URL=... (self-hosted / OpenAI-compatible)");
1489
1538
  process.exit(1);
1490
1539
  }
1491
- const { apiKeys, baseUrls } = (0, import_core2.collectProviderCredentials)(config);
1540
+ const { apiKeys, baseUrls } = (0, import_core5.collectProviderCredentials)(config);
1492
1541
  let models;
1493
1542
  let errors;
1494
1543
  try {
1495
- const result = await (0, import_core2.fetchAllProviderModels)({ apiKeys, baseUrls });
1496
- models = (0, import_core2.toOrchestratorOptions)(result.models, import_core2.ORCHESTRATOR_SHORTCUTS);
1544
+ const result = await (0, import_core5.fetchAllProviderModels)({ apiKeys, baseUrls });
1545
+ models = (0, import_core5.toOrchestratorOptions)(result.models, import_core5.ORCHESTRATOR_SHORTCUTS);
1497
1546
  errors = result.errors;
1498
1547
  } catch (err) {
1499
1548
  console.error(`Failed to fetch models: ${err instanceof Error ? err.message : String(err)}`);
1500
1549
  process.exit(1);
1501
1550
  return;
1502
1551
  }
1503
- const providerLabel = (p) => (0, import_core2.getProviderMeta)(p)?.label ?? p;
1552
+ const providerLabel = (p) => (0, import_core5.getProviderMeta)(p)?.label ?? p;
1504
1553
  console.log(
1505
1554
  `
1506
1555
  Available models (${models.length}) across ${providers.length} configured provider(s):`
@@ -1617,9 +1666,9 @@ function handleSetup(subArgs) {
1617
1666
  }
1618
1667
 
1619
1668
  // src/commands/plugins.ts
1620
- var import_core3 = require("@ordewell/core");
1669
+ var import_core6 = require("@ordewell/core");
1621
1670
  function handlePlugins(subArgs) {
1622
- const registry = new import_core3.RunnerRegistry();
1671
+ const registry = new import_core6.RunnerRegistry();
1623
1672
  registry.loadUserPlugins();
1624
1673
  const sub = subArgs[0];
1625
1674
  if (sub === "list" || !sub) {
@@ -1655,13 +1704,8 @@ ${plugins.length} plugin(s):
1655
1704
  process.exit(1);
1656
1705
  }
1657
1706
  try {
1658
- let manifest;
1659
- if (source.startsWith("github:") || source.startsWith("https://github.com/")) {
1660
- const url = source.startsWith("github:") ? `https://github.com/${source.slice(7)}.git` : source;
1661
- manifest = registry.installFromGit(url);
1662
- } else {
1663
- manifest = registry.installFromPath(source);
1664
- }
1707
+ const resolved = (0, import_core6.classifyPluginSource)(source);
1708
+ const manifest = resolved.kind === "git" ? registry.installFromGit(resolved.url) : registry.installFromPath(resolved.path);
1665
1709
  console.log(`Installed plugin: ${manifest.name} v${manifest.version}`);
1666
1710
  } catch (err) {
1667
1711
  console.error(`Install failed: ${err.message}`);
@@ -2132,7 +2176,7 @@ async function handleSessions(subArgs, injectedApi) {
2132
2176
  }
2133
2177
 
2134
2178
  // src/commands/planner.ts
2135
- var import_core4 = require("@ordewell/core");
2179
+ var import_core7 = require("@ordewell/core");
2136
2180
 
2137
2181
  // src/plannerModelSwitch.ts
2138
2182
  function describePlannerSwitch(settings, provider) {
@@ -2151,7 +2195,7 @@ function rememberedModelFor(settings, provider) {
2151
2195
  }
2152
2196
 
2153
2197
  // src/commands/planner.ts
2154
- var KNOWN_PROVIDERS = Object.keys(import_core4.ALL_PROVIDERS);
2198
+ var KNOWN_PROVIDERS = Object.keys(import_core7.ALL_PROVIDERS);
2155
2199
  async function handlePlanner(subArgs, injectedApi) {
2156
2200
  const api = await connect(subArgs, injectedApi);
2157
2201
  const [wanted] = positionals(subArgs);
@@ -2166,13 +2210,13 @@ async function handlePlanner(subArgs, injectedApi) {
2166
2210
  const settings = await persistEnv(api, { AI_PROVIDER: provider });
2167
2211
  const recall2 = describePlannerSwitch(settings, provider);
2168
2212
  writeEnv({ ORCHESTRATOR_MODEL: recall2.model, ORDEWELL_PLANNER_EFFORT: recall2.effort });
2169
- const meta = import_core4.ALL_PROVIDERS[provider];
2213
+ const meta = import_core7.ALL_PROVIDERS[provider];
2170
2214
  const catalog = await fetchCatalog(api);
2171
2215
  console.log(
2172
- (0, import_core4.isCliProvider)(provider) ? `Planning with ${meta.label} \u2014 no API key needed, it uses that agent's own subscription.` : `Planner set to ${meta.label}.`
2216
+ (0, import_core7.isCliProvider)(provider) ? `Planning with ${meta.label} \u2014 no API key needed, it uses that agent's own subscription.` : `Planner set to ${meta.label}.`
2173
2217
  );
2174
2218
  console.log(plannerModelLine(recall2));
2175
- if (!(0, import_core4.isCliProvider)(provider) && !catalog.providers.includes(provider)) {
2219
+ if (!(0, import_core7.isCliProvider)(provider) && !catalog.providers.includes(provider)) {
2176
2220
  console.log(` No ${meta.apiKeyEnvVar} configured yet \u2014 set one with \`ordewell key set ${provider} <key>\`.`);
2177
2221
  }
2178
2222
  }
@@ -2190,15 +2234,15 @@ async function showPlanners(api) {
2190
2234
  const [settings, catalog] = await Promise.all([api.getSettings(), fetchCatalog(api)]);
2191
2235
  const current = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
2192
2236
  console.log("\nCoding agents (no API key \u2014 they use their own subscription)");
2193
- for (const id of import_core4.CLI_PROVIDERS) {
2194
- const runner = (0, import_core4.runnerForProvider)(id);
2237
+ for (const id of import_core7.CLI_PROVIDERS) {
2238
+ const runner = (0, import_core7.runnerForProvider)(id);
2195
2239
  const discovered = runner ? catalog.models.some((m) => m.runners?.includes(runner)) : false;
2196
- console.log(` ${marker(id === current)} ${id.padEnd(14)} ${import_core4.ALL_PROVIDERS[id].label}${discovered ? "" : " (not detected)"}`);
2240
+ console.log(` ${marker(id === current)} ${id.padEnd(14)} ${import_core7.ALL_PROVIDERS[id].label}${discovered ? "" : " (not detected)"}`);
2197
2241
  }
2198
2242
  console.log("\nAPI providers");
2199
- for (const id of import_core4.PROVIDER_PRIORITY) {
2243
+ for (const id of import_core7.PROVIDER_PRIORITY) {
2200
2244
  const configured = catalog.providers.includes(id);
2201
- const meta = import_core4.ALL_PROVIDERS[id];
2245
+ const meta = import_core7.ALL_PROVIDERS[id];
2202
2246
  console.log(` ${marker(id === current)} ${id.padEnd(14)} ${meta.label}${configured ? "" : ` (no ${meta.apiKeyEnvVar})`}`);
2203
2247
  }
2204
2248
  console.log(`
@@ -2215,7 +2259,7 @@ async function handlePlannerEffort(subArgs, injectedApi) {
2215
2259
  const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
2216
2260
  const modelId = typeof settings.orchestratorModel === "string" ? settings.orchestratorModel : "";
2217
2261
  const currentEffort = typeof settings.plannerThinkingEffort === "string" ? settings.plannerThinkingEffort : "";
2218
- const runner = (0, import_core4.runnerForProvider)(provider);
2262
+ const runner = (0, import_core7.runnerForProvider)(provider);
2219
2263
  if (!runner) {
2220
2264
  fail(
2221
2265
  "Thinking effort applies to a coding-agent planner.",
@@ -2255,7 +2299,7 @@ async function handlePlannerEffort(subArgs, injectedApi) {
2255
2299
  }
2256
2300
 
2257
2301
  // src/commands/key.ts
2258
- var import_core5 = require("@ordewell/core");
2302
+ var import_core8 = require("@ordewell/core");
2259
2303
  var USAGE4 = "Usage: ordewell key [list | set <provider> <key>]";
2260
2304
  async function handleKey(subArgs, injectedApi) {
2261
2305
  const [action, provider, ...keyParts] = positionals(subArgs);
@@ -2266,8 +2310,8 @@ async function handleKey(subArgs, injectedApi) {
2266
2310
  if (action !== "set") {
2267
2311
  const catalog = await fetchCatalog(api);
2268
2312
  console.log("\nAPI provider keys\n");
2269
- for (const id2 of import_core5.PROVIDER_PRIORITY) {
2270
- const meta2 = import_core5.ALL_PROVIDERS[id2];
2313
+ for (const id2 of import_core8.PROVIDER_PRIORITY) {
2314
+ const meta2 = import_core8.ALL_PROVIDERS[id2];
2271
2315
  const configured = catalog.providers.includes(id2);
2272
2316
  console.log(` ${configured ? "\u2713" : " "} ${id2.padEnd(14)} ${meta2.apiKeyEnvVar.padEnd(28)} ${meta2.label}`);
2273
2317
  }
@@ -2277,7 +2321,7 @@ async function handleKey(subArgs, injectedApi) {
2277
2321
  }
2278
2322
  if (!provider) fail(USAGE4);
2279
2323
  const id = provider.toLowerCase();
2280
- const meta = import_core5.ALL_PROVIDERS[id];
2324
+ const meta = import_core8.ALL_PROVIDERS[id];
2281
2325
  if (!meta) {
2282
2326
  fail(`Unknown provider: ${provider}`, "Run `ordewell key` to see the list.");
2283
2327
  }
@@ -2357,7 +2401,7 @@ async function handleApprove(subArgs, injectedApi) {
2357
2401
  // src/tui/terminalLauncher.ts
2358
2402
  var import_child_process3 = require("child_process");
2359
2403
  var import_util = require("util");
2360
- var import_core6 = require("@ordewell/core");
2404
+ var import_core9 = require("@ordewell/core");
2361
2405
  var execFileAsync = (0, import_util.promisify)(import_child_process3.execFile);
2362
2406
  var defaultExecFile = async (command, args) => {
2363
2407
  const { stdout, stderr } = await execFileAsync(command, args);
@@ -2377,16 +2421,16 @@ var defaultWhich = (bin) => {
2377
2421
  async function openTaskTerminal(port, planSessionId, taskId, deps = {}) {
2378
2422
  const platform = deps.platform ?? process.platform;
2379
2423
  const env = deps.env ?? process.env;
2380
- const hasTmuxImpl = deps.hasTmuxImpl ?? import_core6.hasTmux;
2424
+ const hasTmuxImpl = deps.hasTmuxImpl ?? import_core9.hasTmux;
2381
2425
  const execFileImpl = deps.execFileImpl ?? defaultExecFile;
2382
2426
  const spawnImpl = deps.spawnImpl ?? defaultSpawn;
2383
2427
  const which = deps.which ?? defaultWhich;
2384
2428
  if (!hasTmuxImpl()) {
2385
2429
  return { ok: false, message: "tmux is not installed \u2014 install it to get a real terminal per task." };
2386
2430
  }
2387
- const session = (0, import_core6.tmuxSessionName)(port);
2388
- const socket = (0, import_core6.tmuxSocketName)(port);
2389
- const base = (0, import_core6.tmuxWindowName)(taskId, planSessionId);
2431
+ const session = (0, import_core9.tmuxSessionName)(port);
2432
+ const socket = (0, import_core9.tmuxSocketName)(port);
2433
+ const base = (0, import_core9.tmuxWindowName)(taskId, planSessionId);
2390
2434
  let windows;
2391
2435
  try {
2392
2436
  const { stdout } = await execFileImpl("tmux", ["-L", socket, "list-windows", "-t", session, "-F", "#{window_name}"]);
@@ -2680,7 +2724,7 @@ Dependencies \xB7 #${task.order} ${task.title}
2680
2724
  }
2681
2725
 
2682
2726
  // src/tui/index.ts
2683
- var import_core10 = require("@ordewell/core");
2727
+ var import_core13 = require("@ordewell/core");
2684
2728
 
2685
2729
  // src/tui/ansi.ts
2686
2730
  var import_string_width = __toESM(require("string-width"));
@@ -3212,7 +3256,7 @@ function initialState(overrides = {}) {
3212
3256
  }
3213
3257
 
3214
3258
  // src/tui/layout.ts
3215
- var import_core7 = require("@ordewell/core");
3259
+ var import_core10 = require("@ordewell/core");
3216
3260
  var CHROME_ROWS = 4;
3217
3261
  function chatInputWrap(state) {
3218
3262
  const active = state.focus === "chat" && !state.overlay;
@@ -3348,9 +3392,9 @@ function chatScrollMax(state) {
3348
3392
  }
3349
3393
  function plannerSetup(state) {
3350
3394
  const provider = state.plannerProvider;
3351
- const registration = import_core7.ALL_PROVIDERS[provider];
3395
+ const registration = import_core10.ALL_PROVIDERS[provider];
3352
3396
  if (!registration) return { label: "not set", ready: false };
3353
- const runner = (0, import_core7.runnerForProvider)(provider);
3397
+ const runner = (0, import_core10.runnerForProvider)(provider);
3354
3398
  const ready = runner ? state.runners.some((r) => r.id === runner) : state.configuredProviders.includes(provider);
3355
3399
  return { label: registration.label, ready };
3356
3400
  }
@@ -3844,9 +3888,9 @@ function frame(title, body, rows, cols) {
3844
3888
  }
3845
3889
 
3846
3890
  // src/tui/reducer.ts
3847
- var import_core8 = require("@ordewell/core");
3891
+ var import_core11 = require("@ordewell/core");
3848
3892
  var import_plan_utils3 = require("@ordewell/core/plan-utils");
3849
- var KNOWN_PROVIDERS2 = Object.keys(import_core8.ALL_PROVIDERS);
3893
+ var KNOWN_PROVIDERS2 = Object.keys(import_core11.ALL_PROVIDERS);
3850
3894
  var DEFAULT_EFFORT = "__runner_default__";
3851
3895
  function step(state, effects = []) {
3852
3896
  return { state, effects };
@@ -4115,11 +4159,11 @@ function modelDetail(m) {
4115
4159
  function providerErrorHint(state) {
4116
4160
  const failed = Object.keys(state.providerErrors ?? {});
4117
4161
  if (failed.length === 0) return void 0;
4118
- const names = failed.map((p) => import_core8.ALL_PROVIDERS[p]?.label ?? p);
4162
+ const names = failed.map((p) => import_core11.ALL_PROVIDERS[p]?.label ?? p);
4119
4163
  return `\u26A0 Unavailable (key or fetch failed): ${names.join(", ")} \u2014 showing working providers only.`;
4120
4164
  }
4121
4165
  function plannerModelItems(state) {
4122
- const runner = (0, import_core8.runnerForProvider)(state.plannerProvider);
4166
+ const runner = (0, import_core11.runnerForProvider)(state.plannerProvider);
4123
4167
  if (!runner) return state.orchestratorModels.map((m) => ({ id: m.id, label: m.label, detail: modelDetail(m) }));
4124
4168
  const items = state.models.filter((m) => m.runners?.includes(runner)).map((m) => ({
4125
4169
  id: m.id,
@@ -4130,7 +4174,7 @@ function plannerModelItems(state) {
4130
4174
  return items.length > 0 ? items : [{ id: "", label: `No ${runner} models discovered yet`, detail: "run /refresh", disabled: true }];
4131
4175
  }
4132
4176
  function plannerEffortItems(state) {
4133
- const runner = (0, import_core8.runnerForProvider)(state.plannerProvider);
4177
+ const runner = (0, import_core11.runnerForProvider)(state.plannerProvider);
4134
4178
  if (!runner) {
4135
4179
  return [{ id: "", label: "Thinking effort applies to a coding-agent planner", detail: "switch with /planner", disabled: true }];
4136
4180
  }
@@ -4768,7 +4812,7 @@ function newSession(state) {
4768
4812
  }
4769
4813
  function setModel(state, args) {
4770
4814
  if (args[0] === "set" && args[1]) {
4771
- const runner = (0, import_core8.runnerForProvider)(state.plannerProvider);
4815
+ const runner = (0, import_core11.runnerForProvider)(state.plannerProvider);
4772
4816
  const known = runner ? modelsForRunner(state.models, runner) : [];
4773
4817
  if (runner && known.length > 0 && !known.some((m) => m.id === args[1])) {
4774
4818
  return fail2(state, `${args[1]} was not discovered for ${runner}.`);
@@ -4790,21 +4834,21 @@ function setModel(state, args) {
4790
4834
  }
4791
4835
  function plannerItems(state) {
4792
4836
  const installed = new Set(state.runners.map((r) => r.id));
4793
- const agents = import_core8.CLI_PROVIDERS.map((id) => {
4794
- const runner = (0, import_core8.runnerForProvider)(id);
4837
+ const agents = import_core11.CLI_PROVIDERS.map((id) => {
4838
+ const runner = (0, import_core11.runnerForProvider)(id);
4795
4839
  const ready = installed.has(runner);
4796
4840
  return {
4797
4841
  id,
4798
- label: import_core8.ALL_PROVIDERS[id].label,
4842
+ label: import_core11.ALL_PROVIDERS[id].label,
4799
4843
  detail: ready ? "coding agent \xB7 no API key needed" : "CLI not installed or not on PATH",
4800
4844
  selected: state.plannerProvider === id,
4801
4845
  disabled: !ready
4802
4846
  };
4803
4847
  });
4804
- const vendors = import_core8.PROVIDER_PRIORITY.filter((id) => state.configuredProviders.includes(id)).map((id) => ({
4848
+ const vendors = import_core11.PROVIDER_PRIORITY.filter((id) => state.configuredProviders.includes(id)).map((id) => ({
4805
4849
  id,
4806
- label: import_core8.ALL_PROVIDERS[id].label,
4807
- detail: import_core8.ALL_PROVIDERS[id].apiKeyEnvVar,
4850
+ label: import_core11.ALL_PROVIDERS[id].label,
4851
+ detail: import_core11.ALL_PROVIDERS[id].apiKeyEnvVar,
4808
4852
  selected: state.plannerProvider === id
4809
4853
  }));
4810
4854
  return vendors.length > 0 ? [...agents, ...vendors] : [...agents, { id: "", label: "No API providers configured", detail: "add a key with /key", disabled: true }];
@@ -4862,16 +4906,16 @@ function setKey(state, args) {
4862
4906
  }
4863
4907
  return step(state, [{ type: "setApiKey", provider, key: args.slice(2).join(" ") }]);
4864
4908
  }
4865
- const items = import_core8.PROVIDER_PRIORITY.map((id) => ({
4909
+ const items = import_core11.PROVIDER_PRIORITY.map((id) => ({
4866
4910
  id,
4867
- label: import_core8.ALL_PROVIDERS[id].label,
4868
- detail: import_core8.ALL_PROVIDERS[id].apiKeyEnvVar,
4911
+ label: import_core11.ALL_PROVIDERS[id].label,
4912
+ detail: import_core11.ALL_PROVIDERS[id].apiKeyEnvVar,
4869
4913
  selected: state.configuredProviders.includes(id)
4870
4914
  }));
4871
4915
  return step({ ...state, overlay: { kind: "picker", picker: picker("API provider key", items, { kind: "set-key" }) } });
4872
4916
  }
4873
4917
  function keyPrompt(provider) {
4874
- const meta = import_core8.ALL_PROVIDERS[provider];
4918
+ const meta = import_core11.ALL_PROVIDERS[provider];
4875
4919
  return {
4876
4920
  kind: "prompt",
4877
4921
  title: `${meta.label} API key`,
@@ -5229,7 +5273,7 @@ function createApp(deps) {
5229
5273
 
5230
5274
  // src/tui/effects.ts
5231
5275
  var import_child_process4 = require("child_process");
5232
- var import_core9 = require("@ordewell/core");
5276
+ var import_core12 = require("@ordewell/core");
5233
5277
  var import_plan_utils4 = require("@ordewell/core/plan-utils");
5234
5278
  var ConversationQueue = class {
5235
5279
  tail = Promise.resolve();
@@ -5396,7 +5440,7 @@ async function perform(effect, deps) {
5396
5440
  dispatch({ type: "notice", message: `Orchestrator model set to ${effect.modelId}.` });
5397
5441
  return;
5398
5442
  case "setPlanner": {
5399
- const meta = import_core9.ALL_PROVIDERS[effect.provider];
5443
+ const meta = import_core12.ALL_PROVIDERS[effect.provider];
5400
5444
  if (!meta) throw new Error(`Unknown planner: ${effect.provider}`);
5401
5445
  const env = { AI_PROVIDER: effect.provider };
5402
5446
  const settings = await api.updateSettings({ env });
@@ -5408,7 +5452,7 @@ async function perform(effect, deps) {
5408
5452
  });
5409
5453
  dispatch({
5410
5454
  type: "notice",
5411
- message: `${(0, import_core9.isCliProvider)(effect.provider) ? `Planning with ${meta.label} \u2014 no API key needed.` : `Planner set to ${meta.label}.`} ${plannerModelNotice(recall2)}`
5455
+ message: `${(0, import_core12.isCliProvider)(effect.provider) ? `Planning with ${meta.label} \u2014 no API key needed.` : `Planner set to ${meta.label}.`} ${plannerModelNotice(recall2)}`
5412
5456
  });
5413
5457
  await loadModels(deps);
5414
5458
  return;
@@ -5420,7 +5464,7 @@ async function perform(effect, deps) {
5420
5464
  dispatch({ type: "notice", message: `Planner effort set to ${effect.effort || "the runner default"}.` });
5421
5465
  return;
5422
5466
  case "setApiKey": {
5423
- const meta = import_core9.ALL_PROVIDERS[effect.provider];
5467
+ const meta = import_core12.ALL_PROVIDERS[effect.provider];
5424
5468
  if (!meta) throw new Error(`Unknown provider: ${effect.provider}`);
5425
5469
  await api.updateSettings({ env: { [meta.apiKeyEnvVar]: effect.key } });
5426
5470
  deps.setEnvVar(meta.apiKeyEnvVar, effect.key);
@@ -5508,7 +5552,7 @@ function osc52(text) {
5508
5552
  function copySelection(deps, text) {
5509
5553
  if (!text) return;
5510
5554
  const lines = text.split("\n").length;
5511
- const command = (0, import_core9.clipboardCopyCommand)(deps.hasBin);
5555
+ const command = (0, import_core12.clipboardCopyCommand)(deps.hasBin);
5512
5556
  if (command) {
5513
5557
  try {
5514
5558
  (deps.pipeToClipboard ?? pipeToClipboardDefault)(command, text);
@@ -5982,7 +6026,7 @@ async function handleTui(subArgs) {
5982
6026
  }
5983
6027
  const workspace = flag(subArgs, "--workspace") || process.cwd();
5984
6028
  try {
5985
- (0, import_core10.assertWorkspaceExists)(workspace);
6029
+ (0, import_core13.assertWorkspaceExists)(workspace);
5986
6030
  } catch {
5987
6031
  console.error(
5988
6032
  `Workspace "${workspace}" does not exist.
@@ -6007,7 +6051,7 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
6007
6051
  conversationQueue,
6008
6052
  port,
6009
6053
  dispatch: (action) => app.dispatch(action),
6010
- newSessionId: () => `session-${Date.now()}`,
6054
+ newSessionId: () => (0, import_core13.mintSessionId)(),
6011
6055
  setEnvVar: (key, value) => {
6012
6056
  writeEnvVar(findEnvFile(), key, value);
6013
6057
  process.env[key] = value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ordewell/cli",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "Ordewell CLI and TUI — plan, run and verify coding-agent tasks from the terminal.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Alessandro Costanzo Ciano",