@tuned-tensor/cli 0.13.0-beta.1 → 0.13.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,19 +2,16 @@
2
2
  import {
3
3
  TRAINING_MODELS,
4
4
  assertLocalRunInputReady,
5
- createLocalStore,
6
- defaultLocalHome,
7
5
  loadLocalRunInput,
8
6
  loadLocalRunnerConfig,
9
- runLocalPipeline,
10
- runReportSchema
11
- } from "./chunk-2VJ3VVZF.js";
7
+ runLocalPipeline
8
+ } from "./chunk-YJYCGSHJ.js";
12
9
 
13
10
  // src/cli.ts
14
11
  import { spawn as spawn2 } from "child_process";
15
12
  import { constants as osConstants2 } from "os";
16
13
  import { Command } from "commander";
17
- import chalk7 from "chalk";
14
+ import chalk6 from "chalk";
18
15
 
19
16
  // src/local-agent-client.ts
20
17
  import { randomUUID as randomUUID2 } from "crypto";
@@ -576,18 +573,134 @@ async function rejectPreparedAction(action, persist) {
576
573
  }
577
574
 
578
575
  // src/agent-model.ts
576
+ import { ModelRuntime } from "@earendil-works/pi-coding-agent";
577
+ import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
578
+ import { dirname, join as join3 } from "path";
579
+
580
+ // src/config.ts
579
581
  import {
580
- getAgentDir,
581
- ModelRuntime
582
- } from "@earendil-works/pi-coding-agent";
583
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
584
- import { dirname, join as join2 } from "path";
582
+ chmodSync,
583
+ existsSync,
584
+ mkdirSync,
585
+ readFileSync,
586
+ writeFileSync
587
+ } from "fs";
588
+ import { homedir } from "os";
589
+ import { join as join2 } from "path";
590
+ var AGENT_THINKING_LEVELS = [
591
+ "off",
592
+ "minimal",
593
+ "low",
594
+ "medium",
595
+ "high",
596
+ "xhigh",
597
+ "max"
598
+ ];
599
+ var CONFIG_DIR_NAME = "tuned-tensor";
600
+ var CONFIG_FILE_NAME = "config.json";
601
+ function getConfigDir() {
602
+ const xdg = process.env.XDG_CONFIG_HOME;
603
+ const base = xdg || join2(homedir(), ".config");
604
+ return join2(base, CONFIG_DIR_NAME);
605
+ }
606
+ function getAgentConfigDir() {
607
+ return join2(getConfigDir(), "agent");
608
+ }
609
+ function getConfigPath() {
610
+ return join2(getConfigDir(), CONFIG_FILE_NAME);
611
+ }
612
+ function readConfig() {
613
+ const path = getConfigPath();
614
+ if (!existsSync(path)) return {};
615
+ try {
616
+ return JSON.parse(readFileSync(path, "utf-8"));
617
+ } catch {
618
+ return {};
619
+ }
620
+ }
621
+ var configRevision = 0;
622
+ function getConfigRevision() {
623
+ return configRevision;
624
+ }
625
+ function writeConfig(config) {
626
+ const dir = getConfigDir();
627
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
628
+ chmodSync(dir, 448);
629
+ const path = getConfigPath();
630
+ writeFileSync(path, JSON.stringify(config, null, 2) + "\n", {
631
+ mode: 384
632
+ });
633
+ chmodSync(path, 384);
634
+ configRevision += 1;
635
+ }
636
+ function updateConfig(partial) {
637
+ const current = readConfig();
638
+ writeConfig({ ...current, ...partial });
639
+ }
640
+ function getApiKey(opts) {
641
+ return opts?.apiKey || process.env.TUNED_TENSOR_API_KEY || readConfig().api_key;
642
+ }
643
+ function getAgentSelection(env = process.env) {
644
+ const stored = readConfig().agent;
645
+ const provider = env.TUNED_TENSOR_AGENT_PROVIDER || stored?.provider;
646
+ const model = env.TUNED_TENSOR_AGENT_MODEL || stored?.model;
647
+ const rawThinking = env.TUNED_TENSOR_AGENT_THINKING || stored?.thinking || "medium";
648
+ if (!AGENT_THINKING_LEVELS.includes(rawThinking)) {
649
+ throw new Error(
650
+ "Agent thinking must be off, minimal, low, medium, high, xhigh, or max."
651
+ );
652
+ }
653
+ if (!provider && !model) return void 0;
654
+ if (!provider || !model) {
655
+ throw new Error(
656
+ "Both an agent provider and model are required. Run `tt agent configure --provider <provider> --model <model>`."
657
+ );
658
+ }
659
+ return { provider, model, thinking: rawThinking };
660
+ }
661
+
662
+ // src/agent-model.ts
663
+ function missingProviderAuthMessage(provider) {
664
+ return `Provider "${provider}" is not authenticated. Open the tt shell and run /login ${provider} to save a key, then try again.`;
665
+ }
666
+ function unknownAgentProviderMessage(provider) {
667
+ return `Unknown provider "${provider}". Use /login <id> or /model <id>.`;
668
+ }
669
+ var MIN_REDACTED_SECRET_LENGTH = 8;
670
+ function isRecord(value) {
671
+ return value !== null && typeof value === "object" && !Array.isArray(value);
672
+ }
673
+ function pushSecret(secrets, value) {
674
+ if (typeof value !== "string") return;
675
+ const secret = value.trim();
676
+ if (secret.length < MIN_REDACTED_SECRET_LENGTH) return;
677
+ if (!secrets.includes(secret)) secrets.push(secret);
678
+ }
679
+ function readStoredProviderSecrets(authPath = getAgentAuthPath()) {
680
+ if (!existsSync2(authPath)) return [];
681
+ let parsed;
682
+ try {
683
+ parsed = JSON.parse(readFileSync2(authPath, "utf-8"));
684
+ } catch {
685
+ return [];
686
+ }
687
+ if (!isRecord(parsed)) return [];
688
+ const secrets = [];
689
+ for (const credential of Object.values(parsed)) {
690
+ if (!isRecord(credential)) continue;
691
+ if (credential.type === "api_key") {
692
+ pushSecret(secrets, credential.key);
693
+ } else if (credential.type === "oauth") {
694
+ pushSecret(secrets, credential.access);
695
+ pushSecret(secrets, credential.refresh);
696
+ }
697
+ }
698
+ return secrets;
699
+ }
585
700
  function resolveAgentModel(runtime, selection) {
586
701
  const resolved = resolveAgentModelDefinition(runtime, selection);
587
702
  if (!runtime.hasConfiguredAuth(selection.provider)) {
588
- throw new Error(
589
- `Authenticate provider "${selection.provider}" first, then retry. Provider secrets are never accepted by tt flags.`
590
- );
703
+ throw new Error(missingProviderAuthMessage(selection.provider));
591
704
  }
592
705
  return resolved;
593
706
  }
@@ -613,25 +726,46 @@ function resolveAgentModelDefinition(runtime, selection) {
613
726
  }
614
727
  return { model, thinking: selection.thinking };
615
728
  }
