@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/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
- // Load server-side modules
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
- `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, "0")}-${String(dt.getDate()).padStart(2, "0")}`;
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
- `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, "0")}-${String(dt.getDate()).padStart(2, "0")}`;
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
- `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, "0")}-${String(dt.getDate()).padStart(2, "0")}`;
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
- `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, "0")}-${String(dt.getDate()).padStart(2, "0")}`;
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
- `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, "0")}-${String(dt.getDate()).padStart(2, "0")}`;
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
- const providerMatch = pathOnly.match(/^\/api\/pi\/(opencode|gemini|grok)-usage-range$/);
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
  });