@raingor/pi-web-switch 0.4.1 → 0.4.2
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-electron/main/main.cjs +2453 -0
- package/package.json +18 -5
- package/pi-package/index.ts +228 -4
- package/public/apple-touch-icon.png +0 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/pi.svg +6 -41
- package/public/trayIconTemplate.png +0 -0
- package/server/pi-reader.ts +470 -31
- package/src/App.tsx +0 -2
- package/src/components/dashboard/DashboardPage.tsx +45 -13
- package/src/components/layout/Sidebar.tsx +15 -6
- package/src/components/providers/ProvidersModelsPage.tsx +56 -4
- package/src/components/sessions/SessionsPage.tsx +36 -2
- package/src/components/settings/SettingsPage.tsx +72 -0
- package/src/lib/translations/en.ts +20 -58
- package/src/lib/translations/ja.ts +20 -58
- package/src/lib/translations/zh-CN.ts +20 -58
- package/src/lib/translations/zh-TW.ts +20 -58
- package/src/main.tsx +29 -5
- package/src/types/index.ts +2 -0
- package/vite.config.ts +106 -8
- 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
|
@@ -62,7 +62,6 @@ const ja: Record<string, string> = {
|
|
|
62
62
|
|
|
63
63
|
"nav.sessions": "セッション",
|
|
64
64
|
"nav.memory": "メモリ",
|
|
65
|
-
"nav.chat": "チャット",
|
|
66
65
|
"nav.settings": "設定",
|
|
67
66
|
"app.subtitle": "設定マネージャー",
|
|
68
67
|
"app.version": "pi-switch v0.1.0",
|
|
@@ -107,6 +106,10 @@ const ja: Record<string, string> = {
|
|
|
107
106
|
"dashboard.source_opencode": "OpenCode",
|
|
108
107
|
"dashboard.source_gemini": "Gemini",
|
|
109
108
|
"dashboard.source_grok": "Grok",
|
|
109
|
+
"dashboard.source_atomcode": "AtomCode",
|
|
110
|
+
"dashboard.source_copilot": "Copilot",
|
|
111
|
+
"dashboard.copilot_not_configured": "GitHub Copilot 未設定 — 設定 → 詳細設定 でユーザー名とトークンを入力してください",
|
|
112
|
+
"dashboard.copilot_api_error": "GitHub API から Copilot 使用量を取得できませんでした — トークンと請求プラットフォームを確認してください",
|
|
110
113
|
"dashboard.requests_count": "{0} リクエスト · 総コスト {1}",
|
|
111
114
|
"dashboard.range.today": "今日",
|
|
112
115
|
"dashboard.range.7d": "7日",
|
|
@@ -196,6 +199,8 @@ const ja: Record<string, string> = {
|
|
|
196
199
|
"compat.desc": "OpenAI 互換 API が一部の機能をサポートしていない場合の動作を上書きします。",
|
|
197
200
|
"compat.supports_developer_role": "developer ロールをサポート",
|
|
198
201
|
"compat.supports_developer_role_desc": "システムプロンプトを 'developer' ロールで送信(推論モデル)。API が 'system' ロールのみ受け付ける場合は無効にしてください。",
|
|
202
|
+
"compat.supports_finish_reason": "finish_reason の厳格な検証",
|
|
203
|
+
"compat.supports_finish_reason_desc": "ストリーム応答に finish_reason の含有を必須にし、無ければエラーにします。上流がストリームを途中で切断する場合(無料モデルに多い)は無効にして終了理由の自動判定に切り替えてください。",
|
|
199
204
|
"compat.supports_reasoning_effort": "reasoning_effort をサポート",
|
|
200
205
|
"compat.supports_reasoning_effort_desc": "reasoning_effort パラメータを送信します。API がサポートしていない場合は無効にしてください。",
|
|
201
206
|
|
|
@@ -231,6 +236,11 @@ const ja: Record<string, string> = {
|
|
|
231
236
|
"sessions.delete_error": "削除中にエラーが発生しました",
|
|
232
237
|
"sessions.load_failed": "セッションの読み込みに失敗しました",
|
|
233
238
|
"sessions.refresh": "更新",
|
|
239
|
+
"sessions.auto_expire": "自動期限切れ",
|
|
240
|
+
"sessions.auto_expire_tooltip": "{0}日間非活跃のセッションをゴミ箱に移動",
|
|
241
|
+
"sessions.auto_expire_success": "{0}件の期限切れセッションをゴミ箱に移動しました",
|
|
242
|
+
"sessions.auto_expire_none": "期限切れのセッションはありません",
|
|
243
|
+
"sessions.auto_expire_error": "自動期限切れに失敗:{0}",
|
|
234
244
|
"sessions.expand_all": "すべて展開",
|
|
235
245
|
"sessions.collapse_all": "すべて折りたたむ",
|
|
236
246
|
"sessions.more_count": "あと {0} 件",
|
|
@@ -294,6 +304,15 @@ const ja: Record<string, string> = {
|
|
|
294
304
|
"settings.ui_zoom_desc": "インターフェース全体をパーセントで拡大縮小(50%–200%)",
|
|
295
305
|
"settings.ui_zoom_reset": "リセット",
|
|
296
306
|
"settings.enabled_models_desc": "タグをクリックしてモデルを切り替え",
|
|
307
|
+
"settings.copilot_title": "GitHub Copilot",
|
|
308
|
+
"settings.copilot_desc": "GitHub 課金 API から Copilot の使用量を取得(premium requests / AI credits)",
|
|
309
|
+
"settings.copilot_username": "GitHub ユーザー名",
|
|
310
|
+
"settings.copilot_token": "Token(classic PAT)",
|
|
311
|
+
"settings.copilot_save": "保存",
|
|
312
|
+
"settings.copilot_saving": "保存中…",
|
|
313
|
+
"settings.copilot_saved": "保存しました — 次回ダッシュボード読み込み時に反映されます",
|
|
314
|
+
"settings.copilot_save_failed": "保存に失敗しました",
|
|
315
|
+
"settings.copilot_token_hint": "github.com/settings/tokens で classic personal access token を作成してください(課金 usage エンドポイントは fine-grained 非対応)。Copilot 使用量は拡張課金プラットフォームのアカウントのみ表示されます。",
|
|
297
316
|
"settings.updates_title": "アップデート確認",
|
|
298
317
|
"settings.updates_desc": "pi 本体と拡張に npm 上の新バージョンがあるか確認します。",
|
|
299
318
|
"settings.check_updates": "更新を確認",
|
|
@@ -341,63 +360,6 @@ const ja: Record<string, string> = {
|
|
|
341
360
|
"loading.error_title": "設定の読み込みに失敗しました",
|
|
342
361
|
"loading.retry": "再試行",
|
|
343
362
|
|
|
344
|
-
// Chat
|
|
345
|
-
"chat.sessions": "セッション",
|
|
346
|
-
"chat.new_session": "新規セッション",
|
|
347
|
-
"chat.loading": "読み込み中...",
|
|
348
|
-
"chat.no_sessions": "セッションがありません。+ をクリックして新しいチャットを開始してください。",
|
|
349
|
-
"chat.welcome_title": "Pi Chat へようこそ",
|
|
350
|
-
"chat.welcome_desc": "サイドバーからセッションを選択するか、+ をクリックしてプロジェクトディレクトリで新しいチャットを開始します。",
|
|
351
|
-
"chat.select_cwd": "作業ディレクトリを選択",
|
|
352
|
-
"chat.cancel": "キャンセル",
|
|
353
|
-
"chat.start_chat": "チャット開始",
|
|
354
|
-
"chat.delete_confirm": "このセッションを削除しますか?この操作は元に戻せません。",
|
|
355
|
-
"chat.delete": "削除",
|
|
356
|
-
"chat.loading_session": "セッションを読み込み中...",
|
|
357
|
-
"chat.thinking": "考え中...",
|
|
358
|
-
"chat.running_tool": "ツールを実行中...",
|
|
359
|
-
"chat.running_command": "コマンドを実行中...",
|
|
360
|
-
"chat.send_message": "メッセージを送信...(/ でコマンド、! で bash)",
|
|
361
|
-
"chat.type_to_steer": "入力して誘導...",
|
|
362
|
-
"chat.compacting": "コンテキストを圧縮中...",
|
|
363
|
-
"chat.compaction_failed": "圧縮失敗:{0}",
|
|
364
|
-
"chat.retrying": "再試行中 ({0}/{1}){2}",
|
|
365
|
-
"chat.steering_queued": "{0} 件の誘導、{1} 件のフォローアップがキューにあります",
|
|
366
|
-
"chat.recall": "取り消す",
|
|
367
|
-
"chat.default_tools": "デフォルトツール",
|
|
368
|
-
"chat.all_tools": "すべてのツール",
|
|
369
|
-
"chat.no_tools": "ツールなし",
|
|
370
|
-
"chat.auto": "自動",
|
|
371
|
-
"chat.no_model": "モデルなし",
|
|
372
|
-
"chat.no_models_available": "利用可能なモデルがありません。モデル設定を確認してください。",
|
|
373
|
-
"chat.search_models": "モデルを検索...",
|
|
374
|
-
"chat.no_models_found": "モデルが見つかりません",
|
|
375
|
-
"chat.models_count": "{0} モデル",
|
|
376
|
-
"chat.models_filtered": "{0} / {1} モデル",
|
|
377
|
-
"chat.attach_image": "画像を添付",
|
|
378
|
-
"chat.tool_preset": "ツールプリセット",
|
|
379
|
-
"chat.compact_context": "コンテキストを圧縮",
|
|
380
|
-
"chat.stop": "停止",
|
|
381
|
-
"chat.send": "送信",
|
|
382
|
-
"chat.fork_from_here": "ここから分岐",
|
|
383
|
-
"chat.show_less": "折りたたむ",
|
|
384
|
-
"chat.show_more": "残り {0} 文字を表示",
|
|
385
|
-
"chat.tool_call": "ツール呼び出し",
|
|
386
|
-
"chat.thinking_collapsed": "思考(折りたたみ)",
|
|
387
|
-
"chat.thinking_label": "思考",
|
|
388
|
-
"chat.result": "結果",
|
|
389
|
-
"chat.error_label": "(エラー)",
|
|
390
|
-
"chat.exit_code": "終了コード",
|
|
391
|
-
"chat.msg_count": "{0} 件",
|
|
392
|
-
"chat.untitled": "無題",
|
|
393
|
-
"chat.just_now": "たった今",
|
|
394
|
-
"chat.min_ago": "{0} 分前",
|
|
395
|
-
"chat.hr_ago": "{0} 時間前",
|
|
396
|
-
"chat.day_ago": "{0} 日前",
|
|
397
|
-
"chat.invalid_directory": "無効なディレクトリ",
|
|
398
|
-
"chat.expand_all": "すべて展開",
|
|
399
|
-
"chat.collapse_all": "すべて折りたたむ",
|
|
400
|
-
|
|
401
363
|
"language.label": "言語",
|
|
402
364
|
"language.en": "English",
|
|
403
365
|
"language.zh-CN": "简体中文",
|
|
@@ -62,7 +62,6 @@ const zhCN: Record<string, string> = {
|
|
|
62
62
|
|
|
63
63
|
"nav.sessions": "会话",
|
|
64
64
|
"nav.memory": "记忆",
|
|
65
|
-
"nav.chat": "聊天",
|
|
66
65
|
"nav.settings": "设置",
|
|
67
66
|
"app.subtitle": "配置管理器",
|
|
68
67
|
"app.version": "pi-switch v0.1.0",
|
|
@@ -107,6 +106,10 @@ const zhCN: Record<string, string> = {
|
|
|
107
106
|
"dashboard.source_opencode": "OpenCode",
|
|
108
107
|
"dashboard.source_gemini": "Gemini",
|
|
109
108
|
"dashboard.source_grok": "Grok",
|
|
109
|
+
"dashboard.source_atomcode": "AtomCode",
|
|
110
|
+
"dashboard.source_copilot": "Copilot",
|
|
111
|
+
"dashboard.copilot_not_configured": "GitHub Copilot 未配置 — 请在 设置 → 高级 中填写用户名与 Token",
|
|
112
|
+
"dashboard.copilot_api_error": "从 GitHub API 获取 Copilot 用量失败 — 请检查 Token 与计费平台",
|
|
110
113
|
"dashboard.requests_count": "{0} 次请求 · 总成本 {1}",
|
|
111
114
|
"dashboard.range.today": "当天",
|
|
112
115
|
"dashboard.range.7d": "7天",
|
|
@@ -196,6 +199,8 @@ const zhCN: Record<string, string> = {
|
|
|
196
199
|
"compat.desc": "当 OpenAI 兼容 API 不支持某些功能时,覆盖默认行为。",
|
|
197
200
|
"compat.supports_developer_role": "支持 developer 角色",
|
|
198
201
|
"compat.supports_developer_role_desc": "使用 'developer' 角色发送系统提示(推理模型)。如果不支持,请关闭以改用 'system' 角色。",
|
|
202
|
+
"compat.supports_finish_reason": "严格校验 finish_reason",
|
|
203
|
+
"compat.supports_finish_reason_desc": "要求流式响应必须包含 finish_reason,否则报错。上游偶尔掐断流(免费模型常见)时,请关闭以改为自动推断结束原因。",
|
|
199
204
|
"compat.supports_reasoning_effort": "支持 reasoning_effort",
|
|
200
205
|
"compat.supports_reasoning_effort_desc": "发送 reasoning_effort 参数。如果 API 不支持,请关闭。",
|
|
201
206
|
|
|
@@ -231,6 +236,11 @@ const zhCN: Record<string, string> = {
|
|
|
231
236
|
"sessions.delete_error": "删除时发生错误",
|
|
232
237
|
"sessions.load_failed": "加载会话失败",
|
|
233
238
|
"sessions.refresh": "刷新",
|
|
239
|
+
"sessions.auto_expire": "自动清理",
|
|
240
|
+
"sessions.auto_expire_tooltip": "将 {0} 天未活跃的会话移入回收站",
|
|
241
|
+
"sessions.auto_expire_success": "已将 {0} 个过期会话移入回收站",
|
|
242
|
+
"sessions.auto_expire_none": "没有找到过期会话",
|
|
243
|
+
"sessions.auto_expire_error": "自动清理失败:{0}",
|
|
234
244
|
"sessions.expand_all": "全部展开",
|
|
235
245
|
"sessions.collapse_all": "全部折叠",
|
|
236
246
|
"sessions.more_count": "还有 {0} 条",
|
|
@@ -294,6 +304,15 @@ const zhCN: Record<string, string> = {
|
|
|
294
304
|
"settings.ui_zoom_desc": "按百分比缩放整个界面(50%–200%)",
|
|
295
305
|
"settings.ui_zoom_reset": "重置",
|
|
296
306
|
"settings.enabled_models_desc": "点击标签以启用或禁用模型",
|
|
307
|
+
"settings.copilot_title": "GitHub Copilot",
|
|
308
|
+
"settings.copilot_desc": "通过 GitHub 计费 API 统计 Copilot 用量(premium requests / AI credits)",
|
|
309
|
+
"settings.copilot_username": "GitHub 用户名",
|
|
310
|
+
"settings.copilot_token": "Token(classic PAT)",
|
|
311
|
+
"settings.copilot_save": "保存",
|
|
312
|
+
"settings.copilot_saving": "保存中…",
|
|
313
|
+
"settings.copilot_saved": "已保存 — 下次打开仪表盘时刷新用量",
|
|
314
|
+
"settings.copilot_save_failed": "保存失败",
|
|
315
|
+
"settings.copilot_token_hint": "在 github.com/settings/tokens 创建 classic 个人访问令牌(计费用量接口不支持 fine-grained token)。仅增强计费平台的账号会显示 Copilot 用量。",
|
|
297
316
|
"settings.updates_title": "更新检测",
|
|
298
317
|
"settings.updates_desc": "检查 pi 本体与扩展在 npm 上是否有新版本。",
|
|
299
318
|
"settings.check_updates": "检查更新",
|
|
@@ -341,63 +360,6 @@ const zhCN: Record<string, string> = {
|
|
|
341
360
|
"loading.error_title": "加载配置失败",
|
|
342
361
|
"loading.retry": "重试",
|
|
343
362
|
|
|
344
|
-
// Chat
|
|
345
|
-
"chat.sessions": "会话",
|
|
346
|
-
"chat.new_session": "新建会话",
|
|
347
|
-
"chat.loading": "加载中...",
|
|
348
|
-
"chat.no_sessions": "暂无会话。点击 + 开始新对话。",
|
|
349
|
-
"chat.welcome_title": "欢迎使用 Pi Chat",
|
|
350
|
-
"chat.welcome_desc": "从侧栏选择一个会话,或点击 + 在项目目录中开始新的对话。",
|
|
351
|
-
"chat.select_cwd": "选择工作目录",
|
|
352
|
-
"chat.cancel": "取消",
|
|
353
|
-
"chat.start_chat": "开始对话",
|
|
354
|
-
"chat.delete_confirm": "确定删除此会话?此操作无法撤销。",
|
|
355
|
-
"chat.delete": "删除",
|
|
356
|
-
"chat.loading_session": "正在加载会话...",
|
|
357
|
-
"chat.thinking": "思考中...",
|
|
358
|
-
"chat.running_tool": "正在运行工具...",
|
|
359
|
-
"chat.running_command": "正在执行命令...",
|
|
360
|
-
"chat.send_message": "发送消息...(/ 选择命令,! 执行命令)",
|
|
361
|
-
"chat.type_to_steer": "输入以引导...",
|
|
362
|
-
"chat.compacting": "正在压缩上下文...",
|
|
363
|
-
"chat.compaction_failed": "压缩失败:{0}",
|
|
364
|
-
"chat.retrying": "重试中 ({0}/{1}){2}",
|
|
365
|
-
"chat.steering_queued": "{0} 条引导,{1} 条跟进已排队",
|
|
366
|
-
"chat.recall": "撤回",
|
|
367
|
-
"chat.default_tools": "默认工具",
|
|
368
|
-
"chat.all_tools": "全部工具",
|
|
369
|
-
"chat.no_tools": "无工具",
|
|
370
|
-
"chat.auto": "自动",
|
|
371
|
-
"chat.no_model": "无模型",
|
|
372
|
-
"chat.no_models_available": "暂无可用模型,请检查模型配置。",
|
|
373
|
-
"chat.search_models": "搜索模型...",
|
|
374
|
-
"chat.no_models_found": "未找到模型",
|
|
375
|
-
"chat.models_count": "{0} 个模型",
|
|
376
|
-
"chat.models_filtered": "{0} / {1} 个模型",
|
|
377
|
-
"chat.attach_image": "附加图片",
|
|
378
|
-
"chat.tool_preset": "工具预设",
|
|
379
|
-
"chat.compact_context": "压缩上下文",
|
|
380
|
-
"chat.stop": "停止",
|
|
381
|
-
"chat.send": "发送",
|
|
382
|
-
"chat.fork_from_here": "从此处分叉",
|
|
383
|
-
"chat.show_less": "收起",
|
|
384
|
-
"chat.show_more": "展开剩余 {0} 个字符",
|
|
385
|
-
"chat.tool_call": "工具调用",
|
|
386
|
-
"chat.thinking_collapsed": "思考(已折叠)",
|
|
387
|
-
"chat.thinking_label": "思考",
|
|
388
|
-
"chat.result": "结果",
|
|
389
|
-
"chat.error_label": "(错误)",
|
|
390
|
-
"chat.exit_code": "退出码",
|
|
391
|
-
"chat.msg_count": "{0} 条消息",
|
|
392
|
-
"chat.untitled": "未命名",
|
|
393
|
-
"chat.just_now": "刚刚",
|
|
394
|
-
"chat.min_ago": "{0} 分钟前",
|
|
395
|
-
"chat.hr_ago": "{0} 小时前",
|
|
396
|
-
"chat.day_ago": "{0} 天前",
|
|
397
|
-
"chat.invalid_directory": "无效目录",
|
|
398
|
-
"chat.expand_all": "全部展开",
|
|
399
|
-
"chat.collapse_all": "全部折叠",
|
|
400
|
-
|
|
401
363
|
"language.label": "语言",
|
|
402
364
|
"language.en": "English",
|
|
403
365
|
"language.zh-CN": "简体中文",
|
|
@@ -62,7 +62,6 @@ const zhTW: Record<string, string> = {
|
|
|
62
62
|
|
|
63
63
|
"nav.sessions": "會話",
|
|
64
64
|
"nav.memory": "記憶",
|
|
65
|
-
"nav.chat": "聊天",
|
|
66
65
|
"nav.settings": "設定",
|
|
67
66
|
"app.subtitle": "設定管理器",
|
|
68
67
|
"app.version": "pi-switch v0.1.0",
|
|
@@ -107,6 +106,10 @@ const zhTW: Record<string, string> = {
|
|
|
107
106
|
"dashboard.source_opencode": "OpenCode",
|
|
108
107
|
"dashboard.source_gemini": "Gemini",
|
|
109
108
|
"dashboard.source_grok": "Grok",
|
|
109
|
+
"dashboard.source_atomcode": "AtomCode",
|
|
110
|
+
"dashboard.source_copilot": "Copilot",
|
|
111
|
+
"dashboard.copilot_not_configured": "GitHub Copilot 未設定 — 請在 設定 → 進階 中填寫使用者名稱與 Token",
|
|
112
|
+
"dashboard.copilot_api_error": "從 GitHub API 取得 Copilot 用量失敗 — 請檢查 Token 與計費平台",
|
|
110
113
|
"dashboard.requests_count": "{0} 次請求 · 總成本 {1}",
|
|
111
114
|
"dashboard.range.today": "當天",
|
|
112
115
|
"dashboard.range.7d": "7天",
|
|
@@ -196,6 +199,8 @@ const zhTW: Record<string, string> = {
|
|
|
196
199
|
"compat.desc": "當 OpenAI 相容 API 不支援某些功能時,覆蓋預設行為。",
|
|
197
200
|
"compat.supports_developer_role": "支援 developer 角色",
|
|
198
201
|
"compat.supports_developer_role_desc": "使用 'developer' 角色發送系統提示(推理模型)。如果不支援,請關閉以改用 'system' 角色。",
|
|
202
|
+
"compat.supports_finish_reason": "嚴格校驗 finish_reason",
|
|
203
|
+
"compat.supports_finish_reason_desc": "要求串流回應必須包含 finish_reason,否則報錯。上游偶爾掐斷串流(免費模型常見)時,請關閉以改為自動推斷結束原因。",
|
|
199
204
|
"compat.supports_reasoning_effort": "支援 reasoning_effort",
|
|
200
205
|
"compat.supports_reasoning_effort_desc": "發送 reasoning_effort 參數。如果 API 不支援,請關閉。",
|
|
201
206
|
|
|
@@ -231,6 +236,11 @@ const zhTW: Record<string, string> = {
|
|
|
231
236
|
"sessions.delete_error": "刪除時發生錯誤",
|
|
232
237
|
"sessions.load_failed": "載入會話失敗",
|
|
233
238
|
"sessions.refresh": "重新整理",
|
|
239
|
+
"sessions.auto_expire": "自動清理",
|
|
240
|
+
"sessions.auto_expire_tooltip": "將 {0} 天未活躍的會話移入回收筒",
|
|
241
|
+
"sessions.auto_expire_success": "已將 {0} 個過期會話移入回收筒",
|
|
242
|
+
"sessions.auto_expire_none": "沒有找到過期會話",
|
|
243
|
+
"sessions.auto_expire_error": "自動清理失敗:{0}",
|
|
234
244
|
"sessions.expand_all": "全部展開",
|
|
235
245
|
"sessions.collapse_all": "全部收合",
|
|
236
246
|
"sessions.more_count": "還有 {0} 條",
|
|
@@ -294,6 +304,15 @@ const zhTW: Record<string, string> = {
|
|
|
294
304
|
"settings.ui_zoom_desc": "依百分比縮放整個介面(50%–200%)",
|
|
295
305
|
"settings.ui_zoom_reset": "重設",
|
|
296
306
|
"settings.enabled_models_desc": "點擊標籤以啟用或停用模型",
|
|
307
|
+
"settings.copilot_title": "GitHub Copilot",
|
|
308
|
+
"settings.copilot_desc": "透過 GitHub 計費 API 統計 Copilot 用量(premium requests / AI credits)",
|
|
309
|
+
"settings.copilot_username": "GitHub 使用者名稱",
|
|
310
|
+
"settings.copilot_token": "Token(classic PAT)",
|
|
311
|
+
"settings.copilot_save": "儲存",
|
|
312
|
+
"settings.copilot_saving": "儲存中…",
|
|
313
|
+
"settings.copilot_saved": "已儲存 — 下次開啟儀表板時重新整理用量",
|
|
314
|
+
"settings.copilot_save_failed": "儲存失敗",
|
|
315
|
+
"settings.copilot_token_hint": "在 github.com/settings/tokens 建立 classic 個人存取權杖(計費用量 API 不支援 fine-grained token)。僅增強計費平台的帳號會顯示 Copilot 用量。",
|
|
297
316
|
"settings.updates_title": "更新檢測",
|
|
298
317
|
"settings.updates_desc": "檢查 pi 本體與擴充在 npm 上是否有新版本。",
|
|
299
318
|
"settings.check_updates": "檢查更新",
|
|
@@ -341,63 +360,6 @@ const zhTW: Record<string, string> = {
|
|
|
341
360
|
"loading.error_title": "載入設定失敗",
|
|
342
361
|
"loading.retry": "重試",
|
|
343
362
|
|
|
344
|
-
// Chat
|
|
345
|
-
"chat.sessions": "會話",
|
|
346
|
-
"chat.new_session": "新建會話",
|
|
347
|
-
"chat.loading": "載入中...",
|
|
348
|
-
"chat.no_sessions": "尚無會話。點擊 + 開始新對話。",
|
|
349
|
-
"chat.welcome_title": "歡迎使用 Pi Chat",
|
|
350
|
-
"chat.welcome_desc": "從側欄選擇一個會話,或點擊 + 在專案目錄中開始新的對話。",
|
|
351
|
-
"chat.select_cwd": "選擇工作目錄",
|
|
352
|
-
"chat.cancel": "取消",
|
|
353
|
-
"chat.start_chat": "開始對話",
|
|
354
|
-
"chat.delete_confirm": "確定刪除此會話?此操作無法復原。",
|
|
355
|
-
"chat.delete": "刪除",
|
|
356
|
-
"chat.loading_session": "正在載入會話...",
|
|
357
|
-
"chat.thinking": "思考中...",
|
|
358
|
-
"chat.running_tool": "正在執行工具...",
|
|
359
|
-
"chat.running_command": "正在執行指令...",
|
|
360
|
-
"chat.send_message": "發送訊息…(/ 選擇指令,! 執行指令)",
|
|
361
|
-
"chat.type_to_steer": "輸入以引導...",
|
|
362
|
-
"chat.compacting": "正在壓縮上下文...",
|
|
363
|
-
"chat.compaction_failed": "壓縮失敗:{0}",
|
|
364
|
-
"chat.retrying": "重試中 ({0}/{1}){2}",
|
|
365
|
-
"chat.steering_queued": "{0} 條引導,{1} 條跟進已排隊",
|
|
366
|
-
"chat.recall": "撤回",
|
|
367
|
-
"chat.default_tools": "預設工具",
|
|
368
|
-
"chat.all_tools": "全部工具",
|
|
369
|
-
"chat.no_tools": "無工具",
|
|
370
|
-
"chat.auto": "自動",
|
|
371
|
-
"chat.no_model": "無模型",
|
|
372
|
-
"chat.no_models_available": "暫無可用模型,請檢查模型配置。",
|
|
373
|
-
"chat.search_models": "搜尋模型...",
|
|
374
|
-
"chat.no_models_found": "未找到模型",
|
|
375
|
-
"chat.models_count": "{0} 個模型",
|
|
376
|
-
"chat.models_filtered": "{0} / {1} 個模型",
|
|
377
|
-
"chat.attach_image": "附加圖片",
|
|
378
|
-
"chat.tool_preset": "工具預設",
|
|
379
|
-
"chat.compact_context": "壓縮上下文",
|
|
380
|
-
"chat.stop": "停止",
|
|
381
|
-
"chat.send": "發送",
|
|
382
|
-
"chat.fork_from_here": "從此分叉",
|
|
383
|
-
"chat.show_less": "收起",
|
|
384
|
-
"chat.show_more": "展開剩餘 {0} 個字元",
|
|
385
|
-
"chat.tool_call": "工具呼叫",
|
|
386
|
-
"chat.thinking_collapsed": "思考(已摺疊)",
|
|
387
|
-
"chat.thinking_label": "思考",
|
|
388
|
-
"chat.result": "結果",
|
|
389
|
-
"chat.error_label": "(錯誤)",
|
|
390
|
-
"chat.exit_code": "退出碼",
|
|
391
|
-
"chat.msg_count": "{0} 條訊息",
|
|
392
|
-
"chat.untitled": "未命名",
|
|
393
|
-
"chat.just_now": "剛剛",
|
|
394
|
-
"chat.min_ago": "{0} 分鐘前",
|
|
395
|
-
"chat.hr_ago": "{0} 小時前",
|
|
396
|
-
"chat.day_ago": "{0} 天前",
|
|
397
|
-
"chat.invalid_directory": "無效目錄",
|
|
398
|
-
"chat.expand_all": "全部展開",
|
|
399
|
-
"chat.collapse_all": "全部摺疊",
|
|
400
|
-
|
|
401
363
|
"language.label": "語言",
|
|
402
364
|
"language.en": "English",
|
|
403
365
|
"language.zh-CN": "简体中文",
|
package/src/main.tsx
CHANGED
|
@@ -126,10 +126,34 @@ createRoot(document.getElementById("root")!).render(
|
|
|
126
126
|
// ─── PWA: register service worker (production only — dev must not
|
|
127
127
|
// cache HTML pages, or SPA routes like /providers can return stale HTML
|
|
128
128
|
// to API fetches and break JSON parsing) ─────────────────────
|
|
129
|
-
if ("serviceWorker" in navigator
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
if ("serviceWorker" in navigator) {
|
|
130
|
+
if (import.meta.env.PROD) {
|
|
131
|
+
window.addEventListener("load", () => {
|
|
132
|
+
navigator.serviceWorker.register("/sw.js").catch((err) => {
|
|
133
|
+
console.warn("SW registration failed:", err);
|
|
134
|
+
});
|
|
133
135
|
});
|
|
134
|
-
}
|
|
136
|
+
} else {
|
|
137
|
+
// Dev: proactively unregister any leftover service worker (e.g. from a
|
|
138
|
+
// previous `vite preview` or production build on the same origin) and
|
|
139
|
+
// clear its caches. Dev module URLs are not hashed, so a stale SW would
|
|
140
|
+
// serve old code on every plain reload until a hard refresh.
|
|
141
|
+
window.addEventListener("load", () => {
|
|
142
|
+
navigator.serviceWorker
|
|
143
|
+
.getRegistrations()
|
|
144
|
+
.then((registrations) =>
|
|
145
|
+
Promise.all(registrations.map((reg) => reg.unregister()))
|
|
146
|
+
)
|
|
147
|
+
.then(() =>
|
|
148
|
+
window.caches
|
|
149
|
+
? window.caches.keys().then((keys) =>
|
|
150
|
+
Promise.all(keys.map((k) => window.caches.delete(k)))
|
|
151
|
+
)
|
|
152
|
+
: undefined
|
|
153
|
+
)
|
|
154
|
+
.catch((err) => {
|
|
155
|
+
console.warn("SW cleanup failed:", err);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
135
159
|
}
|
package/src/types/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface ThinkingLevelMap {
|
|
|
19
19
|
export interface ModelCompat {
|
|
20
20
|
supportsStore?: boolean;
|
|
21
21
|
supportsDeveloperRole?: boolean;
|
|
22
|
+
supportsFinishReason?: boolean;
|
|
22
23
|
supportsReasoningEffort?: boolean;
|
|
23
24
|
supportsUsageInStreaming?: boolean;
|
|
24
25
|
maxTokensField?: "max_completion_tokens" | "max_tokens";
|
|
@@ -109,6 +110,7 @@ export interface PiSettings {
|
|
|
109
110
|
treeFilterMode?: string;
|
|
110
111
|
doubleEscapeAction?: string;
|
|
111
112
|
enabledModels?: string[];
|
|
113
|
+
sessionExpiryDays?: number;
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
export interface PiAuth {
|
package/vite.config.ts
CHANGED
|
@@ -3,7 +3,6 @@ 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
|
|
|
@@ -19,6 +18,24 @@ function piApiPlugin(): Plugin {
|
|
|
19
18
|
pi = require("./server/pi-reader");
|
|
20
19
|
builtins = require("./src/data/builtin-providers");
|
|
21
20
|
|
|
21
|
+
// Warm the usage cache in the background so the dashboard's first
|
|
22
|
+
// request doesn't block on scanning ~150MB of session JSONL.
|
|
23
|
+
setTimeout(() => {
|
|
24
|
+
try {
|
|
25
|
+
pi.readAllUsage();
|
|
26
|
+
} catch {
|
|
27
|
+
/* ignore warm-up failure */
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
pi.readCopilotUsage();
|
|
31
|
+
} catch {
|
|
32
|
+
/* ignore warm-up failure */
|
|
33
|
+
}
|
|
34
|
+
}, 0);
|
|
35
|
+
|
|
36
|
+
// Start the auto-expiry timer: scan once at startup, then every 24h.
|
|
37
|
+
try { pi.startAutoExpiryTimer(); } catch { /* ignore */ }
|
|
38
|
+
|
|
22
39
|
const routes: Record<string, (req: Connect.IncomingMessage, res: any) => void> = {
|
|
23
40
|
"GET /api/pi/settings"(_, res) {
|
|
24
41
|
const data = pi.readSettings();
|
|
@@ -116,6 +133,28 @@ function piApiPlugin(): Plugin {
|
|
|
116
133
|
res.setHeader("Content-Type", "application/json");
|
|
117
134
|
res.end(JSON.stringify(pi.listTrash()));
|
|
118
135
|
},
|
|
136
|
+
"GET /api/pi/copilot-config"(_, res) {
|
|
137
|
+
res.setHeader("Content-Type", "application/json");
|
|
138
|
+
res.end(JSON.stringify(pi.readCopilotConfig() ?? {}));
|
|
139
|
+
},
|
|
140
|
+
"POST /api/pi/copilot-config"(req, res) {
|
|
141
|
+
let body = "";
|
|
142
|
+
req.on("data", (chunk: string) => (body += chunk));
|
|
143
|
+
req.on("end", () => {
|
|
144
|
+
try {
|
|
145
|
+
const cfg = JSON.parse(body) as { username?: string; token?: string };
|
|
146
|
+
const ok = pi.writeCopilotConfig(cfg);
|
|
147
|
+
// Config changed → drop cached usage so the next view refetches.
|
|
148
|
+
pi.clearCopilotCaches();
|
|
149
|
+
res.setHeader("Content-Type", "application/json");
|
|
150
|
+
res.end(JSON.stringify({ success: ok }));
|
|
151
|
+
} catch {
|
|
152
|
+
res.statusCode = 400;
|
|
153
|
+
res.setHeader("Content-Type", "application/json");
|
|
154
|
+
res.end(JSON.stringify({ success: false, error: "Invalid request body" }));
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
},
|
|
119
158
|
"POST /api/pi/session/trash"(req, res) {
|
|
120
159
|
let body = "";
|
|
121
160
|
req.on("data", (chunk: string) => (body += chunk));
|
|
@@ -148,6 +187,21 @@ function piApiPlugin(): Plugin {
|
|
|
148
187
|
}
|
|
149
188
|
});
|
|
150
189
|
},
|
|
190
|
+
"POST /api/pi/session/auto-expire"(req, res) {
|
|
191
|
+
let body = "";
|
|
192
|
+
req.on("data", (chunk: string) => (body += chunk));
|
|
193
|
+
req.on("end", () => {
|
|
194
|
+
try {
|
|
195
|
+
const result = pi.autoExpireSessions();
|
|
196
|
+
res.setHeader("Content-Type", "application/json");
|
|
197
|
+
res.end(JSON.stringify({ success: true, ...result }));
|
|
198
|
+
} catch {
|
|
199
|
+
res.statusCode = 500;
|
|
200
|
+
res.setHeader("Content-Type", "application/json");
|
|
201
|
+
res.end(JSON.stringify({ success: false, error: "Auto-expire failed" }));
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
},
|
|
151
205
|
"GET /api/pi/session-preview"(req, res) {
|
|
152
206
|
const parsedUrl = new URL(req.url!, "http://localhost");
|
|
153
207
|
const p = parsedUrl.searchParams.get("path") || "";
|
|
@@ -283,8 +337,14 @@ function piApiPlugin(): Plugin {
|
|
|
283
337
|
const toParam = parsedUrl.searchParams.get("to") || "";
|
|
284
338
|
|
|
285
339
|
const now = new Date();
|
|
340
|
+
// Date buckets follow China time (UTC+8) regardless of system timezone
|
|
286
341
|
const localDateStr = (dt: Date) =>
|
|
287
|
-
|
|
342
|
+
new Intl.DateTimeFormat("en-CA", {
|
|
343
|
+
timeZone: "Asia/Shanghai",
|
|
344
|
+
year: "numeric",
|
|
345
|
+
month: "2-digit",
|
|
346
|
+
day: "2-digit",
|
|
347
|
+
}).format(dt);
|
|
288
348
|
let fromDate: string;
|
|
289
349
|
let toDate = localDateStr(now);
|
|
290
350
|
|
|
@@ -315,8 +375,14 @@ function piApiPlugin(): Plugin {
|
|
|
315
375
|
const toParam = parsedUrl.searchParams.get("to") || "";
|
|
316
376
|
|
|
317
377
|
const now = new Date();
|
|
378
|
+
// Date buckets follow China time (UTC+8) regardless of system timezone
|
|
318
379
|
const localDateStr = (dt: Date) =>
|
|
319
|
-
|
|
380
|
+
new Intl.DateTimeFormat("en-CA", {
|
|
381
|
+
timeZone: "Asia/Shanghai",
|
|
382
|
+
year: "numeric",
|
|
383
|
+
month: "2-digit",
|
|
384
|
+
day: "2-digit",
|
|
385
|
+
}).format(dt);
|
|
320
386
|
let fromDate: string;
|
|
321
387
|
let toDate = localDateStr(now);
|
|
322
388
|
|
|
@@ -347,8 +413,14 @@ function piApiPlugin(): Plugin {
|
|
|
347
413
|
const toParam = parsedUrl.searchParams.get("to") || "";
|
|
348
414
|
|
|
349
415
|
const now = new Date();
|
|
416
|
+
// Date buckets follow China time (UTC+8) regardless of system timezone
|
|
350
417
|
const localDateStr = (dt: Date) =>
|
|
351
|
-
|
|
418
|
+
new Intl.DateTimeFormat("en-CA", {
|
|
419
|
+
timeZone: "Asia/Shanghai",
|
|
420
|
+
year: "numeric",
|
|
421
|
+
month: "2-digit",
|
|
422
|
+
day: "2-digit",
|
|
423
|
+
}).format(dt);
|
|
352
424
|
let fromDate: string;
|
|
353
425
|
let toDate = localDateStr(now);
|
|
354
426
|
|
|
@@ -379,8 +451,14 @@ function piApiPlugin(): Plugin {
|
|
|
379
451
|
const toParam = parsedUrl.searchParams.get("to") || "";
|
|
380
452
|
|
|
381
453
|
const now = new Date();
|
|
454
|
+
// Date buckets follow China time (UTC+8) regardless of system timezone
|
|
382
455
|
const localDateStr = (dt: Date) =>
|
|
383
|
-
|
|
456
|
+
new Intl.DateTimeFormat("en-CA", {
|
|
457
|
+
timeZone: "Asia/Shanghai",
|
|
458
|
+
year: "numeric",
|
|
459
|
+
month: "2-digit",
|
|
460
|
+
day: "2-digit",
|
|
461
|
+
}).format(dt);
|
|
384
462
|
let fromDate: string;
|
|
385
463
|
let toDate = localDateStr(now);
|
|
386
464
|
|
|
@@ -406,8 +484,14 @@ function piApiPlugin(): Plugin {
|
|
|
406
484
|
// Helper: resolve date range params
|
|
407
485
|
const resolveDateRange = (range: string, fromParam: string, toParam: string) => {
|
|
408
486
|
const now = new Date();
|
|
487
|
+
// Date buckets follow China time (UTC+8) regardless of system timezone
|
|
409
488
|
const localDateStr = (dt: Date) =>
|
|
410
|
-
|
|
489
|
+
new Intl.DateTimeFormat("en-CA", {
|
|
490
|
+
timeZone: "Asia/Shanghai",
|
|
491
|
+
year: "numeric",
|
|
492
|
+
month: "2-digit",
|
|
493
|
+
day: "2-digit",
|
|
494
|
+
}).format(dt);
|
|
411
495
|
let fromDate: string;
|
|
412
496
|
let toDate = localDateStr(now);
|
|
413
497
|
if (range === "today") fromDate = toDate;
|
|
@@ -432,7 +516,10 @@ function piApiPlugin(): Plugin {
|
|
|
432
516
|
}
|
|
433
517
|
|
|
434
518
|
// Handle provider-filtered endpoints: /api/pi/{provider}-usage-range
|
|
435
|
-
|
|
519
|
+
// Copilot is read from the local ~/.copilot/session-store.db (no
|
|
520
|
+
// GitHub REST API, no PAT), so it shares the same sync pipeline as
|
|
521
|
+
// the other local sources.
|
|
522
|
+
const providerMatch = pathOnly.match(/^\/api\/pi\/(atomcode|copilot|opencode|gemini|grok)-usage-range$/);
|
|
436
523
|
if (method === "GET" && providerMatch) {
|
|
437
524
|
const providerId = providerMatch[1]!;
|
|
438
525
|
const parsedUrl = new URL(url, "http://localhost");
|
|
@@ -463,11 +550,13 @@ function piApiPlugin(): Plugin {
|
|
|
463
550
|
// ─── Vite Config ────────────────────────────────────────
|
|
464
551
|
|
|
465
552
|
export default defineConfig({
|
|
553
|
+
// Relative base so Electron can loadFile() the built HTML from disk —
|
|
554
|
+
// absolute "/assets/..." URLs would resolve to the filesystem root.
|
|
555
|
+
base: './',
|
|
466
556
|
plugins: [
|
|
467
557
|
react(),
|
|
468
558
|
tailwindcss(),
|
|
469
559
|
piApiPlugin(),
|
|
470
|
-
chatApiPlugin(),
|
|
471
560
|
],
|
|
472
561
|
resolve: {
|
|
473
562
|
alias: {
|
|
@@ -478,4 +567,13 @@ export default defineConfig({
|
|
|
478
567
|
port: 5176,
|
|
479
568
|
strictPort: true,
|
|
480
569
|
},
|
|
570
|
+
build: {
|
|
571
|
+
rollupOptions: {
|
|
572
|
+
input: {
|
|
573
|
+
main: path.resolve(__dirname, "index.html"),
|
|
574
|
+
// Menu-bar popup (used by the Electron tray app)
|
|
575
|
+
popup: path.resolve(__dirname, "electron/popup.html"),
|
|
576
|
+
},
|
|
577
|
+
},
|
|
578
|
+
},
|
|
481
579
|
});
|