@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.
Files changed (43) hide show
  1. package/README.md +28 -77
  2. package/package.json +11 -14
  3. package/src/adapters/sqlite-metric-store.ts +11 -2
  4. package/src/adapters/sqlite-session-identity-store.ts +45 -0
  5. package/src/cli-commands/benchmarks.ts +140 -0
  6. package/src/cli-commands/compaction.ts +17 -0
  7. package/src/cli-commands/context.ts +49 -0
  8. package/src/cli-commands/metrics.ts +296 -0
  9. package/src/cli-commands/op.ts +40 -0
  10. package/src/cli-commands/route-args.ts +15 -0
  11. package/src/cli-commands/router.ts +207 -0
  12. package/src/cli-commands/service-daemon.ts +72 -0
  13. package/src/cli-commands/session.ts +42 -0
  14. package/src/cli-commands/support.ts +33 -0
  15. package/src/cli.ts +42 -769
  16. package/src/constants.ts +7 -0
  17. package/src/daemon.ts +13 -3
  18. package/src/db.ts +15 -1
  19. package/src/index.ts +137 -0
  20. package/src/operations/benchmark-operations.ts +12 -0
  21. package/src/operations/context-operations.ts +30 -0
  22. package/src/operations/metrics-operations.ts +77 -0
  23. package/src/operations/model-ranking-operations.ts +16 -0
  24. package/src/operations/router-operations.ts +19 -0
  25. package/src/operations/session-identity-operations.ts +15 -0
  26. package/src/operations/session-scope.ts +31 -0
  27. package/src/operations/types.ts +3 -0
  28. package/src/ports/metric-store.ts +2 -0
  29. package/src/ports/router-controller.ts +9 -9
  30. package/src/ports/session-identity-store.ts +5 -0
  31. package/src/providers/telemetry-sources.ts +2 -1
  32. package/src/router.ts +124 -67
  33. package/src/service.ts +60 -118
  34. package/src/session-identity-service.ts +55 -0
  35. package/docs/USAGE_PRIOR_ART.md +0 -64
  36. package/extension/src/benchmark-tui.ts +0 -105
  37. package/extension/src/footer.ts +0 -366
  38. package/extension/src/index.ts +0 -828
  39. package/extension/src/service-client.ts +0 -26
  40. package/extension/src/settings-tui.ts +0 -153
  41. package/extension/src/settings.ts +0 -103
  42. package/extension/src/tui.ts +0 -270
  43. package/extension/src/usage.ts +0 -320
