@routerlab/cli 0.0.1

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 (47) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +171 -0
  3. package/dist/commands/eval.d.ts +21 -0
  4. package/dist/commands/eval.d.ts.map +1 -0
  5. package/dist/commands/eval.js +104 -0
  6. package/dist/commands/eval.js.map +1 -0
  7. package/dist/commands/frontier.d.ts +29 -0
  8. package/dist/commands/frontier.d.ts.map +1 -0
  9. package/dist/commands/frontier.js +212 -0
  10. package/dist/commands/frontier.js.map +1 -0
  11. package/dist/commands/help.d.ts +3 -0
  12. package/dist/commands/help.d.ts.map +1 -0
  13. package/dist/commands/help.js +55 -0
  14. package/dist/commands/help.js.map +1 -0
  15. package/dist/commands/models.d.ts +7 -0
  16. package/dist/commands/models.d.ts.map +1 -0
  17. package/dist/commands/models.js +71 -0
  18. package/dist/commands/models.js.map +1 -0
  19. package/dist/commands/route.d.ts +7 -0
  20. package/dist/commands/route.d.ts.map +1 -0
  21. package/dist/commands/route.js +173 -0
  22. package/dist/commands/route.js.map +1 -0
  23. package/dist/commands/version.d.ts +8 -0
  24. package/dist/commands/version.d.ts.map +1 -0
  25. package/dist/commands/version.js +21 -0
  26. package/dist/commands/version.js.map +1 -0
  27. package/dist/errors.d.ts +15 -0
  28. package/dist/errors.d.ts.map +1 -0
  29. package/dist/errors.js +32 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +21 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/io.d.ts +33 -0
  36. package/dist/io.d.ts.map +1 -0
  37. package/dist/io.js +47 -0
  38. package/dist/io.js.map +1 -0
  39. package/dist/main.d.ts +14 -0
  40. package/dist/main.d.ts.map +1 -0
  41. package/dist/main.js +64 -0
  42. package/dist/main.js.map +1 -0
  43. package/dist/parse.d.ts +44 -0
  44. package/dist/parse.d.ts.map +1 -0
  45. package/dist/parse.js +125 -0
  46. package/dist/parse.js.map +1 -0
  47. package/package.json +30 -0
