@root-signals/scorable-cli 0.9.1 → 0.11.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.
Files changed (35) hide show
  1. package/README.md +87 -0
  2. package/dist/commands/evaluator/create.js +1 -1
  3. package/dist/commands/evaluator/delete.js +1 -1
  4. package/dist/commands/evaluator/update.js +2 -2
  5. package/dist/commands/file/upload.d.ts +6 -1
  6. package/dist/commands/file/upload.d.ts.map +1 -1
  7. package/dist/commands/file/upload.js +8 -4
  8. package/dist/commands/judge/delete.js +1 -1
  9. package/dist/commands/judge/exec-openai.js +1 -1
  10. package/dist/commands/judge/generate.d.ts.map +1 -1
  11. package/dist/commands/judge/generate.js +13 -0
  12. package/dist/commands/model/create.d.ts +3 -0
  13. package/dist/commands/model/create.d.ts.map +1 -0
  14. package/dist/commands/model/create.js +48 -0
  15. package/dist/commands/model/delete.d.ts +3 -0
  16. package/dist/commands/model/delete.d.ts.map +1 -0
  17. package/dist/commands/model/delete.js +37 -0
  18. package/dist/commands/model/get.d.ts +3 -0
  19. package/dist/commands/model/get.d.ts.map +1 -0
  20. package/dist/commands/model/get.js +23 -0
  21. package/dist/commands/model/index.d.ts +3 -0
  22. package/dist/commands/model/index.d.ts.map +1 -0
  23. package/dist/commands/model/index.js +15 -0
  24. package/dist/commands/model/list.d.ts +3 -0
  25. package/dist/commands/model/list.d.ts.map +1 -0
  26. package/dist/commands/model/list.js +36 -0
  27. package/dist/commands/model/update.d.ts +3 -0
  28. package/dist/commands/model/update.d.ts.map +1 -0
  29. package/dist/commands/model/update.js +54 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +2 -0
  32. package/dist/output.d.ts +2 -1
  33. package/dist/output.d.ts.map +1 -1
  34. package/dist/output.js +17 -0
  35. package/package.json +2 -2
package/README.md CHANGED
@@ -136,6 +136,40 @@ Prompts for confirmation. Use `--yes` to skip.
136
136
  scorable judge duplicate <judge_id>
