@danypops/pi-jittor 0.2.0 → 0.3.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 +19 -3
- package/docs/USAGE_PRIOR_ART.md +1 -1
- package/extension/src/index.ts +379 -118
- package/extension/src/{context-breakdown.ts → observability/context-breakdown.ts} +251 -47
- package/extension/src/observability/context-growth.ts +26 -0
- package/extension/src/{capabilities → observability}/context-hub.ts +1 -5
- package/extension/src/observability/context-report.ts +92 -0
- package/extension/src/observability/context-view.ts +264 -0
- package/extension/src/{footer.ts → observability/footer.ts} +63 -26
- package/extension/src/{capabilities/local-run-telemetry.ts → observability/model-run.ts} +14 -10
- package/extension/src/observability/provider-context-snapshot.ts +246 -0
- package/extension/src/{capabilities/provider-response-telemetry.ts → observability/provider-response.ts} +21 -7
- package/extension/src/{tui.ts → observability/status.ts} +202 -72
- package/extension/src/observability/usage.ts +314 -0
- package/extension/src/optimization/model-selection-panel.ts +160 -0
- package/extension/src/{capabilities/codex-recovery.ts → optimization/recovery/codex.ts} +41 -25
- package/extension/src/service-client.ts +49 -2
- package/extension/src/settings-tui.ts +73 -33
- package/extension/src/settings.ts +40 -29
- package/package.json +11 -5
- package/extension/src/benchmark-tui.ts +0 -113
- package/extension/src/context-report.ts +0 -49
- package/extension/src/context-view.ts +0 -108
- package/extension/src/usage.ts +0 -324
- /package/extension/src/{capabilities → observability}/http-headers.ts +0 -0
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { matchesKey, truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
1
|
import {
|
|
4
2
|
HUMAN_STATUS_MAX_SOURCES,
|
|
5
3
|
HUMAN_TEXT_FIELD_MAX_CHARACTERS,
|
|
@@ -8,9 +6,12 @@ import {
|
|
|
8
6
|
type Route,
|
|
9
7
|
type RouterStatus,
|
|
10
8
|
type StoredMetricObservation,
|
|
9
|
+
TELEMETRY_STALE_AFTER_MS,
|
|
11
10
|
} from "@danypops/jittor";
|
|
11
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
12
|
+
import { matchesKey, truncateToWidth } from "@earendil-works/pi-tui";
|
|
13
|
+
import { sessionSecretField } from "../session-identity.ts";
|
|
12
14
|
import type { ProviderBudget } from "./footer.ts";
|
|
13
|
-
import { sessionSecretField } from "./session-identity.ts";
|
|
14
15
|
|
|
15
16
|
export interface JittorPanelClient {
|
|
16
17
|
call(operation: string, input: unknown): Promise<any>;
|
|
@@ -18,22 +19,34 @@ export interface JittorPanelClient {
|
|
|
18
19
|
|
|
19
20
|
export function providerBudgetMetricQuery(status: RouterStatus): MetricQuery | null {
|
|
20
21
|
switch (status.currentRoute?.provider) {
|
|
21
|
-
case "openai-codex":
|
|
22
|
-
|
|
23
|
-
case "
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
case "openai-codex":
|
|
23
|
+
return { source: "codex-subscription", metric: "used-fraction", order: "desc", limit: 100 };
|
|
24
|
+
case "openrouter":
|
|
25
|
+
return { source: "openrouter", order: "desc", limit: 20 };
|
|
26
|
+
case "anthropic":
|
|
27
|
+
return { source: "anthropic", metric: "used-fraction", order: "desc", limit: 20 };
|
|
28
|
+
case "anthropic-vertex":
|
|
29
|
+
return { source: "anthropic-vertex", metric: "used-fraction", order: "desc", limit: 20 };
|
|
30
|
+
default:
|
|
31
|
+
return null;
|
|
26
32
|
}
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
type PanelAction = "pause" | "resume" | "refresh" | "override" | "clear-override" | "close";
|
|
30
36
|
|
|
31
|
-
function latest(
|
|
37
|
+
function latest(
|
|
38
|
+
rows: StoredMetricObservation[],
|
|
39
|
+
predicate: (row: StoredMetricObservation) => boolean,
|
|
40
|
+
): StoredMetricObservation | undefined {
|
|
32
41
|
return rows.filter(predicate).sort((left, right) => right.observedAt - left.observedAt || right.id - left.id)[0];
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
function sanitizedText(value: string): string {
|
|
36
|
-
return value
|
|
45
|
+
return value
|
|
46
|
+
.replace(/[\r\n\t]/g, " ")
|
|
47
|
+
.replace(/ +/g, " ")
|
|
48
|
+
.trim()
|
|
49
|
+
.slice(0, HUMAN_TEXT_FIELD_MAX_CHARACTERS);
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
function routeText(route: Route): string {
|
|
@@ -45,26 +58,31 @@ function normalizedIdentity(value: unknown): string {
|
|
|
45
58
|
}
|
|
46
59
|
|
|
47
60
|
function longestWindow(rows: StoredMetricObservation[]): StoredMetricObservation | undefined {
|
|
48
|
-
return [...rows].sort(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
return [...rows].sort(
|
|
62
|
+
(left, right) =>
|
|
63
|
+
Number(right.attributes.windowSeconds ?? 0) - Number(left.attributes.windowSeconds ?? 0) ||
|
|
64
|
+
right.observedAt - left.observedAt ||
|
|
65
|
+
right.id - left.id,
|
|
52
66
|
)[0];
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
function codexWindowForModel(rows: StoredMetricObservation[], model: string): StoredMetricObservation | undefined {
|
|
56
|
-
const codexRows = rows.filter(
|
|
70
|
+
const codexRows = rows.filter(
|
|
71
|
+
(row) => row.source === "codex-subscription" && row.metric === "used-fraction" && typeof row.value === "number",
|
|
72
|
+
);
|
|
57
73
|
const modelIdentity = normalizedIdentity(model);
|
|
58
74
|
const matchingAdditional = codexRows.filter((row) => {
|
|
59
|
-
const limitId = normalizedIdentity(row.attributes
|
|
60
|
-
const limitName = normalizedIdentity(row.attributes
|
|
75
|
+
const limitId = normalizedIdentity(row.attributes.limitId);
|
|
76
|
+
const limitName = normalizedIdentity(row.attributes.limitName);
|
|
61
77
|
return limitId !== "codex" && limitName.length > 0 && limitName === modelIdentity;
|
|
62
78
|
});
|
|
63
79
|
if (matchingAdditional.length > 0) return longestWindow(matchingAdditional);
|
|
64
|
-
return longestWindow(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
80
|
+
return longestWindow(
|
|
81
|
+
codexRows.filter((row) => {
|
|
82
|
+
const limitId = normalizedIdentity(row.attributes.limitId);
|
|
83
|
+
return limitId === "codex" || (limitId.length === 0 && row.scope.startsWith("codex:"));
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
function compactWindowName(seconds: number): string {
|
|
@@ -87,25 +105,68 @@ function windowName(seconds: number): string {
|
|
|
87
105
|
* could ever read -- see google-vertex-contracts.ts); the footer omits the segment entirely rather
|
|
88
106
|
* than showing a `?` that can never resolve.
|
|
89
107
|
*/
|
|
90
|
-
|
|
91
|
-
|
|
108
|
+
type CodexTelemetryState = "available" | "missing" | "failed" | "stale";
|
|
109
|
+
|
|
110
|
+
function codexTelemetryState(status: RouterStatus, now: number): CodexTelemetryState {
|
|
111
|
+
const source = status.sources.find((candidate) => candidate.id === "codex-subscription");
|
|
112
|
+
if (!source) return "missing";
|
|
113
|
+
if (!source.ok) return "failed";
|
|
114
|
+
if (source.observedAt !== undefined && now - source.observedAt > TELEMETRY_STALE_AFTER_MS) return "stale";
|
|
115
|
+
return "available";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function unavailableCodexBudget(
|
|
119
|
+
label: string,
|
|
120
|
+
reason: "reset pending" | "telemetry unavailable" | "telemetry failed" | "telemetry stale",
|
|
121
|
+
): ProviderBudget {
|
|
122
|
+
return { kind: "unavailable", label, valueText: reason };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function buildFooterBudget(
|
|
126
|
+
status: RouterStatus,
|
|
127
|
+
metrics: StoredMetricObservation[],
|
|
128
|
+
now = Date.now(),
|
|
129
|
+
): ProviderBudget | null | undefined {
|
|
130
|
+
if (!status.currentRoute) return null;
|
|
92
131
|
if (status.currentRoute.provider === "openai-codex") {
|
|
93
132
|
const codex = codexWindowForModel(metrics, status.currentRoute.model);
|
|
94
|
-
|
|
95
|
-
|
|
133
|
+
const sourceState = codexTelemetryState(status, now);
|
|
134
|
+
if (!codex || typeof codex.value !== "number") {
|
|
135
|
+
if (sourceState === "missing") return unavailableCodexBudget("Codex", "telemetry unavailable");
|
|
136
|
+
if (sourceState === "failed") return unavailableCodexBudget("Codex", "telemetry failed");
|
|
137
|
+
if (sourceState === "stale") return unavailableCodexBudget("Codex", "telemetry stale");
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
const label = compactWindowName(Number(codex.attributes.windowSeconds ?? 0));
|
|
141
|
+
const resetsAtSeconds = Number(codex.attributes.resetsAt);
|
|
142
|
+
const resetsAt = Number.isFinite(resetsAtSeconds) && resetsAtSeconds > 0 ? resetsAtSeconds * 1_000 : undefined;
|
|
143
|
+
if (resetsAt !== undefined && resetsAt <= now) return unavailableCodexBudget(label, "reset pending");
|
|
144
|
+
if (now - codex.observedAt > TELEMETRY_STALE_AFTER_MS) {
|
|
145
|
+
if (sourceState === "missing") return unavailableCodexBudget(label, "telemetry unavailable");
|
|
146
|
+
if (sourceState === "failed") return unavailableCodexBudget(label, "telemetry failed");
|
|
147
|
+
return unavailableCodexBudget(label, "telemetry stale");
|
|
148
|
+
}
|
|
96
149
|
return {
|
|
97
150
|
kind: "bounded",
|
|
98
|
-
label
|
|
151
|
+
label,
|
|
99
152
|
remainingFraction: 1 - codex.value,
|
|
100
153
|
observedAt: codex.observedAt,
|
|
101
|
-
...(
|
|
154
|
+
...(resetsAt !== undefined ? { resetsAt } : {}),
|
|
102
155
|
};
|
|
103
156
|
}
|
|
157
|
+
if (!status.ready) return null;
|
|
104
158
|
if (status.currentRoute.provider === "anthropic") {
|
|
105
|
-
const anthropic =
|
|
106
|
-
|
|
159
|
+
const anthropic =
|
|
160
|
+
latest(
|
|
161
|
+
metrics,
|
|
162
|
+
(row) => row.source === "anthropic" && row.metric === "used-fraction" && row.scope === "tokens" && typeof row.value === "number",
|
|
163
|
+
) ??
|
|
164
|
+
latest(
|
|
165
|
+
metrics,
|
|
166
|
+
(row) => row.source === "anthropic" && row.metric === "used-fraction" && row.scope === "requests" && typeof row.value === "number",
|
|
167
|
+
);
|
|
107
168
|
if (!anthropic || typeof anthropic.value !== "number") return null;
|
|
108
|
-
const resetsAt = Number(anthropic.attributes
|
|
169
|
+
const resetsAt = Number(anthropic.attributes.resetsAt);
|
|
109
170
|
return {
|
|
110
171
|
kind: "bounded",
|
|
111
172
|
label: anthropic.scope === "tokens" ? "tok" : "req",
|
|
@@ -121,10 +182,19 @@ export function buildFooterBudget(status: RouterStatus, metrics: StoredMetricObs
|
|
|
121
182
|
// pool even if the header shape is identical. If nothing was ever observed, this stays null
|
|
122
183
|
// (may still resolve later), not undefined (never possible) -- unlike google-vertex, this
|
|
123
184
|
// provider's transport has not been shown to structurally lack the signal.
|
|
124
|
-
const anthropicVertex =
|
|
125
|
-
|
|
185
|
+
const anthropicVertex =
|
|
186
|
+
latest(
|
|
187
|
+
metrics,
|
|
188
|
+
(row) =>
|
|
189
|
+
row.source === "anthropic-vertex" && row.metric === "used-fraction" && row.scope === "tokens" && typeof row.value === "number",
|
|
190
|
+
) ??
|
|
191
|
+
latest(
|
|
192
|
+
metrics,
|
|
193
|
+
(row) =>
|
|
194
|
+
row.source === "anthropic-vertex" && row.metric === "used-fraction" && row.scope === "requests" && typeof row.value === "number",
|
|
195
|
+
);
|
|
126
196
|
if (!anthropicVertex || typeof anthropicVertex.value !== "number") return null;
|
|
127
|
-
const resetsAt = Number(anthropicVertex.attributes
|
|
197
|
+
const resetsAt = Number(anthropicVertex.attributes.resetsAt);
|
|
128
198
|
return {
|
|
129
199
|
kind: "bounded",
|
|
130
200
|
label: anthropicVertex.scope === "tokens" ? "vtok" : "vreq",
|
|
@@ -135,9 +205,12 @@ export function buildFooterBudget(status: RouterStatus, metrics: StoredMetricObs
|
|
|
135
205
|
}
|
|
136
206
|
if (status.currentRoute.provider === "openrouter") {
|
|
137
207
|
const openRouter = latest(metrics, (row) => row.source === "openrouter" && row.metric === "usage" && typeof row.value === "number");
|
|
138
|
-
const remaining = latest(
|
|
208
|
+
const remaining = latest(
|
|
209
|
+
metrics,
|
|
210
|
+
(row) => row.source === "openrouter" && row.metric === "remaining-fraction" && typeof row.value === "number",
|
|
211
|
+
);
|
|
139
212
|
if (remaining && typeof remaining.value === "number" && (!openRouter || remaining.observedAt >= openRouter.observedAt)) {
|
|
140
|
-
const reset = typeof remaining.attributes
|
|
213
|
+
const reset = typeof remaining.attributes.reset === "string" ? sanitizedText(remaining.attributes.reset) : undefined;
|
|
141
214
|
return {
|
|
142
215
|
kind: "bounded",
|
|
143
216
|
label: "OR",
|
|
@@ -152,79 +225,122 @@ export function buildFooterBudget(status: RouterStatus, metrics: StoredMetricObs
|
|
|
152
225
|
return undefined;
|
|
153
226
|
}
|
|
154
227
|
|
|
155
|
-
export function formatFooterStatus(status: RouterStatus, metrics: StoredMetricObservation[]): string {
|
|
156
|
-
const budget = buildFooterBudget(status, metrics);
|
|
228
|
+
export function formatFooterStatus(status: RouterStatus, metrics: StoredMetricObservation[], now = Date.now()): string {
|
|
229
|
+
const budget = buildFooterBudget(status, metrics, now);
|
|
157
230
|
if (!budget) return "";
|
|
158
|
-
|
|
231
|
+
if (budget.kind === "unbounded") return budget.valueText;
|
|
232
|
+
if (budget.kind === "unavailable") return `${budget.label} ${budget.valueText}`;
|
|
233
|
+
return `${budget.label} ${(budget.remainingFraction * 100).toFixed(1)}% left`;
|
|
159
234
|
}
|
|
160
235
|
|
|
161
236
|
function nextAction(action: PolicyAction | undefined): string {
|
|
162
237
|
switch (action) {
|
|
163
|
-
case "continue":
|
|
164
|
-
|
|
165
|
-
case "
|
|
166
|
-
|
|
167
|
-
case "
|
|
168
|
-
|
|
169
|
-
|
|
238
|
+
case "continue":
|
|
239
|
+
return "throttle";
|
|
240
|
+
case "throttle":
|
|
241
|
+
return "lower thinking";
|
|
242
|
+
case "lower-thinking":
|
|
243
|
+
return "switch model";
|
|
244
|
+
case "switch-model":
|
|
245
|
+
return "switch provider";
|
|
246
|
+
case "switch-provider":
|
|
247
|
+
return "halt";
|
|
248
|
+
case "halt":
|
|
249
|
+
return "halted";
|
|
250
|
+
default:
|
|
251
|
+
return "waiting for decision";
|
|
170
252
|
}
|
|
171
253
|
}
|
|
172
254
|
|
|
173
255
|
function burnLine(rows: StoredMetricObservation[], current: StoredMetricObservation, now: number): string {
|
|
174
256
|
const previous = rows
|
|
175
|
-
.filter(
|
|
257
|
+
.filter(
|
|
258
|
+
(row) =>
|
|
259
|
+
row.source === current.source &&
|
|
260
|
+
row.scope === current.scope &&
|
|
261
|
+
row.metric === current.metric &&
|
|
262
|
+
row.id !== current.id &&
|
|
263
|
+
row.observedAt < current.observedAt,
|
|
264
|
+
)
|
|
176
265
|
.sort((left, right) => right.observedAt - left.observedAt)[0];
|
|
177
|
-
const resetsAt = Number(current.attributes
|
|
266
|
+
const resetsAt = Number(current.attributes.resetsAt ?? 0) * 1_000;
|
|
178
267
|
const remainingSeconds = (resetsAt - now) / 1_000;
|
|
179
268
|
const sustainable = typeof current.value === "number" && remainingSeconds > 0 ? (1 - current.value) / remainingSeconds : null;
|
|
180
|
-
const observed =
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
269
|
+
const observed =
|
|
270
|
+
previous && typeof previous.value === "number" && typeof current.value === "number" && current.observedAt > previous.observedAt
|
|
271
|
+
? (current.value - previous.value) / ((current.observedAt - previous.observedAt) / 1_000)
|
|
272
|
+
: null;
|
|
273
|
+
const perHour = (rate: number | null) => (rate === null ? "n/a" : `${(rate * 3_600 * 100).toFixed(2)}%/h`);
|
|
184
274
|
return `Burn: observed ${perHour(observed)} · sustainable ${perHour(sustainable)}`;
|
|
185
275
|
}
|
|
186
276
|
|
|
187
277
|
export function buildStatusView(status: RouterStatus, metrics: StoredMetricObservation[], now = Date.now()): string[] {
|
|
188
278
|
const lines = [status.ready ? "Ready" : "Not ready"];
|
|
189
279
|
const codex = status.currentRoute?.provider === "openai-codex" ? codexWindowForModel(metrics, status.currentRoute.model) : undefined;
|
|
190
|
-
|
|
191
|
-
|
|
280
|
+
const budget = buildFooterBudget(status, metrics, now);
|
|
281
|
+
if (codex && typeof codex.value === "number" && budget?.kind === "bounded") {
|
|
282
|
+
const seconds = Number(codex.attributes.windowSeconds ?? 0);
|
|
192
283
|
lines.push(`Codex ${windowName(seconds)}: ${((1 - codex.value) * 100).toFixed(1)}% left`);
|
|
193
284
|
lines.push(burnLine(metrics, codex, now));
|
|
285
|
+
} else if (status.currentRoute?.provider === "openai-codex" && budget?.kind === "unavailable") {
|
|
286
|
+
const seconds = Number(codex?.attributes.windowSeconds ?? 0);
|
|
287
|
+
lines.push(`Codex ${codex ? windowName(seconds) : "subscription"}: ${budget.valueText}`);
|
|
194
288
|
}
|
|
195
|
-
const openRouter =
|
|
196
|
-
|
|
197
|
-
|
|
289
|
+
const openRouter =
|
|
290
|
+
status.currentRoute?.provider === "openrouter"
|
|
291
|
+
? latest(metrics, (row) => row.source === "openrouter" && row.metric === "usage" && typeof row.value === "number")
|
|
292
|
+
: undefined;
|
|
198
293
|
if (openRouter && typeof openRouter.value === "number") lines.push(`OpenRouter spend: $${openRouter.value.toFixed(3)}`);
|
|
199
|
-
const anthropic =
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
294
|
+
const anthropic =
|
|
295
|
+
status.currentRoute?.provider === "anthropic"
|
|
296
|
+
? (latest(
|
|
297
|
+
metrics,
|
|
298
|
+
(row) => row.source === "anthropic" && row.metric === "used-fraction" && row.scope === "tokens" && typeof row.value === "number",
|
|
299
|
+
) ??
|
|
300
|
+
latest(
|
|
301
|
+
metrics,
|
|
302
|
+
(row) =>
|
|
303
|
+
row.source === "anthropic" && row.metric === "used-fraction" && row.scope === "requests" && typeof row.value === "number",
|
|
304
|
+
))
|
|
305
|
+
: undefined;
|
|
306
|
+
if (anthropic && typeof anthropic.value === "number")
|
|
307
|
+
lines.push(`Anthropic ${anthropic.scope}: ${((1 - anthropic.value) * 100).toFixed(1)}% left`);
|
|
204
308
|
if (status.currentRoute) lines.push(`Route: ${routeText(status.currentRoute)}`);
|
|
205
|
-
if (status.lastDecision)
|
|
309
|
+
if (status.lastDecision)
|
|
310
|
+
lines.push(
|
|
311
|
+
`Pressure: ${Number.isFinite(status.lastDecision.pressure) ? status.lastDecision.pressure.toFixed(3) : "∞"} · ${status.lastDecision.action}`,
|
|
312
|
+
);
|
|
206
313
|
lines.push(`Next: ${nextAction(status.lastDecision?.action)}`);
|
|
207
314
|
lines.push("Telemetry:");
|
|
208
315
|
const providerSources = status.sources.filter((source) => source.provider === status.currentRoute?.provider);
|
|
316
|
+
if (status.currentRoute?.provider === "openai-codex" && providerSources.length === 0)
|
|
317
|
+
lines.push(" codex-subscription: unavailable · not configured by active daemon");
|
|
209
318
|
for (const source of providerSources.slice(0, HUMAN_STATUS_MAX_SOURCES)) {
|
|
210
319
|
const freshness = !source.ok ? "failed" : source.observedAt !== undefined && now - source.observedAt > 120_000 ? "stale" : "fresh";
|
|
211
320
|
lines.push(` ${sanitizedText(source.id)}: ${freshness} · ${source.metrics} metrics`);
|
|
212
321
|
}
|
|
213
|
-
if (providerSources.length > HUMAN_STATUS_MAX_SOURCES)
|
|
322
|
+
if (providerSources.length > HUMAN_STATUS_MAX_SOURCES)
|
|
323
|
+
lines.push(` … ${providerSources.length - HUMAN_STATUS_MAX_SOURCES} more telemetry sources omitted`);
|
|
214
324
|
if (status.override) lines.push(`Override: ${routeText(status.override.route)}`);
|
|
215
325
|
if (status.paused) lines.push("Emergency halt is active");
|
|
216
326
|
return lines;
|
|
217
327
|
}
|
|
218
328
|
|
|
219
|
-
async function snapshot(
|
|
220
|
-
|
|
329
|
+
async function snapshot(
|
|
330
|
+
client: JittorPanelClient,
|
|
331
|
+
sessionId: string,
|
|
332
|
+
): Promise<{ status: RouterStatus; metrics: StoredMetricObservation[] }> {
|
|
333
|
+
const status = (await client.call("router.status", { session_id: sessionId })) as RouterStatus;
|
|
221
334
|
const query = providerBudgetMetricQuery(status);
|
|
222
|
-
const metrics = query ? await client.call("metrics.query", query) as StoredMetricObservation[] : [];
|
|
335
|
+
const metrics = query ? ((await client.call("metrics.query", query)) as StoredMetricObservation[]) : [];
|
|
223
336
|
return { status, metrics };
|
|
224
337
|
}
|
|
225
338
|
|
|
226
339
|
async function chooseOverride(ctx: ExtensionCommandContext, routes: Route[]): Promise<Route | undefined> {
|
|
227
|
-
if (routes.length === 0) {
|
|
340
|
+
if (routes.length === 0) {
|
|
341
|
+
ctx.ui.notify("Pi reports no authenticated routes for the current provider.", "warning");
|
|
342
|
+
return undefined;
|
|
343
|
+
}
|
|
228
344
|
const labels = routes.map(routeText);
|
|
229
345
|
const selected = await ctx.ui.select("Override route", labels);
|
|
230
346
|
const index = selected ? labels.indexOf(selected) : -1;
|
|
@@ -265,20 +381,34 @@ export async function showJittorPanel(ctx: ExtensionCommandContext, client: Jitt
|
|
|
265
381
|
},
|
|
266
382
|
}));
|
|
267
383
|
if (!action || action === "close") return;
|
|
268
|
-
if (action === "refresh") {
|
|
384
|
+
if (action === "refresh") {
|
|
385
|
+
await client.call("telemetry.poll", {});
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
269
388
|
if (action === "pause" || action === "resume") {
|
|
270
|
-
if (
|
|
389
|
+
if (
|
|
390
|
+
await ctx.ui.confirm(
|
|
391
|
+
action === "pause" ? "Emergency-halt provider requests?" : "Release emergency halt?",
|
|
392
|
+
"This changes provider-request enforcement. Use /jittor off to disable blocking entirely.",
|
|
393
|
+
)
|
|
394
|
+
) {
|
|
271
395
|
await client.call(action === "pause" ? "router.pause" : "router.resume", { session_id, ...sessionSecretField(session_id) });
|
|
272
396
|
}
|
|
273
397
|
continue;
|
|
274
398
|
}
|
|
275
399
|
if (action === "clear-override") {
|
|
276
|
-
if (await ctx.ui.confirm("Clear route override?", "Policy-controlled routing will resume."))
|
|
400
|
+
if (await ctx.ui.confirm("Clear route override?", "Policy-controlled routing will resume."))
|
|
401
|
+
await client.call("router.clear_override", { session_id, ...sessionSecretField(session_id) });
|
|
277
402
|
continue;
|
|
278
403
|
}
|
|
279
404
|
const route = await chooseOverride(ctx, current.status.availableRoutes);
|
|
280
|
-
if (route && await ctx.ui.confirm("Apply route override?", `${routeText(route)} for one hour`)) {
|
|
281
|
-
await client.call("router.override", {
|
|
405
|
+
if (route && (await ctx.ui.confirm("Apply route override?", `${routeText(route)} for one hour`))) {
|
|
406
|
+
await client.call("router.override", {
|
|
407
|
+
route,
|
|
408
|
+
expiresAt: Date.now() + 60 * 60 * 1_000,
|
|
409
|
+
session_id,
|
|
410
|
+
...sessionSecretField(session_id),
|
|
411
|
+
});
|
|
282
412
|
}
|
|
283
413
|
}
|
|
284
414
|
}
|