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

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,16 +2,19 @@
2
2
  import {
3
3
  TRAINING_MODELS,
4
4
  assertLocalRunInputReady,
5
+ createLocalStore,
6
+ defaultLocalHome,
5
7
  loadLocalRunInput,
6
8
  loadLocalRunnerConfig,
7
- runLocalPipeline
8
- } from "./chunk-HDGBDHOB.js";
9
+ runLocalPipeline,
10
+ runReportSchema
11
+ } from "./chunk-2VJ3VVZF.js";
9
12
 
10
13
  // src/cli.ts
11
14
  import { spawn as spawn2 } from "child_process";
12
15
  import { constants as osConstants2 } from "os";
13
16
  import { Command } from "commander";
14
- import chalk6 from "chalk";
17
+ import chalk7 from "chalk";
15
18
 
16
19
  // src/local-agent-client.ts
17
20
  import { randomUUID as randomUUID2 } from "crypto";
@@ -1585,6 +1588,10 @@ function updateConfig(partial) {
1585
1588
  const current = readConfig();
1586
1589
  writeConfig({ ...current, ...partial });
1587
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
+ }
1588
1595
  function getApiKey(opts) {
1589
1596
  return opts?.apiKey || process.env.TUNED_TENSOR_API_KEY || readConfig().api_key;
1590
1597
  }
@@ -1608,7 +1615,7 @@ function getAgentSelection(env = process.env) {
1608
1615
  }
1609
1616
 
1610
1617
  // src/cli.ts
1611
- import { join as join7 } from "path";
1618
+ import { join as join8 } from "path";
1612
1619
 
1613
1620
  // src/agent.ts
1614
1621
  import chalk3 from "chalk";
@@ -1644,7 +1651,11 @@ var COMMAND_CATALOG = [
1644
1651
  { path: "models activate", description: "Activate a verified local model.", group: "Serving", modes: LOCAL },
1645
1652
  { path: "models rollback", description: "Roll back the active local model.", group: "Serving", modes: LOCAL },
1646
1653
  { path: "serve", description: "Serve a local adapter, active model, or base model.", group: "Serving", modes: LOCAL },
1647
- { path: "info", description: "Show local runtime package information.", group: "Inspect", modes: LOCAL }
1654
+ { 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 }
1648
1659
  ];