@@ -0,0 +1,296 @@
1
+ import { CLI_METRICS_HUMAN_MAX_ROWS, MAX_QUERY_LIMIT, MAX_USAGE_BUCKETS, METRIC_BATCH_MAX_OBSERVATIONS, USAGE_MAX_DISTINCT_SCOPES } from "../constants.ts";
2
+ import { METRIC_UNITS, type MetricObservation, type MetricQuery, type MetricUnit, type StoredMetricObservation } from "../domain/metric.ts";
3
+ import type { TaskCostSummary } from "../domain/task-cost.ts";
4
+ import { callAndPrint, humanField, type CliDependencies } from "./support.ts";
5
+
6
+ export const METRICS_USAGE_LINES = [
7
+ " metrics record --source <s> --scope <s> --metric <s> --value <number|null> --unit <unit> [--observed-at <ms>] [--attributes <json>] [--json]",
8
+ ` metrics record-batch --observations <json-array, max ${METRIC_BATCH_MAX_OBSERVATIONS}> [--json]`,
9
+ " metrics query [--source <s>] [--scope <s>] [--metric <s>] [--since <ms>] [--until <ms>] [--limit <n>] [--order asc|desc] [--json]",
10
+ " metrics prune --before <ms> [--force] [--json] (force required if before is newer than 24h ago)",
11
+ ` metrics distinct-scopes --source <s> --since <ms> --until <ms> [--limit 1..${USAGE_MAX_DISTINCT_SCOPES}] [--json]`,
12
+ ` 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]`,
13
+ " metrics cost-by-task --since <ms> --until <ms> [--json]",
14
+ ];
15
+
16
+ interface MetricsRecordArgs { input: MetricObservation; json: boolean }
17
+
18
+ function parseMetricsRecordArgs(args: string[]): MetricsRecordArgs | null {
19
+ let json = false;
20
+ let source: string | undefined;
21
+ let scope: string | undefined;
22
+ let metric: string | undefined;
23
+ let value: number | null | undefined;
24
+ let unit: MetricUnit | undefined;
25
+ let observedAt: number | undefined;
26
+ let attributes: Record<string, unknown> | undefined;
27
+ for (let index = 0; index < args.length; index += 1) {
28
+ const argument = args[index];
29
+ if (argument === "--json") { json = true; continue; }
30
+ if (!["--source", "--scope", "--metric", "--value", "--unit", "--observed-at", "--attributes"].includes(argument ?? "")) return null;
31
+ const raw = args[++index];
32
+ if (raw === undefined || raw.length === 0) return null;
33
+ if (argument === "--source") source = raw;
34
+ else if (argument === "--scope") scope = raw;
35
+ else if (argument === "--metric") metric = raw;
36
+ else if (argument === "--value") {
37
+ if (raw.toLowerCase() === "null") value = null;
38
+ else {
39
+ const parsed = Number(raw);
40
+ if (!Number.isFinite(parsed)) return null;
41
+ value = parsed;
42
+ }
43
+ } else if (argument === "--unit") {
44
+ if (!METRIC_UNITS.includes(raw as MetricUnit)) return null;
45
+ unit = raw as MetricUnit;
46
+ } else if (argument === "--observed-at") {
47
+ const parsed = Number(raw);
48
+ if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
49
+ observedAt = parsed;
50
+ } else {
51
+ try {
52
+ const parsed = JSON.parse(raw);
53
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
54
+ attributes = parsed as Record<string, unknown>;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ }
60
+ if (source === undefined || scope === undefined || metric === undefined || value === undefined || unit === undefined) return null;
61
+ return {
62
+ json,
63
+ input: {
64
+ source, scope, metric, value, unit,
65
+ observedAt: observedAt ?? Date.now(),
66
+ ...(attributes ? { attributes } : {}),
67
+ },
68
+ };
69
+ }
70
+
71
+ interface MetricsRecordBatchArgs { input: { observations: MetricObservation[] }; json: boolean }
72
+
73
+ function parseMetricsRecordBatchArgs(args: string[]): MetricsRecordBatchArgs | null {
74
+ let json = false;
75
+ let observations: unknown;
76
+ for (let index = 0; index < args.length; index += 1) {
77
+ const argument = args[index];
78
+ if (argument === "--json") { json = true; continue; }
79
+ if (argument !== "--observations") return null;
80
+ const raw = args[++index];
81
+ if (raw === undefined || raw.length === 0) return null;
82
+ try {
83
+ observations = JSON.parse(raw);
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+ if (!Array.isArray(observations) || observations.length === 0 || observations.length > METRIC_BATCH_MAX_OBSERVATIONS) return null;
89
+ return { input: { observations: observations as MetricObservation[] }, json };
90
+ }
91
+
92
+ interface MetricsQueryArgs { input: MetricQuery; json: boolean }
93
+
94
+ function parseMetricsQueryArgs(args: string[]): MetricsQueryArgs | null {
95
+ let json = false;
96
+ const input: MetricQuery = {};
97
+ for (let index = 0; index < args.length; index += 1) {
98
+ const argument = args[index];
99
+ if (argument === "--json") { json = true; continue; }
100
+ if (!["--source", "--scope", "--metric", "--since", "--until", "--limit", "--order"].includes(argument ?? "")) return null;
101
+ const raw = args[++index];
102
+ if (raw === undefined || raw.length === 0) return null;
103
+ if (argument === "--source") input.source = raw;
104
+ else if (argument === "--scope") input.scope = raw;
105
+ else if (argument === "--metric") input.metric = raw;
106
+ else if (argument === "--order") {
107
+ if (raw !== "asc" && raw !== "desc") return null;
108
+ input.order = raw;
109
+ } else {
110
+ const parsed = Number(raw);
111
+ if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
112
+ if (argument === "--since") input.since = parsed;
113
+ else if (argument === "--until") input.until = parsed;
114
+ else {
115
+ if (parsed < 1 || parsed > MAX_QUERY_LIMIT) return null;
116
+ input.limit = parsed;
117
+ }
118
+ }
119
+ }
120
+ if (input.since !== undefined && input.until !== undefined && input.until < input.since) return null;
121
+ return { input, json };
122
+ }
123
+
124
+ interface MetricsDistinctScopesArgs { input: { source: string; since: number; until: number; limit?: number }; json: boolean }
125
+
126
+ function parseMetricsDistinctScopesArgs(args: string[]): MetricsDistinctScopesArgs | null {
127
+ let json = false;
128
+ let source: string | undefined;
129
+ let since: number | undefined;
130
+ let until: number | undefined;
131
+ let limit: number | undefined;
132
+ for (let index = 0; index < args.length; index += 1) {
133
+ const argument = args[index];
134
+ if (argument === "--json") { json = true; continue; }
135
+ if (!["--source", "--since", "--until", "--limit"].includes(argument ?? "")) return null;
136
+ const raw = args[++index];
137
+ if (raw === undefined || raw.length === 0) return null;
138
+ if (argument === "--source") { source = raw; continue; }
139
+ const parsed = Number(raw);
140
+ if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
141
+ if (argument === "--since") since = parsed;
142
+ else if (argument === "--until") until = parsed;
143
+ else {
144
+ if (parsed < 1 || parsed > USAGE_MAX_DISTINCT_SCOPES) return null;
145
+ limit = parsed;
146
+ }
147
+ }
148
+ if (source === undefined || since === undefined || until === undefined || until < since) return null;
149
+ return { input: { source, since, until, ...(limit === undefined ? {} : { limit }) }, json };
150
+ }
151
+
152
+ interface MetricsUsageSeriesArgs { input: { source: string; since: number; until: number; bucketSizeMs: number; bucketCount: number; scopeLimit?: number }; json: boolean }
153
+
154
+ function parseMetricsUsageSeriesArgs(args: string[]): MetricsUsageSeriesArgs | null {
155
+ let json = false;
156
+ let source: string | undefined;
157
+ let since: number | undefined;
158
+ let until: number | undefined;
159
+ let bucketSizeMs: number | undefined;
160
+ let bucketCount: number | undefined;
161
+ let scopeLimit: number | undefined;
162
+ for (let index = 0; index < args.length; index += 1) {
163
+ const argument = args[index];
164
+ if (argument === "--json") { json = true; continue; }
165
+ if (!(["--source", "--since", "--until", "--bucket-size-ms", "--bucket-count", "--scope-limit"].includes(argument ?? ""))) return null;
166
+ const raw = args[++index];
167
+ if (raw === undefined || raw.length === 0) return null;
168
+ if (argument === "--source") { source = raw; continue; }
169
+ const parsed = Number(raw);
170
+ if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
171
+ if (argument === "--since") since = parsed;
172
+ else if (argument === "--until") until = parsed;
173
+ else if (argument === "--bucket-size-ms") { if (parsed < 1) return null; bucketSizeMs = parsed; }
174
+ else if (argument === "--bucket-count") { if (parsed < 1 || parsed > MAX_USAGE_BUCKETS) return null; bucketCount = parsed; }
175
+ else { if (parsed < 1 || parsed > USAGE_MAX_DISTINCT_SCOPES) return null; scopeLimit = parsed; }
176
+ }
177
+ if (source === undefined || since === undefined || until === undefined || until < since || bucketSizeMs === undefined || bucketCount === undefined) return null;
178
+ return { input: { source, since, until, bucketSizeMs, bucketCount, ...(scopeLimit === undefined ? {} : { scopeLimit }) }, json };
179
+ }
180
+
181
+ interface CostByTaskArgs { input: { since: number; until: number }; json: boolean }
182
+
183
+ function parseCostByTaskArgs(args: string[]): CostByTaskArgs | null {
184
+ let json = false;
185
+ let since: number | undefined;
186
+ let until: number | undefined;
187
+ for (let index = 0; index < args.length; index += 1) {
188
+ const argument = args[index];
189
+ if (argument === "--json") { json = true; continue; }
190
+ if (!["--since", "--until"].includes(argument ?? "")) return null;
191
+ const raw = args[++index];
192
+ const parsed = Number(raw);
193
+ if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
194
+ if (argument === "--since") since = parsed;
195
+ else until = parsed;
196
+ }
197
+ if (since === undefined || until === undefined || until < since) return null;
198
+ return { input: { since, until }, json };
199
+ }
200
+
201
+ interface MetricsPruneArgs { input: { before: number; force?: boolean }; json: boolean }
202
+
203
+ function parseMetricsPruneArgs(args: string[]): MetricsPruneArgs | null {
204
+ let json = false;
205
+ let force = false;
206
+ let before: number | undefined;
207
+ for (let index = 0; index < args.length; index += 1) {
208
+ const argument = args[index];
209
+ if (argument === "--json") { json = true; continue; }
210
+ if (argument === "--force") { force = true; continue; }
211
+ if (argument !== "--before") return null;
212
+ const raw = args[++index];
213
+ const parsed = Number(raw);
214
+ if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
215
+ before = parsed;
216
+ }
217
+ if (before === undefined) return null;
218
+ return { input: { before, ...(force ? { force } : {}) }, json };
219
+ }
220
+
221
+ export function formatMetricsQuery(rows: StoredMetricObservation[]): string {
222
+ if (rows.length === 0) return "Metrics: no observations matched";
223
+ const shown = rows.slice(0, CLI_METRICS_HUMAN_MAX_ROWS);
224
+ const lines = [
225
+ `Metrics: ${rows.length.toLocaleString()} observation(s)${rows.length > shown.length ? ` (showing first ${shown.length})` : ""}`,
226
+ ...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()}`),
227
+ ];
228
+ return lines.join("\n");
229
+ }
230
+
231
+ export function formatMetricsDistinctScopes(scopes: string[]): string {
232
+ if (scopes.length === 0) return "Scopes: none matched";
233
+ return [`Scopes: ${scopes.length.toLocaleString()}`, ...scopes.map((scope) => `- ${humanField(scope)}`)].join("\n");
234
+ }
235
+
236
+ export function formatMetricsUsageSeries(result: { rows: Array<{ scope: string; metric: string; bucketIndex: number; sum: number }>; truncated: boolean }): string {
237
+ if (result.rows.length === 0) return `Usage series: no data${result.truncated ? " (scope limit reached)" : ""}`;
238
+ const lines = [`Usage series: ${result.rows.length.toLocaleString()} bucket(s)${result.truncated ? " (scope limit reached)" : ""}`];
239
+ for (const row of result.rows) lines.push(`- ${humanField(row.scope)}/${humanField(row.metric)} bucket ${row.bucketIndex}: ${row.sum.toLocaleString()}`);
240
+ return lines.join("\n");
241
+ }
242
+
243
+ function formatUsdAmount(amount: number): string {
244
+ return `$${amount.toFixed(Math.abs(amount) < 0.01 && amount !== 0 ? 4 : 2)}`;
245
+ }
246
+
247
+ export function formatCostByTask(summary: TaskCostSummary): string {
248
+ const lines = [
249
+ `Cost by task: ${summary.entries.length.toLocaleString()} task(s)${summary.truncated ? " (query limit reached; totals are a lower bound)" : ""}`,
250
+ ...summary.entries.flatMap((entry) => [
251
+ `- ${humanField(entry.taskId)}: ${formatUsdAmount(entry.costUsd)} · ↑${entry.inputTokens.toLocaleString()} ↓${entry.outputTokens.toLocaleString()} R${entry.cacheReadTokens.toLocaleString()} W${entry.cacheWriteTokens.toLocaleString()}`,
252
+ ...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()}`),
253
+ ]),
254
+ `Unattributed spend (no task was focused): ${formatUsdAmount(summary.unattributedCostUsd)}`,
255
+ ];
256
+ return lines.join("\n");
257
+ }
258
+
259
+ export async function runMetricsCommand(action: string | undefined, rest: string[], deps: CliDependencies, usage: () => number): Promise<number> {
260
+ if (action === "record") {
261
+ const parsed = parseMetricsRecordArgs(rest);
262
+ if (!parsed) return usage();
263
+ return callAndPrint(deps, "metrics.record", parsed.input, parsed.json, (row) => formatMetricsQuery([row]));
264
+ }
265
+ if (action === "record-batch") {
266
+ const parsed = parseMetricsRecordBatchArgs(rest);
267
+ if (!parsed) return usage();
268
+ return callAndPrint(deps, "metrics.record_batch", parsed.input, parsed.json, formatMetricsQuery);
269
+ }
270
+ if (action === "query") {
271
+ const parsed = parseMetricsQueryArgs(rest);
272
+ if (!parsed) return usage();
273
+ return callAndPrint(deps, "metrics.query", parsed.input, parsed.json, formatMetricsQuery);
274
+ }
275
+ if (action === "prune") {
276
+ const parsed = parseMetricsPruneArgs(rest);
277
+ if (!parsed) return usage();
278
+ return callAndPrint(deps, "metrics.prune", parsed.input, parsed.json, (result) => `Pruned ${result.deleted.toLocaleString()} observation(s)`);
279
+ }
280
+ if (action === "distinct-scopes") {
281
+ const parsed = parseMetricsDistinctScopesArgs(rest);
282
+ if (!parsed) return usage();
283
+ return callAndPrint(deps, "metrics.distinct_scopes", parsed.input, parsed.json, formatMetricsDistinctScopes);
284
+ }
285
+ if (action === "usage-series") {
286
+ const parsed = parseMetricsUsageSeriesArgs(rest);
287
+ if (!parsed) return usage();
288
+ return callAndPrint(deps, "metrics.usage_series", parsed.input, parsed.json, formatMetricsUsageSeries);
289
+ }
290
+ if (action === "cost-by-task") {
291
+ const parsed = parseCostByTaskArgs(rest);
292
+ if (!parsed) return usage();
293
+ return callAndPrint(deps, "metrics.cost_by_task", parsed.input, parsed.json, formatCostByTask);
294
+ }
295
+ return usage();
296
+ }
@@ -0,0 +1,40 @@
1
+ import { EXPECTED_OPERATION_NAMES, type OperationName } from "../service.ts";
2
+ import type { CliDependencies } from "./support.ts";
3
+
4
+ export const OP_USAGE_LINES = [" op <operation> [--input <json>]"];
5
+
6
+ function parseOpArgs(args: string[]): { operation: OperationName; input: Record<string, unknown> } | null {
7
+ const [operation, ...rest] = args;
8
+ if (operation === undefined || !EXPECTED_OPERATION_NAMES.includes(operation as OperationName)) return null;
9
+ let input: Record<string, unknown> = {};
10
+ for (let index = 0; index < rest.length; index += 1) {
11
+ if (rest[index] !== "--input") return null;
12
+ const raw = rest[++index];
13
+ if (raw === undefined) return null;
14
+ try {
15
+ const parsed = JSON.parse(raw);
16
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
17
+ input = parsed as Record<string, unknown>;
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
22
+ return { operation: operation as OperationName, input };
23
+ }
24
+
25
+ export async function runOpCommand(action: string | undefined, rest: string[], deps: CliDependencies, usage: () => number): Promise<number> {
26
+ const parsed = parseOpArgs(action === undefined ? [] : [action, ...rest]);
27
+ if (!parsed) return usage();
28
+ try {
29
+ // The escape hatch dispatches a dynamically named operation; OperationInputs/OperationOutputs
30
+ // are only known statically per literal operation name, so this one call site is intentionally
31
+ // untyped at the boundary. parseOpArgs already restricts `operation` to EXPECTED_OPERATION_NAMES.
32
+ const call = deps.client.call as (operation: OperationName, input: Record<string, unknown>) => Promise<unknown>;
33
+ const result = await call(parsed.operation, parsed.input);
34
+ deps.stdout(JSON.stringify(result));
35
+ return 0;
36
+ } catch (error) {
37
+ deps.stderr(error instanceof Error ? error.message : String(error));
38
+ return 1;
39
+ }
40
+ }
@@ -0,0 +1,15 @@
1
+ import type { ModelCandidate } from "../domain/model-ranking.ts";
2
+ import type { Route } from "../policy.ts";
3
+
4
+ /** Shared `provider/model@thinking` parsing for router and benchmark CLI arguments -- a Route and a ModelCandidate are structurally identical at this boundary. */
5
+ export function parseCandidate(raw: string): ModelCandidate | null {
6
+ const separator = raw.indexOf("/");
7
+ const thinkingSeparator = raw.lastIndexOf("@");
8
+ if (separator <= 0 || thinkingSeparator <= separator + 1 || thinkingSeparator === raw.length - 1) return null;
9
+ return { provider: raw.slice(0, separator), model: raw.slice(separator + 1, thinkingSeparator), thinking: raw.slice(thinkingSeparator + 1) };
10
+ }
11
+
12
+ export function parseRoute(raw: string | undefined): Route | null {
13
+ if (raw === undefined) return null;
14
+ return parseCandidate(raw);
15
+ }
@@ -0,0 +1,207 @@
1
+ import { CLI_AVAILABLE_ROUTES_MAX } from "../constants.ts";
2
+ import type { PolicyDecision, Route } from "../policy.ts";
3
+ import type { RouteOverride, RouterStatus, TelemetryPollResult } from "../ports/router-controller.ts";
4
+ import { callAndPrint, humanField, type CliDependencies } from "./support.ts";
5
+ import { parseRoute } from "./route-args.ts";
6
+
7
+ export const ROUTER_USAGE_LINES = [
8
+ " telemetry poll [--json]",
9
+ " compaction estimate [--json]",
10
+ " router <status|decide|pause|resume|clear-override> [--session-id <id>] [--session-secret <secret>] [--json]",
11
+ " router override --route <provider/model@thinking> [--expires-at <ms>] [--session-id <id>] [--session-secret <secret>] [--json]",
12
+ " router current-route --route <provider/model@thinking> [--session-id <id>] [--session-secret <secret>] [--json]",
13
+ " router available-routes [--route <provider/model@thinking> ...] [--session-id <id>] [--session-secret <secret>] [--json]",
14
+ ];
15
+
16
+ interface SessionScope { session_id?: string; session_secret?: string }
17
+
18
+ function sessionScopeInput(sessionId: string | undefined, sessionSecret: string | undefined): SessionScope {
19
+ return { ...(sessionId ? { session_id: sessionId } : {}), ...(sessionSecret ? { session_secret: sessionSecret } : {}) };
20
+ }
21
+
22
+ interface RouterOverrideArgs { input: RouteOverride & SessionScope; json: boolean }
23
+
24
+ function parseRouterOverrideArgs(args: string[]): RouterOverrideArgs | null {
25
+ let json = false;
26
+ let route: Route | null = null;
27
+ let expiresAt: number | null = null;
28
+ let sessionId: string | undefined;
29
+ let sessionSecret: string | undefined;
30
+ for (let index = 0; index < args.length; index += 1) {
31
+ const argument = args[index];
32
+ if (argument === "--json") { json = true; continue; }
33
+ if (!["--route", "--expires-at", "--session-id", "--session-secret"].includes(argument ?? "")) return null;
34
+ const raw = args[++index];
35
+ if (raw === undefined || raw.length === 0) return null;
36
+ if (argument === "--session-id") sessionId = raw;
37
+ else if (argument === "--session-secret") sessionSecret = raw;
38
+ else if (argument === "--route") {
39
+ route = parseRoute(raw);
40
+ if (!route) return null;
41
+ } else {
42
+ const parsed = Number(raw);
43
+ if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
44
+ expiresAt = parsed;
45
+ }
46
+ }
47
+ if (!route) return null;
48
+ return { input: { route, expiresAt, ...sessionScopeInput(sessionId, sessionSecret) }, json };
49
+ }
50
+
51
+ interface RouterRouteArgs { input: Route & SessionScope; json: boolean }
52
+
53
+ function parseRouterRouteArgs(args: string[]): RouterRouteArgs | null {
54
+ let json = false;
55
+ let route: Route | null = null;
56
+ let sessionId: string | undefined;
57
+ let sessionSecret: string | undefined;
58
+ for (let index = 0; index < args.length; index += 1) {
59
+ const argument = args[index];
60
+ if (argument === "--json") { json = true; continue; }
61
+ if (!["--route", "--session-id", "--session-secret"].includes(argument ?? "")) return null;
62
+ const raw = args[++index];
63
+ if (raw === undefined || raw.length === 0) return null;
64
+ if (argument === "--session-id") sessionId = raw;
65
+ else if (argument === "--session-secret") sessionSecret = raw;
66
+ else {
67
+ route = parseRoute(raw);
68
+ if (!route) return null;
69
+ }
70
+ }
71
+ if (!route) return null;
72
+ return { input: { ...route, ...sessionScopeInput(sessionId, sessionSecret) }, json };
73
+ }
74
+
75
+ interface RouterAvailableRoutesArgs { input: { routes: Route[] } & SessionScope; json: boolean }
76
+
77
+ function parseRouterAvailableRoutesArgs(args: string[]): RouterAvailableRoutesArgs | null {
78
+ let json = false;
79
+ let sessionId: string | undefined;
80
+ let sessionSecret: string | undefined;
81
+ const routes: Route[] = [];
82
+ for (let index = 0; index < args.length; index += 1) {
83
+ const argument = args[index];
84
+ if (argument === "--json") { json = true; continue; }
85
+ if (!["--route", "--session-id", "--session-secret"].includes(argument ?? "")) return null;
86
+ const raw = args[++index];
87
+ if (raw === undefined || raw.length === 0) return null;
88
+ if (argument === "--session-id") sessionId = raw;
89
+ else if (argument === "--session-secret") sessionSecret = raw;
90
+ else {
91
+ const route = parseRoute(raw);
92
+ if (!route) return null;
93
+ if (routes.length >= CLI_AVAILABLE_ROUTES_MAX) return null;
94
+ routes.push(route);
95
+ }
96
+ }
97
+ return { input: { routes, ...sessionScopeInput(sessionId, sessionSecret) }, json };
98
+ }
99
+
100
+ function parseRouterScopeArgs(args: string[]): { input: SessionScope; json: boolean } | null {
101
+ let json = false;
102
+ let sessionId: string | undefined;
103
+ let sessionSecret: string | undefined;
104
+ for (let index = 0; index < args.length; index += 1) {
105
+ const argument = args[index];
106
+ if (argument === "--json") { json = true; continue; }
107
+ if (!["--session-id", "--session-secret"].includes(argument ?? "")) return null;
108
+ const raw = args[++index];
109
+ if (raw === undefined || raw.length === 0) return null;
110
+ if (argument === "--session-id") sessionId = raw;
111
+ else sessionSecret = raw;
112
+ }
113
+ return { input: sessionScopeInput(sessionId, sessionSecret), json };
114
+ }
115
+
116
+ export function parseJsonOnlyArgs(args: string[]): { json: boolean } | null {
117
+ let json = false;
118
+ for (const argument of args) {
119
+ if (argument !== "--json") return null;
120
+ json = true;
121
+ }
122
+ return { json };
123
+ }
124
+
125
+ function formatRoute(route: Route): string {
126
+ return `${humanField(route.provider)}/${humanField(route.model)} · ${humanField(route.thinking)}`;
127
+ }
128
+
129
+ export function formatTelemetryPoll(result: TelemetryPollResult): string {
130
+ if (result.sources.length === 0) return "Telemetry: no sources configured";
131
+ return ["Telemetry:", ...result.sources.map((source) => {
132
+ const freshness = !source.ok ? `failed${source.error ? ` (${humanField(source.error)})` : ""}` : "ok";
133
+ return `- ${humanField(source.id)} (${humanField(source.provider)}): ${freshness} · ${source.metrics} metric(s)`;
134
+ })].join("\n");
135
+ }
136
+
137
+ export function formatRouterStatus(status: RouterStatus): string {
138
+ const lines = [
139
+ `Router: ${status.ready ? "ready" : "not ready"}${status.paused ? " · paused" : ""}`,
140
+ `Current route: ${status.currentRoute ? formatRoute(status.currentRoute) : "none"}`,
141
+ `Available routes: ${status.availableRoutes.length.toLocaleString()}`,
142
+ `Override: ${status.override ? `${formatRoute(status.override.route)}${status.override.expiresAt === null ? "" : ` (expires ${new Date(status.override.expiresAt).toISOString()})`}` : "none"}`,
143
+ ];
144
+ 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)}`);
145
+ lines.push(formatTelemetryPoll({ sources: status.sources, observedAt: Date.now() }));
146
+ return lines.join("\n");
147
+ }
148
+
149
+ export function formatPolicyDecision(decision: PolicyDecision): string {
150
+ const lines = [`Decision: ${decision.action} · pressure ${Number.isFinite(decision.pressure) ? decision.pressure.toFixed(3) : "∞"} · ${humanField(decision.reason)}`];
151
+ if (decision.route) lines.push(`Route: ${formatRoute(decision.route)}`);
152
+ if (decision.delayMs !== undefined) lines.push(`Delay: ${decision.delayMs}ms`);
153
+ return lines.join("\n");
154
+ }
155
+
156
+ export async function runTelemetryCommand(action: string | undefined, rest: string[], deps: CliDependencies, usage: () => number): Promise<number> {
157
+ if (action !== "poll") return usage();
158
+ const parsed = parseJsonOnlyArgs(rest);
159
+ if (!parsed) return usage();
160
+ return callAndPrint(deps, "telemetry.poll", {}, parsed.json, formatTelemetryPoll);
161
+ }
162
+
163
+ export async function runRouterCommand(action: string | undefined, rest: string[], deps: CliDependencies, usage: () => number): Promise<number> {
164
+ switch (action) {
165
+ case "status": {
166
+ const parsed = parseRouterScopeArgs(rest);
167
+ if (!parsed) return usage();
168
+ return callAndPrint(deps, "router.status", parsed.input, parsed.json, formatRouterStatus);
169
+ }
170
+ case "decide": {
171
+ const parsed = parseRouterScopeArgs(rest);
172
+ if (!parsed) return usage();
173
+ return callAndPrint(deps, "router.decide", parsed.input, parsed.json, formatPolicyDecision);
174
+ }
175
+ case "pause": {
176
+ const parsed = parseRouterScopeArgs(rest);
177
+ if (!parsed) return usage();
178
+ return callAndPrint(deps, "router.pause", parsed.input, parsed.json, formatRouterStatus);
179
+ }
180
+ case "resume": {
181
+ const parsed = parseRouterScopeArgs(rest);
182
+ if (!parsed) return usage();
183
+ return callAndPrint(deps, "router.resume", parsed.input, parsed.json, formatRouterStatus);
184
+ }
185
+ case "clear-override": {
186
+ const parsed = parseRouterScopeArgs(rest);
187
+ if (!parsed) return usage();
188
+ return callAndPrint(deps, "router.clear_override", parsed.input, parsed.json, formatRouterStatus);
189
+ }
190
+ case "override": {
191
+ const parsed = parseRouterOverrideArgs(rest);
192
+ if (!parsed) return usage();
193
+ return callAndPrint(deps, "router.override", parsed.input, parsed.json, formatRouterStatus);
194
+ }
195
+ case "current-route": {
196
+ const parsed = parseRouterRouteArgs(rest);
197
+ if (!parsed) return usage();
198
+ return callAndPrint(deps, "router.current_route", parsed.input, parsed.json, formatRouterStatus);
199
+ }
200
+ case "available-routes": {
201
+ const parsed = parseRouterAvailableRoutesArgs(rest);
202
+ if (!parsed) return usage();
203
+ return callAndPrint(deps, "router.available_routes", parsed.input, parsed.json, formatRouterStatus);
204
+ }
205
+ default: return usage();
206
+ }
207
+ }
@@ -0,0 +1,72 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ import { SYSTEMD_UNIT_NAME } from "../constants.ts";
6
+ import { resolveJittorPaths } from "../state.ts";
7
+ import { callAndPrint, type CliDependencies } from "./support.ts";
8
+ import { parseJsonOnlyArgs } from "./router.ts";
9
+
10
+ export const SERVICE_USAGE_LINES = [" service <install|start|stop|restart|status|checkpoint>"];
11
+
12
+ export interface SystemdUnitOptions {
13
+ bunBin: string;
14
+ cliPath: string;
15
+ codexAuthFile?: string;
16
+ openRouterBenchmarks?: boolean;
17
+ }
18
+
19
+ export function renderSystemdUnit(options: SystemdUnitOptions): string {
20
+ return `[Unit]
21
+ Description=Jittor token optimizing router
22
+ After=default.target network-online.target
23
+ Wants=network-online.target
24
+
25
+ [Service]
26
+ Type=simple
27
+ ExecStart=${options.bunBin} ${options.cliPath} serve
28
+ ${options.codexAuthFile ? `Environment="JITTOR_CODEX_AUTH_FILE=${options.codexAuthFile.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"\n` : ""}${options.openRouterBenchmarks ? "Environment=JITTOR_OPENROUTER_BENCHMARKS=1\n" : ""}Restart=always
29
+ RestartSec=2
30
+ NoNewPrivileges=true
31
+ PrivateTmp=true
32
+
33
+ [Install]
34
+ WantedBy=default.target
35
+ `;
36
+ }
37
+
38
+ export function systemctl(...args: string[]): void {
39
+ execFileSync("systemctl", ["--user", ...args], { stdio: "inherit" });
40
+ }
41
+
42
+ /** cliPath is the caller's own entrypoint file -- resolved from the real CLI script's `import.meta.url`, never this module's own, so the installed unit's ExecStart always points at the actual runnable CLI. */
43
+ export function installService(cliPath: string): void {
44
+ const unitPath = resolveJittorPaths().systemdUnit;
45
+ mkdirSync(dirname(unitPath), { recursive: true });
46
+ const codexAuthFile = join(process.env["CODEX_HOME"] ?? join(homedir(), ".codex"), "auth.json");
47
+ writeFileSync(unitPath, renderSystemdUnit({
48
+ bunBin: process.execPath,
49
+ cliPath,
50
+ ...(existsSync(codexAuthFile) ? { codexAuthFile } : {}),
51
+ openRouterBenchmarks: process.env["JITTOR_OPENROUTER_BENCHMARKS"] === "1",
52
+ }));
53
+ systemctl("daemon-reload");
54
+ systemctl("enable", SYSTEMD_UNIT_NAME);
55
+ systemctl("restart", SYSTEMD_UNIT_NAME);
56
+ }
57
+
58
+ export async function runServiceCommand(action: string | undefined, rest: string[], deps: CliDependencies, usage: () => number): Promise<number> {
59
+ switch (action) {
60
+ case "install": deps.installService(); return 0;
61
+ case "start": deps.systemctl("start", SYSTEMD_UNIT_NAME); return 0;
62
+ case "stop": deps.systemctl("stop", SYSTEMD_UNIT_NAME); return 0;
63
+ case "restart": deps.systemctl("restart", SYSTEMD_UNIT_NAME); return 0;
64
+ case "status": deps.systemctl("status", SYSTEMD_UNIT_NAME); return 0;
65
+ case "checkpoint": {
66
+ const parsed = parseJsonOnlyArgs(rest);
67
+ if (!parsed) return usage();
68
+ return callAndPrint(deps, "service.checkpoint", {}, parsed.json, () => "Checkpoint complete");
69
+ }
70
+ default: return usage();
71
+ }
72
+ }
@@ -0,0 +1,42 @@
1
+ import { callAndPrint, humanField, type CliDependencies } from "./support.ts";
2
+
3
+ export const SESSION_USAGE_LINES = [
4
+ " session register --session-id <id> [--json]",
5
+ " session release --session-id <id> [--session-secret <secret>] [--json]",
6
+ ];
7
+
8
+ interface SessionArgs { input: { session_id: string; session_secret?: string }; json: boolean }
9
+
10
+ function parseSessionArgs(args: string[]): SessionArgs | null {
11
+ let json = false;
12
+ let sessionId: string | undefined;
13
+ let sessionSecret: string | undefined;
14
+ for (let index = 0; index < args.length; index += 1) {
15
+ const argument = args[index];
16
+ if (argument === "--json") { json = true; continue; }
17
+ if (!["--session-id", "--session-secret"].includes(argument ?? "")) return null;
18
+ const raw = args[++index];
19
+ if (raw === undefined || raw.length === 0) return null;
20
+ if (argument === "--session-id") sessionId = raw;
21
+ else sessionSecret = raw;
22
+ }
23
+ if (sessionId === undefined) return null;
24
+ return { input: { session_id: sessionId, ...(sessionSecret ? { session_secret: sessionSecret } : {}) }, json };
25
+ }
26
+
27
+ export function formatSessionRegistration(result: { sessionId: string; secret: string }): string {
28
+ return `Session registered: ${humanField(result.sessionId)} · secret ${humanField(result.secret)} (shown once; keep it to mutate this session's router state)`;
29
+ }
30
+
31
+ export function formatSessionRelease(result: { released: boolean }): string {
32
+ return result.released ? "Session released" : "Session was not registered, or the secret did not match";
33
+ }
34
+
35
+ export async function runSessionCommand(action: string | undefined, rest: string[], deps: CliDependencies, usage: () => number): Promise<number> {
36
+ if (action !== "register" && action !== "release") return usage();
37
+ const parsed = parseSessionArgs(rest);
38
+ if (!parsed) return usage();
39
+ return action === "register"
40
+ ? callAndPrint(deps, "session.register", parsed.input, parsed.json, formatSessionRegistration)
41
+ : callAndPrint(deps, "session.release", parsed.input, parsed.json, formatSessionRelease);
42
+ }