729
+ function getAgentAuthPath() {
730
+ return join3(getAgentConfigDir(), "auth.json");
731
+ }
732
+ function getAgentModelsPath() {
733
+ return join3(getAgentConfigDir(), "models.json");
734
+ }
616
735
  async function createPiModelRuntime() {
617
- const agentDir = getAgentDir();
618
- const modelsPath = join2(agentDir, "models.json");
736
+ const agentDir = getAgentConfigDir();
737
+ ensurePrivateDir(agentDir);
738
+ const modelsPath = getAgentModelsPath();
739
+ const authPath = getAgentAuthPath();
619
740
  ensureOpenRouterAppHeaders(modelsPath);
620
- return await ModelRuntime.create({
621
- authPath: join2(agentDir, "auth.json"),
741
+ const runtime = await ModelRuntime.create({
742
+ authPath,
622
743
  modelsPath,
623
744
  allowModelNetwork: false
624
745
  });
746
+ if (existsSync2(authPath)) chmodSync2(authPath, 384);
747
+ return runtime;
625
748
  }
626
749
  var OPENROUTER_APP_HEADERS = {
627
750
  "HTTP-Referer": "https://tunedtensor.com",
751
+ "X-OpenRouter-Title": "Tuned Tensor",
752
+ "X-OpenRouter-Categories": "cli-agent",
628
753
  "X-Title": "Tuned Tensor"
629
754
  };
755
+ function ensurePrivateDir(dir) {
756
+ if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
757
+ chmodSync2(dir, 448);
758
+ }
759
+ function writePrivateJson(path, value) {
760
+ ensurePrivateDir(dirname(path));
761
+ writeFileSync2(path, JSON.stringify(value, null, 2) + "\n", { mode: 384 });
762
+ chmodSync2(path, 384);
763
+ }
630
764
  function ensureOpenRouterAppHeaders(modelsPath) {
631
765
  let config = {};
632
- if (existsSync(modelsPath)) {
766
+ if (existsSync2(modelsPath)) {
633
767
  try {
634
- const parsed = JSON.parse(readFileSync(modelsPath, "utf-8"));
768
+ const parsed = JSON.parse(readFileSync2(modelsPath, "utf-8"));
635
769
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
636
770
  config = parsed;
637
771
  }
@@ -645,10 +779,15 @@ function ensureOpenRouterAppHeaders(modelsPath) {
645
779
  const providers = asRecord(config.providers) ?? (config.providers = empty());
646
780
  const openrouter = asRecord(providers.openrouter) ?? (providers.openrouter = empty());
647
781
  const headers = asRecord(openrouter.headers) ?? (openrouter.headers = empty());
782
+ const attributionNames = new Set(
783
+ Object.keys(OPENROUTER_APP_HEADERS).map((name) => name.toLowerCase())
784
+ );
785
+ for (const name of Object.keys(headers)) {
786
+ if (attributionNames.has(name.toLowerCase())) delete headers[name];
787
+ }
648
788
  Object.assign(headers, OPENROUTER_APP_HEADERS);
649
789
  if (JSON.stringify(config) === before) return;
650
- mkdirSync(dirname(modelsPath), { recursive: true });
651
- writeFileSync(modelsPath, JSON.stringify(config, null, 2) + "\n");
790
+ writePrivateJson(modelsPath, config);
652
791
  }
653
792
 
654
793
  // src/agent-tools.ts
@@ -1338,7 +1477,7 @@ import {
1338
1477
  writeFile as writeFile2
1339
1478
  } from "fs/promises";
1340
1479
  import { randomUUID as randomUUID3 } from "crypto";
1341
- import { join as join3 } from "path";
1480
+ import { join as join4 } from "path";
1342
1481
  var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1343
1482
  var DEFAULT_MAX_THREAD_BYTES = 1e6;
1344
1483
  var DEFAULT_MAX_MESSAGES = 200;
@@ -1348,7 +1487,7 @@ var DEFAULT_MAX_CLAIMS = 1e3;
1348
1487
  function assertThreadId(id) {
1349
1488
  if (!UUID.test(id)) throw new Error("Invalid local agent thread ID.");
1350
1489
  }
1351
- function isRecord(value) {
1490
+ function isRecord2(value) {
1352
1491
  return value !== null && typeof value === "object" && !Array.isArray(value);
1353
1492
  }
1354
1493
  function scrub(value, secrets) {
@@ -1359,7 +1498,7 @@ function scrub(value, secrets) {
1359
1498
  );
1360
1499
  }
1361
1500
  if (Array.isArray(value)) return value.map((entry) => scrub(entry, secrets));
1362
- if (isRecord(value)) {
1501
+ if (isRecord2(value)) {
1363
1502
  return Object.fromEntries(
1364
1503
  Object.entries(value).map(([key, entry]) => [key, scrub(entry, secrets)])
1365
1504
  );
@@ -1369,9 +1508,10 @@ function scrub(value, secrets) {
1369
1508
  var LocalAgentStore = class {
1370
1509
  constructor(rootDir, options = {}) {
1371
1510
  this.rootDir = rootDir;
1372
- this.threadsDir = join3(rootDir, "threads");
1373
- this.claimsDir = join3(rootDir, "action-claims");
1511
+ this.threadsDir = join4(rootDir, "threads");
1512
+ this.claimsDir = join4(rootDir, "action-claims");
1374
1513
  this.secrets = options.secretValues?.filter(Boolean) ?? [];
1514
+ this.secretValueProvider = options.secretValueProvider;
1375
1515
  this.maxThreadBytes = options.maxThreadBytes ?? DEFAULT_MAX_THREAD_BYTES;
1376
1516
  this.maxMessages = options.maxMessages ?? DEFAULT_MAX_MESSAGES;
1377
1517
  this.maxActions = options.maxActions ?? DEFAULT_MAX_ACTIONS;
@@ -1381,13 +1521,20 @@ var LocalAgentStore = class {
1381
1521
  threadsDir;
1382
1522
  claimsDir;
1383
1523
  secrets;
1524
+ secretValueProvider;
1384
1525
  maxThreadBytes;
1385
1526
  maxMessages;
1386
1527
  maxActions;
1387
1528
  maxThreads;
1388
1529
  maxClaims;
1389
1530
  redact(value) {
1390
- return scrub(value, this.secrets);
1531
+ let extra = [];
1532
+ try {
1533
+ extra = this.secretValueProvider?.() ?? [];
1534
+ } catch {
1535
+ extra = [];
1536
+ }
1537
+ return scrub(value, [...this.secrets, ...extra]);
1391
1538
  }
1392
1539
  async ensureDirectories() {
1393
1540
  await mkdir2(this.rootDir, { recursive: true, mode: 448 });
@@ -1399,7 +1546,7 @@ var LocalAgentStore = class {
1399
1546
  }
1400
1547
  path(id) {
1401
1548
  assertThreadId(id);
1402
- return join3(this.threadsDir, `${id}.json`);
1549
+ return join4(this.threadsDir, `${id}.json`);
1403
1550
  }
1404
1551
  serializeWithinQuota(state) {
1405
1552
  const active = state.actions.filter(
@@ -1424,11 +1571,11 @@ var LocalAgentStore = class {
1424
1571
  return serialized;
1425
1572
  }
1426
1573
  async pruneDirectory(directory, suffix, maximum, preserve) {
1427
- const entries = await Promise.all((await readdir(directory)).filter((name) => suffix.test(name) && name !== preserve).map(async (name) => ({ name, modified: (await stat(join3(directory, name))).mtimeMs })));
1574
+ const entries = await Promise.all((await readdir(directory)).filter((name) => suffix.test(name) && name !== preserve).map(async (name) => ({ name, modified: (await stat(join4(directory, name))).mtimeMs })));
1428
1575
  entries.sort((a, b) => a.modified - b.modified);
1429
1576
  const keepOther = Math.max(0, maximum - (preserve ? 1 : 0));
1430
1577
  for (const entry of entries.slice(0, Math.max(0, entries.length - keepOther))) {
1431
- await unlink(join3(directory, entry.name));
1578
+ await unlink(join4(directory, entry.name));
1432
1579
  }
1433
1580
  }
1434
1581
  async pruneThreads(preserve) {
@@ -1448,19 +1595,19 @@ var LocalAgentStore = class {
1448
1595
  }
1449
1596
  const candidates = await Promise.all(names.filter((name) => !protectedNames.has(name)).map(async (name) => ({
1450
1597
  name,
1451
- modified: (await stat(join3(this.threadsDir, name))).mtimeMs
1598
+ modified: (await stat(join4(this.threadsDir, name))).mtimeMs
1452
1599
  })));
1453
1600
  candidates.sort((a, b) => a.modified - b.modified);
1454
1601
  const keep = Math.max(0, this.maxThreads - protectedNames.size);
1455
1602
  for (const entry of candidates.slice(0, Math.max(0, candidates.length - keep))) {
1456
- await unlink(join3(this.threadsDir, entry.name));
1603
+ await unlink(join4(this.threadsDir, entry.name));
1457
1604
  }
1458
1605
  }
1459
1606
  async save(state) {
1460
1607
  assertThreadId(state.thread.id);
1461
1608
  await this.ensureDirectories();
1462
1609
  const destination = this.path(state.thread.id);
1463
- const temporary = join3(
1610
+ const temporary = join4(
1464
1611
  this.threadsDir,
1465
1612
  `.${state.thread.id}.${randomUUID3()}.tmp`
1466
1613
  );
@@ -1480,7 +1627,7 @@ var LocalAgentStore = class {
1480
1627
  async claimAction(actionId) {
1481
1628
  assertThreadId(actionId);
1482
1629
  await this.ensureDirectories();
1483
- const path = join3(this.claimsDir, `${actionId}.claim`);
1630
+ const path = join4(this.claimsDir, `${actionId}.claim`);
1484
1631
  try {
1485
1632
  await writeFile2(path, `${(/* @__PURE__ */ new Date()).toISOString()}
1486
1633
  `, {
@@ -1508,7 +1655,7 @@ var LocalAgentStore = class {
1508
1655
  throw new Error("Invalid local agent thread file.");
1509
1656
  }
1510
1657
  const parsed = JSON.parse(await readFile(path, "utf8"));
1511
- if (!isRecord(parsed) || !isRecord(parsed.thread) || parsed.thread.id !== id || !Array.isArray(parsed.messages) || !Array.isArray(parsed.actions)) {
1658
+ if (!isRecord2(parsed) || !isRecord2(parsed.thread) || parsed.thread.id !== id || !Array.isArray(parsed.messages) || !Array.isArray(parsed.actions)) {
1512
1659
  throw new Error("Local agent thread file is malformed.");
1513
1660
  }
1514
1661
  return parsed;
@@ -1531,92 +1678,6 @@ var LocalAgentStore = class {
1531
1678
  }
1532
1679
  };
1533
1680
 
1534
- // src/config.ts
1535
- import {
1536
- chmodSync,
1537
- existsSync as existsSync2,
1538
- mkdirSync as mkdirSync2,
1539
- readFileSync as readFileSync2,
1540
- writeFileSync as writeFileSync2
1541
- } from "fs";
1542
- import { homedir } from "os";
1543
- import { join as join4 } from "path";
1544
- var AGENT_THINKING_LEVELS = [
1545
- "off",
1546
- "minimal",
1547
- "low",
1548
- "medium",
1549
- "high",
1550
- "xhigh",
1551
- "max"
1552
- ];
1553
- var CONFIG_DIR_NAME = "tuned-tensor";
1554
- var CONFIG_FILE_NAME = "config.json";
1555
- function getConfigDir() {
1556
- const xdg = process.env.XDG_CONFIG_HOME;
1557
- const base = xdg || join4(homedir(), ".config");
1558
- return join4(base, CONFIG_DIR_NAME);
1559
- }
1560
- function getConfigPath() {
1561
- return join4(getConfigDir(), CONFIG_FILE_NAME);
1562
- }
1563
- function readConfig() {
1564
- const path = getConfigPath();
1565
- if (!existsSync2(path)) return {};
1566
- try {
1567
- return JSON.parse(readFileSync2(path, "utf-8"));
1568
- } catch {
1569
- return {};
1570
- }
1571
- }
1572
- var configRevision = 0;
1573
- function getConfigRevision() {
1574
- return configRevision;
1575
- }
1576
- function writeConfig(config) {
1577
- const dir = getConfigDir();
1578
- if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
1579
- chmodSync(dir, 448);
1580
- const path = getConfigPath();
1581
- writeFileSync2(path, JSON.stringify(config, null, 2) + "\n", {
1582
- mode: 384
1583
- });
1584
- chmodSync(path, 384);
1585
- configRevision += 1;
1586
- }
1587
- function updateConfig(partial) {
1588
- const current = readConfig();
1589
- writeConfig({ ...current, ...partial });
1590
- }
1591
- var DEFAULT_BASE_URL = "https://tunedtensor.com";
1592
- function getBaseUrl(opts) {
1593
- return opts?.baseUrl || process.env.TUNED_TENSOR_URL || readConfig().base_url || DEFAULT_BASE_URL;
1594
- }
1595
- function getApiKey(opts) {
1596
- return opts?.apiKey || process.env.TUNED_TENSOR_API_KEY || readConfig().api_key;
1597
- }
1598
- function getAgentSelection(env = process.env) {
1599
- const stored = readConfig().agent;
1600
- const provider = env.TUNED_TENSOR_AGENT_PROVIDER || stored?.provider;
1601
- const model = env.TUNED_TENSOR_AGENT_MODEL || stored?.model;
1602
- const rawThinking = env.TUNED_TENSOR_AGENT_THINKING || stored?.thinking || "medium";
1603
- if (!AGENT_THINKING_LEVELS.includes(rawThinking)) {
1604
- throw new Error(
1605
- "Agent thinking must be off, minimal, low, medium, high, xhigh, or max."
1606
- );
1607
- }
1608
- if (!provider && !model) return void 0;
1609
- if (!provider || !model) {
1610
- throw new Error(
1611
- "Both an agent provider and model are required. Run `tt agent configure --provider <provider> --model <model>`."
1612
- );
1613
- }
1614
- return { provider, model, thinking: rawThinking };
1615
- }
1616
-
1617
- // src/cli.ts
1618
- import { join as join8 } from "path";
1619
-
1620
1681
  // src/agent.ts
1621
1682
  import chalk3 from "chalk";
1622
1683
  import { stripVTControlCharacters as stripVTControlCharacters3 } from "util";
@@ -1626,7 +1687,7 @@ import chalk from "chalk";
1626
1687
  import { stat as stat3 } from "fs/promises";
1627
1688
  import { homedir as homedir3 } from "os";
1628
1689
  import { isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
1629
- import { createInterface } from "readline";
1690
+ import { createInterface as createInterface2 } from "readline";
1630
1691
  import { stripVTControlCharacters } from "util";
1631
1692
 
1632
1693
  // src/command-catalog.ts
@@ -1636,6 +1697,11 @@ var COMMAND_CATALOG = [
1636
1697
  { path: "validate", description: "Validate a local fine-tuning project.", group: "Workflow", modes: LOCAL },
1637
1698
  { path: "doctor", description: "Check the local host and run prerequisites.", group: "Workflow", modes: LOCAL },
1638
1699
  { path: "run", description: "Run the local fine-tuning and evaluation workflow.", group: "Workflow", modes: LOCAL },
1700
+ { path: "pipeline init", description: "Write a canonical v1 pipeline recipe.", group: "Workflow", modes: LOCAL },
1701
+ { path: "pipeline validate", description: "Validate a pipeline without executing it.", group: "Workflow", modes: LOCAL },
1702
+ { path: "pipeline plan", description: "Resolve pipeline steps and required artifacts.", group: "Workflow", modes: LOCAL },
1703
+ { path: "pipeline run", description: "Run or dry-run a local pipeline.", group: "Workflow", modes: LOCAL },
1704
+ { path: "shell", description: "Open the conversational terminal.", group: "Workflow", modes: LOCAL },
1639
1705
  { path: "runs list", description: "List local runs.", group: "Inspect", modes: LOCAL },
1640
1706
  { path: "runs get", description: "Show a local run.", group: "Inspect", modes: LOCAL },
1641
1707
  { path: "runs report", description: "Show a local run report.", group: "Inspect", modes: LOCAL },
@@ -1652,16 +1718,17 @@ var COMMAND_CATALOG = [
1652
1718
  { path: "models rollback", description: "Roll back the active local model.", group: "Serving", modes: LOCAL },
1653
1719
  { path: "serve", description: "Serve a local adapter, active model, or base model.", group: "Serving", modes: LOCAL },
1654
1720
  { path: "info", description: "Show local runtime package information.", group: "Inspect", modes: LOCAL },
1655
- { path: "auth login", description: "Store a Tuned Tensor API key.", group: "Account", modes: LOCAL },
1656
- { path: "auth logout", description: "Remove the stored Tuned Tensor API key.", group: "Account", modes: LOCAL },
1657
- { path: "auth status", description: "Show Tuned Tensor authentication status.", group: "Account", modes: LOCAL },
1658
- { path: "publish", description: "Publish local run evidence to the dashboard.", group: "Account", modes: LOCAL }
1721
+ { path: "status", description: "Show local project context.", group: "Inspect", modes: LOCAL },
1722
+ { path: "agent models", description: "List provider models for the laptop-local TT agent.", group: "Inspect", modes: LOCAL },
1723
+ { path: "agent configure", description: "Select the laptop-local TT agent model.", group: "Inspect", modes: LOCAL },
1724
+ { path: "agent status", description: "Show the laptop-local TT agent selection.", group: "Inspect", modes: LOCAL }
1659
1725
  ];
1660
1726
  var SLASH_COMMANDS = [
1661
1727
  { path: "/help", description: "Show commands; add a word to filter." },
1662
1728
  { path: "/status", description: "Show lightweight workflow status." },
1663
1729
  { path: "/context", description: "Show the current project context." },
1664
- { path: "/model", description: "Show or change the TT agent model." },
1730
+ { path: "/model", description: "Show or change the TT agent provider and model." },
1731
+ { path: "/login", description: "Save a provider API key; asks which provider if omitted." },
1665
1732
  { path: "/cd", description: "Change the shell's working directory." },
1666
1733
  { path: "/clear", description: "Clear the terminal." },
1667
1734
  { path: "/exit", description: "Exit the TT shell." }
@@ -1903,6 +1970,24 @@ function formatShellStatus(context) {
1903
1970
  }
1904
1971
 
1905
1972
  // src/agent-control.ts
1973
+ import { chmodSync as chmodSync3, existsSync as existsSync3 } from "fs";
1974
+ var FEATURED_AGENT_PROVIDERS = [
1975
+ "openai",
1976
+ "openrouter"
1977
+ ];
1978
+ var RECOMMENDED_AGENT_MODELS = [
1979
+ { provider: "openai", ids: ["gpt-5.6-sol"] },
1980
+ {
1981
+ provider: "openrouter",
1982
+ ids: ["deepseek/deepseek-v4-flash-0731", "deepseek/deepseek-v4-flash"]
1983
+ }
1984
+ ];
1985
+ var FEATURED_PROVIDER_INDEX = new Map(
1986
+ FEATURED_AGENT_PROVIDERS.map((id, index) => [id, index])
1987
+ );
1988
+ function isFeaturedProvider(id) {
1989
+ return FEATURED_PROVIDER_INDEX.has(id);
1990
+ }
1906
1991
  function modelKey(model) {
1907
1992
  return `${model.provider}/${model.id}`;
1908
1993
  }
@@ -1919,9 +2004,28 @@ function modelRelevance(model, query) {
1919
2004
  if (name.includes(q)) return 4;
1920
2005
  return void 0;
1921
2006
  }
2007
+ function listAgentProviders(runtime, options = {}) {
2008
+ return runtime.getProviders().filter((provider) => !options.featuredOnly || isFeaturedProvider(provider.id)).map((provider) => ({
2009
+ id: provider.id,
2010
+ name: provider.name ?? provider.id,
2011
+ authenticated: runtime.hasConfiguredAuth(provider.id)
2012
+ })).sort((left, right) => {
2013
+ const leftRank = FEATURED_PROVIDER_INDEX.get(left.id);
2014
+ const rightRank = FEATURED_PROVIDER_INDEX.get(right.id);
2015
+ if (leftRank !== void 0 || rightRank !== void 0) {
2016
+ return (leftRank ?? Number.MAX_SAFE_INTEGER) - (rightRank ?? Number.MAX_SAFE_INTEGER) || left.id.localeCompare(right.id);
2017
+ }
2018
+ return left.id.localeCompare(right.id);
2019
+ });
2020
+ }
2021
+ function findAgentProvider(runtime, id) {
2022
+ const needle = id.trim().toLowerCase();
2023
+ if (!needle) return void 0;
2024
+ return listAgentProviders(runtime).find((provider) => provider.id.toLowerCase() === needle);
2025
+ }
1922
2026
  function listAgentModels(runtime, options = {}) {
1923
2027
  let models = runtime.getModels(options.provider).filter(
1924
- (model) => options.includeUnauthenticated || runtime.hasConfiguredAuth(model.provider)
2028
+ (model) => (options.includeUnauthenticated || runtime.hasConfiguredAuth(model.provider)) && (!options.featuredOnly || isFeaturedProvider(model.provider))
1925
2029
  ).map((model) => ({
1926
2030
  provider: model.provider,
1927
2031
  id: model.id,
@@ -1948,6 +2052,19 @@ function listAgentModels(runtime, options = {}) {
1948
2052
  }
1949
2053
  return models;
1950
2054
  }
2055
+ function recommendAgentModels(runtime, options = {}) {
2056
+ const models = listAgentModels(runtime, {
2057
+ includeUnauthenticated: options.includeUnauthenticated ?? true
2058
+ });
2059
+ const picks = [];
2060
+ for (const recommendation of RECOMMENDED_AGENT_MODELS) {
2061
+ const match = recommendation.ids.map((id) => models.find(
2062
+ (model) => model.provider === recommendation.provider && model.id === id
2063
+ )).find((model) => model !== void 0);
2064
+ if (match) picks.push(match);
2065
+ }
2066
+ return picks;
2067
+ }
1951
2068
  function describeAgentModel(runtime, env) {
1952
2069
  const selection = getAgentSelection(env);
1953
2070
  if (!selection) return void 0;
@@ -1963,6 +2080,23 @@ function describeAgentModel(runtime, env) {
1963
2080
  supportsThinking: resolved.model.reasoning !== false
1964
2081
  };
1965
2082
  }
2083
+ async function loginAgentProvider(runtime, provider, apiKey) {
2084
+ const choice = findAgentProvider(runtime, provider);
2085
+ if (!choice) {
2086
+ throw new Error(unknownAgentProviderMessage(provider));
2087
+ }
2088
+ const key = apiKey.trim();
2089
+ if (!key) {
2090
+ throw new Error("API key cannot be empty.");
2091
+ }
2092
+ if (typeof runtime.setRuntimeApiKey !== "function") {
2093
+ throw new Error("This session cannot store provider credentials.");
2094
+ }
2095
+ await runtime.setRuntimeApiKey(choice.id, key);
2096
+ const authPath = getAgentAuthPath();
2097
+ if (existsSync3(authPath)) chmodSync3(authPath, 384);
2098
+ return { provider: choice.id };
2099
+ }
1966
2100
  function setAgentModel(runtime, env, provider, model, options = {}) {
1967
2101
  const current = getAgentSelection(env);
1968
2102
  const requestedThinking = options.thinking ?? current?.thinking ?? "medium";
@@ -1999,6 +2133,65 @@ function setAgentModel(runtime, env, provider, model, options = {}) {
1999
2133
  return { selection, adjustedThinking: effectiveThinking !== requestedThinking };
2000
2134
  }
2001
2135
 
2136
+ // src/secret-prompt.ts
2137
+ import { createInterface } from "readline/promises";
2138
+ import { stdin, stdout } from "process";
2139
+ import { Writable } from "stream";
2140
+ async function promptVisibleInput(message, input = stdin, output = stdout) {
2141
+ return await promptInput(message, false, input, output);
2142
+ }
2143
+ async function promptHiddenInput(message, input = stdin, output = stdout) {
2144
+ return await promptInput(message, true, input, output);
2145
+ }
2146
+ async function withDetachedKeypress(input, run) {
2147
+ const listeners = [...input.listeners("keypress")];
2148
+ input.removeAllListeners("keypress");
2149
+ try {
2150
+ return await run();
2151
+ } finally {
2152
+ input.removeAllListeners("keypress");
2153
+ for (const listener of listeners) {
2154
+ input.on("keypress", listener);
2155
+ }
2156
+ }
2157
+ }
2158
+ async function promptInput(message, hidden, input, output) {
2159
+ if (input.isTTY !== true || output.isTTY !== true) {
2160
+ throw new Error("Provider login needs an interactive tt session.");
2161
+ }
2162
+ return await withDetachedKeypress(input, async () => {
2163
+ if (!hidden) {
2164
+ const rl2 = createInterface({ input, output, terminal: true });
2165
+ try {
2166
+ return await rl2.question(message);
2167
+ } finally {
2168
+ rl2.close();
2169
+ }
2170
+ }
2171
+ let muted = false;
2172
+ const maskedOutput = new Writable({
2173
+ write(chunk, _encoding, callback) {
2174
+ if (!muted) output.write(chunk);
2175
+ callback();
2176
+ }
2177
+ });
2178
+ const rl = createInterface({
2179
+ input,
2180
+ output: maskedOutput,
2181
+ terminal: true
2182
+ });
2183
+ try {
2184
+ const pending = rl.question(message);
2185
+ muted = true;
2186
+ return await pending;
2187
+ } finally {
2188
+ muted = false;
2189
+ output.write("\n");
2190
+ rl.close();
2191
+ }
2192
+ });
2193
+ }
2194
+
2002
2195
  // src/shell.ts
2003
2196
  var AGENT_SLASH_COMMANDS = /* @__PURE__ */ new Set([
2004
2197
  "new",
@@ -2157,6 +2350,7 @@ var SLASH_NAMES = /* @__PURE__ */ new Set([
2157
2350
  "status",
2158
2351
  "context",
2159
2352
  "model",
2353
+ "login",
2160
2354
  "clear",
2161
2355
  "cd",
2162
2356
  "exit"
@@ -2326,6 +2520,9 @@ function agentModelLabel(context) {
2326
2520
  }
2327
2521
  function renderShellBanner(snapshot) {
2328
2522
  const heading = snapshot.version ? `${accent.bold("tt")} ${chalk.dim(`v${snapshot.version}`)}` : accent.bold("tt");
2523
+ const configured = Boolean(
2524
+ snapshot.context.agent?.provider && snapshot.context.agent?.model
2525
+ );
2329
2526
  const lines = [
2330
2527
  heading,
2331
2528
  chalk.dim(
@@ -2333,7 +2530,9 @@ function renderShellBanner(snapshot) {
2333
2530
  ),
2334
2531
  chalk.dim("ctrl+c stop/clear \xB7 ctrl+d exit \xB7 /help commands \xB7 tab complete"),
2335
2532
  "",
2336
- chalk.dim("Ask TT anything. Known commands run directly.")
2533
+ chalk.dim(
2534
+ configured ? "Ask TT anything. Known commands run directly." : "Use /model to choose a provider and model. Workflow commands work now."
2535
+ )
2337
2536
  ];
2338
2537
  return `${lines.join("\n")}
2339
2538
 
@@ -2442,45 +2641,74 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
2442
2641
  const lines = styleDetailLines([
2443
2642
  detailLine("Agent model", label),
2444
2643
  detailLine("Change", "/model <provider>/<model>"),
2445
- detailLine("Search", "/model <query>")
2644
+ detailLine("List", "/model <provider>"),
2645
+ detailLine("Search", "/model <query>"),
2646
+ detailLine("Login", "/login <provider>")
2446
2647
  ]);
2447
- const all = listAgentModels(runtime);
2448
- if (all.length === 0) {
2449
- lines.push("", chalk.dim(" no authenticated models \u2014 run `tt agent models --all` for the full catalog"));
2648
+ const catalog = { includeUnauthenticated: true };
2649
+ const featuredCatalog = { ...catalog, featuredOnly: true };
2650
+ const provider = query ? findAgentProvider(runtime, query) : void 0;
2651
+ if (provider) {
2652
+ const matches = listAgentModels(runtime, { ...catalog, provider: provider.id });
2653
+ lines.push("", chalk.bold(`Models from ${provider.id}`));
2654
+ this.appendAgentModelChoices(lines, matches, summary, 20, "/model <query> to search");
2655
+ if (matches.length === 0) {
2656
+ lines.push(chalk.dim(" no models"));
2657
+ }
2450
2658
  return lines;
2451
2659
  }
2452
2660
  if (query) {
2453
- const matches = listAgentModels(runtime, { query });
2661
+ const matches = listAgentModels(runtime, { ...catalog, query });
2454
2662
  lines.push("", chalk.bold(`Models matching ${JSON.stringify(query)}`));
2663
+ this.appendAgentModelChoices(lines, matches, summary, 10, "refine the query");
2455
2664
  if (matches.length === 0) {
2456
2665
  lines.push(chalk.dim(" no matches"));
2457
- } else {
2458
- const shown2 = matches.slice(0, 10);
2459
- for (const model of shown2) {
2460
- lines.push(this.formatAgentModelChoice(model, summary));
2461
- }
2462
- if (matches.length > shown2.length) {
2463
- lines.push(chalk.dim(` \u2026 ${matches.length - shown2.length} more \u2014 refine the query`));
2464
- }
2465
2666
  }
2466
2667
  return lines;
2467
2668
  }
2468
- const preferred = summary ? all.filter((model) => model.provider === summary.provider) : [];
2469
- const rest = summary ? all.filter((model) => model.provider !== summary.provider) : all;
2470
- const shown = [...preferred, ...rest].slice(0, 8);
2471
- lines.push("", chalk.bold(summary ? "Suggestions" : "Available models"));
2669
+ const providers = listAgentProviders(runtime, { featuredOnly: true });
2670
+ lines.push("", chalk.bold("Providers"));
2671
+ if (providers.length === 0) {
2672
+ lines.push(chalk.dim(" no providers in the catalog"));
2673
+ } else {
2674
+ for (const choice of providers) {
2675
+ lines.push(this.formatAgentProviderChoice(choice));
2676
+ }
2677
+ lines.push(chalk.dim(" Other providers: /login <id> or /model <id>"));
2678
+ if (providers.some((choice) => !choice.authenticated)) {
2679
+ lines.push(chalk.dim(" Use /login <provider> to save a key."));
2680
+ }
2681
+ }
2682
+ const suggestions = recommendAgentModels(runtime, featuredCatalog);
2683
+ lines.push("", chalk.bold("Suggestions"));
2684
+ this.appendAgentModelChoices(lines, suggestions, summary);
2685
+ if (suggestions.length === 0) {
2686
+ lines.push(chalk.dim(" no models in the catalog"));
2687
+ } else {
2688
+ lines.push(chalk.dim(" Use /model <provider> to list models, /model <query> to search"));
2689
+ }
2690
+ return lines;
2691
+ }
2692
+ appendAgentModelChoices(lines, models, summary, limit, moreHint) {
2693
+ const shown = limit ? models.slice(0, limit) : models;
2472
2694
  for (const model of shown) {
2473
2695
  lines.push(this.formatAgentModelChoice(model, summary));
2474
2696
  }
2475
- if (all.length > shown.length) {
2476
- lines.push(chalk.dim(` \u2026 ${all.length - shown.length} more \u2014 /model <query> to search`));
2697
+ if (limit && models.length > shown.length) {
2698
+ const hint = moreHint ? ` \u2014 ${moreHint}` : "";
2699
+ lines.push(chalk.dim(` \u2026 ${models.length - shown.length} more${hint}`));
2477
2700
  }
2478
- return lines;
2701
+ }
2702
+ formatAgentProviderChoice(provider) {
2703
+ const displayName = provider.name !== provider.id ? ` ${provider.name}` : "";
2704
+ const auth = provider.authenticated ? "" : chalk.dim(" auth required");
2705
+ return ` ${accent(provider.id)}${displayName}${auth}`;
2479
2706
  }
2480
2707
  formatAgentModelChoice(model, summary) {
2481
2708
  const active = summary?.provider === model.provider && summary?.model === model.id;
2482
2709
  const displayName = model.name !== model.id ? ` ${model.name}` : "";
2483
- return ` ${accent(`${model.provider}/${model.id}`)}${displayName}${active ? chalk.dim(" (active)") : ""}`;
2710
+ const auth = model.authenticated ? "" : chalk.dim(" auth required");
2711
+ return ` ${accent(`${model.provider}/${model.id}`)}${displayName}${active ? chalk.dim(" (active)") : ""}${auth}`;
2484
2712
  }
2485
2713
  async handleSlash(command) {
2486
2714
  switch (command.name) {
@@ -2508,7 +2736,7 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
2508
2736
  return "continue";
2509
2737
  }
2510
2738
  if (command.args.length !== 1) {
2511
- throw new ShellParseError("Usage: /model [<query> | <provider>/<model>]");
2739
+ throw new ShellParseError("Usage: /model [<query> | <provider> | <provider>/<model>]");
2512
2740
  }
2513
2741
  const target = command.args[0];
2514
2742
  const slash = target.indexOf("/");
@@ -2529,6 +2757,48 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
2529
2757
  const note = result2.adjustedThinking ? " \u2014 thinking set to off for this model" : "";
2530
2758
  this.io.write(
2531
2759
  `${successMark()} Agent model: ${result2.selection.provider}/${result2.selection.model} (thinking ${result2.selection.thinking})${note}.
2760
+ `
2761
+ );
2762
+ return "continue";
2763
+ }
2764
+ case "login": {
2765
+ const runtime = await this.requireAgentModelRuntime();
2766
+ if (command.args.length > 1) {
2767
+ throw new ShellParseError("Usage: /login [<provider>]");
2768
+ }
2769
+ let providerId = command.args[0]?.trim();
2770
+ if (!providerId) {
2771
+ const providers = listAgentProviders(runtime, { featuredOnly: true });
2772
+ if (providers.length === 0) {
2773
+ throw new ShellParseError("No providers in the catalog.");
2774
+ }
2775
+ this.writeLines([
2776
+ "",
2777
+ chalk.bold("Providers"),
2778
+ ...providers.map((choice) => this.formatAgentProviderChoice(choice)),
2779
+ chalk.dim(" Other providers: /login <id>")
2780
+ ]);
2781
+ if (!this.io.promptLine) {
2782
+ throw new ShellParseError("Usage: /login <provider>");
2783
+ }
2784
+ providerId = (await this.io.promptLine("Provider: ")).trim();
2785
+ if (!providerId) {
2786
+ throw new ShellParseError("Usage: /login <provider>");
2787
+ }
2788
+ }
2789
+ const provider = findAgentProvider(runtime, providerId);
2790
+ if (!provider) {
2791
+ throw new ShellParseError(unknownAgentProviderMessage(providerId));
2792
+ }
2793
+ if (!this.io.promptSecret) {
2794
+ throw new ShellParseError("Provider login needs an interactive tt session.");
2795
+ }
2796
+ const label = provider.name !== provider.id ? provider.name : provider.id;
2797
+ const apiKey = await this.io.promptSecret(`${label} API key: `);
2798
+ const result2 = await loginAgentProvider(runtime, provider.id, apiKey);
2799
+ await this.refreshContext();
2800
+ this.io.write(
2801
+ `${successMark()} Saved ${result2.provider} credentials. Chat and /model can use this provider now.
2532
2802
  `
2533
2803
  );
2534
2804
  return "continue";
@@ -2655,17 +2925,37 @@ async function startInteractiveShell(options) {
2655
2925
  }
2656
2926
  let readline;
2657
2927
  const terminalInput = input;
2658
- const foregroundRunner = (request2) => withForegroundSignalHandoff(
2928
+ const withPrompt = async (message, prompt) => {
2929
+ readline?.pause();
2930
+ const restoreRawMode = terminalInput.isRaw === true && typeof terminalInput.setRawMode === "function";
2931
+ if (restoreRawMode) terminalInput.setRawMode(false);
2932
+ try {
2933
+ return await prompt(message, input, output);
2934
+ } finally {
2935
+ if (restoreRawMode) terminalInput.setRawMode(true);
2936
+ readline?.resume();
2937
+ }
2938
+ };
2939
+ const io = {
2940
+ ...streamIO(output, error),
2941
+ async promptLine(message) {
2942
+ return await withPrompt(message, promptVisibleInput);
2943
+ },
2944
+ async promptSecret(message) {
2945
+ return await withPrompt(message, promptHiddenInput);
2946
+ }
2947
+ };
2948
+ const foregroundRunner = (request) => withForegroundSignalHandoff(
2659
2949
  {
2660
2950
  input: terminalInput,
2661
2951
  pauseReadline: () => readline?.pause(),
2662
2952
  resumeReadline: () => readline?.resume()
2663
2953
  },
2664
- () => options.runner(request2)
2954
+ () => options.runner(request)
2665
2955
  );
2666
2956
  const session = await createShellSession({
2667
2957
  runner: foregroundRunner,
2668
- io: streamIO(output, error),
2958
+ io,
2669
2959
  agent: options.agent,
2670
2960
  cwd: options.cwd,
2671
2961
  env: options.env,
@@ -2673,7 +2963,7 @@ async function startInteractiveShell(options) {
2673
2963
  agentModelRuntime: options.agentModelRuntime
2674
2964
  });
2675
2965
  const completer = createCommandCompleter(() => session.snapshot().mode);
2676
- readline = createInterface({
2966
+ readline = createInterface2({
2677
2967
  input,
2678
2968
  output,
2679
2969
  terminal: true,
@@ -2846,7 +3136,7 @@ var StreamingTerminalMarkdown = class {
2846
3136
  var accent3 = chalk3.hex("#8B5CF6");
2847
3137
  var successMark2 = () => chalk3.green("\u2713");
2848
3138
  var errorMark2 = () => chalk3.red("\u2717");
2849
- function isRecord2(value) {
3139
+ function isRecord3(value) {
2850
3140
  return value !== null && typeof value === "object" && !Array.isArray(value);
2851
3141
  }
2852
3142
  function stringValue(value) {
@@ -2887,7 +3177,7 @@ function formatThread(thread, activeId) {
2887
3177
  return `${active} ${chalk3.dim(thread.id.slice(0, 8))} ${sanitizeTerminalText2(thread.title)} ${chalk3.dim(dateLabel)}`;
2888
3178
  }
2889
3179
  function actionFromPayload(payload) {
2890
- const value = isRecord2(payload.action) ? payload.action : isRecord2(payload.approval) ? payload.approval : payload;
3180
+ const value = isRecord3(payload.action) ? payload.action : isRecord3(payload.approval) ? payload.approval : payload;
2891
3181
  const id = stringValue(value.id);
2892
3182
  const title = stringValue(value.title);
2893
3183
  if (!id || !title) return null;
@@ -2975,10 +3265,10 @@ ${chalk3.yellow.bold("Approval required")} ${chalk3.dim(action.id.slice(0, 8))}
2975
3265
  `);
2976
3266
  if (action.summary) this.options.io.write(`${sanitizeTerminalText2(action.summary)}
2977
3267
  `);
2978
- const request2 = [action.method, action.path].filter(Boolean).join(" ");
3268
+ const request = [action.method, action.path].filter(Boolean).join(" ");
2979
3269
  const technical = {
2980
3270
  operation: action.operation,
2981
- ...request2 ? { request: request2 } : {},
3271
+ ...request ? { request } : {},
2982
3272
  arguments: action.arguments ?? null,
2983
3273
  preview: action.preview ?? null
2984
3274
  };
@@ -3020,7 +3310,7 @@ ${chalk3.yellow.bold("Approval required")} ${chalk3.dim(action.id.slice(0, 8))}
3020
3310
  if (event.type === "tool_call") {
3021
3311
  this.flushResponse();
3022
3312
  this.endOpenLine();
3023
- const name = stringValue(payload.name) ?? stringValue(isRecord2(payload.tool_call) ? payload.tool_call.name : null) ?? "tool";
3313
+ const name = stringValue(payload.name) ?? stringValue(isRecord3(payload.tool_call) ? payload.tool_call.name : null) ?? "tool";
3024
3314
  this.options.io.write(chalk3.dim(` \u25CB ${name}
3025
3315
  `));
3026
3316
  return;
@@ -3278,7 +3568,7 @@ ${chalk3.yellow.bold("Approval required")} ${chalk3.dim(action.id.slice(0, 8))}
3278
3568
  import { constants as osConstants } from "os";
3279
3569
  import { randomUUID as randomUUID4 } from "crypto";
3280
3570
  import { spawn } from "child_process";
3281
- import { existsSync as existsSync3 } from "fs";
3571
+ import { existsSync as existsSync4 } from "fs";
3282
3572
  import { fileURLToPath } from "url";
3283
3573
  import {
3284
3574
  readFile as readFile3,
@@ -3361,59 +3651,6 @@ var ApiError = class extends Error {
3361
3651
  this.name = "ApiError";
3362
3652
  }
3363
3653
  };
3364
- function resolveKey(opts) {
3365
- const key = getApiKey(opts);
3366
- if (!key) {
3367
- throw new ApiError(
3368
- 401,
3369
- "NO_API_KEY",
3370
- "No API key found. Run `tt auth login` or set TUNED_TENSOR_API_KEY."
3371
- );
3372
- }
3373
- return key;
3374
- }
3375
- function buildUrl(path, query, opts) {
3376
- const base = getBaseUrl(opts);
3377
- const url = new URL(`/api/v1${path}`, base);
3378
- if (query) {
3379
- for (const [k, v] of Object.entries(query)) {
3380
- if (v !== void 0) url.searchParams.set(k, String(v));
3381
- }
3382
- }
3383
- return url.toString();
3384
- }
3385
- async function request(method, path, options = {}) {
3386
- const key = resolveKey(options.opts);
3387
- const url = buildUrl(path, options.query, options.opts);
3388
- const headers = {
3389
- ...options.headers,
3390
- Authorization: `Bearer ${key}`
3391
- };
3392
- let bodyPayload;
3393
- if (options.body) {
3394
- headers["Content-Type"] = "application/json";
3395
- bodyPayload = JSON.stringify(options.body);
3396
- }
3397
- const res = await fetch(url, { method, headers, body: bodyPayload });
3398
- if (!res.ok) {
3399
- let errBody;
3400
- try {
3401
- errBody = await res.json();
3402
- } catch {
3403
- throw new ApiError(res.status, "UNKNOWN", res.statusText);
3404
- }
3405
- throw new ApiError(
3406
- res.status,
3407
- errBody.error?.code || "UNKNOWN",
3408
- errBody.error?.message || res.statusText
3409
- );
3410
- }
3411
- if (res.status === 204) return { data: null };
3412
- return await res.json();
3413
- }
3414
- function post(path, body, opts, headers) {
3415
- return request("POST", path, { body, opts, headers });
3416
- }
3417
3654
 
3418
3655
  // src/output.ts
3419
3656
  var labelStyle = chalk4.bold.hex("#A78BFA");
@@ -3509,7 +3746,7 @@ function shortId(id) {
3509
3746
  }
3510
3747
 
3511
3748
  // src/local-output.ts
3512
- function isRecord3(value) {
3749
+ function isRecord4(value) {
3513
3750
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3514
3751
  }
3515
3752
  function text(value) {
@@ -3523,14 +3760,16 @@ function number(value) {
3523
3760
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3524
3761
  }
3525
3762
  function record3(value) {
3526
- return isRecord3(value) ? value : void 0;
3763
+ return isRecord4(value) ? value : void 0;
3527
3764
  }
3528
3765
  function rows(value) {
3529
- return Array.isArray(value) ? value.filter(isRecord3) : [];
3766
+ return Array.isArray(value) ? value.filter(isRecord4) : [];
3530
3767
  }
3531
3768
  function displayId(value) {
3532
3769
  const id = text(value);
3533
- return id ? shortId(id) : "\u2014";
3770
+ if (!id) return "\u2014";
3771
+ if (id.startsWith("local-")) return `local-${shortId(id.slice("local-".length))}`;
3772
+ return shortId(id);
3534
3773
  }
3535
3774
  function displayNumber(value, digits = 4) {
3536
3775
  const parsed = number(value);
@@ -3814,19 +4053,19 @@ function renderServePlan(value) {
3814
4053
  function renderKnownJson(args2, value) {
3815
4054
  const command = args2[0];
3816
4055
  const subcommand = args2[1];
3817
- if (command === "doctor" && isRecord3(value)) {
4056
+ if (command === "doctor" && isRecord4(value)) {
3818
4057
  renderDoctor(value);
3819
4058
  return true;
3820
4059
  }
3821
- if (command === "init" && isRecord3(value)) {
4060
+ if (command === "init" && isRecord4(value)) {
3822
4061
  renderInit(value);
3823
4062
  return true;
3824
4063
  }
3825
- if (command === "validate" && isRecord3(value)) {
4064
+ if (command === "validate" && isRecord4(value)) {
3826
4065
  renderValidate(value);
3827
4066
  return true;
3828
4067
  }
3829
- if (command === "run" && isRecord3(value)) {
4068
+ if (command === "run" && isRecord4(value)) {
3830
4069
  renderRun(value);
3831
4070
  return true;
3832
4071
  }
@@ -3839,15 +4078,15 @@ function renderKnownJson(args2, value) {
3839
4078
  renderRunEvents(value);
3840
4079
  return true;
3841
4080
  }
3842
- if (subcommand === "get" && isRecord3(value)) {
4081
+ if (subcommand === "get" && isRecord4(value)) {
3843
4082
  renderRunRecord(value);
3844
4083
  return true;
3845
4084
  }
3846
- if (subcommand === "report" && isRecord3(value)) {
4085
+ if (subcommand === "report" && isRecord4(value)) {
3847
4086
  renderRunReport(value);
3848
4087
  return true;
3849
4088
  }
3850
- if (subcommand === "compare" && isRecord3(value)) {
4089
+ if (subcommand === "compare" && isRecord4(value)) {
3851
4090
  renderRunComparison(value);
3852
4091
  return true;
3853
4092
  }
@@ -3857,25 +4096,25 @@ function renderKnownJson(args2, value) {
3857
4096
  renderModelsList(value);
3858
4097
  return true;
3859
4098
  }
3860
- if ((subcommand === "get" || subcommand === "verify") && isRecord3(value)) {
4099
+ if ((subcommand === "get" || subcommand === "verify") && isRecord4(value)) {
3861
4100
  if (subcommand === "verify") printSuccess("Local model artifact is valid");
3862
4101
  renderModelRecord(value);
3863
4102
  return true;
3864
4103
  }
3865
- if ((subcommand === "prefetch" || subcommand === "verify-base") && isRecord3(value)) {
4104
+ if ((subcommand === "prefetch" || subcommand === "verify-base") && isRecord4(value)) {
3866
4105
  renderModelPrefetch(value);
3867
4106
  return true;
3868
4107
  }
3869
- if (["active", "activate", "rollback"].includes(subcommand ?? "") && isRecord3(value)) {
4108
+ if (["active", "activate", "rollback"].includes(subcommand ?? "") && isRecord4(value)) {
3870
4109
  renderActiveModel(value);
3871
4110
  return true;
3872
4111
  }
3873
- if (subcommand === "serve" && isRecord3(value)) {
4112
+ if (subcommand === "serve" && isRecord4(value)) {
3874
4113
  renderServePlan(value);
3875
4114
  return true;
3876
4115
  }
3877
4116
  }
3878
- if (command === "serve" && isRecord3(value)) {
4117
+ if (command === "serve" && isRecord4(value)) {
3879
4118
  renderServePlan(value);
3880
4119
  return true;
3881
4120
  }
@@ -4149,7 +4388,7 @@ async function projectSpecArguments(originalArgs, cwd) {
4149
4388
  }
4150
4389
  function resolveLocalCliEntrypoint() {
4151
4390
  const entrypoint = fileURLToPath(new URL("./local-runtime.js", import.meta.url));
4152
- if (!existsSync3(entrypoint)) {
4391
+ if (!existsSync4(entrypoint)) {
4153
4392
  throw new Error(
4154
4393
  "The bundled local runtime is missing. Rebuild or reinstall @tuned-tensor/cli."
4155
4394
  );
@@ -4478,294 +4717,9 @@ function registerAgentCommands(parent, options) {
4478
4717
  });
4479
4718
  }
4480
4719
 
4481
- // src/commands/auth.ts
4482
- import { createInterface as createInterface2 } from "readline/promises";
4483
- import { stdin, stdout } from "process";
4484
- import { Writable } from "stream";
4485
- import chalk6 from "chalk";
4486
- async function promptForApiKey(input = stdin, output = stdout) {
4487
- if (input.isTTY !== true || output.isTTY !== true) {
4488
- throw new Error(
4489
- "An API key is required in non-interactive mode. Pass it to `tt auth login <key>` or set TUNED_TENSOR_API_KEY."
4490
- );
4491
- }
4492
- let muted = false;
4493
- const maskedOutput = new Writable({
4494
- write(chunk, _encoding, callback) {
4495
- if (!muted) output.write(chunk);
4496
- callback();
4497
- }
4498
- });
4499
- const rl = createInterface2({
4500
- input,
4501
- output: maskedOutput,
4502
- terminal: true
4503
- });
4504
- try {
4505
- const pending = rl.question("Enter your API key (tt_...): ");
4506
- muted = true;
4507
- return await pending;
4508
- } finally {
4509
- muted = false;
4510
- output.write("\n");
4511
- rl.close();
4512
- }
4513
- }
4514
- function registerAuthCommands(parent) {
4515
- const auth = parent.command("auth").description("Manage authentication");
4516
- auth.command("login").description("Store an API key for authentication").argument("[key]", "API key (tt_...). If omitted, you will be prompted.").action(async (key) => {
4517
- let apiKey = key;
4518
- if (!apiKey) {
4519
- if (isJsonMode()) {
4520
- throw new Error(
4521
- "An API key argument is required in JSON mode. Use `tt --json auth login <key>`."
4522
- );
4523
- }
4524
- const settingsUrl = `${DEFAULT_BASE_URL}/dashboard/settings`;
4525
- console.log();
4526
- console.log(
4527
- ` To get your API key, go to ${chalk6.bold("Settings \u2192 API Keys")} in the Tuned Tensor dashboard:`
4528
- );
4529
- console.log(` ${chalk6.hex("#A78BFA").underline(settingsUrl)}`);
4530
- console.log();
4531
- apiKey = await promptForApiKey();
4532
- }
4533
- apiKey = apiKey.trim();
4534
- if (!apiKey.startsWith("tt_") || apiKey.length !== 51) {
4535
- throw new Error(
4536
- "Invalid API key format. Keys start with tt_ and are 51 characters long."
4537
- );
4538
- }
4539
- updateConfig({ api_key: apiKey });
4540
- if (isJsonMode()) {
4541
- printJson({
4542
- authenticated: true,
4543
- key_prefix: `${apiKey.slice(0, 8)}...`,
4544
- base_url: getBaseUrl(parent.opts())
4545
- });
4546
- return;
4547
- }
4548
- printSuccess(
4549
- `API key stored (${apiKey.slice(0, 8)}...). You're ready to go.`
4550
- );
4551
- });
4552
- auth.command("logout").description("Remove stored credentials").action(() => {
4553
- const current = readConfig();
4554
- const { api_key: _removed, ...rest } = current;
4555
- writeConfig(rest);
4556
- if (isJsonMode()) {
4557
- printJson({ authenticated: false });
4558
- return;
4559
- }
4560
- printSuccess("Credentials removed.");
4561
- });
4562
- auth.command("status").description("Show current authentication status").action(() => {
4563
- const opts = parent.opts();
4564
- const apiKey = getApiKey(opts);
4565
- const baseUrl = getBaseUrl(opts);
4566
- if (isJsonMode()) {
4567
- printJson({
4568
- authenticated: !!apiKey,
4569
- key_prefix: apiKey ? apiKey.slice(0, 8) + "..." : null,
4570
- base_url: baseUrl
4571
- });
4572
- return;
4573
- }
4574
- if (apiKey) {
4575
- printDetail([
4576
- ["Authenticated", "Yes"],
4577
- ["API Key", apiKey.slice(0, 8) + "..."],
4578
- ["Base URL", baseUrl]
4579
- ]);
4580
- } else {
4581
- printWarning(
4582
- "Not authenticated. Run `tt auth login` to store an API key."
4583
- );
4584
- printDetail([["Base URL", baseUrl]]);
4585
- }
4586
- });
4587
- }
4588
-
4589
- // src/commands/publish.ts
4590
- import { createInterface as createInterface3 } from "readline/promises";
4591
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
4592
- import { homedir as homedir4 } from "os";
4593
- import { isAbsolute as isAbsolute4, join as join7, resolve as resolve4 } from "path";
4594
- import { stdin as defaultStdin, stdout as defaultStdout } from "process";
4595
- var MAX_PUBLISH_BYTES = 2 * 1024 * 1024;
4596
- function expandPath2(value, baseDirectory, homeDirectory) {
4597
- if (value === "~") return homeDirectory;
4598
- if (value.startsWith("~/")) return resolve4(homeDirectory, value.slice(2));
4599
- return isAbsolute4(value) ? resolve4(value) : resolve4(baseDirectory, value);
4600
- }
4601
- function resolvePublishStoreRoot(options) {
4602
- const cwd = options?.cwd ?? process.cwd();
4603
- const env = options?.env ?? process.env;
4604
- const homeDirectory = options?.homeDirectory ?? (env.HOME ? resolve4(env.HOME) : homedir4());
4605
- const localConfigPath = join7(cwd, "local-runner.json");
4606
- if (existsSync4(localConfigPath)) {
4607
- try {
4608
- const raw = JSON.parse(readFileSync4(localConfigPath, "utf-8"));
4609
- if (typeof raw.storeRoot === "string" && raw.storeRoot.trim()) {
4610
- return expandPath2(raw.storeRoot, cwd, homeDirectory);
4611
- }
4612
- } catch {
4613
- }
4614
- }
4615
- if (env.TT_LOCAL_HOME && env.TT_LOCAL_HOME.trim()) {
4616
- return expandPath2(env.TT_LOCAL_HOME, cwd, homeDirectory);
4617
- }
4618
- return defaultLocalHome();
4619
- }
4620
- async function confirmPublish(question, input = defaultStdin, output = defaultStdout) {
4621
- if (input.isTTY !== true || output.isTTY !== true) {
4622
- throw new Error(
4623
- "Publishing requires confirmation in interactive mode. Re-run with --yes."
4624
- );
4625
- }
4626
- const rl = createInterface3({ input, output, terminal: true });
4627
- try {
4628
- const answer = (await rl.question(question)).trim().toLowerCase();
4629
- return answer === "y" || answer === "yes";
4630
- } finally {
4631
- rl.close();
4632
- }
4633
- }
4634
- function pickPublishableRun(runs, runId) {
4635
- if (runId) {
4636
- const exact = runs.find((run) => run.id === runId);
4637
- const prefixMatches = exact ? [exact] : runs.filter((run) => run.id.startsWith(runId));
4638
- if (prefixMatches.length === 0) {
4639
- throw new Error(`Run not found: ${runId}`);
4640
- }
4641
- if (prefixMatches.length > 1) {
4642
- throw new Error(
4643
- `Ambiguous run id prefix ${JSON.stringify(runId)}; matches ${prefixMatches.map((run) => shortId(run.id)).join(", ")}. Pass a longer prefix or the full id.`
4644
- );
4645
- }
4646
- const match = prefixMatches[0];
4647
- if (match.status !== "completed" && match.status !== "failed") {
4648
- throw new Error(
4649
- `Run ${shortId(match.id)} is still ${match.status}; only completed or failed runs can be published.`
4650
- );
4651
- }
4652
- return match;
4653
- }
4654
- const candidates = runs.filter((run) => run.status === "completed" || run.status === "failed").sort((a, b) => b.updated_at.localeCompare(a.updated_at));
4655
- const latest = candidates[0];
4656
- if (!latest) {
4657
- throw new Error(
4658
- "No completed or failed local runs found. Run `tt run` first, then publish."
4659
- );
4660
- }
4661
- return latest;
4662
- }
4663
- function assertPublishPayloadSize(payload) {
4664
- const bytes = Buffer.byteLength(JSON.stringify(payload), "utf8");
4665
- if (bytes > MAX_PUBLISH_BYTES) {
4666
- throw new Error(
4667
- `Publish payload is ${bytes} bytes; maximum is ${MAX_PUBLISH_BYTES} bytes. Reduce eval examples or publish a smaller run.`
4668
- );
4669
- }
4670
- }
4671
- function registerPublishCommand(parent) {
4672
- parent.command("publish").description("Publish local run evidence to the Tuned Tensor dashboard").argument("[runId]", "Local run id or prefix (defaults to latest completed/failed run)").option("-k, --api-key <key>", "API key (tt_...)").option("-u, --base-url <url>", "API base URL").option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show what would be published without uploading").action(async (runId, cmdOpts) => {
4673
- const opts = {
4674
- apiKey: cmdOpts.apiKey,
4675
- baseUrl: cmdOpts.baseUrl
4676
- };
4677
- if (!cmdOpts.dryRun && !getApiKey(opts)) {
4678
- throw new Error(
4679
- "No API key found. Run `tt auth login` or set TUNED_TENSOR_API_KEY."
4680
- );
4681
- }
4682
- const storeRoot = resolvePublishStoreRoot();
4683
- const store = createLocalStore(storeRoot);
4684
- const runs = await store.listRuns();
4685
- const run = pickPublishableRun(runs, runId);
4686
- const rawReport = await store.getRunReport(run.id);
4687
- const report = runReportSchema.parse(rawReport);
4688
- const specRecord = await store.getSpec(run.behavior_spec_id);
4689
- const exampleCount = report.candidate.results.length || report.baseline.results.length;
4690
- const payload = {
4691
- spec: {
4692
- id: specRecord.id,
4693
- ...specRecord.spec
4694
- },
4695
- report
4696
- };
4697
- assertPublishPayloadSize(payload);
4698
- if (isJsonMode() && cmdOpts.dryRun) {
4699
- printJson({
4700
- dry_run: true,
4701
- store_root: storeRoot,
4702
- run_id: run.id,
4703
- spec_id: specRecord.id,
4704
- spec_name: specRecord.spec.name,
4705
- run_number: run.run_number,
4706
- status: run.status,
4707
- example_count: exampleCount,
4708
- payload
4709
- });
4710
- return;
4711
- }
4712
- if (!isJsonMode()) {
4713
- printDetail([
4714
- ["Spec", specRecord.spec.name],
4715
- ["Run", `${shortId(run.id)} (#${run.run_number})`],
4716
- ["Status", run.status],
4717
- ["Examples", String(exampleCount)],
4718
- ["Store", storeRoot]
4719
- ]);
4720
- printWarning(
4721
- "Prompts and model outputs from the run report will be uploaded to your Tuned Tensor account."
4722
- );
4723
- }
4724
- if (cmdOpts.dryRun) {
4725
- if (isJsonMode()) {
4726
- printJson({
4727
- dry_run: true,
4728
- run_id: run.id,
4729
- spec_id: specRecord.id,
4730
- example_count: exampleCount
4731
- });
4732
- return;
4733
- }
4734
- printSuccess("Dry run only \u2014 nothing was uploaded.");
4735
- return;
4736
- }
4737
- if (!cmdOpts.yes && !isJsonMode()) {
4738
- const ok = await confirmPublish("Publish this run evidence? [y/N] ");
4739
- if (!ok) {
4740
- printWarning("Publish cancelled.");
4741
- return;
4742
- }
4743
- }
4744
- if (!cmdOpts.yes && isJsonMode()) {
4745
- throw new Error(
4746
- "JSON mode requires --yes to publish without an interactive prompt."
4747
- );
4748
- }
4749
- const { data: data3 } = await post("/publish/runs", payload, opts);
4750
- const baseUrl = getBaseUrl(opts).replace(/\/$/, "");
4751
- const dashboardUrl = `${baseUrl}/dashboard/runs/${data3.id}`;
4752
- if (isJsonMode()) {
4753
- printJson({ ...data3, dashboard_url: dashboardUrl });
4754
- return;
4755
- }
4756
- printSuccess(
4757
- `Published run #${data3.run_number} (${shortId(data3.id)}) for ${specRecord.spec.name}`
4758
- );
4759
- printDetail([
4760
- ["Dashboard", dashboardUrl],
4761
- ["Spec ID", shortId(data3.spec_id)]
4762
- ]);
4763
- });
4764
- }
4765
-
4766
4720
  // src/commands/pipeline.ts
4767
- import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
4768
- import { resolve as resolve5 } from "path";
4721
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
4722
+ import { resolve as resolve4 } from "path";
4769
4723
  var DEFAULT_PIPELINE_FILE = "tunedtensor.pipeline.json";
4770
4724
  function parseList(value) {
4771
4725
  if (!value) return void 0;
@@ -4774,10 +4728,10 @@ function parseList(value) {
4774
4728
  return ids;
4775
4729
  }
4776
4730
  function loadPipeline(path) {
4777
- const resolved = resolve5(path);
4731
+ const resolved = resolve4(path);
4778
4732
  if (!existsSync5(resolved)) throw new Error(`Pipeline file not found: ${path}. Run \`tt pipeline init\` to create one.`);
4779
4733
  try {
4780
- return JSON.parse(readFileSync5(resolved, "utf8"));
4734
+ return JSON.parse(readFileSync4(resolved, "utf8"));
4781
4735
  } catch (error) {
4782
4736
  throw new Error(`Pipeline file must be JSON: ${error.message}`);
4783
4737
  }
@@ -4793,7 +4747,7 @@ function outputPlan(plan) {
4793
4747
  function registerPipelineCommands(parent) {
4794
4748
  const pipeline = parent.command("pipeline").description("Create, validate, inspect, and safely dry-run ordered pipeline recipes");
4795
4749
  pipeline.command("init").description("Write a canonical v1 pipeline recipe").option("-f, --file <path>", "Output file", DEFAULT_PIPELINE_FILE).action((options) => {
4796
- const path = resolve5(options.file);
4750
+ const path = resolve4(options.file);
4797
4751
  if (existsSync5(path)) throw new Error(`Pipeline file already exists: ${options.file}.`);
4798
4752
  const recipe = canonicalPipeline("local");
4799
4753
  writeFileSync3(path, `${JSON.stringify(recipe, null, 2)}
@@ -4823,9 +4777,9 @@ function registerPipelineCommands(parent) {
4823
4777
  if (remote) {
4824
4778
  throw new Error(`Step "${remote.id}" targets cloud execution. This CLI is local-only; rewrite that step to local or use --dry-run.`);
4825
4779
  }
4826
- const input = await loadLocalRunInput(resolve5(options.spec));
4780
+ const input = await loadLocalRunInput(resolve4(options.spec));
4827
4781
  assertLocalRunInputReady(input.request);
4828
- const config = await loadLocalRunnerConfig(options.config ? resolve5(options.config) : void 0);
4782
+ const config = await loadLocalRunnerConfig(options.config ? resolve4(options.config) : void 0);
4829
4783
  const localPipeline = {
4830
4784
  version: 1,
4831
4785
  ...plan.name ? { name: plan.name } : {},
@@ -4888,7 +4842,7 @@ async function checkForCliUpdate(currentVersion, options = {}) {
4888
4842
  const controller = new AbortController();
4889
4843
  const timeoutMs = options.timeoutMs ?? 750;
4890
4844
  let timeout;
4891
- const request2 = (async () => {
4845
+ const request = (async () => {
4892
4846
  try {
4893
4847
  const response = await (options.fetchImpl ?? fetch)(PACKAGE_LATEST_URL, {
4894
4848
  headers: { accept: "application/json" },
@@ -4907,14 +4861,14 @@ async function checkForCliUpdate(currentVersion, options = {}) {
4907
4861
  return null;
4908
4862
  }
4909
4863
  })();
4910
- const deadline = new Promise((resolve6) => {
4864
+ const deadline = new Promise((resolve5) => {
4911
4865
  timeout = setTimeout(() => {
4912
4866
  controller.abort();
4913
- resolve6(null);
4867
+ resolve5(null);
4914
4868
  }, timeoutMs);
4915
4869
  });
4916
4870
  try {
4917
- return await Promise.race([request2, deadline]);
4871
+ return await Promise.race([request, deadline]);
4918
4872
  } finally {
4919
4873
  if (timeout) clearTimeout(timeout);
4920
4874
  }
@@ -4949,7 +4903,7 @@ async function createDefaultAgentClient(runtime, env, workspaceRoot, getModelRun
4949
4903
  const selection = getAgentSelection(env);
4950
4904
  if (!selection) {
4951
4905
  throw new Error(
4952
- "The laptop-local agent is not configured. Run `tt agent models --all`, then `tt agent configure --provider <provider> --model <model>`."
4906
+ "The laptop-local agent is not configured. Use /model to choose a provider and model, then try again."
4953
4907
  );
4954
4908
  }
4955
4909
  const modelRuntime = await getModelRuntime();
@@ -4958,8 +4912,9 @@ async function createDefaultAgentClient(runtime, env, workspaceRoot, getModelRun
4958
4912
  const secret = getApiKey({ apiKey: env.TUNED_TENSOR_API_KEY });
4959
4913
  const providerSecrets = Object.entries(env).filter(([name, value]) => value && /(?:API_KEY|TOKEN|SECRET)$/i.test(name)).map(([, value]) => value).filter((value) => value.length >= 8 && value !== secret);
4960
4914
  return createLocalAgentClient({
4961
- store: runtime.agentStore ?? new LocalAgentStore(join8(getConfigDir(), "agent"), {
4962
- secretValues: [...secret ? [secret] : [], ...providerSecrets]
4915
+ store: runtime.agentStore ?? new LocalAgentStore(getAgentConfigDir(), {
4916
+ secretValues: [...secret ? [secret] : [], ...providerSecrets],
4917
+ secretValueProvider: readStoredProviderSecrets
4963
4918
  }),
4964
4919
  workspaceRoot,
4965
4920
  selection,
@@ -5007,7 +4962,7 @@ async function runSelfCommand(args2, options = {}) {
5007
4962
  if (!entrypoint) {
5008
4963
  throw new Error("Cannot locate the tt CLI entrypoint.");
5009
4964
  }
5010
- return await new Promise((resolve6, reject) => {
4965
+ return await new Promise((resolve5, reject) => {
5011
4966
  const child = spawn2(process.execPath, [entrypoint, ...args2], {
5012
4967
  cwd: options.cwd,
5013
4968
  env: options.env ?? process.env,
@@ -5043,7 +4998,7 @@ async function runSelfCommand(args2, options = {}) {
5043
4998
  child.once("close", (exitCode, signal) => {
5044
4999
  cleanup();
5045
5000
  const signalNumber = signal ? osConstants2.signals[signal] : void 0;
5046
- resolve6({
5001
+ resolve5({
5047
5002
  exitCode: exitCode ?? (typeof signalNumber === "number" ? 128 + signalNumber : 1),
5048
5003
  signal
5049
5004
  });
@@ -5064,10 +5019,10 @@ function createProgram(version, runtime = {}) {
5064
5019
  const launchShell = runtime.startShell ?? startInteractiveShell;
5065
5020
  const cwd = runtime.cwd ?? process.cwd();
5066
5021
  const getModelRuntime = createModelRuntimeGetter(runtime);
5067
- const shellRunner = async (request2) => {
5022
+ const shellRunner = async (request) => {
5068
5023
  const root = program.opts();
5069
- return await invokeSelf(request2.args, {
5070
- cwd: request2.cwd,
5024
+ return await invokeSelf(request.args, {
5025
+ cwd: request.cwd,
5071
5026
  env: childEnvironment2(root, env),
5072
5027
  entrypoint: runtime.argv?.[1]
5073
5028
  });
@@ -5104,7 +5059,7 @@ function createProgram(version, runtime = {}) {
5104
5059
  if (root.json) setJsonMode(true);
5105
5060
  if (root.color === false) {
5106
5061
  process.env.FORCE_COLOR = "0";
5107
- chalk7.level = 0;
5062
+ chalk6.level = 0;
5108
5063
  }
5109
5064
  });
5110
5065
  registerLocalCommands(program, {
@@ -5115,8 +5070,6 @@ function createProgram(version, runtime = {}) {
5115
5070
  stdout: runtime.stdout ?? process.stdout,
5116
5071
  stderr: runtime.stderr ?? process.stderr
5117
5072
  });
5118
- registerAuthCommands(program);
5119
- registerPublishCommand(program);
5120
5073
  registerPipelineCommands(program);
5121
5074
  registerAgentCommands(program, {
5122
5075
  env,
@@ -5135,12 +5088,12 @@ function createProgram(version, runtime = {}) {
5135
5088
  `);
5136
5089
  return;
5137
5090
  }
5138
- output.write(`${chalk7.bold.hex("#8B5CF6")("Tuned Tensor status")}
5091
+ output.write(`${chalk6.bold.hex("#8B5CF6")("Tuned Tensor status")}
5139
5092
  `);
5140
5093
  output.write(`${formatShellStatus(context).join("\n")}
5141
5094
 
5142
5095
  `);
5143
- output.write(`${chalk7.bold("Context")}
5096
+ output.write(`${chalk6.bold("Context")}
5144
5097
  `);
5145
5098
  output.write(`${formatShellContext(context).join("\n")}
5146
5099
  `);
@@ -5149,19 +5102,11 @@ function createProgram(version, runtime = {}) {
5149
5102
  program.addHelpText(
5150
5103
  "after",
5151
5104
  `
5152
- Commands:
5153
- tt Open the conversational terminal (TTY only)
5154
- tt status Inspect local project context
5155
- tt doctor Check the host and run prerequisites
5156
- tt run Fine-tune and evaluate on your GPU
5157
- tt auth Store a dashboard API key
5158
- tt publish Upload local run evidence to the dashboard
5159
-
5160
5105
  Examples:
5106
+ tt Open the conversational terminal (TTY only)
5161
5107
  tt doctor tunedtensor.json
5162
5108
  tt run tunedtensor.json --dry-run
5163
5109
  tt runs list
5164
- tt publish --yes
5165
5110
  `
5166
5111
  );
5167
5112
  return program;
@@ -5217,10 +5162,10 @@ async function runCli(version, runtime = {}) {
5217
5162
  error
5218
5163
  });
5219
5164
  await (runtime.startShell ?? startInteractiveShell)({
5220
- runner: async (request2) => await invokeSelf(
5221
- request2.args,
5165
+ runner: async (request) => await invokeSelf(
5166
+ request.args,
5222
5167
  {
5223
- cwd: request2.cwd,
5168
+ cwd: request.cwd,
5224
5169
  env,
5225
5170
  entrypoint: argv[1]
5226
5171
  }
@@ -5241,7 +5186,7 @@ async function runCli(version, runtime = {}) {
5241
5186
  }
5242
5187
 
5243
5188
  // src/index.ts
5244
- runCli("0.13.0-beta.1").catch((err) => {
5189
+ runCli("0.13.1-beta.0").catch((err) => {
5245
5190
  reportError(err);
5246
5191
  process.exit(1);
5247
5192
  });