@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/README.md +1 -1
- package/dist/{chunk-HDGBDHOB.js → chunk-2VJ3VVZF.js} +383 -370
- package/dist/chunk-2VJ3VVZF.js.map +1 -0
- package/dist/index.js +395 -41
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-HDGBDHOB.js.map +0 -1
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
|
-
|
|
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
|
|
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
|
|
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 = (
|
|
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(
|
|
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
|
|
2978
|
+
const request2 = [action.method, action.path].filter(Boolean).join(" ");
|
|
2968
2979
|
const technical = {
|
|
2969
2980
|
operation: action.operation,
|
|
2970
|
-
...
|
|
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(
|
|
4100
|
-
if (!
|
|
4163
|
+
function parseJsonOutput(stdout2) {
|
|
4164
|
+
if (!stdout2.trim()) return { hasJson: false, json: void 0 };
|
|
4101
4165
|
try {
|
|
4102
|
-
return { hasJson: true, json: JSON.parse(
|
|
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
|
|
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
|
|
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
|
|
4419
|
-
import { resolve as
|
|
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 =
|
|
4429
|
-
if (!
|
|
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(
|
|
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 =
|
|
4448
|
-
if (
|
|
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(
|
|
4826
|
+
const input = await loadLocalRunInput(resolve5(options.spec));
|
|
4478
4827
|
assertLocalRunInputReady(input.request);
|
|
4479
|
-
const config = await loadLocalRunnerConfig(options.config ?
|
|
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
|
|
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((
|
|
4910
|
+
const deadline = new Promise((resolve6) => {
|
|
4562
4911
|
timeout = setTimeout(() => {
|
|
4563
4912
|
controller.abort();
|
|
4564
|
-
|
|
4913
|
+
resolve6(null);
|
|
4565
4914
|
}, timeoutMs);
|
|
4566
4915
|
});
|
|
4567
4916
|
try {
|
|
4568
|
-
return await Promise.race([
|
|
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(
|
|
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((
|
|
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
|
-
|
|
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 (
|
|
5067
|
+
const shellRunner = async (request2) => {
|
|
4719
5068
|
const root = program.opts();
|
|
4720
|
-
return await invokeSelf(
|
|
4721
|
-
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
|
-
|
|
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(`${
|
|
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(`${
|
|
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 (
|
|
4867
|
-
|
|
5220
|
+
runner: async (request2) => await invokeSelf(
|
|
5221
|
+
request2.args,
|
|
4868
5222
|
{
|
|
4869
|
-
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.
|
|
5244
|
+
runCli("0.13.0-beta.1").catch((err) => {
|
|
4891
5245
|
reportError(err);
|
|
4892
5246
|
process.exit(1);
|
|
4893
5247
|
});
|