@danypops/jittor 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -77
- package/package.json +11 -14
- package/src/adapters/sqlite-metric-store.ts +11 -2
- package/src/adapters/sqlite-session-identity-store.ts +45 -0
- package/src/cli-commands/benchmarks.ts +140 -0
- package/src/cli-commands/compaction.ts +17 -0
- package/src/cli-commands/context.ts +49 -0
- package/src/cli-commands/metrics.ts +296 -0
- package/src/cli-commands/op.ts +40 -0
- package/src/cli-commands/route-args.ts +15 -0
- package/src/cli-commands/router.ts +207 -0
- package/src/cli-commands/service-daemon.ts +72 -0
- package/src/cli-commands/session.ts +42 -0
- package/src/cli-commands/support.ts +33 -0
- package/src/cli.ts +42 -769
- package/src/constants.ts +7 -0
- package/src/daemon.ts +13 -3
- package/src/db.ts +15 -1
- package/src/index.ts +137 -0
- package/src/operations/benchmark-operations.ts +12 -0
- package/src/operations/context-operations.ts +30 -0
- package/src/operations/metrics-operations.ts +77 -0
- package/src/operations/model-ranking-operations.ts +16 -0
- package/src/operations/router-operations.ts +19 -0
- package/src/operations/session-identity-operations.ts +15 -0
- package/src/operations/session-scope.ts +31 -0
- package/src/operations/types.ts +3 -0
- package/src/ports/metric-store.ts +2 -0
- package/src/ports/router-controller.ts +9 -9
- package/src/ports/session-identity-store.ts +5 -0
- package/src/providers/telemetry-sources.ts +2 -1
- package/src/router.ts +124 -67
- package/src/service.ts +60 -118
- package/src/session-identity-service.ts +55 -0
- package/docs/USAGE_PRIOR_ART.md +0 -64
- package/extension/src/benchmark-tui.ts +0 -105
- package/extension/src/footer.ts +0 -366
- package/extension/src/index.ts +0 -828
- package/extension/src/service-client.ts +0 -26
- package/extension/src/settings-tui.ts +0 -153
- package/extension/src/settings.ts +0 -103
- package/extension/src/tui.ts +0 -270
- package/extension/src/usage.ts +0 -320
package/src/cli.ts
CHANGED
|
@@ -1,100 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { execFileSync } from "node:child_process";
|
|
3
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { homedir } from "node:os";
|
|
5
|
-
import { dirname, join } from "node:path";
|
|
6
2
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import {
|
|
8
|
-
BENCHMARK_MAX_QUERY_LIMIT,
|
|
9
|
-
CLI_AVAILABLE_ROUTES_MAX,
|
|
10
|
-
CLI_METRICS_HUMAN_MAX_ROWS,
|
|
11
|
-
HUMAN_TEXT_FIELD_MAX_CHARACTERS,
|
|
12
|
-
MAX_QUERY_LIMIT,
|
|
13
|
-
MODEL_RANKING_DEFAULT_CONTEXT_WEIGHT,
|
|
14
|
-
MODEL_RANKING_DEFAULT_COST_WEIGHT,
|
|
15
|
-
MODEL_RANKING_DEFAULT_LATENCY_WEIGHT,
|
|
16
|
-
MODEL_RANKING_DEFAULT_QUALITY_WEIGHT,
|
|
17
|
-
MODEL_RANKING_DEFAULT_RELIABILITY_WEIGHT,
|
|
18
|
-
MODEL_RANKING_MAX_SOURCES,
|
|
19
|
-
SYSTEMD_UNIT_NAME,
|
|
20
|
-
MAX_USAGE_BUCKETS,
|
|
21
|
-
USAGE_MAX_DISTINCT_SCOPES,
|
|
22
|
-
} from "./constants.ts";
|
|
23
|
-
import { connectJittorClient, type JittorClient } from "./client.ts";
|
|
3
|
+
import { connectJittorClient } from "./client.ts";
|
|
24
4
|
import { serveMain } from "./daemon.ts";
|
|
25
|
-
import type {
|
|
26
|
-
import
|
|
27
|
-
import
|
|
28
|
-
import {
|
|
29
|
-
import
|
|
30
|
-
import {
|
|
31
|
-
import
|
|
32
|
-
import
|
|
33
|
-
import
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
export
|
|
39
|
-
bunBin: string;
|
|
40
|
-
cliPath: string;
|
|
41
|
-
codexAuthFile?: string;
|
|
42
|
-
openRouterBenchmarks?: boolean;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function renderSystemdUnit(options: SystemdUnitOptions): string {
|
|
46
|
-
return `[Unit]
|
|
47
|
-
Description=Jittor token optimizing router
|
|
48
|
-
After=default.target network-online.target
|
|
49
|
-
Wants=network-online.target
|
|
50
|
-
|
|
51
|
-
[Service]
|
|
52
|
-
Type=simple
|
|
53
|
-
ExecStart=${options.bunBin} ${options.cliPath} serve
|
|
54
|
-
${options.codexAuthFile ? `Environment="JITTOR_CODEX_AUTH_FILE=${options.codexAuthFile.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"\n` : ""}${options.openRouterBenchmarks ? "Environment=JITTOR_OPENROUTER_BENCHMARKS=1\n" : ""}Restart=always
|
|
55
|
-
RestartSec=2
|
|
56
|
-
NoNewPrivileges=true
|
|
57
|
-
PrivateTmp=true
|
|
58
|
-
|
|
59
|
-
[Install]
|
|
60
|
-
WantedBy=default.target
|
|
61
|
-
`;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface CliDependencies {
|
|
65
|
-
client: Pick<JittorClient, "call">;
|
|
66
|
-
stdout(line: string): void;
|
|
67
|
-
stderr(line: string): void;
|
|
68
|
-
systemctl(...args: string[]): void;
|
|
69
|
-
installService(): void;
|
|
70
|
-
serve(): void;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function systemctl(...args: string[]): void {
|
|
74
|
-
execFileSync("systemctl", ["--user", ...args], { stdio: "inherit" });
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function installService(): void {
|
|
78
|
-
const unitPath = resolveJittorPaths().systemdUnit;
|
|
79
|
-
mkdirSync(dirname(unitPath), { recursive: true });
|
|
80
|
-
const codexAuthFile = join(process.env["CODEX_HOME"] ?? join(homedir(), ".codex"), "auth.json");
|
|
81
|
-
writeFileSync(unitPath, renderSystemdUnit({
|
|
82
|
-
bunBin: process.execPath,
|
|
83
|
-
cliPath: fileURLToPath(import.meta.url),
|
|
84
|
-
...(existsSync(codexAuthFile) ? { codexAuthFile } : {}),
|
|
85
|
-
openRouterBenchmarks: process.env["JITTOR_OPENROUTER_BENCHMARKS"] === "1",
|
|
86
|
-
}));
|
|
87
|
-
systemctl("daemon-reload");
|
|
88
|
-
systemctl("enable", SYSTEMD_UNIT_NAME);
|
|
89
|
-
systemctl("restart", SYSTEMD_UNIT_NAME);
|
|
90
|
-
}
|
|
5
|
+
import type { CliDependencies } from "./cli-commands/support.ts";
|
|
6
|
+
import { installService, renderSystemdUnit, systemctl, runServiceCommand, SERVICE_USAGE_LINES } from "./cli-commands/service-daemon.ts";
|
|
7
|
+
import { runSessionCommand, SESSION_USAGE_LINES } from "./cli-commands/session.ts";
|
|
8
|
+
import { runMetricsCommand, METRICS_USAGE_LINES, formatMetricsQuery, formatCostByTask } from "./cli-commands/metrics.ts";
|
|
9
|
+
import { runTelemetryCommand, runRouterCommand, ROUTER_USAGE_LINES, formatRouterStatus } from "./cli-commands/router.ts";
|
|
10
|
+
import { runCompactionCommand } from "./cli-commands/compaction.ts";
|
|
11
|
+
import { runOpCommand, OP_USAGE_LINES } from "./cli-commands/op.ts";
|
|
12
|
+
import { runBenchmarksCommand, BENCHMARKS_USAGE_LINES } from "./cli-commands/benchmarks.ts";
|
|
13
|
+
import { runContextCommand, CONTEXT_USAGE_LINES, formatContextAssessment } from "./cli-commands/context.ts";
|
|
14
|
+
|
|
15
|
+
// Re-exported for external callers (tests, daemon.ts's systemd-unit test) that import these
|
|
16
|
+
// directly from cli.ts rather than reaching into src/cli-commands/*.
|
|
17
|
+
export type { CliDependencies };
|
|
18
|
+
export { renderSystemdUnit, formatMetricsQuery, formatCostByTask, formatRouterStatus, formatContextAssessment };
|
|
91
19
|
|
|
92
20
|
const DEFAULT_DEPENDENCIES: CliDependencies = {
|
|
93
21
|
get client() { return connectJittorClient(); },
|
|
94
22
|
stdout: console.log,
|
|
95
23
|
stderr: console.error,
|
|
96
24
|
systemctl,
|
|
97
|
-
installService,
|
|
25
|
+
installService: () => installService(fileURLToPath(import.meta.url)),
|
|
98
26
|
serve: serveMain,
|
|
99
27
|
};
|
|
100
28
|
|
|
@@ -102,694 +30,39 @@ function usage(stderr: (line: string) => void): number {
|
|
|
102
30
|
stderr([
|
|
103
31
|
"Usage: jittor <command> [options]",
|
|
104
32
|
" serve",
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
` metrics usage-series --source <s> --since <ms> --until <ms> --bucket-size-ms <ms> --bucket-count 1..${MAX_USAGE_BUCKETS} [--scope-limit 1..${USAGE_MAX_DISTINCT_SCOPES}] [--json]`,
|
|
113
|
-
" metrics cost-by-task --since <ms> --until <ms> [--json]",
|
|
114
|
-
" telemetry poll [--json]",
|
|
115
|
-
" compaction estimate [--json]",
|
|
116
|
-
" router <status|decide|pause|resume|clear-override> [--json]",
|
|
117
|
-
" router override --route <provider/model@thinking> [--expires-at <ms>] [--json]",
|
|
118
|
-
" router current-route --route <provider/model@thinking> [--json]",
|
|
119
|
-
" router available-routes [--route <provider/model@thinking> ...] [--json]",
|
|
120
|
-
" op <operation> [--input <json>]",
|
|
33
|
+
...SERVICE_USAGE_LINES,
|
|
34
|
+
...CONTEXT_USAGE_LINES,
|
|
35
|
+
...BENCHMARKS_USAGE_LINES,
|
|
36
|
+
...METRICS_USAGE_LINES,
|
|
37
|
+
...ROUTER_USAGE_LINES,
|
|
38
|
+
...SESSION_USAGE_LINES,
|
|
39
|
+
...OP_USAGE_LINES,
|
|
121
40
|
].join("\n"));
|
|
122
41
|
return 2;
|
|
123
42
|
}
|
|
124
43
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const value = raw === undefined ? Number.NaN : Number(raw);
|
|
134
|
-
if (!Number.isSafeInteger(value) || value < 0) return null;
|
|
135
|
-
if (argument === "--since") input.since = value;
|
|
136
|
-
else input.until = value;
|
|
137
|
-
}
|
|
138
|
-
if (input.since !== undefined && input.until !== undefined && input.until < input.since) return null;
|
|
139
|
-
return { input, json };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function parseRoute(raw: string | undefined): Route | null {
|
|
143
|
-
if (raw === undefined) return null;
|
|
144
|
-
return parseCandidate(raw);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
interface MetricsRecordArgs { input: MetricObservation; json: boolean }
|
|
148
|
-
|
|
149
|
-
function parseMetricsRecordArgs(args: string[]): MetricsRecordArgs | null {
|
|
150
|
-
let json = false;
|
|
151
|
-
let source: string | undefined;
|
|
152
|
-
let scope: string | undefined;
|
|
153
|
-
let metric: string | undefined;
|
|
154
|
-
let value: number | null | undefined;
|
|
155
|
-
let unit: MetricUnit | undefined;
|
|
156
|
-
let observedAt: number | undefined;
|
|
157
|
-
let attributes: Record<string, unknown> | undefined;
|
|
158
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
159
|
-
const argument = args[index];
|
|
160
|
-
if (argument === "--json") { json = true; continue; }
|
|
161
|
-
if (!["--source", "--scope", "--metric", "--value", "--unit", "--observed-at", "--attributes"].includes(argument ?? "")) return null;
|
|
162
|
-
const raw = args[++index];
|
|
163
|
-
if (raw === undefined || raw.length === 0) return null;
|
|
164
|
-
if (argument === "--source") source = raw;
|
|
165
|
-
else if (argument === "--scope") scope = raw;
|
|
166
|
-
else if (argument === "--metric") metric = raw;
|
|
167
|
-
else if (argument === "--value") {
|
|
168
|
-
if (raw.toLowerCase() === "null") value = null;
|
|
169
|
-
else {
|
|
170
|
-
const parsed = Number(raw);
|
|
171
|
-
if (!Number.isFinite(parsed)) return null;
|
|
172
|
-
value = parsed;
|
|
173
|
-
}
|
|
174
|
-
} else if (argument === "--unit") {
|
|
175
|
-
if (!METRIC_UNITS.includes(raw as MetricUnit)) return null;
|
|
176
|
-
unit = raw as MetricUnit;
|
|
177
|
-
} else if (argument === "--observed-at") {
|
|
178
|
-
const parsed = Number(raw);
|
|
179
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
|
|
180
|
-
observedAt = parsed;
|
|
181
|
-
} else {
|
|
182
|
-
try {
|
|
183
|
-
const parsed = JSON.parse(raw);
|
|
184
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
185
|
-
attributes = parsed as Record<string, unknown>;
|
|
186
|
-
} catch {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (source === undefined || scope === undefined || metric === undefined || value === undefined || unit === undefined) return null;
|
|
192
|
-
return {
|
|
193
|
-
json,
|
|
194
|
-
input: {
|
|
195
|
-
source, scope, metric, value, unit,
|
|
196
|
-
observedAt: observedAt ?? Date.now(),
|
|
197
|
-
...(attributes ? { attributes } : {}),
|
|
198
|
-
},
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
interface MetricsQueryArgs { input: MetricQuery; json: boolean }
|
|
203
|
-
|
|
204
|
-
function parseMetricsQueryArgs(args: string[]): MetricsQueryArgs | null {
|
|
205
|
-
let json = false;
|
|
206
|
-
const input: MetricQuery = {};
|
|
207
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
208
|
-
const argument = args[index];
|
|
209
|
-
if (argument === "--json") { json = true; continue; }
|
|
210
|
-
if (!["--source", "--scope", "--metric", "--since", "--until", "--limit", "--order"].includes(argument ?? "")) return null;
|
|
211
|
-
const raw = args[++index];
|
|
212
|
-
if (raw === undefined || raw.length === 0) return null;
|
|
213
|
-
if (argument === "--source") input.source = raw;
|
|
214
|
-
else if (argument === "--scope") input.scope = raw;
|
|
215
|
-
else if (argument === "--metric") input.metric = raw;
|
|
216
|
-
else if (argument === "--order") {
|
|
217
|
-
if (raw !== "asc" && raw !== "desc") return null;
|
|
218
|
-
input.order = raw;
|
|
219
|
-
} else {
|
|
220
|
-
const parsed = Number(raw);
|
|
221
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
|
|
222
|
-
if (argument === "--since") input.since = parsed;
|
|
223
|
-
else if (argument === "--until") input.until = parsed;
|
|
224
|
-
else {
|
|
225
|
-
if (parsed < 1 || parsed > MAX_QUERY_LIMIT) return null;
|
|
226
|
-
input.limit = parsed;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
if (input.since !== undefined && input.until !== undefined && input.until < input.since) return null;
|
|
231
|
-
return { input, json };
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
interface MetricsDistinctScopesArgs { input: { source: string; since: number; until: number; limit?: number }; json: boolean }
|
|
235
|
-
|
|
236
|
-
function parseMetricsDistinctScopesArgs(args: string[]): MetricsDistinctScopesArgs | null {
|
|
237
|
-
let json = false;
|
|
238
|
-
let source: string | undefined;
|
|
239
|
-
let since: number | undefined;
|
|
240
|
-
let until: number | undefined;
|
|
241
|
-
let limit: number | undefined;
|
|
242
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
243
|
-
const argument = args[index];
|
|
244
|
-
if (argument === "--json") { json = true; continue; }
|
|
245
|
-
if (!["--source", "--since", "--until", "--limit"].includes(argument ?? "")) return null;
|
|
246
|
-
const raw = args[++index];
|
|
247
|
-
if (raw === undefined || raw.length === 0) return null;
|
|
248
|
-
if (argument === "--source") { source = raw; continue; }
|
|
249
|
-
const parsed = Number(raw);
|
|
250
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
|
|
251
|
-
if (argument === "--since") since = parsed;
|
|
252
|
-
else if (argument === "--until") until = parsed;
|
|
253
|
-
else {
|
|
254
|
-
if (parsed < 1 || parsed > USAGE_MAX_DISTINCT_SCOPES) return null;
|
|
255
|
-
limit = parsed;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
if (source === undefined || since === undefined || until === undefined || until < since) return null;
|
|
259
|
-
return { input: { source, since, until, ...(limit === undefined ? {} : { limit }) }, json };
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
interface MetricsUsageSeriesArgs { input: { source: string; since: number; until: number; bucketSizeMs: number; bucketCount: number; scopeLimit?: number }; json: boolean }
|
|
263
|
-
|
|
264
|
-
function parseMetricsUsageSeriesArgs(args: string[]): MetricsUsageSeriesArgs | null {
|
|
265
|
-
let json = false;
|
|
266
|
-
let source: string | undefined;
|
|
267
|
-
let since: number | undefined;
|
|
268
|
-
let until: number | undefined;
|
|
269
|
-
let bucketSizeMs: number | undefined;
|
|
270
|
-
let bucketCount: number | undefined;
|
|
271
|
-
let scopeLimit: number | undefined;
|
|
272
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
273
|
-
const argument = args[index];
|
|
274
|
-
if (argument === "--json") { json = true; continue; }
|
|
275
|
-
if (!(["--source", "--since", "--until", "--bucket-size-ms", "--bucket-count", "--scope-limit"].includes(argument ?? ""))) return null;
|
|
276
|
-
const raw = args[++index];
|
|
277
|
-
if (raw === undefined || raw.length === 0) return null;
|
|
278
|
-
if (argument === "--source") { source = raw; continue; }
|
|
279
|
-
const parsed = Number(raw);
|
|
280
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
|
|
281
|
-
if (argument === "--since") since = parsed;
|
|
282
|
-
else if (argument === "--until") until = parsed;
|
|
283
|
-
else if (argument === "--bucket-size-ms") { if (parsed < 1) return null; bucketSizeMs = parsed; }
|
|
284
|
-
else if (argument === "--bucket-count") { if (parsed < 1 || parsed > MAX_USAGE_BUCKETS) return null; bucketCount = parsed; }
|
|
285
|
-
else { if (parsed < 1 || parsed > USAGE_MAX_DISTINCT_SCOPES) return null; scopeLimit = parsed; }
|
|
286
|
-
}
|
|
287
|
-
if (source === undefined || since === undefined || until === undefined || until < since || bucketSizeMs === undefined || bucketCount === undefined) return null;
|
|
288
|
-
return { input: { source, since, until, bucketSizeMs, bucketCount, ...(scopeLimit === undefined ? {} : { scopeLimit }) }, json };
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
interface CostByTaskArgs { input: { since: number; until: number }; json: boolean }
|
|
292
|
-
|
|
293
|
-
function parseCostByTaskArgs(args: string[]): CostByTaskArgs | null {
|
|
294
|
-
let json = false;
|
|
295
|
-
let since: number | undefined;
|
|
296
|
-
let until: number | undefined;
|
|
297
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
298
|
-
const argument = args[index];
|
|
299
|
-
if (argument === "--json") { json = true; continue; }
|
|
300
|
-
if (!["--since", "--until"].includes(argument ?? "")) return null;
|
|
301
|
-
const raw = args[++index];
|
|
302
|
-
const parsed = Number(raw);
|
|
303
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
|
|
304
|
-
if (argument === "--since") since = parsed;
|
|
305
|
-
else until = parsed;
|
|
306
|
-
}
|
|
307
|
-
if (since === undefined || until === undefined || until < since) return null;
|
|
308
|
-
return { input: { since, until }, json };
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
interface MetricsPruneArgs { input: { before: number; force?: boolean }; json: boolean }
|
|
312
|
-
|
|
313
|
-
function parseMetricsPruneArgs(args: string[]): MetricsPruneArgs | null {
|
|
314
|
-
let json = false;
|
|
315
|
-
let force = false;
|
|
316
|
-
let before: number | undefined;
|
|
317
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
318
|
-
const argument = args[index];
|
|
319
|
-
if (argument === "--json") { json = true; continue; }
|
|
320
|
-
if (argument === "--force") { force = true; continue; }
|
|
321
|
-
if (argument !== "--before") return null;
|
|
322
|
-
const raw = args[++index];
|
|
323
|
-
const parsed = Number(raw);
|
|
324
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
|
|
325
|
-
before = parsed;
|
|
326
|
-
}
|
|
327
|
-
if (before === undefined) return null;
|
|
328
|
-
return { input: { before, ...(force ? { force } : {}) }, json };
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
interface RouterOverrideArgs { input: RouteOverride; json: boolean }
|
|
332
|
-
|
|
333
|
-
function parseRouterOverrideArgs(args: string[]): RouterOverrideArgs | null {
|
|
334
|
-
let json = false;
|
|
335
|
-
let route: Route | null = null;
|
|
336
|
-
let expiresAt: number | null = null;
|
|
337
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
338
|
-
const argument = args[index];
|
|
339
|
-
if (argument === "--json") { json = true; continue; }
|
|
340
|
-
if (!["--route", "--expires-at"].includes(argument ?? "")) return null;
|
|
341
|
-
const raw = args[++index];
|
|
342
|
-
if (raw === undefined || raw.length === 0) return null;
|
|
343
|
-
if (argument === "--route") {
|
|
344
|
-
route = parseRoute(raw);
|
|
345
|
-
if (!route) return null;
|
|
346
|
-
} else {
|
|
347
|
-
const parsed = Number(raw);
|
|
348
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
|
|
349
|
-
expiresAt = parsed;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
if (!route) return null;
|
|
353
|
-
return { input: { route, expiresAt }, json };
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
interface RouterRouteArgs { input: Route; json: boolean }
|
|
357
|
-
|
|
358
|
-
function parseRouterRouteArgs(args: string[]): RouterRouteArgs | null {
|
|
359
|
-
let json = false;
|
|
360
|
-
let route: Route | null = null;
|
|
361
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
362
|
-
const argument = args[index];
|
|
363
|
-
if (argument === "--json") { json = true; continue; }
|
|
364
|
-
if (argument !== "--route") return null;
|
|
365
|
-
const raw = args[++index];
|
|
366
|
-
if (raw === undefined) return null;
|
|
367
|
-
route = parseRoute(raw);
|
|
368
|
-
if (!route) return null;
|
|
369
|
-
}
|
|
370
|
-
if (!route) return null;
|
|
371
|
-
return { input: route, json };
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
interface RouterAvailableRoutesArgs { input: { routes: Route[] }; json: boolean }
|
|
375
|
-
|
|
376
|
-
function parseRouterAvailableRoutesArgs(args: string[]): RouterAvailableRoutesArgs | null {
|
|
377
|
-
let json = false;
|
|
378
|
-
const routes: Route[] = [];
|
|
379
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
380
|
-
const argument = args[index];
|
|
381
|
-
if (argument === "--json") { json = true; continue; }
|
|
382
|
-
if (argument !== "--route") return null;
|
|
383
|
-
const raw = args[++index];
|
|
384
|
-
if (raw === undefined) return null;
|
|
385
|
-
const route = parseRoute(raw);
|
|
386
|
-
if (!route) return null;
|
|
387
|
-
if (routes.length >= CLI_AVAILABLE_ROUTES_MAX) return null;
|
|
388
|
-
routes.push(route);
|
|
389
|
-
}
|
|
390
|
-
return { input: { routes }, json };
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function parseJsonOnlyArgs(args: string[]): { json: boolean } | null {
|
|
394
|
-
let json = false;
|
|
395
|
-
for (const argument of args) {
|
|
396
|
-
if (argument !== "--json") return null;
|
|
397
|
-
json = true;
|
|
398
|
-
}
|
|
399
|
-
return { json };
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
function parseOpArgs(args: string[]): { operation: OperationName; input: Record<string, unknown> } | null {
|
|
403
|
-
const [operation, ...rest] = args;
|
|
404
|
-
if (operation === undefined || !EXPECTED_OPERATION_NAMES.includes(operation as OperationName)) return null;
|
|
405
|
-
let input: Record<string, unknown> = {};
|
|
406
|
-
for (let index = 0; index < rest.length; index += 1) {
|
|
407
|
-
if (rest[index] !== "--input") return null;
|
|
408
|
-
const raw = rest[++index];
|
|
409
|
-
if (raw === undefined) return null;
|
|
410
|
-
try {
|
|
411
|
-
const parsed = JSON.parse(raw);
|
|
412
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
413
|
-
input = parsed as Record<string, unknown>;
|
|
414
|
-
} catch {
|
|
415
|
-
return null;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
return { operation: operation as OperationName, input };
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
interface BenchmarkArgs {
|
|
422
|
-
action: "status" | "refresh" | "list" | "rank";
|
|
423
|
-
json: boolean;
|
|
424
|
-
force: boolean;
|
|
425
|
-
query?: BenchmarkQuery;
|
|
426
|
-
recommendation?: ModelRecommendationInput;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function parseCandidate(raw: string): ModelCandidate | null {
|
|
430
|
-
const separator = raw.indexOf("/");
|
|
431
|
-
const thinkingSeparator = raw.lastIndexOf("@");
|
|
432
|
-
if (separator <= 0 || thinkingSeparator <= separator + 1 || thinkingSeparator === raw.length - 1) return null;
|
|
433
|
-
return { provider: raw.slice(0, separator), model: raw.slice(separator + 1, thinkingSeparator), thinking: raw.slice(thinkingSeparator + 1) };
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
function parseBenchmarkArgs(action: string | undefined, args: string[]): BenchmarkArgs | null {
|
|
437
|
-
if (action !== "status" && action !== "refresh" && action !== "list" && action !== "rank") return null;
|
|
438
|
-
let json = false;
|
|
439
|
-
let force = false;
|
|
440
|
-
const query: Partial<BenchmarkQuery> = {};
|
|
441
|
-
const candidates: ModelCandidate[] = [];
|
|
442
|
-
const sourceIds: string[] = [];
|
|
443
|
-
let scopeAuthority: ScopeAuthority = "available-models";
|
|
444
|
-
let domain: ModelTaskDomain = "general";
|
|
445
|
-
let type: ModelTaskType = "general";
|
|
446
|
-
let budgetPressure = 0;
|
|
447
|
-
const weights: UtilityWeights = {
|
|
448
|
-
quality: MODEL_RANKING_DEFAULT_QUALITY_WEIGHT, cost: MODEL_RANKING_DEFAULT_COST_WEIGHT,
|
|
449
|
-
latency: MODEL_RANKING_DEFAULT_LATENCY_WEIGHT, context: MODEL_RANKING_DEFAULT_CONTEXT_WEIGHT,
|
|
450
|
-
reliability: MODEL_RANKING_DEFAULT_RELIABILITY_WEIGHT,
|
|
451
|
-
};
|
|
452
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
453
|
-
const argument = args[index];
|
|
454
|
-
if (argument === "--json") { json = true; continue; }
|
|
455
|
-
if (argument === "--force" && action === "refresh") { force = true; continue; }
|
|
456
|
-
const allowed = action === "list" ? ["--source", "--model", "--dimension", "--limit"]
|
|
457
|
-
: action === "rank" ? ["--candidate", "--source", "--domain", "--type", "--scope", "--budget", "--weight-quality", "--weight-cost", "--weight-latency", "--weight-context", "--weight-reliability"] : [];
|
|
458
|
-
if (!allowed.includes(argument ?? "")) return null;
|
|
459
|
-
const raw = args[++index];
|
|
460
|
-
if (raw === undefined || raw.length === 0) return null;
|
|
461
|
-
if (action === "list") {
|
|
462
|
-
if (argument === "--limit") {
|
|
463
|
-
const limit = Number(raw);
|
|
464
|
-
if (!Number.isSafeInteger(limit) || limit < 1 || limit > BENCHMARK_MAX_QUERY_LIMIT) return null;
|
|
465
|
-
query.limit = limit;
|
|
466
|
-
} else if (argument === "--source") query.sourceId = raw;
|
|
467
|
-
else if (argument === "--model") query.model = raw;
|
|
468
|
-
else query.dimension = raw;
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
if (argument === "--candidate") {
|
|
472
|
-
const candidate = parseCandidate(raw);
|
|
473
|
-
if (!candidate) return null;
|
|
474
|
-
candidates.push(candidate);
|
|
475
|
-
} else if (argument === "--source") sourceIds.push(raw);
|
|
476
|
-
else if (argument === "--domain") {
|
|
477
|
-
if (!TASK_DOMAINS.includes(raw as ModelTaskDomain)) return null;
|
|
478
|
-
domain = raw as ModelTaskDomain;
|
|
479
|
-
} else if (argument === "--type") {
|
|
480
|
-
if (!TASK_TYPES.includes(raw as ModelTaskType)) return null;
|
|
481
|
-
type = raw as ModelTaskType;
|
|
482
|
-
} else if (argument === "--scope") {
|
|
483
|
-
if (raw !== "exact-session" && raw !== "available-models") return null;
|
|
484
|
-
scopeAuthority = raw;
|
|
485
|
-
} else if (argument === "--budget") budgetPressure = Number(raw);
|
|
486
|
-
else {
|
|
487
|
-
const weight = Number(raw);
|
|
488
|
-
if (!Number.isFinite(weight) || weight < 0 || weight > 10) return null;
|
|
489
|
-
weights[argument!.slice("--weight-".length) as keyof UtilityWeights] = weight;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
if (action === "list" && query.sourceId === undefined) return null;
|
|
493
|
-
if (action === "rank" && (candidates.length === 0 || sourceIds.length > MODEL_RANKING_MAX_SOURCES || !Number.isFinite(budgetPressure) || budgetPressure < 0 || budgetPressure > 2)) return null;
|
|
494
|
-
return {
|
|
495
|
-
action, json, force,
|
|
496
|
-
...(action === "list" ? { query: query as BenchmarkQuery } : {}),
|
|
497
|
-
...(action === "rank" ? { recommendation: { candidates, sourceIds: [...new Set(sourceIds)], scopeAuthority, domain, type, budgetPressure, weights } } : {}),
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
function value(value: number | null, suffix = ""): string {
|
|
502
|
-
return value === null ? "unknown" : `${Math.round(value).toLocaleString()}${suffix}`;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
export function formatBenchmarkStatus(result: BenchmarkRefreshResult): string {
|
|
506
|
-
if (result.sources.length === 0) return "Benchmark sources: none configured";
|
|
507
|
-
return ["Benchmark sources:", ...result.sources.map((source) => {
|
|
508
|
-
const state = source.ok === null ? "not refreshed" : source.ok ? "ready" : "refresh failed";
|
|
509
|
-
return `- ${source.id}: ${state} · ${source.observations.toLocaleString()} observations · ${source.hasEvidence ? "evidence retained" : "no evidence"}`;
|
|
510
|
-
})].join("\n");
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
function humanField(value: string): string {
|
|
514
|
-
return value.length <= HUMAN_TEXT_FIELD_MAX_CHARACTERS ? value : `${value.slice(0, HUMAN_TEXT_FIELD_MAX_CHARACTERS - 1)}…`;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
export function formatBenchmarkQuery(result: BenchmarkQueryResult): string {
|
|
518
|
-
return [
|
|
519
|
-
`Benchmark evidence: ${humanField(result.sourceId)} · ${result.completeness} · ${result.freshness} · ${result.observations.length.toLocaleString()} observations`,
|
|
520
|
-
...result.observations.map((observation) => `- ${humanField(observation.model.canonical)} · ${humanField(observation.dimension)} ${observation.value.toLocaleString()} ${observation.unit} · ${humanField(observation.provenance.publisher)} · confidence ${(observation.provenance.confidence * 100).toFixed(0)}%`),
|
|
521
|
-
].join("\n");
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
export function formatModelRanking(result: ModelRankingResult): string {
|
|
525
|
-
return [
|
|
526
|
-
`Model ranking: ${result.completeness} · scope ${result.scopeAuthority}${result.scopeWarning ? " · advisory only" : ""}`,
|
|
527
|
-
...result.ranked.map((item, index) => `${index + 1}. ${humanField(item.identity)} · utility ${item.utility === null ? "unknown" : item.utility.toFixed(3)} · confidence ${(item.confidence * 100).toFixed(0)}%`),
|
|
528
|
-
...(result.scopeWarning ? [result.scopeWarning] : []),
|
|
529
|
-
].join("\n");
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
export function formatContextAssessment(summary: ContextAssessment): string {
|
|
533
|
-
return [
|
|
534
|
-
`Context assessment: ${summary.completeness}`,
|
|
535
|
-
`Papyrus injection: ${summary.injection.runs} runs · avg ${value(summary.injection.averageCharacters, " chars")} · p95 ${value(summary.injection.p95Characters, " chars")} · max ${value(summary.injection.maxCharacters, " chars")}`,
|
|
536
|
-
`Injection mix: rules ${summary.injection.ruleCharacters.toLocaleString()} chars · tasks ${summary.injection.taskCharacters.toLocaleString()} chars · estimated ${summary.injection.estimatedTokens.toLocaleString()} tokens · unchanged ${summary.injection.unchangedRate === null ? "unknown" : `${(summary.injection.unchangedRate * 100).toFixed(1)}%`}`,
|
|
537
|
-
`Compactions: ${summary.compaction.completed} completed · ${summary.compaction.aborted} aborted · avg ${value(summary.compaction.averageDurationMs, "ms")} · ${summary.compaction.perRun === null ? "unknown" : summary.compaction.perRun.toFixed(3)} per agent run · ${summary.compaction.perTurn === null ? "unknown" : summary.compaction.perTurn.toFixed(3)} per turn`,
|
|
538
|
-
`Between compactions: ${value(summary.compaction.averageTurnsBetween, " turns")} · ${value(summary.compaction.averageProviderTokensBetween, " provider tokens")} · ${value(summary.compaction.averageCacheReadTokensBetween, " cache-read tokens")}`,
|
|
539
|
-
`Reasons: threshold ${summary.compaction.reasons.threshold} · overflow ${summary.compaction.reasons.overflow} · manual ${summary.compaction.reasons.manual}`,
|
|
540
|
-
].join("\n");
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
function formatRoute(route: Route): string {
|
|
544
|
-
return `${humanField(route.provider)}/${humanField(route.model)} · ${humanField(route.thinking)}`;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
export function formatMetricsQuery(rows: StoredMetricObservation[]): string {
|
|
548
|
-
if (rows.length === 0) return "Metrics: no observations matched";
|
|
549
|
-
const shown = rows.slice(0, CLI_METRICS_HUMAN_MAX_ROWS);
|
|
550
|
-
const lines = [
|
|
551
|
-
`Metrics: ${rows.length.toLocaleString()} observation(s)${rows.length > shown.length ? ` (showing first ${shown.length})` : ""}`,
|
|
552
|
-
...shown.map((row) => `- ${humanField(row.source)}/${humanField(row.scope)}/${humanField(row.metric)} = ${row.value === null ? "null" : row.value} ${row.unit} @ ${new Date(row.observedAt).toISOString()}`),
|
|
553
|
-
];
|
|
554
|
-
return lines.join("\n");
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
export function formatMetricsDistinctScopes(scopes: string[]): string {
|
|
558
|
-
if (scopes.length === 0) return "Scopes: none matched";
|
|
559
|
-
return [`Scopes: ${scopes.length.toLocaleString()}`, ...scopes.map((scope) => `- ${humanField(scope)}`)].join("\n");
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
export function formatMetricsUsageSeries(result: { rows: Array<{ scope: string; metric: string; bucketIndex: number; sum: number }>; truncated: boolean }): string {
|
|
563
|
-
if (result.rows.length === 0) return `Usage series: no data${result.truncated ? " (scope limit reached)" : ""}`;
|
|
564
|
-
const lines = [`Usage series: ${result.rows.length.toLocaleString()} bucket(s)${result.truncated ? " (scope limit reached)" : ""}`];
|
|
565
|
-
for (const row of result.rows) lines.push(`- ${humanField(row.scope)}/${humanField(row.metric)} bucket ${row.bucketIndex}: ${row.sum.toLocaleString()}`);
|
|
566
|
-
return lines.join("\n");
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
function formatUsdAmount(amount: number): string {
|
|
570
|
-
return `$${amount.toFixed(Math.abs(amount) < 0.01 && amount !== 0 ? 4 : 2)}`;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
export function formatCostByTask(summary: TaskCostSummary): string {
|
|
574
|
-
const lines = [
|
|
575
|
-
`Cost by task: ${summary.entries.length.toLocaleString()} task(s)${summary.truncated ? " (query limit reached; totals are a lower bound)" : ""}`,
|
|
576
|
-
...summary.entries.flatMap((entry) => [
|
|
577
|
-
`- ${humanField(entry.taskId)}: ${formatUsdAmount(entry.costUsd)} · ↑${entry.inputTokens.toLocaleString()} ↓${entry.outputTokens.toLocaleString()} R${entry.cacheReadTokens.toLocaleString()} W${entry.cacheWriteTokens.toLocaleString()}`,
|
|
578
|
-
...entry.byModel.map((model) => ` · ${humanField(model.provider)}/${humanField(model.model)} (${humanField(model.thinking)}): ${formatUsdAmount(model.costUsd)} · ↑${model.inputTokens.toLocaleString()} ↓${model.outputTokens.toLocaleString()} R${model.cacheReadTokens.toLocaleString()} W${model.cacheWriteTokens.toLocaleString()}`),
|
|
579
|
-
]),
|
|
580
|
-
`Unattributed spend (no task was focused): ${formatUsdAmount(summary.unattributedCostUsd)}`,
|
|
581
|
-
];
|
|
582
|
-
return lines.join("\n");
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
export function formatTelemetryPoll(result: TelemetryPollResult): string {
|
|
586
|
-
if (result.sources.length === 0) return "Telemetry: no sources configured";
|
|
587
|
-
return ["Telemetry:", ...result.sources.map((source) => {
|
|
588
|
-
const freshness = !source.ok ? `failed${source.error ? ` (${humanField(source.error)})` : ""}` : "ok";
|
|
589
|
-
return `- ${humanField(source.id)} (${humanField(source.provider)}): ${freshness} · ${source.metrics} metric(s)`;
|
|
590
|
-
})].join("\n");
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
export function formatRouterStatus(status: RouterStatus): string {
|
|
594
|
-
const lines = [
|
|
595
|
-
`Router: ${status.ready ? "ready" : "not ready"}${status.paused ? " · paused" : ""}`,
|
|
596
|
-
`Current route: ${status.currentRoute ? formatRoute(status.currentRoute) : "none"}`,
|
|
597
|
-
`Available routes: ${status.availableRoutes.length.toLocaleString()}`,
|
|
598
|
-
`Override: ${status.override ? `${formatRoute(status.override.route)}${status.override.expiresAt === null ? "" : ` (expires ${new Date(status.override.expiresAt).toISOString()})`}` : "none"}`,
|
|
599
|
-
];
|
|
600
|
-
if (status.lastDecision) lines.push(`Last decision: ${status.lastDecision.action} · pressure ${Number.isFinite(status.lastDecision.pressure) ? status.lastDecision.pressure.toFixed(3) : "∞"} · ${humanField(status.lastDecision.reason)}`);
|
|
601
|
-
lines.push(formatTelemetryPoll({ sources: status.sources, observedAt: Date.now() }));
|
|
602
|
-
return lines.join("\n");
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
export function formatCompactionEstimate(estimate: CompactionDurationEstimate): string {
|
|
606
|
-
if (estimate.confidence === "cold-start" || estimate.ms === null) {
|
|
607
|
-
return `Compaction duration: cold-start (${estimate.sampleSize.toLocaleString()} sample(s), not enough evidence yet)`;
|
|
608
|
-
}
|
|
609
|
-
return `Compaction duration: ~${estimate.ms.toLocaleString()}ms learned from ${estimate.sampleSize.toLocaleString()} sample(s)`;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
export function formatPolicyDecision(decision: PolicyDecision): string {
|
|
613
|
-
const lines = [`Decision: ${decision.action} · pressure ${Number.isFinite(decision.pressure) ? decision.pressure.toFixed(3) : "∞"} · ${humanField(decision.reason)}`];
|
|
614
|
-
if (decision.route) lines.push(`Route: ${formatRoute(decision.route)}`);
|
|
615
|
-
if (decision.delayMs !== undefined) lines.push(`Delay: ${decision.delayMs}ms`);
|
|
616
|
-
return lines.join("\n");
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
async function callAndPrint<Name extends OperationName>(
|
|
620
|
-
deps: CliDependencies,
|
|
621
|
-
operation: Name,
|
|
622
|
-
input: OperationInputs[Name],
|
|
623
|
-
json: boolean,
|
|
624
|
-
formatHuman: (result: OperationOutputs[Name]) => string,
|
|
625
|
-
): Promise<number> {
|
|
626
|
-
try {
|
|
627
|
-
const result = await deps.client.call(operation, input);
|
|
628
|
-
deps.stdout(json ? JSON.stringify(result) : formatHuman(result));
|
|
629
|
-
return 0;
|
|
630
|
-
} catch (error) {
|
|
631
|
-
deps.stderr(error instanceof Error ? error.message : String(error));
|
|
632
|
-
return 1;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Composes one command dispatcher per capability module (metrics, router+telemetry+compaction,
|
|
46
|
+
* session identity, benchmarks, context, service lifecycle, the raw op escape hatch) instead of
|
|
47
|
+
* a single switch that used to combine every command's own argument parsing, validation, and
|
|
48
|
+
* presentation in one place. Each module owns its own usage lines and command handler; this
|
|
49
|
+
* function only routes the top-level command word and forwards `usage` for the shared "not
|
|
50
|
+
* recognized" fallback.
|
|
51
|
+
*/
|
|
636
52
|
export async function runCli(args: string[], deps: CliDependencies = DEFAULT_DEPENDENCIES): Promise<number> {
|
|
637
53
|
const [command, action, ...rest] = args;
|
|
54
|
+
const fail = () => usage(deps.stderr);
|
|
638
55
|
if (command === "serve") { deps.serve(); return 0; }
|
|
639
|
-
if (command === "
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
if (action === "prune") {
|
|
651
|
-
const parsed = parseMetricsPruneArgs(rest);
|
|
652
|
-
if (!parsed) return usage(deps.stderr);
|
|
653
|
-
return callAndPrint(deps, "metrics.prune", parsed.input, parsed.json, (result) => `Pruned ${result.deleted.toLocaleString()} observation(s)`);
|
|
654
|
-
}
|
|
655
|
-
if (action === "distinct-scopes") {
|
|
656
|
-
const parsed = parseMetricsDistinctScopesArgs(rest);
|
|
657
|
-
if (!parsed) return usage(deps.stderr);
|
|
658
|
-
return callAndPrint(deps, "metrics.distinct_scopes", parsed.input, parsed.json, formatMetricsDistinctScopes);
|
|
659
|
-
}
|
|
660
|
-
if (action === "usage-series") {
|
|
661
|
-
const parsed = parseMetricsUsageSeriesArgs(rest);
|
|
662
|
-
if (!parsed) return usage(deps.stderr);
|
|
663
|
-
return callAndPrint(deps, "metrics.usage_series", parsed.input, parsed.json, formatMetricsUsageSeries);
|
|
664
|
-
}
|
|
665
|
-
if (action === "cost-by-task") {
|
|
666
|
-
const parsed = parseCostByTaskArgs(rest);
|
|
667
|
-
if (!parsed) return usage(deps.stderr);
|
|
668
|
-
return callAndPrint(deps, "metrics.cost_by_task", parsed.input, parsed.json, formatCostByTask);
|
|
669
|
-
}
|
|
670
|
-
return usage(deps.stderr);
|
|
671
|
-
}
|
|
672
|
-
if (command === "telemetry") {
|
|
673
|
-
if (action !== "poll") return usage(deps.stderr);
|
|
674
|
-
const parsed = parseJsonOnlyArgs(rest);
|
|
675
|
-
if (!parsed) return usage(deps.stderr);
|
|
676
|
-
return callAndPrint(deps, "telemetry.poll", {}, parsed.json, formatTelemetryPoll);
|
|
677
|
-
}
|
|
678
|
-
if (command === "compaction") {
|
|
679
|
-
if (action !== "estimate") return usage(deps.stderr);
|
|
680
|
-
const parsed = parseJsonOnlyArgs(rest);
|
|
681
|
-
if (!parsed) return usage(deps.stderr);
|
|
682
|
-
return callAndPrint(deps, "compaction.estimate", {}, parsed.json, formatCompactionEstimate);
|
|
683
|
-
}
|
|
684
|
-
if (command === "router") {
|
|
685
|
-
switch (action) {
|
|
686
|
-
case "status": {
|
|
687
|
-
const parsed = parseJsonOnlyArgs(rest);
|
|
688
|
-
if (!parsed) return usage(deps.stderr);
|
|
689
|
-
return callAndPrint(deps, "router.status", {}, parsed.json, formatRouterStatus);
|
|
690
|
-
}
|
|
691
|
-
case "decide": {
|
|
692
|
-
const parsed = parseJsonOnlyArgs(rest);
|
|
693
|
-
if (!parsed) return usage(deps.stderr);
|
|
694
|
-
return callAndPrint(deps, "router.decide", {}, parsed.json, formatPolicyDecision);
|
|
695
|
-
}
|
|
696
|
-
case "pause": {
|
|
697
|
-
const parsed = parseJsonOnlyArgs(rest);
|
|
698
|
-
if (!parsed) return usage(deps.stderr);
|
|
699
|
-
return callAndPrint(deps, "router.pause", {}, parsed.json, formatRouterStatus);
|
|
700
|
-
}
|
|
701
|
-
case "resume": {
|
|
702
|
-
const parsed = parseJsonOnlyArgs(rest);
|
|
703
|
-
if (!parsed) return usage(deps.stderr);
|
|
704
|
-
return callAndPrint(deps, "router.resume", {}, parsed.json, formatRouterStatus);
|
|
705
|
-
}
|
|
706
|
-
case "clear-override": {
|
|
707
|
-
const parsed = parseJsonOnlyArgs(rest);
|
|
708
|
-
if (!parsed) return usage(deps.stderr);
|
|
709
|
-
return callAndPrint(deps, "router.clear_override", {}, parsed.json, formatRouterStatus);
|
|
710
|
-
}
|
|
711
|
-
case "override": {
|
|
712
|
-
const parsed = parseRouterOverrideArgs(rest);
|
|
713
|
-
if (!parsed) return usage(deps.stderr);
|
|
714
|
-
return callAndPrint(deps, "router.override", parsed.input, parsed.json, formatRouterStatus);
|
|
715
|
-
}
|
|
716
|
-
case "current-route": {
|
|
717
|
-
const parsed = parseRouterRouteArgs(rest);
|
|
718
|
-
if (!parsed) return usage(deps.stderr);
|
|
719
|
-
return callAndPrint(deps, "router.current_route", parsed.input, parsed.json, formatRouterStatus);
|
|
720
|
-
}
|
|
721
|
-
case "available-routes": {
|
|
722
|
-
const parsed = parseRouterAvailableRoutesArgs(rest);
|
|
723
|
-
if (!parsed) return usage(deps.stderr);
|
|
724
|
-
return callAndPrint(deps, "router.available_routes", parsed.input, parsed.json, formatRouterStatus);
|
|
725
|
-
}
|
|
726
|
-
default: return usage(deps.stderr);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
if (command === "op") {
|
|
730
|
-
const parsed = parseOpArgs(action === undefined ? [] : [action, ...rest]);
|
|
731
|
-
if (!parsed) return usage(deps.stderr);
|
|
732
|
-
try {
|
|
733
|
-
// The escape hatch dispatches a dynamically named operation; OperationInputs/OperationOutputs
|
|
734
|
-
// are only known statically per literal operation name, so this one call site is intentionally
|
|
735
|
-
// untyped at the boundary. parseOpArgs already restricts `operation` to EXPECTED_OPERATION_NAMES.
|
|
736
|
-
const call = deps.client.call as (operation: OperationName, input: Record<string, unknown>) => Promise<unknown>;
|
|
737
|
-
const result = await call(parsed.operation, parsed.input);
|
|
738
|
-
deps.stdout(JSON.stringify(result));
|
|
739
|
-
return 0;
|
|
740
|
-
} catch (error) {
|
|
741
|
-
deps.stderr(error instanceof Error ? error.message : String(error));
|
|
742
|
-
return 1;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
if (command === "benchmarks") {
|
|
746
|
-
const parsed = parseBenchmarkArgs(action, rest);
|
|
747
|
-
if (!parsed) return usage(deps.stderr);
|
|
748
|
-
try {
|
|
749
|
-
if (parsed.action === "list") {
|
|
750
|
-
const result = await deps.client.call("benchmark.query", parsed.query!);
|
|
751
|
-
deps.stdout(parsed.json ? JSON.stringify(result) : formatBenchmarkQuery(result));
|
|
752
|
-
} else if (parsed.action === "rank") {
|
|
753
|
-
const result = await deps.client.call("models.rank", parsed.recommendation!);
|
|
754
|
-
deps.stdout(parsed.json ? JSON.stringify(result) : formatModelRanking(result));
|
|
755
|
-
} else {
|
|
756
|
-
const result = parsed.action === "refresh"
|
|
757
|
-
? await deps.client.call("benchmark.refresh", { force: parsed.force })
|
|
758
|
-
: await deps.client.call("benchmark.status", {});
|
|
759
|
-
deps.stdout(parsed.json ? JSON.stringify(result) : formatBenchmarkStatus(result));
|
|
760
|
-
}
|
|
761
|
-
return 0;
|
|
762
|
-
} catch (error) {
|
|
763
|
-
deps.stderr(error instanceof Error ? error.message : String(error));
|
|
764
|
-
return 1;
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
if (command === "context") {
|
|
768
|
-
const parsed = parseContextArgs([...(action === undefined ? [] : [action]), ...rest]);
|
|
769
|
-
if (!parsed) return usage(deps.stderr);
|
|
770
|
-
try {
|
|
771
|
-
const summary = await deps.client.call("context.assess", parsed.input);
|
|
772
|
-
deps.stdout(parsed.json ? JSON.stringify(summary) : formatContextAssessment(summary));
|
|
773
|
-
return 0;
|
|
774
|
-
} catch (error) {
|
|
775
|
-
deps.stderr(error instanceof Error ? error.message : String(error));
|
|
776
|
-
return 1;
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
if (command !== "service") return usage(deps.stderr);
|
|
780
|
-
switch (action) {
|
|
781
|
-
case "install": deps.installService(); return 0;
|
|
782
|
-
case "start": deps.systemctl("start", SYSTEMD_UNIT_NAME); return 0;
|
|
783
|
-
case "stop": deps.systemctl("stop", SYSTEMD_UNIT_NAME); return 0;
|
|
784
|
-
case "restart": deps.systemctl("restart", SYSTEMD_UNIT_NAME); return 0;
|
|
785
|
-
case "status": deps.systemctl("status", SYSTEMD_UNIT_NAME); return 0;
|
|
786
|
-
case "checkpoint": {
|
|
787
|
-
const parsed = parseJsonOnlyArgs(rest);
|
|
788
|
-
if (!parsed) return usage(deps.stderr);
|
|
789
|
-
return callAndPrint(deps, "service.checkpoint", {}, parsed.json, () => "Checkpoint complete");
|
|
790
|
-
}
|
|
791
|
-
default: return usage(deps.stderr);
|
|
792
|
-
}
|
|
56
|
+
if (command === "session") return runSessionCommand(action, rest, deps, fail);
|
|
57
|
+
if (command === "metrics") return runMetricsCommand(action, rest, deps, fail);
|
|
58
|
+
if (command === "telemetry") return runTelemetryCommand(action, rest, deps, fail);
|
|
59
|
+
if (command === "compaction") return runCompactionCommand(action, rest, deps, fail);
|
|
60
|
+
if (command === "router") return runRouterCommand(action, rest, deps, fail);
|
|
61
|
+
if (command === "op") return runOpCommand(action, rest, deps, fail);
|
|
62
|
+
if (command === "benchmarks") return runBenchmarksCommand(action, rest, deps, fail);
|
|
63
|
+
if (command === "context") return runContextCommand(action, rest, deps, fail);
|
|
64
|
+
if (command === "service") return runServiceCommand(action, rest, deps, fail);
|
|
65
|
+
return fail();
|
|
793
66
|
}
|
|
794
67
|
|
|
795
68
|
export async function main(args: string[] = process.argv.slice(2)): Promise<void> {
|