@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/README.md +89 -32
- package/dist/{chunk-2VJ3VVZF.js → chunk-YJYCGSHJ.js} +537 -536
- package/dist/chunk-YJYCGSHJ.js.map +1 -0
- package/dist/index.js +499 -554
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.js +39 -10
- package/dist/local-runtime.js.map +1 -1
- package/docs/local-runtime/architecture.md +6 -5
- package/docs/local-runtime/spark.md +23 -17
- package/package.json +1 -1
- package/training/local-runner/src/evaluate.py +4 -1
- package/training/local-runner/src/model_contract.py +5 -3
- package/training/local-runner/src/serve.py +1 -1
- package/dist/chunk-2VJ3VVZF.js.map +0 -1
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
|
-
|
|
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
|
|
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
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
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 =
|
|
618
|
-
|
|
736
|
+
const agentDir = getAgentConfigDir();
|
|
737
|
+
ensurePrivateDir(agentDir);
|
|
738
|
+
const modelsPath = getAgentModelsPath();
|
|
739
|
+
const authPath = getAgentAuthPath();
|
|
619
740
|
ensureOpenRouterAppHeaders(modelsPath);
|
|
620
|
-
|
|
621
|
-
authPath
|
|
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 (
|
|
766
|
+
if (existsSync2(modelsPath)) {
|
|
633
767
|
try {
|
|
634
|
-
const parsed = JSON.parse(
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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 =
|
|
1373
|
-
this.claimsDir =
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
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: "
|
|
1656
|
-
{ path: "
|
|
1657
|
-
{ path: "
|
|
1658
|
-
{ path: "
|
|
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(
|
|
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("
|
|
2644
|
+
detailLine("List", "/model <provider>"),
|
|
2645
|
+
detailLine("Search", "/model <query>"),
|
|
2646
|
+
detailLine("Login", "/login <provider>")
|
|
2446
2647
|
]);
|
|
2447
|
-
const
|
|
2448
|
-
|
|
2449
|
-
|
|
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
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
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 (
|
|
2476
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
2954
|
+
() => options.runner(request)
|
|
2665
2955
|
);
|
|
2666
2956
|
const session = await createShellSession({
|
|
2667
2957
|
runner: foregroundRunner,
|
|
2668
|
-
io
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
3268
|
+
const request = [action.method, action.path].filter(Boolean).join(" ");
|
|
2979
3269
|
const technical = {
|
|
2980
3270
|
operation: action.operation,
|
|
2981
|
-
...
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
3763
|
+
return isRecord4(value) ? value : void 0;
|
|
3527
3764
|
}
|
|
3528
3765
|
function rows(value) {
|
|
3529
|
-
return Array.isArray(value) ? value.filter(
|
|
3766
|
+
return Array.isArray(value) ? value.filter(isRecord4) : [];
|
|
3530
3767
|
}
|
|
3531
3768
|
function displayId(value) {
|
|
3532
3769
|
const id = text(value);
|
|
3533
|
-
|
|
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" &&
|
|
4056
|
+
if (command === "doctor" && isRecord4(value)) {
|
|
3818
4057
|
renderDoctor(value);
|
|
3819
4058
|
return true;
|
|
3820
4059
|
}
|
|
3821
|
-
if (command === "init" &&
|
|
4060
|
+
if (command === "init" && isRecord4(value)) {
|
|
3822
4061
|
renderInit(value);
|
|
3823
4062
|
return true;
|
|
3824
4063
|
}
|
|
3825
|
-
if (command === "validate" &&
|
|
4064
|
+
if (command === "validate" && isRecord4(value)) {
|
|
3826
4065
|
renderValidate(value);
|
|
3827
4066
|
return true;
|
|
3828
4067
|
}
|
|
3829
|
-
if (command === "run" &&
|
|
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" &&
|
|
4081
|
+
if (subcommand === "get" && isRecord4(value)) {
|
|
3843
4082
|
renderRunRecord(value);
|
|
3844
4083
|
return true;
|
|
3845
4084
|
}
|
|
3846
|
-
if (subcommand === "report" &&
|
|
4085
|
+
if (subcommand === "report" && isRecord4(value)) {
|
|
3847
4086
|
renderRunReport(value);
|
|
3848
4087
|
return true;
|
|
3849
4088
|
}
|
|
3850
|
-
if (subcommand === "compare" &&
|
|
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") &&
|
|
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") &&
|
|
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 ?? "") &&
|
|
4108
|
+
if (["active", "activate", "rollback"].includes(subcommand ?? "") && isRecord4(value)) {
|
|
3870
4109
|
renderActiveModel(value);
|
|
3871
4110
|
return true;
|
|
3872
4111
|
}
|
|
3873
|
-
if (subcommand === "serve" &&
|
|
4112
|
+
if (subcommand === "serve" && isRecord4(value)) {
|
|
3874
4113
|
renderServePlan(value);
|
|
3875
4114
|
return true;
|
|
3876
4115
|
}
|
|
3877
4116
|
}
|
|
3878
|
-
if (command === "serve" &&
|
|
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 (!
|
|
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
|
|
4768
|
-
import { resolve as
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
4780
|
+
const input = await loadLocalRunInput(resolve4(options.spec));
|
|
4827
4781
|
assertLocalRunInputReady(input.request);
|
|
4828
|
-
const config = await loadLocalRunnerConfig(options.config ?
|
|
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
|
|
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((
|
|
4864
|
+
const deadline = new Promise((resolve5) => {
|
|
4911
4865
|
timeout = setTimeout(() => {
|
|
4912
4866
|
controller.abort();
|
|
4913
|
-
|
|
4867
|
+
resolve5(null);
|
|
4914
4868
|
}, timeoutMs);
|
|
4915
4869
|
});
|
|
4916
4870
|
try {
|
|
4917
|
-
return await Promise.race([
|
|
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.
|
|
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(
|
|
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((
|
|
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
|
-
|
|
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 (
|
|
5022
|
+
const shellRunner = async (request) => {
|
|
5068
5023
|
const root = program.opts();
|
|
5069
|
-
return await invokeSelf(
|
|
5070
|
-
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
|
-
|
|
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(`${
|
|
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(`${
|
|
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 (
|
|
5221
|
-
|
|
5165
|
+
runner: async (request) => await invokeSelf(
|
|
5166
|
+
request.args,
|
|
5222
5167
|
{
|
|
5223
|
-
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.
|
|
5189
|
+
runCli("0.13.1-beta.0").catch((err) => {
|
|
5245
5190
|
reportError(err);
|
|
5246
5191
|
process.exit(1);
|
|
5247
5192
|
});
|