@tuned-tensor/cli 0.27.0 → 0.28.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 +20 -2
- package/dist/{chunk-VQWJ2XKR.js → chunk-7CO5DBCY.js} +448 -123
- package/dist/chunk-7CO5DBCY.js.map +1 -0
- package/dist/{chunk-2AIP63QO.js → chunk-FSVTMX4L.js} +13 -7
- package/dist/chunk-FSVTMX4L.js.map +1 -0
- package/dist/index.js +277 -179
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.js +22 -9
- package/dist/local-runtime.js.map +1 -1
- package/dist/{prefetch-B6I7B4LR.js → prefetch-LBIOO7ZO.js} +2 -2
- package/docs/windows.md +151 -0
- package/package.json +3 -1
- package/dist/chunk-2AIP63QO.js.map +0 -1
- package/dist/chunk-VQWJ2XKR.js.map +0 -1
- /package/dist/{prefetch-B6I7B4LR.js.map → prefetch-LBIOO7ZO.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
readHardwareSnapshot,
|
|
50
50
|
readJsonlTail,
|
|
51
51
|
readSpecHistory,
|
|
52
|
+
renameAtomicFile,
|
|
52
53
|
resolveProjectConfig,
|
|
53
54
|
runGpuProcess,
|
|
54
55
|
runLocalPipeline,
|
|
@@ -64,7 +65,7 @@ import {
|
|
|
64
65
|
warningsFromSnapshot,
|
|
65
66
|
withAppendLock,
|
|
66
67
|
writePrivateJsonAtomic
|
|
67
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-7CO5DBCY.js";
|
|
68
69
|
import {
|
|
69
70
|
DECISION_MODELS,
|
|
70
71
|
DEFAULT_ARTIFACT_ROOT,
|
|
@@ -96,10 +97,10 @@ import {
|
|
|
96
97
|
specLockPath,
|
|
97
98
|
withDecisionPythonEnvironment,
|
|
98
99
|
withFoundationPythonEnvironment
|
|
99
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-FSVTMX4L.js";
|
|
100
101
|
|
|
101
102
|
// src/cli.ts
|
|
102
|
-
import { spawn as
|
|
103
|
+
import { spawn as spawn7 } from "child_process";
|
|
103
104
|
import { constants as osConstants2 } from "os";
|
|
104
105
|
import { Command } from "commander";
|
|
105
106
|
import chalk15 from "chalk";
|
|
@@ -661,7 +662,38 @@ import { randomUUID as randomUUID3 } from "crypto";
|
|
|
661
662
|
import { closeSync, openSync } from "fs";
|
|
662
663
|
import { mkdir, open, readdir, readFile as readFile2, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
663
664
|
import { hostname, userInfo } from "os";
|
|
664
|
-
import { basename as basename2, dirname as dirname3, join as
|
|
665
|
+
import { basename as basename2, dirname as dirname3, join as join4 } from "path";
|
|
666
|
+
|
|
667
|
+
// src/local-runtime/windows-child-signals.ts
|
|
668
|
+
import { spawn } from "child_process";
|
|
669
|
+
import { join as join3 } from "path";
|
|
670
|
+
function windowsChildSignals(child) {
|
|
671
|
+
let timer;
|
|
672
|
+
const dispose = () => {
|
|
673
|
+
if (timer) clearTimeout(timer);
|
|
674
|
+
};
|
|
675
|
+
child.once("close", dispose);
|
|
676
|
+
return {
|
|
677
|
+
dispose,
|
|
678
|
+
forward(signal) {
|
|
679
|
+
if (process.platform !== "win32" || !child.connected) return false;
|
|
680
|
+
child.send({ ttSignal: signal }, () => {
|
|
681
|
+
});
|
|
682
|
+
timer ??= setTimeout(() => {
|
|
683
|
+
if (child.pid) {
|
|
684
|
+
spawn(
|
|
685
|
+
join3(process.env.SystemRoot ?? "C:\\Windows", "System32", "taskkill.exe"),
|
|
686
|
+
["/PID", String(child.pid), "/T", "/F"],
|
|
687
|
+
{ windowsHide: true, stdio: "ignore" }
|
|
688
|
+
).on("error", () => child.kill());
|
|
689
|
+
}
|
|
690
|
+
}, 5e3);
|
|
691
|
+
return true;
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// src/local-runtime/background-runs.ts
|
|
665
697
|
var ACTIVE = /* @__PURE__ */ new Set(["starting", "running", "cancel_requested"]);
|
|
666
698
|
var DEFAULT_MAX_PARALLEL_RUNS = 1;
|
|
667
699
|
var MAX_PARALLEL_RUNS_LIMIT = 8;
|
|
@@ -675,7 +707,7 @@ var BackgroundRunError = class extends Error {
|
|
|
675
707
|
}
|
|
676
708
|
};
|
|
677
709
|
function backgroundRunsRoot(env = process.env) {
|
|
678
|
-
return
|
|
710
|
+
return join4(getTunedTensorHome(env), "background-runs");
|
|
679
711
|
}
|
|
680
712
|
function maxParallelRuns(env = process.env) {
|
|
681
713
|
const raw = env.TT_MAX_PARALLEL_RUNS?.trim();
|
|
@@ -736,17 +768,17 @@ function plainLogText(text3) {
|
|
|
736
768
|
}
|
|
737
769
|
function createBackgroundRunStore(root = backgroundRunsRoot()) {
|
|
738
770
|
const paths = (id) => {
|
|
739
|
-
const dir =
|
|
771
|
+
const dir = join4(root, id);
|
|
740
772
|
return {
|
|
741
773
|
dir,
|
|
742
|
-
record:
|
|
743
|
-
spec:
|
|
744
|
-
events:
|
|
745
|
-
log:
|
|
746
|
-
result:
|
|
774
|
+
record: join4(dir, "record.json"),
|
|
775
|
+
spec: join4(dir, "spec.json"),
|
|
776
|
+
events: join4(dir, "events.jsonl"),
|
|
777
|
+
log: join4(dir, "output.log"),
|
|
778
|
+
result: join4(dir, "result.json")
|
|
747
779
|
};
|
|
748
780
|
};
|
|
749
|
-
const lockPath = (id) =>
|
|
781
|
+
const lockPath = (id) => join4(paths(id).dir, ".lock");
|
|
750
782
|
async function readRecord(id) {
|
|
751
783
|
return JSON.parse(await readFile2(paths(id).record, "utf8"));
|
|
752
784
|
}
|
|
@@ -845,7 +877,7 @@ function createBackgroundRunStore(root = backgroundRunsRoot()) {
|
|
|
845
877
|
async create(input, options = {}) {
|
|
846
878
|
const limit = options.maxParallel ?? DEFAULT_MAX_PARALLEL_RUNS;
|
|
847
879
|
await mkdir(root, { recursive: true, mode: 448 });
|
|
848
|
-
return await withAppendLock(
|
|
880
|
+
return await withAppendLock(join4(root, ".launch.lock"), async () => {
|
|
849
881
|
const active = (await list()).filter(isActiveBackgroundRun);
|
|
850
882
|
if (active.length >= limit) {
|
|
851
883
|
throw new BackgroundRunError(
|
|
@@ -896,7 +928,7 @@ function createBackgroundRunStore(root = backgroundRunsRoot()) {
|
|
|
896
928
|
details: { reason }
|
|
897
929
|
});
|
|
898
930
|
const pid = record6.process?.supervisor_pid;
|
|
899
|
-
if (wasRunning && pid !== void 0 && record6.process?.host === hostname()) {
|
|
931
|
+
if (process.platform !== "win32" && wasRunning && pid !== void 0 && record6.process?.host === hostname()) {
|
|
900
932
|
try {
|
|
901
933
|
process.kill(pid, "SIGTERM");
|
|
902
934
|
} catch {
|
|
@@ -1069,8 +1101,8 @@ async function launchBackgroundSupervisor(args2) {
|
|
|
1069
1101
|
});
|
|
1070
1102
|
}
|
|
1071
1103
|
function workspaceSnapshotPath(run) {
|
|
1072
|
-
const specFile =
|
|
1073
|
-
return
|
|
1104
|
+
const specFile = join4(run.choices.workspace_root, run.choices.spec_path);
|
|
1105
|
+
return join4(dirname3(specFile), `.${basename2(specFile)}.tt-agent-${run.id}.json`);
|
|
1074
1106
|
}
|
|
1075
1107
|
async function superviseBackgroundRun(options) {
|
|
1076
1108
|
const { store } = options;
|
|
@@ -1095,28 +1127,34 @@ async function superviseBackgroundRun(options) {
|
|
|
1095
1127
|
return await finishEarly("failed", `Could not prepare the run: ${error.message}`);
|
|
1096
1128
|
}
|
|
1097
1129
|
let child;
|
|
1130
|
+
let windowsSignals;
|
|
1131
|
+
let cancellationPoll;
|
|
1098
1132
|
const state = { exitCode: null, exitSignal: null };
|
|
1099
1133
|
const stop = (signal) => {
|
|
1100
1134
|
state.stopSignal ??= signal;
|
|
1101
1135
|
if (!child?.pid) return;
|
|
1102
1136
|
try {
|
|
1103
1137
|
if (process.platform !== "win32") process.kill(-child.pid, "SIGTERM");
|
|
1104
|
-
else child.kill("SIGTERM");
|
|
1138
|
+
else if (!windowsSignals?.forward("SIGTERM")) child.kill("SIGTERM");
|
|
1105
1139
|
} catch {
|
|
1106
1140
|
}
|
|
1107
1141
|
};
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1142
|
+
const onSigterm = () => stop("SIGTERM");
|
|
1143
|
+
const onSigint = () => stop("SIGINT");
|
|
1144
|
+
const onSighup = () => stop("SIGHUP");
|
|
1145
|
+
process.on("SIGTERM", onSigterm);
|
|
1146
|
+
process.on("SIGINT", onSigint);
|
|
1147
|
+
if (process.platform !== "win32") process.on("SIGHUP", onSighup);
|
|
1112
1148
|
try {
|
|
1113
1149
|
const spawnImpl = options.spawn ?? nodeSpawn;
|
|
1114
1150
|
child = spawnImpl(options.execPath ?? process.execPath, [options.entrypoint, "--json", "pipeline", "run", "--spec", snapshot], {
|
|
1115
1151
|
cwd: run.choices.workspace_root,
|
|
1116
|
-
env: options.env ?? process.env,
|
|
1152
|
+
env: process.platform === "win32" ? { ...options.env ?? process.env, TT_PARENT_IPC: "1" } : options.env ?? process.env,
|
|
1117
1153
|
detached: process.platform !== "win32",
|
|
1118
|
-
stdio: ["ignore", resultFd, logFd]
|
|
1154
|
+
stdio: ["ignore", resultFd, logFd, ...process.platform === "win32" ? ["ipc"] : []],
|
|
1155
|
+
windowsHide: true
|
|
1119
1156
|
});
|
|
1157
|
+
windowsSignals = windowsChildSignals(child);
|
|
1120
1158
|
const closed = new Promise((resolve14) => {
|
|
1121
1159
|
child.once("error", (error) => {
|
|
1122
1160
|
state.spawnError = error;
|
|
@@ -1135,12 +1173,27 @@ async function superviseBackgroundRun(options) {
|
|
|
1135
1173
|
current.process = { ...current.process, host: hostname(), supervisor_pid: process.pid, pid: child.pid };
|
|
1136
1174
|
}, { type: "started", message: `Pipeline process ${child.pid} started.` });
|
|
1137
1175
|
if ((await store.get(run.id)).status === "cancel_requested") stop("SIGTERM");
|
|
1176
|
+
if (process.platform === "win32") {
|
|
1177
|
+
let polling = false;
|
|
1178
|
+
cancellationPoll = setInterval(() => {
|
|
1179
|
+
if (polling || state.stopSignal) return;
|
|
1180
|
+
polling = true;
|
|
1181
|
+
void store.get(run.id).then((current) => {
|
|
1182
|
+
if (current.status === "cancel_requested") stop("SIGTERM");
|
|
1183
|
+
}).catch(() => {
|
|
1184
|
+
}).finally(() => {
|
|
1185
|
+
polling = false;
|
|
1186
|
+
});
|
|
1187
|
+
}, 250);
|
|
1188
|
+
}
|
|
1138
1189
|
}
|
|
1139
1190
|
await closed;
|
|
1140
1191
|
} finally {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1192
|
+
if (cancellationPoll) clearInterval(cancellationPoll);
|
|
1193
|
+
windowsSignals?.dispose();
|
|
1194
|
+
process.off("SIGTERM", onSigterm);
|
|
1195
|
+
process.off("SIGINT", onSigint);
|
|
1196
|
+
if (process.platform !== "win32") process.off("SIGHUP", onSighup);
|
|
1144
1197
|
closeSync(logFd);
|
|
1145
1198
|
closeSync(resultFd);
|
|
1146
1199
|
await rm3(snapshot, { force: true });
|
|
@@ -1325,7 +1378,7 @@ function createBackgroundRunExecutor(args2) {
|
|
|
1325
1378
|
// src/agent-labeling.ts
|
|
1326
1379
|
import { createHash as createHash4 } from "crypto";
|
|
1327
1380
|
import { existsSync as existsSync3, lstatSync as lstatSync2, readFileSync as readFileSync4 } from "fs";
|
|
1328
|
-
import { isAbsolute as isAbsolute2, join as
|
|
1381
|
+
import { isAbsolute as isAbsolute2, join as join6, relative as relative4 } from "path";
|
|
1329
1382
|
import { Type } from "typebox";
|
|
1330
1383
|
|
|
1331
1384
|
// src/commands/label.ts
|
|
@@ -1345,7 +1398,7 @@ import {
|
|
|
1345
1398
|
readFileSync,
|
|
1346
1399
|
writeFileSync
|
|
1347
1400
|
} from "fs";
|
|
1348
|
-
import { join as
|
|
1401
|
+
import { join as join5 } from "path";
|
|
1349
1402
|
var AGENT_THINKING_LEVELS = [
|
|
1350
1403
|
"off",
|
|
1351
1404
|
"minimal",
|
|
@@ -1371,7 +1424,7 @@ function validateAccessToken(value) {
|
|
|
1371
1424
|
}
|
|
1372
1425
|
var CONFIG_FILE_NAME = "config.json";
|
|
1373
1426
|
function getConfigPath() {
|
|
1374
|
-
return
|
|
1427
|
+
return join5(getConfigDir(), CONFIG_FILE_NAME);
|
|
1375
1428
|
}
|
|
1376
1429
|
function readConfig() {
|
|
1377
1430
|
const path = getConfigPath();
|
|
@@ -2453,7 +2506,7 @@ async function workspaceFile(workspaceRoot, file) {
|
|
|
2453
2506
|
}
|
|
2454
2507
|
let current = root;
|
|
2455
2508
|
for (const part of parts) {
|
|
2456
|
-
current =
|
|
2509
|
+
current = join6(current, part);
|
|
2457
2510
|
let info;
|
|
2458
2511
|
try {
|
|
2459
2512
|
info = lstatSync2(current);
|
|
@@ -2629,7 +2682,7 @@ async function revalidateLabelDownload(workspaceRoot, args2) {
|
|
|
2629
2682
|
if (isAbsolute2(String(args2.out_dir)) || parts.some((part) => !part || part === "." || part === "..")) {
|
|
2630
2683
|
throw new Error("Download directory must be inside the current workspace.");
|
|
2631
2684
|
}
|
|
2632
|
-
const outDir =
|
|
2685
|
+
const outDir = join6(spec.root, ...parts);
|
|
2633
2686
|
assertLabelDownloadPaths(outDir, args2.overwrite === true);
|
|
2634
2687
|
return {
|
|
2635
2688
|
workspaceRoot,
|
|
@@ -3063,7 +3116,7 @@ async function rejectPreparedAction(action, persist) {
|
|
|
3063
3116
|
// src/agent-model.ts
|
|
3064
3117
|
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
3065
3118
|
import { chmodSync as chmodSync2, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
3066
|
-
import { dirname as dirname5, join as
|
|
3119
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
3067
3120
|
function missingProviderAuthMessage() {
|
|
3068
3121
|
return "No API key is saved for the selected model. Run /login, or /model to pick a different one.";
|
|
3069
3122
|
}
|
|
@@ -3162,10 +3215,10 @@ function resolveAgentModelDefinition(runtime, selection) {
|
|
|
3162
3215
|
return { model, thinking: selection.thinking };
|
|
3163
3216
|
}
|
|
3164
3217
|
function getAgentAuthPath() {
|
|
3165
|
-
return
|
|
3218
|
+
return join7(getAgentConfigDir(), "auth.json");
|
|
3166
3219
|
}
|
|
3167
3220
|
function getAgentModelsPath() {
|
|
3168
|
-
return
|
|
3221
|
+
return join7(getAgentConfigDir(), "models.json");
|
|
3169
3222
|
}
|
|
3170
3223
|
async function createPiModelRuntime(env = process.env) {
|
|
3171
3224
|
const agentDir = getAgentConfigDir();
|
|
@@ -4142,11 +4195,21 @@ function createTunedTensorTools(api, options = {}) {
|
|
|
4142
4195
|
}, { additionalProperties: false }),
|
|
4143
4196
|
async (p) => {
|
|
4144
4197
|
const current = await inspectLocalSpec(workspaceRoot, p.spec_path);
|
|
4198
|
+
const spec = record4(current.document);
|
|
4199
|
+
const inline = current.validation.valid && spec && (spec.engine ?? "adapter") === "adapter" && !spec.dataset_prebuilt && Array.isArray(spec.examples) ? spec.examples : void 0;
|
|
4200
|
+
const split = inline ? splitSpecExamples(inline, 0) : void 0;
|
|
4145
4201
|
return {
|
|
4146
4202
|
spec_path: current.displayPath,
|
|
4147
4203
|
sha256: current.sha256,
|
|
4148
4204
|
spec: current.document ?? current.source,
|
|
4149
|
-
validation: current.validation
|
|
4205
|
+
validation: current.validation,
|
|
4206
|
+
...split ? { inline_data_split: {
|
|
4207
|
+
method: "deterministic_holdout",
|
|
4208
|
+
training_examples: split.train.length,
|
|
4209
|
+
heldout_examples: split.holdout.length,
|
|
4210
|
+
real_training_minimum_examples: 2,
|
|
4211
|
+
note: "Inline adapter data is split automatically. A small held-out set is not evidence of model quality."
|
|
4212
|
+
} } : {}
|
|
4150
4213
|
};
|
|
4151
4214
|
}
|
|
4152
4215
|
),
|
|
@@ -4485,6 +4548,8 @@ function createTunedTensorTools(api, options = {}) {
|
|
|
4485
4548
|
// src/local-agent-client.ts
|
|
4486
4549
|
var MAX_TOOL_CALLS_PER_TURN = 12;
|
|
4487
4550
|
var SYSTEM_PROMPT = `You are the local Tuned Tensor assistant running on the user's laptop.
|
|
4551
|
+
Use get_local_spec's inline_data_split counts when describing adapter training and evaluation data. An automatically held-out set is distinct from training, but a small set is not evidence of model quality.
|
|
4552
|
+
Downloads may also write TT manifests, reports and logs. Summarize the proposed effects without claiming the cache and lock are the only writes.
|
|
4488
4553
|
The workspace tunedtensor.json behavior spec is the source of truth for model behavior, training, evaluation policy, runtime placement and optional pipeline settings. For requests to change the model's behavior, inspect it with get_local_spec first, then prepare_update_local_spec with the returned SHA-256 and only the requested field changes; use add_examples to append examples. Preserve unrelated examples, instructions, engine and training settings. Explain the proposed diff and validation; never claim an edit was saved before approval. Read-only questions about the current spec use get_local_spec without proposing an edit. If no spec exists, help prepare a new one with prepare_create_local_spec; it is created as tunedtensor.json in the current workspace. For an initial draft, default to three concise representative examples unless the user requests otherwise; more examples can be added later. Malformed JSON must be repaired in an editor before prepare_update_local_spec can operate; never offer that tool as a way to reconstruct a truncated file. For direct review, point users to /spec (a readable overview), /spec <section> such as /spec examples, /spec show for the JSON, and /spec diff, /spec validate and /spec history. General educational or inspection questions do not require a spec mutation.
|
|
4489
4554
|
Local workflow commands need no TT access token. For local inspection commands such as runs, models, or doctor, tell the user to run the matching TT command in this shell (for example \`runs list\` or \`doctor\`); those commands execute outside the agent. User-owned AWS GPU training uses \`tt pipeline run\` with runtime.gpu configuration in tunedtensor.json, without a TT token. \`tt cloud\` is for TT account records, not GPU execution; hosted training start/estimate are retired. AWS credentials authorize instance lookup; separate SSH access is required. The instance must already be running, and the user manages quota, capacity, costs and shutdown. Keep the laptop running until outputs return. Model-provider choice is independent of GPU placement.
|
|
4490
4555
|
When the user requests commands to inspect a run or serve a result, provide the direct CLI handoff: \`tt runs list\` to find a run, \`tt runs get <run-id>\` for its details, \`tt runs report <run-id>\` for its evaluation report, \`tt models list\` to find the resulting model ID, and \`tt serve <model-id>\` to serve that model. Replace placeholders with actual IDs from those commands; never invent results or IDs. Until the user supplies command output, keep literal <run-id> and <model-id> placeholders; do not fabricate example IDs or prefixes. Local run IDs are UUIDs and adapter model IDs are local-<run-id>. Do not refresh hardware or propose spec edits merely to supply those commands; perform those actions only when requested.
|
|
@@ -4851,13 +4916,12 @@ import {
|
|
|
4851
4916
|
mkdir as mkdir2,
|
|
4852
4917
|
readFile as readFile3,
|
|
4853
4918
|
readdir as readdir2,
|
|
4854
|
-
rename,
|
|
4855
4919
|
stat,
|
|
4856
4920
|
unlink,
|
|
4857
4921
|
writeFile as writeFile4
|
|
4858
4922
|
} from "fs/promises";
|
|
4859
4923
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
4860
|
-
import { join as
|
|
4924
|
+
import { join as join8 } from "path";
|
|
4861
4925
|
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;
|
|
4862
4926
|
var DEFAULT_MAX_THREAD_BYTES = 1e6;
|
|
4863
4927
|
var DEFAULT_MAX_MESSAGES = 200;
|
|
@@ -4888,8 +4952,8 @@ function scrub(value, secrets) {
|
|
|
4888
4952
|
var LocalAgentStore = class {
|
|
4889
4953
|
constructor(rootDir, options = {}) {
|
|
4890
4954
|
this.rootDir = rootDir;
|
|
4891
|
-
this.threadsDir =
|
|
4892
|
-
this.claimsDir =
|
|
4955
|
+
this.threadsDir = join8(rootDir, "threads");
|
|
4956
|
+
this.claimsDir = join8(rootDir, "action-claims");
|
|
4893
4957
|
this.secrets = options.secretValues?.filter(Boolean) ?? [];
|
|
4894
4958
|
this.secretValueProvider = options.secretValueProvider;
|
|
4895
4959
|
this.maxThreadBytes = options.maxThreadBytes ?? DEFAULT_MAX_THREAD_BYTES;
|
|
@@ -4927,7 +4991,7 @@ var LocalAgentStore = class {
|
|
|
4927
4991
|
}
|
|
4928
4992
|
path(id) {
|
|
4929
4993
|
assertThreadId(id);
|
|
4930
|
-
return
|
|
4994
|
+
return join8(this.threadsDir, `${id}.json`);
|
|
4931
4995
|
}
|
|
4932
4996
|
serializeWithinQuota(state) {
|
|
4933
4997
|
const active = state.actions.filter(
|
|
@@ -4966,11 +5030,11 @@ var LocalAgentStore = class {
|
|
|
4966
5030
|
return serialized;
|
|
4967
5031
|
}
|
|
4968
5032
|
async pruneDirectory(directory, suffix, maximum, preserve) {
|
|
4969
|
-
const entries = await Promise.all((await readdir2(directory)).filter((name) => suffix.test(name) && name !== preserve).map(async (name) => ({ name, modified: (await stat(
|
|
5033
|
+
const entries = await Promise.all((await readdir2(directory)).filter((name) => suffix.test(name) && name !== preserve).map(async (name) => ({ name, modified: (await stat(join8(directory, name))).mtimeMs })));
|
|
4970
5034
|
entries.sort((a, b) => a.modified - b.modified);
|
|
4971
5035
|
const keepOther = Math.max(0, maximum - (preserve ? 1 : 0));
|
|
4972
5036
|
for (const entry of entries.slice(0, Math.max(0, entries.length - keepOther))) {
|
|
4973
|
-
await unlink(
|
|
5037
|
+
await unlink(join8(directory, entry.name));
|
|
4974
5038
|
}
|
|
4975
5039
|
}
|
|
4976
5040
|
async pruneThreads(preserve) {
|
|
@@ -4990,19 +5054,19 @@ var LocalAgentStore = class {
|
|
|
4990
5054
|
}
|
|
4991
5055
|
const candidates = await Promise.all(names.filter((name) => !protectedNames.has(name)).map(async (name) => ({
|
|
4992
5056
|
name,
|
|
4993
|
-
modified: (await stat(
|
|
5057
|
+
modified: (await stat(join8(this.threadsDir, name))).mtimeMs
|
|
4994
5058
|
})));
|
|
4995
5059
|
candidates.sort((a, b) => a.modified - b.modified);
|
|
4996
5060
|
const keep = Math.max(0, this.maxThreads - protectedNames.size);
|
|
4997
5061
|
for (const entry of candidates.slice(0, Math.max(0, candidates.length - keep))) {
|
|
4998
|
-
await unlink(
|
|
5062
|
+
await unlink(join8(this.threadsDir, entry.name));
|
|
4999
5063
|
}
|
|
5000
5064
|
}
|
|
5001
5065
|
async save(state) {
|
|
5002
5066
|
assertThreadId(state.thread.id);
|
|
5003
5067
|
await this.ensureDirectories();
|
|
5004
5068
|
const destination = this.path(state.thread.id);
|
|
5005
|
-
const temporary =
|
|
5069
|
+
const temporary = join8(
|
|
5006
5070
|
this.threadsDir,
|
|
5007
5071
|
`.${state.thread.id}.${randomUUID6()}.tmp`
|
|
5008
5072
|
);
|
|
@@ -5015,14 +5079,13 @@ var LocalAgentStore = class {
|
|
|
5015
5079
|
}
|
|
5016
5080
|
await writeFile4(temporary, serialized, { mode: 384, flag: "wx" });
|
|
5017
5081
|
await chmod(temporary, 384);
|
|
5018
|
-
await
|
|
5019
|
-
await chmod(destination, 384);
|
|
5082
|
+
await renameAtomicFile(temporary, destination);
|
|
5020
5083
|
await this.pruneThreads(`${state.thread.id}.json`);
|
|
5021
5084
|
}
|
|
5022
5085
|
async claimAction(actionId) {
|
|
5023
5086
|
assertThreadId(actionId);
|
|
5024
5087
|
await this.ensureDirectories();
|
|
5025
|
-
const path =
|
|
5088
|
+
const path = join8(this.claimsDir, `${actionId}.claim`);
|
|
5026
5089
|
try {
|
|
5027
5090
|
await writeFile4(path, `${(/* @__PURE__ */ new Date()).toISOString()}
|
|
5028
5091
|
`, {
|
|
@@ -6331,7 +6394,7 @@ function createCommandCompleter(getMode = () => "local") {
|
|
|
6331
6394
|
|
|
6332
6395
|
// src/shell-context.ts
|
|
6333
6396
|
import { readFile as readFile4, readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
6334
|
-
import { basename as basename5, dirname as dirname6, join as
|
|
6397
|
+
import { basename as basename5, dirname as dirname6, join as join9, resolve as resolve3 } from "path";
|
|
6335
6398
|
async function readJsonObject(path) {
|
|
6336
6399
|
try {
|
|
6337
6400
|
const parsed = JSON.parse(await readFile4(path, "utf8"));
|
|
@@ -6358,7 +6421,7 @@ function expandPath(value, baseDirectory, env) {
|
|
|
6358
6421
|
return expandUserPath(value, env, baseDirectory);
|
|
6359
6422
|
}
|
|
6360
6423
|
function configPath(env) {
|
|
6361
|
-
return
|
|
6424
|
+
return join9(getConfigDir(env), "config.json");
|
|
6362
6425
|
}
|
|
6363
6426
|
function agentSelectionFrom(env, configAgent, hasToken) {
|
|
6364
6427
|
const stored = configAgent && typeof configAgent === "object" && !Array.isArray(configAgent) ? configAgent : {};
|
|
@@ -6376,14 +6439,14 @@ async function adjacentFile(path) {
|
|
|
6376
6439
|
}
|
|
6377
6440
|
}
|
|
6378
6441
|
async function readActiveModelId(storeRoot) {
|
|
6379
|
-
const result2 = await readJsonObject(
|
|
6442
|
+
const result2 = await readJsonObject(join9(storeRoot, "active-model.json"));
|
|
6380
6443
|
if (!result2.found || result2.invalid) return void 0;
|
|
6381
6444
|
const modelId = result2.value?.model_id;
|
|
6382
6445
|
if (modelId === null) return "base";
|
|
6383
6446
|
return typeof modelId === "string" && modelId ? modelId : void 0;
|
|
6384
6447
|
}
|
|
6385
6448
|
async function readLatestRun(storeRoot) {
|
|
6386
|
-
const runsRoot =
|
|
6449
|
+
const runsRoot = join9(storeRoot, "runs");
|
|
6387
6450
|
let names;
|
|
6388
6451
|
try {
|
|
6389
6452
|
names = (await readdir3(runsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
@@ -6391,7 +6454,7 @@ async function readLatestRun(storeRoot) {
|
|
|
6391
6454
|
return void 0;
|
|
6392
6455
|
}
|
|
6393
6456
|
const states = await Promise.all(names.map(async (name) => {
|
|
6394
|
-
const result2 = await readJsonObject(
|
|
6457
|
+
const result2 = await readJsonObject(join9(runsRoot, name, "state.json"));
|
|
6395
6458
|
if (!result2.found || result2.invalid || !result2.value) return void 0;
|
|
6396
6459
|
const id = stringField(result2.value, "id") ?? name;
|
|
6397
6460
|
const state = {
|
|
@@ -6428,7 +6491,7 @@ async function discoverShellContext(options = {}) {
|
|
|
6428
6491
|
const env = options.env ?? process.env;
|
|
6429
6492
|
const cwd = resolve3(options.cwd ?? process.cwd());
|
|
6430
6493
|
const warnings = [];
|
|
6431
|
-
const specPath =
|
|
6494
|
+
const specPath = join9(cwd, "tunedtensor.json");
|
|
6432
6495
|
const specJson = await readJsonObject(specPath);
|
|
6433
6496
|
let spec;
|
|
6434
6497
|
if (specJson.found) {
|
|
@@ -6453,7 +6516,7 @@ async function discoverShellContext(options = {}) {
|
|
|
6453
6516
|
};
|
|
6454
6517
|
}
|
|
6455
6518
|
}
|
|
6456
|
-
const localConfigPath =
|
|
6519
|
+
const localConfigPath = join9(cwd, "local-runner.json");
|
|
6457
6520
|
const hasAdjacentLocalConfig = await adjacentFile(localConfigPath);
|
|
6458
6521
|
const localConfigJson = hasAdjacentLocalConfig ? await readJsonObject(localConfigPath) : { found: false, invalid: false };
|
|
6459
6522
|
if (localConfigJson.invalid) {
|
|
@@ -6834,7 +6897,7 @@ function operatorError(operator) {
|
|
|
6834
6897
|
`Shell operator ${JSON.stringify(operator)} is not supported. Run TT commands directly; pipes, redirects, and command substitution are disabled.`
|
|
6835
6898
|
);
|
|
6836
6899
|
}
|
|
6837
|
-
function tokenizeShellInput(input) {
|
|
6900
|
+
function tokenizeShellInput(input, platform = process.platform) {
|
|
6838
6901
|
if (input.includes("\0")) {
|
|
6839
6902
|
throw new ShellParseError("Command input must not contain NUL bytes.");
|
|
6840
6903
|
}
|
|
@@ -6869,7 +6932,7 @@ function tokenizeShellInput(input) {
|
|
|
6869
6932
|
quote = "none";
|
|
6870
6933
|
continue;
|
|
6871
6934
|
}
|
|
6872
|
-
if (character === "\\") {
|
|
6935
|
+
if (character === "\\" && platform !== "win32") {
|
|
6873
6936
|
const escaped = input[index + 1];
|
|
6874
6937
|
if (escaped === void 0) {
|
|
6875
6938
|
throw new ShellParseError("Command ends with an unfinished escape.");
|
|
@@ -6901,7 +6964,7 @@ function tokenizeShellInput(input) {
|
|
|
6901
6964
|
tokenStarted = true;
|
|
6902
6965
|
continue;
|
|
6903
6966
|
}
|
|
6904
|
-
if (character === "\\") {
|
|
6967
|
+
if (character === "\\" && platform !== "win32") {
|
|
6905
6968
|
const escaped = input[index + 1];
|
|
6906
6969
|
if (escaped === void 0) {
|
|
6907
6970
|
throw new ShellParseError("Command ends with an unfinished escape.");
|
|
@@ -8446,7 +8509,7 @@ var TunedTensorAgentSession = class {
|
|
|
8446
8509
|
// src/local-runner.ts
|
|
8447
8510
|
import { constants as osConstants } from "os";
|
|
8448
8511
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
8449
|
-
import { spawn } from "child_process";
|
|
8512
|
+
import { spawn as spawn2 } from "child_process";
|
|
8450
8513
|
import { existsSync as existsSync5 } from "fs";
|
|
8451
8514
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8452
8515
|
import {
|
|
@@ -8459,7 +8522,7 @@ import {
|
|
|
8459
8522
|
basename as basename6,
|
|
8460
8523
|
dirname as dirname7,
|
|
8461
8524
|
isAbsolute as isAbsolute4,
|
|
8462
|
-
join as
|
|
8525
|
+
join as join10,
|
|
8463
8526
|
resolve as resolve5
|
|
8464
8527
|
} from "path";
|
|
8465
8528
|
|
|
@@ -9248,17 +9311,17 @@ function positionalSpecArgument(args2, start, defaultPath) {
|
|
|
9248
9311
|
function selectedSpecArgument(args2, cwd) {
|
|
9249
9312
|
const command2 = args2[0];
|
|
9250
9313
|
if (["doctor", "validate", "run"].includes(command2 ?? "")) {
|
|
9251
|
-
return positionalSpecArgument(args2, 1,
|
|
9314
|
+
return positionalSpecArgument(args2, 1, join10(cwd, DEFAULT_SPEC_NAME));
|
|
9252
9315
|
}
|
|
9253
9316
|
if (command2 === "models" && ["prefetch", "verify-base"].includes(args2[1] ?? "")) {
|
|
9254
|
-
return positionalSpecArgument(args2, 2,
|
|
9317
|
+
return positionalSpecArgument(args2, 2, join10(cwd, DEFAULT_SPEC_NAME));
|
|
9255
9318
|
}
|
|
9256
9319
|
if (command2 === "datasets") {
|
|
9257
9320
|
if (args2[1] === void 0 || args2[1].startsWith("-") || args2[1].endsWith(".json")) {
|
|
9258
9321
|
args2.splice(1, 0, "prefetch");
|
|
9259
9322
|
}
|
|
9260
9323
|
if (["prefetch", "verify"].includes(args2[1] ?? "")) {
|
|
9261
|
-
return positionalSpecArgument(args2, 2,
|
|
9324
|
+
return positionalSpecArgument(args2, 2, join10(cwd, DEFAULT_SPEC_NAME));
|
|
9262
9325
|
}
|
|
9263
9326
|
}
|
|
9264
9327
|
const serves = command2 === "serve" || command2 === "models" && args2[1] === "serve";
|
|
@@ -9346,7 +9409,7 @@ async function projectSpecArguments(originalArgs, cwd) {
|
|
|
9346
9409
|
}
|
|
9347
9410
|
};
|
|
9348
9411
|
}
|
|
9349
|
-
const temporaryPath =
|
|
9412
|
+
const temporaryPath = join10(
|
|
9350
9413
|
dirname7(sourcePath),
|
|
9351
9414
|
`.${basename6(sourcePath)}.tt-local-${process.pid}-${randomUUID7()}.json`
|
|
9352
9415
|
);
|
|
@@ -9416,18 +9479,20 @@ async function spawnLocalCli(args2) {
|
|
|
9416
9479
|
const childEnv = errorTerminal.isTTY && !args2.jsonMode && args2.env.TT_PROGRESS_TTY === void 0 ? { ...args2.env, TT_PROGRESS_TTY: "1", TT_PROGRESS_COLUMNS: String(errorTerminal.columns ?? 100) } : args2.env;
|
|
9417
9480
|
const pipeInput = Boolean(args2.stdin && args2.stdin !== process.stdin);
|
|
9418
9481
|
const pipeStreamingOutput = args2.streamingStdout && Boolean(args2.stdout && args2.stdout !== process.stdout);
|
|
9419
|
-
const child =
|
|
9482
|
+
const child = spawn2(
|
|
9420
9483
|
args2.nodeExecutable,
|
|
9421
9484
|
[args2.entrypoint, ...args2.commandArgs],
|
|
9422
9485
|
{
|
|
9423
9486
|
cwd: args2.cwd,
|
|
9424
|
-
env: childEnv,
|
|
9487
|
+
env: process.platform === "win32" ? { ...childEnv, TT_PARENT_IPC: "1" } : childEnv,
|
|
9425
9488
|
stdio: [
|
|
9426
9489
|
pipeInput ? "pipe" : "inherit",
|
|
9427
9490
|
args2.streamingStdout && !pipeStreamingOutput ? "inherit" : "pipe",
|
|
9428
|
-
"pipe"
|
|
9491
|
+
"pipe",
|
|
9492
|
+
...process.platform === "win32" ? ["ipc"] : []
|
|
9429
9493
|
],
|
|
9430
|
-
detached: process.platform !== "win32"
|
|
9494
|
+
detached: process.platform !== "win32",
|
|
9495
|
+
windowsHide: true
|
|
9431
9496
|
}
|
|
9432
9497
|
);
|
|
9433
9498
|
if (pipeInput && args2.stdin && child.stdin) args2.stdin.pipe(child.stdin);
|
|
@@ -9456,8 +9521,10 @@ async function spawnLocalCli(args2) {
|
|
|
9456
9521
|
});
|
|
9457
9522
|
return new Promise((resolveChild) => {
|
|
9458
9523
|
let spawnError;
|
|
9524
|
+
const windowsSignals = windowsChildSignals(child);
|
|
9459
9525
|
const forwardSignal = (signal) => {
|
|
9460
9526
|
const childSignal = signal === "SIGHUP" ? "SIGTERM" : signal;
|
|
9527
|
+
if (windowsSignals.forward(childSignal)) return;
|
|
9461
9528
|
if (child.pid && process.platform !== "win32") {
|
|
9462
9529
|
try {
|
|
9463
9530
|
process.kill(-child.pid, childSignal);
|
|
@@ -9470,14 +9537,16 @@ async function spawnLocalCli(args2) {
|
|
|
9470
9537
|
const onSigint = () => forwardSignal("SIGINT");
|
|
9471
9538
|
const onSigterm = () => forwardSignal("SIGTERM");
|
|
9472
9539
|
const onSighup = () => forwardSignal("SIGHUP");
|
|
9540
|
+
const signals = args2.signals ?? process;
|
|
9473
9541
|
const cleanup = () => {
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9542
|
+
windowsSignals.dispose();
|
|
9543
|
+
signals.off("SIGINT", onSigint);
|
|
9544
|
+
signals.off("SIGTERM", onSigterm);
|
|
9545
|
+
if (process.platform !== "win32") signals.off("SIGHUP", onSighup);
|
|
9477
9546
|
};
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
if (process.platform !== "win32")
|
|
9547
|
+
signals.on("SIGINT", onSigint);
|
|
9548
|
+
signals.on("SIGTERM", onSigterm);
|
|
9549
|
+
if (process.platform !== "win32") signals.on("SIGHUP", onSighup);
|
|
9481
9550
|
child.once("error", (error) => {
|
|
9482
9551
|
spawnError = error;
|
|
9483
9552
|
});
|
|
@@ -9528,6 +9597,7 @@ async function runLocalCommand(args2, options = {}) {
|
|
|
9528
9597
|
stdin: options.stdin,
|
|
9529
9598
|
stdout: options.stdout,
|
|
9530
9599
|
stderr: options.stderr,
|
|
9600
|
+
signals: options.signals,
|
|
9531
9601
|
pathReplacements: projected.pathReplacements
|
|
9532
9602
|
});
|
|
9533
9603
|
const parsed = parseJsonOutput(result2.stdout);
|
|
@@ -9715,11 +9785,11 @@ function registerAgentCommands(parent, options) {
|
|
|
9715
9785
|
|
|
9716
9786
|
// src/commands/pipeline.ts
|
|
9717
9787
|
import { existsSync as existsSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
9718
|
-
import { basename as basename8, dirname as dirname11, join as
|
|
9788
|
+
import { basename as basename8, dirname as dirname11, join as join14, resolve as resolve8 } from "path";
|
|
9719
9789
|
|
|
9720
9790
|
// src/local-runtime/project-migration.ts
|
|
9721
|
-
import { lstat as lstat3, readFile as readFile6, rename
|
|
9722
|
-
import { dirname as dirname8, join as
|
|
9791
|
+
import { lstat as lstat3, readFile as readFile6, rename, writeFile as writeFile6, unlink as unlink2 } from "fs/promises";
|
|
9792
|
+
import { dirname as dirname8, join as join11 } from "path";
|
|
9723
9793
|
async function migrateProjectWorkflow(specPath) {
|
|
9724
9794
|
const info = await lstat3(specPath);
|
|
9725
9795
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error("Migration requires a regular spec file.");
|
|
@@ -9730,7 +9800,7 @@ async function migrateProjectWorkflow(specPath) {
|
|
|
9730
9800
|
const files = [];
|
|
9731
9801
|
const sources = /* @__PURE__ */ new Map([[specPath, original]]);
|
|
9732
9802
|
for (const name of ["tunedtensor.pipeline.json", "local-runner.json"]) {
|
|
9733
|
-
const path =
|
|
9803
|
+
const path = join11(dirname8(specPath), name);
|
|
9734
9804
|
const entry = await lstat3(path).catch((error) => {
|
|
9735
9805
|
if (error.code === "ENOENT") return void 0;
|
|
9736
9806
|
throw error;
|
|
@@ -9779,7 +9849,7 @@ async function migrateProjectWorkflow(specPath) {
|
|
|
9779
9849
|
throw new Error(`Configuration changed during migration: ${path}; retry after reviewing it.`);
|
|
9780
9850
|
}
|
|
9781
9851
|
}
|
|
9782
|
-
await
|
|
9852
|
+
await rename(temp, specPath);
|
|
9783
9853
|
} catch (error) {
|
|
9784
9854
|
if (createdTemp) await unlink2(temp).catch(() => {
|
|
9785
9855
|
});
|
|
@@ -9797,8 +9867,8 @@ async function migrateProjectWorkflow(specPath) {
|
|
|
9797
9867
|
// src/local-runtime/foundation-runner.ts
|
|
9798
9868
|
import { createHash as createHash7, randomUUID as randomUUID8 } from "crypto";
|
|
9799
9869
|
import { createReadStream } from "fs";
|
|
9800
|
-
import { chmod as chmod2, lstat as lstat4, mkdir as mkdir3, open as open2, readdir as readdir4, readFile as readFile7, rename as
|
|
9801
|
-
import { basename as basename7, dirname as dirname9, join as
|
|
9870
|
+
import { chmod as chmod2, lstat as lstat4, mkdir as mkdir3, open as open2, readdir as readdir4, readFile as readFile7, rename as rename2, rm as rm5 } from "fs/promises";
|
|
9871
|
+
import { basename as basename7, dirname as dirname9, join as join12, resolve as resolve6 } from "path";
|
|
9802
9872
|
var INSTRUCTION_CORPUS_VERSION = 1;
|
|
9803
9873
|
function isRef(value) {
|
|
9804
9874
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value) && typeof value.from === "string";
|
|
@@ -9827,7 +9897,7 @@ async function hashPath(path) {
|
|
|
9827
9897
|
if (info.isDirectory()) {
|
|
9828
9898
|
for (const name of (await readdir4(path)).sort()) {
|
|
9829
9899
|
hash.update(name);
|
|
9830
|
-
hash.update(await hashPath(
|
|
9900
|
+
hash.update(await hashPath(join12(path, name)));
|
|
9831
9901
|
}
|
|
9832
9902
|
return hash.digest("hex");
|
|
9833
9903
|
}
|
|
@@ -9850,7 +9920,7 @@ async function makePrivateTree(path) {
|
|
|
9850
9920
|
if (info.isDirectory()) {
|
|
9851
9921
|
await chmod2(path, 448);
|
|
9852
9922
|
for (const name of await readdir4(path)) {
|
|
9853
|
-
await makePrivateTree(
|
|
9923
|
+
await makePrivateTree(join12(path, name));
|
|
9854
9924
|
}
|
|
9855
9925
|
return;
|
|
9856
9926
|
}
|
|
@@ -9879,7 +9949,7 @@ async function readRequiredJsonObject(path, description) {
|
|
|
9879
9949
|
return parsed;
|
|
9880
9950
|
}
|
|
9881
9951
|
async function writePrivateTextAtomic(path, content) {
|
|
9882
|
-
const temporary =
|
|
9952
|
+
const temporary = join12(dirname9(path), `.${basename7(path)}.${randomUUID8()}.tmp`);
|
|
9883
9953
|
const file = await open2(temporary, "wx", 384);
|
|
9884
9954
|
let closed = false;
|
|
9885
9955
|
try {
|
|
@@ -9888,7 +9958,7 @@ async function writePrivateTextAtomic(path, content) {
|
|
|
9888
9958
|
await file.sync();
|
|
9889
9959
|
await file.close();
|
|
9890
9960
|
closed = true;
|
|
9891
|
-
await
|
|
9961
|
+
await rename2(temporary, path);
|
|
9892
9962
|
} finally {
|
|
9893
9963
|
if (!closed) await file.close().catch(() => void 0);
|
|
9894
9964
|
await rm5(temporary, { force: true }).catch(() => void 0);
|
|
@@ -9995,7 +10065,7 @@ async function runFoundationPipeline(args2) {
|
|
|
9995
10065
|
assertFoundationPlanSupported(args2.spec, args2.plan);
|
|
9996
10066
|
assertFoundationSettingsMatch(args2.spec.foundation, args2.plan);
|
|
9997
10067
|
const outputDir = resolve6(
|
|
9998
|
-
args2.outputDir ?? (args2.spec.runtime?.artifactRoot ?
|
|
10068
|
+
args2.outputDir ?? (args2.spec.runtime?.artifactRoot ? join12(resolve6(dirname9(args2.specPath), args2.spec.runtime.artifactRoot), "foundation-runs", randomUUID8()) : join12(dirname9(resolve6(args2.specPath)), DEFAULT_FOUNDATION_RUNS_DIR, randomUUID8()))
|
|
9999
10069
|
);
|
|
10000
10070
|
if (args2.gpu && args2.spec.foundation.checkpoint_backup_dir && localPathsOverlap(outputDir, args2.spec.foundation.checkpoint_backup_dir)) {
|
|
10001
10071
|
throw new Error("AWS checkpoint_backup_dir must be separate from the foundation run directory; overlapping backups can overwrite returned checkpoints.");
|
|
@@ -10020,7 +10090,7 @@ async function runFoundationPipeline(args2) {
|
|
|
10020
10090
|
await mkdir3(outputDir, { recursive: true, mode: 448 });
|
|
10021
10091
|
}
|
|
10022
10092
|
await makePrivateTree(outputDir);
|
|
10023
|
-
const workflowPath =
|
|
10093
|
+
const workflowPath = join12(outputDir, "resolved-workflow.json");
|
|
10024
10094
|
const workflow = { spec: args2.spec, plan: args2.plan };
|
|
10025
10095
|
if (outputExists) {
|
|
10026
10096
|
const previous = await readFile7(workflowPath, "utf8").catch((error) => {
|
|
@@ -10053,16 +10123,16 @@ async function runFoundationPipeline(args2) {
|
|
|
10053
10123
|
throw new Error(`Missing ${kind} artifact.`);
|
|
10054
10124
|
};
|
|
10055
10125
|
for (const step of args2.plan.steps) {
|
|
10056
|
-
const stepDir =
|
|
10057
|
-
const output =
|
|
10126
|
+
const stepDir = join12(outputDir, step.id);
|
|
10127
|
+
const output = join12(stepDir, "output");
|
|
10058
10128
|
await mkdir3(stepDir, { recursive: true, mode: 448 });
|
|
10059
10129
|
await chmod2(stepDir, 448);
|
|
10060
10130
|
await mkdir3(output, { recursive: true, mode: 448 });
|
|
10061
10131
|
await chmod2(output, 448);
|
|
10062
|
-
const configPath2 =
|
|
10063
|
-
const logPath =
|
|
10064
|
-
const metricsPath =
|
|
10065
|
-
const completionPath =
|
|
10132
|
+
const configPath2 = join12(stepDir, "config.json");
|
|
10133
|
+
const logPath = join12(stepDir, "step.log");
|
|
10134
|
+
const metricsPath = join12(output, "metrics.json");
|
|
10135
|
+
const completionPath = join12(stepDir, "completion.json");
|
|
10066
10136
|
const fields = stepConfig(step);
|
|
10067
10137
|
let entrypoint;
|
|
10068
10138
|
let config;
|
|
@@ -10083,7 +10153,7 @@ async function runFoundationPipeline(args2) {
|
|
|
10083
10153
|
config = {
|
|
10084
10154
|
tokenizer_dir: tokenizerDir,
|
|
10085
10155
|
output_dir: output,
|
|
10086
|
-
work_dir:
|
|
10156
|
+
work_dir: join12(stepDir, "recovery"),
|
|
10087
10157
|
depth: fields.depth ?? hp.depth,
|
|
10088
10158
|
steps: fields.steps ?? hp.pretrain_steps,
|
|
10089
10159
|
batch_size: fields.batchSize ?? hp.batch_size,
|
|
@@ -10188,15 +10258,15 @@ async function runFoundationPipeline(args2) {
|
|
|
10188
10258
|
const artifacts = {};
|
|
10189
10259
|
const record6 = {};
|
|
10190
10260
|
if (step.uses === "tokenize") {
|
|
10191
|
-
const tokenizerPath =
|
|
10261
|
+
const tokenizerPath = join12(output, "tokenizer.json");
|
|
10192
10262
|
await requireRegularNonemptyFile(tokenizerPath, `Foundation step "${step.id}" tokenizer.json`);
|
|
10193
10263
|
await readRequiredJsonObject(tokenizerPath, `Foundation step "${step.id}" tokenizer.json`);
|
|
10194
10264
|
record6.tokenizer = output;
|
|
10195
10265
|
tokenizerDir = output;
|
|
10196
10266
|
artifacts.tokenizer = await artifactFor(output);
|
|
10197
10267
|
} else if (step.uses === "pretrain" || step.uses === "finetune" || step.uses === "rl") {
|
|
10198
|
-
const modelPath =
|
|
10199
|
-
const modelConfigPath =
|
|
10268
|
+
const modelPath = join12(output, "model.safetensors");
|
|
10269
|
+
const modelConfigPath = join12(output, "config.json");
|
|
10200
10270
|
await requireRegularNonemptyFile(modelPath, `Foundation step "${step.id}" model.safetensors`);
|
|
10201
10271
|
const modelConfig = await readRequiredJsonObject(
|
|
10202
10272
|
modelConfigPath,
|
|
@@ -10211,7 +10281,7 @@ async function runFoundationPipeline(args2) {
|
|
|
10211
10281
|
modelDir = output;
|
|
10212
10282
|
artifacts.model = await artifactFor(output);
|
|
10213
10283
|
} else if (step.uses === "evaluate") {
|
|
10214
|
-
const reportPath2 =
|
|
10284
|
+
const reportPath2 = join12(output, "report.json");
|
|
10215
10285
|
const stepReport = await readRequiredJsonObject(
|
|
10216
10286
|
reportPath2,
|
|
10217
10287
|
`Foundation step "${step.id}" report.json`
|
|
@@ -10251,7 +10321,7 @@ async function runFoundationPipeline(args2) {
|
|
|
10251
10321
|
spec: args2.specPath,
|
|
10252
10322
|
steps
|
|
10253
10323
|
};
|
|
10254
|
-
const reportPath =
|
|
10324
|
+
const reportPath = join12(outputDir, "report.json");
|
|
10255
10325
|
await writePrivateTextAtomic(reportPath, `${JSON.stringify(report, null, 2)}
|
|
10256
10326
|
`);
|
|
10257
10327
|
return {
|
|
@@ -10264,8 +10334,8 @@ async function runFoundationPipeline(args2) {
|
|
|
10264
10334
|
|
|
10265
10335
|
// src/local-runtime/decision-runner.ts
|
|
10266
10336
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
10267
|
-
import { lstat as lstat5, mkdir as mkdir4, readFile as readFile8, rename as
|
|
10268
|
-
import { dirname as dirname10, join as
|
|
10337
|
+
import { lstat as lstat5, mkdir as mkdir4, readFile as readFile8, rename as rename3, rm as rm6, writeFile as writeFile7 } from "fs/promises";
|
|
10338
|
+
import { dirname as dirname10, join as join13, resolve as resolve7 } from "path";
|
|
10269
10339
|
var PROBABILITY_FLOOR = 1e-6;
|
|
10270
10340
|
function assertDecisionPlanSupported(plan) {
|
|
10271
10341
|
for (const step of plan.steps) {
|
|
@@ -10428,7 +10498,7 @@ async function writePrivateAtomic(path, content) {
|
|
|
10428
10498
|
const temporary = `${path}.${process.pid}.${randomUUID9()}.tmp`;
|
|
10429
10499
|
try {
|
|
10430
10500
|
await writeFile7(temporary, content, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
10431
|
-
await
|
|
10501
|
+
await rename3(temporary, path);
|
|
10432
10502
|
} finally {
|
|
10433
10503
|
await rm6(temporary, { force: true });
|
|
10434
10504
|
}
|
|
@@ -10447,7 +10517,7 @@ async function runDecisionPipeline(args2) {
|
|
|
10447
10517
|
assertDecisionPlanSupported(args2.plan);
|
|
10448
10518
|
const model = resolveDecisionModel(args2.spec.base_model);
|
|
10449
10519
|
const specDir = dirname10(resolve7(args2.specPath));
|
|
10450
|
-
const outputDir = resolve7(args2.outputDir ?? (args2.spec.runtime?.artifactRoot ?
|
|
10520
|
+
const outputDir = resolve7(args2.outputDir ?? (args2.spec.runtime?.artifactRoot ? join13(resolve7(specDir, args2.spec.runtime.artifactRoot), "decision-runs", randomUUID9()) : join13(specDir, DEFAULT_DECISION_RUNS_DIR, randomUUID9())));
|
|
10451
10521
|
if (await lstat5(outputDir).catch(() => null)) {
|
|
10452
10522
|
throw new Error(`Decision output directory already exists: ${outputDir}`);
|
|
10453
10523
|
}
|
|
@@ -10466,13 +10536,13 @@ async function runDecisionPipeline(args2) {
|
|
|
10466
10536
|
const hp = args2.spec.hyperparameters ?? {};
|
|
10467
10537
|
const device = hp.device ?? "auto";
|
|
10468
10538
|
const evalIds = split.holdout.map((_, index) => `eval-${index}`);
|
|
10469
|
-
const dataDir =
|
|
10539
|
+
const dataDir = join13(outputDir, "data");
|
|
10470
10540
|
await mkdir4(dataDir, { recursive: true, mode: 448 });
|
|
10471
|
-
const trainPath =
|
|
10472
|
-
const evalInputsPath =
|
|
10541
|
+
const trainPath = join13(dataDir, "train.jsonl");
|
|
10542
|
+
const evalInputsPath = join13(dataDir, "eval-inputs.jsonl");
|
|
10473
10543
|
await writePrivateAtomic(trainPath, jsonl(split.train));
|
|
10474
10544
|
await writePrivateAtomic(evalInputsPath, jsonl(split.holdout.map((example, index) => ({ id: evalIds[index], input: example.input }))));
|
|
10475
|
-
await writePrivateJson2(
|
|
10545
|
+
await writePrivateJson2(join13(outputDir, "resolved-workflow.json"), {
|
|
10476
10546
|
spec: args2.spec,
|
|
10477
10547
|
plan: args2.plan,
|
|
10478
10548
|
model: { id: model.id, revision: model.revision },
|
|
@@ -10485,13 +10555,13 @@ async function runDecisionPipeline(args2) {
|
|
|
10485
10555
|
const steps = [];
|
|
10486
10556
|
let tunedModelDir;
|
|
10487
10557
|
for (const step of args2.plan.steps) {
|
|
10488
|
-
const stepDir =
|
|
10558
|
+
const stepDir = join13(outputDir, step.id);
|
|
10489
10559
|
await mkdir4(stepDir, { recursive: true, mode: 448 });
|
|
10490
|
-
const configPath2 =
|
|
10491
|
-
const logPath =
|
|
10492
|
-
const metricsPath =
|
|
10560
|
+
const configPath2 = join13(stepDir, "config.json");
|
|
10561
|
+
const logPath = join13(stepDir, "step.log");
|
|
10562
|
+
const metricsPath = join13(stepDir, "metrics.json");
|
|
10493
10563
|
if (step.uses === "train") {
|
|
10494
|
-
const modelDir =
|
|
10564
|
+
const modelDir = join13(stepDir, "model");
|
|
10495
10565
|
args2.onProgress?.(`${step.id}: fine-tuning ${model.id} on ${split.train.length} examples (log: ${logPath})`);
|
|
10496
10566
|
await writePrivateJson2(configPath2, {
|
|
10497
10567
|
model: model.id,
|
|
@@ -10511,8 +10581,8 @@ async function runDecisionPipeline(args2) {
|
|
|
10511
10581
|
await spawnStep({ entrypoint: "train.py", configPath: configPath2, logPath, stepId: step.id, onProgress: args2.onProgress });
|
|
10512
10582
|
const metrics = await readJsonObject2(metricsPath, `Decision step "${step.id}" metrics`);
|
|
10513
10583
|
if (metrics.ok !== true) throw new Error(`Decision step "${step.id}" metrics must report ok: true.`);
|
|
10514
|
-
await requireRegularNonemptyFile2(
|
|
10515
|
-
await readJsonObject2(
|
|
10584
|
+
await requireRegularNonemptyFile2(join13(modelDir, "model.safetensors"), `Decision step "${step.id}" model.safetensors`);
|
|
10585
|
+
await readJsonObject2(join13(modelDir, "rl_agent_config.json"), `Decision step "${step.id}" rl_agent_config.json`);
|
|
10516
10586
|
producedModels.set(step.id, modelDir);
|
|
10517
10587
|
tunedModelDir = modelDir;
|
|
10518
10588
|
steps.push({ id: step.id, uses: step.uses, model: modelDir, training: metrics });
|
|
@@ -10523,7 +10593,7 @@ async function runDecisionPipeline(args2) {
|
|
|
10523
10593
|
const tuned = source !== "base";
|
|
10524
10594
|
const modelDir = tuned ? producedModels.get(producer(source.from)) : void 0;
|
|
10525
10595
|
if (tuned && !modelDir) throw new Error(`Step ${step.id} needs a model from ${source.from}.`);
|
|
10526
|
-
const predictionsPath =
|
|
10596
|
+
const predictionsPath = join13(stepDir, "predictions.jsonl");
|
|
10527
10597
|
args2.onProgress?.(`${step.id}: evaluating ${tuned ? "tuned" : "base"} model on ${split.holdout.length} held-out examples`);
|
|
10528
10598
|
await writePrivateJson2(configPath2, {
|
|
10529
10599
|
model: modelDir ?? model.id,
|
|
@@ -10537,7 +10607,7 @@ async function runDecisionPipeline(args2) {
|
|
|
10537
10607
|
await spawnStep({ entrypoint: "evaluate.py", configPath: configPath2, logPath, stepId: step.id, onProgress: args2.onProgress });
|
|
10538
10608
|
const metrics = await readJsonObject2(metricsPath, `Decision step "${step.id}" metrics`);
|
|
10539
10609
|
if (metrics.ok !== true) throw new Error(`Decision step "${step.id}" metrics must report ok: true.`);
|
|
10540
|
-
const reportPath2 =
|
|
10610
|
+
const reportPath2 = join13(stepDir, "report.json");
|
|
10541
10611
|
const scored = scoreDecisionPredictions({
|
|
10542
10612
|
kind: tuned ? "candidate" : "baseline",
|
|
10543
10613
|
modelId: modelDir ? fileUri(modelDir) : model.id,
|
|
@@ -10561,10 +10631,10 @@ async function runDecisionPipeline(args2) {
|
|
|
10561
10631
|
log_loss_delta: after.metrics.log_loss - before.metrics.log_loss,
|
|
10562
10632
|
brier_delta: after.metrics.brier - before.metrics.brier
|
|
10563
10633
|
};
|
|
10564
|
-
await writePrivateJson2(
|
|
10634
|
+
await writePrivateJson2(join13(stepDir, "comparison.json"), comparison);
|
|
10565
10635
|
steps.push({ id: step.id, uses: step.uses, comparison });
|
|
10566
10636
|
}
|
|
10567
|
-
const reportPath =
|
|
10637
|
+
const reportPath = join13(outputDir, "report.json");
|
|
10568
10638
|
const report = {
|
|
10569
10639
|
status: "succeeded",
|
|
10570
10640
|
engine: "decision",
|
|
@@ -11190,7 +11260,7 @@ function registerTopupCommands(parent) {
|
|
|
11190
11260
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
11191
11261
|
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
|
|
11192
11262
|
import { homedir as homedir2 } from "os";
|
|
11193
|
-
import { isAbsolute as isAbsolute5, join as
|
|
11263
|
+
import { isAbsolute as isAbsolute5, join as join15, resolve as resolve9 } from "path";
|
|
11194
11264
|
import { stdin as defaultStdin, stdout as defaultStdout } from "process";
|
|
11195
11265
|
var MAX_PUBLISH_BYTES = 2 * 1024 * 1024;
|
|
11196
11266
|
function expandPath2(value, baseDirectory, homeDirectory) {
|
|
@@ -11202,8 +11272,8 @@ function resolvePublishStoreRoot(options) {
|
|
|
11202
11272
|
const cwd = options?.cwd ?? process.cwd();
|
|
11203
11273
|
const env = options?.env ?? process.env;
|
|
11204
11274
|
const homeDirectory = options?.homeDirectory ?? (env.HOME ? resolve9(env.HOME) : homedir2());
|
|
11205
|
-
const localConfigPath =
|
|
11206
|
-
const projectPath =
|
|
11275
|
+
const localConfigPath = join15(cwd, "local-runner.json");
|
|
11276
|
+
const projectPath = join15(cwd, "tunedtensor.json");
|
|
11207
11277
|
if (existsSync7(projectPath)) {
|
|
11208
11278
|
const spec = localBehaviorSpecFileSchema.parse(JSON.parse(readFileSync8(projectPath, "utf8")));
|
|
11209
11279
|
if (existsSync7(localConfigPath) && (spec.runtime || spec.evaluation)) throw new Error("Conflicting configuration sources; migrate local-runner.json first.");
|
|
@@ -12277,11 +12347,11 @@ import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
|
|
|
12277
12347
|
import { createReadStream as createReadStream2, readFileSync as readFileSync11, statSync as statSync4, openSync as openSync3, readSync, closeSync as closeSync3 } from "fs";
|
|
12278
12348
|
import { open as open4 } from "fs/promises";
|
|
12279
12349
|
import { finished } from "stream/promises";
|
|
12280
|
-
import { resolve as resolve10, join as
|
|
12350
|
+
import { resolve as resolve10, join as join16 } from "path";
|
|
12281
12351
|
import { z } from "zod";
|
|
12282
12352
|
async function inspectCheckpoint(modelPath) {
|
|
12283
12353
|
const directory = resolve10(modelPath);
|
|
12284
|
-
const config = JSON.parse(readFileSync11(
|
|
12354
|
+
const config = JSON.parse(readFileSync11(join16(directory, "rl_agent_config.json"), "utf8"));
|
|
12285
12355
|
if (config.encoder !== "answerdotai/ModernBERT-large" || config.head_layers !== 2 || config.max_len !== 512 || config.head_max_len !== 192) {
|
|
12286
12356
|
throw new Error("Hosting currently supports only the pinned English Laya checkpoint and TT decision fine-tunes.");
|
|
12287
12357
|
}
|
|
@@ -12289,7 +12359,7 @@ async function inspectCheckpoint(modelPath) {
|
|
|
12289
12359
|
if (kind === "tuned" && (JSON.stringify(config.temperature) !== "[1,1,1]" || config.temperature_by_options)) {
|
|
12290
12360
|
throw new Error("Custom calibration is not supported by hosted decision inference.");
|
|
12291
12361
|
}
|
|
12292
|
-
const file =
|
|
12362
|
+
const file = join16(directory, "model.safetensors");
|
|
12293
12363
|
const size = statSync4(file).size;
|
|
12294
12364
|
if (size <= 8 || size > 2e9) throw new Error("Weights must be a safetensors file smaller than 2 GB. GPU and adapter models are not supported.");
|
|
12295
12365
|
const fd = openSync3(file, "r");
|
|
@@ -12408,7 +12478,7 @@ function registerEndpointCommands(parent) {
|
|
|
12408
12478
|
}
|
|
12409
12479
|
|
|
12410
12480
|
// src/commands/models.ts
|
|
12411
|
-
import { spawn as
|
|
12481
|
+
import { spawn as spawn4, execFileSync } from "child_process";
|
|
12412
12482
|
import { createHash as createHash9 } from "crypto";
|
|
12413
12483
|
import {
|
|
12414
12484
|
existsSync as existsSync10,
|
|
@@ -12422,7 +12492,7 @@ import {
|
|
|
12422
12492
|
rmSync
|
|
12423
12493
|
} from "fs";
|
|
12424
12494
|
import { homedir as homedir3 } from "os";
|
|
12425
|
-
import { dirname as dirname13, join as
|
|
12495
|
+
import { dirname as dirname13, join as join17, resolve as resolve12, basename as basename9, normalize, isAbsolute as isAbsolute6 } from "path";
|
|
12426
12496
|
import { Readable, Transform } from "stream";
|
|
12427
12497
|
import { pipeline } from "stream/promises";
|
|
12428
12498
|
|
|
@@ -12444,7 +12514,7 @@ Run \`tt init\` to create one.`
|
|
|
12444
12514
|
}
|
|
12445
12515
|
|
|
12446
12516
|
// src/serve-manager.ts
|
|
12447
|
-
import { spawn as
|
|
12517
|
+
import { spawn as spawn3 } from "child_process";
|
|
12448
12518
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
12449
12519
|
import { appendFileSync, mkdirSync as mkdirSync4 } from "fs";
|
|
12450
12520
|
import { createServer } from "http";
|
|
@@ -12589,7 +12659,7 @@ async function allocatePort(host) {
|
|
|
12589
12659
|
var ManagedModelServer = class {
|
|
12590
12660
|
constructor(options) {
|
|
12591
12661
|
this.options = options;
|
|
12592
|
-
this.spawnModelServer = options.spawnModelServer ??
|
|
12662
|
+
this.spawnModelServer = options.spawnModelServer ?? spawn3;
|
|
12593
12663
|
this.idleTimeoutMs = parsePositiveInteger(options.idleTimeoutSeconds, 300) * 1e3;
|
|
12594
12664
|
this.restartAfterRequests = parsePositiveInteger(options.restartAfterRequests, 100);
|
|
12595
12665
|
this.logRecordOverride = options.logRecord;
|
|
@@ -13464,7 +13534,7 @@ function resolveOutputPath(output, filename) {
|
|
|
13464
13534
|
validateDownloadFilename(filename);
|
|
13465
13535
|
if (!output) return filename;
|
|
13466
13536
|
if (existsSync10(output) && statSync5(output).isDirectory()) {
|
|
13467
|
-
return
|
|
13537
|
+
return join17(output, filename);
|
|
13468
13538
|
}
|
|
13469
13539
|
return output;
|
|
13470
13540
|
}
|
|
@@ -13571,15 +13641,15 @@ async function downloadUrlToFile(url, outputPath) {
|
|
|
13571
13641
|
return contentLength;
|
|
13572
13642
|
}
|
|
13573
13643
|
function defaultCacheDir() {
|
|
13574
|
-
const root = process.env.XDG_CACHE_HOME ||
|
|
13575
|
-
return
|
|
13644
|
+
const root = process.env.XDG_CACHE_HOME || join17(homedir3(), ".cache");
|
|
13645
|
+
return join17(root, "tuned-tensor", "models");
|
|
13576
13646
|
}
|
|
13577
13647
|
function runtimeDir(cacheDir) {
|
|
13578
|
-
return
|
|
13648
|
+
return join17(cacheDir, "_runtime");
|
|
13579
13649
|
}
|
|
13580
13650
|
function runtimePythonPath(cacheDir) {
|
|
13581
13651
|
const dir = runtimeDir(cacheDir);
|
|
13582
|
-
return process.platform === "win32" ?
|
|
13652
|
+
return process.platform === "win32" ? join17(dir, "Scripts", "python.exe") : join17(dir, "bin", "python");
|
|
13583
13653
|
}
|
|
13584
13654
|
function resolveServePython(cacheDir, explicitPython) {
|
|
13585
13655
|
if (explicitPython) return explicitPython;
|
|
@@ -13652,7 +13722,7 @@ print(json.dumps({"missing": missing}))
|
|
|
13652
13722
|
}
|
|
13653
13723
|
}
|
|
13654
13724
|
function setupRuntimeCommands(python, venvDir, deps) {
|
|
13655
|
-
const venvPython = process.platform === "win32" ?
|
|
13725
|
+
const venvPython = process.platform === "win32" ? join17(venvDir, "Scripts", "python.exe") : join17(venvDir, "bin", "python");
|
|
13656
13726
|
return [
|
|
13657
13727
|
[python, "-m", "venv", venvDir],
|
|
13658
13728
|
[venvPython, "-m", "pip", "install", "--upgrade", "pip"],
|
|
@@ -13692,7 +13762,7 @@ function extractArchive(archivePath, targetDir, force) {
|
|
|
13692
13762
|
function localArchiveCacheDir(archivePath, cacheDir) {
|
|
13693
13763
|
const resolved = resolve12(archivePath);
|
|
13694
13764
|
const digest = createHash9("sha256").update(resolved).digest("hex").slice(0, 12);
|
|
13695
|
-
return
|
|
13765
|
+
return join17(cacheDir, `local-${safeSegment(basename9(archivePath))}-${digest}`);
|
|
13696
13766
|
}
|
|
13697
13767
|
function buildSystemPrompt(spec) {
|
|
13698
13768
|
const parts = [];
|
|
@@ -13708,7 +13778,7 @@ ${spec.constraints.map((c) => `- ${c}`).join("\n")}`);
|
|
|
13708
13778
|
return parts.join("\n\n");
|
|
13709
13779
|
}
|
|
13710
13780
|
function findSpecPath(explicitPath, modelPath) {
|
|
13711
|
-
const candidates = explicitPath ? [explicitPath] : [
|
|
13781
|
+
const candidates = explicitPath ? [explicitPath] : [join17(process.cwd(), DEFAULT_SPEC_FILE2), join17(modelPath, DEFAULT_SPEC_FILE2)];
|
|
13712
13782
|
for (const candidate of candidates) {
|
|
13713
13783
|
const resolved = resolve12(candidate);
|
|
13714
13784
|
if (existsSync10(resolved) && statSync5(resolved).isFile()) return resolved;
|
|
@@ -13732,9 +13802,9 @@ function loadJsonSchemaForServe(schemaPath) {
|
|
|
13732
13802
|
}
|
|
13733
13803
|
}
|
|
13734
13804
|
function writeReferenceServerScript(cacheDir) {
|
|
13735
|
-
const scriptDir =
|
|
13805
|
+
const scriptDir = join17(cacheDir, "_server");
|
|
13736
13806
|
mkdirSync5(scriptDir, { recursive: true });
|
|
13737
|
-
const scriptPath =
|
|
13807
|
+
const scriptPath = join17(scriptDir, "openai_reference_server.py");
|
|
13738
13808
|
writeFileSync6(scriptPath, REFERENCE_SERVER_SCRIPT, "utf8");
|
|
13739
13809
|
return scriptPath;
|
|
13740
13810
|
}
|
|
@@ -13750,7 +13820,7 @@ async function resolveServeTarget(target, opts, cacheDir, forceDownload) {
|
|
|
13750
13820
|
};
|
|
13751
13821
|
}
|
|
13752
13822
|
if (stats.isFile() && (resolved.endsWith(".tar.gz") || resolved.endsWith(".tgz"))) {
|
|
13753
|
-
const extractDir2 =
|
|
13823
|
+
const extractDir2 = join17(localArchiveCacheDir(resolved, cacheDir), "model");
|
|
13754
13824
|
return {
|
|
13755
13825
|
modelPath: extractArchive(resolved, extractDir2, forceDownload),
|
|
13756
13826
|
modelName: basename9(resolved).replace(/\.t(ar\.)?gz$/, ""),
|
|
@@ -13764,9 +13834,9 @@ async function resolveServeTarget(target, opts, cacheDir, forceDownload) {
|
|
|
13764
13834
|
const { data: data3 } = await get(`/models/${fullId}/download`, void 0, opts);
|
|
13765
13835
|
validateDownloadFilename(fullId);
|
|
13766
13836
|
validateDownloadFilename(data3.filename);
|
|
13767
|
-
const modelCacheDir =
|
|
13768
|
-
const archivePath =
|
|
13769
|
-
const extractDir =
|
|
13837
|
+
const modelCacheDir = join17(cacheDir, fullId);
|
|
13838
|
+
const archivePath = join17(modelCacheDir, data3.filename);
|
|
13839
|
+
const extractDir = join17(modelCacheDir, "model");
|
|
13770
13840
|
if (!existsSync10(archivePath) || forceDownload) {
|
|
13771
13841
|
await downloadUrlToFile(data3.url, archivePath);
|
|
13772
13842
|
}
|
|
@@ -13889,7 +13959,7 @@ function resolveConvertScript(opts, required) {
|
|
|
13889
13959
|
return resolved;
|
|
13890
13960
|
}
|
|
13891
13961
|
const dir = llamaCppDir(opts.llamaCpp);
|
|
13892
|
-
const candidates = dir ? [
|
|
13962
|
+
const candidates = dir ? [join17(dir, "convert_hf_to_gguf.py"), join17(dir, "convert-hf-to-gguf.py")] : [];
|
|
13893
13963
|
const found = firstExisting(candidates);
|
|
13894
13964
|
if (found) return found;
|
|
13895
13965
|
if (required) {
|
|
@@ -13897,7 +13967,7 @@ function resolveConvertScript(opts, required) {
|
|
|
13897
13967
|
"Could not find convert_hf_to_gguf.py. Pass --llama-cpp <dir> (a llama.cpp checkout) or --convert-script <path>. Get it from https://github.com/ggml-org/llama.cpp."
|
|
13898
13968
|
);
|
|
13899
13969
|
}
|
|
13900
|
-
return dir ?
|
|
13970
|
+
return dir ? join17(dir, "convert_hf_to_gguf.py") : "convert_hf_to_gguf.py";
|
|
13901
13971
|
}
|
|
13902
13972
|
function resolveQuantizeBin(opts, required) {
|
|
13903
13973
|
if (opts.quantizeBin) {
|
|
@@ -13910,10 +13980,10 @@ function resolveQuantizeBin(opts, required) {
|
|
|
13910
13980
|
const dir = llamaCppDir(opts.llamaCpp);
|
|
13911
13981
|
if (dir) {
|
|
13912
13982
|
const candidates = [
|
|
13913
|
-
|
|
13914
|
-
|
|
13915
|
-
|
|
13916
|
-
|
|
13983
|
+
join17(dir, "build", "bin", "llama-quantize"),
|
|
13984
|
+
join17(dir, "build", "bin", "quantize"),
|
|
13985
|
+
join17(dir, "llama-quantize"),
|
|
13986
|
+
join17(dir, "quantize")
|
|
13917
13987
|
];
|
|
13918
13988
|
const found = firstExisting(candidates);
|
|
13919
13989
|
if (found) return found;
|
|
@@ -14070,9 +14140,9 @@ function registerModelsCommands(parent) {
|
|
|
14070
14140
|
Boolean(cmdOpts.forceDownload)
|
|
14071
14141
|
);
|
|
14072
14142
|
const slug = ollamaSlug(serveTarget.modelName);
|
|
14073
|
-
const outputDir = resolve12(cmdOpts.output ||
|
|
14074
|
-
const finalPath =
|
|
14075
|
-
const intermediatePath = quantPlan.requiresQuantize ?
|
|
14143
|
+
const outputDir = resolve12(cmdOpts.output || join17(process.cwd(), `${slug}-gguf`));
|
|
14144
|
+
const finalPath = join17(outputDir, `${slug}.${quantPlan.quant}.gguf`);
|
|
14145
|
+
const intermediatePath = quantPlan.requiresQuantize ? join17(outputDir, `${slug}.f16.gguf`) : void 0;
|
|
14076
14146
|
const convertScript = resolveConvertScript(cmdOpts, !printOnly);
|
|
14077
14147
|
const convertOutfile = intermediatePath ?? finalPath;
|
|
14078
14148
|
const steps = [
|
|
@@ -14110,7 +14180,7 @@ function registerModelsCommands(parent) {
|
|
|
14110
14180
|
}
|
|
14111
14181
|
if (specPath) systemPrompt2 = loadSystemPromptFromSpec(specPath);
|
|
14112
14182
|
}
|
|
14113
|
-
modelfilePath =
|
|
14183
|
+
modelfilePath = join17(outputDir, "Modelfile");
|
|
14114
14184
|
modelfileContent = buildModelfile(basename9(finalPath), systemPrompt2);
|
|
14115
14185
|
const createModel = cmdOpts.ollamaCreate !== false;
|
|
14116
14186
|
if (createModel) {
|
|
@@ -14334,7 +14404,7 @@ function registerModelsCommands(parent) {
|
|
|
14334
14404
|
return;
|
|
14335
14405
|
}
|
|
14336
14406
|
await new Promise((resolvePromise, reject) => {
|
|
14337
|
-
const child =
|
|
14407
|
+
const child = spawn4(python, args2, {
|
|
14338
14408
|
stdio: "inherit",
|
|
14339
14409
|
env: { ...process.env, ...env }
|
|
14340
14410
|
});
|
|
@@ -14461,15 +14531,15 @@ Use \`tt balance\` for cloud training credits.`);
|
|
|
14461
14531
|
}
|
|
14462
14532
|
|
|
14463
14533
|
// src/commands/upgrade.ts
|
|
14464
|
-
import { spawn as
|
|
14534
|
+
import { spawn as spawn6 } from "child_process";
|
|
14465
14535
|
import { accessSync, constants as constants2, existsSync as existsSync11, readFileSync as readFileSync16, realpathSync } from "fs";
|
|
14466
|
-
import { dirname as dirname15, join as
|
|
14536
|
+
import { dirname as dirname15, join as join19, win32 } from "path";
|
|
14467
14537
|
import chalk14 from "chalk";
|
|
14468
14538
|
|
|
14469
14539
|
// src/update-check.ts
|
|
14470
|
-
import { spawn as
|
|
14540
|
+
import { spawn as spawn5 } from "child_process";
|
|
14471
14541
|
import { mkdirSync as mkdirSync6, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
|
|
14472
|
-
import { dirname as dirname14, join as
|
|
14542
|
+
import { dirname as dirname14, join as join18 } from "path";
|
|
14473
14543
|
import chalk13 from "chalk";
|
|
14474
14544
|
var CLI_PACKAGE_NAME = "@tuned-tensor/cli";
|
|
14475
14545
|
var PACKAGE_LATEST_URL = "https://registry.npmjs.org/@tuned-tensor%2fcli/latest";
|
|
@@ -14518,7 +14588,7 @@ function compareVersions(left, right) {
|
|
|
14518
14588
|
return comparePrerelease(left.prerelease, right.prerelease);
|
|
14519
14589
|
}
|
|
14520
14590
|
function getCliUpdateCacheFile(env = process.env) {
|
|
14521
|
-
return
|
|
14591
|
+
return join18(getTunedTensorHome(env), "update-check.json");
|
|
14522
14592
|
}
|
|
14523
14593
|
function cliUpdateChecksDisabled(env = process.env) {
|
|
14524
14594
|
return [env.TT_NO_UPDATE_CHECK, env.NO_UPDATE_NOTIFIER, env.CI].some((value) => value !== void 0 && value !== "" && value !== "0" && value !== "false");
|
|
@@ -14620,7 +14690,7 @@ function spawnCliUpdateRefresh(cacheFile) {
|
|
|
14620
14690
|
const cached = readUpdateCache(cacheFile);
|
|
14621
14691
|
if (cached) recordLatestCliVersion(cacheFile, cached.latestVersion);
|
|
14622
14692
|
try {
|
|
14623
|
-
const child =
|
|
14693
|
+
const child = spawn5(
|
|
14624
14694
|
process.execPath,
|
|
14625
14695
|
["-e", REFRESH_SCRIPT, PACKAGE_LATEST_URL, cacheFile],
|
|
14626
14696
|
{ detached: true, stdio: "ignore", windowsHide: true }
|
|
@@ -14662,10 +14732,11 @@ function resolveGlobalInstall(entrypoint, platform = process.platform) {
|
|
|
14662
14732
|
if (/[\\/]_npx[\\/]/.test(script)) return null;
|
|
14663
14733
|
const match = platform === "win32" ? script.match(/^(.*)\\node_modules\\@tuned-tensor\\cli\\dist\\index\.js$/i) : script.match(/^(.*)\/lib\/node_modules\/@tuned-tensor\/cli\/dist\/index\.js$/);
|
|
14664
14734
|
if (!match?.[1]) return null;
|
|
14665
|
-
|
|
14735
|
+
const pathApi = platform === "win32" ? win32 : { dirname: dirname15 };
|
|
14736
|
+
return { prefix: match[1], packageDir: pathApi.dirname(pathApi.dirname(script)) };
|
|
14666
14737
|
}
|
|
14667
14738
|
function prefixIsWritable(prefix, platform) {
|
|
14668
|
-
const modules = platform === "win32" ?
|
|
14739
|
+
const modules = platform === "win32" ? join19(prefix, "node_modules") : join19(prefix, "lib", "node_modules");
|
|
14669
14740
|
try {
|
|
14670
14741
|
accessSync(existsSync11(modules) ? modules : prefix, constants2.W_OK);
|
|
14671
14742
|
return true;
|
|
@@ -14675,23 +14746,38 @@ function prefixIsWritable(prefix, platform) {
|
|
|
14675
14746
|
}
|
|
14676
14747
|
function installedVersion(packageDir) {
|
|
14677
14748
|
try {
|
|
14678
|
-
const manifest = JSON.parse(readFileSync16(
|
|
14749
|
+
const manifest = JSON.parse(readFileSync16(join19(packageDir, "package.json"), "utf8"));
|
|
14679
14750
|
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
14680
14751
|
} catch {
|
|
14681
14752
|
return void 0;
|
|
14682
14753
|
}
|
|
14683
14754
|
}
|
|
14684
|
-
function
|
|
14685
|
-
|
|
14755
|
+
function resolveWindowsNpmCli(env, execPath) {
|
|
14756
|
+
const path = Object.entries(env).find(([key]) => key.toLowerCase() === "path")?.[1] ?? "";
|
|
14757
|
+
const directories = [win32.dirname(execPath), ...path.split(";")];
|
|
14758
|
+
for (const directory of directories) {
|
|
14759
|
+
if (!directory) continue;
|
|
14760
|
+
const candidate = win32.join(directory.replace(/^"|"$/g, ""), "node_modules", "npm", "bin", "npm-cli.js");
|
|
14761
|
+
if (existsSync11(candidate)) return candidate;
|
|
14762
|
+
}
|
|
14763
|
+
throw new Error("Could not locate npm-cli.js beside Node.js or on PATH. Reinstall Node.js with npm, then retry.");
|
|
14764
|
+
}
|
|
14765
|
+
function isWindowsInstaller(prefix) {
|
|
14766
|
+
try {
|
|
14767
|
+
return JSON.parse(readFileSync16(join19(prefix, "tt-install.json"), "utf8")).manager === "windows-installer";
|
|
14768
|
+
} catch {
|
|
14769
|
+
return false;
|
|
14770
|
+
}
|
|
14686
14771
|
}
|
|
14687
|
-
function defaultNpmRunner(platform) {
|
|
14772
|
+
function defaultNpmRunner(platform, env) {
|
|
14688
14773
|
return async (args2) => await new Promise((resolve14, reject) => {
|
|
14689
14774
|
const windows = platform === "win32";
|
|
14690
|
-
const child =
|
|
14691
|
-
windows ?
|
|
14692
|
-
windows ?
|
|
14775
|
+
const child = spawn6(
|
|
14776
|
+
windows ? process.execPath : "npm",
|
|
14777
|
+
windows ? [resolveWindowsNpmCli(env, process.execPath), ...args2] : args2,
|
|
14693
14778
|
{
|
|
14694
|
-
|
|
14779
|
+
env,
|
|
14780
|
+
shell: false,
|
|
14695
14781
|
// Keep stdout clean for --json; npm progress goes to stderr.
|
|
14696
14782
|
stdio: ["ignore", isJsonMode() ? 2 : "inherit", "inherit"]
|
|
14697
14783
|
}
|
|
@@ -14700,7 +14786,10 @@ function defaultNpmRunner(platform) {
|
|
|
14700
14786
|
child.once("close", (code2) => resolve14(code2 ?? 1));
|
|
14701
14787
|
});
|
|
14702
14788
|
}
|
|
14703
|
-
function formatCommand(args2) {
|
|
14789
|
+
function formatCommand(args2, platform) {
|
|
14790
|
+
if (platform === "win32") {
|
|
14791
|
+
return ["npm.cmd", ...args2].map((arg) => /^[a-zA-Z0-9_@./:\\+=-]+$/.test(arg) ? arg : "'" + arg.replaceAll("'", "''") + "'").join(" ");
|
|
14792
|
+
}
|
|
14704
14793
|
return ["npm", ...args2].map((arg) => /\s/.test(arg) ? JSON.stringify(arg) : arg).join(" ");
|
|
14705
14794
|
}
|
|
14706
14795
|
function registerUpgradeCommand(program, deps) {
|
|
@@ -14720,6 +14809,15 @@ Set TT_NO_UPDATE_CHECK=1 to silence update notices.
|
|
|
14720
14809
|
const cacheFile = getCliUpdateCacheFile(deps.env);
|
|
14721
14810
|
const current = deps.version;
|
|
14722
14811
|
const target = requested?.trim() || "latest";
|
|
14812
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9.+-]*$/.test(target)) {
|
|
14813
|
+
throw new Error("Choose an exact version or npm dist-tag, such as 0.27.0, latest, or beta.");
|
|
14814
|
+
}
|
|
14815
|
+
const install = resolveGlobalInstall(deps.entrypoint, platform);
|
|
14816
|
+
if (!options.check && install && isWindowsInstaller(install.prefix)) {
|
|
14817
|
+
throw new Error(
|
|
14818
|
+
"This tt is managed by the Windows installer. Upgrade with:\n winget upgrade --id TunedTensor.CLI --exact --source winget\nOr download and run the newer Windows setup from https://github.com/tunedtensor/tuned-tensor-cli/releases.\nWinGet requires the release to be available in its catalog; use setup for prereleases."
|
|
14819
|
+
);
|
|
14820
|
+
}
|
|
14723
14821
|
let latest;
|
|
14724
14822
|
if (target === "latest") {
|
|
14725
14823
|
latest = await fetchLatestCliVersion({ fetchImpl: deps.fetchImpl, timeoutMs: 1e4 }) ?? void 0;
|
|
@@ -14750,7 +14848,6 @@ Set TT_NO_UPDATE_CHECK=1 to silence update notices.
|
|
|
14750
14848
|
}
|
|
14751
14849
|
return;
|
|
14752
14850
|
}
|
|
14753
|
-
const install = resolveGlobalInstall(deps.entrypoint, platform);
|
|
14754
14851
|
const packageSpec = `${CLI_PACKAGE_NAME}@${target}`;
|
|
14755
14852
|
if (!install) {
|
|
14756
14853
|
throw new Error(
|
|
@@ -14762,26 +14859,27 @@ Upgrade it the way you installed it, for example:
|
|
|
14762
14859
|
const npmArgs = ["install", "-g", "--ignore-scripts", "--prefix", install.prefix, packageSpec];
|
|
14763
14860
|
if (!prefixIsWritable(install.prefix, platform)) {
|
|
14764
14861
|
throw new Error(
|
|
14765
|
-
`tt is installed in ${install.prefix}, which this user can't write to.
|
|
14766
|
-
|
|
14862
|
+
`tt is installed in ${install.prefix}, which this user can't write to. ` + (platform === "win32" ? `Use an account with access to that directory and run:
|
|
14863
|
+
${formatCommand(npmArgs, platform)}` : `Run:
|
|
14864
|
+
sudo ${formatCommand(npmArgs, platform)}`)
|
|
14767
14865
|
);
|
|
14768
14866
|
}
|
|
14769
14867
|
if (!isJsonMode()) {
|
|
14770
14868
|
console.log(`Upgrading tt ${current} \u2192 ${chalk14.bold(latest ?? target)}`);
|
|
14771
|
-
console.log(chalk14.dim(`$ ${formatCommand(npmArgs)}`));
|
|
14869
|
+
console.log(chalk14.dim(`$ ${formatCommand(npmArgs, platform)}`));
|
|
14772
14870
|
}
|
|
14773
14871
|
let code2;
|
|
14774
14872
|
try {
|
|
14775
|
-
code2 = await (deps.runNpm ?? defaultNpmRunner(platform))(npmArgs);
|
|
14873
|
+
code2 = await (deps.runNpm ?? defaultNpmRunner(platform, deps.env))(npmArgs);
|
|
14776
14874
|
} catch (error) {
|
|
14777
14875
|
throw new Error(
|
|
14778
14876
|
`Could not run npm (${error instanceof Error ? error.message : String(error)}). Run it yourself:
|
|
14779
|
-
${formatCommand(npmArgs)}`
|
|
14877
|
+
${formatCommand(npmArgs, platform)}`
|
|
14780
14878
|
);
|
|
14781
14879
|
}
|
|
14782
14880
|
if (code2 !== 0) {
|
|
14783
14881
|
throw new Error(`npm exited with code ${code2}. Run it yourself to see details:
|
|
14784
|
-
${formatCommand(npmArgs)}`);
|
|
14882
|
+
${formatCommand(npmArgs, platform)}`);
|
|
14785
14883
|
}
|
|
14786
14884
|
const installed = installedVersion(install.packageDir) ?? latest ?? target;
|
|
14787
14885
|
if (isJsonMode()) {
|
|
@@ -14939,7 +15037,7 @@ async function runSelfCommand(args2, options = {}) {
|
|
|
14939
15037
|
throw new Error("Cannot locate the tt CLI entrypoint.");
|
|
14940
15038
|
}
|
|
14941
15039
|
return await new Promise((resolve14, reject) => {
|
|
14942
|
-
const child =
|
|
15040
|
+
const child = spawn7(process.execPath, [entrypoint, ...options.captureJson ? ["--json"] : [], ...args2], {
|
|
14943
15041
|
cwd: options.cwd,
|
|
14944
15042
|
env: options.env ?? process.env,
|
|
14945
15043
|
stdio: options.captureJson ? ["inherit", "pipe", "inherit"] : "inherit",
|
|
@@ -15220,7 +15318,7 @@ ${formatCliUpdateNotice(update)}
|
|
|
15220
15318
|
}
|
|
15221
15319
|
|
|
15222
15320
|
// src/index.ts
|
|
15223
|
-
runCli("0.
|
|
15321
|
+
runCli("0.28.0").catch((err) => {
|
|
15224
15322
|
reportError(err);
|
|
15225
15323
|
process.exit(1);
|
|
15226
15324
|
});
|