@akiojin/gwt 2.13.0 → 3.0.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.ja.md +41 -0
- package/README.md +38 -0
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +17 -11
- package/dist/claude.js.map +1 -1
- package/dist/cli/ui/components/App.d.ts +0 -6
- package/dist/cli/ui/components/App.d.ts.map +1 -1
- package/dist/cli/ui/components/App.js +27 -88
- package/dist/cli/ui/components/App.js.map +1 -1
- package/dist/cli/ui/components/common/Select.js +2 -2
- package/dist/cli/ui/components/common/Select.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +3 -3
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +1 -1
- package/dist/cli/ui/utils/continueSession.d.ts.map +1 -1
- package/dist/cli/ui/utils/continueSession.js +1 -1
- package/dist/cli/ui/utils/continueSession.js.map +1 -1
- package/dist/client/assets/index-DsDNCy5f.css +1 -0
- package/dist/client/assets/index-f5D2XwDh.js +72 -0
- package/dist/client/index.html +2 -2
- package/dist/codex.d.ts.map +1 -1
- package/dist/codex.js +21 -11
- package/dist/codex.js.map +1 -1
- package/dist/config/builtin-tools.d.ts.map +1 -1
- package/dist/config/builtin-tools.js +3 -2
- package/dist/config/builtin-tools.js.map +1 -1
- package/dist/config/shared-env.d.ts +41 -0
- package/dist/config/shared-env.d.ts.map +1 -0
- package/dist/config/shared-env.js +114 -0
- package/dist/config/shared-env.js.map +1 -0
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +20 -17
- package/dist/gemini.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -8
- package/dist/index.js.map +1 -1
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/logging/logger.js +4 -1
- package/dist/logging/logger.js.map +1 -1
- package/dist/qwen.d.ts.map +1 -1
- package/dist/qwen.js +15 -11
- package/dist/qwen.js.map +1 -1
- package/dist/services/aiToolResolver.d.ts +41 -0
- package/dist/services/aiToolResolver.d.ts.map +1 -0
- package/dist/services/aiToolResolver.js +194 -0
- package/dist/services/aiToolResolver.js.map +1 -0
- package/dist/services/customToolResolver.d.ts +10 -0
- package/dist/services/customToolResolver.d.ts.map +1 -0
- package/dist/services/customToolResolver.js +71 -0
- package/dist/services/customToolResolver.js.map +1 -0
- package/dist/shared/aiToolConstants.d.ts +9 -0
- package/dist/shared/aiToolConstants.d.ts.map +1 -0
- package/dist/shared/aiToolConstants.js +29 -0
- package/dist/shared/aiToolConstants.js.map +1 -0
- package/dist/types/tools.d.ts +12 -0
- package/dist/types/tools.d.ts.map +1 -1
- package/dist/utils/prompt.d.ts.map +1 -1
- package/dist/utils/prompt.js.map +1 -1
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +15 -6
- package/dist/utils/session.js.map +1 -1
- package/dist/utils/terminal.d.ts +12 -3
- package/dist/utils/terminal.d.ts.map +1 -1
- package/dist/utils/terminal.js +5 -34
- package/dist/utils/terminal.js.map +1 -1
- package/dist/utils/webui.d.ts +8 -0
- package/dist/utils/webui.d.ts.map +1 -0
- package/dist/utils/webui.js +35 -0
- package/dist/utils/webui.js.map +1 -0
- package/dist/web/client/src/components/AIToolLaunchModal.d.ts +9 -0
- package/dist/web/client/src/components/AIToolLaunchModal.d.ts.map +1 -0
- package/dist/web/client/src/components/AIToolLaunchModal.js +363 -0
- package/dist/web/client/src/components/AIToolLaunchModal.js.map +1 -0
- package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
- package/dist/web/client/src/components/BranchGraph.js +46 -49
- package/dist/web/client/src/components/BranchGraph.js.map +1 -1
- package/dist/web/client/src/components/CustomToolForm.d.ts +23 -0
- package/dist/web/client/src/components/CustomToolForm.d.ts.map +1 -0
- package/dist/web/client/src/components/CustomToolForm.js +209 -0
- package/dist/web/client/src/components/CustomToolForm.js.map +1 -0
- package/dist/web/client/src/components/CustomToolList.d.ts +10 -0
- package/dist/web/client/src/components/CustomToolList.d.ts.map +1 -0
- package/dist/web/client/src/components/CustomToolList.js +57 -0
- package/dist/web/client/src/components/CustomToolList.js.map +1 -0
- package/dist/web/client/src/components/EnvEditor.d.ts.map +1 -1
- package/dist/web/client/src/components/EnvEditor.js +33 -26
- package/dist/web/client/src/components/EnvEditor.js.map +1 -1
- package/dist/web/client/src/components/EnvironmentEditor.d.ts +17 -0
- package/dist/web/client/src/components/EnvironmentEditor.d.ts.map +1 -0
- package/dist/web/client/src/components/EnvironmentEditor.js +22 -0
- package/dist/web/client/src/components/EnvironmentEditor.js.map +1 -0
- package/dist/web/client/src/components/Terminal.d.ts.map +1 -1
- package/dist/web/client/src/components/Terminal.js +10 -3
- package/dist/web/client/src/components/Terminal.js.map +1 -1
- package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts +10 -0
- package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts.map +1 -0
- package/dist/web/client/src/components/branch-detail/BranchInfoCards.js +104 -0
- package/dist/web/client/src/components/branch-detail/BranchInfoCards.js.map +1 -0
- package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts +22 -0
- package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts.map +1 -0
- package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js +79 -0
- package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js.map +1 -0
- package/dist/web/client/src/components/branch-detail/TerminalPanel.d.ts +11 -0
- package/dist/web/client/src/components/branch-detail/TerminalPanel.d.ts.map +1 -0
- package/dist/web/client/src/components/branch-detail/TerminalPanel.js +32 -0
- package/dist/web/client/src/components/branch-detail/TerminalPanel.js.map +1 -0
- package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts +40 -0
- package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts.map +1 -0
- package/dist/web/client/src/components/branch-detail/ToolLauncher.js +147 -0
- package/dist/web/client/src/components/branch-detail/ToolLauncher.js.map +1 -0
- package/dist/web/client/src/components/branch-detail/index.d.ts +5 -0
- package/dist/web/client/src/components/branch-detail/index.d.ts.map +1 -0
- package/dist/web/client/src/components/branch-detail/index.js +5 -0
- package/dist/web/client/src/components/branch-detail/index.js.map +1 -0
- package/dist/web/client/src/components/common/BranchCard.d.ts +17 -0
- package/dist/web/client/src/components/common/BranchCard.d.ts.map +1 -0
- package/dist/web/client/src/components/common/BranchCard.js +36 -0
- package/dist/web/client/src/components/common/BranchCard.js.map +1 -0
- package/dist/web/client/src/components/common/MetricCard.d.ts +10 -0
- package/dist/web/client/src/components/common/MetricCard.d.ts.map +1 -0
- package/dist/web/client/src/components/common/MetricCard.js +10 -0
- package/dist/web/client/src/components/common/MetricCard.js.map +1 -0
- package/dist/web/client/src/components/common/PageHeader.d.ts +12 -0
- package/dist/web/client/src/components/common/PageHeader.d.ts.map +1 -0
- package/dist/web/client/src/components/common/PageHeader.js +14 -0
- package/dist/web/client/src/components/common/PageHeader.js.map +1 -0
- package/dist/web/client/src/components/common/SearchInput.d.ts +14 -0
- package/dist/web/client/src/components/common/SearchInput.d.ts.map +1 -0
- package/dist/web/client/src/components/common/SearchInput.js +15 -0
- package/dist/web/client/src/components/common/SearchInput.js.map +1 -0
- package/dist/web/client/src/components/common/StatusBadge.d.ts +10 -0
- package/dist/web/client/src/components/common/StatusBadge.d.ts.map +1 -0
- package/dist/web/client/src/components/common/StatusBadge.js +15 -0
- package/dist/web/client/src/components/common/StatusBadge.js.map +1 -0
- package/dist/web/client/src/components/common/index.d.ts +6 -0
- package/dist/web/client/src/components/common/index.d.ts.map +1 -0
- package/dist/web/client/src/components/common/index.js +6 -0
- package/dist/web/client/src/components/common/index.js.map +1 -0
- package/dist/web/client/src/components/ui/alert.d.ts +9 -0
- package/dist/web/client/src/components/ui/alert.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/alert.js +25 -0
- package/dist/web/client/src/components/ui/alert.js.map +1 -0
- package/dist/web/client/src/components/ui/badge.d.ts +10 -0
- package/dist/web/client/src/components/ui/badge.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/badge.js +25 -0
- package/dist/web/client/src/components/ui/badge.js.map +1 -0
- package/dist/web/client/src/components/ui/button.d.ts +12 -0
- package/dist/web/client/src/components/ui/button.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/button.js +33 -0
- package/dist/web/client/src/components/ui/button.js.map +1 -0
- package/dist/web/client/src/components/ui/card.d.ts +9 -0
- package/dist/web/client/src/components/ui/card.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/card.js +16 -0
- package/dist/web/client/src/components/ui/card.js.map +1 -0
- package/dist/web/client/src/components/ui/index.d.ts +8 -0
- package/dist/web/client/src/components/ui/index.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/index.js +8 -0
- package/dist/web/client/src/components/ui/index.js.map +1 -0
- package/dist/web/client/src/components/ui/input.d.ts +4 -0
- package/dist/web/client/src/components/ui/input.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/input.js +8 -0
- package/dist/web/client/src/components/ui/input.js.map +1 -0
- package/dist/web/client/src/components/ui/select.d.ts +14 -0
- package/dist/web/client/src/components/ui/select.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/select.js +39 -0
- package/dist/web/client/src/components/ui/select.js.map +1 -0
- package/dist/web/client/src/components/ui/table.d.ts +11 -0
- package/dist/web/client/src/components/ui/table.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/table.js +21 -0
- package/dist/web/client/src/components/ui/table.js.map +1 -0
- package/dist/web/client/src/hooks/useSessions.d.ts.map +1 -1
- package/dist/web/client/src/hooks/useSessions.js +6 -1
- package/dist/web/client/src/hooks/useSessions.js.map +1 -1
- package/dist/web/client/src/lib/utils.d.ts +7 -0
- package/dist/web/client/src/lib/utils.d.ts.map +1 -0
- package/dist/web/client/src/lib/utils.js +10 -0
- package/dist/web/client/src/lib/utils.js.map +1 -0
- package/dist/web/client/src/lib/websocket.d.ts +7 -0
- package/dist/web/client/src/lib/websocket.d.ts.map +1 -1
- package/dist/web/client/src/lib/websocket.js +44 -0
- package/dist/web/client/src/lib/websocket.js.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.js +125 -376
- package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
- package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/BranchListPage.js +89 -127
- package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
- package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/ConfigManagementPage.js +46 -41
- package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
- package/dist/web/client/src/pages/ConfigPage.d.ts +3 -0
- package/dist/web/client/src/pages/ConfigPage.d.ts.map +1 -0
- package/dist/web/client/src/pages/ConfigPage.js +216 -0
- package/dist/web/client/src/pages/ConfigPage.js.map +1 -0
- package/dist/web/client/vite.config.d.ts.map +1 -1
- package/dist/web/client/vite.config.js +8 -1
- package/dist/web/client/vite.config.js.map +1 -1
- package/dist/web/server/index.d.ts +24 -2
- package/dist/web/server/index.d.ts.map +1 -1
- package/dist/web/server/index.js +46 -15
- package/dist/web/server/index.js.map +1 -1
- package/dist/web/server/pty/manager.d.ts +12 -10
- package/dist/web/server/pty/manager.d.ts.map +1 -1
- package/dist/web/server/pty/manager.js +76 -43
- package/dist/web/server/pty/manager.js.map +1 -1
- package/dist/web/server/routes/sessions.d.ts.map +1 -1
- package/dist/web/server/routes/sessions.js +35 -2
- package/dist/web/server/routes/sessions.js.map +1 -1
- package/dist/web/server/routes/worktrees.js +2 -2
- package/dist/web/server/routes/worktrees.js.map +1 -1
- package/dist/web/server/services/worktrees.d.ts.map +1 -1
- package/dist/web/server/services/worktrees.js +7 -1
- package/dist/web/server/services/worktrees.js.map +1 -1
- package/dist/web/server/tray.d.ts +25 -0
- package/dist/web/server/tray.d.ts.map +1 -0
- package/dist/web/server/tray.js +98 -0
- package/dist/web/server/tray.js.map +1 -0
- package/dist/web/server/websocket/handler.d.ts +18 -2
- package/dist/web/server/websocket/handler.d.ts.map +1 -1
- package/dist/web/server/websocket/handler.js +82 -9
- package/dist/web/server/websocket/handler.js.map +1 -1
- package/package.json +15 -2
- package/src/claude.ts +26 -15
- package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +1 -1
- package/src/cli/ui/__tests__/components/common/Select.test.tsx +11 -0
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +3 -1
- package/src/cli/ui/__tests__/components/screens/BranchQuickStartScreen.test.tsx +4 -4
- package/src/cli/ui/components/App.tsx +33 -133
- package/src/cli/ui/components/common/Select.tsx +2 -2
- package/src/cli/ui/components/screens/BranchListScreen.tsx +28 -21
- package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +41 -46
- package/src/cli/ui/utils/continueSession.ts +1 -7
- package/src/codex.ts +31 -22
- package/src/config/builtin-tools.ts +6 -2
- package/src/config/shared-env.ts +139 -0
- package/src/gemini.ts +35 -22
- package/src/index.ts +13 -8
- package/src/logging/logger.ts +5 -2
- package/src/qwen.ts +28 -19
- package/src/services/aiToolResolver.ts +276 -0
- package/src/services/customToolResolver.ts +98 -0
- package/src/shared/aiToolConstants.ts +30 -0
- package/src/trayicon.d.ts +30 -0
- package/src/types/tools.ts +15 -0
- package/src/utils/prompt.ts +15 -9
- package/src/utils/session.ts +80 -26
- package/src/utils/terminal.ts +11 -41
- package/src/utils/webui.ts +43 -0
- package/src/web/client/components.json +21 -0
- package/src/web/client/src/components/AIToolLaunchModal.tsx +575 -0
- package/src/web/client/src/components/BranchGraph.tsx +95 -75
- package/src/web/client/src/components/CustomToolForm.tsx +386 -0
- package/src/web/client/src/components/CustomToolList.tsx +119 -0
- package/src/web/client/src/components/EnvEditor.tsx +91 -81
- package/src/web/client/src/components/EnvironmentEditor.tsx +97 -0
- package/src/web/client/src/components/Terminal.tsx +11 -3
- package/src/web/client/src/components/branch-detail/BranchInfoCards.tsx +179 -0
- package/src/web/client/src/components/branch-detail/SessionHistoryTable.tsx +181 -0
- package/src/web/client/src/components/branch-detail/TerminalPanel.tsx +92 -0
- package/src/web/client/src/components/branch-detail/ToolLauncher.tsx +327 -0
- package/src/web/client/src/components/branch-detail/index.ts +4 -0
- package/src/web/client/src/components/common/BranchCard.tsx +117 -0
- package/src/web/client/src/components/common/MetricCard.tsx +22 -0
- package/src/web/client/src/components/common/PageHeader.tsx +44 -0
- package/src/web/client/src/components/common/SearchInput.tsx +40 -0
- package/src/web/client/src/components/common/StatusBadge.tsx +37 -0
- package/src/web/client/src/components/common/index.ts +5 -0
- package/src/web/client/src/components/ui/alert.tsx +63 -0
- package/src/web/client/src/components/ui/badge.tsx +44 -0
- package/src/web/client/src/components/ui/button.tsx +57 -0
- package/src/web/client/src/components/ui/card.tsx +82 -0
- package/src/web/client/src/components/ui/index.ts +32 -0
- package/src/web/client/src/components/ui/input.tsx +21 -0
- package/src/web/client/src/components/ui/select.tsx +156 -0
- package/src/web/client/src/components/ui/table.tsx +119 -0
- package/src/web/client/src/hooks/useSessions.ts +10 -1
- package/src/web/client/src/index.css +46 -816
- package/src/web/client/src/lib/utils.ts +10 -0
- package/src/web/client/src/lib/websocket.ts +48 -1
- package/src/web/client/src/pages/BranchDetailPage.tsx +247 -723
- package/src/web/client/src/pages/BranchListPage.tsx +190 -236
- package/src/web/client/src/pages/ConfigManagementPage.tsx +94 -76
- package/src/web/client/src/pages/ConfigPage.tsx +362 -0
- package/src/web/client/vite.config.ts +8 -1
- package/src/web/server/index.ts +72 -15
- package/src/web/server/pty/manager.ts +128 -55
- package/src/web/server/routes/sessions.ts +59 -7
- package/src/web/server/routes/worktrees.ts +3 -3
- package/src/web/server/services/worktrees.ts +12 -4
- package/src/web/server/tray.ts +120 -0
- package/src/web/server/websocket/handler.ts +119 -13
- package/dist/client/assets/index-DeNwPosA.css +0 -1
- package/dist/client/assets/index-Dl798X5w.js +0 -32
package/src/web/server/index.ts
CHANGED
|
@@ -16,14 +16,42 @@ import { registerRoutes } from "./routes/index.js";
|
|
|
16
16
|
import { importOsEnvIntoSharedConfig } from "./env/importer.js";
|
|
17
17
|
import { createLogger } from "../../logging/logger.js";
|
|
18
18
|
import type { WebFastifyInstance } from "./types.js";
|
|
19
|
+
import { disposeSystemTray, startSystemTray } from "./tray.js";
|
|
20
|
+
import { resolveWebUiPort } from "../../utils/webui.js";
|
|
19
21
|
|
|
20
22
|
const __filename = fileURLToPath(import.meta.url);
|
|
21
23
|
const __dirname = dirname(__filename);
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
|
-
* Web
|
|
26
|
+
* Web UI サーバーのライフサイクル操作ハンドル。
|
|
27
|
+
*
|
|
28
|
+
* `close()` は Web UI サーバーを停止し、関連リソース(PTY/トレイ等)を解放します。
|
|
29
|
+
*/
|
|
30
|
+
export interface WebServerHandle {
|
|
31
|
+
close: () => Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* `startWebServer` の起動オプション。
|
|
25
36
|
*/
|
|
26
|
-
export
|
|
37
|
+
export interface StartWebServerOptions {
|
|
38
|
+
/**
|
|
39
|
+
* true の場合、Web UI サーバーが CLI 本体の終了をブロックしないようにします。
|
|
40
|
+
* (内部で `server.unref()` を呼び、サーバーの存在がプロセス生存を維持しないようにします)
|
|
41
|
+
*/
|
|
42
|
+
background?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Web UI サーバーを起動します。
|
|
47
|
+
*
|
|
48
|
+
* @param options - 起動オプション
|
|
49
|
+
* @returns Web UI サーバー停止用のハンドル
|
|
50
|
+
* @throws サーバー起動(listen/初期化)に失敗した場合
|
|
51
|
+
*/
|
|
52
|
+
export async function startWebServer(
|
|
53
|
+
options: StartWebServerOptions = {},
|
|
54
|
+
): Promise<WebServerHandle> {
|
|
27
55
|
const serverLogger = createLogger({ category: "server" });
|
|
28
56
|
|
|
29
57
|
const fastify: WebFastifyInstance = Fastify({
|
|
@@ -32,7 +60,7 @@ export async function startWebServer(): Promise<void> {
|
|
|
32
60
|
|
|
33
61
|
// PTYマネージャーとWebSocketハンドラーを初期化
|
|
34
62
|
const ptyManager = new PTYManager();
|
|
35
|
-
const wsHandler = new WebSocketHandler(ptyManager);
|
|
63
|
+
const wsHandler = new WebSocketHandler(ptyManager, fastify.log);
|
|
36
64
|
|
|
37
65
|
// WebSocketサポートを追加
|
|
38
66
|
await fastify.register(fastifyWebsocket);
|
|
@@ -60,17 +88,46 @@ export async function startWebServer(): Promise<void> {
|
|
|
60
88
|
});
|
|
61
89
|
|
|
62
90
|
// サーバー起動
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
91
|
+
const port = resolveWebUiPort();
|
|
92
|
+
// Docker環境からホストOSでアクセスできるよう、0.0.0.0でリッスン
|
|
93
|
+
// IPv4/IPv6両方対応のため、listenOnStart: false も検討可能
|
|
94
|
+
const host = process.env.HOST || "0.0.0.0";
|
|
95
|
+
|
|
96
|
+
await fastify.listen({ port, host });
|
|
97
|
+
const accessUrl = `http://localhost:${port}`;
|
|
98
|
+
serverLogger.info({ host, port, accessUrl }, "Web UI server started");
|
|
99
|
+
await startSystemTray(accessUrl);
|
|
100
|
+
|
|
101
|
+
if (options.background) {
|
|
102
|
+
fastify.server?.unref?.();
|
|
75
103
|
}
|
|
104
|
+
|
|
105
|
+
let closed = false;
|
|
106
|
+
return {
|
|
107
|
+
close: async () => {
|
|
108
|
+
if (closed) return;
|
|
109
|
+
closed = true;
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
try {
|
|
113
|
+
disposeSystemTray();
|
|
114
|
+
} catch (err) {
|
|
115
|
+
serverLogger.warn({ err }, "System tray cleanup failed");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const session of ptyManager.list()) {
|
|
119
|
+
try {
|
|
120
|
+
ptyManager.delete(session.sessionId);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
serverLogger.warn(
|
|
123
|
+
{ err, sessionId: session.sessionId },
|
|
124
|
+
"Failed to delete PTY session",
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} finally {
|
|
129
|
+
await fastify.close();
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
};
|
|
76
133
|
}
|
|
@@ -9,6 +9,14 @@ import * as pty from "node-pty";
|
|
|
9
9
|
import type { IPty } from "node-pty";
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
11
|
import type { AIToolSession } from "../../../types/api.js";
|
|
12
|
+
import {
|
|
13
|
+
resolveClaudeCommand,
|
|
14
|
+
resolveCodexCommand,
|
|
15
|
+
resolveCustomToolCommand,
|
|
16
|
+
AIToolResolutionError,
|
|
17
|
+
type ResolvedCommand,
|
|
18
|
+
} from "../../../services/aiToolResolver.js";
|
|
19
|
+
import { loadToolsConfig } from "../../../config/tools.js";
|
|
12
20
|
|
|
13
21
|
export interface PTYInstance {
|
|
14
22
|
ptyProcess: IPty;
|
|
@@ -24,37 +32,80 @@ export class PTYManager {
|
|
|
24
32
|
/**
|
|
25
33
|
* 新しいPTYセッションを作成
|
|
26
34
|
*/
|
|
27
|
-
public spawn(
|
|
35
|
+
public async spawn(
|
|
28
36
|
toolType: "claude-code" | "codex-cli" | "custom",
|
|
29
37
|
worktreePath: string,
|
|
30
38
|
mode: "normal" | "continue" | "resume",
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
options: {
|
|
40
|
+
toolName?: string | null;
|
|
41
|
+
cols?: number;
|
|
42
|
+
rows?: number;
|
|
43
|
+
skipPermissions?: boolean;
|
|
44
|
+
bypassApprovals?: boolean;
|
|
45
|
+
extraArgs?: string[];
|
|
46
|
+
customToolId?: string | null;
|
|
47
|
+
} = {},
|
|
48
|
+
): Promise<{ sessionId: string; session: AIToolSession }> {
|
|
49
|
+
const cols = options.cols ?? 80;
|
|
50
|
+
const rows = options.rows ?? 24;
|
|
51
|
+
const toolName = options.toolName ?? null;
|
|
35
52
|
const sessionId = randomUUID();
|
|
36
53
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
54
|
+
const resolverOptions: {
|
|
55
|
+
toolName?: string | null;
|
|
56
|
+
skipPermissions?: boolean;
|
|
57
|
+
bypassApprovals?: boolean;
|
|
58
|
+
extraArgs?: string[];
|
|
59
|
+
customToolId?: string | null;
|
|
60
|
+
} = {};
|
|
61
|
+
|
|
62
|
+
if (toolName !== null) {
|
|
63
|
+
resolverOptions.toolName = toolName;
|
|
64
|
+
}
|
|
65
|
+
if (options.skipPermissions !== undefined) {
|
|
66
|
+
resolverOptions.skipPermissions = options.skipPermissions;
|
|
67
|
+
}
|
|
68
|
+
if (options.bypassApprovals !== undefined) {
|
|
69
|
+
resolverOptions.bypassApprovals = options.bypassApprovals;
|
|
70
|
+
}
|
|
71
|
+
if (options.extraArgs && options.extraArgs.length > 0) {
|
|
72
|
+
resolverOptions.extraArgs = options.extraArgs;
|
|
73
|
+
}
|
|
74
|
+
if (options.customToolId !== undefined) {
|
|
75
|
+
resolverOptions.customToolId = options.customToolId;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const resolved = await this.resolveCommand(toolType, mode, resolverOptions);
|
|
79
|
+
const sharedEnv = await this.loadSharedEnv();
|
|
80
|
+
|
|
81
|
+
const env: NodeJS.ProcessEnv = {
|
|
82
|
+
...process.env,
|
|
83
|
+
...sharedEnv,
|
|
84
|
+
TERM: "xterm-256color",
|
|
85
|
+
COLORTERM: "truecolor",
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
if (resolved.env) {
|
|
89
|
+
Object.assign(env, resolved.env);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (toolType === "claude-code" && options.skipPermissions && isRootUser()) {
|
|
93
|
+
env.IS_SANDBOX = "1";
|
|
94
|
+
}
|
|
40
95
|
|
|
41
96
|
// PTYプロセスをスポーン
|
|
42
|
-
const ptyProcess = pty.spawn(command, args, {
|
|
97
|
+
const ptyProcess = pty.spawn(resolved.command, resolved.args, {
|
|
43
98
|
name: "xterm-256color",
|
|
44
99
|
cols,
|
|
45
100
|
rows,
|
|
46
101
|
cwd: worktreePath,
|
|
47
|
-
env
|
|
48
|
-
...process.env,
|
|
49
|
-
TERM: "xterm-256color",
|
|
50
|
-
COLORTERM: "truecolor",
|
|
51
|
-
},
|
|
102
|
+
env,
|
|
52
103
|
});
|
|
53
104
|
|
|
54
105
|
const session: AIToolSession = {
|
|
55
106
|
sessionId,
|
|
56
107
|
toolType,
|
|
57
|
-
toolName: toolName
|
|
108
|
+
toolName: options.customToolId ?? toolName ?? null,
|
|
58
109
|
mode,
|
|
59
110
|
worktreePath,
|
|
60
111
|
ptyPid: ptyProcess.pid,
|
|
@@ -133,57 +184,79 @@ export class PTYManager {
|
|
|
133
184
|
return Array.from(this.instances.values()).map((inst) => inst.session);
|
|
134
185
|
}
|
|
135
186
|
|
|
136
|
-
|
|
137
|
-
* AI Toolのコマンドを構築
|
|
138
|
-
*/
|
|
139
|
-
private buildCommand(
|
|
187
|
+
private async resolveCommand(
|
|
140
188
|
toolType: "claude-code" | "codex-cli" | "custom",
|
|
141
189
|
mode: "normal" | "continue" | "resume",
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (toolType === "codex-cli") {
|
|
151
|
-
return "codex";
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// claude-code
|
|
155
|
-
return "claude";
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* AI Toolの引数を構築
|
|
160
|
-
*/
|
|
161
|
-
private buildArgs(
|
|
162
|
-
toolType: "claude-code" | "codex-cli" | "custom",
|
|
163
|
-
mode: "normal" | "continue" | "resume",
|
|
164
|
-
): string[] {
|
|
190
|
+
options: {
|
|
191
|
+
toolName?: string | null;
|
|
192
|
+
skipPermissions?: boolean;
|
|
193
|
+
bypassApprovals?: boolean;
|
|
194
|
+
extraArgs?: string[];
|
|
195
|
+
customToolId?: string | null;
|
|
196
|
+
},
|
|
197
|
+
): Promise<ResolvedCommand> {
|
|
165
198
|
if (toolType === "custom") {
|
|
166
|
-
|
|
167
|
-
|
|
199
|
+
const toolId = options.customToolId ?? options.toolName;
|
|
200
|
+
if (!toolId) {
|
|
201
|
+
throw new AIToolResolutionError(
|
|
202
|
+
"COMMAND_NOT_FOUND",
|
|
203
|
+
"Custom tool identifier is required to start a session.",
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return resolveCustomToolCommand({
|
|
208
|
+
toolId,
|
|
209
|
+
mode,
|
|
210
|
+
...(options.skipPermissions !== undefined
|
|
211
|
+
? { skipPermissions: options.skipPermissions }
|
|
212
|
+
: {}),
|
|
213
|
+
...(options.extraArgs ? { extraArgs: options.extraArgs } : {}),
|
|
214
|
+
});
|
|
168
215
|
}
|
|
169
216
|
|
|
170
217
|
if (toolType === "codex-cli") {
|
|
171
|
-
|
|
172
|
-
|
|
218
|
+
const codexOptions: {
|
|
219
|
+
mode: "normal" | "continue" | "resume";
|
|
220
|
+
bypassApprovals?: boolean;
|
|
221
|
+
extraArgs?: string[];
|
|
222
|
+
} = { mode };
|
|
223
|
+
|
|
224
|
+
if (options.bypassApprovals !== undefined) {
|
|
225
|
+
codexOptions.bypassApprovals = options.bypassApprovals;
|
|
173
226
|
}
|
|
174
|
-
if (
|
|
175
|
-
|
|
227
|
+
if (options.extraArgs && options.extraArgs.length > 0) {
|
|
228
|
+
codexOptions.extraArgs = options.extraArgs;
|
|
176
229
|
}
|
|
177
|
-
|
|
230
|
+
|
|
231
|
+
return resolveCodexCommand(codexOptions);
|
|
178
232
|
}
|
|
179
233
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
234
|
+
const claudeOptions: {
|
|
235
|
+
mode: "normal" | "continue" | "resume";
|
|
236
|
+
skipPermissions?: boolean;
|
|
237
|
+
extraArgs?: string[];
|
|
238
|
+
} = { mode };
|
|
239
|
+
|
|
240
|
+
if (options.skipPermissions !== undefined) {
|
|
241
|
+
claudeOptions.skipPermissions = options.skipPermissions;
|
|
183
242
|
}
|
|
184
|
-
if (
|
|
185
|
-
|
|
243
|
+
if (options.extraArgs && options.extraArgs.length > 0) {
|
|
244
|
+
claudeOptions.extraArgs = options.extraArgs;
|
|
186
245
|
}
|
|
187
|
-
|
|
246
|
+
|
|
247
|
+
return resolveClaudeCommand(claudeOptions);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private async loadSharedEnv(): Promise<Record<string, string>> {
|
|
251
|
+
const config = await loadToolsConfig();
|
|
252
|
+
return { ...(config.env ?? {}) };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function isRootUser(): boolean {
|
|
257
|
+
try {
|
|
258
|
+
return typeof process.getuid === "function" && process.getuid() === 0;
|
|
259
|
+
} catch {
|
|
260
|
+
return false;
|
|
188
261
|
}
|
|
189
262
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { PTYManager } from "../pty/manager.js";
|
|
8
|
+
import { AIToolResolutionError } from "../../../services/aiToolResolver.js";
|
|
8
9
|
import type {
|
|
9
10
|
ApiResponse,
|
|
10
11
|
AIToolSession,
|
|
@@ -28,7 +29,7 @@ export async function registerSessionRoutes(
|
|
|
28
29
|
try {
|
|
29
30
|
const sessions = ptyManager.list();
|
|
30
31
|
return { success: true, data: sessions };
|
|
31
|
-
} catch (error) {
|
|
32
|
+
} catch (error: unknown) {
|
|
32
33
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
33
34
|
reply.code(500);
|
|
34
35
|
return {
|
|
@@ -46,13 +47,55 @@ export async function registerSessionRoutes(
|
|
|
46
47
|
Reply: ApiResponse<AIToolSession>;
|
|
47
48
|
}>("/api/sessions", async (request, reply) => {
|
|
48
49
|
try {
|
|
49
|
-
const {
|
|
50
|
+
const {
|
|
51
|
+
toolType,
|
|
52
|
+
toolName,
|
|
53
|
+
mode,
|
|
54
|
+
worktreePath,
|
|
55
|
+
skipPermissions,
|
|
56
|
+
bypassApprovals,
|
|
57
|
+
extraArgs,
|
|
58
|
+
customToolId,
|
|
59
|
+
} = request.body;
|
|
60
|
+
|
|
61
|
+
const spawnOptions: {
|
|
62
|
+
toolName?: string | null;
|
|
63
|
+
skipPermissions?: boolean;
|
|
64
|
+
bypassApprovals?: boolean;
|
|
65
|
+
extraArgs?: string[];
|
|
66
|
+
customToolId?: string | null;
|
|
67
|
+
} = {};
|
|
68
|
+
|
|
69
|
+
if (typeof toolName !== "undefined") {
|
|
70
|
+
spawnOptions.toolName = toolName;
|
|
71
|
+
}
|
|
72
|
+
if (typeof skipPermissions !== "undefined") {
|
|
73
|
+
spawnOptions.skipPermissions = skipPermissions;
|
|
74
|
+
}
|
|
75
|
+
if (typeof bypassApprovals !== "undefined") {
|
|
76
|
+
spawnOptions.bypassApprovals = bypassApprovals;
|
|
77
|
+
}
|
|
78
|
+
if (Array.isArray(extraArgs) && extraArgs.length > 0) {
|
|
79
|
+
spawnOptions.extraArgs = extraArgs;
|
|
80
|
+
}
|
|
81
|
+
if (typeof customToolId !== "undefined") {
|
|
82
|
+
spawnOptions.customToolId = customToolId;
|
|
83
|
+
}
|
|
50
84
|
|
|
51
|
-
|
|
85
|
+
if (toolType === "custom" && !toolName && !customToolId) {
|
|
86
|
+
reply.code(400);
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: "Custom tool requires toolName or customToolId",
|
|
90
|
+
details: null,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const { session } = await ptyManager.spawn(
|
|
52
95
|
toolType,
|
|
53
96
|
worktreePath,
|
|
54
97
|
mode,
|
|
55
|
-
|
|
98
|
+
spawnOptions,
|
|
56
99
|
);
|
|
57
100
|
|
|
58
101
|
// 履歴を永続化(best-effort)
|
|
@@ -95,7 +138,16 @@ export async function registerSessionRoutes(
|
|
|
95
138
|
|
|
96
139
|
reply.code(201);
|
|
97
140
|
return { success: true, data: session };
|
|
98
|
-
} catch (error) {
|
|
141
|
+
} catch (error: unknown) {
|
|
142
|
+
if (error instanceof AIToolResolutionError) {
|
|
143
|
+
reply.code(400);
|
|
144
|
+
return {
|
|
145
|
+
success: false,
|
|
146
|
+
error: error.message,
|
|
147
|
+
details: error.hints?.join("\n") ?? null,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
99
151
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
100
152
|
reply.code(500);
|
|
101
153
|
return {
|
|
@@ -125,7 +177,7 @@ export async function registerSessionRoutes(
|
|
|
125
177
|
}
|
|
126
178
|
|
|
127
179
|
return { success: true, data: instance.session };
|
|
128
|
-
} catch (error) {
|
|
180
|
+
} catch (error: unknown) {
|
|
129
181
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
130
182
|
reply.code(500);
|
|
131
183
|
return {
|
|
@@ -157,7 +209,7 @@ export async function registerSessionRoutes(
|
|
|
157
209
|
}
|
|
158
210
|
|
|
159
211
|
return { success: true };
|
|
160
|
-
} catch (error) {
|
|
212
|
+
} catch (error: unknown) {
|
|
161
213
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
162
214
|
reply.code(500);
|
|
163
215
|
return {
|
|
@@ -64,13 +64,13 @@ export async function registerWorktreeRoutes(
|
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
// DELETE /api/worktrees - Worktreeを削除
|
|
67
|
+
// DELETE /api/worktrees/delete - Worktreeを削除
|
|
68
68
|
fastify.delete<{
|
|
69
|
-
Querystring: { path: string };
|
|
69
|
+
Querystring: { path: string; force?: boolean };
|
|
70
70
|
Reply:
|
|
71
71
|
| { success: true }
|
|
72
72
|
| { success: false; error: string; details?: string | null };
|
|
73
|
-
}>("/api/worktrees", async (request, reply) => {
|
|
73
|
+
}>("/api/worktrees/delete", async (request, reply) => {
|
|
74
74
|
try {
|
|
75
75
|
const { path } = request.query;
|
|
76
76
|
|
|
@@ -30,6 +30,8 @@ export async function listWorktrees(): Promise<Worktree[]> {
|
|
|
30
30
|
isProtected: isProtectedBranchName(wt.branch),
|
|
31
31
|
createdAt: null, // git worktreeからは取得不可
|
|
32
32
|
lastAccessedAt: null, // git worktreeからは取得不可
|
|
33
|
+
divergence: null,
|
|
34
|
+
prInfo: null,
|
|
33
35
|
}));
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -50,16 +52,22 @@ export async function createNewWorktree(
|
|
|
50
52
|
branchName: string,
|
|
51
53
|
createBranch: boolean,
|
|
52
54
|
): Promise<Worktree> {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
// 保護ブランチのチェック
|
|
56
|
+
if (isProtectedBranchName(branchName)) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Cannot create worktree for protected branch: ${branchName}. Protected branches (main, develop, master) must remain in the main repository.`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const { getRepositoryRoot, getCurrentBranch } =
|
|
63
|
+
await import("../../../git.js");
|
|
56
64
|
|
|
57
65
|
const [repoRoot, currentBranch] = await Promise.all([
|
|
58
66
|
getRepositoryRoot(),
|
|
59
67
|
getCurrentBranch(),
|
|
60
68
|
]);
|
|
61
69
|
|
|
62
|
-
const worktreePath = await generateWorktreePath(
|
|
70
|
+
const worktreePath = await generateWorktreePath(repoRoot, branchName);
|
|
63
71
|
|
|
64
72
|
await createWorktreeCore({
|
|
65
73
|
branchName,
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
import { createLogger } from "../../logging/logger.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* URL を開く関数の型定義
|
|
6
|
+
* @param url - 開くURL
|
|
7
|
+
*/
|
|
8
|
+
export type OpenUrlFn = (url: string) => Promise<void> | void;
|
|
9
|
+
|
|
10
|
+
const TRAY_ICON_BASE64 =
|
|
11
|
+
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAJ1BMVEUAAAAAvNQAvNQAvNQAvNQAvNQAvNQAvNQAvNQAvNQAvNQAvNT////J1ubyAAAAC3RSTlMAJYTcgyQJnJ3U3WXfUogAAAABYktHRAyBs1FjAAAAB3RJTUUH6QwMCRccbOpRBQAAAFdJREFUCNdjYEAARuXNriCarXr37t1tQEYmkN69M4GBoRvE2F3AwAqmd29kYIEwNjEwQxibGbghjN0MXDARJpgaqK6tDAzVUHMQJoPtKgPZyuq8S5WBAQBeRj51tvdhawAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyNS0xMi0xMlQwOToyMzoyOCswMDowMBPEA5UAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjUtMTItMTJUMDk6MjM6MjgrMDA6MDBimbspAAAAAElFTkSuQmCC";
|
|
12
|
+
|
|
13
|
+
let trayInitAttempted = false;
|
|
14
|
+
type TrayHandle = { dispose?: () => void; kill?: () => void };
|
|
15
|
+
let trayInstance: TrayHandle | null = null;
|
|
16
|
+
let trayInitPromise: Promise<TrayHandle> | null = null;
|
|
17
|
+
|
|
18
|
+
function shouldEnableTray(
|
|
19
|
+
platform: NodeJS.Platform = process.platform,
|
|
20
|
+
): boolean {
|
|
21
|
+
// NOTE: `trayicon` is a win32-only dependency.
|
|
22
|
+
if (platform !== "win32") return false;
|
|
23
|
+
if (process.env.GWT_DISABLE_TRAY?.toLowerCase() === "true") return false;
|
|
24
|
+
if (process.env.GWT_DISABLE_TRAY === "1") return false;
|
|
25
|
+
if (process.env.CI) return false;
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* デフォルトブラウザでURLを開く
|
|
31
|
+
* @param url - 開くURL
|
|
32
|
+
*/
|
|
33
|
+
export async function openUrl(url: string): Promise<void> {
|
|
34
|
+
const platform = process.platform;
|
|
35
|
+
try {
|
|
36
|
+
if (platform === "win32") {
|
|
37
|
+
await execa("explorer.exe", [url], { windowsHide: true });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (platform === "darwin") {
|
|
41
|
+
await execa("open", [url]);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
await execa("xdg-open", [url], { stdio: "ignore" });
|
|
45
|
+
} catch {
|
|
46
|
+
// Ignore errors to avoid disrupting CLI/Web UI
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* システムトレイアイコンを初期化
|
|
52
|
+
* @param url - Web UI のURL(ダブルクリック時に開く)
|
|
53
|
+
* @param opts - オプション設定
|
|
54
|
+
* @param opts.openUrl - URL を開くカスタム関数(テスト用)
|
|
55
|
+
*/
|
|
56
|
+
export async function startSystemTray(
|
|
57
|
+
url: string,
|
|
58
|
+
opts?: { openUrl?: OpenUrlFn; platform?: NodeJS.Platform },
|
|
59
|
+
): Promise<void> {
|
|
60
|
+
if (trayInitAttempted || !shouldEnableTray(opts?.platform)) return;
|
|
61
|
+
trayInitAttempted = true;
|
|
62
|
+
|
|
63
|
+
const logger = createLogger({ category: "tray" });
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const mod = (await import("trayicon")) as Record<string, unknown> & {
|
|
67
|
+
default?: Record<string, unknown>;
|
|
68
|
+
};
|
|
69
|
+
const create = mod.create ?? mod.default?.create;
|
|
70
|
+
if (typeof create !== "function") {
|
|
71
|
+
throw new Error("trayicon.create not available");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const icon = Buffer.from(TRAY_ICON_BASE64, "base64");
|
|
75
|
+
const open = opts?.openUrl ?? openUrl;
|
|
76
|
+
|
|
77
|
+
const initPromise = Promise.resolve(
|
|
78
|
+
create({
|
|
79
|
+
icon,
|
|
80
|
+
title: "gwt Web UI",
|
|
81
|
+
tooltip: "Double-click to open Web UI",
|
|
82
|
+
action: async () => {
|
|
83
|
+
await open(url);
|
|
84
|
+
},
|
|
85
|
+
}) as TrayHandle,
|
|
86
|
+
);
|
|
87
|
+
trayInitPromise = initPromise;
|
|
88
|
+
|
|
89
|
+
void initPromise
|
|
90
|
+
.then((tray) => {
|
|
91
|
+
if (trayInitPromise !== initPromise) {
|
|
92
|
+
tray.dispose?.();
|
|
93
|
+
tray.kill?.();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
trayInstance = tray;
|
|
97
|
+
})
|
|
98
|
+
.catch((err) => {
|
|
99
|
+
if (trayInitPromise !== initPromise) return;
|
|
100
|
+
logger.warn({ err }, "System tray failed to initialize");
|
|
101
|
+
});
|
|
102
|
+
} catch (err) {
|
|
103
|
+
logger.warn({ err }, "System tray failed to initialize");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* システムトレイアイコンを破棄
|
|
109
|
+
*/
|
|
110
|
+
export function disposeSystemTray(): void {
|
|
111
|
+
trayInitPromise = null;
|
|
112
|
+
|
|
113
|
+
const instance = trayInstance;
|
|
114
|
+
trayInstance = null;
|
|
115
|
+
|
|
116
|
+
instance?.dispose?.();
|
|
117
|
+
instance?.kill?.();
|
|
118
|
+
|
|
119
|
+
trayInitAttempted = false;
|
|
120
|
+
}
|