137
137
  ```
138
138
 
139
+ ### Generate a judge
140
+
141
+ AI-powered judge generation from a plain-language description of what you want to evaluate.
142
+
143
+ ```bash
144
+ scorable judge generate --intent "I am building a customer support chatbot. Evaluate that responses are helpful and follow our refund policy."
145
+ ```
146
+
147
+ Attach a policy document so the generated evaluators can check compliance against it:
148
+
149
+ ```bash
150
+ # Upload and generate in one step
151
+ scorable judge generate --intent "Evaluate responses against the attached policy." --file ./policy.pdf
152
+
153
+ # Or reuse an already-uploaded file
154
+ scorable judge generate --intent "Evaluate responses against the attached policy." --file-id <file_uuid>
155
+ ```
156
+
157
+ Options: `--intent` (required), `--file` (path to PDF/PNG/JPG — uploads and attaches), `--file-id` (UUID of an already-uploaded file), `--visibility` (`private`/`public`, default `private`), `--name`, `--stage`, `--extra-contexts` (JSON object, e.g. `'{"Domain":"hotel","Tone":"formal"}'`), `--reasoning-effort` (`off`/`low`/`medium`/`high`), `--judge-id` (regenerate an existing judge), `--overwrite`, `--context-aware`
158
+
159
+ ## File Management
160
+
161
+ ### Upload a file
162
+
163
+ Upload a PDF or image for use as context in judge generation or evaluator execution.
164
+
165
+ ```bash
166
+ scorable file upload ./policy.pdf
167
+ ```
168
+
169
+ Returns a file UUID that can be passed to `judge generate --file-id` or `evaluator execute --file-ids`.
170
+
171
+ Supported formats: PDF, PNG, JPG, JPEG, WEBP, SVG.
172
+
139
173
  ## Judge Execution
140
174
 
141
175
  ### Execute by ID
@@ -242,6 +276,59 @@ scorable evaluator execute-by-name "My Evaluator" --request "What is 2+2?" --res
242
276
 
243
277
  Accepts the same options as `execute`, including `--variables`.
244
278
 
279
+ ## Custom Model Management
280
+
281
+ Bring your own LLM (BYO-LLM) — register a custom or self-hosted model, then reference it from evaluators and judges.
282
+
283
+ ### List models
284
+
285
+ ```bash
286
+ scorable model list
287
+ ```
288
+
289
+ Shows ID, name, provider, and visibility. Options: `--page-size`, `--cursor`, `--ordering`.
290
+
291
+ ### Get a model
292
+
293
+ ```bash
294
+ scorable model get <model_id>
295
+ ```
296
+
297
+ ### Create a model
298
+
299
+ ```bash
300
+ # SaaS provider (key inline)
301
+ scorable model create --name my-gpt --model gpt-5.5 --key sk-...
302
+
303
+ # Self-hosted / custom endpoint
304
+ scorable model create \
305
+ --name azure/gpt-5.5 \
306
+ --model azure/gpt-5.5 \
307
+ --url https://my-azure-openai.openai.azure.com \
308
+ --key sk-...
309
+
310
+ # Read the key from stdin (keeps it out of shell history)
311
+ echo "$MY_PROVIDER_KEY" | scorable model create --name my-gpt --model gpt-5.5 --key -
312
+ ```
313
+
314
+ Options: `--name` (required), `--model`, `--url` (for self-hosted endpoints), `--key` (provider API key; `-` reads from stdin), `--max-token-count`, `--max-output-token-count`.
315
+
316
+ ### Update a model
317
+
318
+ ```bash
319
+ scorable model update <model_id> --max-output-token-count 4096
320
+ ```
321
+
322
+ `update` is a PATCH — only fields you pass are sent. All `create` flags are accepted as optional updates, including `--key -` for stdin.
323
+
324
+ ### Delete a model
325
+
326
+ ```bash
327
+ scorable model delete <model_id>
328
+ ```
329
+
330
+ Prompts for confirmation. Use `--yes` to skip.
331
+
245
332
  ## Execution Logs
246
333
 
247
334
  ### List execution logs
@@ -25,7 +25,7 @@ export function registerCreateCommand(evaluator) {
25
25
  }
26
26
  const payload = {
27
27
  name: opts.name,
28
- predicate: opts.scoringCriteria,
28
+ scoring_criteria: opts.scoringCriteria,
29
29
  };
30
30
  if (opts.intent)
31
31
  payload.intent = opts.intent;
@@ -10,7 +10,7 @@ export function registerDeleteCommand(evaluator) {
10
10
  .action(async (evaluatorId, opts) => {
11
11
  const apiKey = await requireApiKey();
12
12
  if (!opts.yes) {
13
- if (!process.stdin.isTTY && !process.stdout.isTTY) {
13
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
14
14
  throw new CliError(1, "Refusing to prompt for confirmation in a non-interactive environment. Pass --yes to skip the prompt.");
15
15
  }
16
16
  const { confirm } = await import("@inquirer/prompts");
@@ -7,7 +7,7 @@ export function registerUpdateCommand(evaluator) {
7
7
  .description("Update an existing evaluator (PATCH)")
8
8
  .option("--name <name>", "The new name for the evaluator")
9
9
  .option("--scoring-criteria <text>", "The new scoring criteria (prompt text)")
10
- .option("--models <json>", "JSON array of model names. E.g., '[\"gpt-4\"]'")
10
+ .option("--models <json>", "JSON array of model names. E.g., '[\"gpt-5.5\"]'")
11
11
  .option("--objective-id <id>", "The new objective ID")
12
12
  .option("--objective-version-id <id>", "The new objective version ID")
13
13
  .action(async (evaluatorId, opts) => {
@@ -16,7 +16,7 @@ export function registerUpdateCommand(evaluator) {
16
16
  if (opts.name !== undefined)
17
17
  payload.name = opts.name;
18
18
  if (opts.scoringCriteria !== undefined)
19
- payload.prompt = opts.scoringCriteria;
19
+ payload.scoring_criteria = opts.scoringCriteria;
20
20
  if (opts.objectiveId !== undefined)
21
21
  payload.objective_id = opts.objectiveId;
22
22
  if (opts.objectiveVersionId !== undefined)
@@ -1,4 +1,9 @@
1
1
  import { Command } from "commander";
2
- export declare function uploadFile(filePath: string): Promise<void>;
2
+ export interface UploadedFile {
3
+ id: string;
4
+ }
5
+ export declare function uploadFile(filePath: string, { silent }?: {
6
+ silent?: boolean;
7
+ }): Promise<UploadedFile>;
3
8
  export declare function registerUploadCommand(file: Command): void;
4
9
  //# sourceMappingURL=upload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/commands/file/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhE;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CA2BzD"}
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/commands/file/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,EAAE,MAAc,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAC5C,OAAO,CAAC,YAAY,CAAC,CA0BvB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CA2BzD"}
@@ -4,11 +4,12 @@ import * as path from "path";
4
4
  import { requireApiKey } from "../../auth.js";
5
5
  import { printSuccess, printError, printJson, handleSdkError } from "../../output.js";
6
6
  import { getSdkClient } from "../../auth.js";
7
- export async function uploadFile(filePath) {
7
+ import { CliError } from "../../types.js";
8
+ export async function uploadFile(filePath, { silent = false } = {}) {
8
9
  const apiKey = await requireApiKey();
9
10
  if (!fs.existsSync(filePath)) {
10
11
  printError(`File not found: ${filePath}`);
11
- process.exit(1);
12
+ throw new CliError(1, "file_not_found");
12
13
  }
13
14
  const client = getSdkClient(apiKey);
14
15
  const fileName = path.basename(filePath);
@@ -18,8 +19,11 @@ export async function uploadFile(filePath) {
18
19
  try {
19
20
  const result = await client.files.upload(blob, fileName);
20
21
  spinner.stop();
21
- printSuccess("File uploaded successfully!");
22
- printJson(result);
22
+ if (!silent) {
23
+ printSuccess("File uploaded successfully!");
24
+ printJson(result);
25
+ }
26
+ return result;
23
27
  }
24
28
  catch (e) {
25
29
  spinner.stop();
@@ -10,7 +10,7 @@ export function registerDeleteCommand(judge) {
10
10
  .action(async (judgeId, opts) => {
11
11
  const apiKey = await requireApiKey();
12
12
  if (!opts.yes) {
13
- if (!process.stdin.isTTY && !process.stdout.isTTY) {
13
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
14
14
  throw new CliError(1, "Refusing to prompt for confirmation in a non-interactive environment. Pass --yes to skip the prompt.");
15
15
  }
16
16
  const { confirm } = await import("@inquirer/prompts");
@@ -5,7 +5,7 @@ export function registerExecOpenaiCommand(judge) {
5
5
  judge
6
6
  .command("exec-openai <judgeIdInPath>")
7
7
  .description("Execute a specific judge via the OpenAI compatible API")
8
- .requiredOption("--model <model>", "LLM model for judge execution (e.g., gpt-4o)")
8
+ .requiredOption("--model <model>", "LLM model for judge execution (e.g., gpt-5.5)")
9
9
  .requiredOption("--messages <json>", "JSON string of the messages payload")
10
10
  .option("--extra-body <json>", "Optional JSON string for extra_body parameters")
11
11
  .action(async (judgeIdInPath, opts) => {
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CA2H5D"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CA8I5D"}
@@ -3,6 +3,7 @@ import chalk from "chalk";
3
3
  import { requireApiKey, getSdkClient } from "../../auth.js";
4
4
  import { printSuccess, printError, printWarning, printInfo, printJson, handleSdkError, } from "../../output.js";
5
5
  import { CliError } from "../../types.js";
6
+ import { uploadFile } from "../file/upload.js";
6
7
  export function registerGenerateCommand(judge) {
7
8
  judge
8
9
  .command("generate")
@@ -23,6 +24,8 @@ offer to connect the guest with a human agent when uncertain."`)
23
24
  .option("--overwrite", "Overwrite existing judge with the same name", false)
