@danypops/jittor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,180 @@
1
+ import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
+ import { matchesKey, truncateToWidth } from "@earendil-works/pi-tui";
3
+ import type { StoredMetricObservation } from "../../src/domain/metric.ts";
4
+ import type { PolicyAction, Route } from "../../src/policy.ts";
5
+ import type { RouterStatus } from "../../src/ports/router-controller.ts";
6
+ import type { ProviderBudget } from "./footer.ts";
7
+
8
+ export interface JittorPanelClient {
9
+ call(operation: string, input: unknown): Promise<any>;
10
+ }
11
+
12
+ type PanelAction = "pause" | "resume" | "refresh" | "override" | "clear-override" | "close";
13
+
14
+ function latest(rows: StoredMetricObservation[], predicate: (row: StoredMetricObservation) => boolean): StoredMetricObservation | undefined {
15
+ return rows.filter(predicate).sort((left, right) => right.observedAt - left.observedAt || right.id - left.id)[0];
16
+ }
17
+
18
+ function longestCodexWindow(rows: StoredMetricObservation[]): StoredMetricObservation | undefined {
19
+ return rows
20
+ .filter((row) => row.source === "codex-subscription" && row.metric === "used-fraction" && typeof row.value === "number")
21
+ .sort((left, right) => Number(right.attributes["windowSeconds"] ?? 0) - Number(left.attributes["windowSeconds"] ?? 0) || right.observedAt - left.observedAt)[0];
22
+ }
23
+
24
+ function compactWindowName(seconds: number): string {
25
+ if (seconds >= 6 * 24 * 60 * 60) return "W";
26
+ if (seconds >= 60 * 60) return `${Math.round(seconds / 3_600)}h`;
27
+ return `${Math.round(seconds / 60)}m`;
28
+ }
29
+
30
+ function windowName(seconds: number): string {
31
+ if (seconds >= 6 * 24 * 60 * 60) return "weekly";
32
+ if (seconds >= 60 * 60) return `${Math.round(seconds / 3_600)}h`;
33
+ return `${Math.round(seconds / 60)}m`;
34
+ }
35
+
36
+ export function buildFooterBudget(status: RouterStatus, metrics: StoredMetricObservation[]): ProviderBudget | null {
37
+ if (!status.ready || !status.currentRoute) return null;
38
+ if (status.currentRoute.provider === "openai-codex") {
39
+ const codex = longestCodexWindow(metrics);
40
+ if (!codex || typeof codex.value !== "number") return null;
41
+ return {
42
+ label: compactWindowName(Number(codex.attributes["windowSeconds"] ?? 0)),
43
+ fraction: codex.value,
44
+ valueText: `${(codex.value * 100).toFixed(1)}% used`,
45
+ observedAt: codex.observedAt,
46
+ };
47
+ }
48
+ if (status.currentRoute.provider === "openrouter") {
49
+ const openRouter = latest(metrics, (row) => row.source === "openrouter" && row.metric === "usage" && typeof row.value === "number");
50
+ if (!openRouter || typeof openRouter.value !== "number") return null;
51
+ return { label: "spend", fraction: null, valueText: `$${openRouter.value.toFixed(3)}`, observedAt: openRouter.observedAt };
52
+ }
53
+ return null;
54
+ }
55
+
56
+ export function formatFooterStatus(status: RouterStatus, metrics: StoredMetricObservation[]): string {
57
+ const budget = buildFooterBudget(status, metrics);
58
+ if (!budget) return "";
59
+ return budget.fraction === null ? budget.valueText : `${budget.label} ${(budget.fraction * 100).toFixed(1)}%`;
60
+ }
61
+
62
+ function nextAction(action: PolicyAction | undefined): string {
63
+ switch (action) {
64
+ case "continue": return "throttle";
65
+ case "throttle": return "lower thinking";
66
+ case "lower-thinking": return "switch model";
67
+ case "switch-model": return "switch provider";
68
+ case "switch-provider": return "halt";
69
+ case "halt": return "halted";
70
+ default: return "waiting for decision";
71
+ }
72
+ }
73
+
74
+ function burnLine(rows: StoredMetricObservation[], current: StoredMetricObservation, now: number): string {
75
+ const previous = rows
76
+ .filter((row) => row.source === current.source && row.scope === current.scope && row.metric === current.metric && row.id !== current.id && row.observedAt < current.observedAt)
77
+ .sort((left, right) => right.observedAt - left.observedAt)[0];
78
+ const resetsAt = Number(current.attributes["resetsAt"] ?? 0) * 1_000;
79
+ const remainingSeconds = (resetsAt - now) / 1_000;
80
+ const sustainable = typeof current.value === "number" && remainingSeconds > 0 ? (1 - current.value) / remainingSeconds : null;
81
+ const observed = previous && typeof previous.value === "number" && typeof current.value === "number" && current.observedAt > previous.observedAt
82
+ ? (current.value - previous.value) / ((current.observedAt - previous.observedAt) / 1_000)
83
+ : null;
84
+ const perHour = (rate: number | null) => rate === null ? "n/a" : `${(rate * 3_600 * 100).toFixed(2)}%/h`;
85
+ return `Burn: observed ${perHour(observed)} · sustainable ${perHour(sustainable)}`;
86
+ }
87
+
88
+ export function buildStatusView(status: RouterStatus, metrics: StoredMetricObservation[], now = Date.now()): string[] {
89
+ const lines = [status.ready ? "Ready" : "Not ready"];
90
+ const codex = status.currentRoute?.provider === "openai-codex" ? longestCodexWindow(metrics) : undefined;
91
+ if (codex && typeof codex.value === "number") {
92
+ const seconds = Number(codex.attributes["windowSeconds"] ?? 0);
93
+ lines.push(`Codex ${windowName(seconds)}: ${(codex.value * 100).toFixed(1)}%`);
94
+ lines.push(burnLine(metrics, codex, now));
95
+ }
96
+ const openRouter = status.currentRoute?.provider === "openrouter"
97
+ ? latest(metrics, (row) => row.source === "openrouter" && row.metric === "usage" && typeof row.value === "number")
98
+ : undefined;
99
+ if (openRouter && typeof openRouter.value === "number") lines.push(`OpenRouter spend: $${openRouter.value.toFixed(3)}`);
100
+ if (status.currentRoute) lines.push(`Route: ${status.currentRoute.provider}/${status.currentRoute.model} · ${status.currentRoute.thinking}`);
101
+ if (status.lastDecision) lines.push(`Pressure: ${Number.isFinite(status.lastDecision.pressure) ? status.lastDecision.pressure.toFixed(3) : "∞"} · ${status.lastDecision.action}`);
102
+ lines.push(`Next: ${nextAction(status.lastDecision?.action)}`);
103
+ lines.push("Telemetry:");
104
+ for (const source of status.sources.filter((source) => source.provider === status.currentRoute?.provider)) {
105
+ const freshness = !source.ok ? "failed" : source.observedAt !== undefined && now - source.observedAt > 120_000 ? "stale" : "fresh";
106
+ lines.push(` ${source.id}: ${freshness} · ${source.metrics} metrics`);
107
+ }
108
+ if (status.override) lines.push(`Override: ${status.override.route.provider}/${status.override.route.model} · ${status.override.route.thinking}`);
109
+ if (status.paused) lines.push("Emergency halt is active");
110
+ return lines;
111
+ }
112
+
113
+ async function snapshot(client: JittorPanelClient): Promise<{ status: RouterStatus; metrics: StoredMetricObservation[] }> {
114
+ const status = await client.call("router.status", {}) as RouterStatus;
115
+ const provider = status.currentRoute?.provider;
116
+ const query = provider === "openai-codex"
117
+ ? { source: "codex-subscription", metric: "used-fraction", order: "desc", limit: 100 }
118
+ : provider === "openrouter" ? { source: "openrouter", order: "desc", limit: 20 } : null;
119
+ const metrics = query ? await client.call("metrics.query", query) as StoredMetricObservation[] : [];
120
+ return { status, metrics };
121
+ }
122
+
123
+ async function chooseOverride(ctx: ExtensionCommandContext, routes: Route[]): Promise<Route | undefined> {
124
+ if (routes.length === 0) { ctx.ui.notify("Pi reports no authenticated routes for the current provider.", "warning"); return undefined; }
125
+ const labels = routes.map((route) => `${route.provider}/${route.model} · ${route.thinking}`);
126
+ const selected = await ctx.ui.select("Override route", labels);
127
+ const index = selected ? labels.indexOf(selected) : -1;
128
+ return index >= 0 ? routes[index] : undefined;
129
+ }
130
+
131
+ export async function showJittorPanel(ctx: ExtensionCommandContext, client: JittorPanelClient): Promise<void> {
132
+ for (;;) {
133
+ const current = await snapshot(client);
134
+ if (ctx.mode !== "tui") {
135
+ ctx.ui.notify(buildStatusView(current.status, current.metrics).join("\n"), "info");
136
+ return;
137
+ }
138
+ const action = await ctx.ui.custom<PanelAction>((_tui, theme, _keybindings, done) => ({
139
+ invalidate() {},
140
+ render(width: number): string[] {
141
+ const border = theme.fg("borderMuted", "─".repeat(Math.max(1, width)));
142
+ const controls = current.status.paused
143
+ ? "r refresh · p release emergency halt · o override · c clear override · Esc close"
144
+ : "r refresh · p emergency halt · o override · c clear override · Esc close";
145
+ return [
146
+ border,
147
+ truncateToWidth(theme.bold("Jittor"), width, ""),
148
+ border,
149
+ ...buildStatusView(current.status, current.metrics).map((line) => truncateToWidth(` ${line}`, width, "…")),
150
+ border,
151
+ truncateToWidth(theme.fg("dim", controls), width, "…"),
152
+ border,
153
+ ];
154
+ },
155
+ handleInput(data: string): void {
156
+ if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) done("close");
157
+ else if (data === "r") done("refresh");
158
+ else if (data === "p") done(current.status.paused ? "resume" : "pause");
159
+ else if (data === "o") done("override");
160
+ else if (data === "c") done("clear-override");
161
+ },
162
+ }));
163
+ if (!action || action === "close") return;
164
+ if (action === "refresh") { await client.call("telemetry.poll", {}); continue; }
165
+ if (action === "pause" || action === "resume") {
166
+ if (await ctx.ui.confirm(action === "pause" ? "Emergency-halt provider requests?" : "Release emergency halt?", "This changes provider-request enforcement. Use /jittor off to disable blocking entirely.")) {
167
+ await client.call(action === "pause" ? "router.pause" : "router.resume", {});
168
+ }
169
+ continue;
170
+ }
171
+ if (action === "clear-override") {
172
+ if (await ctx.ui.confirm("Clear route override?", "Policy-controlled routing will resume.")) await client.call("router.clear_override", {});
173
+ continue;
174
+ }
175
+ const route = await chooseOverride(ctx, current.status.availableRoutes);
176
+ if (route && await ctx.ui.confirm("Apply route override?", `${route.provider}/${route.model} · ${route.thinking} for one hour`)) {
177
+ await client.call("router.override", { route, expiresAt: Date.now() + 60 * 60 * 1_000 });
178
+ }
179
+ }
180
+ }
@@ -0,0 +1,169 @@
1
+ import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
+ import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
3
+ import { USAGE_CHART_HEIGHT, USAGE_TOKEN_QUERY_LIMIT, USAGE_Y_AXIS_WIDTH } from "../../src/constants.ts";
4
+ import type { StoredMetricObservation } from "../../src/domain/metric.ts";
5
+ import {
6
+ buildUsageHistogram,
7
+ USAGE_RANGES,
8
+ usageRangeStart,
9
+ type UsageBucket,
10
+ type UsageHistogram,
11
+ type UsageRange,
12
+ } from "../../src/domain/usage.ts";
13
+ import type { JittorPanelClient } from "./tui.ts";
14
+
15
+ type UsageAction = "range-prev" | "range-next" | "refresh" | "close";
16
+ type UsageColor = "accent" | "success" | "warning" | "error" | "thinkingText" | "muted" | "dim" | "borderMuted";
17
+
18
+ export interface UsageTheme {
19
+ fg(color: UsageColor, text: string): string;
20
+ bold(text: string): string;
21
+ }
22
+
23
+ const SERIES_COLORS: UsageColor[] = ["accent", "success", "warning", "thinkingText", "error"];
24
+ const PARTIAL_BLOCKS = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
25
+
26
+ function compact(value: number): string {
27
+ if (value >= 1_000_000_000) return `${(value / 1_000_000_000).toFixed(value >= 10_000_000_000 ? 0 : 1)}B`;
28
+ if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(value >= 10_000_000 ? 0 : 1)}M`;
29
+ if (value >= 1_000) return `${(value / 1_000).toFixed(value >= 10_000 ? 0 : 1)}k`;
30
+ return String(Math.round(value));
31
+ }
32
+
33
+ function mergeBuckets(buckets: UsageBucket[], maximum: number): UsageBucket[] {
34
+ if (buckets.length <= maximum) return buckets;
35
+ const result: UsageBucket[] = [];
36
+ for (let index = 0; index < maximum; index += 1) {
37
+ const from = Math.floor(index * buckets.length / maximum);
38
+ const to = Math.max(from + 1, Math.floor((index + 1) * buckets.length / maximum));
39
+ const selected = buckets.slice(from, to);
40
+ const series: Record<string, number> = {};
41
+ for (const bucket of selected) {
42
+ for (const [key, value] of Object.entries(bucket.series)) series[key] = (series[key] ?? 0) + value;
43
+ }
44
+ result.push({
45
+ start: selected[0]!.start,
46
+ end: selected[selected.length - 1]!.end,
47
+ total: selected.reduce((sum, bucket) => sum + bucket.total, 0),
48
+ series,
49
+ });
50
+ }
51
+ return result;
52
+ }
53
+
54
+ function seriesAt(bucket: UsageBucket, chart: UsageHistogram, tokenHeight: number): number {
55
+ let cumulative = 0;
56
+ for (let index = 0; index < chart.series.length; index += 1) {
57
+ cumulative += bucket.series[chart.series[index]!.key] ?? 0;
58
+ if (tokenHeight <= cumulative) return index;
59
+ }
60
+ return Math.max(0, chart.series.length - 1);
61
+ }
62
+
63
+ function formatRangePoint(value: number, range: UsageRange): string {
64
+ const date = new Date(value);
65
+ if (range === "24h") return date.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" });
66
+ return date.toLocaleDateString(undefined, { month: "short", day: "numeric" });
67
+ }
68
+
69
+ function axisLabels(start: number, end: number, range: UsageRange, width: number): string {
70
+ const labels = [formatRangePoint(start, range), formatRangePoint(start + (end - start) / 2, range), formatRangePoint(end, range)];
71
+ const positions = [0, Math.max(0, Math.floor((width - labels[1]!.length) / 2)), Math.max(0, width - labels[2]!.length)];
72
+ const characters = Array.from({ length: width }, () => " ");
73
+ for (let labelIndex = 0; labelIndex < labels.length; labelIndex += 1) {
74
+ for (let index = 0; index < labels[labelIndex]!.length && positions[labelIndex]! + index < width; index += 1) {
75
+ characters[positions[labelIndex]! + index] = labels[labelIndex]![index]!;
76
+ }
77
+ }
78
+ return characters.join("");
79
+ }
80
+
81
+ function plainTheme(): UsageTheme {
82
+ return { fg: (_color, text) => text, bold: (text) => text };
83
+ }
84
+
85
+ export function renderUsageHistogram(chart: UsageHistogram, width: number, theme: UsageTheme): string[] {
86
+ const safeWidth = Math.max(20, width);
87
+ const chartColumns = Math.max(1, Math.floor((safeWidth - USAGE_Y_AXIS_WIDTH - 1) / 2));
88
+ const buckets = mergeBuckets(chart.buckets, chartColumns);
89
+ const barStep = buckets.length * 2 <= safeWidth - USAGE_Y_AXIS_WIDTH ? 2 : 1;
90
+ const plotWidth = buckets.length * barStep;
91
+ const maximum = Math.max(0, ...buckets.map((bucket) => bucket.total));
92
+ const lines = [
93
+ truncateToWidth(theme.bold(`Tokens over time · ${chart.range}`), safeWidth, ""),
94
+ truncateToWidth(`${compact(chart.totalTokens)} tokens · input ${compact(chart.breakdown.input)} · output ${compact(chart.breakdown.output)} · cache ${compact(chart.breakdown.cacheRead + chart.breakdown.cacheWrite)}`, safeWidth, "…"),
95
+ "",
96
+ ];
97
+ if (maximum === 0) {
98
+ lines.push(theme.fg("dim", "No recorded Pi token usage in this range."));
99
+ return lines.map((line) => truncateToWidth(line, safeWidth, "…"));
100
+ }
101
+
102
+ for (let row = 0; row < USAGE_CHART_HEIGHT; row += 1) {
103
+ const fromBottom = USAGE_CHART_HEIGHT - row - 1;
104
+ const label = row === 0 ? compact(maximum) : row === Math.floor(USAGE_CHART_HEIGHT / 2) ? compact(maximum / 2) : "";
105
+ let plot = "";
106
+ for (const bucket of buckets) {
107
+ const scaled = bucket.total / maximum * USAGE_CHART_HEIGHT;
108
+ const occupancy = Math.max(0, Math.min(1, scaled - fromBottom));
109
+ if (occupancy <= 0) {
110
+ plot += " ".repeat(barStep);
111
+ continue;
112
+ }
113
+ const block = PARTIAL_BLOCKS[Math.max(0, Math.ceil(occupancy * PARTIAL_BLOCKS.length) - 1)]!;
114
+ const tokenHeight = Math.min(bucket.total, maximum * (fromBottom + Math.min(occupancy, 0.5)) / USAGE_CHART_HEIGHT);
115
+ const color = SERIES_COLORS[seriesAt(bucket, chart, tokenHeight) % SERIES_COLORS.length]!;
116
+ plot += theme.fg(color, block) + (barStep === 2 ? " " : "");
117
+ }
118
+ lines.push(`${label.padStart(USAGE_Y_AXIS_WIDTH - 2)} ${theme.fg("borderMuted", "│")}${plot}`);
119
+ }
120
+ lines.push(`${"0".padStart(USAGE_Y_AXIS_WIDTH - 2)} ${theme.fg("borderMuted", `└${"─".repeat(plotWidth)}`)}`);
121
+ lines.push(`${" ".repeat(USAGE_Y_AXIS_WIDTH)}${axisLabels(chart.start, chart.end, chart.range, plotWidth)}`);
122
+ lines.push("");
123
+ for (let index = 0; index < chart.series.length; index += 1) {
124
+ const series = chart.series[index]!;
125
+ const bullet = theme.fg(SERIES_COLORS[index % SERIES_COLORS.length]!, "■");
126
+ lines.push(truncateToWidth(`${bullet} ${series.provider}/${series.model} ${compact(series.total)}`, safeWidth, "…"));
127
+ }
128
+ return lines.map((line) => visibleWidth(line) <= safeWidth ? line : truncateToWidth(line, safeWidth, "…"));
129
+ }
130
+
131
+ async function loadUsage(client: JittorPanelClient, range: UsageRange, now: number): Promise<UsageHistogram> {
132
+ const rows = await client.call("metrics.query", {
133
+ source: "pi",
134
+ since: usageRangeStart(range, now),
135
+ until: now,
136
+ order: "desc",
137
+ limit: USAGE_TOKEN_QUERY_LIMIT,
138
+ }) as StoredMetricObservation[];
139
+ return buildUsageHistogram(rows, { range, now });
140
+ }
141
+
142
+ export async function showUsagePanel(ctx: ExtensionCommandContext, client: JittorPanelClient, now = Date.now()): Promise<void> {
143
+ let rangeIndex = 0;
144
+ for (;;) {
145
+ const range = USAGE_RANGES[rangeIndex]!;
146
+ const chart = await loadUsage(client, range, now);
147
+ if (ctx.mode !== "tui") {
148
+ ctx.ui.notify(renderUsageHistogram(chart, 80, plainTheme()).join("\n"), "info");
149
+ return;
150
+ }
151
+ const action = await ctx.ui.custom<UsageAction>((_tui, theme, _keybindings, done) => ({
152
+ invalidate() {},
153
+ render(width: number): string[] {
154
+ const lines = renderUsageHistogram(chart, width, theme);
155
+ const controls = theme.fg("dim", "←/→ range · r refresh · Esc close");
156
+ return [...lines, "", truncateToWidth(controls, width, "…")];
157
+ },
158
+ handleInput(data: string): void {
159
+ if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || data === "q") done("close");
160
+ else if (matchesKey(data, "left")) done("range-prev");
161
+ else if (matchesKey(data, "right")) done("range-next");
162
+ else if (data === "r") done("refresh");
163
+ },
164
+ }));
165
+ if (!action || action === "close") return;
166
+ if (action === "range-prev") rangeIndex = (rangeIndex - 1 + USAGE_RANGES.length) % USAGE_RANGES.length;
167
+ if (action === "range-next") rangeIndex = (rangeIndex + 1) % USAGE_RANGES.length;
168
+ }
169
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@danypops/jittor",
3
+ "version": "0.1.0",
4
+ "description": "Just-in-Time Token Optimizing Router for Pi",
5
+ "type": "module",
6
+ "keywords": ["pi-package", "llm-router", "token-budget"],
7
+ "scripts": {
8
+ "test": "bun test",
9
+ "typecheck": "tsc --noEmit",
10
+ "serve": "bun src/cli.ts serve",
11
+ "service:install": "bun src/cli.ts service install",
12
+ "guard:install": "git config core.hooksPath .githooks"
13
+ },
14
+ "pi": {
15
+ "extensions": ["extension/src/index.ts"]
16
+ },
17
+ "peerDependencies": {
18
+ "@earendil-works/pi-coding-agent": "*",
19
+ "@earendil-works/pi-tui": "*",
20
+ "typebox": "*"
21
+ },
22
+ "devDependencies": {
23
+ "bun-types": "latest"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/DanyPops/jittor.git"
28
+ },
29
+ "files": ["src", "extension", "docs", "README.md"]
30
+ }
@@ -0,0 +1,99 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import { DEFAULT_QUERY_LIMIT, MAX_QUERY_LIMIT } from "../constants.ts";
3
+ import type { MetricObservation, MetricQuery, StoredMetricObservation } from "../domain/metric.ts";
4
+ import { validateMetricObservation } from "../domain/metric.ts";
5
+ import type { MetricStore } from "../ports/metric-store.ts";
6
+
7
+ interface MetricRow {
8
+ id: number;
9
+ source: string;
10
+ scope: string;
11
+ metric: string;
12
+ value: number | null;
13
+ unit: StoredMetricObservation["unit"];
14
+ observed_at: number;
15
+ attributes: string;
16
+ }
17
+
18
+ function fromRow(row: MetricRow): StoredMetricObservation {
19
+ return {
20
+ id: row.id,
21
+ source: row.source,
22
+ scope: row.scope,
23
+ metric: row.metric,
24
+ value: row.value,
25
+ unit: row.unit,
26
+ observedAt: row.observed_at,
27
+ attributes: JSON.parse(row.attributes) as Record<string, unknown>,
28
+ };
29
+ }
30
+
31
+ export class SQLiteMetricStore implements MetricStore {
32
+ constructor(private readonly db: Database) {}
33
+
34
+ record(input: MetricObservation): StoredMetricObservation {
35
+ const observation = validateMetricObservation(input);
36
+ const result = this.db.query(`
37
+ INSERT INTO metric_observations (source, scope, metric, value, unit, observed_at, attributes)
38
+ VALUES (?, ?, ?, ?, ?, ?, ?)
39
+ `).run(
40
+ observation.source,
41
+ observation.scope,
42
+ observation.metric,
43
+ observation.value,
44
+ observation.unit,
45
+ observation.observedAt,
46
+ JSON.stringify(observation.attributes ?? {}),
47
+ );
48
+ return this.get(Number(result.lastInsertRowid));
49
+ }
50
+
51
+ query(filter: MetricQuery = {}): StoredMetricObservation[] {
52
+ const conditions: string[] = [];
53
+ const parameters: Array<string | number> = [];
54
+ const addEquals = (column: string, value: string | undefined): void => {
55
+ if (value === undefined) return;
56
+ conditions.push(`${column} = ?`);
57
+ parameters.push(value);
58
+ };
59
+ addEquals("source", filter.source);
60
+ addEquals("scope", filter.scope);
61
+ addEquals("metric", filter.metric);
62
+ if (filter.since !== undefined) { conditions.push("observed_at >= ?"); parameters.push(filter.since); }
63
+ if (filter.until !== undefined) { conditions.push("observed_at <= ?"); parameters.push(filter.until); }
64
+ const requestedLimit = Number.isFinite(filter.limit) ? Math.floor(filter.limit!) : DEFAULT_QUERY_LIMIT;
65
+ const limit = Math.max(1, Math.min(MAX_QUERY_LIMIT, requestedLimit));
66
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
67
+ const order = filter.order === "desc" ? "DESC" : "ASC";
68
+ const rows = this.db.query(`
69
+ SELECT id, source, scope, metric, value, unit, observed_at, attributes
70
+ FROM metric_observations
71
+ ${where}
72
+ ORDER BY observed_at ${order}, id ${order}
73
+ LIMIT ${limit}
74
+ `).all(...parameters) as MetricRow[];
75
+ return rows.map(fromRow);
76
+ }
77
+
78
+ pruneBefore(cutoff: number): number {
79
+ if (!Number.isSafeInteger(cutoff) || cutoff < 0) throw new Error("cutoff must be a non-negative integer timestamp");
80
+ return this.db.query("DELETE FROM metric_observations WHERE observed_at < ?").run(cutoff).changes;
81
+ }
82
+
83
+ checkpoint(): void {
84
+ this.db.exec("PRAGMA wal_checkpoint(PASSIVE)");
85
+ }
86
+
87
+ close(): void {
88
+ this.db.close();
89
+ }
90
+
91
+ private get(id: number): StoredMetricObservation {
92
+ const row = this.db.query(`
93
+ SELECT id, source, scope, metric, value, unit, observed_at, attributes
94
+ FROM metric_observations WHERE id = ?
95
+ `).get(id) as MetricRow | null;
96
+ if (!row) throw new Error(`metric observation ${id} was not persisted`);
97
+ return fromRow(row);
98
+ }
99
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,73 @@
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
+ import { fileURLToPath } from "node:url";
7
+ import { SYSTEMD_UNIT_NAME } from "./constants.ts";
8
+ import { serveMain } from "./daemon.ts";
9
+ import { resolveJittorPaths } from "./state.ts";
10
+
11
+ export interface SystemdUnitOptions {
12
+ bunBin: string;
13
+ cliPath: string;
14
+ codexAuthFile?: string;
15
+ }
16
+
17
+ export function renderSystemdUnit(options: SystemdUnitOptions): string {
18
+ return `[Unit]
19
+ Description=Jittor token optimizing router
20
+ After=default.target network-online.target
21
+ Wants=network-online.target
22
+
23
+ [Service]
24
+ Type=simple
25
+ ExecStart=${options.bunBin} ${options.cliPath} serve
26
+ ${options.codexAuthFile ? `Environment="JITTOR_CODEX_AUTH_FILE=${options.codexAuthFile.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"\n` : ""}Restart=always
27
+ RestartSec=2
28
+ NoNewPrivileges=true
29
+ PrivateTmp=true
30
+
31
+ [Install]
32
+ WantedBy=default.target
33
+ `;
34
+ }
35
+
36
+ function systemctl(...args: string[]): void {
37
+ execFileSync("systemctl", ["--user", ...args], { stdio: "inherit" });
38
+ }
39
+
40
+ function installService(): void {
41
+ const unitPath = resolveJittorPaths().systemdUnit;
42
+ mkdirSync(dirname(unitPath), { recursive: true });
43
+ const codexAuthFile = join(process.env["CODEX_HOME"] ?? join(homedir(), ".codex"), "auth.json");
44
+ writeFileSync(unitPath, renderSystemdUnit({
45
+ bunBin: process.execPath,
46
+ cliPath: fileURLToPath(import.meta.url),
47
+ ...(existsSync(codexAuthFile) ? { codexAuthFile } : {}),
48
+ }));
49
+ systemctl("daemon-reload");
50
+ systemctl("enable", SYSTEMD_UNIT_NAME);
51
+ systemctl("restart", SYSTEMD_UNIT_NAME);
52
+ }
53
+
54
+ function usage(): never {
55
+ console.error("Usage: jittor serve | service <install|start|stop|restart|status>");
56
+ process.exit(2);
57
+ }
58
+
59
+ export function main(args: string[] = process.argv.slice(2)): void {
60
+ const [command, action] = args;
61
+ if (command === "serve") { serveMain(); return; }
62
+ if (command !== "service") usage();
63
+ switch (action) {
64
+ case "install": installService(); break;
65
+ case "start": systemctl("start", SYSTEMD_UNIT_NAME); break;
66
+ case "stop": systemctl("stop", SYSTEMD_UNIT_NAME); break;
67
+ case "restart": systemctl("restart", SYSTEMD_UNIT_NAME); break;
68
+ case "status": systemctl("status", SYSTEMD_UNIT_NAME); break;
69
+ default: usage();
70
+ }
71
+ }
72
+
73
+ if (import.meta.main) main();
package/src/client.ts ADDED
@@ -0,0 +1,57 @@
1
+ import type { OperationInputs, OperationName, OperationOutputs } from "./service.ts";
2
+ import { ensureAuthToken, readDaemonHandle, resolveJittorPaths, type JittorPaths } from "./state.ts";
3
+
4
+ export type FetchTransport = (request: Request) => Promise<Response>;
5
+
6
+ export class JittorClient {
7
+ constructor(
8
+ private readonly baseUrl: string,
9
+ private readonly token: string,
10
+ private readonly transport: FetchTransport = fetch,
11
+ ) {}
12
+
13
+ async call<Name extends OperationName>(operation: Name, input: OperationInputs[Name]): Promise<OperationOutputs[Name]> {
14
+ const response = await this.transport(new Request(`${this.baseUrl}/api/v1/ops`, {
15
+ method: "POST",
16
+ headers: { authorization: `Bearer ${this.token}`, "content-type": "application/json" },
17
+ body: JSON.stringify({ op: operation, input }),
18
+ }));
19
+ const body = await response.json() as { result?: OperationOutputs[Name]; error?: string };
20
+ if (!response.ok) throw new Error(body.error ?? `Jittor operation failed with HTTP ${response.status}`);
21
+ return body.result as OperationOutputs[Name];
22
+ }
23
+
24
+ async operations(): Promise<OperationName[]> {
25
+ const response = await this.transport(new Request(`${this.baseUrl}/api/v1/ops`, {
26
+ headers: { authorization: `Bearer ${this.token}` },
27
+ }));
28
+ const body = await response.json() as { operations?: OperationName[]; error?: string };
29
+ if (!response.ok) throw new Error(body.error ?? `Jittor discovery failed with HTTP ${response.status}`);
30
+ return body.operations ?? [];
31
+ }
32
+
33
+ async ready(): Promise<boolean> {
34
+ const response = await this.transport(new Request(`${this.baseUrl}/ready`, {
35
+ headers: { authorization: `Bearer ${this.token}` },
36
+ }));
37
+ if (response.status === 503) return false;
38
+ if (!response.ok) throw new Error(`Jittor readiness check failed with HTTP ${response.status}`);
39
+ return true;
40
+ }
41
+
42
+ async health(): Promise<{ ok: true; version: string }> {
43
+ const response = await this.transport(new Request(`${this.baseUrl}/health`, {
44
+ headers: { authorization: `Bearer ${this.token}` },
45
+ }));
46
+ const body = await response.json() as { ok?: boolean; version?: string; error?: string };
47
+ if (!response.ok || body.ok !== true || typeof body.version !== "string") throw new Error(body.error ?? "Jittor health check failed");
48
+ return { ok: true, version: body.version };
49
+ }
50
+ }
51
+
52
+ export function connectJittorClient(paths: JittorPaths = resolveJittorPaths()): JittorClient {
53
+ const handle = readDaemonHandle(paths);
54
+ if (!handle) throw new Error("Jittor daemon is not running; install or start jittor.service");
55
+ const token = ensureAuthToken(paths);
56
+ return new JittorClient(`http://${handle.host}:${handle.port}`, token);
57
+ }
package/src/config.ts ADDED
@@ -0,0 +1,20 @@
1
+ import type { PolicyConfig, Route } from "./policy.ts";
2
+
3
+ /** Replaced by Pi's active model and authenticated ModelRegistry routes before enforcement. */
4
+ export const UNCONFIGURED_ROUTE: Route = { provider: "unconfigured", model: "unconfigured", thinking: "off" };
5
+
6
+ export const DEFAULT_POLICY: PolicyConfig = {
7
+ maxTelemetryAgeMs: 120_000,
8
+ cooldownMs: 300_000,
9
+ hysteresisFraction: 0.1,
10
+ thresholds: {
11
+ throttle: 1,
12
+ lowerThinking: 1.25,
13
+ switchModel: 1.5,
14
+ switchProvider: 2,
15
+ halt: 3,
16
+ },
17
+ maxThrottleMs: 30_000,
18
+ hardStopUsedFraction: 0.99,
19
+ minimumConfidence: 0.5,
20
+ };
@@ -0,0 +1,26 @@
1
+ export const VERSION = "0.1.0";
2
+ export const SQLITE_SCHEMA_VERSION = 1;
3
+ export const SQLITE_BUSY_TIMEOUT_MS = 5_000;
4
+ export const DEFAULT_QUERY_LIMIT = 1_000;
5
+ export const MAX_QUERY_LIMIT = 10_000;
6
+ export const SERVICE_MAX_BODY_BYTES = 1_048_576;
7
+ export const MAINTENANCE_INTERVAL_MS = 15 * 60 * 1_000;
8
+ export const TELEMETRY_POLL_INTERVAL_MS = 60_000;
9
+ export const TELEMETRY_STALE_AFTER_MS = 120_000;
10
+ export const FOOTER_CONTEXT_ACCENT_FRACTION = 0.5;
11
+ export const FOOTER_CONTEXT_WARNING_FRACTION = 0.7;
12
+ export const FOOTER_CONTEXT_ERROR_FRACTION = 0.9;
13
+ export const FOOTER_BAR_MIN_WIDTH = 4;
14
+ export const FOOTER_BAR_MAX_WIDTH = 8;
15
+ export const FOOTER_WIDE_TERMINAL_WIDTH = 100;
16
+ export const LOOPBACK_HOST = "127.0.0.1";
17
+ export const JITTOR_STATE_DIRECTORY = "jittor";
18
+ export const JITTOR_EXTENSION_SETTINGS_FILENAME = "extension.json";
19
+ export const DATABASE_FILENAME = "jittor.db";
20
+ export const TOKEN_FILENAME = "auth-token";
21
+ export const HANDLE_FILENAME = "daemon.json";
22
+ export const SYSTEMD_UNIT_NAME = "jittor.service";
23
+ export const USAGE_CHART_HEIGHT = 8;
24
+ export const USAGE_Y_AXIS_WIDTH = 7;
25
+ export const USAGE_TOKEN_QUERY_LIMIT = 10_000;
26
+ export const MAX_DYNAMIC_ROUTES = 100;