@tuned-tensor/cli 0.13.1-beta.0 → 0.13.1-beta.2
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 +31 -12
- package/dist/{chunk-YJYCGSHJ.js → chunk-2P2RL23O.js} +144 -6
- package/dist/chunk-2P2RL23O.js.map +1 -0
- package/dist/index.js +548 -43
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.js +77 -9
- package/dist/local-runtime.js.map +1 -1
- package/docs/local-runtime/architecture.md +24 -0
- package/examples/local-runtime/foundation-spec.json +31 -0
- package/package.json +6 -3
- package/training/foundation/pyproject.toml +24 -0
- package/training/foundation/src/common.py +41 -0
- package/training/foundation/src/data.py +135 -0
- package/training/foundation/src/evaluate.py +115 -0
- package/training/foundation/src/finetune.py +96 -0
- package/training/foundation/src/model.py +157 -0
- package/training/foundation/src/pretrain.py +66 -0
- package/training/foundation/src/rl.py +121 -0
- package/training/foundation/src/train_tokenizer.py +26 -0
- package/training/foundation/uv.lock +922 -0
- package/dist/chunk-YJYCGSHJ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
TRAINING_MODELS,
|
|
4
|
+
assertFoundationSpecReady,
|
|
4
5
|
assertLocalRunInputReady,
|
|
6
|
+
buildFoundationPythonCommand,
|
|
5
7
|
loadLocalRunInput,
|
|
6
8
|
loadLocalRunnerConfig,
|
|
7
|
-
runLocalPipeline
|
|
8
|
-
|
|
9
|
+
runLocalPipeline,
|
|
10
|
+
runLoggedProcess,
|
|
11
|
+
withFoundationPythonEnvironment
|
|
12
|
+
} from "./chunk-2P2RL23O.js";
|
|
9
13
|
|
|
10
14
|
// src/cli.ts
|
|
11
15
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -38,16 +42,18 @@ function validateSpec(spec) {
|
|
|
38
42
|
passed: Boolean(spec.system_prompt?.trim()),
|
|
39
43
|
message: spec.system_prompt?.trim() ? void 0 : "Spec is missing a system_prompt"
|
|
40
44
|
});
|
|
45
|
+
const isFoundation = spec.engine === "foundation";
|
|
41
46
|
checks.push({
|
|
42
|
-
name: "Has base model",
|
|
43
|
-
passed: Boolean(spec.base_model?.trim()),
|
|
44
|
-
message: spec.base_model?.trim() ? void 0 : "Spec is missing a base_model"
|
|
47
|
+
name: isFoundation ? "Has foundation engine" : "Has base model",
|
|
48
|
+
passed: isFoundation ? spec.foundation != null && typeof spec.foundation === "object" : Boolean(spec.base_model?.trim()),
|
|
49
|
+
message: isFoundation ? spec.foundation != null ? void 0 : "Foundation spec is missing a foundation block" : spec.base_model?.trim() ? void 0 : "Spec is missing a base_model"
|
|
45
50
|
});
|
|
46
51
|
const exCount = spec.examples?.length ?? 0;
|
|
52
|
+
const minExamples = isFoundation ? 2 : 1;
|
|
47
53
|
checks.push({
|
|
48
54
|
name: "Has examples",
|
|
49
|
-
passed: exCount
|
|
50
|
-
message: exCount
|
|
55
|
+
passed: exCount >= minExamples,
|
|
56
|
+
message: exCount >= minExamples ? `${exCount} example(s)` : isFoundation ? "Add at least two chat examples" : "Add at least one example"
|
|
51
57
|
});
|
|
52
58
|
checks.push({
|
|
53
59
|
name: "Has guidelines",
|
|
@@ -660,22 +666,44 @@ function getAgentSelection(env = process.env) {
|
|
|
660
666
|
}
|
|
661
667
|
|
|
662
668
|
// src/agent-model.ts
|
|
663
|
-
function missingProviderAuthMessage(
|
|
664
|
-
return
|
|
669
|
+
function missingProviderAuthMessage() {
|
|
670
|
+
return "No API key is saved for the selected model. Run /login, or /model to pick a different one.";
|
|
665
671
|
}
|
|
666
672
|
function unknownAgentProviderMessage(provider) {
|
|
667
673
|
return `Unknown provider "${provider}". Use /login <id> or /model <id>.`;
|
|
668
674
|
}
|
|
669
|
-
var MIN_REDACTED_SECRET_LENGTH = 8;
|
|
670
675
|
function isRecord(value) {
|
|
671
676
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
672
677
|
}
|
|
673
678
|
function pushSecret(secrets, value) {
|
|
674
679
|
if (typeof value !== "string") return;
|
|
675
680
|
const secret = value.trim();
|
|
676
|
-
if (secret
|
|
681
|
+
if (!secret) return;
|
|
677
682
|
if (!secrets.includes(secret)) secrets.push(secret);
|
|
678
683
|
}
|
|
684
|
+
function persistProviderApiKey(provider, apiKey, authPath = getAgentAuthPath()) {
|
|
685
|
+
const key = apiKey.trim();
|
|
686
|
+
if (!key) {
|
|
687
|
+
throw new Error("API key cannot be empty.");
|
|
688
|
+
}
|
|
689
|
+
let stored = {};
|
|
690
|
+
if (existsSync2(authPath)) {
|
|
691
|
+
let parsed;
|
|
692
|
+
try {
|
|
693
|
+
parsed = JSON.parse(readFileSync2(authPath, "utf-8"));
|
|
694
|
+
} catch (error) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
`Failed to read ${authPath}: ${error instanceof Error ? error.message : String(error)}`
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
if (!isRecord(parsed)) {
|
|
700
|
+
throw new Error(`Invalid auth.json: expected an object at ${authPath}`);
|
|
701
|
+
}
|
|
702
|
+
stored = parsed;
|
|
703
|
+
}
|
|
704
|
+
stored[provider] = { type: "api_key", key };
|
|
705
|
+
writePrivateJson(authPath, stored);
|
|
706
|
+
}
|
|
679
707
|
function readStoredProviderSecrets(authPath = getAgentAuthPath()) {
|
|
680
708
|
if (!existsSync2(authPath)) return [];
|
|
681
709
|
let parsed;
|
|
@@ -700,7 +728,7 @@ function readStoredProviderSecrets(authPath = getAgentAuthPath()) {
|
|
|
700
728
|
function resolveAgentModel(runtime, selection) {
|
|
701
729
|
const resolved = resolveAgentModelDefinition(runtime, selection);
|
|
702
730
|
if (!runtime.hasConfiguredAuth(selection.provider)) {
|
|
703
|
-
throw new Error(missingProviderAuthMessage(
|
|
731
|
+
throw new Error(missingProviderAuthMessage());
|
|
704
732
|
}
|
|
705
733
|
return resolved;
|
|
706
734
|
}
|
|
@@ -891,7 +919,9 @@ import {
|
|
|
891
919
|
PIPELINE_STEP_CAPABILITIES,
|
|
892
920
|
PIPELINE_STEP_USES,
|
|
893
921
|
PIPELINE_TARGETS,
|
|
922
|
+
canonicalFoundationPipeline as canonicalFoundationPipeline2,
|
|
894
923
|
canonicalJson,
|
|
924
|
+
isFoundationPipeline,
|
|
895
925
|
parsePipeline as parsePipeline2,
|
|
896
926
|
pipelineDocumentSchema as pipelineDocumentSchema2,
|
|
897
927
|
pipelineHash
|
|
@@ -899,8 +929,84 @@ import {
|
|
|
899
929
|
function stepReferences(step) {
|
|
900
930
|
if (step.uses === "evaluate") return step.with.model === "base" ? [] : [step.with.model];
|
|
901
931
|
if (step.uses === "compare") return [step.with.before, step.with.after];
|
|
932
|
+
if (step.uses === "pretrain") return [step.with.tokenizer];
|
|
933
|
+
if (step.uses === "finetune" || step.uses === "rl") return [step.with.model];
|
|
902
934
|
return [];
|
|
903
935
|
}
|
|
936
|
+
function pipelineFromFoundationHyperparameters(name, hp) {
|
|
937
|
+
const lastTrainId = hp.rl_steps > 0 ? "rl" : "sft";
|
|
938
|
+
const steps = [
|
|
939
|
+
{
|
|
940
|
+
id: "tokenize",
|
|
941
|
+
uses: "tokenize",
|
|
942
|
+
target: "local",
|
|
943
|
+
with: { vocabSize: hp.vocab_size, maxChars: hp.max_chars }
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
id: "pretrain",
|
|
947
|
+
uses: "pretrain",
|
|
948
|
+
target: "local",
|
|
949
|
+
with: {
|
|
950
|
+
tokenizer: { from: "tokenize.tokenizer" },
|
|
951
|
+
depth: hp.depth,
|
|
952
|
+
steps: hp.pretrain_steps,
|
|
953
|
+
batchSize: hp.batch_size,
|
|
954
|
+
sequenceLength: hp.sequence_length,
|
|
955
|
+
nprocPerNode: hp.nproc_per_node
|
|
956
|
+
}
|
|
957
|
+
},
|
|
958
|
+
{
|
|
959
|
+
id: "bpb",
|
|
960
|
+
uses: "evaluate",
|
|
961
|
+
target: "local",
|
|
962
|
+
with: { model: { from: "pretrain.model" }, evaluator: "bpb" }
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
id: "sft",
|
|
966
|
+
uses: "finetune",
|
|
967
|
+
target: "local",
|
|
968
|
+
with: {
|
|
969
|
+
model: { from: "pretrain.model" },
|
|
970
|
+
steps: hp.finetune_steps,
|
|
971
|
+
batchSize: hp.batch_size
|
|
972
|
+
}
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
id: "chat",
|
|
976
|
+
uses: "evaluate",
|
|
977
|
+
target: "local",
|
|
978
|
+
with: { model: { from: "sft.model" }, evaluator: "chat" }
|
|
979
|
+
}
|
|
980
|
+
];
|
|
981
|
+
if (hp.rl_steps > 0) {
|
|
982
|
+
steps.push(
|
|
983
|
+
{
|
|
984
|
+
id: "rl",
|
|
985
|
+
uses: "rl",
|
|
986
|
+
target: "local",
|
|
987
|
+
with: { model: { from: "sft.model" }, steps: hp.rl_steps }
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
id: "chat_rl",
|
|
991
|
+
uses: "evaluate",
|
|
992
|
+
target: "local",
|
|
993
|
+
with: { model: { from: "rl.model" }, evaluator: "chat" }
|
|
994
|
+
}
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
steps.push({
|
|
998
|
+
id: "infer",
|
|
999
|
+
uses: "evaluate",
|
|
1000
|
+
target: "local",
|
|
1001
|
+
with: { model: { from: `${lastTrainId}.model` }, evaluator: "inference" }
|
|
1002
|
+
});
|
|
1003
|
+
return {
|
|
1004
|
+
version: 1,
|
|
1005
|
+
name,
|
|
1006
|
+
runtime: { engine: "foundation" },
|
|
1007
|
+
steps
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
904
1010
|
function producerId(reference) {
|
|
905
1011
|
return reference.from.slice(0, reference.from.lastIndexOf("."));
|
|
906
1012
|
}
|
|
@@ -1970,7 +2076,6 @@ function formatShellStatus(context) {
|
|
|
1970
2076
|
}
|
|
1971
2077
|
|
|
1972
2078
|
// src/agent-control.ts
|
|
1973
|
-
import { chmodSync as chmodSync3, existsSync as existsSync3 } from "fs";
|
|
1974
2079
|
var FEATURED_AGENT_PROVIDERS = [
|
|
1975
2080
|
"openai",
|
|
1976
2081
|
"openrouter"
|
|
@@ -2089,12 +2194,10 @@ async function loginAgentProvider(runtime, provider, apiKey) {
|
|
|
2089
2194
|
if (!key) {
|
|
2090
2195
|
throw new Error("API key cannot be empty.");
|
|
2091
2196
|
}
|
|
2092
|
-
|
|
2093
|
-
|
|
2197
|
+
persistProviderApiKey(choice.id, key);
|
|
2198
|
+
if (typeof runtime.setRuntimeApiKey === "function") {
|
|
2199
|
+
await runtime.setRuntimeApiKey(choice.id, key);
|
|
2094
2200
|
}
|
|
2095
|
-
await runtime.setRuntimeApiKey(choice.id, key);
|
|
2096
|
-
const authPath = getAgentAuthPath();
|
|
2097
|
-
if (existsSync3(authPath)) chmodSync3(authPath, 384);
|
|
2098
2201
|
return { provider: choice.id };
|
|
2099
2202
|
}
|
|
2100
2203
|
function setAgentModel(runtime, env, provider, model, options = {}) {
|
|
@@ -3568,7 +3671,7 @@ ${chalk3.yellow.bold("Approval required")} ${chalk3.dim(action.id.slice(0, 8))}
|
|
|
3568
3671
|
import { constants as osConstants } from "os";
|
|
3569
3672
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
3570
3673
|
import { spawn } from "child_process";
|
|
3571
|
-
import { existsSync as
|
|
3674
|
+
import { existsSync as existsSync3 } from "fs";
|
|
3572
3675
|
import { fileURLToPath } from "url";
|
|
3573
3676
|
import {
|
|
3574
3677
|
readFile as readFile3,
|
|
@@ -3597,7 +3700,9 @@ var CLOUD_SPEC_KEYS = [
|
|
|
3597
3700
|
];
|
|
3598
3701
|
var LOCAL_ONLY_SPEC_KEYS = [
|
|
3599
3702
|
"hyperparameters",
|
|
3600
|
-
"dataset_prebuilt"
|
|
3703
|
+
"dataset_prebuilt",
|
|
3704
|
+
"engine",
|
|
3705
|
+
"foundation"
|
|
3601
3706
|
];
|
|
3602
3707
|
var LOCAL_SPEC_KEYS = [
|
|
3603
3708
|
"id",
|
|
@@ -4388,7 +4493,7 @@ async function projectSpecArguments(originalArgs, cwd) {
|
|
|
4388
4493
|
}
|
|
4389
4494
|
function resolveLocalCliEntrypoint() {
|
|
4390
4495
|
const entrypoint = fileURLToPath(new URL("./local-runtime.js", import.meta.url));
|
|
4391
|
-
if (!
|
|
4496
|
+
if (!existsSync3(entrypoint)) {
|
|
4392
4497
|
throw new Error(
|
|
4393
4498
|
"The bundled local runtime is missing. Rebuild or reinstall @tuned-tensor/cli."
|
|
4394
4499
|
);
|
|
@@ -4718,24 +4823,389 @@ function registerAgentCommands(parent, options) {
|
|
|
4718
4823
|
}
|
|
4719
4824
|
|
|
4720
4825
|
// src/commands/pipeline.ts
|
|
4721
|
-
import { existsSync as
|
|
4722
|
-
import { resolve as
|
|
4826
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
4827
|
+
import { resolve as resolve5 } from "path";
|
|
4828
|
+
|
|
4829
|
+
// src/local-runtime/foundation-runner.ts
|
|
4830
|
+
import { createHash as createHash2, randomUUID as randomUUID5 } from "crypto";
|
|
4831
|
+
import { createReadStream } from "fs";
|
|
4832
|
+
import { chmod as chmod2, lstat as lstat3, mkdir as mkdir3, open as open2, readdir as readdir3, readFile as readFile4 } from "fs/promises";
|
|
4833
|
+
import { dirname as dirname4, join as join7, resolve as resolve4 } from "path";
|
|
4834
|
+
function isRef(value) {
|
|
4835
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value) && typeof value.from === "string";
|
|
4836
|
+
}
|
|
4837
|
+
function parseRef(value) {
|
|
4838
|
+
const index = value.from.lastIndexOf(".");
|
|
4839
|
+
if (index <= 0) throw new Error(`Invalid artifact reference: ${value.from}`);
|
|
4840
|
+
return { stepId: value.from.slice(0, index), kind: value.from.slice(index + 1) };
|
|
4841
|
+
}
|
|
4842
|
+
async function hashPath(path) {
|
|
4843
|
+
const info = await lstat3(path);
|
|
4844
|
+
if (info.isSymbolicLink()) {
|
|
4845
|
+
throw new Error(`Foundation artifacts must not contain symbolic links: ${path}`);
|
|
4846
|
+
}
|
|
4847
|
+
const hash = createHash2("sha256");
|
|
4848
|
+
if (info.isDirectory()) {
|
|
4849
|
+
for (const name of (await readdir3(path)).sort()) {
|
|
4850
|
+
hash.update(name);
|
|
4851
|
+
hash.update(await hashPath(join7(path, name)));
|
|
4852
|
+
}
|
|
4853
|
+
return hash.digest("hex");
|
|
4854
|
+
}
|
|
4855
|
+
if (!info.isFile()) {
|
|
4856
|
+
throw new Error(`Foundation artifacts must contain only regular files and directories: ${path}`);
|
|
4857
|
+
}
|
|
4858
|
+
await new Promise((done, reject) => {
|
|
4859
|
+
const stream = createReadStream(path);
|
|
4860
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
4861
|
+
stream.on("error", reject);
|
|
4862
|
+
stream.on("end", () => done());
|
|
4863
|
+
});
|
|
4864
|
+
return hash.digest("hex");
|
|
4865
|
+
}
|
|
4866
|
+
async function makePrivateTree(path) {
|
|
4867
|
+
const info = await lstat3(path);
|
|
4868
|
+
if (info.isSymbolicLink()) {
|
|
4869
|
+
throw new Error(`Foundation artifacts must not contain symbolic links: ${path}`);
|
|
4870
|
+
}
|
|
4871
|
+
if (info.isDirectory()) {
|
|
4872
|
+
await chmod2(path, 448);
|
|
4873
|
+
for (const name of await readdir3(path)) {
|
|
4874
|
+
await makePrivateTree(join7(path, name));
|
|
4875
|
+
}
|
|
4876
|
+
return;
|
|
4877
|
+
}
|
|
4878
|
+
if (info.isFile()) {
|
|
4879
|
+
await chmod2(path, 384);
|
|
4880
|
+
return;
|
|
4881
|
+
}
|
|
4882
|
+
throw new Error(`Foundation artifacts must contain only regular files and directories: ${path}`);
|
|
4883
|
+
}
|
|
4884
|
+
async function readRequiredJsonObject(path, description) {
|
|
4885
|
+
let content;
|
|
4886
|
+
try {
|
|
4887
|
+
content = await readFile4(path, "utf8");
|
|
4888
|
+
} catch (error) {
|
|
4889
|
+
throw new Error(`${description} is missing or unreadable: ${path}`, { cause: error });
|
|
4890
|
+
}
|
|
4891
|
+
let parsed;
|
|
4892
|
+
try {
|
|
4893
|
+
parsed = JSON.parse(content);
|
|
4894
|
+
} catch (error) {
|
|
4895
|
+
throw new Error(`${description} is not valid JSON: ${path}`, { cause: error });
|
|
4896
|
+
}
|
|
4897
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4898
|
+
throw new Error(`${description} must be a JSON object: ${path}`);
|
|
4899
|
+
}
|
|
4900
|
+
return parsed;
|
|
4901
|
+
}
|
|
4902
|
+
async function writePrivateText(path, content) {
|
|
4903
|
+
const file = await open2(path, "w", 384);
|
|
4904
|
+
try {
|
|
4905
|
+
await file.chmod(384);
|
|
4906
|
+
await file.writeFile(content, "utf8");
|
|
4907
|
+
} finally {
|
|
4908
|
+
await file.close();
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4911
|
+
async function artifactFor(path) {
|
|
4912
|
+
return { path, sha256: await hashPath(path) };
|
|
4913
|
+
}
|
|
4914
|
+
async function requireRegularNonemptyFile(path, description) {
|
|
4915
|
+
let info;
|
|
4916
|
+
try {
|
|
4917
|
+
info = await lstat3(path);
|
|
4918
|
+
} catch (error) {
|
|
4919
|
+
throw new Error(`${description} is missing: ${path}`, { cause: error });
|
|
4920
|
+
}
|
|
4921
|
+
if (info.isSymbolicLink() || !info.isFile() || info.size === 0) {
|
|
4922
|
+
throw new Error(`${description} must be a non-empty regular file: ${path}`);
|
|
4923
|
+
}
|
|
4924
|
+
}
|
|
4925
|
+
function requirePositiveIntegerFields(value, fields, description) {
|
|
4926
|
+
for (const field of fields) {
|
|
4927
|
+
if (!Number.isInteger(value[field]) || Number(value[field]) <= 0) {
|
|
4928
|
+
throw new Error(`${description}.${field} must be a positive integer.`);
|
|
4929
|
+
}
|
|
4930
|
+
}
|
|
4931
|
+
}
|
|
4932
|
+
function stepConfig(step) {
|
|
4933
|
+
if (step.uses === "train") return {};
|
|
4934
|
+
if (step.uses === "tokenize") return { ...step.with ?? {} };
|
|
4935
|
+
return { ...step.with };
|
|
4936
|
+
}
|
|
4937
|
+
function assertFoundationPlanSupported(spec, plan) {
|
|
4938
|
+
const supported = /* @__PURE__ */ new Set(["tokenize", "pretrain", "finetune", "rl", "evaluate"]);
|
|
4939
|
+
const unsupported = plan.steps.find((step) => !supported.has(step.uses));
|
|
4940
|
+
if (unsupported) {
|
|
4941
|
+
throw new Error(
|
|
4942
|
+
`Foundation runner does not implement ${unsupported.uses} (step ${unsupported.id}).`
|
|
4943
|
+
);
|
|
4944
|
+
}
|
|
4945
|
+
if (plan.steps.some((step) => step.uses === "rl")) {
|
|
4946
|
+
const invalidReward = spec.examples.findIndex(
|
|
4947
|
+
(example) => !/-?\d+(?:\.\d+)?/.test(example.output.replaceAll(",", ""))
|
|
4948
|
+
);
|
|
4949
|
+
if (invalidReward >= 0) {
|
|
4950
|
+
throw new Error(
|
|
4951
|
+
`Foundation RL requires a numeric expected output for every example; examples[${invalidReward}].output has none.`
|
|
4952
|
+
);
|
|
4953
|
+
}
|
|
4954
|
+
}
|
|
4955
|
+
for (const step of plan.steps) {
|
|
4956
|
+
if (step.uses !== "pretrain") continue;
|
|
4957
|
+
const processCount = step.with.nprocPerNode ?? spec.foundation.nproc_per_node;
|
|
4958
|
+
if (processCount !== 1) {
|
|
4959
|
+
throw new Error(
|
|
4960
|
+
`Foundation nproc_per_node is ${processCount}; only 1 is supported by the current single-process runtime.`
|
|
4961
|
+
);
|
|
4962
|
+
}
|
|
4963
|
+
}
|
|
4964
|
+
}
|
|
4965
|
+
async function defaultFoundationSpawn(args2) {
|
|
4966
|
+
const launched = buildFoundationPythonCommand(args2.entrypoint, ["--config", args2.configPath]);
|
|
4967
|
+
const result2 = await runLoggedProcess({
|
|
4968
|
+
command: launched.command,
|
|
4969
|
+
commandArgs: launched.commandArgs,
|
|
4970
|
+
env: withFoundationPythonEnvironment(process.env),
|
|
4971
|
+
logPath: args2.logPath,
|
|
4972
|
+
stage: args2.stepId
|
|
4973
|
+
});
|
|
4974
|
+
if (result2.exitCode !== 0) {
|
|
4975
|
+
throw new Error(
|
|
4976
|
+
`Foundation step "${args2.stepId}" failed (exit ${result2.exitCode}).${result2.stderr ? `
|
|
4977
|
+
${result2.stderr}` : ""}`
|
|
4978
|
+
);
|
|
4979
|
+
}
|
|
4980
|
+
}
|
|
4981
|
+
async function runFoundationPipeline(args2) {
|
|
4982
|
+
assertFoundationPlanSupported(args2.spec, args2.plan);
|
|
4983
|
+
const outputDir = resolve4(
|
|
4984
|
+
args2.outputDir ?? join7(dirname4(resolve4(args2.specPath)), ".tt-foundation-runs", randomUUID5())
|
|
4985
|
+
);
|
|
4986
|
+
await mkdir3(outputDir, { recursive: true, mode: 448 });
|
|
4987
|
+
await chmod2(outputDir, 448);
|
|
4988
|
+
const spawnStep = args2.spawnStep ?? defaultFoundationSpawn;
|
|
4989
|
+
const produced = /* @__PURE__ */ new Map();
|
|
4990
|
+
const steps = [];
|
|
4991
|
+
const examples = args2.spec.examples.map((example) => ({ input: example.input, output: example.output }));
|
|
4992
|
+
const hp = args2.spec.foundation;
|
|
4993
|
+
let tokenizerDir;
|
|
4994
|
+
let modelDir;
|
|
4995
|
+
const resolveKind = (stepId, kind) => {
|
|
4996
|
+
const path = produced.get(stepId)?.[kind];
|
|
4997
|
+
if (!path) throw new Error(`Step ${stepId} did not produce ${kind}.`);
|
|
4998
|
+
return path;
|
|
4999
|
+
};
|
|
5000
|
+
const resolveFrom = (value, kind, fallback) => {
|
|
5001
|
+
if (isRef(value)) return resolveKind(parseRef(value).stepId, parseRef(value).kind);
|
|
5002
|
+
if (fallback) return fallback;
|
|
5003
|
+
throw new Error(`Missing ${kind} artifact.`);
|
|
5004
|
+
};
|
|
5005
|
+
for (const step of args2.plan.steps) {
|
|
5006
|
+
const stepDir = join7(outputDir, step.id);
|
|
5007
|
+
const output = join7(stepDir, "output");
|
|
5008
|
+
await mkdir3(stepDir, { recursive: true, mode: 448 });
|
|
5009
|
+
await chmod2(stepDir, 448);
|
|
5010
|
+
await mkdir3(output, { recursive: true, mode: 448 });
|
|
5011
|
+
await chmod2(output, 448);
|
|
5012
|
+
const configPath2 = join7(stepDir, "config.json");
|
|
5013
|
+
const logPath = join7(stepDir, "step.log");
|
|
5014
|
+
const fields = stepConfig(step);
|
|
5015
|
+
let entrypoint;
|
|
5016
|
+
let config;
|
|
5017
|
+
if (step.uses === "tokenize") {
|
|
5018
|
+
entrypoint = "train_tokenizer.py";
|
|
5019
|
+
config = {
|
|
5020
|
+
output_dir: output,
|
|
5021
|
+
vocab_size: fields.vocabSize ?? hp.vocab_size,
|
|
5022
|
+
max_chars: fields.maxChars ?? hp.max_chars,
|
|
5023
|
+
system_prompt: args2.spec.system_prompt,
|
|
5024
|
+
examples
|
|
5025
|
+
};
|
|
5026
|
+
} else if (step.uses === "pretrain") {
|
|
5027
|
+
tokenizerDir = resolveFrom(fields.tokenizer, "tokenizer", tokenizerDir);
|
|
5028
|
+
entrypoint = "pretrain.py";
|
|
5029
|
+
config = {
|
|
5030
|
+
tokenizer_dir: tokenizerDir,
|
|
5031
|
+
output_dir: output,
|
|
5032
|
+
depth: fields.depth ?? hp.depth,
|
|
5033
|
+
steps: fields.steps ?? hp.pretrain_steps,
|
|
5034
|
+
batch_size: fields.batchSize ?? hp.batch_size,
|
|
5035
|
+
sequence_length: fields.sequenceLength ?? hp.sequence_length,
|
|
5036
|
+
nproc_per_node: fields.nprocPerNode ?? hp.nproc_per_node,
|
|
5037
|
+
max_chars: hp.max_chars,
|
|
5038
|
+
system_prompt: args2.spec.system_prompt,
|
|
5039
|
+
examples
|
|
5040
|
+
};
|
|
5041
|
+
} else if (step.uses === "finetune") {
|
|
5042
|
+
modelDir = resolveFrom(fields.model, "model", modelDir);
|
|
5043
|
+
entrypoint = "finetune.py";
|
|
5044
|
+
config = {
|
|
5045
|
+
model_dir: modelDir,
|
|
5046
|
+
tokenizer_dir: tokenizerDir,
|
|
5047
|
+
output_dir: output,
|
|
5048
|
+
steps: fields.steps ?? hp.finetune_steps,
|
|
5049
|
+
batch_size: fields.batchSize ?? hp.batch_size,
|
|
5050
|
+
sequence_length: hp.sequence_length,
|
|
5051
|
+
system_prompt: args2.spec.system_prompt,
|
|
5052
|
+
examples
|
|
5053
|
+
};
|
|
5054
|
+
} else if (step.uses === "rl") {
|
|
5055
|
+
modelDir = resolveFrom(fields.model, "model", modelDir);
|
|
5056
|
+
entrypoint = "rl.py";
|
|
5057
|
+
config = {
|
|
5058
|
+
model_dir: modelDir,
|
|
5059
|
+
tokenizer_dir: tokenizerDir,
|
|
5060
|
+
output_dir: output,
|
|
5061
|
+
steps: fields.steps ?? hp.rl_steps,
|
|
5062
|
+
system_prompt: args2.spec.system_prompt,
|
|
5063
|
+
examples
|
|
5064
|
+
};
|
|
5065
|
+
} else if (step.uses === "evaluate") {
|
|
5066
|
+
modelDir = resolveFrom(fields.model, "model", modelDir);
|
|
5067
|
+
entrypoint = "evaluate.py";
|
|
5068
|
+
config = {
|
|
5069
|
+
evaluator: fields.evaluator ?? "bpb",
|
|
5070
|
+
model_dir: modelDir,
|
|
5071
|
+
tokenizer_dir: tokenizerDir,
|
|
5072
|
+
output_dir: output,
|
|
5073
|
+
sequence_length: hp.sequence_length,
|
|
5074
|
+
max_chars: hp.max_chars,
|
|
5075
|
+
system_prompt: args2.spec.system_prompt,
|
|
5076
|
+
examples
|
|
5077
|
+
};
|
|
5078
|
+
} else {
|
|
5079
|
+
throw new Error(`Foundation runner does not implement ${step.uses} (step ${step.id}).`);
|
|
5080
|
+
}
|
|
5081
|
+
if ((step.uses === "finetune" || step.uses === "rl" || step.uses === "evaluate") && !tokenizerDir) {
|
|
5082
|
+
throw new Error(`Step ${step.id} needs a tokenizer from an earlier tokenize step.`);
|
|
5083
|
+
}
|
|
5084
|
+
await writePrivateText(configPath2, `${JSON.stringify(config, null, 2)}
|
|
5085
|
+
`);
|
|
5086
|
+
await spawnStep({ entrypoint, configPath: configPath2, logPath, stepId: step.id });
|
|
5087
|
+
await makePrivateTree(stepDir);
|
|
5088
|
+
const metrics = await readRequiredJsonObject(
|
|
5089
|
+
join7(output, "metrics.json"),
|
|
5090
|
+
`Foundation step "${step.id}" metrics`
|
|
5091
|
+
);
|
|
5092
|
+
if (metrics.ok !== true) {
|
|
5093
|
+
throw new Error(`Foundation step "${step.id}" metrics must report ok: true.`);
|
|
5094
|
+
}
|
|
5095
|
+
const artifacts = {};
|
|
5096
|
+
const record4 = {};
|
|
5097
|
+
if (step.uses === "tokenize") {
|
|
5098
|
+
const tokenizerPath = join7(output, "tokenizer.json");
|
|
5099
|
+
await requireRegularNonemptyFile(tokenizerPath, `Foundation step "${step.id}" tokenizer.json`);
|
|
5100
|
+
await readRequiredJsonObject(tokenizerPath, `Foundation step "${step.id}" tokenizer.json`);
|
|
5101
|
+
record4.tokenizer = output;
|
|
5102
|
+
tokenizerDir = output;
|
|
5103
|
+
artifacts.tokenizer = await artifactFor(output);
|
|
5104
|
+
} else if (step.uses === "pretrain" || step.uses === "finetune" || step.uses === "rl") {
|
|
5105
|
+
const modelPath = join7(output, "model.safetensors");
|
|
5106
|
+
const modelConfigPath = join7(output, "config.json");
|
|
5107
|
+
await requireRegularNonemptyFile(modelPath, `Foundation step "${step.id}" model.safetensors`);
|
|
5108
|
+
const modelConfig = await readRequiredJsonObject(
|
|
5109
|
+
modelConfigPath,
|
|
5110
|
+
`Foundation step "${step.id}" config.json`
|
|
5111
|
+
);
|
|
5112
|
+
requirePositiveIntegerFields(
|
|
5113
|
+
modelConfig,
|
|
5114
|
+
["depth", "width", "heads", "vocab_size", "sequence_length"],
|
|
5115
|
+
`Foundation step "${step.id}" config.json`
|
|
5116
|
+
);
|
|
5117
|
+
record4.model = output;
|
|
5118
|
+
modelDir = output;
|
|
5119
|
+
artifacts.model = await artifactFor(output);
|
|
5120
|
+
} else if (step.uses === "evaluate") {
|
|
5121
|
+
const reportPath2 = join7(output, "report.json");
|
|
5122
|
+
const stepReport = await readRequiredJsonObject(
|
|
5123
|
+
reportPath2,
|
|
5124
|
+
`Foundation step "${step.id}" report.json`
|
|
5125
|
+
);
|
|
5126
|
+
if (stepReport.ok !== true) {
|
|
5127
|
+
throw new Error(`Foundation step "${step.id}" report.json must report ok: true.`);
|
|
5128
|
+
}
|
|
5129
|
+
record4.report = reportPath2;
|
|
5130
|
+
artifacts.report = await artifactFor(reportPath2);
|
|
5131
|
+
}
|
|
5132
|
+
produced.set(step.id, record4);
|
|
5133
|
+
steps.push({ id: step.id, uses: step.uses, metrics, artifacts });
|
|
5134
|
+
}
|
|
5135
|
+
const report = {
|
|
5136
|
+
status: "succeeded",
|
|
5137
|
+
...args2.plan.name ? { name: args2.plan.name } : {},
|
|
5138
|
+
spec: args2.specPath,
|
|
5139
|
+
steps
|
|
5140
|
+
};
|
|
5141
|
+
const reportPath = join7(outputDir, "report.json");
|
|
5142
|
+
await writePrivateText(reportPath, `${JSON.stringify(report, null, 2)}
|
|
5143
|
+
`);
|
|
5144
|
+
return {
|
|
5145
|
+
status: "succeeded",
|
|
5146
|
+
...args2.plan.name ? { name: args2.plan.name } : {},
|
|
5147
|
+
report_path: reportPath,
|
|
5148
|
+
steps
|
|
5149
|
+
};
|
|
5150
|
+
}
|
|
5151
|
+
|
|
5152
|
+
// src/commands/pipeline.ts
|
|
4723
5153
|
var DEFAULT_PIPELINE_FILE = "tunedtensor.pipeline.json";
|
|
5154
|
+
var DEFAULT_SPEC_FILE = "tunedtensor.json";
|
|
4724
5155
|
function parseList(value) {
|
|
4725
5156
|
if (!value) return void 0;
|
|
4726
5157
|
const ids = value.split(",").map((id) => id.trim()).filter(Boolean);
|
|
4727
5158
|
if (!ids.length) throw new Error("Step selection must name at least one step.");
|
|
4728
5159
|
return ids;
|
|
4729
5160
|
}
|
|
4730
|
-
function
|
|
4731
|
-
const resolved =
|
|
4732
|
-
if (!
|
|
5161
|
+
function loadPipelineFile(path) {
|
|
5162
|
+
const resolved = resolve5(path);
|
|
5163
|
+
if (!existsSync4(resolved)) throw new Error(`Pipeline file not found: ${path}. Run \`tt pipeline init\` to create one.`);
|
|
4733
5164
|
try {
|
|
4734
5165
|
return JSON.parse(readFileSync4(resolved, "utf8"));
|
|
4735
5166
|
} catch (error) {
|
|
4736
5167
|
throw new Error(`Pipeline file must be JSON: ${error.message}`);
|
|
4737
5168
|
}
|
|
4738
5169
|
}
|
|
5170
|
+
function pipelineFromSpec(input) {
|
|
5171
|
+
return pipelineFromFoundationHyperparameters(input.spec.name, input.spec.foundation);
|
|
5172
|
+
}
|
|
5173
|
+
function isParsedFoundationPipeline(document) {
|
|
5174
|
+
try {
|
|
5175
|
+
return isFoundationPipeline(parsePipeline2(document));
|
|
5176
|
+
} catch {
|
|
5177
|
+
return false;
|
|
5178
|
+
}
|
|
5179
|
+
}
|
|
5180
|
+
async function resolvePipelineDocument(options) {
|
|
5181
|
+
const filePath = resolve5(options.file);
|
|
5182
|
+
const fileExists = existsSync4(filePath);
|
|
5183
|
+
const specPath = options.spec ? resolve5(options.spec) : void 0;
|
|
5184
|
+
const specInput = specPath && existsSync4(specPath) ? await loadLocalRunInput(specPath) : void 0;
|
|
5185
|
+
if (specInput?.kind === "foundation-spec") {
|
|
5186
|
+
if (fileExists) {
|
|
5187
|
+
const document = loadPipelineFile(options.file);
|
|
5188
|
+
if (isParsedFoundationPipeline(document)) return document;
|
|
5189
|
+
throw new Error(
|
|
5190
|
+
`Pipeline file ${options.file} is an adapter recipe, but ${options.spec} is a foundation spec. Use a foundation pipeline, omit --file, or pass an adapter spec.`
|
|
5191
|
+
);
|
|
5192
|
+
}
|
|
5193
|
+
return pipelineFromSpec(specInput);
|
|
5194
|
+
}
|
|
5195
|
+
if (fileExists) {
|
|
5196
|
+
const document = loadPipelineFile(options.file);
|
|
5197
|
+
if (specInput && isParsedFoundationPipeline(document)) {
|
|
5198
|
+
throw new Error(
|
|
5199
|
+
`Pipeline file ${options.file} is a foundation recipe, but ${options.spec} is an adapter spec. Use a foundation spec, omit --file, or pass an adapter pipeline.`
|
|
5200
|
+
);
|
|
5201
|
+
}
|
|
5202
|
+
return document;
|
|
5203
|
+
}
|
|
5204
|
+
if (specPath && !existsSync4(specPath)) await loadLocalRunInput(specPath);
|
|
5205
|
+
throw new Error(
|
|
5206
|
+
`Pipeline file not found: ${options.file}. Run \`tt pipeline init\` or pass a foundation --spec.`
|
|
5207
|
+
);
|
|
5208
|
+
}
|
|
4739
5209
|
function outputPlan(plan) {
|
|
4740
5210
|
if (isJsonMode()) return printJson(plan);
|
|
4741
5211
|
const typed = plan;
|
|
@@ -4746,28 +5216,49 @@ function outputPlan(plan) {
|
|
|
4746
5216
|
}
|
|
4747
5217
|
function registerPipelineCommands(parent) {
|
|
4748
5218
|
const pipeline = parent.command("pipeline").description("Create, validate, inspect, and safely dry-run ordered pipeline recipes");
|
|
4749
|
-
pipeline.command("init").description("Write a canonical v1 pipeline recipe").option("-f, --file <path>", "Output file", DEFAULT_PIPELINE_FILE).action((options) => {
|
|
4750
|
-
const
|
|
4751
|
-
if (
|
|
4752
|
-
|
|
5219
|
+
pipeline.command("init").description("Write a canonical v1 pipeline recipe").option("-f, --file <path>", "Output file", DEFAULT_PIPELINE_FILE).option("--engine <engine>", "adapter (default) or foundation").option("--spec <path>", "Foundation spec whose hyperparameters stamp the DAG").action(async (options) => {
|
|
5220
|
+
const engine = options.engine ?? "adapter";
|
|
5221
|
+
if (engine !== "adapter" && engine !== "foundation") {
|
|
5222
|
+
throw new Error(`--engine must be adapter or foundation, got: ${engine}`);
|
|
5223
|
+
}
|
|
5224
|
+
if (engine !== "foundation" && options.spec) {
|
|
5225
|
+
throw new Error("--spec is only valid with --engine foundation.");
|
|
5226
|
+
}
|
|
5227
|
+
const path = resolve5(options.file);
|
|
5228
|
+
if (existsSync4(path)) throw new Error(`Pipeline file already exists: ${options.file}.`);
|
|
5229
|
+
let recipe;
|
|
5230
|
+
if (engine === "foundation") {
|
|
5231
|
+
if (options.spec) {
|
|
5232
|
+
const input = await loadLocalRunInput(resolve5(options.spec));
|
|
5233
|
+
if (input.kind !== "foundation-spec") {
|
|
5234
|
+
throw new Error("--spec must be a foundation tunedtensor.json.");
|
|
5235
|
+
}
|
|
5236
|
+
recipe = pipelineFromSpec(input);
|
|
5237
|
+
} else {
|
|
5238
|
+
recipe = canonicalFoundationPipeline2();
|
|
5239
|
+
}
|
|
5240
|
+
} else {
|
|
5241
|
+
recipe = canonicalPipeline("local");
|
|
5242
|
+
}
|
|
4753
5243
|
writeFileSync3(path, `${JSON.stringify(recipe, null, 2)}
|
|
4754
5244
|
`);
|
|
4755
5245
|
if (isJsonMode()) return printJson({ created: true, path, pipeline: recipe });
|
|
4756
5246
|
printSuccess(`Created ${options.file}`);
|
|
4757
5247
|
});
|
|
4758
|
-
pipeline.command("validate").description("Validate a pipeline without any execution or transfer").option("-f, --file <path>", "Pipeline file", DEFAULT_PIPELINE_FILE).action((options) => {
|
|
4759
|
-
const errors = validatePipeline(
|
|
5248
|
+
pipeline.command("validate").description("Validate a pipeline without any execution or transfer").option("-f, --file <path>", "Pipeline file", DEFAULT_PIPELINE_FILE).option("--spec <path>", "Local behavior spec used to detect leftover engine mismatches", DEFAULT_SPEC_FILE).action(async (options) => {
|
|
5249
|
+
const errors = validatePipeline(await resolvePipelineDocument(options));
|
|
4760
5250
|
const result2 = { valid: errors.length === 0, errors };
|
|
4761
5251
|
if (isJsonMode()) return printJson(result2);
|
|
4762
5252
|
if (errors.length) throw new Error(`Invalid pipeline:
|
|
4763
5253
|
- ${errors.join("\n- ")}`);
|
|
4764
5254
|
printSuccess("Pipeline is valid.");
|
|
4765
5255
|
});
|
|
4766
|
-
pipeline.command("plan").description("Resolve step targets and required artifact transfers").option("-f, --file <path>", "Pipeline file", DEFAULT_PIPELINE_FILE).option("--only <ids>", "Comma-separated step IDs to include").option("--skip <ids>", "Comma-separated step IDs to omit").action((options) => {
|
|
4767
|
-
outputPlan(createExecutionPlan(
|
|
5256
|
+
pipeline.command("plan").description("Resolve step targets and required artifact transfers").option("-f, --file <path>", "Pipeline file", DEFAULT_PIPELINE_FILE).option("--spec <path>", "Local behavior spec used to detect leftover engine mismatches", DEFAULT_SPEC_FILE).option("--only <ids>", "Comma-separated step IDs to include").option("--skip <ids>", "Comma-separated step IDs to omit").action(async (options) => {
|
|
5257
|
+
outputPlan(createExecutionPlan(await resolvePipelineDocument(options), { only: parseList(options.only), skip: parseList(options.skip) }));
|
|
4768
5258
|
});
|
|
4769
|
-
pipeline.command("run").description("Run an ordered local pipeline, or safely preview any pipeline").option("--dry-run", "Resolve and display only; never execute, transfer, or reserve credits").option("-f, --file <path>", "Pipeline file", DEFAULT_PIPELINE_FILE).option("--spec <path>", "Local behavior spec",
|
|
4770
|
-
const
|
|
5259
|
+
pipeline.command("run").description("Run an ordered local pipeline, or safely preview any pipeline").option("--dry-run", "Resolve and display only; never execute, transfer, or reserve credits").option("-f, --file <path>", "Pipeline file", DEFAULT_PIPELINE_FILE).option("--spec <path>", "Local behavior spec", DEFAULT_SPEC_FILE).option("--config <path>", "Local runtime config").option("--only <ids>", "Comma-separated step IDs to include").option("--skip <ids>", "Comma-separated step IDs to omit").action(async (options) => {
|
|
5260
|
+
const document = await resolvePipelineDocument(options);
|
|
5261
|
+
const plan = createExecutionPlan(document, { only: parseList(options.only), skip: parseList(options.skip) });
|
|
4771
5262
|
if (options.dryRun) {
|
|
4772
5263
|
if (isJsonMode()) return printJson({ dry_run: true, ...plan });
|
|
4773
5264
|
console.log("Dry run only \u2014 no execution, artifact transfer, or credit reservation will occur.");
|
|
@@ -4777,9 +5268,23 @@ function registerPipelineCommands(parent) {
|
|
|
4777
5268
|
if (remote) {
|
|
4778
5269
|
throw new Error(`Step "${remote.id}" targets cloud execution. This CLI is local-only; rewrite that step to local or use --dry-run.`);
|
|
4779
5270
|
}
|
|
4780
|
-
const input = await loadLocalRunInput(
|
|
5271
|
+
const input = await loadLocalRunInput(resolve5(options.spec));
|
|
5272
|
+
if (isParsedFoundationPipeline(document) || input.kind === "foundation-spec") {
|
|
5273
|
+
if (input.kind !== "foundation-spec") {
|
|
5274
|
+
throw new Error("Foundation pipelines require a foundation tunedtensor.json --spec.");
|
|
5275
|
+
}
|
|
5276
|
+
assertFoundationSpecReady(input.spec);
|
|
5277
|
+
const result3 = await runFoundationPipeline({
|
|
5278
|
+
spec: input.spec,
|
|
5279
|
+
plan,
|
|
5280
|
+
specPath: resolve5(options.spec)
|
|
5281
|
+
});
|
|
5282
|
+
if (isJsonMode()) return printJson(result3);
|
|
5283
|
+
printSuccess(`Foundation pipeline completed. Report: ${result3.report_path}`);
|
|
5284
|
+
return;
|
|
5285
|
+
}
|
|
4781
5286
|
assertLocalRunInputReady(input.request);
|
|
4782
|
-
const config = await loadLocalRunnerConfig(options.config ?
|
|
5287
|
+
const config = await loadLocalRunnerConfig(options.config ? resolve5(options.config) : void 0);
|
|
4783
5288
|
const localPipeline = {
|
|
4784
5289
|
version: 1,
|
|
4785
5290
|
...plan.name ? { name: plan.name } : {},
|
|
@@ -4861,10 +5366,10 @@ async function checkForCliUpdate(currentVersion, options = {}) {
|
|
|
4861
5366
|
return null;
|
|
4862
5367
|
}
|
|
4863
5368
|
})();
|
|
4864
|
-
const deadline = new Promise((
|
|
5369
|
+
const deadline = new Promise((resolve6) => {
|
|
4865
5370
|
timeout = setTimeout(() => {
|
|
4866
5371
|
controller.abort();
|
|
4867
|
-
|
|
5372
|
+
resolve6(null);
|
|
4868
5373
|
}, timeoutMs);
|
|
4869
5374
|
});
|
|
4870
5375
|
try {
|
|
@@ -4962,7 +5467,7 @@ async function runSelfCommand(args2, options = {}) {
|
|
|
4962
5467
|
if (!entrypoint) {
|
|
4963
5468
|
throw new Error("Cannot locate the tt CLI entrypoint.");
|
|
4964
5469
|
}
|
|
4965
|
-
return await new Promise((
|
|
5470
|
+
return await new Promise((resolve6, reject) => {
|
|
4966
5471
|
const child = spawn2(process.execPath, [entrypoint, ...args2], {
|
|
4967
5472
|
cwd: options.cwd,
|
|
4968
5473
|
env: options.env ?? process.env,
|
|
@@ -4998,7 +5503,7 @@ async function runSelfCommand(args2, options = {}) {
|
|
|
4998
5503
|
child.once("close", (exitCode, signal) => {
|
|
4999
5504
|
cleanup();
|
|
5000
5505
|
const signalNumber = signal ? osConstants2.signals[signal] : void 0;
|
|
5001
|
-
|
|
5506
|
+
resolve6({
|
|
5002
5507
|
exitCode: exitCode ?? (typeof signalNumber === "number" ? 128 + signalNumber : 1),
|
|
5003
5508
|
signal
|
|
5004
5509
|
});
|
|
@@ -5186,7 +5691,7 @@ async function runCli(version, runtime = {}) {
|
|
|
5186
5691
|
}
|
|
5187
5692
|
|
|
5188
5693
|
// src/index.ts
|
|
5189
|
-
runCli("0.13.1-beta.
|
|
5694
|
+
runCli("0.13.1-beta.2").catch((err) => {
|
|
5190
5695
|
reportError(err);
|
|
5191
5696
|
process.exit(1);
|
|
5192
5697
|
});
|