package/dist/errors.js ADDED
@@ -0,0 +1,32 @@
1
+ // errors.ts — canonical CLI exit codes + a typed error for graceful failures.
2
+ //
3
+ // Exit-code semantics (mirrored in the CLI's README so users can script
4
+ // against them):
5
+ //
6
+ // 0 — success
7
+ // 1 — no candidates pass the filters (engine ran, decided nothing routable)
8
+ // 2 — invalid input (bad flag, malformed value, missing required arg)
9
+ // 3 — downstream error (calibration file malformed, missing file on disk,
10
+ // provider runner failure, etc.)
11
+ //
12
+ // The CLI never throws raw `Error` to the user. Every user-visible failure
13
+ // is funneled through `CliError`, which carries the exit code and a clean
14
+ // message. `main()` catches and renders.
15
+ export const EXIT_SUCCESS = 0;
16
+ export const EXIT_NO_CANDIDATES = 1;
17
+ export const EXIT_INVALID_INPUT = 2;
18
+ export const EXIT_DOWNSTREAM = 3;
19
+ /**
20
+ * The one error type the CLI throws for expected, user-facing failures.
21
+ * Unknown exceptions still bubble up to `main()` and are mapped to
22
+ * `EXIT_DOWNSTREAM` with the underlying message preserved.
23
+ */
24
+ export class CliError extends Error {
25
+ code;
26
+ constructor(code, message) {
27
+ super(message);
28
+ this.name = "CliError";
29
+ this.code = code;
30
+ }
31
+ }
32
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,wEAAwE;AACxE,iBAAiB;AACjB,EAAE;AACF,gBAAgB;AAChB,8EAA8E;AAC9E,wEAAwE;AACxE,4EAA4E;AAC5E,uCAAuC;AACvC,EAAE;AACF,2EAA2E;AAC3E,0EAA0E;AAC1E,yCAAyC;AAEzC,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAC9B,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAQjC;;;;GAIG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,CAAc;IAE3B,YAAY,IAAiB,EAAE,OAAe;QAC5C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bun
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env bun
2
+ // @routerlab/cli — `route` command entrypoint.
3
+ //
4
+ // This file is the binary's argv shim. Real CLI logic lives in `main.ts`
5
+ // (and the per-subcommand modules) so it can be imported by tests without
6
+ // the side effect of consuming `process.argv`.
7
+ //
8
+ // We exit with `main()`'s return code so shell pipelines see the right
9
+ // status (0 success, 1 no candidates, 2 invalid input, 3 downstream error).
10
+ // See `./errors.ts` for the canonical exit-code definitions.
11
+ import { main } from "./main.js";
12
+ const code = await main({
13
+ argv: process.argv.slice(2),
14
+ stdout: process.stdout,
15
+ stderr: process.stderr,
16
+ stdin: process.stdin,
17
+ env: process.env,
18
+ cwd: process.cwd(),
19
+ });
20
+ process.exit(code);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,+CAA+C;AAC/C,EAAE;AACF,yEAAyE;AACzE,0EAA0E;AAC1E,+CAA+C;AAC/C,EAAE;AACF,uEAAuE;AACvE,4EAA4E;AAC5E,6DAA6D;AAE7D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC,MAAM;IACtB,MAAM,EAAE,OAAO,CAAC,MAAM;IACtB,KAAK,EAAE,OAAO,CAAC,KAAK;IACpB,GAAG,EAAE,OAAO,CAAC,GAAG;IAChB,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;CACnB,CAAC,CAAC;AAEH,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC"}
package/dist/io.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { Readable, Writable } from "node:stream";
2
+ export interface CliContext {
3
+ /** argv slice with the binary name and node entry already removed. */
4
+ argv: readonly string[];
5
+ /** Where normal output goes. Tests inject a `MemoryStream`. */
6
+ stdout: Writable;
7
+ /** Where error and diagnostic output goes. */
8
+ stderr: Writable;
9
+ /**
10
+ * Stdin handle. The `route` subcommand reads from this when `--input`
11
+ * is not supplied. Tests inject a primed `Readable` to simulate piping.
12
+ */
13
+ stdin: Readable;
14
+ /** Environment snapshot. Used to read `ROUTERLAB_*` overrides. */
15
+ env: NodeJS.ProcessEnv;
16
+ /** Working directory. Used to resolve relative `--input` paths. */
17
+ cwd: string;
18
+ }
19
+ /**
20
+ * Read stdin to completion as a UTF-8 string.
21
+ *
22
+ * Returns an empty string if stdin is a TTY (interactive shell, no pipe)
23
+ * — that way `route --task=qa --quality-bar=0.85` without `--input` and
24
+ * without a piped stdin doesn't hang forever waiting on the user.
25
+ */
26
+ export declare function readStdinToString(stdin: Readable): Promise<string>;
27
+ /**
28
+ * Convenience: write a string + newline to a stream. Centralizing this
29
+ * keeps the subcommands free of `\n` boilerplate and lets us swap the
30
+ * underlying writer (e.g. if we ever want to color-tag stderr).
31
+ */
32
+ export declare function writeLine(stream: Writable, line: string): void;
33
+ //# sourceMappingURL=io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,WAAW,UAAU;IACzB,sEAAsE;IACtE,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,+DAA+D;IAC/D,MAAM,EAAE,QAAQ,CAAC;IACjB,8CAA8C;IAC9C,MAAM,EAAE,QAAQ,CAAC;IACjB;;;OAGG;IACH,KAAK,EAAE,QAAQ,CAAC;IAChB,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBxE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE9D"}
package/dist/io.js ADDED
@@ -0,0 +1,47 @@
1
+ // io.ts — IO seam for the CLI.
2
+ //
3
+ // Every subcommand takes a `CliContext` instead of poking at `process.*`
4
+ // globals directly. This is the seam that makes the CLI fully unit-testable
5
+ // from `bun test` without spawning a child process: tests instantiate a
6
+ // `CliContext` with in-memory streams, pass in argv, and read the captured
7
+ // stdout/stderr after the call returns.
8
+ /**
9
+ * Read stdin to completion as a UTF-8 string.
10
+ *
11
+ * Returns an empty string if stdin is a TTY (interactive shell, no pipe)
12
+ * — that way `route --task=qa --quality-bar=0.85` without `--input` and
13
+ * without a piped stdin doesn't hang forever waiting on the user.
14
+ */
15
+ export async function readStdinToString(stdin) {
16
+ // Bun's stdin exposes `isTTY` (matches Node), so a TTY-attached caller
17
+ // doesn't block. Real pipes (`echo "hi" | route ...`) and injected
18
+ // memory streams in tests both have `isTTY` falsy.
19
+ const maybeTty = stdin;
20
+ if (maybeTty.isTTY === true) {
21
+ return "";
22
+ }
23
+ const chunks = [];
24
+ for await (const chunk of stdin) {
25
+ if (typeof chunk === "string") {
26
+ chunks.push(Buffer.from(chunk, "utf8"));
27
+ }
28
+ else if (chunk instanceof Uint8Array) {
29
+ chunks.push(Buffer.from(chunk));
30
+ }
31
+ else {
32
+ // Defensive: shouldn't happen on Node/Bun stdin, but keeps the
33
+ // function total-typed under `noImplicitAny`.
34
+ chunks.push(Buffer.from(String(chunk), "utf8"));
35
+ }
36
+ }
37
+ return Buffer.concat(chunks).toString("utf8");
38
+ }
39
+ /**
40
+ * Convenience: write a string + newline to a stream. Centralizing this
41
+ * keeps the subcommands free of `\n` boilerplate and lets us swap the
42
+ * underlying writer (e.g. if we ever want to color-tag stderr).
43
+ */
44
+ export function writeLine(stream, line) {
45
+ stream.write(line + "\n");
46
+ }
47
+ //# sourceMappingURL=io.js.map
package/dist/io.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.js","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,yEAAyE;AACzE,4EAA4E;AAC5E,wEAAwE;AACxE,2EAA2E;AAC3E,wCAAwC;AAsBxC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAe;IACrD,uEAAuE;IACvE,mEAAmE;IACnE,mDAAmD;IACnD,MAAM,QAAQ,GAAG,KAAuC,CAAC;IACzD,IAAI,QAAQ,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;QAChC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;QAC1C,CAAC;aAAM,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,+DAA+D;YAC/D,8CAA8C;YAC9C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,MAAgB,EAAE,IAAY;IACtD,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AAC5B,CAAC"}
package/dist/main.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { type CliContext } from "./io.ts";
2
+ /**
3
+ * Top-level entry point. Always resolves with an exit code; never throws.
4
+ *
5
+ * Lifecycle:
6
+ * 1. Extract first positional as the subcommand name.
7
+ * 2. Dispatch to that subcommand's `run*` function with a shifted argv.
8
+ * 3. Any `CliError` thrown by a subcommand is rendered to stderr with
9
+ * its embedded exit code returned verbatim.
10
+ * 4. Any other exception is mapped to `EXIT_DOWNSTREAM` with the
11
+ * original message preserved.
12
+ */
13
+ export declare function main(ctx: CliContext): Promise<number>;
14
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,UAAU,EAAa,MAAM,SAAS,CAAC;AAErD;;;;;;;;;;GAUG;AACH,wBAAsB,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAwC3D"}
package/dist/main.js ADDED
@@ -0,0 +1,64 @@
1
+ // main.ts — top-level CLI dispatcher.
2
+ //
3
+ // Reads the first argv positional, routes it to the matching subcommand
4
+ // module, and converts thrown errors into exit codes. Tests import this
5
+ // function directly (rather than spawning a subprocess) by passing a
6
+ // `CliContext` with in-memory streams.
7
+ //
8
+ // Exit codes are defined in `./errors.ts` and mirrored in the CLI README.
9
+ import { CliError, EXIT_DOWNSTREAM, EXIT_INVALID_INPUT } from "./errors.js";
10
+ import { runEval } from "./commands/eval.js";
11
+ import { runFrontier } from "./commands/frontier.js";
12
+ import { runHelp } from "./commands/help.js";
13
+ import { runModels } from "./commands/models.js";
14
+ import { runRoute } from "./commands/route.js";
15
+ import { runVersion } from "./commands/version.js";
16
+ import { writeLine } from "./io.js";
17
+ /**
18
+ * Top-level entry point. Always resolves with an exit code; never throws.
19
+ *
20
+ * Lifecycle:
21
+ * 1. Extract first positional as the subcommand name.
22
+ * 2. Dispatch to that subcommand's `run*` function with a shifted argv.
23
+ * 3. Any `CliError` thrown by a subcommand is rendered to stderr with
24
+ * its embedded exit code returned verbatim.
25
+ * 4. Any other exception is mapped to `EXIT_DOWNSTREAM` with the
26
+ * original message preserved.
27
+ */
28
+ export async function main(ctx) {
29
+ const [first, ...rest] = ctx.argv;
30
+ // No subcommand → show help and exit 0. This is the same behavior as
31
+ // `git` (interactive shells) and is friendlier than a usage error.
32
+ if (first === undefined || first === "--help" || first === "-h" || first === "help") {
33
+ return runHelp({ ...ctx, argv: rest });
34
+ }
35
+ const subCtx = { ...ctx, argv: rest };
36
+ try {
37
+ switch (first) {
38
+ case "route":
39
+ return await runRoute(subCtx);
40
+ case "frontier":
41
+ return runFrontier(subCtx);
42
+ case "models":
43
+ return runModels(subCtx);
44
+ case "eval":
45
+ return await runEval(subCtx);
46
+ case "version":
47
+ case "--version":
48
+ case "-v":
49
+ return runVersion(subCtx);
50
+ default:
51
+ throw new CliError(EXIT_INVALID_INPUT, `unknown subcommand "${first}". Run \`route help\` for usage.`);
52
+ }
53
+ }
54
+ catch (e) {
55
+ if (e instanceof CliError) {
56
+ writeLine(ctx.stderr, `error: ${e.message}`);
57
+ return e.code;
58
+ }
59
+ const msg = e instanceof Error ? e.message : String(e);
60
+ writeLine(ctx.stderr, `error: ${msg}`);
61
+ return EXIT_DOWNSTREAM;
62
+ }
63
+ }
64
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,qEAAqE;AACrE,uCAAuC;AACvC,EAAE;AACF,0EAA0E;AAE1E,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAmB,SAAS,EAAE,MAAM,SAAS,CAAC;AAErD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,GAAe;IACxC,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC;IAElC,qEAAqE;IACrE,mEAAmE;IACnE,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACpF,OAAO,OAAO,CAAC,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,MAAM,GAAe,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAElD,IAAI,CAAC;QACH,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,OAAO;gBACV,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;YAChC,KAAK,UAAU;gBACb,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;YAC7B,KAAK,QAAQ;gBACX,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC;YAC3B,KAAK,MAAM;gBACT,OAAO,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;YAC/B,KAAK,SAAS,CAAC;YACf,KAAK,WAAW,CAAC;YACjB,KAAK,IAAI;gBACP,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;YAC5B;gBACE,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,uBAAuB,KAAK,kCAAkC,CAC/D,CAAC;QACN,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;YAC1B,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAC7C,OAAO,CAAC,CAAC,IAAI,CAAC;QAChB,CAAC;QACD,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvD,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,GAAG,EAAE,CAAC,CAAC;QACvC,OAAO,eAAe,CAAC;IACzB,CAAC;AACH,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { Provider, TaskClass } from "@routerlab/core";
2
+ /**
3
+ * The value type `node:util`'s `parseArgs` returns per flag. We accept the
4
+ * full union (including the array case from `multiple: true`, which the
5
+ * CLI doesn't currently use) so we can swap a flag to multi-value later
6
+ * without rewriting validators. The validators below all reject arrays
7
+ * because none of the current flags are multi-value.
8
+ */
9
+ export type ParsedFlagValue = string | boolean | (string | boolean)[] | undefined;
10
+ /**
11
+ * Require a string-typed flag value and return it, else throw.
12
+ */
13
+ export declare function requireString(flag: string, value: ParsedFlagValue): string;
14
+ /**
15
+ * Parse a `--task=...` value into a `TaskClass`, else throw.
16
+ */
17
+ export declare function parseTask(value: ParsedFlagValue): TaskClass;
18
+ /**
19
+ * Parse a `--provider=...` value into a `Provider`, else throw.
20
+ * Unlike `parseTask` this one is optional because `models` accepts no filter.
21
+ */
22
+ export declare function parseProviderOptional(value: ParsedFlagValue): Provider | undefined;
23
+ /**
24
+ * Parse a `--quality-bar=...` value into a [0, 1] number, else throw.
25
+ *
26
+ * The engine itself also validates this, but doing it here lets us return
27
+ * exit code 2 (invalid input) consistently rather than depending on the
28
+ * engine's exception bubbling up to the catch-all that returns 3.
29
+ */
30
+ export declare function parseQualityBar(value: ParsedFlagValue): number;
31
+ /**
32
+ * Parse an optional numeric flag like `--max-cost-usd=0.005`. Returns
33
+ * `undefined` if the flag is absent.
34
+ */
35
+ export declare function parseNonNegativeFloatOptional(flag: string, value: ParsedFlagValue): number | undefined;
36
+ /**
37
+ * Parse an optional positive integer flag like `--n=20`.
38
+ */
39
+ export declare function parsePositiveIntOptional(flag: string, value: ParsedFlagValue): number | undefined;
40
+ /**
41
+ * Parse a `--format=table|json` value with a sensible default.
42
+ */
43
+ export declare function parseFormat(value: ParsedFlagValue, defaultValue?: "table" | "json"): "table" | "json";
44
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAwB3D;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,EAAE,GAAG,SAAS,CAAC;AAElF;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,eAAe,GACrB,MAAM,CAQR;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,CAS3D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,eAAe,GACrB,QAAQ,GAAG,SAAS,CAYtB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAgB9D;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,eAAe,GACrB,MAAM,GAAG,SAAS,CAmBpB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,eAAe,GACrB,MAAM,GAAG,SAAS,CAapB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,eAAe,EACtB,YAAY,GAAE,OAAO,GAAG,MAAgB,GACvC,OAAO,GAAG,MAAM,CASlB"}
package/dist/parse.js ADDED
@@ -0,0 +1,125 @@
1
+ // parse.ts — shared argument validation helpers.
2
+ //
3
+ // Every subcommand parses flags with `node:util` `parseArgs`. The helpers
4
+ // here turn the raw `string | undefined` values into validated, typed
5
+ // values, throwing `CliError(EXIT_INVALID_INPUT, ...)` on bad input.
6
+ //
7
+ // Centralizing the validation keeps the subcommands focused on rendering
8
+ // and ensures the user gets the same error message for the same kind of
9
+ // mistake regardless of which subcommand they're running.
10
+ import { CliError, EXIT_INVALID_INPUT } from "./errors.js";
11
+ const VALID_TASKS = new Set([
12
+ "qa",
13
+ "codegen",
14
+ "summarization",
15
+ "classification",
16
+ "reasoning",
17
+ ]);
18
+ const VALID_PROVIDERS = new Set([
19
+ "anthropic",
20
+ "openai",
21
+ "google",
22
+ "groq",
23
+ "together",
24
+ "hf",
25
+ "openrouter",
26
+ ]);
27
+ const VALID_FORMATS = new Set(["table", "json"]);
28
+ /**
29
+ * Require a string-typed flag value and return it, else throw.
30
+ */
31
+ export function requireString(flag, value) {
32
+ if (typeof value !== "string" || value.length === 0) {
33
+ throw new CliError(EXIT_INVALID_INPUT, `missing required flag --${flag}`);
34
+ }
35
+ return value;
36
+ }
37
+ /**
38
+ * Parse a `--task=...` value into a `TaskClass`, else throw.
39
+ */
40
+ export function parseTask(value) {
41
+ const raw = requireString("task", value);
42
+ if (!VALID_TASKS.has(raw)) {
43
+ throw new CliError(EXIT_INVALID_INPUT, `invalid --task "${raw}". Expected one of: ${[...VALID_TASKS].join(", ")}`);
44
+ }
45
+ return raw;
46
+ }
47
+ /**
48
+ * Parse a `--provider=...` value into a `Provider`, else throw.
49
+ * Unlike `parseTask` this one is optional because `models` accepts no filter.
50
+ */
51
+ export function parseProviderOptional(value) {
52
+ if (value === undefined)
53
+ return undefined;
54
+ if (typeof value !== "string" || value.length === 0) {
55
+ throw new CliError(EXIT_INVALID_INPUT, `--provider expects a value`);
56
+ }
57
+ if (!VALID_PROVIDERS.has(value)) {
58
+ throw new CliError(EXIT_INVALID_INPUT, `invalid --provider "${value}". Expected one of: ${[...VALID_PROVIDERS].join(", ")}`);
59
+ }
60
+ return value;
61
+ }
62
+ /**
63
+ * Parse a `--quality-bar=...` value into a [0, 1] number, else throw.
64
+ *
65
+ * The engine itself also validates this, but doing it here lets us return
66
+ * exit code 2 (invalid input) consistently rather than depending on the
67
+ * engine's exception bubbling up to the catch-all that returns 3.
68
+ */
69
+ export function parseQualityBar(value) {
70
+ const raw = requireString("quality-bar", value);
71
+ const n = Number(raw);
72
+ if (!Number.isFinite(n)) {
73
+ throw new CliError(EXIT_INVALID_INPUT, `invalid --quality-bar "${raw}": not a finite number`);
74
+ }
75
+ if (n < 0 || n > 1) {
76
+ throw new CliError(EXIT_INVALID_INPUT, `invalid --quality-bar "${raw}": must be in [0, 1]`);
77
+ }
78
+ return n;
79
+ }
80
+ /**
81
+ * Parse an optional numeric flag like `--max-cost-usd=0.005`. Returns
82
+ * `undefined` if the flag is absent.
83
+ */
84
+ export function parseNonNegativeFloatOptional(flag, value) {
85
+ if (value === undefined)
86
+ return undefined;
87
+ if (typeof value !== "string" || value.length === 0) {
88
+ throw new CliError(EXIT_INVALID_INPUT, `--${flag} expects a value`);
89
+ }
90
+ const n = Number(value);
91
+ if (!Number.isFinite(n)) {
92
+ throw new CliError(EXIT_INVALID_INPUT, `invalid --${flag} "${value}": not a finite number`);
93
+ }
94
+ if (n < 0) {
95
+ throw new CliError(EXIT_INVALID_INPUT, `invalid --${flag} "${value}": must be non-negative`);
96
+ }
97
+ return n;
98
+ }
99
+ /**
100
+ * Parse an optional positive integer flag like `--n=20`.
101
+ */
102
+ export function parsePositiveIntOptional(flag, value) {
103
+ if (value === undefined)
104
+ return undefined;
105
+ if (typeof value !== "string" || value.length === 0) {
106
+ throw new CliError(EXIT_INVALID_INPUT, `--${flag} expects a value`);
107
+ }
108
+ const n = Number(value);
109
+ if (!Number.isInteger(n) || n <= 0) {
110
+ throw new CliError(EXIT_INVALID_INPUT, `invalid --${flag} "${value}": must be a positive integer`);
111
+ }
112
+ return n;
113
+ }
114
+ /**
115
+ * Parse a `--format=table|json` value with a sensible default.
116
+ */
117
+ export function parseFormat(value, defaultValue = "table") {
118
+ if (value === undefined)
119
+ return defaultValue;
120
+ if (typeof value !== "string" || !VALID_FORMATS.has(value)) {
121
+ throw new CliError(EXIT_INVALID_INPUT, `invalid --format "${String(value)}". Expected one of: ${[...VALID_FORMATS].join(", ")}`);
122
+ }
123
+ return value;
124
+ }
125
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,0EAA0E;AAC1E,sEAAsE;AACtE,qEAAqE;AACrE,EAAE;AACF,yEAAyE;AACzE,wEAAwE;AACxE,0DAA0D;AAI1D,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,WAAW,GAA2B,IAAI,GAAG,CAAC;IAClD,IAAI;IACJ,SAAS;IACT,eAAe;IACf,gBAAgB;IAChB,WAAW;CACZ,CAAC,CAAC;AAEH,MAAM,eAAe,GAA0B,IAAI,GAAG,CAAC;IACrD,WAAW;IACX,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,UAAU;IACV,IAAI;IACJ,YAAY;CACb,CAAC,CAAC;AAEH,MAAM,aAAa,GAAkC,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAWhF;;GAEG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,KAAsB;IAEtB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,2BAA2B,IAAI,EAAE,CAClC,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,KAAsB;IAC9C,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACzC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAgB,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,mBAAmB,GAAG,uBAAuB,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3E,CAAC;IACJ,CAAC;IACD,OAAO,GAAgB,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAsB;IAEtB,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,QAAQ,CAAC,kBAAkB,EAAE,4BAA4B,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,KAAiB,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,uBAAuB,KAAK,uBAAuB,CAAC,GAAG,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACrF,CAAC;IACJ,CAAC;IACD,OAAO,KAAiB,CAAC;AAC3B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,KAAsB;IACpD,MAAM,GAAG,GAAG,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,0BAA0B,GAAG,wBAAwB,CACtD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,0BAA0B,GAAG,sBAAsB,CACpD,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,6BAA6B,CAC3C,IAAY,EACZ,KAAsB;IAEtB,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,QAAQ,CAAC,kBAAkB,EAAE,KAAK,IAAI,kBAAkB,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,aAAa,IAAI,KAAK,KAAK,wBAAwB,CACpD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACV,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,aAAa,IAAI,KAAK,KAAK,yBAAyB,CACrD,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAAY,EACZ,KAAsB;IAEtB,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,QAAQ,CAAC,kBAAkB,EAAE,KAAK,IAAI,kBAAkB,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,aAAa,IAAI,KAAK,KAAK,+BAA+B,CAC3D,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CACzB,KAAsB,EACtB,eAAiC,OAAO;IAExC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,YAAY,CAAC;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAyB,CAAC,EAAE,CAAC;QAC/E,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,qBAAqB,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,GAAG,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzF,CAAC;IACJ,CAAC;IACD,OAAO,KAAyB,CAAC;AACnC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@routerlab/cli",
3
+ "version": "0.0.1",
4
+ "description": "CLI for routerlab — cost-quality routing for LLM APIs.",
5
+ "license": "Apache-2.0",
6
+ "author": "Faraazuddin Mohammed",
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "bin": {
11
+ "route": "dist/index.js"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "README.md",
16
+ "LICENSE"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/faraa2m/routerlab.git",
21
+ "directory": "packages/cli"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc -p tsconfig.json",
25
+ "test": "bun test"
26
+ },
27
+ "dependencies": {
28
+ "@routerlab/core": "^0.0.1"
29
+ }
30
+ }