@tuned-tensor/cli 0.1.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/LICENSE +21 -0
- package/README.md +132 -0
- package/dist/index.js +708 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Weijian Zhang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# tt — Tuned Tensor CLI
|
|
2
|
+
|
|
3
|
+
Command-line interface for [Tuned Tensor](https://www.tunedtensor.com), the end-to-end model behaviour shaping platform for instruction-tuning open-weight LLMs.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @tuned-tensor/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or run from source:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git clone https://github.com/tuned-tensor/tuned-tensor-cli.git && cd tuned-tensor-cli
|
|
15
|
+
npm install
|
|
16
|
+
npm run build
|
|
17
|
+
npm link
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Authentication
|
|
21
|
+
|
|
22
|
+
Generate an API key from the Tuned Tensor dashboard (**Settings → API Keys**), then:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
tt auth login
|
|
26
|
+
# paste your tt_... key when prompted
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Alternatively, set the `TUNED_TENSOR_API_KEY` environment variable or pass `--api-key` on every call.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
tt auth status # check current auth
|
|
33
|
+
tt auth logout # remove stored credentials
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
### Behavior Specs
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
tt specs list # list all specs
|
|
42
|
+
tt specs get <id> # show spec details + examples
|
|
43
|
+
tt specs create -f spec.json # create from JSON file
|
|
44
|
+
tt specs create --name "My Spec" # create with flags
|
|
45
|
+
tt specs update <id> -f updates.json # update from JSON file
|
|
46
|
+
tt specs delete <id> # delete a spec
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Spec JSON format:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"name": "Customer Support Bot",
|
|
54
|
+
"description": "Friendly, concise support responses",
|
|
55
|
+
"system_prompt": "You are a helpful customer support agent.",
|
|
56
|
+
"guidelines": ["Be concise", "Use a friendly tone"],
|
|
57
|
+
"examples": [
|
|
58
|
+
{ "input": "How do I reset my password?", "output": "Go to Settings → Security → Reset Password." }
|
|
59
|
+
],
|
|
60
|
+
"constraints": ["Never share internal URLs"],
|
|
61
|
+
"base_model": "meta-llama/Llama-3.2-3B-Instruct"
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Runs
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
tt runs list # list all runs
|
|
69
|
+
tt runs list --spec <spec-id> # list runs for a spec
|
|
70
|
+
tt runs get <id> # show run details + eval results
|
|
71
|
+
tt runs start <spec-id> # start a new run
|
|
72
|
+
tt runs start <spec-id> --epochs 5 --lr 0.0001
|
|
73
|
+
tt runs cancel <id> # cancel a running run
|
|
74
|
+
tt runs watch <id> # poll until run completes
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Datasets
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
tt datasets list # list datasets
|
|
81
|
+
tt datasets get <id> # show dataset details
|
|
82
|
+
tt datasets upload data.jsonl # upload a JSONL file
|
|
83
|
+
tt datasets upload data.jsonl --name "Training Set v2"
|
|
84
|
+
tt datasets delete <id> # delete a dataset
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Models
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
tt models list # list fine-tuned models
|
|
91
|
+
tt models get <id> # show model details
|
|
92
|
+
tt models delete <id> # delete a model
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Usage
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
tt usage # show usage summary & tier info
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Global Options
|
|
102
|
+
|
|
103
|
+
| Flag | Description |
|
|
104
|
+
|------|-------------|
|
|
105
|
+
| `-k, --api-key <key>` | Override stored API key |
|
|
106
|
+
| `-u, --base-url <url>` | Override API base URL |
|
|
107
|
+
| `--json` | Output raw JSON instead of formatted tables |
|
|
108
|
+
| `--no-color` | Disable ANSI colors |
|
|
109
|
+
| `-V, --version` | Show version |
|
|
110
|
+
| `-h, --help` | Show help |
|
|
111
|
+
|
|
112
|
+
## Configuration
|
|
113
|
+
|
|
114
|
+
Credentials are stored in `~/.config/tuned-tensor/config.json` (respects `XDG_CONFIG_HOME`).
|
|
115
|
+
|
|
116
|
+
API key resolution order:
|
|
117
|
+
1. `--api-key` flag
|
|
118
|
+
2. `TUNED_TENSOR_API_KEY` environment variable
|
|
119
|
+
3. Stored config file
|
|
120
|
+
|
|
121
|
+
## Development
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npm install # install dependencies
|
|
125
|
+
npm run build # build with tsup
|
|
126
|
+
npm run dev # watch mode
|
|
127
|
+
npm run typecheck # type-check without emitting
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/output.ts
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import Table from "cli-table3";
|
|
9
|
+
var jsonMode = false;
|
|
10
|
+
function setJsonMode(enabled) {
|
|
11
|
+
jsonMode = enabled;
|
|
12
|
+
}
|
|
13
|
+
function isJsonMode() {
|
|
14
|
+
return jsonMode;
|
|
15
|
+
}
|
|
16
|
+
function printJson(data) {
|
|
17
|
+
console.log(JSON.stringify(data, null, 2));
|
|
18
|
+
}
|
|
19
|
+
function printTable(headers, rows, meta) {
|
|
20
|
+
const table = new Table({
|
|
21
|
+
head: headers.map((h) => chalk.bold.cyan(h)),
|
|
22
|
+
style: { head: [], border: [] }
|
|
23
|
+
});
|
|
24
|
+
for (const row of rows) {
|
|
25
|
+
table.push(row);
|
|
26
|
+
}
|
|
27
|
+
console.log(table.toString());
|
|
28
|
+
if (meta) {
|
|
29
|
+
const totalPages = Math.ceil(meta.total / meta.per_page);
|
|
30
|
+
console.log(
|
|
31
|
+
chalk.dim(
|
|
32
|
+
`
|
|
33
|
+
Page ${meta.page}/${totalPages} (${meta.total} total)`
|
|
34
|
+
)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function printDetail(fields) {
|
|
39
|
+
const maxLabel = Math.max(...fields.map(([l]) => l.length));
|
|
40
|
+
for (const [label, value] of fields) {
|
|
41
|
+
const paddedLabel = label.padEnd(maxLabel);
|
|
42
|
+
console.log(`${chalk.bold.cyan(paddedLabel)} ${value ?? chalk.dim("\u2014")}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function printSuccess(message) {
|
|
46
|
+
console.log(chalk.green("\u2713") + " " + message);
|
|
47
|
+
}
|
|
48
|
+
function printWarning(message) {
|
|
49
|
+
console.log(chalk.yellow("!") + " " + message);
|
|
50
|
+
}
|
|
51
|
+
function printError(message) {
|
|
52
|
+
console.error(chalk.red("\u2717") + " " + message);
|
|
53
|
+
}
|
|
54
|
+
function formatDate(iso) {
|
|
55
|
+
if (!iso) return "\u2014";
|
|
56
|
+
return new Date(iso).toLocaleString();
|
|
57
|
+
}
|
|
58
|
+
function formatStatus(status) {
|
|
59
|
+
const map = {
|
|
60
|
+
completed: chalk.green,
|
|
61
|
+
running: chalk.blue,
|
|
62
|
+
training: chalk.blue,
|
|
63
|
+
evaluating: chalk.blue,
|
|
64
|
+
preparing: chalk.yellow,
|
|
65
|
+
pending: chalk.yellow,
|
|
66
|
+
uploading: chalk.yellow,
|
|
67
|
+
failed: chalk.red,
|
|
68
|
+
cancelled: chalk.dim,
|
|
69
|
+
validated: chalk.green,
|
|
70
|
+
invalid: chalk.red
|
|
71
|
+
};
|
|
72
|
+
const colorFn = map[status] || chalk.white;
|
|
73
|
+
return colorFn(status);
|
|
74
|
+
}
|
|
75
|
+
function truncate(str, max) {
|
|
76
|
+
if (str.length <= max) return str;
|
|
77
|
+
return str.slice(0, max - 1) + "\u2026";
|
|
78
|
+
}
|
|
79
|
+
function shortId(id) {
|
|
80
|
+
return id.slice(0, 8);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/commands/auth.ts
|
|
84
|
+
import { createInterface } from "readline/promises";
|
|
85
|
+
import { stdin, stdout } from "process";
|
|
86
|
+
|
|
87
|
+
// src/config.ts
|
|
88
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
89
|
+
import { homedir } from "os";
|
|
90
|
+
import { join } from "path";
|
|
91
|
+
var CONFIG_DIR_NAME = "tuned-tensor";
|
|
92
|
+
var CONFIG_FILE_NAME = "config.json";
|
|
93
|
+
function getConfigDir() {
|
|
94
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
95
|
+
const base = xdg || join(homedir(), ".config");
|
|
96
|
+
return join(base, CONFIG_DIR_NAME);
|
|
97
|
+
}
|
|
98
|
+
function getConfigPath() {
|
|
99
|
+
return join(getConfigDir(), CONFIG_FILE_NAME);
|
|
100
|
+
}
|
|
101
|
+
function readConfig() {
|
|
102
|
+
const path = getConfigPath();
|
|
103
|
+
if (!existsSync(path)) return {};
|
|
104
|
+
try {
|
|
105
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
106
|
+
} catch {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function writeConfig(config) {
|
|
111
|
+
const dir = getConfigDir();
|
|
112
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
113
|
+
writeFileSync(getConfigPath(), JSON.stringify(config, null, 2) + "\n");
|
|
114
|
+
}
|
|
115
|
+
function updateConfig(partial) {
|
|
116
|
+
const current = readConfig();
|
|
117
|
+
writeConfig({ ...current, ...partial });
|
|
118
|
+
}
|
|
119
|
+
function clearConfig() {
|
|
120
|
+
const path = getConfigPath();
|
|
121
|
+
if (existsSync(path)) writeFileSync(path, "{}\n");
|
|
122
|
+
}
|
|
123
|
+
var DEFAULT_BASE_URL = "https://www.tunedtensor.com";
|
|
124
|
+
function getBaseUrl(opts) {
|
|
125
|
+
return opts?.baseUrl || process.env.TUNED_TENSOR_URL || readConfig().base_url || DEFAULT_BASE_URL;
|
|
126
|
+
}
|
|
127
|
+
function getApiKey(opts) {
|
|
128
|
+
return opts?.apiKey || process.env.TUNED_TENSOR_API_KEY || readConfig().api_key;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/commands/auth.ts
|
|
132
|
+
import chalk2 from "chalk";
|
|
133
|
+
function registerAuthCommands(parent) {
|
|
134
|
+
const auth = parent.command("auth").description("Manage authentication");
|
|
135
|
+
auth.command("login").description("Store an API key for authentication").argument("[key]", "API key (tt_...). If omitted, you will be prompted.").action(async (key) => {
|
|
136
|
+
let apiKey = key;
|
|
137
|
+
if (!apiKey) {
|
|
138
|
+
const settingsUrl = `${DEFAULT_BASE_URL}/dashboard/settings`;
|
|
139
|
+
console.log();
|
|
140
|
+
console.log(
|
|
141
|
+
` To get your API key, go to ${chalk2.bold("Settings \u2192 API Keys")} in the Tuned Tensor dashboard:`
|
|
142
|
+
);
|
|
143
|
+
console.log(` ${chalk2.cyan.underline(settingsUrl)}`);
|
|
144
|
+
console.log();
|
|
145
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
146
|
+
apiKey = await rl.question("Enter your API key (tt_...): ");
|
|
147
|
+
rl.close();
|
|
148
|
+
}
|
|
149
|
+
apiKey = apiKey.trim();
|
|
150
|
+
if (!apiKey.startsWith("tt_") || apiKey.length !== 51) {
|
|
151
|
+
printError(
|
|
152
|
+
"Invalid API key format. Keys start with tt_ and are 51 characters long."
|
|
153
|
+
);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
updateConfig({ api_key: apiKey });
|
|
157
|
+
printSuccess(
|
|
158
|
+
`API key stored (${apiKey.slice(0, 8)}...). You're ready to go.`
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
auth.command("logout").description("Remove stored credentials").action(() => {
|
|
162
|
+
clearConfig();
|
|
163
|
+
printSuccess("Credentials removed.");
|
|
164
|
+
});
|
|
165
|
+
auth.command("status").description("Show current authentication status").action(() => {
|
|
166
|
+
const opts = parent.opts();
|
|
167
|
+
const apiKey = getApiKey(opts);
|
|
168
|
+
const baseUrl = getBaseUrl(opts);
|
|
169
|
+
if (isJsonMode()) {
|
|
170
|
+
printJson({
|
|
171
|
+
authenticated: !!apiKey,
|
|
172
|
+
key_prefix: apiKey ? apiKey.slice(0, 8) + "..." : null,
|
|
173
|
+
base_url: baseUrl
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (apiKey) {
|
|
178
|
+
printDetail([
|
|
179
|
+
["Authenticated", "Yes"],
|
|
180
|
+
["API Key", apiKey.slice(0, 8) + "..."],
|
|
181
|
+
["Base URL", baseUrl]
|
|
182
|
+
]);
|
|
183
|
+
} else {
|
|
184
|
+
printWarning(
|
|
185
|
+
"Not authenticated. Run `tt auth login` to store an API key."
|
|
186
|
+
);
|
|
187
|
+
printDetail([["Base URL", baseUrl]]);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/commands/specs.ts
|
|
193
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
194
|
+
|
|
195
|
+
// src/client.ts
|
|
196
|
+
import { readFileSync as readFileSync2, statSync } from "fs";
|
|
197
|
+
import { basename } from "path";
|
|
198
|
+
var ApiError = class extends Error {
|
|
199
|
+
constructor(status, code, message) {
|
|
200
|
+
super(message);
|
|
201
|
+
this.status = status;
|
|
202
|
+
this.code = code;
|
|
203
|
+
this.name = "ApiError";
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
function resolveKey(opts) {
|
|
207
|
+
const key = getApiKey(opts);
|
|
208
|
+
if (!key) {
|
|
209
|
+
throw new ApiError(
|
|
210
|
+
401,
|
|
211
|
+
"NO_API_KEY",
|
|
212
|
+
"No API key found. Run `tt auth login` or set TUNED_TENSOR_API_KEY."
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
return key;
|
|
216
|
+
}
|
|
217
|
+
function buildUrl(path, query, opts) {
|
|
218
|
+
const base = getBaseUrl(opts);
|
|
219
|
+
const url = new URL(`/api/v1${path}`, base);
|
|
220
|
+
if (query) {
|
|
221
|
+
for (const [k, v] of Object.entries(query)) {
|
|
222
|
+
if (v !== void 0) url.searchParams.set(k, String(v));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return url.toString();
|
|
226
|
+
}
|
|
227
|
+
async function request(method, path, options = {}) {
|
|
228
|
+
const key = resolveKey(options.opts);
|
|
229
|
+
const url = buildUrl(path, options.query, options.opts);
|
|
230
|
+
const headers = {
|
|
231
|
+
Authorization: `Bearer ${key}`
|
|
232
|
+
};
|
|
233
|
+
let bodyPayload;
|
|
234
|
+
if (options.body) {
|
|
235
|
+
headers["Content-Type"] = "application/json";
|
|
236
|
+
bodyPayload = JSON.stringify(options.body);
|
|
237
|
+
}
|
|
238
|
+
const res = await fetch(url, { method, headers, body: bodyPayload });
|
|
239
|
+
if (!res.ok) {
|
|
240
|
+
let errBody;
|
|
241
|
+
try {
|
|
242
|
+
errBody = await res.json();
|
|
243
|
+
} catch {
|
|
244
|
+
throw new ApiError(res.status, "UNKNOWN", res.statusText);
|
|
245
|
+
}
|
|
246
|
+
throw new ApiError(
|
|
247
|
+
res.status,
|
|
248
|
+
errBody.error?.code || "UNKNOWN",
|
|
249
|
+
errBody.error?.message || res.statusText
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
if (res.status === 204) return { data: null };
|
|
253
|
+
return await res.json();
|
|
254
|
+
}
|
|
255
|
+
function get(path, query, opts) {
|
|
256
|
+
return request("GET", path, { query, opts });
|
|
257
|
+
}
|
|
258
|
+
function post(path, body, opts) {
|
|
259
|
+
return request("POST", path, { body, opts });
|
|
260
|
+
}
|
|
261
|
+
function put(path, body, opts) {
|
|
262
|
+
return request("PUT", path, { body, opts });
|
|
263
|
+
}
|
|
264
|
+
function del(path, opts) {
|
|
265
|
+
return request("DELETE", path, { opts });
|
|
266
|
+
}
|
|
267
|
+
async function upload(path, filePath, fields, opts) {
|
|
268
|
+
const key = resolveKey(opts);
|
|
269
|
+
const url = buildUrl(path, void 0, opts);
|
|
270
|
+
const form = new FormData();
|
|
271
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
272
|
+
form.append(k, v);
|
|
273
|
+
}
|
|
274
|
+
const fileBytes = readFileSync2(filePath);
|
|
275
|
+
const fileSize = statSync(filePath).size;
|
|
276
|
+
const blob = new Blob([fileBytes], { type: "application/jsonl" });
|
|
277
|
+
form.append("file", blob, basename(filePath));
|
|
278
|
+
const res = await fetch(url, {
|
|
279
|
+
method: "POST",
|
|
280
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
281
|
+
body: form
|
|
282
|
+
});
|
|
283
|
+
if (!res.ok) {
|
|
284
|
+
let errBody;
|
|
285
|
+
try {
|
|
286
|
+
errBody = await res.json();
|
|
287
|
+
} catch {
|
|
288
|
+
throw new ApiError(res.status, "UNKNOWN", res.statusText);
|
|
289
|
+
}
|
|
290
|
+
throw new ApiError(
|
|
291
|
+
res.status,
|
|
292
|
+
errBody.error?.code || "UNKNOWN",
|
|
293
|
+
errBody.error?.message || res.statusText
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
return await res.json();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/commands/specs.ts
|
|
300
|
+
function registerSpecsCommands(parent) {
|
|
301
|
+
const specs = parent.command("specs").description("Manage behavior specs");
|
|
302
|
+
specs.command("list").description("List behavior specs").option("-p, --page <n>", "Page number", "1").option("--per-page <n>", "Results per page", "20").action(async (cmdOpts) => {
|
|
303
|
+
const opts = parent.opts();
|
|
304
|
+
const { data, meta } = await get(
|
|
305
|
+
"/behavior-specs",
|
|
306
|
+
{ page: cmdOpts.page, per_page: cmdOpts.perPage },
|
|
307
|
+
opts
|
|
308
|
+
);
|
|
309
|
+
if (isJsonMode()) return printJson({ data, meta });
|
|
310
|
+
printTable(
|
|
311
|
+
["ID", "Name", "Model", "Examples", "Runs", "Status", "Updated"],
|
|
312
|
+
data.map((s) => [
|
|
313
|
+
shortId(s.id),
|
|
314
|
+
truncate(s.name, 30),
|
|
315
|
+
s.base_model.split("/").pop() || s.base_model,
|
|
316
|
+
String(s.examples?.length ?? 0),
|
|
317
|
+
String(s._run_count ?? 0),
|
|
318
|
+
s._latest_run_status ? formatStatus(s._latest_run_status) : "\u2014",
|
|
319
|
+
formatDate(s.updated_at)
|
|
320
|
+
]),
|
|
321
|
+
meta
|
|
322
|
+
);
|
|
323
|
+
});
|
|
324
|
+
specs.command("get").description("Show spec details").argument("<id>", "Spec ID (full or prefix)").action(async (id) => {
|
|
325
|
+
const opts = parent.opts();
|
|
326
|
+
const { data } = await get(`/behavior-specs/${id}`, void 0, opts);
|
|
327
|
+
if (isJsonMode()) return printJson(data);
|
|
328
|
+
printDetail([
|
|
329
|
+
["ID", data.id],
|
|
330
|
+
["Name", data.name],
|
|
331
|
+
["Description", data.description || void 0],
|
|
332
|
+
["Base Model", data.base_model],
|
|
333
|
+
["System Prompt", data.system_prompt ? truncate(data.system_prompt, 80) : void 0],
|
|
334
|
+
["Guidelines", data.guidelines?.length ? data.guidelines.join(", ") : void 0],
|
|
335
|
+
["Constraints", data.constraints?.length ? data.constraints.join(", ") : void 0],
|
|
336
|
+
["Examples", String(data.examples?.length ?? 0)],
|
|
337
|
+
["Runs", String(data._run_count ?? data._runs?.length ?? 0)],
|
|
338
|
+
["Created", formatDate(data.created_at)],
|
|
339
|
+
["Updated", formatDate(data.updated_at)]
|
|
340
|
+
]);
|
|
341
|
+
if (data.examples?.length) {
|
|
342
|
+
console.log("\nExamples:");
|
|
343
|
+
for (const [i, ex] of data.examples.slice(0, 5).entries()) {
|
|
344
|
+
console.log(` ${i + 1}. Input: ${truncate(ex.input, 60)}`);
|
|
345
|
+
console.log(` Output: ${truncate(ex.output, 60)}`);
|
|
346
|
+
}
|
|
347
|
+
if (data.examples.length > 5) {
|
|
348
|
+
console.log(` ... and ${data.examples.length - 5} more`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
specs.command("create").description("Create a behavior spec").option("-f, --file <path>", "JSON file with spec definition").option("-n, --name <name>", "Spec name").option("--model <model>", "Base model ID").action(async (cmdOpts) => {
|
|
353
|
+
const opts = parent.opts();
|
|
354
|
+
let body;
|
|
355
|
+
if (cmdOpts.file) {
|
|
356
|
+
body = JSON.parse(readFileSync3(cmdOpts.file, "utf-8"));
|
|
357
|
+
} else if (cmdOpts.name) {
|
|
358
|
+
body = { name: cmdOpts.name };
|
|
359
|
+
if (cmdOpts.model) body.base_model = cmdOpts.model;
|
|
360
|
+
} else {
|
|
361
|
+
console.error("Provide --file or --name");
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
const { data } = await post("/behavior-specs", body, opts);
|
|
365
|
+
if (isJsonMode()) return printJson(data);
|
|
366
|
+
printSuccess(`Spec created: ${data.name} (${shortId(data.id)})`);
|
|
367
|
+
});
|
|
368
|
+
specs.command("update").description("Update a behavior spec").argument("<id>", "Spec ID").option("-f, --file <path>", "JSON file with fields to update").option("-n, --name <name>", "New name").option("--model <model>", "New base model ID").action(async (id, cmdOpts) => {
|
|
369
|
+
const opts = parent.opts();
|
|
370
|
+
let body;
|
|
371
|
+
if (cmdOpts.file) {
|
|
372
|
+
body = JSON.parse(readFileSync3(cmdOpts.file, "utf-8"));
|
|
373
|
+
} else {
|
|
374
|
+
body = {};
|
|
375
|
+
if (cmdOpts.name) body.name = cmdOpts.name;
|
|
376
|
+
if (cmdOpts.model) body.base_model = cmdOpts.model;
|
|
377
|
+
}
|
|
378
|
+
const { data } = await put(
|
|
379
|
+
`/behavior-specs/${id}`,
|
|
380
|
+
body,
|
|
381
|
+
opts
|
|
382
|
+
);
|
|
383
|
+
if (isJsonMode()) return printJson(data);
|
|
384
|
+
printSuccess(`Spec updated: ${data.name} (${shortId(data.id)})`);
|
|
385
|
+
});
|
|
386
|
+
specs.command("delete").description("Delete a behavior spec").argument("<id>", "Spec ID").action(async (id) => {
|
|
387
|
+
const opts = parent.opts();
|
|
388
|
+
await del(`/behavior-specs/${id}`, opts);
|
|
389
|
+
if (isJsonMode()) return printJson({ id, deleted: true });
|
|
390
|
+
printSuccess(`Spec deleted: ${shortId(id)}`);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/commands/runs.ts
|
|
395
|
+
import ora from "ora";
|
|
396
|
+
function registerRunsCommands(parent) {
|
|
397
|
+
const runs = parent.command("runs").description("Manage runs");
|
|
398
|
+
runs.command("list").description("List runs").option("-s, --spec <id>", "Filter by spec ID").option("-p, --page <n>", "Page number", "1").option("--per-page <n>", "Results per page", "20").action(async (cmdOpts) => {
|
|
399
|
+
const opts = parent.opts();
|
|
400
|
+
let path = "/runs";
|
|
401
|
+
const query = {
|
|
402
|
+
page: cmdOpts.page,
|
|
403
|
+
per_page: cmdOpts.perPage
|
|
404
|
+
};
|
|
405
|
+
if (cmdOpts.spec) {
|
|
406
|
+
path = `/behavior-specs/${cmdOpts.spec}/runs`;
|
|
407
|
+
}
|
|
408
|
+
const { data, meta } = await get(path, query, opts);
|
|
409
|
+
if (isJsonMode()) return printJson({ data, meta });
|
|
410
|
+
printTable(
|
|
411
|
+
["ID", "Spec", "#", "Status", "Score", "Started", "Completed"],
|
|
412
|
+
data.map((r) => [
|
|
413
|
+
shortId(r.id),
|
|
414
|
+
r._spec_name ? truncate(r._spec_name, 20) : shortId(r.behavior_spec_id),
|
|
415
|
+
String(r.run_number),
|
|
416
|
+
formatStatus(r.status),
|
|
417
|
+
r.eval_summary?.mean_score != null ? (r.eval_summary.mean_score * 100).toFixed(1) + "%" : "\u2014",
|
|
418
|
+
formatDate(r.started_at),
|
|
419
|
+
formatDate(r.completed_at)
|
|
420
|
+
]),
|
|
421
|
+
meta
|
|
422
|
+
);
|
|
423
|
+
});
|
|
424
|
+
runs.command("get").description("Show run details and eval results").argument("<id>", "Run ID").action(async (id) => {
|
|
425
|
+
const opts = parent.opts();
|
|
426
|
+
const { data } = await get(`/runs/${id}`, void 0, opts);
|
|
427
|
+
if (isJsonMode()) return printJson(data);
|
|
428
|
+
printDetail([
|
|
429
|
+
["ID", data.id],
|
|
430
|
+
["Spec", data._spec_name ?? shortId(data.behavior_spec_id)],
|
|
431
|
+
["Run #", String(data.run_number)],
|
|
432
|
+
["Status", formatStatus(data.status)],
|
|
433
|
+
["Score", data.eval_summary?.mean_score != null ? (data.eval_summary.mean_score * 100).toFixed(1) + "%" : void 0],
|
|
434
|
+
["Pass Rate", data.eval_summary?.pass_rate != null ? (data.eval_summary.pass_rate * 100).toFixed(1) + "%" : void 0],
|
|
435
|
+
["Error", data.error ?? void 0],
|
|
436
|
+
["Started", formatDate(data.started_at)],
|
|
437
|
+
["Completed", formatDate(data.completed_at)],
|
|
438
|
+
["Created", formatDate(data.created_at)]
|
|
439
|
+
]);
|
|
440
|
+
if (data.hyperparameters && Object.keys(data.hyperparameters).length) {
|
|
441
|
+
console.log("\nHyperparameters:");
|
|
442
|
+
for (const [k, v] of Object.entries(data.hyperparameters)) {
|
|
443
|
+
console.log(` ${k}: ${v}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (data._evals?.length) {
|
|
447
|
+
console.log(`
|
|
448
|
+
Eval Results (${data._evals.length}):`);
|
|
449
|
+
printTable(
|
|
450
|
+
["#", "Passed", "Score", "Prompt", "Latency"],
|
|
451
|
+
data._evals.map((e, i) => [
|
|
452
|
+
String(i + 1),
|
|
453
|
+
e.passed ? "\u2713" : "\u2717",
|
|
454
|
+
e.score != null ? e.score.toFixed(2) : "\u2014",
|
|
455
|
+
truncate(e.prompt, 40),
|
|
456
|
+
e.latency_ms != null ? `${e.latency_ms}ms` : "\u2014"
|
|
457
|
+
])
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
runs.command("start").description("Start a new run for a behavior spec").argument("<spec-id>", "Behavior spec ID").option("--no-augment", "Disable data augmentation").option("--epochs <n>", "Number of training epochs").option("--lr <rate>", "Learning rate").option("--batch-size <n>", "Batch size").option("--lora-rank <n>", "LoRA rank").option("--lora-alpha <n>", "LoRA alpha").action(async (specId, cmdOpts) => {
|
|
462
|
+
const opts = parent.opts();
|
|
463
|
+
const body = {};
|
|
464
|
+
if (cmdOpts.augment === false) body.augment = false;
|
|
465
|
+
const hp = {};
|
|
466
|
+
if (cmdOpts.epochs) hp.n_epochs = Number(cmdOpts.epochs);
|
|
467
|
+
if (cmdOpts.lr) hp.learning_rate = Number(cmdOpts.lr);
|
|
468
|
+
if (cmdOpts.batchSize) hp.batch_size = Number(cmdOpts.batchSize);
|
|
469
|
+
if (cmdOpts.loraRank) hp.lora_rank = Number(cmdOpts.loraRank);
|
|
470
|
+
if (cmdOpts.loraAlpha) hp.lora_alpha = Number(cmdOpts.loraAlpha);
|
|
471
|
+
if (Object.keys(hp).length) body.hyperparameters = hp;
|
|
472
|
+
const { data } = await post(
|
|
473
|
+
`/behavior-specs/${specId}/runs`,
|
|
474
|
+
body,
|
|
475
|
+
opts
|
|
476
|
+
);
|
|
477
|
+
if (isJsonMode()) return printJson(data);
|
|
478
|
+
printSuccess(
|
|
479
|
+
`Run #${data.run_number} started (${shortId(data.id)}). Status: ${formatStatus(data.status)}`
|
|
480
|
+
);
|
|
481
|
+
});
|
|
482
|
+
runs.command("cancel").description("Cancel a running run").argument("<id>", "Run ID").action(async (id) => {
|
|
483
|
+
const opts = parent.opts();
|
|
484
|
+
const { data } = await post(`/runs/${id}/cancel`, void 0, opts);
|
|
485
|
+
if (isJsonMode()) return printJson(data);
|
|
486
|
+
printSuccess(`Run ${shortId(id)} cancelled.`);
|
|
487
|
+
});
|
|
488
|
+
runs.command("watch").description("Watch a run until it completes").argument("<id>", "Run ID").option("--interval <ms>", "Poll interval in milliseconds", "5000").action(async (id, cmdOpts) => {
|
|
489
|
+
const opts = parent.opts();
|
|
490
|
+
const interval = Number(cmdOpts.interval);
|
|
491
|
+
const spinner = ora(`Watching run ${shortId(id)}...`).start();
|
|
492
|
+
const terminalStates = /* @__PURE__ */ new Set([
|
|
493
|
+
"completed",
|
|
494
|
+
"failed",
|
|
495
|
+
"cancelled"
|
|
496
|
+
]);
|
|
497
|
+
let run;
|
|
498
|
+
while (true) {
|
|
499
|
+
const { data } = await get(`/runs/${id}`, void 0, opts);
|
|
500
|
+
run = data;
|
|
501
|
+
spinner.text = `Run ${shortId(id)} \u2014 ${formatStatus(run.status)}`;
|
|
502
|
+
if (terminalStates.has(run.status)) break;
|
|
503
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
504
|
+
}
|
|
505
|
+
spinner.stop();
|
|
506
|
+
if (isJsonMode()) return printJson(run);
|
|
507
|
+
printDetail([
|
|
508
|
+
["Run", shortId(run.id)],
|
|
509
|
+
["Status", formatStatus(run.status)],
|
|
510
|
+
["Score", run.eval_summary?.mean_score != null ? (run.eval_summary.mean_score * 100).toFixed(1) + "%" : void 0],
|
|
511
|
+
["Error", run.error ?? void 0],
|
|
512
|
+
["Completed", formatDate(run.completed_at)]
|
|
513
|
+
]);
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// src/commands/datasets.ts
|
|
518
|
+
import { existsSync as existsSync2 } from "fs";
|
|
519
|
+
function formatBytes(bytes) {
|
|
520
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
521
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
522
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
523
|
+
}
|
|
524
|
+
function registerDatasetsCommands(parent) {
|
|
525
|
+
const datasets = parent.command("datasets").description("Manage datasets");
|
|
526
|
+
datasets.command("list").description("List datasets").option("-p, --page <n>", "Page number", "1").option("--per-page <n>", "Results per page", "20").action(async (cmdOpts) => {
|
|
527
|
+
const opts = parent.opts();
|
|
528
|
+
const { data, meta } = await get(
|
|
529
|
+
"/datasets",
|
|
530
|
+
{ page: cmdOpts.page, per_page: cmdOpts.perPage },
|
|
531
|
+
opts
|
|
532
|
+
);
|
|
533
|
+
if (isJsonMode()) return printJson({ data, meta });
|
|
534
|
+
printTable(
|
|
535
|
+
["ID", "Name", "Rows", "Size", "Status", "Created"],
|
|
536
|
+
data.map((d) => [
|
|
537
|
+
shortId(d.id),
|
|
538
|
+
truncate(d.name, 30),
|
|
539
|
+
String(d.row_count),
|
|
540
|
+
formatBytes(d.file_size_bytes),
|
|
541
|
+
formatStatus(d.status),
|
|
542
|
+
formatDate(d.created_at)
|
|
543
|
+
]),
|
|
544
|
+
meta
|
|
545
|
+
);
|
|
546
|
+
});
|
|
547
|
+
datasets.command("get").description("Show dataset details").argument("<id>", "Dataset ID").action(async (id) => {
|
|
548
|
+
const opts = parent.opts();
|
|
549
|
+
const { data } = await get(`/datasets/${id}`, void 0, opts);
|
|
550
|
+
if (isJsonMode()) return printJson(data);
|
|
551
|
+
printDetail([
|
|
552
|
+
["ID", data.id],
|
|
553
|
+
["Name", data.name],
|
|
554
|
+
["Description", data.description ?? void 0],
|
|
555
|
+
["Format", data.format],
|
|
556
|
+
["Rows", String(data.row_count)],
|
|
557
|
+
["Size", formatBytes(data.file_size_bytes)],
|
|
558
|
+
["Status", formatStatus(data.status)],
|
|
559
|
+
["Created", formatDate(data.created_at)],
|
|
560
|
+
["Updated", formatDate(data.updated_at)]
|
|
561
|
+
]);
|
|
562
|
+
if (data.validation_errors?.length) {
|
|
563
|
+
console.log("\nValidation Errors:");
|
|
564
|
+
for (const err of data.validation_errors) {
|
|
565
|
+
console.log(` - ${err}`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
datasets.command("upload").description("Upload a JSONL dataset file").argument("<file>", "Path to JSONL file").option("-n, --name <name>", "Dataset name (defaults to filename)").option("-d, --description <desc>", "Dataset description").action(async (file, cmdOpts) => {
|
|
570
|
+
const opts = parent.opts();
|
|
571
|
+
if (!existsSync2(file)) {
|
|
572
|
+
printError(`File not found: ${file}`);
|
|
573
|
+
process.exit(1);
|
|
574
|
+
}
|
|
575
|
+
const fields = {
|
|
576
|
+
name: cmdOpts.name || file.split("/").pop().replace(/\.jsonl?$/, "")
|
|
577
|
+
};
|
|
578
|
+
if (cmdOpts.description) fields.description = cmdOpts.description;
|
|
579
|
+
const { data } = await upload(
|
|
580
|
+
"/datasets",
|
|
581
|
+
file,
|
|
582
|
+
fields,
|
|
583
|
+
opts
|
|
584
|
+
);
|
|
585
|
+
if (isJsonMode()) return printJson(data);
|
|
586
|
+
printSuccess(
|
|
587
|
+
`Dataset uploaded: ${data.name} (${shortId(data.id)}) \u2014 ${data.row_count} rows`
|
|
588
|
+
);
|
|
589
|
+
});
|
|
590
|
+
datasets.command("delete").description("Delete a dataset").argument("<id>", "Dataset ID").action(async (id) => {
|
|
591
|
+
const opts = parent.opts();
|
|
592
|
+
await del(`/datasets/${id}`, opts);
|
|
593
|
+
if (isJsonMode()) return printJson({ id, deleted: true });
|
|
594
|
+
printSuccess(`Dataset deleted: ${shortId(id)}`);
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// src/commands/models.ts
|
|
599
|
+
function registerModelsCommands(parent) {
|
|
600
|
+
const models = parent.command("models").description("Manage fine-tuned models");
|
|
601
|
+
models.command("list").description("List fine-tuned models").option("-p, --page <n>", "Page number", "1").option("--per-page <n>", "Results per page", "20").action(async (cmdOpts) => {
|
|
602
|
+
const opts = parent.opts();
|
|
603
|
+
const { data, meta } = await get(
|
|
604
|
+
"/models",
|
|
605
|
+
{ page: cmdOpts.page, per_page: cmdOpts.perPage },
|
|
606
|
+
opts
|
|
607
|
+
);
|
|
608
|
+
if (isJsonMode()) return printJson({ data, meta });
|
|
609
|
+
printTable(
|
|
610
|
+
["ID", "Name", "Base Model", "Provider", "Created"],
|
|
611
|
+
data.map((m) => [
|
|
612
|
+
shortId(m.id),
|
|
613
|
+
truncate(m.name, 30),
|
|
614
|
+
m.base_model.split("/").pop() || m.base_model,
|
|
615
|
+
m.provider,
|
|
616
|
+
formatDate(m.created_at)
|
|
617
|
+
]),
|
|
618
|
+
meta
|
|
619
|
+
);
|
|
620
|
+
});
|
|
621
|
+
models.command("get").description("Show model details").argument("<id>", "Model ID").action(async (id) => {
|
|
622
|
+
const opts = parent.opts();
|
|
623
|
+
const { data } = await get(`/models/${id}`, void 0, opts);
|
|
624
|
+
if (isJsonMode()) return printJson(data);
|
|
625
|
+
printDetail([
|
|
626
|
+
["ID", data.id],
|
|
627
|
+
["Name", data.name],
|
|
628
|
+
["Base Model", data.base_model],
|
|
629
|
+
["Provider", data.provider],
|
|
630
|
+
["Provider Model", data.provider_model_id],
|
|
631
|
+
["Description", data.description ?? void 0],
|
|
632
|
+
["Created", formatDate(data.created_at)]
|
|
633
|
+
]);
|
|
634
|
+
});
|
|
635
|
+
models.command("delete").description("Delete a model").argument("<id>", "Model ID").action(async (id) => {
|
|
636
|
+
const opts = parent.opts();
|
|
637
|
+
await del(`/models/${id}`, opts);
|
|
638
|
+
if (isJsonMode()) return printJson({ id, deleted: true });
|
|
639
|
+
printSuccess(`Model deleted: ${shortId(id)}`);
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// src/commands/usage.ts
|
|
644
|
+
import chalk3 from "chalk";
|
|
645
|
+
function registerUsageCommands(parent) {
|
|
646
|
+
parent.command("usage").description("Show usage summary and tier info").action(async () => {
|
|
647
|
+
const opts = parent.opts();
|
|
648
|
+
const { data } = await get("/usage", void 0, opts);
|
|
649
|
+
if (isJsonMode()) return printJson(data);
|
|
650
|
+
const u = data.usage;
|
|
651
|
+
const pct = u.runsLimit > 0 ? (u.runsUsed / u.runsLimit * 100).toFixed(0) : "\u221E";
|
|
652
|
+
const bar = u.runsLimit > 0 ? progressBar(u.runsUsed, u.runsLimit, 20) : chalk3.green("unlimited");
|
|
653
|
+
printDetail([
|
|
654
|
+
["Tier", `${u.tierName} (${u.tier})`],
|
|
655
|
+
["Runs", `${u.runsUsed} / ${u.runsLimit} (${pct}%)`],
|
|
656
|
+
["Usage", bar],
|
|
657
|
+
["Can Start Run", u.canStartRun ? chalk3.green("Yes") : chalk3.red("No")],
|
|
658
|
+
["Period Start", formatDate(u.periodStart)],
|
|
659
|
+
["Period End", formatDate(u.periodEnd)]
|
|
660
|
+
]);
|
|
661
|
+
if (data.tiers?.length) {
|
|
662
|
+
console.log("\nAvailable Tiers:");
|
|
663
|
+
for (const t of data.tiers) {
|
|
664
|
+
const price = t.monthlyPriceUsd === 0 ? "Free" : `$${t.monthlyPriceUsd}/mo`;
|
|
665
|
+
const current = t.slug === u.tier ? chalk3.green(" (current)") : "";
|
|
666
|
+
console.log(` ${chalk3.bold(t.name)}${current} \u2014 ${price}, ${t.runsPerMonth} runs/mo`);
|
|
667
|
+
for (const f of t.features) {
|
|
668
|
+
console.log(` \u2022 ${f}`);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
function progressBar(used, total, width) {
|
|
675
|
+
const ratio = Math.min(used / total, 1);
|
|
676
|
+
const filled = Math.round(ratio * width);
|
|
677
|
+
const empty = width - filled;
|
|
678
|
+
const color = ratio > 0.9 ? chalk3.red : ratio > 0.7 ? chalk3.yellow : chalk3.green;
|
|
679
|
+
return color("\u2588".repeat(filled)) + chalk3.dim("\u2591".repeat(empty));
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// src/index.ts
|
|
683
|
+
var program = new Command();
|
|
684
|
+
program.name("tt").description("Tuned Tensor CLI \u2014 fine-tune and evaluate LLMs").version("0.1.0").option("-k, --api-key <key>", "API key (overrides stored key)").option(
|
|
685
|
+
"-u, --base-url <url>",
|
|
686
|
+
"API base URL (default: https://www.tunedtensor.com)"
|
|
687
|
+
).option("--json", "Output raw JSON").option("--no-color", "Disable colors").hook("preAction", (_thisCommand, actionCommand) => {
|
|
688
|
+
const rootOpts = program.opts();
|
|
689
|
+
if (rootOpts.json) setJsonMode(true);
|
|
690
|
+
if (rootOpts.color === false) {
|
|
691
|
+
process.env.FORCE_COLOR = "0";
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
registerAuthCommands(program);
|
|
695
|
+
registerSpecsCommands(program);
|
|
696
|
+
registerRunsCommands(program);
|
|
697
|
+
registerDatasetsCommands(program);
|
|
698
|
+
registerModelsCommands(program);
|
|
699
|
+
registerUsageCommands(program);
|
|
700
|
+
program.parseAsync().catch((err) => {
|
|
701
|
+
if (err instanceof ApiError) {
|
|
702
|
+
printError(`[${err.status}] ${err.message}`);
|
|
703
|
+
process.exit(1);
|
|
704
|
+
}
|
|
705
|
+
printError(err.message || String(err));
|
|
706
|
+
process.exit(1);
|
|
707
|
+
});
|
|
708
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/output.ts","../src/commands/auth.ts","../src/config.ts","../src/commands/specs.ts","../src/client.ts","../src/commands/runs.ts","../src/commands/datasets.ts","../src/commands/models.ts","../src/commands/usage.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { setJsonMode, printError } from \"./output.js\";\nimport { registerAuthCommands } from \"./commands/auth.js\";\nimport { registerSpecsCommands } from \"./commands/specs.js\";\nimport { registerRunsCommands } from \"./commands/runs.js\";\nimport { registerDatasetsCommands } from \"./commands/datasets.js\";\nimport { registerModelsCommands } from \"./commands/models.js\";\n\nimport { registerUsageCommands } from \"./commands/usage.js\";\nimport { ApiError } from \"./client.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"tt\")\n .description(\"Tuned Tensor CLI — fine-tune and evaluate LLMs\")\n .version(\"0.1.0\")\n .option(\"-k, --api-key <key>\", \"API key (overrides stored key)\")\n .option(\n \"-u, --base-url <url>\",\n \"API base URL (default: https://www.tunedtensor.com)\",\n )\n .option(\"--json\", \"Output raw JSON\")\n .option(\"--no-color\", \"Disable colors\")\n .hook(\"preAction\", (_thisCommand, actionCommand) => {\n const rootOpts = program.opts();\n if (rootOpts.json) setJsonMode(true);\n if (rootOpts.color === false) {\n process.env.FORCE_COLOR = \"0\";\n }\n });\n\nregisterAuthCommands(program);\nregisterSpecsCommands(program);\nregisterRunsCommands(program);\nregisterDatasetsCommands(program);\nregisterModelsCommands(program);\n\nregisterUsageCommands(program);\n\nprogram.parseAsync().catch((err) => {\n if (err instanceof ApiError) {\n printError(`[${err.status}] ${err.message}`);\n process.exit(1);\n }\n printError(err.message || String(err));\n process.exit(1);\n});\n","import chalk from \"chalk\";\nimport Table from \"cli-table3\";\n\nlet jsonMode = false;\n\nexport function setJsonMode(enabled: boolean) {\n jsonMode = enabled;\n}\n\nexport function isJsonMode(): boolean {\n return jsonMode;\n}\n\nexport function printJson(data: unknown) {\n console.log(JSON.stringify(data, null, 2));\n}\n\nexport function printTable(\n headers: string[],\n rows: string[][],\n meta?: { page: number; per_page: number; total: number },\n) {\n const table = new Table({\n head: headers.map((h) => chalk.bold.cyan(h)),\n style: { head: [], border: [] },\n });\n for (const row of rows) {\n table.push(row);\n }\n console.log(table.toString());\n\n if (meta) {\n const totalPages = Math.ceil(meta.total / meta.per_page);\n console.log(\n chalk.dim(\n `\\nPage ${meta.page}/${totalPages} (${meta.total} total)`,\n ),\n );\n }\n}\n\nexport function printDetail(fields: [string, string | undefined][]) {\n const maxLabel = Math.max(...fields.map(([l]) => l.length));\n for (const [label, value] of fields) {\n const paddedLabel = label.padEnd(maxLabel);\n console.log(`${chalk.bold.cyan(paddedLabel)} ${value ?? chalk.dim(\"—\")}`);\n }\n}\n\nexport function printSuccess(message: string) {\n console.log(chalk.green(\"✓\") + \" \" + message);\n}\n\nexport function printWarning(message: string) {\n console.log(chalk.yellow(\"!\") + \" \" + message);\n}\n\nexport function printError(message: string) {\n console.error(chalk.red(\"✗\") + \" \" + message);\n}\n\nexport function formatDate(iso?: string | null): string {\n if (!iso) return \"—\";\n return new Date(iso).toLocaleString();\n}\n\nexport function formatStatus(status: string): string {\n const map: Record<string, (s: string) => string> = {\n completed: chalk.green,\n running: chalk.blue,\n training: chalk.blue,\n evaluating: chalk.blue,\n preparing: chalk.yellow,\n pending: chalk.yellow,\n uploading: chalk.yellow,\n failed: chalk.red,\n cancelled: chalk.dim,\n validated: chalk.green,\n invalid: chalk.red,\n };\n const colorFn = map[status] || chalk.white;\n return colorFn(status);\n}\n\nexport function truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n return str.slice(0, max - 1) + \"…\";\n}\n\nexport function shortId(id: string): string {\n return id.slice(0, 8);\n}\n","import { Command } from \"commander\";\nimport { createInterface } from \"node:readline/promises\";\nimport { stdin, stdout } from \"node:process\";\nimport {\n readConfig,\n updateConfig,\n clearConfig,\n getApiKey,\n getBaseUrl,\n DEFAULT_BASE_URL,\n} from \"../config.js\";\nimport {\n printSuccess,\n printDetail,\n printError,\n printWarning,\n isJsonMode,\n printJson,\n} from \"../output.js\";\nimport chalk from \"chalk\";\n\nexport function registerAuthCommands(parent: Command) {\n const auth = parent.command(\"auth\").description(\"Manage authentication\");\n\n auth\n .command(\"login\")\n .description(\"Store an API key for authentication\")\n .argument(\"[key]\", \"API key (tt_...). If omitted, you will be prompted.\")\n .action(async (key?: string) => {\n let apiKey = key;\n\n if (!apiKey) {\n const settingsUrl = `${DEFAULT_BASE_URL}/dashboard/settings`;\n console.log();\n console.log(\n ` To get your API key, go to ${chalk.bold(\"Settings → API Keys\")} in the Tuned Tensor dashboard:`,\n );\n console.log(` ${chalk.cyan.underline(settingsUrl)}`);\n console.log();\n\n const rl = createInterface({ input: stdin, output: stdout });\n apiKey = await rl.question(\"Enter your API key (tt_...): \");\n rl.close();\n }\n\n apiKey = apiKey.trim();\n\n if (!apiKey.startsWith(\"tt_\") || apiKey.length !== 51) {\n printError(\n \"Invalid API key format. Keys start with tt_ and are 51 characters long.\",\n );\n process.exit(1);\n }\n\n updateConfig({ api_key: apiKey });\n printSuccess(\n `API key stored (${apiKey.slice(0, 8)}...). You're ready to go.`,\n );\n });\n\n auth\n .command(\"logout\")\n .description(\"Remove stored credentials\")\n .action(() => {\n clearConfig();\n printSuccess(\"Credentials removed.\");\n });\n\n auth\n .command(\"status\")\n .description(\"Show current authentication status\")\n .action(() => {\n const opts = parent.opts();\n const apiKey = getApiKey(opts);\n const baseUrl = getBaseUrl(opts);\n\n if (isJsonMode()) {\n printJson({\n authenticated: !!apiKey,\n key_prefix: apiKey ? apiKey.slice(0, 8) + \"...\" : null,\n base_url: baseUrl,\n });\n return;\n }\n\n if (apiKey) {\n printDetail([\n [\"Authenticated\", \"Yes\"],\n [\"API Key\", apiKey.slice(0, 8) + \"...\"],\n [\"Base URL\", baseUrl],\n ]);\n } else {\n printWarning(\n \"Not authenticated. Run `tt auth login` to store an API key.\",\n );\n printDetail([[\"Base URL\", baseUrl]]);\n }\n });\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport interface Config {\n api_key?: string;\n base_url?: string;\n}\n\nconst CONFIG_DIR_NAME = \"tuned-tensor\";\nconst CONFIG_FILE_NAME = \"config.json\";\n\nfunction getConfigDir(): string {\n const xdg = process.env.XDG_CONFIG_HOME;\n const base = xdg || join(homedir(), \".config\");\n return join(base, CONFIG_DIR_NAME);\n}\n\nfunction getConfigPath(): string {\n return join(getConfigDir(), CONFIG_FILE_NAME);\n}\n\nexport function readConfig(): Config {\n const path = getConfigPath();\n if (!existsSync(path)) return {};\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"));\n } catch {\n return {};\n }\n}\n\nexport function writeConfig(config: Config): void {\n const dir = getConfigDir();\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(getConfigPath(), JSON.stringify(config, null, 2) + \"\\n\");\n}\n\nexport function updateConfig(partial: Partial<Config>): void {\n const current = readConfig();\n writeConfig({ ...current, ...partial });\n}\n\nexport function clearConfig(): void {\n const path = getConfigPath();\n if (existsSync(path)) writeFileSync(path, \"{}\\n\");\n}\n\nexport const DEFAULT_BASE_URL = \"https://www.tunedtensor.com\";\n\nexport function getBaseUrl(opts?: { baseUrl?: string }): string {\n return opts?.baseUrl || process.env.TUNED_TENSOR_URL || readConfig().base_url || DEFAULT_BASE_URL;\n}\n\nexport function getApiKey(opts?: { apiKey?: string }): string | undefined {\n return opts?.apiKey || process.env.TUNED_TENSOR_API_KEY || readConfig().api_key;\n}\n","import { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { get, post, put, del, type ClientOpts } from \"../client.js\";\nimport {\n printTable,\n printDetail,\n printSuccess,\n printJson,\n isJsonMode,\n formatDate,\n formatStatus,\n truncate,\n shortId,\n} from \"../output.js\";\n\ninterface BehaviorSpec {\n id: string;\n name: string;\n description: string;\n system_prompt: string;\n guidelines: string[];\n examples: { input: string; output: string }[];\n constraints: string[];\n base_model: string;\n created_at: string;\n updated_at: string;\n _run_count?: number;\n _latest_run_status?: string;\n _latest_run_score?: number;\n _runs?: unknown[];\n}\n\nexport function registerSpecsCommands(parent: Command) {\n const specs = parent\n .command(\"specs\")\n .description(\"Manage behavior specs\");\n\n specs\n .command(\"list\")\n .description(\"List behavior specs\")\n .option(\"-p, --page <n>\", \"Page number\", \"1\")\n .option(\"--per-page <n>\", \"Results per page\", \"20\")\n .action(async (cmdOpts) => {\n const opts = parent.opts() as ClientOpts;\n const { data, meta } = await get<BehaviorSpec[]>(\n \"/behavior-specs\",\n { page: cmdOpts.page, per_page: cmdOpts.perPage },\n opts,\n );\n\n if (isJsonMode()) return printJson({ data, meta });\n\n printTable(\n [\"ID\", \"Name\", \"Model\", \"Examples\", \"Runs\", \"Status\", \"Updated\"],\n data.map((s) => [\n shortId(s.id),\n truncate(s.name, 30),\n s.base_model.split(\"/\").pop() || s.base_model,\n String(s.examples?.length ?? 0),\n String(s._run_count ?? 0),\n s._latest_run_status ? formatStatus(s._latest_run_status) : \"—\",\n formatDate(s.updated_at),\n ]),\n meta,\n );\n });\n\n specs\n .command(\"get\")\n .description(\"Show spec details\")\n .argument(\"<id>\", \"Spec ID (full or prefix)\")\n .action(async (id: string) => {\n const opts = parent.opts() as ClientOpts;\n const { data } = await get<BehaviorSpec>(`/behavior-specs/${id}`, undefined, opts);\n\n if (isJsonMode()) return printJson(data);\n\n printDetail([\n [\"ID\", data.id],\n [\"Name\", data.name],\n [\"Description\", data.description || undefined],\n [\"Base Model\", data.base_model],\n [\"System Prompt\", data.system_prompt ? truncate(data.system_prompt, 80) : undefined],\n [\"Guidelines\", data.guidelines?.length ? data.guidelines.join(\", \") : undefined],\n [\"Constraints\", data.constraints?.length ? data.constraints.join(\", \") : undefined],\n [\"Examples\", String(data.examples?.length ?? 0)],\n [\"Runs\", String(data._run_count ?? data._runs?.length ?? 0)],\n [\"Created\", formatDate(data.created_at)],\n [\"Updated\", formatDate(data.updated_at)],\n ]);\n\n if (data.examples?.length) {\n console.log(\"\\nExamples:\");\n for (const [i, ex] of data.examples.slice(0, 5).entries()) {\n console.log(` ${i + 1}. Input: ${truncate(ex.input, 60)}`);\n console.log(` Output: ${truncate(ex.output, 60)}`);\n }\n if (data.examples.length > 5) {\n console.log(` ... and ${data.examples.length - 5} more`);\n }\n }\n });\n\n specs\n .command(\"create\")\n .description(\"Create a behavior spec\")\n .option(\"-f, --file <path>\", \"JSON file with spec definition\")\n .option(\"-n, --name <name>\", \"Spec name\")\n .option(\"--model <model>\", \"Base model ID\")\n .action(async (cmdOpts) => {\n const opts = parent.opts() as ClientOpts;\n let body: Record<string, unknown>;\n\n if (cmdOpts.file) {\n body = JSON.parse(readFileSync(cmdOpts.file, \"utf-8\"));\n } else if (cmdOpts.name) {\n body = { name: cmdOpts.name };\n if (cmdOpts.model) body.base_model = cmdOpts.model;\n } else {\n console.error(\"Provide --file or --name\");\n process.exit(1);\n }\n\n const { data } = await post<BehaviorSpec>(\"/behavior-specs\", body, opts);\n\n if (isJsonMode()) return printJson(data);\n printSuccess(`Spec created: ${data.name} (${shortId(data.id)})`);\n });\n\n specs\n .command(\"update\")\n .description(\"Update a behavior spec\")\n .argument(\"<id>\", \"Spec ID\")\n .option(\"-f, --file <path>\", \"JSON file with fields to update\")\n .option(\"-n, --name <name>\", \"New name\")\n .option(\"--model <model>\", \"New base model ID\")\n .action(async (id: string, cmdOpts) => {\n const opts = parent.opts() as ClientOpts;\n let body: Record<string, unknown>;\n\n if (cmdOpts.file) {\n body = JSON.parse(readFileSync(cmdOpts.file, \"utf-8\"));\n } else {\n body = {};\n if (cmdOpts.name) body.name = cmdOpts.name;\n if (cmdOpts.model) body.base_model = cmdOpts.model;\n }\n\n const { data } = await put<BehaviorSpec>(\n `/behavior-specs/${id}`,\n body,\n opts,\n );\n\n if (isJsonMode()) return printJson(data);\n printSuccess(`Spec updated: ${data.name} (${shortId(data.id)})`);\n });\n\n specs\n .command(\"delete\")\n .description(\"Delete a behavior spec\")\n .argument(\"<id>\", \"Spec ID\")\n .action(async (id: string) => {\n const opts = parent.opts() as ClientOpts;\n await del(`/behavior-specs/${id}`, opts);\n\n if (isJsonMode()) return printJson({ id, deleted: true });\n printSuccess(`Spec deleted: ${shortId(id)}`);\n });\n}\n","import { readFileSync, statSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { getApiKey, getBaseUrl } from \"./config.js\";\n\nexport interface ApiResponse<T> {\n data: T;\n meta?: { page: number; per_page: number; total: number };\n}\n\nexport interface ApiErrorBody {\n error: { code: string; message: string };\n}\n\nexport class ApiError extends Error {\n constructor(\n public status: number,\n public code: string,\n message: string,\n ) {\n super(message);\n this.name = \"ApiError\";\n }\n}\n\nexport interface ClientOpts {\n apiKey?: string;\n baseUrl?: string;\n}\n\nfunction resolveKey(opts?: ClientOpts): string {\n const key = getApiKey(opts);\n if (!key) {\n throw new ApiError(\n 401,\n \"NO_API_KEY\",\n \"No API key found. Run `tt auth login` or set TUNED_TENSOR_API_KEY.\",\n );\n }\n return key;\n}\n\nfunction buildUrl(\n path: string,\n query?: Record<string, string | number | undefined>,\n opts?: ClientOpts,\n): string {\n const base = getBaseUrl(opts);\n const url = new URL(`/api/v1${path}`, base);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n}\n\nasync function request<T>(\n method: string,\n path: string,\n options: {\n query?: Record<string, string | number | undefined>;\n body?: unknown;\n opts?: ClientOpts;\n } = {},\n): Promise<ApiResponse<T>> {\n const key = resolveKey(options.opts);\n const url = buildUrl(path, options.query, options.opts);\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${key}`,\n };\n\n let bodyPayload: string | undefined;\n if (options.body) {\n headers[\"Content-Type\"] = \"application/json\";\n bodyPayload = JSON.stringify(options.body);\n }\n\n const res = await fetch(url, { method, headers, body: bodyPayload });\n\n if (!res.ok) {\n let errBody: ApiErrorBody;\n try {\n errBody = (await res.json()) as ApiErrorBody;\n } catch {\n throw new ApiError(res.status, \"UNKNOWN\", res.statusText);\n }\n throw new ApiError(\n res.status,\n errBody.error?.code || \"UNKNOWN\",\n errBody.error?.message || res.statusText,\n );\n }\n\n if (res.status === 204) return { data: null as T };\n return (await res.json()) as ApiResponse<T>;\n}\n\nexport function get<T>(\n path: string,\n query?: Record<string, string | number | undefined>,\n opts?: ClientOpts,\n) {\n return request<T>(\"GET\", path, { query, opts });\n}\n\nexport function post<T>(\n path: string,\n body?: unknown,\n opts?: ClientOpts,\n) {\n return request<T>(\"POST\", path, { body, opts });\n}\n\nexport function put<T>(\n path: string,\n body?: unknown,\n opts?: ClientOpts,\n) {\n return request<T>(\"PUT\", path, { body, opts });\n}\n\nexport function patch<T>(\n path: string,\n body?: unknown,\n opts?: ClientOpts,\n) {\n return request<T>(\"PATCH\", path, { body, opts });\n}\n\nexport function del<T>(path: string, opts?: ClientOpts) {\n return request<T>(\"DELETE\", path, { opts });\n}\n\nexport async function upload<T>(\n path: string,\n filePath: string,\n fields: Record<string, string>,\n opts?: ClientOpts,\n): Promise<ApiResponse<T>> {\n const key = resolveKey(opts);\n const url = buildUrl(path, undefined, opts);\n\n const form = new FormData();\n for (const [k, v] of Object.entries(fields)) {\n form.append(k, v);\n }\n\n const fileBytes = readFileSync(filePath);\n const fileSize = statSync(filePath).size;\n const blob = new Blob([fileBytes], { type: \"application/jsonl\" });\n form.append(\"file\", blob, basename(filePath));\n\n const res = await fetch(url, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${key}` },\n body: form,\n });\n\n if (!res.ok) {\n let errBody: ApiErrorBody;\n try {\n errBody = (await res.json()) as ApiErrorBody;\n } catch {\n throw new ApiError(res.status, \"UNKNOWN\", res.statusText);\n }\n throw new ApiError(\n res.status,\n errBody.error?.code || \"UNKNOWN\",\n errBody.error?.message || res.statusText,\n );\n }\n\n return (await res.json()) as ApiResponse<T>;\n}\n","import { Command } from \"commander\";\nimport ora from \"ora\";\nimport { get, post, type ClientOpts } from \"../client.js\";\nimport {\n printTable,\n printDetail,\n printSuccess,\n printJson,\n isJsonMode,\n formatDate,\n formatStatus,\n shortId,\n truncate,\n} from \"../output.js\";\n\ninterface Run {\n id: string;\n behavior_spec_id: string;\n run_number: number;\n status: string;\n hyperparameters: Record<string, unknown> | null;\n eval_summary: { mean_score?: number; pass_rate?: number } | null;\n error: string | null;\n started_at: string | null;\n completed_at: string | null;\n created_at: string;\n updated_at: string;\n _spec_name?: string;\n _evals?: RunEval[];\n}\n\ninterface RunEval {\n id: string;\n prompt: string;\n expected: string;\n actual: string;\n passed: boolean;\n score: number | null;\n reasoning: string | null;\n latency_ms: number | null;\n}\n\nexport function registerRunsCommands(parent: Command) {\n const runs = parent.command(\"runs\").description(\"Manage runs\");\n\n runs\n .command(\"list\")\n .description(\"List runs\")\n .option(\"-s, --spec <id>\", \"Filter by spec ID\")\n .option(\"-p, --page <n>\", \"Page number\", \"1\")\n .option(\"--per-page <n>\", \"Results per page\", \"20\")\n .action(async (cmdOpts) => {\n const opts = parent.opts() as ClientOpts;\n\n let path = \"/runs\";\n const query: Record<string, string | number> = {\n page: cmdOpts.page,\n per_page: cmdOpts.perPage,\n };\n\n if (cmdOpts.spec) {\n path = `/behavior-specs/${cmdOpts.spec}/runs`;\n }\n\n const { data, meta } = await get<Run[]>(path, query, opts);\n\n if (isJsonMode()) return printJson({ data, meta });\n\n printTable(\n [\"ID\", \"Spec\", \"#\", \"Status\", \"Score\", \"Started\", \"Completed\"],\n data.map((r) => [\n shortId(r.id),\n r._spec_name ? truncate(r._spec_name, 20) : shortId(r.behavior_spec_id),\n String(r.run_number),\n formatStatus(r.status),\n r.eval_summary?.mean_score != null\n ? (r.eval_summary.mean_score * 100).toFixed(1) + \"%\"\n : \"—\",\n formatDate(r.started_at),\n formatDate(r.completed_at),\n ]),\n meta,\n );\n });\n\n runs\n .command(\"get\")\n .description(\"Show run details and eval results\")\n .argument(\"<id>\", \"Run ID\")\n .action(async (id: string) => {\n const opts = parent.opts() as ClientOpts;\n const { data } = await get<Run>(`/runs/${id}`, undefined, opts);\n\n if (isJsonMode()) return printJson(data);\n\n printDetail([\n [\"ID\", data.id],\n [\"Spec\", data._spec_name ?? shortId(data.behavior_spec_id)],\n [\"Run #\", String(data.run_number)],\n [\"Status\", formatStatus(data.status)],\n [\"Score\", data.eval_summary?.mean_score != null\n ? (data.eval_summary.mean_score * 100).toFixed(1) + \"%\"\n : undefined],\n [\"Pass Rate\", data.eval_summary?.pass_rate != null\n ? (data.eval_summary.pass_rate * 100).toFixed(1) + \"%\"\n : undefined],\n [\"Error\", data.error ?? undefined],\n [\"Started\", formatDate(data.started_at)],\n [\"Completed\", formatDate(data.completed_at)],\n [\"Created\", formatDate(data.created_at)],\n ]);\n\n if (data.hyperparameters && Object.keys(data.hyperparameters).length) {\n console.log(\"\\nHyperparameters:\");\n for (const [k, v] of Object.entries(data.hyperparameters)) {\n console.log(` ${k}: ${v}`);\n }\n }\n\n if (data._evals?.length) {\n console.log(`\\nEval Results (${data._evals.length}):`);\n printTable(\n [\"#\", \"Passed\", \"Score\", \"Prompt\", \"Latency\"],\n data._evals.map((e, i) => [\n String(i + 1),\n e.passed ? \"✓\" : \"✗\",\n e.score != null ? e.score.toFixed(2) : \"—\",\n truncate(e.prompt, 40),\n e.latency_ms != null ? `${e.latency_ms}ms` : \"—\",\n ]),\n );\n }\n });\n\n runs\n .command(\"start\")\n .description(\"Start a new run for a behavior spec\")\n .argument(\"<spec-id>\", \"Behavior spec ID\")\n .option(\"--no-augment\", \"Disable data augmentation\")\n .option(\"--epochs <n>\", \"Number of training epochs\")\n .option(\"--lr <rate>\", \"Learning rate\")\n .option(\"--batch-size <n>\", \"Batch size\")\n .option(\"--lora-rank <n>\", \"LoRA rank\")\n .option(\"--lora-alpha <n>\", \"LoRA alpha\")\n .action(async (specId: string, cmdOpts) => {\n const opts = parent.opts() as ClientOpts;\n const body: Record<string, unknown> = {};\n\n if (cmdOpts.augment === false) body.augment = false;\n\n const hp: Record<string, unknown> = {};\n if (cmdOpts.epochs) hp.n_epochs = Number(cmdOpts.epochs);\n if (cmdOpts.lr) hp.learning_rate = Number(cmdOpts.lr);\n if (cmdOpts.batchSize) hp.batch_size = Number(cmdOpts.batchSize);\n if (cmdOpts.loraRank) hp.lora_rank = Number(cmdOpts.loraRank);\n if (cmdOpts.loraAlpha) hp.lora_alpha = Number(cmdOpts.loraAlpha);\n if (Object.keys(hp).length) body.hyperparameters = hp;\n\n const { data } = await post<Run>(\n `/behavior-specs/${specId}/runs`,\n body,\n opts,\n );\n\n if (isJsonMode()) return printJson(data);\n printSuccess(\n `Run #${data.run_number} started (${shortId(data.id)}). Status: ${formatStatus(data.status)}`,\n );\n });\n\n runs\n .command(\"cancel\")\n .description(\"Cancel a running run\")\n .argument(\"<id>\", \"Run ID\")\n .action(async (id: string) => {\n const opts = parent.opts() as ClientOpts;\n const { data } = await post<Run>(`/runs/${id}/cancel`, undefined, opts);\n\n if (isJsonMode()) return printJson(data);\n printSuccess(`Run ${shortId(id)} cancelled.`);\n });\n\n runs\n .command(\"watch\")\n .description(\"Watch a run until it completes\")\n .argument(\"<id>\", \"Run ID\")\n .option(\"--interval <ms>\", \"Poll interval in milliseconds\", \"5000\")\n .action(async (id: string, cmdOpts) => {\n const opts = parent.opts() as ClientOpts;\n const interval = Number(cmdOpts.interval);\n const spinner = ora(`Watching run ${shortId(id)}...`).start();\n\n const terminalStates = new Set([\n \"completed\",\n \"failed\",\n \"cancelled\",\n ]);\n\n let run: Run;\n while (true) {\n const { data } = await get<Run>(`/runs/${id}`, undefined, opts);\n run = data;\n spinner.text = `Run ${shortId(id)} — ${formatStatus(run.status)}`;\n\n if (terminalStates.has(run.status)) break;\n await new Promise((r) => setTimeout(r, interval));\n }\n\n spinner.stop();\n\n if (isJsonMode()) return printJson(run);\n\n printDetail([\n [\"Run\", shortId(run.id)],\n [\"Status\", formatStatus(run.status)],\n [\"Score\", run.eval_summary?.mean_score != null\n ? (run.eval_summary.mean_score * 100).toFixed(1) + \"%\"\n : undefined],\n [\"Error\", run.error ?? undefined],\n [\"Completed\", formatDate(run.completed_at)],\n ]);\n });\n}\n","import { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { get, del, upload, type ClientOpts } from \"../client.js\";\nimport {\n printTable,\n printDetail,\n printSuccess,\n printJson,\n printError,\n isJsonMode,\n formatDate,\n formatStatus,\n shortId,\n truncate,\n} from \"../output.js\";\n\ninterface Dataset {\n id: string;\n name: string;\n description: string | null;\n storage_path: string;\n file_size_bytes: number;\n row_count: number;\n format: string;\n status: string;\n validation_errors: string[] | null;\n created_at: string;\n updated_at: string;\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport function registerDatasetsCommands(parent: Command) {\n const datasets = parent\n .command(\"datasets\")\n .description(\"Manage datasets\");\n\n datasets\n .command(\"list\")\n .description(\"List datasets\")\n .option(\"-p, --page <n>\", \"Page number\", \"1\")\n .option(\"--per-page <n>\", \"Results per page\", \"20\")\n .action(async (cmdOpts) => {\n const opts = parent.opts() as ClientOpts;\n const { data, meta } = await get<Dataset[]>(\n \"/datasets\",\n { page: cmdOpts.page, per_page: cmdOpts.perPage },\n opts,\n );\n\n if (isJsonMode()) return printJson({ data, meta });\n\n printTable(\n [\"ID\", \"Name\", \"Rows\", \"Size\", \"Status\", \"Created\"],\n data.map((d) => [\n shortId(d.id),\n truncate(d.name, 30),\n String(d.row_count),\n formatBytes(d.file_size_bytes),\n formatStatus(d.status),\n formatDate(d.created_at),\n ]),\n meta,\n );\n });\n\n datasets\n .command(\"get\")\n .description(\"Show dataset details\")\n .argument(\"<id>\", \"Dataset ID\")\n .action(async (id: string) => {\n const opts = parent.opts() as ClientOpts;\n const { data } = await get<Dataset>(`/datasets/${id}`, undefined, opts);\n\n if (isJsonMode()) return printJson(data);\n\n printDetail([\n [\"ID\", data.id],\n [\"Name\", data.name],\n [\"Description\", data.description ?? undefined],\n [\"Format\", data.format],\n [\"Rows\", String(data.row_count)],\n [\"Size\", formatBytes(data.file_size_bytes)],\n [\"Status\", formatStatus(data.status)],\n [\"Created\", formatDate(data.created_at)],\n [\"Updated\", formatDate(data.updated_at)],\n ]);\n\n if (data.validation_errors?.length) {\n console.log(\"\\nValidation Errors:\");\n for (const err of data.validation_errors) {\n console.log(` - ${err}`);\n }\n }\n });\n\n datasets\n .command(\"upload\")\n .description(\"Upload a JSONL dataset file\")\n .argument(\"<file>\", \"Path to JSONL file\")\n .option(\"-n, --name <name>\", \"Dataset name (defaults to filename)\")\n .option(\"-d, --description <desc>\", \"Dataset description\")\n .action(async (file: string, cmdOpts) => {\n const opts = parent.opts() as ClientOpts;\n\n if (!existsSync(file)) {\n printError(`File not found: ${file}`);\n process.exit(1);\n }\n\n const fields: Record<string, string> = {\n name: cmdOpts.name || file.split(\"/\").pop()!.replace(/\\.jsonl?$/, \"\"),\n };\n if (cmdOpts.description) fields.description = cmdOpts.description;\n\n const { data } = await upload<Dataset>(\n \"/datasets\",\n file,\n fields,\n opts,\n );\n\n if (isJsonMode()) return printJson(data);\n printSuccess(\n `Dataset uploaded: ${data.name} (${shortId(data.id)}) — ${data.row_count} rows`,\n );\n });\n\n datasets\n .command(\"delete\")\n .description(\"Delete a dataset\")\n .argument(\"<id>\", \"Dataset ID\")\n .action(async (id: string) => {\n const opts = parent.opts() as ClientOpts;\n await del(`/datasets/${id}`, opts);\n\n if (isJsonMode()) return printJson({ id, deleted: true });\n printSuccess(`Dataset deleted: ${shortId(id)}`);\n });\n}\n","import { Command } from \"commander\";\nimport { get, del, type ClientOpts } from \"../client.js\";\nimport {\n printTable,\n printDetail,\n printSuccess,\n printJson,\n isJsonMode,\n formatDate,\n shortId,\n truncate,\n} from \"../output.js\";\n\ninterface Model {\n id: string;\n name: string;\n provider: string;\n provider_model_id: string;\n base_model: string;\n description: string | null;\n created_at: string;\n}\n\nexport function registerModelsCommands(parent: Command) {\n const models = parent.command(\"models\").description(\"Manage fine-tuned models\");\n\n models\n .command(\"list\")\n .description(\"List fine-tuned models\")\n .option(\"-p, --page <n>\", \"Page number\", \"1\")\n .option(\"--per-page <n>\", \"Results per page\", \"20\")\n .action(async (cmdOpts) => {\n const opts = parent.opts() as ClientOpts;\n const { data, meta } = await get<Model[]>(\n \"/models\",\n { page: cmdOpts.page, per_page: cmdOpts.perPage },\n opts,\n );\n\n if (isJsonMode()) return printJson({ data, meta });\n\n printTable(\n [\"ID\", \"Name\", \"Base Model\", \"Provider\", \"Created\"],\n data.map((m) => [\n shortId(m.id),\n truncate(m.name, 30),\n m.base_model.split(\"/\").pop() || m.base_model,\n m.provider,\n formatDate(m.created_at),\n ]),\n meta,\n );\n });\n\n models\n .command(\"get\")\n .description(\"Show model details\")\n .argument(\"<id>\", \"Model ID\")\n .action(async (id: string) => {\n const opts = parent.opts() as ClientOpts;\n const { data } = await get<Model>(`/models/${id}`, undefined, opts);\n\n if (isJsonMode()) return printJson(data);\n\n printDetail([\n [\"ID\", data.id],\n [\"Name\", data.name],\n [\"Base Model\", data.base_model],\n [\"Provider\", data.provider],\n [\"Provider Model\", data.provider_model_id],\n [\"Description\", data.description ?? undefined],\n [\"Created\", formatDate(data.created_at)],\n ]);\n });\n\n models\n .command(\"delete\")\n .description(\"Delete a model\")\n .argument(\"<id>\", \"Model ID\")\n .action(async (id: string) => {\n const opts = parent.opts() as ClientOpts;\n await del(`/models/${id}`, opts);\n\n if (isJsonMode()) return printJson({ id, deleted: true });\n printSuccess(`Model deleted: ${shortId(id)}`);\n });\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { get, type ClientOpts } from \"../client.js\";\nimport {\n printDetail,\n printJson,\n isJsonMode,\n formatDate,\n} from \"../output.js\";\n\ninterface UsageSummary {\n tier: string;\n tierName: string;\n runsUsed: number;\n runsLimit: number;\n periodStart: string;\n periodEnd: string;\n canStartRun: boolean;\n}\n\ninterface TierDefinition {\n slug: string;\n name: string;\n runsPerMonth: number;\n monthlyPriceUsd: number;\n features: string[];\n}\n\ninterface UsageResponse {\n usage: UsageSummary;\n tiers: TierDefinition[];\n}\n\nexport function registerUsageCommands(parent: Command) {\n parent\n .command(\"usage\")\n .description(\"Show usage summary and tier info\")\n .action(async () => {\n const opts = parent.opts() as ClientOpts;\n const { data } = await get<UsageResponse>(\"/usage\", undefined, opts);\n\n if (isJsonMode()) return printJson(data);\n\n const u = data.usage;\n const pct = u.runsLimit > 0 ? ((u.runsUsed / u.runsLimit) * 100).toFixed(0) : \"∞\";\n const bar = u.runsLimit > 0\n ? progressBar(u.runsUsed, u.runsLimit, 20)\n : chalk.green(\"unlimited\");\n\n printDetail([\n [\"Tier\", `${u.tierName} (${u.tier})`],\n [\"Runs\", `${u.runsUsed} / ${u.runsLimit} (${pct}%)`],\n [\"Usage\", bar],\n [\"Can Start Run\", u.canStartRun ? chalk.green(\"Yes\") : chalk.red(\"No\")],\n [\"Period Start\", formatDate(u.periodStart)],\n [\"Period End\", formatDate(u.periodEnd)],\n ]);\n\n if (data.tiers?.length) {\n console.log(\"\\nAvailable Tiers:\");\n for (const t of data.tiers) {\n const price = t.monthlyPriceUsd === 0 ? \"Free\" : `$${t.monthlyPriceUsd}/mo`;\n const current = t.slug === u.tier ? chalk.green(\" (current)\") : \"\";\n console.log(` ${chalk.bold(t.name)}${current} — ${price}, ${t.runsPerMonth} runs/mo`);\n for (const f of t.features) {\n console.log(` • ${f}`);\n }\n }\n }\n });\n}\n\nfunction progressBar(used: number, total: number, width: number): string {\n const ratio = Math.min(used / total, 1);\n const filled = Math.round(ratio * width);\n const empty = width - filled;\n const color = ratio > 0.9 ? chalk.red : ratio > 0.7 ? chalk.yellow : chalk.green;\n return color(\"█\".repeat(filled)) + chalk.dim(\"░\".repeat(empty));\n}\n"],"mappings":"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","names":["chalk","readFileSync","readFileSync","readFileSync","readFileSync","existsSync","existsSync","chalk","chalk"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tuned-tensor/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for Tuned Tensor — fine-tune and evaluate LLMs from the command line",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"tt": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsup",
|
|
11
|
+
"dev": "tsup --watch",
|
|
12
|
+
"typecheck": "tsc --noEmit",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"llm",
|
|
21
|
+
"fine-tuning",
|
|
22
|
+
"cli",
|
|
23
|
+
"tuned-tensor"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"chalk": "^5.6.2",
|
|
28
|
+
"cli-table3": "^0.6.5",
|
|
29
|
+
"commander": "^14.0.3",
|
|
30
|
+
"ora": "^9.3.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^25.3.5",
|
|
34
|
+
"tsup": "^8.5.1",
|
|
35
|
+
"typescript": "^5.9.3",
|
|
36
|
+
"vitest": "^4.0.18"
|
|
37
|
+
}
|
|
38
|
+
}
|