1649
1660
  var SLASH_COMMANDS = [
1650
1661
  { path: "/help", description: "Show commands; add a word to filter." },
@@ -2644,13 +2655,13 @@ async function startInteractiveShell(options) {
2644
2655
  }
2645
2656
  let readline;
2646
2657
  const terminalInput = input;
2647
- const foregroundRunner = (request) => withForegroundSignalHandoff(
2658
+ const foregroundRunner = (request2) => withForegroundSignalHandoff(
2648
2659
  {
2649
2660
  input: terminalInput,
2650
2661
  pauseReadline: () => readline?.pause(),
2651
2662
  resumeReadline: () => readline?.resume()
2652
2663
  },
2653
- () => options.runner(request)
2664
+ () => options.runner(request2)
2654
2665
  );
2655
2666
  const session = await createShellSession({
2656
2667
  runner: foregroundRunner,
@@ -2964,10 +2975,10 @@ ${chalk3.yellow.bold("Approval required")} ${chalk3.dim(action.id.slice(0, 8))}
2964
2975
  `);
2965
2976
  if (action.summary) this.options.io.write(`${sanitizeTerminalText2(action.summary)}
2966
2977
  `);
2967
- const request = [action.method, action.path].filter(Boolean).join(" ");
2978
+ const request2 = [action.method, action.path].filter(Boolean).join(" ");
2968
2979
  const technical = {
2969
2980
  operation: action.operation,
2970
- ...request ? { request } : {},
2981
+ ...request2 ? { request: request2 } : {},
2971
2982
  arguments: action.arguments ?? null,
2972
2983
  preview: action.preview ?? null
2973
2984
  };
@@ -3350,6 +3361,59 @@ var ApiError = class extends Error {
3350
3361
  this.name = "ApiError";
3351
3362
  }
3352
3363
  };
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
+ }
3353
3417
 
3354
3418
  // src/output.ts
3355
3419
  var labelStyle = chalk4.bold.hex("#A78BFA");
@@ -4096,10 +4160,10 @@ function localCommandStreamsStdout(args2) {
4096
4160
  const serves = args2[0] === "serve" || args2[0] === "models" && args2[1] === "serve";
4097
4161
  return serves && !args2.includes("--print-command") && !args2.includes("--help") && !args2.includes("-h");
4098
4162
  }
4099
- function parseJsonOutput(stdout) {
4100
- if (!stdout.trim()) return { hasJson: false, json: void 0 };
4163
+ function parseJsonOutput(stdout2) {
4164
+ if (!stdout2.trim()) return { hasJson: false, json: void 0 };
4101
4165
  try {
4102
- return { hasJson: true, json: JSON.parse(stdout) };
4166
+ return { hasJson: true, json: JSON.parse(stdout2) };
4103
4167
  } catch {
4104
4168
  return { hasJson: false, json: void 0 };
4105
4169
  }
@@ -4145,7 +4209,7 @@ async function spawnLocalCli(args2) {
4145
4209
  }
4146
4210
  );
4147
4211
  if (pipeInput && args2.stdin && child.stdin) args2.stdin.pipe(child.stdin);
4148
- let stdout = "";
4212
+ let stdout2 = "";
4149
4213
  let stderr = "";
4150
4214
  child.stdout?.setEncoding("utf8");
4151
4215
  child.stderr?.setEncoding("utf8");
@@ -4155,7 +4219,7 @@ async function spawnLocalCli(args2) {
4155
4219
  args2.pathReplacements
4156
4220
  );
4157
4221
  if (args2.streamingStdout) writeStream(output, normalized);
4158
- else stdout += normalized;
4222
+ else stdout2 += normalized;
4159
4223
  });
4160
4224
  child.stderr?.on("data", (chunk) => {
4161
4225
  const normalized = normalizeProjectedPaths(
@@ -4200,7 +4264,7 @@ async function spawnLocalCli(args2) {
4200
4264
  resolveChild({
4201
4265
  exitCode: spawnError ? 1 : code2 ?? signalExitCode(signal),
4202
4266
  signal,
4203
- stdout,
4267
+ stdout: stdout2,
4204
4268
  stderr,
4205
4269
  ...spawnError ? { errorMessage: spawnError.message } : {}
4206
4270
  });
@@ -4414,9 +4478,294 @@ function registerAgentCommands(parent, options) {
4414
4478
  });
4415
4479
  }
4416
4480
 
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
+
4417
4766
  // src/commands/pipeline.ts
4418
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
4419
- import { resolve as resolve4 } from "path";
4767
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
4768
+ import { resolve as resolve5 } from "path";
4420
4769
  var DEFAULT_PIPELINE_FILE = "tunedtensor.pipeline.json";
4421
4770
  function parseList(value) {
4422
4771
  if (!value) return void 0;
@@ -4425,10 +4774,10 @@ function parseList(value) {
4425
4774
  return ids;
4426
4775
  }
4427
4776
  function loadPipeline(path) {
4428
- const resolved = resolve4(path);
4429
- if (!existsSync4(resolved)) throw new Error(`Pipeline file not found: ${path}. Run \`tt pipeline init\` to create one.`);
4777
+ const resolved = resolve5(path);
4778
+ if (!existsSync5(resolved)) throw new Error(`Pipeline file not found: ${path}. Run \`tt pipeline init\` to create one.`);
4430
4779
  try {
4431
- return JSON.parse(readFileSync4(resolved, "utf8"));
4780
+ return JSON.parse(readFileSync5(resolved, "utf8"));
4432
4781
  } catch (error) {
4433
4782
  throw new Error(`Pipeline file must be JSON: ${error.message}`);
4434
4783
  }
@@ -4444,8 +4793,8 @@ function outputPlan(plan) {
4444
4793
  function registerPipelineCommands(parent) {
4445
4794
  const pipeline = parent.command("pipeline").description("Create, validate, inspect, and safely dry-run ordered pipeline recipes");
4446
4795
  pipeline.command("init").description("Write a canonical v1 pipeline recipe").option("-f, --file <path>", "Output file", DEFAULT_PIPELINE_FILE).action((options) => {
4447
- const path = resolve4(options.file);
4448
- if (existsSync4(path)) throw new Error(`Pipeline file already exists: ${options.file}.`);
4796
+ const path = resolve5(options.file);
4797
+ if (existsSync5(path)) throw new Error(`Pipeline file already exists: ${options.file}.`);
4449
4798
  const recipe = canonicalPipeline("local");
4450
4799
  writeFileSync3(path, `${JSON.stringify(recipe, null, 2)}
4451
4800
  `);
@@ -4474,9 +4823,9 @@ function registerPipelineCommands(parent) {
4474
4823
  if (remote) {
4475
4824
  throw new Error(`Step "${remote.id}" targets cloud execution. This CLI is local-only; rewrite that step to local or use --dry-run.`);
4476
4825
  }
4477
- const input = await loadLocalRunInput(resolve4(options.spec));
4826
+ const input = await loadLocalRunInput(resolve5(options.spec));
4478
4827
  assertLocalRunInputReady(input.request);
4479
- const config = await loadLocalRunnerConfig(options.config ? resolve4(options.config) : void 0);
4828
+ const config = await loadLocalRunnerConfig(options.config ? resolve5(options.config) : void 0);
4480
4829
  const localPipeline = {
4481
4830
  version: 1,
4482
4831
  ...plan.name ? { name: plan.name } : {},
@@ -4539,7 +4888,7 @@ async function checkForCliUpdate(currentVersion, options = {}) {
4539
4888
  const controller = new AbortController();
4540
4889
  const timeoutMs = options.timeoutMs ?? 750;
4541
4890
  let timeout;
4542
- const request = (async () => {
4891
+ const request2 = (async () => {
4543
4892
  try {
4544
4893
  const response = await (options.fetchImpl ?? fetch)(PACKAGE_LATEST_URL, {
4545
4894
  headers: { accept: "application/json" },
@@ -4558,14 +4907,14 @@ async function checkForCliUpdate(currentVersion, options = {}) {
4558
4907
  return null;
4559
4908
  }
4560
4909
  })();
4561
- const deadline = new Promise((resolve5) => {
4910
+ const deadline = new Promise((resolve6) => {
4562
4911
  timeout = setTimeout(() => {
4563
4912
  controller.abort();
4564
- resolve5(null);
4913
+ resolve6(null);
4565
4914
  }, timeoutMs);
4566
4915
  });
4567
4916
  try {
4568
- return await Promise.race([request, deadline]);
4917
+ return await Promise.race([request2, deadline]);
4569
4918
  } finally {
4570
4919
  if (timeout) clearTimeout(timeout);
4571
4920
  }
@@ -4609,7 +4958,7 @@ async function createDefaultAgentClient(runtime, env, workspaceRoot, getModelRun
4609
4958
  const secret = getApiKey({ apiKey: env.TUNED_TENSOR_API_KEY });
4610
4959
  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);
4611
4960
  return createLocalAgentClient({
4612
- store: runtime.agentStore ?? new LocalAgentStore(join7(getConfigDir(), "agent"), {
4961
+ store: runtime.agentStore ?? new LocalAgentStore(join8(getConfigDir(), "agent"), {
4613
4962
  secretValues: [...secret ? [secret] : [], ...providerSecrets]
4614
4963
  }),
4615
4964
  workspaceRoot,
@@ -4658,7 +5007,7 @@ async function runSelfCommand(args2, options = {}) {
4658
5007
  if (!entrypoint) {
4659
5008
  throw new Error("Cannot locate the tt CLI entrypoint.");
4660
5009
  }
4661
- return await new Promise((resolve5, reject) => {
5010
+ return await new Promise((resolve6, reject) => {
4662
5011
  const child = spawn2(process.execPath, [entrypoint, ...args2], {
4663
5012
  cwd: options.cwd,
4664
5013
  env: options.env ?? process.env,
@@ -4694,7 +5043,7 @@ async function runSelfCommand(args2, options = {}) {
4694
5043
  child.once("close", (exitCode, signal) => {
4695
5044
  cleanup();
4696
5045
  const signalNumber = signal ? osConstants2.signals[signal] : void 0;
4697
- resolve5({
5046
+ resolve6({
4698
5047
  exitCode: exitCode ?? (typeof signalNumber === "number" ? 128 + signalNumber : 1),
4699
5048
  signal
4700
5049
  });
@@ -4715,10 +5064,10 @@ function createProgram(version, runtime = {}) {
4715
5064
  const launchShell = runtime.startShell ?? startInteractiveShell;
4716
5065
  const cwd = runtime.cwd ?? process.cwd();
4717
5066
  const getModelRuntime = createModelRuntimeGetter(runtime);
4718
- const shellRunner = async (request) => {
5067
+ const shellRunner = async (request2) => {
4719
5068
  const root = program.opts();
4720
- return await invokeSelf(request.args, {
4721
- cwd: request.cwd,
5069
+ return await invokeSelf(request2.args, {
5070
+ cwd: request2.cwd,
4722
5071
  env: childEnvironment2(root, env),
4723
5072
  entrypoint: runtime.argv?.[1]
4724
5073
  });
@@ -4755,7 +5104,7 @@ function createProgram(version, runtime = {}) {
4755
5104
  if (root.json) setJsonMode(true);
4756
5105
  if (root.color === false) {
4757
5106
  process.env.FORCE_COLOR = "0";
4758
- chalk6.level = 0;
5107
+ chalk7.level = 0;
4759
5108
  }
4760
5109
  });
4761
5110
  registerLocalCommands(program, {
@@ -4766,6 +5115,8 @@ function createProgram(version, runtime = {}) {
4766
5115
  stdout: runtime.stdout ?? process.stdout,
4767
5116
  stderr: runtime.stderr ?? process.stderr
4768
5117
  });
5118
+ registerAuthCommands(program);
5119
+ registerPublishCommand(program);
4769
5120
  registerPipelineCommands(program);
4770
5121
  registerAgentCommands(program, {
4771
5122
  env,
@@ -4784,12 +5135,12 @@ function createProgram(version, runtime = {}) {
4784
5135
  `);
4785
5136
  return;
4786
5137
  }
4787
- output.write(`${chalk6.bold.hex("#8B5CF6")("Tuned Tensor status")}
5138
+ output.write(`${chalk7.bold.hex("#8B5CF6")("Tuned Tensor status")}
4788
5139
  `);
4789
5140
  output.write(`${formatShellStatus(context).join("\n")}
4790
5141
 
4791
5142
  `);
4792
- output.write(`${chalk6.bold("Context")}
5143
+ output.write(`${chalk7.bold("Context")}
4793
5144
  `);
4794
5145
  output.write(`${formatShellContext(context).join("\n")}
4795
5146
  `);
@@ -4803,11 +5154,14 @@ Commands:
4803
5154
  tt status Inspect local project context
4804
5155
  tt doctor Check the host and run prerequisites
4805
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
4806
5159
 
4807
5160
  Examples:
4808
5161
  tt doctor tunedtensor.json
4809
5162
  tt run tunedtensor.json --dry-run
4810
5163
  tt runs list
5164
+ tt publish --yes
4811
5165
  `
4812
5166
  );
4813
5167
  return program;
@@ -4863,10 +5217,10 @@ async function runCli(version, runtime = {}) {
4863
5217
  error
4864
5218
  });
4865
5219
  await (runtime.startShell ?? startInteractiveShell)({
4866
- runner: async (request) => await invokeSelf(
4867
- request.args,
5220
+ runner: async (request2) => await invokeSelf(
5221
+ request2.args,
4868
5222
  {
4869
- cwd: request.cwd,
5223
+ cwd: request2.cwd,
4870
5224
  env,
4871
5225
  entrypoint: argv[1]
4872
5226
  }
@@ -4887,7 +5241,7 @@ async function runCli(version, runtime = {}) {
4887
5241
  }
4888
5242
 
4889
5243
  // src/index.ts
4890
- runCli("0.13.0-beta.0").catch((err) => {
5244
+ runCli("0.13.0-beta.1").catch((err) => {
4891
5245
  reportError(err);
4892
5246
  process.exit(1);
4893
5247
  });