@akiojin/gwt 2.12.1 → 2.14.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 +33 -0
- package/README.md +31 -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 +93 -85
- 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 +3 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js +168 -34
- 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/hooks/useGitData.d.ts.map +1 -1
- package/dist/cli/ui/hooks/useGitData.js +17 -0
- package/dist/cli/ui/hooks/useGitData.js.map +1 -1
- package/dist/cli/ui/types.d.ts +2 -0
- package/dist/cli/ui/types.d.ts.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.js +7 -2
- package/dist/cli/ui/utils/branchFormatter.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/cli/ui/utils/modelOptions.d.ts.map +1 -1
- package/dist/cli/ui/utils/modelOptions.js +7 -0
- package/dist/cli/ui/utils/modelOptions.js.map +1 -1
- package/dist/client/assets/index-DPWWHorC.js +72 -0
- package/dist/client/assets/index-DsDNCy5f.css +1 -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 +55 -7
- package/dist/index.js.map +1 -1
- package/dist/logging/logger.d.ts +24 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +60 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/logging/rotation.d.ts +6 -0
- package/dist/logging/rotation.d.ts.map +1 -0
- package/dist/logging/rotation.js +26 -0
- package/dist/logging/rotation.js.map +1 -0
- 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 +113 -361
- 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 +49 -18
- 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/branches.d.ts +2 -2
- package/dist/web/server/routes/branches.d.ts.map +1 -1
- package/dist/web/server/routes/branches.js.map +1 -1
- package/dist/web/server/routes/config.d.ts +2 -2
- package/dist/web/server/routes/config.d.ts.map +1 -1
- package/dist/web/server/routes/config.js.map +1 -1
- package/dist/web/server/routes/index.d.ts +2 -2
- package/dist/web/server/routes/index.d.ts.map +1 -1
- package/dist/web/server/routes/index.js.map +1 -1
- package/dist/web/server/routes/sessions.d.ts +2 -2
- 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.d.ts +2 -2
- package/dist/web/server/routes/worktrees.d.ts.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 +24 -0
- package/dist/web/server/tray.d.ts.map +1 -0
- package/dist/web/server/tray.js +79 -0
- package/dist/web/server/tray.js.map +1 -0
- package/dist/web/server/types.d.ts +4 -0
- package/dist/web/server/types.d.ts.map +1 -0
- package/dist/web/server/types.js +2 -0
- package/dist/web/server/types.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/dist/worktree.d.ts +1 -0
- package/dist/worktree.d.ts.map +1 -1
- package/dist/worktree.js.map +1 -1
- package/package.json +17 -3
- package/src/claude.ts +26 -15
- package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +13 -13
- package/src/cli/ui/__tests__/components/common/Select.test.tsx +11 -0
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +97 -33
- package/src/cli/ui/__tests__/components/screens/BranchQuickStartScreen.test.tsx +4 -4
- package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +7 -3
- package/src/cli/ui/components/App.tsx +111 -125
- package/src/cli/ui/components/common/Select.tsx +2 -2
- package/src/cli/ui/components/screens/BranchListScreen.tsx +220 -34
- package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +41 -46
- package/src/cli/ui/hooks/useGitData.ts +20 -0
- package/src/cli/ui/types.ts +3 -0
- package/src/cli/ui/utils/branchFormatter.ts +7 -2
- package/src/cli/ui/utils/continueSession.ts +1 -7
- package/src/cli/ui/utils/modelOptions.test.ts +14 -0
- package/src/cli/ui/utils/modelOptions.ts +7 -0
- 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 +61 -7
- package/src/logging/logger.ts +82 -0
- package/src/logging/rotation.ts +25 -0
- 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 +222 -694
- 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 +78 -19
- package/src/web/server/pty/manager.ts +128 -55
- package/src/web/server/routes/branches.ts +2 -2
- package/src/web/server/routes/config.ts +2 -2
- package/src/web/server/routes/index.ts +2 -2
- package/src/web/server/routes/sessions.ts +61 -9
- package/src/web/server/routes/worktrees.ts +5 -5
- package/src/web/server/services/worktrees.ts +12 -4
- package/src/web/server/tray.ts +93 -0
- package/src/web/server/types.ts +14 -0
- package/src/web/server/websocket/handler.ts +119 -13
- package/src/worktree.ts +1 -0
- package/dist/client/assets/index-DeNwPosA.css +0 -1
- package/dist/client/assets/index-Dl798X5w.js +0 -32
package/src/codex.ts
CHANGED
|
@@ -3,10 +3,7 @@ import chalk from "chalk";
|
|
|
3
3
|
import { platform } from "os";
|
|
4
4
|
import { existsSync } from "fs";
|
|
5
5
|
import { createChildStdio, getTerminalStreams } from "./utils/terminal.js";
|
|
6
|
-
import {
|
|
7
|
-
findLatestCodexSession,
|
|
8
|
-
waitForCodexSessionId,
|
|
9
|
-
} from "./utils/session.js";
|
|
6
|
+
import { findLatestCodexSession } from "./utils/session.js";
|
|
10
7
|
|
|
11
8
|
const CODEX_CLI_PACKAGE = "@openai/codex@latest";
|
|
12
9
|
|
|
@@ -85,11 +82,9 @@ export async function launchCodexCLI(
|
|
|
85
82
|
options.sessionId && options.sessionId.trim().length > 0
|
|
86
83
|
? options.sessionId.trim()
|
|
87
84
|
: null;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
() => null,
|
|
92
|
-
);
|
|
85
|
+
const usedExplicitSessionId =
|
|
86
|
+
Boolean(resumeSessionId) &&
|
|
87
|
+
(options.mode === "continue" || options.mode === "resume");
|
|
93
88
|
|
|
94
89
|
switch (options.mode) {
|
|
95
90
|
case "continue":
|
|
@@ -141,15 +136,23 @@ export async function launchCodexCLI(
|
|
|
141
136
|
|
|
142
137
|
const childStdio = createChildStdio();
|
|
143
138
|
|
|
144
|
-
const env =
|
|
139
|
+
const env = Object.fromEntries(
|
|
140
|
+
Object.entries({
|
|
141
|
+
...process.env,
|
|
142
|
+
...(options.envOverrides ?? {}),
|
|
143
|
+
}).filter(
|
|
144
|
+
(entry): entry is [string, string] => typeof entry[1] === "string",
|
|
145
|
+
),
|
|
146
|
+
);
|
|
145
147
|
|
|
146
148
|
try {
|
|
147
|
-
const execChild = async (child:
|
|
149
|
+
const execChild = async (child: Promise<unknown>) => {
|
|
148
150
|
try {
|
|
149
151
|
await child;
|
|
150
|
-
} catch (execError:
|
|
152
|
+
} catch (execError: unknown) {
|
|
151
153
|
// Treat SIGINT/SIGTERM as normal exit (user pressed Ctrl+C)
|
|
152
|
-
|
|
154
|
+
const signal = (execError as { signal?: unknown })?.signal;
|
|
155
|
+
if (signal === "SIGINT" || signal === "SIGTERM") {
|
|
153
156
|
return;
|
|
154
157
|
}
|
|
155
158
|
throw execError;
|
|
@@ -163,7 +166,7 @@ export async function launchCodexCLI(
|
|
|
163
166
|
stdout: childStdio.stdout,
|
|
164
167
|
stderr: childStdio.stderr,
|
|
165
168
|
env,
|
|
166
|
-
}
|
|
169
|
+
});
|
|
167
170
|
await execChild(child);
|
|
168
171
|
} finally {
|
|
169
172
|
childStdio.cleanup();
|
|
@@ -181,10 +184,13 @@ export async function launchCodexCLI(
|
|
|
181
184
|
windowMs: 10 * 60 * 1000,
|
|
182
185
|
cwd: worktreePath,
|
|
183
186
|
});
|
|
184
|
-
|
|
185
|
-
|
|
187
|
+
const detectedSessionId = latest?.id ?? null;
|
|
188
|
+
// When we explicitly resumed a specific session, keep that ID as the source of truth.
|
|
189
|
+
capturedSessionId = usedExplicitSessionId
|
|
190
|
+
? resumeSessionId
|
|
191
|
+
: detectedSessionId;
|
|
186
192
|
} catch {
|
|
187
|
-
capturedSessionId = resumeSessionId
|
|
193
|
+
capturedSessionId = usedExplicitSessionId ? resumeSessionId : null;
|
|
188
194
|
}
|
|
189
195
|
|
|
190
196
|
if (capturedSessionId) {
|
|
@@ -201,11 +207,13 @@ export async function launchCodexCLI(
|
|
|
201
207
|
}
|
|
202
208
|
|
|
203
209
|
return capturedSessionId ? { sessionId: capturedSessionId } : {};
|
|
204
|
-
} catch (error:
|
|
210
|
+
} catch (error: unknown) {
|
|
211
|
+
const err = error as NodeJS.ErrnoException;
|
|
212
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
205
213
|
const errorMessage =
|
|
206
|
-
|
|
214
|
+
err.code === "ENOENT"
|
|
207
215
|
? "bunx command not found. Please ensure Bun is installed so Codex CLI can run via bunx."
|
|
208
|
-
: `Failed to launch Codex CLI: ${
|
|
216
|
+
: `Failed to launch Codex CLI: ${details || "Unknown error"}`;
|
|
209
217
|
|
|
210
218
|
if (platform() === "win32") {
|
|
211
219
|
console.error(chalk.red("\n💡 Windows troubleshooting tips:"));
|
|
@@ -234,8 +242,9 @@ export async function isCodexAvailable(): Promise<boolean> {
|
|
|
234
242
|
try {
|
|
235
243
|
await execa("bunx", [CODEX_CLI_PACKAGE, "--help"]);
|
|
236
244
|
return true;
|
|
237
|
-
} catch (error:
|
|
238
|
-
|
|
245
|
+
} catch (error: unknown) {
|
|
246
|
+
const err = error as NodeJS.ErrnoException;
|
|
247
|
+
if (err.code === "ENOENT") {
|
|
239
248
|
console.error(chalk.yellow("\n⚠️ bunx command not found"));
|
|
240
249
|
}
|
|
241
250
|
return false;
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { CustomAITool } from "../types/tools.js";
|
|
8
|
+
import {
|
|
9
|
+
CLAUDE_PERMISSION_SKIP_ARGS,
|
|
10
|
+
CODEX_DEFAULT_ARGS,
|
|
11
|
+
} from "../shared/aiToolConstants.js";
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* Claude Code のビルトイン定義
|
|
@@ -19,7 +23,7 @@ export const CLAUDE_CODE_TOOL: CustomAITool = {
|
|
|
19
23
|
continue: ["-c"],
|
|
20
24
|
resume: ["-r"],
|
|
21
25
|
},
|
|
22
|
-
permissionSkipArgs:
|
|
26
|
+
permissionSkipArgs: Array.from(CLAUDE_PERMISSION_SKIP_ARGS),
|
|
23
27
|
};
|
|
24
28
|
|
|
25
29
|
/**
|
|
@@ -30,7 +34,7 @@ export const CODEX_CLI_TOOL: CustomAITool = {
|
|
|
30
34
|
displayName: "Codex",
|
|
31
35
|
type: "bunx",
|
|
32
36
|
command: "@openai/codex@latest",
|
|
33
|
-
defaultArgs:
|
|
37
|
+
defaultArgs: Array.from(CODEX_DEFAULT_ARGS),
|
|
34
38
|
modeArgs: {
|
|
35
39
|
normal: [],
|
|
36
40
|
continue: ["resume", "--last"],
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared environment variable helpers.
|
|
3
|
+
*
|
|
4
|
+
* 管理画面やサーバー側の設定ロジックから共通で利用する
|
|
5
|
+
* 環境変数スキーマ/バリデーション/マージ処理を集約する。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const ENV_KEY_PATTERN = /^[A-Z0-9_]+$/;
|
|
9
|
+
const MAX_KEY_LENGTH = 100;
|
|
10
|
+
const MAX_VALUE_LENGTH = 500;
|
|
11
|
+
|
|
12
|
+
const DEFAULT_BOOTSTRAP_ENV_KEYS = [
|
|
13
|
+
"ANTHROPIC_API_KEY",
|
|
14
|
+
"ANTHROPIC_API_KEY_PATH",
|
|
15
|
+
"ANTHROPIC_API_BASE",
|
|
16
|
+
"OPENAI_API_KEY",
|
|
17
|
+
"OPENAI_BASE_URL",
|
|
18
|
+
"OPENAI_API_BASE",
|
|
19
|
+
"OPENAI_ORG_ID",
|
|
20
|
+
"OPENAI_PROJECT",
|
|
21
|
+
"OPENAI_API_TYPE",
|
|
22
|
+
"OPENAI_API_VERSION",
|
|
23
|
+
"GITHUB_TOKEN",
|
|
24
|
+
"GH_TOKEN",
|
|
25
|
+
"GITLAB_TOKEN",
|
|
26
|
+
"AZURE_OPENAI_API_KEY",
|
|
27
|
+
"AZURE_OPENAI_ENDPOINT",
|
|
28
|
+
] as const;
|
|
29
|
+
|
|
30
|
+
export type EnvironmentRecord = Record<string, string>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 環境変数キーが有効かどうかを判定。
|
|
34
|
+
*/
|
|
35
|
+
export function isValidEnvKey(key: string): boolean {
|
|
36
|
+
if (!key || typeof key !== "string") {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (key.length === 0 || key.length > MAX_KEY_LENGTH) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return ENV_KEY_PATTERN.test(key);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 許容されるキー長を取得。
|
|
47
|
+
*/
|
|
48
|
+
export function getEnvConstraints(): {
|
|
49
|
+
maxKeyLength: number;
|
|
50
|
+
maxValueLength: number;
|
|
51
|
+
} {
|
|
52
|
+
return {
|
|
53
|
+
maxKeyLength: MAX_KEY_LENGTH,
|
|
54
|
+
maxValueLength: MAX_VALUE_LENGTH,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 任意のレコードをサニタイズして string -> string のみに整形する。
|
|
60
|
+
*/
|
|
61
|
+
export function sanitizeEnvRecord(
|
|
62
|
+
record?: Record<string, unknown>,
|
|
63
|
+
): EnvironmentRecord {
|
|
64
|
+
if (!record || typeof record !== "object") {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
const result: EnvironmentRecord = {};
|
|
68
|
+
for (const [key, value] of Object.entries(record)) {
|
|
69
|
+
if (value === undefined || value === null) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
result[key] = String(value);
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* envレコードを検証し、フォーマットが不正な場合は例外を投げる。
|
|
79
|
+
*/
|
|
80
|
+
export function validateEnvRecord(record: EnvironmentRecord): void {
|
|
81
|
+
for (const [key, value] of Object.entries(record)) {
|
|
82
|
+
if (!isValidEnvKey(key)) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Invalid environment variable key: "${key}". Use 1-${MAX_KEY_LENGTH} characters (A-Z, 0-9, underscore).`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (value.length === 0) {
|
|
88
|
+
throw new Error(`Environment variable "${key}" must not be empty.`);
|
|
89
|
+
}
|
|
90
|
+
if (value.length > MAX_VALUE_LENGTH) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Environment variable "${key}" exceeds the maximum length (${MAX_VALUE_LENGTH}).`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 起動時にOSから読み取って取り込みたい環境変数キー一覧を返す。
|
|
100
|
+
* CLAUDE_WORKTREE_BOOTSTRAP_ENV_KEYS=KEY1,KEY2 で上書き可能。
|
|
101
|
+
*/
|
|
102
|
+
export function getBootstrapEnvKeys(): string[] {
|
|
103
|
+
const override = process.env.CLAUDE_WORKTREE_BOOTSTRAP_ENV_KEYS;
|
|
104
|
+
if (!override) {
|
|
105
|
+
return [...DEFAULT_BOOTSTRAP_ENV_KEYS];
|
|
106
|
+
}
|
|
107
|
+
return override
|
|
108
|
+
.split(",")
|
|
109
|
+
.map((entry) => entry.trim())
|
|
110
|
+
.filter(Boolean)
|
|
111
|
+
.map((entry) => entry.toUpperCase());
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 現在のenvレコードへ、指定されたキーのみOS環境変数から補完する。
|
|
116
|
+
*/
|
|
117
|
+
export function mergeWithBootstrapEnv(
|
|
118
|
+
current: EnvironmentRecord,
|
|
119
|
+
sourceEnv: NodeJS.ProcessEnv,
|
|
120
|
+
keys: string[] = getBootstrapEnvKeys(),
|
|
121
|
+
): { merged: EnvironmentRecord; addedKeys: string[] } {
|
|
122
|
+
const merged: EnvironmentRecord = { ...current };
|
|
123
|
+
const addedKeys: string[] = [];
|
|
124
|
+
|
|
125
|
+
for (const key of keys) {
|
|
126
|
+
if (merged[key] !== undefined) {
|
|
127
|
+
continue; // 既に設定済み
|
|
128
|
+
}
|
|
129
|
+
const value = sourceEnv[key];
|
|
130
|
+
if (typeof value === "string" && value.length > 0) {
|
|
131
|
+
merged[key] = value;
|
|
132
|
+
addedKeys.push(key);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { merged, addedKeys };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export { ENV_KEY_PATTERN };
|
package/src/gemini.ts
CHANGED
|
@@ -50,6 +50,9 @@ export async function launchGeminiCLI(
|
|
|
50
50
|
options.sessionId && options.sessionId.trim().length > 0
|
|
51
51
|
? options.sessionId.trim()
|
|
52
52
|
: null;
|
|
53
|
+
const usedExplicitSessionId =
|
|
54
|
+
Boolean(resumeSessionId) &&
|
|
55
|
+
(options.mode === "continue" || options.mode === "resume");
|
|
53
56
|
|
|
54
57
|
const buildArgs = (useResumeId: boolean) => {
|
|
55
58
|
const a: string[] = [];
|
|
@@ -59,12 +62,6 @@ export async function launchGeminiCLI(
|
|
|
59
62
|
|
|
60
63
|
switch (options.mode) {
|
|
61
64
|
case "continue":
|
|
62
|
-
if (useResumeId && resumeSessionId) {
|
|
63
|
-
a.push("--resume", resumeSessionId);
|
|
64
|
-
} else {
|
|
65
|
-
a.push("--resume");
|
|
66
|
-
}
|
|
67
|
-
break;
|
|
68
65
|
case "resume":
|
|
69
66
|
if (useResumeId && resumeSessionId) {
|
|
70
67
|
a.push("--resume", resumeSessionId);
|
|
@@ -123,10 +120,14 @@ export async function launchGeminiCLI(
|
|
|
123
120
|
}
|
|
124
121
|
terminal.exitRawMode();
|
|
125
122
|
|
|
126
|
-
const baseEnv =
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
const baseEnv = Object.fromEntries(
|
|
124
|
+
Object.entries({
|
|
125
|
+
...process.env,
|
|
126
|
+
...(options.envOverrides ?? {}),
|
|
127
|
+
}).filter(
|
|
128
|
+
(entry): entry is [string, string] => typeof entry[1] === "string",
|
|
129
|
+
),
|
|
130
|
+
);
|
|
130
131
|
|
|
131
132
|
const childStdio = createChildStdio();
|
|
132
133
|
|
|
@@ -156,7 +157,9 @@ export async function launchGeminiCLI(
|
|
|
156
157
|
}
|
|
157
158
|
};
|
|
158
159
|
|
|
159
|
-
const runGemini = async (
|
|
160
|
+
const runGemini = async (
|
|
161
|
+
runArgs: string[],
|
|
162
|
+
): Promise<string | undefined> => {
|
|
160
163
|
// Capture stdout while passing through to terminal
|
|
161
164
|
// Store chunks to extract session ID after process exits
|
|
162
165
|
const outputChunks: string[] = [];
|
|
@@ -169,7 +172,7 @@ export async function launchGeminiCLI(
|
|
|
169
172
|
stdout: "pipe",
|
|
170
173
|
stderr: childStdio.stderr,
|
|
171
174
|
env: baseEnv,
|
|
172
|
-
}
|
|
175
|
+
});
|
|
173
176
|
|
|
174
177
|
// Pass stdout through to terminal while capturing
|
|
175
178
|
child.stdout?.on("data", (chunk: Buffer) => {
|
|
@@ -203,6 +206,7 @@ export async function launchGeminiCLI(
|
|
|
203
206
|
};
|
|
204
207
|
|
|
205
208
|
let output: string | undefined;
|
|
209
|
+
let fellBackToLatest = false;
|
|
206
210
|
try {
|
|
207
211
|
// Try with explicit session ID first (if any), then fallback to --resume (latest) once
|
|
208
212
|
try {
|
|
@@ -212,6 +216,7 @@ export async function launchGeminiCLI(
|
|
|
212
216
|
(options.mode === "resume" || options.mode === "continue") &&
|
|
213
217
|
resumeSessionId;
|
|
214
218
|
if (shouldRetry) {
|
|
219
|
+
fellBackToLatest = true;
|
|
215
220
|
console.log(
|
|
216
221
|
chalk.yellow(
|
|
217
222
|
` ⚠️ Failed to resume session ${resumeSessionId}. Retrying with latest session...`,
|
|
@@ -229,17 +234,22 @@ export async function launchGeminiCLI(
|
|
|
229
234
|
// Extract session ID from Gemini's exit summary output
|
|
230
235
|
extractSessionId(output);
|
|
231
236
|
|
|
232
|
-
|
|
237
|
+
const explicitResumeSucceeded = usedExplicitSessionId && !fellBackToLatest;
|
|
238
|
+
|
|
239
|
+
// If we explicitly resumed a specific session (and did not fall back), keep that ID.
|
|
240
|
+
if (explicitResumeSucceeded) {
|
|
241
|
+
capturedSessionId = resumeSessionId;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Fallback to file-based detection if stdout capture failed (and we don't have an explicit-resume ID)
|
|
233
245
|
if (!capturedSessionId) {
|
|
234
246
|
try {
|
|
235
247
|
capturedSessionId =
|
|
236
248
|
(await findLatestGeminiSessionId(worktreePath, {
|
|
237
249
|
cwd: worktreePath,
|
|
238
|
-
})) ??
|
|
239
|
-
resumeSessionId ??
|
|
240
|
-
null;
|
|
250
|
+
})) ?? null;
|
|
241
251
|
} catch {
|
|
242
|
-
capturedSessionId =
|
|
252
|
+
capturedSessionId = null;
|
|
243
253
|
}
|
|
244
254
|
}
|
|
245
255
|
|
|
@@ -257,11 +267,12 @@ export async function launchGeminiCLI(
|
|
|
257
267
|
}
|
|
258
268
|
|
|
259
269
|
return capturedSessionId ? { sessionId: capturedSessionId } : {};
|
|
260
|
-
} catch (error:
|
|
270
|
+
} catch (error: unknown) {
|
|
261
271
|
const hasLocalGemini = await isGeminiCommandAvailable();
|
|
262
272
|
let errorMessage: string;
|
|
273
|
+
const err = error as NodeJS.ErrnoException;
|
|
263
274
|
|
|
264
|
-
if (
|
|
275
|
+
if (err.code === "ENOENT") {
|
|
265
276
|
if (hasLocalGemini) {
|
|
266
277
|
errorMessage =
|
|
267
278
|
"gemini command not found. Please ensure Gemini CLI is properly installed.";
|
|
@@ -270,7 +281,8 @@ export async function launchGeminiCLI(
|
|
|
270
281
|
"bunx command not found. Please ensure Bun is installed so Gemini CLI can run via bunx.";
|
|
271
282
|
}
|
|
272
283
|
} else {
|
|
273
|
-
|
|
284
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
285
|
+
errorMessage = `Failed to launch Gemini CLI: ${details || "Unknown error"}`;
|
|
274
286
|
}
|
|
275
287
|
|
|
276
288
|
if (process.platform === "win32") {
|
|
@@ -331,8 +343,9 @@ export async function isGeminiCLIAvailable(): Promise<boolean> {
|
|
|
331
343
|
try {
|
|
332
344
|
await execa("bunx", [GEMINI_CLI_PACKAGE, "--version"], { shell: true });
|
|
333
345
|
return true;
|
|
334
|
-
} catch (error:
|
|
335
|
-
|
|
346
|
+
} catch (error: unknown) {
|
|
347
|
+
const err = error as NodeJS.ErrnoException;
|
|
348
|
+
if (err.code === "ENOENT") {
|
|
336
349
|
console.error(chalk.yellow("\n⚠️ bunx command not found"));
|
|
337
350
|
console.error(
|
|
338
351
|
chalk.gray(
|
package/src/index.ts
CHANGED
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
getTerminalStreams,
|
|
34
34
|
waitForUserAcknowledgement,
|
|
35
35
|
} from "./utils/terminal.js";
|
|
36
|
+
import { resolveWebUiPort, isPortInUse } from "./utils/webui.js";
|
|
37
|
+
import { createLogger } from "./logging/logger.js";
|
|
36
38
|
import { getToolById, getSharedEnvironment } from "./config/tools.js";
|
|
37
39
|
import { launchCustomAITool } from "./launcher.js";
|
|
38
40
|
import { saveSession, loadSession } from "./config/index.js";
|
|
@@ -55,6 +57,9 @@ const ERROR_PROMPT = chalk.yellow(
|
|
|
55
57
|
"Review the error details, then press Enter to continue.",
|
|
56
58
|
);
|
|
57
59
|
|
|
60
|
+
// Category: cli
|
|
61
|
+
const appLogger = createLogger({ category: "cli" });
|
|
62
|
+
|
|
58
63
|
async function waitForErrorAcknowledgement(): Promise<void> {
|
|
59
64
|
await waitForUserAcknowledgement(ERROR_PROMPT);
|
|
60
65
|
}
|
|
@@ -64,14 +69,17 @@ async function waitForErrorAcknowledgement(): Promise<void> {
|
|
|
64
69
|
*/
|
|
65
70
|
function printError(message: string): void {
|
|
66
71
|
console.error(chalk.red(`❌ ${message}`));
|
|
72
|
+
appLogger.error({ message });
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
function printInfo(message: string): void {
|
|
70
76
|
console.log(chalk.blue(`ℹ️ ${message}`));
|
|
77
|
+
appLogger.info({ message });
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
function printWarning(message: string): void {
|
|
74
81
|
console.warn(chalk.yellow(`⚠️ ${message}`));
|
|
82
|
+
appLogger.warn({ message });
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
type GitStepResult<T> = { ok: true; value: T } | { ok: false };
|
|
@@ -545,12 +553,12 @@ export async function handleAIToolWorkflow(
|
|
|
545
553
|
{ skipHistory: true },
|
|
546
554
|
);
|
|
547
555
|
|
|
548
|
-
// Lookup saved session ID for
|
|
556
|
+
// Lookup saved session ID for Continue (auto attach)
|
|
549
557
|
let resumeSessionId: string | null =
|
|
550
558
|
selectedSessionId && selectedSessionId.length > 0
|
|
551
559
|
? selectedSessionId
|
|
552
560
|
: null;
|
|
553
|
-
if (mode === "continue"
|
|
561
|
+
if (mode === "continue") {
|
|
554
562
|
const existingSession = await loadSession(repoRoot);
|
|
555
563
|
const history = existingSession?.history ?? [];
|
|
556
564
|
|
|
@@ -693,14 +701,15 @@ export async function handleAIToolWorkflow(
|
|
|
693
701
|
|
|
694
702
|
if (!finalSessionId && tool === "claude-code") {
|
|
695
703
|
try {
|
|
696
|
-
finalSessionId =
|
|
704
|
+
finalSessionId =
|
|
705
|
+
(await findLatestClaudeSessionId(worktreePath)) ?? null;
|
|
697
706
|
} catch {
|
|
698
707
|
finalSessionId = null;
|
|
699
708
|
}
|
|
700
709
|
}
|
|
701
710
|
const finishedAt = Date.now();
|
|
702
711
|
|
|
703
|
-
if (tool === "codex-cli") {
|
|
712
|
+
if (!finalSessionId && tool === "codex-cli") {
|
|
704
713
|
try {
|
|
705
714
|
const latest = await findLatestCodexSession({
|
|
706
715
|
since: launchStartedAt - 60_000,
|
|
@@ -715,7 +724,7 @@ export async function handleAIToolWorkflow(
|
|
|
715
724
|
} catch {
|
|
716
725
|
// ignore fallback failure
|
|
717
726
|
}
|
|
718
|
-
} else if (tool === "claude-code") {
|
|
727
|
+
} else if (!finalSessionId && tool === "claude-code") {
|
|
719
728
|
try {
|
|
720
729
|
const latestClaude = await findLatestClaudeSession(worktreePath, {
|
|
721
730
|
since: launchStartedAt - 60_000,
|
|
@@ -729,7 +738,7 @@ export async function handleAIToolWorkflow(
|
|
|
729
738
|
} catch {
|
|
730
739
|
// ignore
|
|
731
740
|
}
|
|
732
|
-
} else if (tool === "gemini-cli") {
|
|
741
|
+
} else if (!finalSessionId && tool === "gemini-cli") {
|
|
733
742
|
try {
|
|
734
743
|
const latestGemini = await findLatestGeminiSession(worktreePath, {
|
|
735
744
|
since: launchStartedAt - 60_000,
|
|
@@ -867,7 +876,52 @@ export async function main(): Promise<void> {
|
|
|
867
876
|
process.exit(1);
|
|
868
877
|
}
|
|
869
878
|
|
|
870
|
-
|
|
879
|
+
// Start Web UI server in background (skip if port is in use)
|
|
880
|
+
const port = resolveWebUiPort();
|
|
881
|
+
const portInUse = await isPortInUse(port);
|
|
882
|
+
let webServerHandlePromise: Promise<{
|
|
883
|
+
close: () => Promise<void>;
|
|
884
|
+
} | null> | null = null;
|
|
885
|
+
if (portInUse) {
|
|
886
|
+
printWarning(`Port ${port} is already in use. Skipping Web UI server.`);
|
|
887
|
+
} else {
|
|
888
|
+
const { startWebServer } = await import("./web/server/index.js");
|
|
889
|
+
webServerHandlePromise = startWebServer({ background: true }).catch(
|
|
890
|
+
(err) => {
|
|
891
|
+
appLogger.warn({ err }, "Web UI server failed to start");
|
|
892
|
+
return null;
|
|
893
|
+
},
|
|
894
|
+
);
|
|
895
|
+
printInfo(`Web UI available at http://localhost:${port}`);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
try {
|
|
899
|
+
await runInteractiveLoop();
|
|
900
|
+
} finally {
|
|
901
|
+
if (webServerHandlePromise) {
|
|
902
|
+
const shutdownTimeoutMs = 2000;
|
|
903
|
+
const handleOrTimeout = await Promise.race([
|
|
904
|
+
webServerHandlePromise,
|
|
905
|
+
new Promise<"timeout">((resolve) => {
|
|
906
|
+
const timer = setTimeout(() => resolve("timeout"), shutdownTimeoutMs);
|
|
907
|
+
timer.unref?.();
|
|
908
|
+
}),
|
|
909
|
+
]);
|
|
910
|
+
|
|
911
|
+
if (handleOrTimeout === "timeout") {
|
|
912
|
+
appLogger.warn(
|
|
913
|
+
{ timeoutMs: shutdownTimeoutMs },
|
|
914
|
+
"Web UI server startup did not finish before shutdown timeout; skipping stop",
|
|
915
|
+
);
|
|
916
|
+
} else if (handleOrTimeout) {
|
|
917
|
+
try {
|
|
918
|
+
await handleOrTimeout.close();
|
|
919
|
+
} catch (err) {
|
|
920
|
+
appLogger.warn({ err }, "Web UI server failed to stop");
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
871
925
|
}
|
|
872
926
|
|
|
873
927
|
// Run the application if this module is executed directly
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import pino, { type LoggerOptions, type Logger } from "pino";
|
|
5
|
+
import { pruneOldLogs } from "./rotation.js";
|
|
6
|
+
|
|
7
|
+
type Category = "cli" | "server" | "worker" | string;
|
|
8
|
+
|
|
9
|
+
export interface LoggerConfig {
|
|
10
|
+
level?: string;
|
|
11
|
+
logDir?: string;
|
|
12
|
+
filename?: string;
|
|
13
|
+
category?: Category;
|
|
14
|
+
base?: Record<string, unknown>;
|
|
15
|
+
keepDays?: number;
|
|
16
|
+
/** For tests or sync writes use pino.destination sync */
|
|
17
|
+
sync?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create a pino logger with unified structure and category field.
|
|
22
|
+
* - Writes to a single file stream (no per-component files)
|
|
23
|
+
* - Adds `category` to each log record
|
|
24
|
+
* - Prunes files older than keepDays at startup
|
|
25
|
+
*/
|
|
26
|
+
export function createLogger(config: LoggerConfig = {}): Logger {
|
|
27
|
+
const level = config.level ?? process.env.LOG_LEVEL ?? "info";
|
|
28
|
+
const cwdBase = path.basename(process.cwd()) || "workspace";
|
|
29
|
+
const defaultLogDir = path.join(os.homedir(), ".gwt", "logs", cwdBase);
|
|
30
|
+
const logDir = config.logDir ?? defaultLogDir;
|
|
31
|
+
const filename = config.filename ?? `${formatDate(new Date())}.jsonl`;
|
|
32
|
+
const category = config.category ?? "default";
|
|
33
|
+
const keepDays = config.keepDays ?? 7;
|
|
34
|
+
|
|
35
|
+
if (!fs.existsSync(logDir)) {
|
|
36
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Startup rotation
|
|
40
|
+
pruneOldLogs(logDir, keepDays);
|
|
41
|
+
|
|
42
|
+
const destination = path.join(logDir, filename);
|
|
43
|
+
|
|
44
|
+
const options: LoggerOptions = {
|
|
45
|
+
level,
|
|
46
|
+
base: {
|
|
47
|
+
category,
|
|
48
|
+
...(config.base ?? {}),
|
|
49
|
+
},
|
|
50
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if (config.sync) {
|
|
54
|
+
const destinationStream = pino.destination({
|
|
55
|
+
dest: destination,
|
|
56
|
+
sync: true,
|
|
57
|
+
});
|
|
58
|
+
return pino(options, destinationStream);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const transport = pino.transport({
|
|
62
|
+
targets: [
|
|
63
|
+
{
|
|
64
|
+
target: "pino/file",
|
|
65
|
+
options: { destination, mkdir: true, append: true },
|
|
66
|
+
level,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return pino(options, transport);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Convenience logger for quick use (category defaults to "default"). */
|
|
75
|
+
export const logger = createLogger();
|
|
76
|
+
|
|
77
|
+
export function formatDate(date: Date): string {
|
|
78
|
+
const year = date.getFullYear();
|
|
79
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
80
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
81
|
+
return `${year}-${month}-${day}`;
|
|
82
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Delete log files older than `keepDays` from the given directory.
|
|
6
|
+
* This is called at startup to enforce 7-day retention without size limits.
|
|
7
|
+
*/
|
|
8
|
+
export function pruneOldLogs(logDir: string, keepDays = 7): void {
|
|
9
|
+
if (!fs.existsSync(logDir)) return;
|
|
10
|
+
|
|
11
|
+
const cutoff = Date.now() - keepDays * 24 * 60 * 60 * 1000;
|
|
12
|
+
|
|
13
|
+
for (const entry of fs.readdirSync(logDir)) {
|
|
14
|
+
const full = path.join(logDir, entry);
|
|
15
|
+
try {
|
|
16
|
+
const stat = fs.statSync(full);
|
|
17
|
+
if (!stat.isFile()) continue;
|
|
18
|
+
if (stat.mtime.getTime() < cutoff) {
|
|
19
|
+
fs.unlinkSync(full);
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
// Ignore individual file errors to avoid breaking startup
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|