24
25
  .option("--extra-contexts <json>", 'JSON object of additional context key-value pairs, e.g. \'{"Tone Of Voice":"formal","Domain":"hotel chatbot"}\'')
25
26
  .option("--context-aware", "Enable context-aware evaluators for RAG applications (hallucination detection, context drift, etc.)", false)
27
+ .option("--file <path>", "Path to a file (PDF, PNG, JPG) to upload and attach as context for the judge")
28
+ .option("--file-id <id>", "ID of an already-uploaded file to use as context for the judge")
26
29
  .action(async (opts) => {
27
30
  const acceptedVisibilities = ["private", "public"];
28
31
  const visibility = opts.visibility;
@@ -30,6 +33,10 @@ offer to connect the guest with a human agent when uncertain."`)
30
33
  printError(`Invalid --visibility value: "${visibility}". Accepted values: private, public.`);
31
34
  throw new CliError(1, "invalid_visibility");
32
35
  }
36
+ if (opts.file && opts.fileId) {
37
+ printError("Cannot use both --file and --file-id. Provide one or the other.");
38
+ throw new CliError(1, "conflicting_file_options");
39
+ }
33
40
  const apiKey = await requireApiKey();
34
41
  let extra_contexts;
35
42
  if (opts.extraContexts) {
@@ -43,6 +50,11 @@ offer to connect the guest with a human agent when uncertain."`)
43
50
  }
44
51
  const spinner = ora("Generating judge (this may take a moment)...").start();
45
52
  try {
53
+ let fileId = opts.fileId;
54
+ if (opts.file) {
55
+ const uploaded = await uploadFile(opts.file, { silent: true });
56
+ fileId = uploaded.id;
57
+ }
46
58
  const client = getSdkClient(apiKey);
47
59
  const result = (await client.judges.generate({
48
60
  intent: opts.intent,
@@ -51,6 +63,7 @@ offer to connect the guest with a human agent when uncertain."`)
51
63
  name: opts.name,
52
64
  stage: opts.stage,
53
65
  judge_id: opts.judgeId,
66
+ file_id: fileId,
54
67
  extra_contexts: extra_contexts ?? null,
55
68
  enable_context_aware_evaluators: opts.contextAware || undefined,
56
69
  ...(opts.reasoningEffort && {
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerCreateCommand(model: Command): void;
3
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/model/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAgD1D"}
@@ -0,0 +1,48 @@
1
+ import ora from "ora";
2
+ import { requireApiKey, getSdkClient } from "../../auth.js";
3
+ import { printSuccess, printJson, handleSdkError } from "../../output.js";
4
+ async function readKeyFromStdin() {
5
+ const chunks = [];
6
+ for await (const chunk of process.stdin)
7
+ chunks.push(chunk);
8
+ return Buffer.concat(chunks).toString().trim();
9
+ }
10
+ export function registerCreateCommand(model) {
11
+ model
12
+ .command("create")
13
+ .description("Create a new custom model")
14
+ .requiredOption("--name <name>", "Model name (unique within your organization)")
15
+ .option("--model <model>", "Underlying model identifier (e.g. 'gpt-5.5')")
16
+ .option("--url <url>", "API base URL (for self-hosted or non-SaaS endpoints)")
17
+ .option("--key <value>", "Provider API key. Pass '-' to read the key from stdin (avoids shell history leak)")
18
+ .option("--max-token-count <number>", "Maximum total tokens", parseInt)
19
+ .option("--max-output-token-count <number>", "Maximum output tokens", parseInt)
20
+ .action(async (opts) => {
21
+ const apiKey = await requireApiKey();
22
+ const payload = { name: opts.name };
23
+ if (opts.model !== undefined)
24
+ payload.model = opts.model;
25
+ if (opts.url !== undefined)
26
+ payload.url = opts.url;
27
+ if (opts.maxTokenCount !== undefined)
28
+ payload.max_token_count = opts.maxTokenCount;
29
+ if (opts.maxOutputTokenCount !== undefined) {
30
+ payload.max_output_token_count = opts.maxOutputTokenCount;
31
+ }
32
+ if (opts.key !== undefined) {
33
+ payload.default_key = opts.key === "-" ? await readKeyFromStdin() : opts.key;
34
+ }
35
+ const spinner = ora("Creating...").start();
36
+ try {
37
+ const client = getSdkClient(apiKey);
38
+ const result = await client.models.create(payload);
39
+ spinner.stop();
40
+ printSuccess("Model created successfully!");
41
+ printJson(result);
42
+ }
43
+ catch (e) {
44
+ spinner.stop();
45
+ handleSdkError(e);
46
+ }
47
+ });
48
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerDeleteCommand(model: Command): void;
3
+ //# sourceMappingURL=delete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/commands/model/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAoC1D"}
@@ -0,0 +1,37 @@
1
+ import ora from "ora";
2
+ import { requireApiKey, getSdkClient } from "../../auth.js";
3
+ import { printSuccess, handleSdkError } from "../../output.js";
4
+ import { CliError } from "../../types.js";
5
+ export function registerDeleteCommand(model) {
6
+ model
7
+ .command("delete <modelId>")
8
+ .description("Delete a custom model by its ID")
9
+ .option("--yes", "Skip confirmation prompt")
10
+ .action(async (modelId, opts) => {
11
+ const apiKey = await requireApiKey();
12
+ if (!opts.yes) {
13
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
14
+ throw new CliError(1, "Refusing to prompt for confirmation in a non-interactive environment. Pass --yes to skip the prompt.");
15
+ }
16
+ const { confirm } = await import("@inquirer/prompts");
17
+ const ok = await confirm({
18
+ message: "Are you sure you want to delete this model?",
19
+ default: false,
20
+ });
21
+ if (!ok) {
22
+ throw new CliError(1, "Aborted");
23
+ }
24
+ }
25
+ const spinner = ora("Deleting...").start();
26
+ try {
27
+ const client = getSdkClient(apiKey);
28
+ await client.models.delete(modelId);
29
+ spinner.stop();
30
+ printSuccess(`Model ${modelId} deleted successfully.`);
31
+ }
32
+ catch (e) {
33
+ spinner.stop();
34
+ handleSdkError(e);
35
+ }
36
+ });
37
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerGetCommand(model: Command): void;
3
+ //# sourceMappingURL=get.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../../../src/commands/model/get.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAkBvD"}
@@ -0,0 +1,23 @@
1
+ import ora from "ora";
2
+ import { requireApiKey, getSdkClient } from "../../auth.js";
3
+ import { printSuccess, printJson, handleSdkError } from "../../output.js";
4
+ export function registerGetCommand(model) {
5
+ model
6
+ .command("get <modelId>")
7
+ .description("Get a specific model by its ID")
8
+ .action(async (modelId) => {
9
+ const apiKey = await requireApiKey();
10
+ const spinner = ora("Fetching...").start();
11
+ try {
12
+ const client = getSdkClient(apiKey);
13
+ const result = await client.models.get(modelId);
14
+ spinner.stop();
15
+ printSuccess(`Model '${result.name}' details:`);
16
+ printJson(result);
17
+ }
18
+ catch (e) {
19
+ spinner.stop();
20
+ handleSdkError(e);
21
+ }
22
+ });
23
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerModelCommands(program: Command): void;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/model/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAU5D"}
@@ -0,0 +1,15 @@
1
+ import { Command } from "commander";
2
+ import { registerListCommand } from "./list.js";
3
+ import { registerGetCommand } from "./get.js";
4
+ import { registerCreateCommand } from "./create.js";
5
+ import { registerUpdateCommand } from "./update.js";
6
+ import { registerDeleteCommand } from "./delete.js";
7
+ export function registerModelCommands(program) {
8
+ const model = new Command("model").description("Custom model management commands");
9
+ registerListCommand(model);
10
+ registerGetCommand(model);
11
+ registerCreateCommand(model);
12
+ registerUpdateCommand(model);
13
+ registerDeleteCommand(model);
14
+ program.addCommand(model);
15
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerListCommand(model: Command): void;
3
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/model/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAgCxD"}
@@ -0,0 +1,36 @@
1
+ import ora from "ora";
2
+ import { requireApiKey, getSdkClient } from "../../auth.js";
3
+ import { printMessage, printModelTable, handleSdkError } from "../../output.js";
4
+ export function registerListCommand(model) {
5
+ model
6
+ .command("list")
7
+ .description("List models")
8
+ .option("--page-size <number>", "Number of results to return per page", parseInt)
9
+ .option("--cursor <cursor>", "The pagination cursor value")
10
+ .option("--ordering <field>", "Which field to use for ordering the results")
11
+ .action(async (opts) => {
12
+ const apiKey = await requireApiKey();
13
+ const params = {};
14
+ if (opts.pageSize !== undefined)
15
+ params.page_size = opts.pageSize;
16
+ if (opts.cursor !== undefined)
17
+ params.cursor = opts.cursor;
18
+ if (opts.ordering !== undefined)
19
+ params.ordering = opts.ordering;
20
+ const spinner = ora("Fetching...").start();
21
+ try {
22
+ const client = getSdkClient(apiKey);
23
+ const response = await client.models.list(params);
24
+ spinner.stop();
25
+ if (!response.results.length) {
26
+ printMessage("No models found.");
27
+ return;
28
+ }
29
+ printModelTable(response.results, response.next);
30
+ }
31
+ catch (e) {
32
+ spinner.stop();
33
+ handleSdkError(e);
34
+ }
35
+ });
36
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerUpdateCommand(model: Command): void;
3
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/commands/model/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAyD1D"}
@@ -0,0 +1,54 @@
1
+ import ora from "ora";
2
+ import { requireApiKey, getSdkClient } from "../../auth.js";
3
+ import { printInfo, printSuccess, printJson, handleSdkError } from "../../output.js";
4
+ async function readKeyFromStdin() {
5
+ const chunks = [];
6
+ for await (const chunk of process.stdin)
7
+ chunks.push(chunk);
8
+ return Buffer.concat(chunks).toString().trim();
9
+ }
10
+ export function registerUpdateCommand(model) {
11
+ model
12
+ .command("update <modelId>")
13
+ .description("Update an existing model (PATCH — only provided fields are sent)")
14
+ .option("--name <name>", "New model name")
15
+ .option("--model <model>", "Underlying model identifier")
16
+ .option("--url <url>", "API base URL")
17
+ .option("--key <value>", "Provider API key. Pass '-' to read the key from stdin (avoids shell history leak)")
18
+ .option("--max-token-count <number>", "Maximum total tokens", parseInt)
19
+ .option("--max-output-token-count <number>", "Maximum output tokens", parseInt)
20
+ .action(async (modelId, opts) => {
21
+ const apiKey = await requireApiKey();
22
+ const payload = {};
23
+ if (opts.name !== undefined)
24
+ payload.name = opts.name;
25
+ if (opts.model !== undefined)
26
+ payload.model = opts.model;
27
+ if (opts.url !== undefined)
28
+ payload.url = opts.url;
29
+ if (opts.maxTokenCount !== undefined)
30
+ payload.max_token_count = opts.maxTokenCount;
31
+ if (opts.maxOutputTokenCount !== undefined) {
32
+ payload.max_output_token_count = opts.maxOutputTokenCount;
33
+ }
34
+ if (opts.key !== undefined) {
35
+ payload.default_key = opts.key === "-" ? await readKeyFromStdin() : opts.key;
36
+ }
37
+ if (Object.keys(payload).length === 0) {
38
+ printInfo("No update parameters provided. Aborting.");
39
+ return;
40
+ }
41
+ const spinner = ora("Updating...").start();
42
+ try {
43
+ const client = getSdkClient(apiKey);
44
+ const result = await client.models.patch(modelId, payload);
45
+ spinner.stop();
46
+ printSuccess(`Model ${modelId} updated successfully!`);
47
+ printJson(result);
48
+ }
49
+ catch (e) {
50
+ spinner.stop();
51
+ handleSdkError(e);
52
+ }
53
+ });
54
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmDpC,wBAAgB,SAAS,IAAI,OAAO,CAuBnC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoDpC,wBAAgB,SAAS,IAAI,OAAO,CAwBnC"}
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import { registerSkillsAddCommand } from "./commands/skills-add.js";
13
13
  import { registerFileCommands } from "./commands/file/index.js";
14
14
  import { registerOtelFilterCommands } from "./commands/otel-filter/index.js";
15
15
  import { registerOtelTraceCommands } from "./commands/otel-trace/index.js";
16
+ import { registerModelCommands } from "./commands/model/index.js";
16
17
  const { version } = createRequire(import.meta.url)("../package.json");
17
18
  function buildBanner(ver) {
18
19
  const logo = chalk.hex("#4D9FFF");
@@ -67,6 +68,7 @@ export function createCli() {
67
68
  registerFileCommands(program);
68
69
  registerOtelFilterCommands(program);
69
70
  registerOtelTraceCommands(program);
71
+ registerModelCommands(program);
70
72
  return program;
71
73
  }
72
74
  if (realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
package/dist/output.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Judge, EvaluatorListItem, ExecutionLogList } from "@root-signals/scorable";
1
+ import type { Judge, EvaluatorListItem, ExecutionLogList, ModelList } from "@root-signals/scorable";
2
2
  export declare function printJson(data: unknown): void;
3
3
  export declare function printError(msg: string): void;
4
4
  export declare function handleSdkError(e: unknown): never;
@@ -8,5 +8,6 @@ export declare function printWarning(msg: string): void;
8
8
  export declare function printMessage(msg: string): void;
9
9
  export declare function printJudgeTable(judges: Judge[], nextCursor?: string): void;
10
10
  export declare function printEvaluatorTable(evaluators: EvaluatorListItem[], nextCursor?: string): void;
11
+ export declare function printModelTable(models: ModelList[], nextCursor?: string): void;
11
12
  export declare function printExecutionLogTable(logs: ExecutionLogList[], nextCursor?: string): void;
12
13
  //# sourceMappingURL=output.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAoBzF,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAE7C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,KAAK,CAKhD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAMD,wBAAgB,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAmB1E;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAmB9F;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAoB1F"}
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAoBpG,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAE7C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,KAAK,CAKhD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAMD,wBAAgB,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAmB1E;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAmB9F;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAmB9E;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAoB1F"}
package/dist/output.js CHANGED
@@ -80,6 +80,23 @@ export function printEvaluatorTable(evaluators, nextCursor) {
80
80
  printInfo(`Next page available. Use --cursor "${cursor}"`);
81
81
  }
82
82
  }
83
+ export function printModelTable(models, nextCursor) {
84
+ const table = new Table({
85
+ head: ["ID", "Name", "Provider", "Visibility"].map((h) => chalk.bold.cyan(h)),
86
+ chars: UNICODE_CHARS,
87
+ colWidths: [38, 32, 22, 18],
88
+ wordWrap: true,
89
+ });
90
+ for (const m of models) {
91
+ const provider = m.provider?.name ?? "";
92
+ table.push([m.id, m.name, provider, m.visibility ?? ""]);
93
+ }
94
+ console.log(table.toString());
95
+ if (nextCursor) {
96
+ const cursor = nextCursor.split("cursor=")[1] ?? nextCursor;
97
+ printInfo(`Next page available. Use --cursor "${cursor}"`);
98
+ }
99
+ }
83
100
  export function printExecutionLogTable(logs, nextCursor) {
84
101
  const table = new Table({
85
102
  head: ["ID", "Item Name", "Type", "Score", "Created At"].map((h) => chalk.bold.cyan(h)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@root-signals/scorable-cli",
3
- "version": "0.9.1",
3
+ "version": "0.11.0",
4
4
  "description": "CLI for Scorable",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Scorable",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@inquirer/prompts": "^8.3.2",
35
- "@root-signals/scorable": "^0.6.1",
35
+ "@root-signals/scorable": "^0.8.0",
36
36
  "chalk": "^5.6.2",
37
37
  "cli-table3": "^0.6.5",
38
38
  "commander": "^14.0.3",