@ordewell/cli 0.4.7 → 0.4.9

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 +128 -79
  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) {
@@ -1360,7 +1409,8 @@ function normalizeCatalog(result) {
1360
1409
  (modes ?? []).map((m) => ({
1361
1410
  id: String(m.id),
1362
1411
  label: String(m.label ?? m.id),
1363
- description: m.description ? String(m.description) : void 0
1412
+ description: m.description ? String(m.description) : void 0,
1413
+ autonomous: m.autonomous ? true : void 0
1364
1414
  }))
1365
1415
  ])
1366
1416
  )
@@ -1425,7 +1475,7 @@ async function handleModel(subArgs, injectedApi) {
1425
1475
  if (!modelId) fail(USAGE);
1426
1476
  const settings = await api.getSettings();
1427
1477
  const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
1428
- const runner = (0, import_core.runnerForProvider)(provider);
1478
+ const runner = (0, import_core4.runnerForProvider)(provider);
1429
1479
  if (runner) {
1430
1480
  const known = (await fetchCatalog(api)).models.filter((m) => m.runners?.includes(runner));
1431
1481
  if (known.length > 0 && !known.some((m) => m.id === modelId)) {
@@ -1444,7 +1494,7 @@ async function showModels(api) {
1444
1494
  const [settings, catalog] = await Promise.all([api.getSettings(), fetchCatalog(api)]);
1445
1495
  const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
1446
1496
  const current = typeof settings.orchestratorModel === "string" ? settings.orchestratorModel : "";
1447
- const runner = (0, import_core.runnerForProvider)(provider);
1497
+ const runner = (0, import_core4.runnerForProvider)(provider);
1448
1498
  const options = runner ? catalog.models.filter((m) => m.runners?.includes(runner)) : catalog.orchestratorModels;
1449
1499
  console.log(`
1450
1500
  Planner model: ${current || "(backend default)"}`);
@@ -1478,8 +1528,8 @@ async function handleModels(subArgs) {
1478
1528
  const rest = subArgs.filter((_, i) => i !== setIdx && i !== setIdx + 1);
1479
1529
  return handleModel(["set", subArgs[setIdx + 1], ...rest]);
1480
1530
  }
1481
- const config = new import_core2.EnvConfig();
1482
- const providers = (0, import_core2.configuredProviders)(config);
1531
+ const config = new import_core5.EnvConfig();
1532
+ const providers = (0, import_core5.configuredProviders)(config);
1483
1533
  if (providers.length === 0) {
1484
1534
  console.error("No providers configured. Set an API key for a provider, e.g.:");
1485
1535
  console.error(" OPENROUTER_API_KEY=... (OpenRouter)");
@@ -1488,19 +1538,19 @@ async function handleModels(subArgs) {
1488
1538
  console.error(" OPENAI_COMPATIBLE_BASE_URL=... (self-hosted / OpenAI-compatible)");
1489
1539
  process.exit(1);
1490
1540
  }
1491
- const { apiKeys, baseUrls } = (0, import_core2.collectProviderCredentials)(config);
1541
+ const { apiKeys, baseUrls } = (0, import_core5.collectProviderCredentials)(config);
1492
1542
  let models;
1493
1543
  let errors;
1494
1544
  try {
1495
- const result = await (0, import_core2.fetchAllProviderModels)({ apiKeys, baseUrls });
1496
- models = (0, import_core2.toOrchestratorOptions)(result.models, import_core2.ORCHESTRATOR_SHORTCUTS);
1545
+ const result = await (0, import_core5.fetchAllProviderModels)({ apiKeys, baseUrls });
1546
+ models = (0, import_core5.toOrchestratorOptions)(result.models, import_core5.ORCHESTRATOR_SHORTCUTS);
1497
1547
  errors = result.errors;
1498
1548
  } catch (err) {
1499
1549
  console.error(`Failed to fetch models: ${err instanceof Error ? err.message : String(err)}`);
1500
1550
  process.exit(1);
1501
1551
  return;
1502
1552
  }
1503
- const providerLabel = (p) => (0, import_core2.getProviderMeta)(p)?.label ?? p;
1553
+ const providerLabel = (p) => (0, import_core5.getProviderMeta)(p)?.label ?? p;
1504
1554
  console.log(
1505
1555
  `
1506
1556
  Available models (${models.length}) across ${providers.length} configured provider(s):`
@@ -1617,9 +1667,9 @@ function handleSetup(subArgs) {
1617
1667
  }
1618
1668
 
1619
1669
  // src/commands/plugins.ts
1620
- var import_core3 = require("@ordewell/core");
1670
+ var import_core6 = require("@ordewell/core");
1621
1671
  function handlePlugins(subArgs) {
1622
- const registry = new import_core3.RunnerRegistry();
1672
+ const registry = new import_core6.RunnerRegistry();
1623
1673
  registry.loadUserPlugins();
1624
1674
  const sub = subArgs[0];
1625
1675
  if (sub === "list" || !sub) {
@@ -1655,13 +1705,8 @@ ${plugins.length} plugin(s):
1655
1705
  process.exit(1);
1656
1706
  }
1657
1707
  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
- }
1708
+ const resolved = (0, import_core6.classifyPluginSource)(source);
1709
+ const manifest = resolved.kind === "git" ? registry.installFromGit(resolved.url) : registry.installFromPath(resolved.path);
1665
1710
  console.log(`Installed plugin: ${manifest.name} v${manifest.version}`);
1666
1711
  } catch (err) {
1667
1712
  console.error(`Install failed: ${err.message}`);
@@ -2132,7 +2177,7 @@ async function handleSessions(subArgs, injectedApi) {
2132
2177
  }
2133
2178
 
2134
2179
  // src/commands/planner.ts
2135
- var import_core4 = require("@ordewell/core");
2180
+ var import_core7 = require("@ordewell/core");
2136
2181
 
2137
2182
  // src/plannerModelSwitch.ts
2138
2183
  function describePlannerSwitch(settings, provider) {
@@ -2151,7 +2196,7 @@ function rememberedModelFor(settings, provider) {
2151
2196
  }
2152
2197
 
2153
2198
  // src/commands/planner.ts
2154
- var KNOWN_PROVIDERS = Object.keys(import_core4.ALL_PROVIDERS);
2199
+ var KNOWN_PROVIDERS = Object.keys(import_core7.ALL_PROVIDERS);
2155
2200
  async function handlePlanner(subArgs, injectedApi) {
2156
2201
  const api = await connect(subArgs, injectedApi);
2157
2202
  const [wanted] = positionals(subArgs);
@@ -2166,13 +2211,13 @@ async function handlePlanner(subArgs, injectedApi) {
2166
2211
  const settings = await persistEnv(api, { AI_PROVIDER: provider });
2167
2212
  const recall2 = describePlannerSwitch(settings, provider);
2168
2213
  writeEnv({ ORCHESTRATOR_MODEL: recall2.model, ORDEWELL_PLANNER_EFFORT: recall2.effort });
2169
- const meta = import_core4.ALL_PROVIDERS[provider];
2214
+ const meta = import_core7.ALL_PROVIDERS[provider];
2170
2215
  const catalog = await fetchCatalog(api);
2171
2216
  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}.`
2217
+ (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
2218
  );
2174
2219
  console.log(plannerModelLine(recall2));
2175
- if (!(0, import_core4.isCliProvider)(provider) && !catalog.providers.includes(provider)) {
2220
+ if (!(0, import_core7.isCliProvider)(provider) && !catalog.providers.includes(provider)) {
2176
2221
  console.log(` No ${meta.apiKeyEnvVar} configured yet \u2014 set one with \`ordewell key set ${provider} <key>\`.`);
2177
2222
  }
2178
2223
  }
@@ -2190,15 +2235,15 @@ async function showPlanners(api) {
2190
2235
  const [settings, catalog] = await Promise.all([api.getSettings(), fetchCatalog(api)]);
2191
2236
  const current = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
2192
2237
  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);
2238
+ for (const id of import_core7.CLI_PROVIDERS) {
2239
+ const runner = (0, import_core7.runnerForProvider)(id);
2195
2240
  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)"}`);
2241
+ console.log(` ${marker(id === current)} ${id.padEnd(14)} ${import_core7.ALL_PROVIDERS[id].label}${discovered ? "" : " (not detected)"}`);
2197
2242
  }
2198
2243
  console.log("\nAPI providers");
2199
- for (const id of import_core4.PROVIDER_PRIORITY) {
2244
+ for (const id of import_core7.PROVIDER_PRIORITY) {
2200
2245
  const configured = catalog.providers.includes(id);
2201
- const meta = import_core4.ALL_PROVIDERS[id];
2246
+ const meta = import_core7.ALL_PROVIDERS[id];
2202
2247
  console.log(` ${marker(id === current)} ${id.padEnd(14)} ${meta.label}${configured ? "" : ` (no ${meta.apiKeyEnvVar})`}`);
2203
2248
  }
2204
2249
  console.log(`
@@ -2215,7 +2260,7 @@ async function handlePlannerEffort(subArgs, injectedApi) {
2215
2260
  const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
2216
2261
  const modelId = typeof settings.orchestratorModel === "string" ? settings.orchestratorModel : "";
2217
2262
  const currentEffort = typeof settings.plannerThinkingEffort === "string" ? settings.plannerThinkingEffort : "";
2218
- const runner = (0, import_core4.runnerForProvider)(provider);
2263
+ const runner = (0, import_core7.runnerForProvider)(provider);
2219
2264
  if (!runner) {
2220
2265
  fail(
2221
2266
  "Thinking effort applies to a coding-agent planner.",
@@ -2255,7 +2300,7 @@ async function handlePlannerEffort(subArgs, injectedApi) {
2255
2300
  }
2256
2301
 
2257
2302
  // src/commands/key.ts
2258
- var import_core5 = require("@ordewell/core");
2303
+ var import_core8 = require("@ordewell/core");
2259
2304
  var USAGE4 = "Usage: ordewell key [list | set <provider> <key>]";
2260
2305
  async function handleKey(subArgs, injectedApi) {
2261
2306
  const [action, provider, ...keyParts] = positionals(subArgs);
@@ -2266,8 +2311,8 @@ async function handleKey(subArgs, injectedApi) {
2266
2311
  if (action !== "set") {
2267
2312
  const catalog = await fetchCatalog(api);
2268
2313
  console.log("\nAPI provider keys\n");
2269
- for (const id2 of import_core5.PROVIDER_PRIORITY) {
2270
- const meta2 = import_core5.ALL_PROVIDERS[id2];
2314
+ for (const id2 of import_core8.PROVIDER_PRIORITY) {
2315
+ const meta2 = import_core8.ALL_PROVIDERS[id2];
2271
2316
  const configured = catalog.providers.includes(id2);
2272
2317
  console.log(` ${configured ? "\u2713" : " "} ${id2.padEnd(14)} ${meta2.apiKeyEnvVar.padEnd(28)} ${meta2.label}`);
2273
2318
  }
@@ -2277,7 +2322,7 @@ async function handleKey(subArgs, injectedApi) {
2277
2322
  }
2278
2323
  if (!provider) fail(USAGE4);
2279
2324
  const id = provider.toLowerCase();
2280
- const meta = import_core5.ALL_PROVIDERS[id];
2325
+ const meta = import_core8.ALL_PROVIDERS[id];
2281
2326
  if (!meta) {
2282
2327
  fail(`Unknown provider: ${provider}`, "Run `ordewell key` to see the list.");
2283
2328
  }
@@ -2357,7 +2402,7 @@ async function handleApprove(subArgs, injectedApi) {
2357
2402
  // src/tui/terminalLauncher.ts
2358
2403
  var import_child_process3 = require("child_process");
2359
2404
  var import_util = require("util");
2360
- var import_core6 = require("@ordewell/core");
2405
+ var import_core9 = require("@ordewell/core");
2361
2406
  var execFileAsync = (0, import_util.promisify)(import_child_process3.execFile);
2362
2407
  var defaultExecFile = async (command, args) => {
2363
2408
  const { stdout, stderr } = await execFileAsync(command, args);
@@ -2377,16 +2422,16 @@ var defaultWhich = (bin) => {
2377
2422
  async function openTaskTerminal(port, planSessionId, taskId, deps = {}) {
2378
2423
  const platform = deps.platform ?? process.platform;
2379
2424
  const env = deps.env ?? process.env;
2380
- const hasTmuxImpl = deps.hasTmuxImpl ?? import_core6.hasTmux;
2425
+ const hasTmuxImpl = deps.hasTmuxImpl ?? import_core9.hasTmux;
2381
2426
  const execFileImpl = deps.execFileImpl ?? defaultExecFile;
2382
2427
  const spawnImpl = deps.spawnImpl ?? defaultSpawn;
2383
2428
  const which = deps.which ?? defaultWhich;
2384
2429
  if (!hasTmuxImpl()) {
2385
2430
  return { ok: false, message: "tmux is not installed \u2014 install it to get a real terminal per task." };
2386
2431
  }
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);
2432
+ const session = (0, import_core9.tmuxSessionName)(port);
2433
+ const socket = (0, import_core9.tmuxSocketName)(port);
2434
+ const base = (0, import_core9.tmuxWindowName)(taskId, planSessionId);
2390
2435
  let windows;
2391
2436
  try {
2392
2437
  const { stdout } = await execFileImpl("tmux", ["-L", socket, "list-windows", "-t", session, "-F", "#{window_name}"]);
@@ -2680,7 +2725,7 @@ Dependencies \xB7 #${task.order} ${task.title}
2680
2725
  }
2681
2726
 
2682
2727
  // src/tui/index.ts
2683
- var import_core10 = require("@ordewell/core");
2728
+ var import_core13 = require("@ordewell/core");
2684
2729
 
2685
2730
  // src/tui/ansi.ts
2686
2731
  var import_string_width = __toESM(require("string-width"));
@@ -3212,7 +3257,7 @@ function initialState(overrides = {}) {
3212
3257
  }
3213
3258
 
3214
3259
  // src/tui/layout.ts
3215
- var import_core7 = require("@ordewell/core");
3260
+ var import_core10 = require("@ordewell/core");
3216
3261
  var CHROME_ROWS = 4;
3217
3262
  function chatInputWrap(state) {
3218
3263
  const active = state.focus === "chat" && !state.overlay;
@@ -3348,9 +3393,9 @@ function chatScrollMax(state) {
3348
3393
  }
3349
3394
  function plannerSetup(state) {
3350
3395
  const provider = state.plannerProvider;
3351
- const registration = import_core7.ALL_PROVIDERS[provider];
3396
+ const registration = import_core10.ALL_PROVIDERS[provider];
3352
3397
  if (!registration) return { label: "not set", ready: false };
3353
- const runner = (0, import_core7.runnerForProvider)(provider);
3398
+ const runner = (0, import_core10.runnerForProvider)(provider);
3354
3399
  const ready = runner ? state.runners.some((r) => r.id === runner) : state.configuredProviders.includes(provider);
3355
3400
  return { label: registration.label, ready };
3356
3401
  }
@@ -3509,7 +3554,8 @@ function taskLines(state, task, index, cols) {
3509
3554
  });
3510
3555
  const model = task.assignedModel?.modelLabel ?? (task.type === "ai" ? "default model" : "");
3511
3556
  const effort = task.type === "ai" ? `effort: ${task.assignedModel?.thinkingEffort ?? "default"}` : "";
3512
- const mode = task.type === "ai" ? `mode: ${task.taskMode ?? "default"}` : "";
3557
+ const modeInfo = task.type === "ai" ? modesForTask(state.modesByRunner, task).find((m) => m.id === task.taskMode) : void 0;
3558
+ const mode = task.type === "ai" ? `mode: ${task.taskMode ?? "default"}${modeInfo?.autonomous ? style.yellow(" \u26A1") : ""}` : "";
3513
3559
  const runner = task.assignedRunner ?? "";
3514
3560
  const meta = [running ? "working" : "", runner, model].filter(Boolean).join(" \xB7 ");
3515
3561
  if (meta) lines.push(style.grey(truncate(` ${meta}`, cols)));
@@ -3517,6 +3563,9 @@ function taskLines(state, task, index, cols) {
3517
3563
  let editorLine;
3518
3564
  if (expanded) {
3519
3565
  lines.push(style.grey(` ${task.status.replace(/_/g, " ")}`));
3566
+ if (modeInfo?.autonomous) {
3567
+ lines.push(...taskText("Autonomy", "Runs without permission prompts. Toggle with /auto.", cols));
3568
+ }
3520
3569
  const editableSeed = task.prompt ?? task.description ?? task.title;
3521
3570
  if (task.description && task.description !== task.title && task.description !== editableSeed) {
3522
3571
  lines.push(...taskText("Description", task.description, cols));
@@ -3844,9 +3893,9 @@ function frame(title, body, rows, cols) {
3844
3893
  }
3845
3894
 
3846
3895
  // src/tui/reducer.ts
3847
- var import_core8 = require("@ordewell/core");
3896
+ var import_core11 = require("@ordewell/core");
3848
3897
  var import_plan_utils3 = require("@ordewell/core/plan-utils");
3849
- var KNOWN_PROVIDERS2 = Object.keys(import_core8.ALL_PROVIDERS);
3898
+ var KNOWN_PROVIDERS2 = Object.keys(import_core11.ALL_PROVIDERS);
3850
3899
  var DEFAULT_EFFORT = "__runner_default__";
3851
3900
  function step(state, effects = []) {
3852
3901
  return { state, effects };
@@ -4115,11 +4164,11 @@ function modelDetail(m) {
4115
4164
  function providerErrorHint(state) {
4116
4165
  const failed = Object.keys(state.providerErrors ?? {});
4117
4166
  if (failed.length === 0) return void 0;
4118
- const names = failed.map((p) => import_core8.ALL_PROVIDERS[p]?.label ?? p);
4167
+ const names = failed.map((p) => import_core11.ALL_PROVIDERS[p]?.label ?? p);
4119
4168
  return `\u26A0 Unavailable (key or fetch failed): ${names.join(", ")} \u2014 showing working providers only.`;
4120
4169
  }
4121
4170
  function plannerModelItems(state) {
4122
- const runner = (0, import_core8.runnerForProvider)(state.plannerProvider);
4171
+ const runner = (0, import_core11.runnerForProvider)(state.plannerProvider);
4123
4172
  if (!runner) return state.orchestratorModels.map((m) => ({ id: m.id, label: m.label, detail: modelDetail(m) }));
4124
4173
  const items = state.models.filter((m) => m.runners?.includes(runner)).map((m) => ({
4125
4174
  id: m.id,
@@ -4130,7 +4179,7 @@ function plannerModelItems(state) {
4130
4179
  return items.length > 0 ? items : [{ id: "", label: `No ${runner} models discovered yet`, detail: "run /refresh", disabled: true }];
4131
4180
  }
4132
4181
  function plannerEffortItems(state) {
4133
- const runner = (0, import_core8.runnerForProvider)(state.plannerProvider);
4182
+ const runner = (0, import_core11.runnerForProvider)(state.plannerProvider);
4134
4183
  if (!runner) {
4135
4184
  return [{ id: "", label: "Thinking effort applies to a coding-agent planner", detail: "switch with /planner", disabled: true }];
4136
4185
  }
@@ -4768,7 +4817,7 @@ function newSession(state) {
4768
4817
  }
4769
4818
  function setModel(state, args) {
4770
4819
  if (args[0] === "set" && args[1]) {
4771
- const runner = (0, import_core8.runnerForProvider)(state.plannerProvider);
4820
+ const runner = (0, import_core11.runnerForProvider)(state.plannerProvider);
4772
4821
  const known = runner ? modelsForRunner(state.models, runner) : [];
4773
4822
  if (runner && known.length > 0 && !known.some((m) => m.id === args[1])) {
4774
4823
  return fail2(state, `${args[1]} was not discovered for ${runner}.`);
@@ -4790,21 +4839,21 @@ function setModel(state, args) {
4790
4839
  }
4791
4840
  function plannerItems(state) {
4792
4841
  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);
4842
+ const agents = import_core11.CLI_PROVIDERS.map((id) => {
4843
+ const runner = (0, import_core11.runnerForProvider)(id);
4795
4844
  const ready = installed.has(runner);
4796
4845
  return {
4797
4846
  id,
4798
- label: import_core8.ALL_PROVIDERS[id].label,
4847
+ label: import_core11.ALL_PROVIDERS[id].label,
4799
4848
  detail: ready ? "coding agent \xB7 no API key needed" : "CLI not installed or not on PATH",
4800
4849
  selected: state.plannerProvider === id,
4801
4850
  disabled: !ready
4802
4851
  };
4803
4852
  });
4804
- const vendors = import_core8.PROVIDER_PRIORITY.filter((id) => state.configuredProviders.includes(id)).map((id) => ({
4853
+ const vendors = import_core11.PROVIDER_PRIORITY.filter((id) => state.configuredProviders.includes(id)).map((id) => ({
4805
4854
  id,
4806
- label: import_core8.ALL_PROVIDERS[id].label,
4807
- detail: import_core8.ALL_PROVIDERS[id].apiKeyEnvVar,
4855
+ label: import_core11.ALL_PROVIDERS[id].label,
4856
+ detail: import_core11.ALL_PROVIDERS[id].apiKeyEnvVar,
4808
4857
  selected: state.plannerProvider === id
4809
4858
  }));
4810
4859
  return vendors.length > 0 ? [...agents, ...vendors] : [...agents, { id: "", label: "No API providers configured", detail: "add a key with /key", disabled: true }];
@@ -4862,16 +4911,16 @@ function setKey(state, args) {
4862
4911
  }
4863
4912
  return step(state, [{ type: "setApiKey", provider, key: args.slice(2).join(" ") }]);
4864
4913
  }
4865
- const items = import_core8.PROVIDER_PRIORITY.map((id) => ({
4914
+ const items = import_core11.PROVIDER_PRIORITY.map((id) => ({
4866
4915
  id,
4867
- label: import_core8.ALL_PROVIDERS[id].label,
4868
- detail: import_core8.ALL_PROVIDERS[id].apiKeyEnvVar,
4916
+ label: import_core11.ALL_PROVIDERS[id].label,
4917
+ detail: import_core11.ALL_PROVIDERS[id].apiKeyEnvVar,
4869
4918
  selected: state.configuredProviders.includes(id)
4870
4919
  }));
4871
4920
  return step({ ...state, overlay: { kind: "picker", picker: picker("API provider key", items, { kind: "set-key" }) } });
4872
4921
  }
4873
4922
  function keyPrompt(provider) {
4874
- const meta = import_core8.ALL_PROVIDERS[provider];
4923
+ const meta = import_core11.ALL_PROVIDERS[provider];
4875
4924
  return {
4876
4925
  kind: "prompt",
4877
4926
  title: `${meta.label} API key`,
@@ -5229,7 +5278,7 @@ function createApp(deps) {
5229
5278
 
5230
5279
  // src/tui/effects.ts
5231
5280
  var import_child_process4 = require("child_process");
5232
- var import_core9 = require("@ordewell/core");
5281
+ var import_core12 = require("@ordewell/core");
5233
5282
  var import_plan_utils4 = require("@ordewell/core/plan-utils");
5234
5283
  var ConversationQueue = class {
5235
5284
  tail = Promise.resolve();
@@ -5396,7 +5445,7 @@ async function perform(effect, deps) {
5396
5445
  dispatch({ type: "notice", message: `Orchestrator model set to ${effect.modelId}.` });
5397
5446
  return;
5398
5447
  case "setPlanner": {
5399
- const meta = import_core9.ALL_PROVIDERS[effect.provider];
5448
+ const meta = import_core12.ALL_PROVIDERS[effect.provider];
5400
5449
  if (!meta) throw new Error(`Unknown planner: ${effect.provider}`);
5401
5450
  const env = { AI_PROVIDER: effect.provider };
5402
5451
  const settings = await api.updateSettings({ env });
@@ -5408,7 +5457,7 @@ async function perform(effect, deps) {
5408
5457
  });
5409
5458
  dispatch({
5410
5459
  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)}`
5460
+ message: `${(0, import_core12.isCliProvider)(effect.provider) ? `Planning with ${meta.label} \u2014 no API key needed.` : `Planner set to ${meta.label}.`} ${plannerModelNotice(recall2)}`
5412
5461
  });
5413
5462
  await loadModels(deps);
5414
5463
  return;
@@ -5420,7 +5469,7 @@ async function perform(effect, deps) {
5420
5469
  dispatch({ type: "notice", message: `Planner effort set to ${effect.effort || "the runner default"}.` });
5421
5470
  return;
5422
5471
  case "setApiKey": {
5423
- const meta = import_core9.ALL_PROVIDERS[effect.provider];
5472
+ const meta = import_core12.ALL_PROVIDERS[effect.provider];
5424
5473
  if (!meta) throw new Error(`Unknown provider: ${effect.provider}`);
5425
5474
  await api.updateSettings({ env: { [meta.apiKeyEnvVar]: effect.key } });
5426
5475
  deps.setEnvVar(meta.apiKeyEnvVar, effect.key);
@@ -5508,7 +5557,7 @@ function osc52(text) {
5508
5557
  function copySelection(deps, text) {
5509
5558
  if (!text) return;
5510
5559
  const lines = text.split("\n").length;
5511
- const command = (0, import_core9.clipboardCopyCommand)(deps.hasBin);
5560
+ const command = (0, import_core12.clipboardCopyCommand)(deps.hasBin);
5512
5561
  if (command) {
5513
5562
  try {
5514
5563
  (deps.pipeToClipboard ?? pipeToClipboardDefault)(command, text);
@@ -5982,7 +6031,7 @@ async function handleTui(subArgs) {
5982
6031
  }
5983
6032
  const workspace = flag(subArgs, "--workspace") || process.cwd();
5984
6033
  try {
5985
- (0, import_core10.assertWorkspaceExists)(workspace);
6034
+ (0, import_core13.assertWorkspaceExists)(workspace);
5986
6035
  } catch {
5987
6036
  console.error(
5988
6037
  `Workspace "${workspace}" does not exist.
@@ -6007,7 +6056,7 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
6007
6056
  conversationQueue,
6008
6057
  port,
6009
6058
  dispatch: (action) => app.dispatch(action),
6010
- newSessionId: () => `session-${Date.now()}`,
6059
+ newSessionId: () => (0, import_core13.mintSessionId)(),
6011
6060
  setEnvVar: (key, value) => {
6012
6061
  writeEnvVar(findEnvFile(), key, value);
6013
6062
  process.env[key] = value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ordewell/cli",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
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",