@raingor/pi-web-switch 0.4.1 → 0.4.3
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.ja.md +32 -8
- package/README.md +58 -10
- package/README.zh-CN.md +31 -7
- package/dist/index.html +18 -0
- package/package.json +5 -25
- package/pi-package/index.ts +228 -4
- package/public/trayIconTemplate.png +0 -0
- package/server/pi-reader.ts +251 -3
- package/src/App.tsx +0 -2
- package/src/components/dashboard/DashboardPage.tsx +43 -40
- package/src/components/layout/Sidebar.tsx +1 -3
- package/src/components/providers/ProvidersModelsPage.tsx +56 -4
- package/src/components/sessions/MemoryPage.tsx +2 -1
- package/src/components/settings/SettingsPage.tsx +72 -0
- package/src/lib/translations/en.ts +15 -58
- package/src/lib/translations/ja.ts +15 -58
- package/src/lib/translations/zh-CN.ts +15 -58
- package/src/lib/translations/zh-TW.ts +15 -58
- package/src/types/index.ts +1 -0
- package/vite.config.ts +89 -15
- package/server/agent-session-manager.ts +0 -827
- package/server/chat-api-plugin.ts +0 -488
- package/src/components/chat/ChatInput.tsx +0 -863
- package/src/components/chat/ChatPage.tsx +0 -617
- package/src/components/chat/ChatWindow.tsx +0 -338
- package/src/components/chat/MessageView.tsx +0 -595
- package/src/hooks/useAgentSession.ts +0 -1104
package/vite.config.ts
CHANGED
|
@@ -3,21 +3,31 @@ import react from "@vitejs/plugin-react";
|
|
|
3
3
|
import tailwindcss from "@tailwindcss/vite";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import type { Connect } from "vite";
|
|
6
|
-
import { chatApiPlugin } from "./server/chat-api-plugin";
|
|
7
6
|
|
|
8
7
|
// ─── Pi Config API Plugin ───────────────────────────────
|
|
9
8
|
|
|
10
9
|
function piApiPlugin(): Plugin {
|
|
11
|
-
// Lazy-load the server-side module (Node.js only)
|
|
12
|
-
let pi: typeof import("./server/pi-reader");
|
|
13
|
-
let builtins: typeof import("./src/data/builtin-providers");
|
|
14
|
-
|
|
15
10
|
return {
|
|
16
11
|
name: "pi-api",
|
|
17
12
|
configureServer(server) {
|
|
18
|
-
//
|
|
19
|
-
pi = require("./server/pi-reader");
|
|
20
|
-
builtins = require("./src/data/builtin-providers");
|
|
13
|
+
// Lazy-load the server-side module (Node.js only)
|
|
14
|
+
const pi = require("./server/pi-reader");
|
|
15
|
+
const builtins = require("./src/data/builtin-providers");
|
|
16
|
+
|
|
17
|
+
// Warm the usage cache in the background so the dashboard's first
|
|
18
|
+
// request doesn't block on scanning ~150MB of session JSONL.
|
|
19
|
+
setTimeout(() => {
|
|
20
|
+
try {
|
|
21
|
+
pi.readAllUsage();
|
|
22
|
+
} catch {
|
|
23
|
+
/* ignore warm-up failure */
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
pi.readCopilotUsage();
|
|
27
|
+
} catch {
|
|
28
|
+
/* ignore warm-up failure */
|
|
29
|
+
}
|
|
30
|
+
}, 0);
|
|
21
31
|
|
|
22
32
|
const routes: Record<string, (req: Connect.IncomingMessage, res: any) => void> = {
|
|
23
33
|
"GET /api/pi/settings"(_, res) {
|
|
@@ -116,6 +126,28 @@ function piApiPlugin(): Plugin {
|
|
|
116
126
|
res.setHeader("Content-Type", "application/json");
|
|
117
127
|
res.end(JSON.stringify(pi.listTrash()));
|
|
118
128
|
},
|
|
129
|
+
"GET /api/pi/copilot-config"(_, res) {
|
|
130
|
+
res.setHeader("Content-Type", "application/json");
|
|
131
|
+
res.end(JSON.stringify(pi.readCopilotConfig() ?? {}));
|
|
132
|
+
},
|
|
133
|
+
"POST /api/pi/copilot-config"(req, res) {
|
|
134
|
+
let body = "";
|
|
135
|
+
req.on("data", (chunk: string) => (body += chunk));
|
|
136
|
+
req.on("end", () => {
|
|
137
|
+
try {
|
|
138
|
+
const cfg = JSON.parse(body) as { username?: string; token?: string };
|
|
139
|
+
const ok = pi.writeCopilotConfig(cfg);
|
|
140
|
+
// Config changed → drop cached usage so the next view refetches.
|
|
141
|
+
pi.clearCopilotCaches();
|
|
142
|
+
res.setHeader("Content-Type", "application/json");
|
|
143
|
+
res.end(JSON.stringify({ success: ok }));
|
|
144
|
+
} catch {
|
|
145
|
+
res.statusCode = 400;
|
|
146
|
+
res.setHeader("Content-Type", "application/json");
|
|
147
|
+
res.end(JSON.stringify({ success: false, error: "Invalid request body" }));
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
},
|
|
119
151
|
"POST /api/pi/session/trash"(req, res) {
|
|
120
152
|
let body = "";
|
|
121
153
|
req.on("data", (chunk: string) => (body += chunk));
|
|
@@ -283,8 +315,14 @@ function piApiPlugin(): Plugin {
|
|
|
283
315
|
const toParam = parsedUrl.searchParams.get("to") || "";
|
|
284
316
|
|
|
285
317
|
const now = new Date();
|
|
318
|
+
// Date buckets follow China time (UTC+8) regardless of system timezone
|
|
286
319
|
const localDateStr = (dt: Date) =>
|
|
287
|
-
|
|
320
|
+
new Intl.DateTimeFormat("en-CA", {
|
|
321
|
+
timeZone: "Asia/Shanghai",
|
|
322
|
+
year: "numeric",
|
|
323
|
+
month: "2-digit",
|
|
324
|
+
day: "2-digit",
|
|
325
|
+
}).format(dt);
|
|
288
326
|
let fromDate: string;
|
|
289
327
|
let toDate = localDateStr(now);
|
|
290
328
|
|
|
@@ -315,8 +353,14 @@ function piApiPlugin(): Plugin {
|
|
|
315
353
|
const toParam = parsedUrl.searchParams.get("to") || "";
|
|
316
354
|
|
|
317
355
|
const now = new Date();
|
|
356
|
+
// Date buckets follow China time (UTC+8) regardless of system timezone
|
|
318
357
|
const localDateStr = (dt: Date) =>
|
|
319
|
-
|
|
358
|
+
new Intl.DateTimeFormat("en-CA", {
|
|
359
|
+
timeZone: "Asia/Shanghai",
|
|
360
|
+
year: "numeric",
|
|
361
|
+
month: "2-digit",
|
|
362
|
+
day: "2-digit",
|
|
363
|
+
}).format(dt);
|
|
320
364
|
let fromDate: string;
|
|
321
365
|
let toDate = localDateStr(now);
|
|
322
366
|
|
|
@@ -347,8 +391,14 @@ function piApiPlugin(): Plugin {
|
|
|
347
391
|
const toParam = parsedUrl.searchParams.get("to") || "";
|
|
348
392
|
|
|
349
393
|
const now = new Date();
|
|
394
|
+
// Date buckets follow China time (UTC+8) regardless of system timezone
|
|
350
395
|
const localDateStr = (dt: Date) =>
|
|
351
|
-
|
|
396
|
+
new Intl.DateTimeFormat("en-CA", {
|
|
397
|
+
timeZone: "Asia/Shanghai",
|
|
398
|
+
year: "numeric",
|
|
399
|
+
month: "2-digit",
|
|
400
|
+
day: "2-digit",
|
|
401
|
+
}).format(dt);
|
|
352
402
|
let fromDate: string;
|
|
353
403
|
let toDate = localDateStr(now);
|
|
354
404
|
|
|
@@ -379,8 +429,14 @@ function piApiPlugin(): Plugin {
|
|
|
379
429
|
const toParam = parsedUrl.searchParams.get("to") || "";
|
|
380
430
|
|
|
381
431
|
const now = new Date();
|
|
432
|
+
// Date buckets follow China time (UTC+8) regardless of system timezone
|
|
382
433
|
const localDateStr = (dt: Date) =>
|
|
383
|
-
|
|
434
|
+
new Intl.DateTimeFormat("en-CA", {
|
|
435
|
+
timeZone: "Asia/Shanghai",
|
|
436
|
+
year: "numeric",
|
|
437
|
+
month: "2-digit",
|
|
438
|
+
day: "2-digit",
|
|
439
|
+
}).format(dt);
|
|
384
440
|
let fromDate: string;
|
|
385
441
|
let toDate = localDateStr(now);
|
|
386
442
|
|
|
@@ -406,8 +462,14 @@ function piApiPlugin(): Plugin {
|
|
|
406
462
|
// Helper: resolve date range params
|
|
407
463
|
const resolveDateRange = (range: string, fromParam: string, toParam: string) => {
|
|
408
464
|
const now = new Date();
|
|
465
|
+
// Date buckets follow China time (UTC+8) regardless of system timezone
|
|
409
466
|
const localDateStr = (dt: Date) =>
|
|
410
|
-
|
|
467
|
+
new Intl.DateTimeFormat("en-CA", {
|
|
468
|
+
timeZone: "Asia/Shanghai",
|
|
469
|
+
year: "numeric",
|
|
470
|
+
month: "2-digit",
|
|
471
|
+
day: "2-digit",
|
|
472
|
+
}).format(dt);
|
|
411
473
|
let fromDate: string;
|
|
412
474
|
let toDate = localDateStr(now);
|
|
413
475
|
if (range === "today") fromDate = toDate;
|
|
@@ -432,7 +494,10 @@ function piApiPlugin(): Plugin {
|
|
|
432
494
|
}
|
|
433
495
|
|
|
434
496
|
// Handle provider-filtered endpoints: /api/pi/{provider}-usage-range
|
|
435
|
-
|
|
497
|
+
// Copilot is read from the local ~/.copilot/session-store.db (no
|
|
498
|
+
// GitHub REST API, no PAT), so it shares the same sync pipeline as
|
|
499
|
+
// the other local sources.
|
|
500
|
+
const providerMatch = pathOnly.match(/^\/api\/pi\/(atomcode|copilot|opencode|gemini|grok)-usage-range$/);
|
|
436
501
|
if (method === "GET" && providerMatch) {
|
|
437
502
|
const providerId = providerMatch[1]!;
|
|
438
503
|
const parsedUrl = new URL(url, "http://localhost");
|
|
@@ -463,11 +528,13 @@ function piApiPlugin(): Plugin {
|
|
|
463
528
|
// ─── Vite Config ────────────────────────────────────────
|
|
464
529
|
|
|
465
530
|
export default defineConfig({
|
|
531
|
+
// Relative base so Electron can loadFile() the built HTML from disk —
|
|
532
|
+
// absolute "/assets/..." URLs would resolve to the filesystem root.
|
|
533
|
+
base: './',
|
|
466
534
|
plugins: [
|
|
467
535
|
react(),
|
|
468
536
|
tailwindcss(),
|
|
469
537
|
piApiPlugin(),
|
|
470
|
-
chatApiPlugin(),
|
|
471
538
|
],
|
|
472
539
|
resolve: {
|
|
473
540
|
alias: {
|
|
@@ -478,4 +545,11 @@ export default defineConfig({
|
|
|
478
545
|
port: 5176,
|
|
479
546
|
strictPort: true,
|
|
480
547
|
},
|
|
548
|
+
build: {
|
|
549
|
+
rollupOptions: {
|
|
550
|
+
input: {
|
|
551
|
+
main: path.resolve(__dirname, "index.html"),
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
},
|
|
481
555
|
});
|