@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
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
import { platform } from "os";
|
|
3
|
+
import { getToolById } from "../config/tools.js";
|
|
4
|
+
import {
|
|
5
|
+
CODEX_DEFAULT_ARGS,
|
|
6
|
+
CLAUDE_PERMISSION_SKIP_ARGS,
|
|
7
|
+
} from "../shared/aiToolConstants.js";
|
|
8
|
+
import { prepareCustomToolExecution } from "./customToolResolver.js";
|
|
9
|
+
import type { LaunchOptions } from "../types/tools.js";
|
|
10
|
+
|
|
11
|
+
const DETECTION_COMMAND = platform() === "win32" ? "where" : "which";
|
|
12
|
+
const MIN_BUN_MAJOR = 1;
|
|
13
|
+
|
|
14
|
+
export const CLAUDE_CLI_PACKAGE = "@anthropic-ai/claude-code@latest";
|
|
15
|
+
export const CODEX_CLI_PACKAGE = "@openai/codex@latest";
|
|
16
|
+
|
|
17
|
+
export type ResolverErrorCode =
|
|
18
|
+
| "COMMAND_NOT_FOUND"
|
|
19
|
+
| "BUNX_NOT_FOUND"
|
|
20
|
+
| "BUN_TOO_OLD"
|
|
21
|
+
| "CUSTOM_TOOL_NOT_FOUND";
|
|
22
|
+
|
|
23
|
+
export interface ResolvedCommand {
|
|
24
|
+
command: string;
|
|
25
|
+
args: string[];
|
|
26
|
+
usesFallback: boolean;
|
|
27
|
+
env?: NodeJS.ProcessEnv;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class AIToolResolutionError extends Error {
|
|
31
|
+
constructor(
|
|
32
|
+
public code: ResolverErrorCode,
|
|
33
|
+
message: string,
|
|
34
|
+
public hints?: string[],
|
|
35
|
+
) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "AIToolResolutionError";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function commandExists(command: string): Promise<boolean> {
|
|
42
|
+
try {
|
|
43
|
+
await execa(DETECTION_COMMAND, [command], { shell: true });
|
|
44
|
+
return true;
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let bunxCheckPromise: Promise<void> | null = null;
|
|
51
|
+
|
|
52
|
+
async function ensureBunxAvailable(): Promise<void> {
|
|
53
|
+
if (!bunxCheckPromise) {
|
|
54
|
+
bunxCheckPromise = (async () => {
|
|
55
|
+
const bunxExists = await commandExists("bunx");
|
|
56
|
+
if (!bunxExists) {
|
|
57
|
+
throw new AIToolResolutionError(
|
|
58
|
+
"BUNX_NOT_FOUND",
|
|
59
|
+
"bunx command not found. Install Bun 1.0+ so bunx is available on PATH.",
|
|
60
|
+
[
|
|
61
|
+
"Install Bun: https://bun.sh/docs/installation",
|
|
62
|
+
"After installation, restart your terminal so bunx is on PATH.",
|
|
63
|
+
],
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const { stdout } = await execa("bun", ["--version"]);
|
|
69
|
+
const version = stdout.trim();
|
|
70
|
+
const major = parseInt(version.split(".")[0] ?? "0", 10);
|
|
71
|
+
if (!Number.isFinite(major) || major < MIN_BUN_MAJOR) {
|
|
72
|
+
throw new AIToolResolutionError(
|
|
73
|
+
"BUN_TOO_OLD",
|
|
74
|
+
`Detected Bun ${version}. Bun ${MIN_BUN_MAJOR}.0+ is required for bunx fallback execution.`,
|
|
75
|
+
[
|
|
76
|
+
"Upgrade Bun: curl -fsSL https://bun.sh/install | bash",
|
|
77
|
+
"Verify with 'bun --version' (needs >= 1.0)",
|
|
78
|
+
],
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
} catch (error: unknown) {
|
|
82
|
+
if (error instanceof AIToolResolutionError) {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
const err = error as NodeJS.ErrnoException;
|
|
86
|
+
if (err?.code === "ENOENT") {
|
|
87
|
+
throw new AIToolResolutionError(
|
|
88
|
+
"BUNX_NOT_FOUND",
|
|
89
|
+
"bun command not found while verifying bunx. Install Bun 1.0+ and ensure it is on PATH.",
|
|
90
|
+
[
|
|
91
|
+
"Install Bun: https://bun.sh/docs/installation",
|
|
92
|
+
"After installation, run 'bun --version' to confirm.",
|
|
93
|
+
],
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
throw new AIToolResolutionError(
|
|
97
|
+
"BUN_TOO_OLD",
|
|
98
|
+
`Failed to verify Bun version: ${err?.message ?? "unknown error"}`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
})();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
await bunxCheckPromise;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
bunxCheckPromise = null;
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ClaudeCommandOptions {
|
|
113
|
+
mode?: "normal" | "continue" | "resume";
|
|
114
|
+
skipPermissions?: boolean;
|
|
115
|
+
extraArgs?: string[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function buildClaudeArgs(options: ClaudeCommandOptions = {}): string[] {
|
|
119
|
+
const args: string[] = [];
|
|
120
|
+
|
|
121
|
+
switch (options.mode) {
|
|
122
|
+
case "continue":
|
|
123
|
+
args.push("-c");
|
|
124
|
+
break;
|
|
125
|
+
case "resume":
|
|
126
|
+
args.push("-r");
|
|
127
|
+
break;
|
|
128
|
+
default:
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (options.skipPermissions) {
|
|
133
|
+
args.push(...CLAUDE_PERMISSION_SKIP_ARGS);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (options.extraArgs?.length) {
|
|
137
|
+
args.push(...options.extraArgs);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return args;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function resolveClaudeCommand(
|
|
144
|
+
options: ClaudeCommandOptions = {},
|
|
145
|
+
): Promise<ResolvedCommand> {
|
|
146
|
+
const args = buildClaudeArgs(options);
|
|
147
|
+
|
|
148
|
+
if (await commandExists("claude")) {
|
|
149
|
+
return {
|
|
150
|
+
command: "claude",
|
|
151
|
+
args,
|
|
152
|
+
usesFallback: false,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await ensureBunxAvailable();
|
|
157
|
+
return {
|
|
158
|
+
command: "bunx",
|
|
159
|
+
args: [CLAUDE_CLI_PACKAGE, ...args],
|
|
160
|
+
usesFallback: true,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface CodexCommandOptions {
|
|
165
|
+
mode?: "normal" | "continue" | "resume";
|
|
166
|
+
bypassApprovals?: boolean;
|
|
167
|
+
extraArgs?: string[];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function buildCodexArgs(options: CodexCommandOptions = {}): string[] {
|
|
171
|
+
const args: string[] = [];
|
|
172
|
+
|
|
173
|
+
switch (options.mode) {
|
|
174
|
+
case "continue":
|
|
175
|
+
args.push("resume", "--last");
|
|
176
|
+
break;
|
|
177
|
+
case "resume":
|
|
178
|
+
args.push("resume");
|
|
179
|
+
break;
|
|
180
|
+
default:
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (options.bypassApprovals) {
|
|
185
|
+
args.push("--yolo");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (options.extraArgs?.length) {
|
|
189
|
+
args.push(...options.extraArgs);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
args.push(...CODEX_DEFAULT_ARGS);
|
|
193
|
+
return args;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export async function resolveCodexCommand(
|
|
197
|
+
options: CodexCommandOptions = {},
|
|
198
|
+
): Promise<ResolvedCommand> {
|
|
199
|
+
const args = buildCodexArgs(options);
|
|
200
|
+
|
|
201
|
+
if (await commandExists("codex")) {
|
|
202
|
+
return {
|
|
203
|
+
command: "codex",
|
|
204
|
+
args,
|
|
205
|
+
usesFallback: false,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
await ensureBunxAvailable();
|
|
210
|
+
return {
|
|
211
|
+
command: "bunx",
|
|
212
|
+
args: [CODEX_CLI_PACKAGE, ...args],
|
|
213
|
+
usesFallback: true,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface CustomToolCommandOptions extends LaunchOptions {
|
|
218
|
+
toolId: string;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export async function resolveCustomToolCommand(
|
|
222
|
+
options: CustomToolCommandOptions,
|
|
223
|
+
): Promise<ResolvedCommand> {
|
|
224
|
+
const tool = await getToolById(options.toolId);
|
|
225
|
+
if (!tool) {
|
|
226
|
+
throw new AIToolResolutionError(
|
|
227
|
+
"CUSTOM_TOOL_NOT_FOUND",
|
|
228
|
+
`Custom tool not found: ${options.toolId}`,
|
|
229
|
+
[
|
|
230
|
+
"Update ~/.gwt/tools.json to include this ID",
|
|
231
|
+
"Reload the Web UI after editing the tools list",
|
|
232
|
+
],
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const execution = await prepareCustomToolExecution(tool, options);
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
command: execution.command,
|
|
240
|
+
args: execution.args,
|
|
241
|
+
usesFallback: tool.type === "bunx",
|
|
242
|
+
...(execution.env ? { env: execution.env } : {}),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export async function isClaudeCodeAvailable(): Promise<boolean> {
|
|
247
|
+
try {
|
|
248
|
+
await resolveClaudeCommand();
|
|
249
|
+
return true;
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (error instanceof AIToolResolutionError) {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export async function isCodexAvailable(): Promise<boolean> {
|
|
259
|
+
try {
|
|
260
|
+
await resolveCodexCommand();
|
|
261
|
+
return true;
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (error instanceof AIToolResolutionError) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Test-helper: resets cached bunx availability check.
|
|
272
|
+
* Not exported in type definitions to avoid production usage.
|
|
273
|
+
*/
|
|
274
|
+
export function __resetBunxCacheForTests(): void {
|
|
275
|
+
bunxCheckPromise = null;
|
|
276
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
import type { CustomAITool, LaunchOptions } from "../types/tools.js";
|
|
3
|
+
|
|
4
|
+
export interface CustomToolExecutionPlan {
|
|
5
|
+
command: string;
|
|
6
|
+
args: string[];
|
|
7
|
+
env?: NodeJS.ProcessEnv;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const WHICH_COMMAND = process.platform === "win32" ? "where" : "which";
|
|
11
|
+
|
|
12
|
+
export async function resolveCommandPath(commandName: string): Promise<string> {
|
|
13
|
+
try {
|
|
14
|
+
const { stdout } = await execa(WHICH_COMMAND, [commandName]);
|
|
15
|
+
const resolvedPath = (stdout.split("\n")[0] ?? "").trim();
|
|
16
|
+
|
|
17
|
+
if (!resolvedPath) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`Command "${commandName}" not found in PATH.\n` +
|
|
20
|
+
"Please ensure it is installed and available in your PATH.",
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return resolvedPath;
|
|
25
|
+
} catch (error) {
|
|
26
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Failed to resolve command "${commandName}".\n${reason}\n` +
|
|
29
|
+
"Please ensure the command is installed and available in your PATH.",
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function buildCustomToolArgs(
|
|
35
|
+
tool: CustomAITool,
|
|
36
|
+
options: LaunchOptions = {},
|
|
37
|
+
): string[] {
|
|
38
|
+
const args: string[] = [];
|
|
39
|
+
|
|
40
|
+
if (tool.defaultArgs?.length) {
|
|
41
|
+
args.push(...tool.defaultArgs);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const mode = options.mode ?? "normal";
|
|
45
|
+
const modeArgs = tool.modeArgs?.[mode];
|
|
46
|
+
if (modeArgs?.length) {
|
|
47
|
+
args.push(...modeArgs);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (options.skipPermissions && tool.permissionSkipArgs?.length) {
|
|
51
|
+
args.push(...tool.permissionSkipArgs);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (options.extraArgs?.length) {
|
|
55
|
+
args.push(...options.extraArgs);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return args;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function prepareCustomToolExecution(
|
|
62
|
+
tool: CustomAITool,
|
|
63
|
+
options: LaunchOptions = {},
|
|
64
|
+
): Promise<CustomToolExecutionPlan> {
|
|
65
|
+
const args = buildCustomToolArgs(tool, options);
|
|
66
|
+
const envOverrides: NodeJS.ProcessEnv | undefined = tool.env
|
|
67
|
+
? ({ ...tool.env } as NodeJS.ProcessEnv)
|
|
68
|
+
: undefined;
|
|
69
|
+
|
|
70
|
+
switch (tool.type) {
|
|
71
|
+
case "path": {
|
|
72
|
+
return {
|
|
73
|
+
command: tool.command,
|
|
74
|
+
args,
|
|
75
|
+
...(envOverrides ? { env: envOverrides } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
case "bunx": {
|
|
79
|
+
return {
|
|
80
|
+
command: "bunx",
|
|
81
|
+
args: [tool.command, ...args],
|
|
82
|
+
...(envOverrides ? { env: envOverrides } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
case "command": {
|
|
86
|
+
const resolved = await resolveCommandPath(tool.command);
|
|
87
|
+
return {
|
|
88
|
+
command: resolved,
|
|
89
|
+
args,
|
|
90
|
+
...(envOverrides ? { env: envOverrides } : {}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
default: {
|
|
94
|
+
const exhaustive: never = tool.type;
|
|
95
|
+
throw new Error(`Unknown custom tool type: ${exhaustive as string}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants for AI tool integrations.
|
|
3
|
+
*
|
|
4
|
+
* These values are consumed by both the CLI (Node) runtime and the Web UI so
|
|
5
|
+
* that command previews, permission flags, and default arguments stay in sync.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const CLAUDE_PERMISSION_SKIP_ARGS = [
|
|
9
|
+
"--dangerously-skip-permissions",
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
export const CODEX_DEFAULT_ARGS = [
|
|
13
|
+
"--enable",
|
|
14
|
+
"web_search_request",
|
|
15
|
+
"--model=gpt-5-codex",
|
|
16
|
+
"--sandbox",
|
|
17
|
+
"workspace-write",
|
|
18
|
+
"-c",
|
|
19
|
+
"model_reasoning_effort=high",
|
|
20
|
+
"-c",
|
|
21
|
+
"model_reasoning_summaries=detailed",
|
|
22
|
+
"-c",
|
|
23
|
+
"sandbox_workspace_write.network_access=true",
|
|
24
|
+
"-c",
|
|
25
|
+
"shell_environment_policy.inherit=all",
|
|
26
|
+
"-c",
|
|
27
|
+
"shell_environment_policy.ignore_default_excludes=true",
|
|
28
|
+
"-c",
|
|
29
|
+
"shell_environment_policy.experimental_use_profile=true",
|
|
30
|
+
] as const;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
declare module "trayicon" {
|
|
2
|
+
export interface TrayIconMenuItem {
|
|
3
|
+
text: string;
|
|
4
|
+
action?: () => void | Promise<void>;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
checked?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface TrayIconOptions {
|
|
10
|
+
icon: Buffer;
|
|
11
|
+
title?: string;
|
|
12
|
+
tooltip?: string;
|
|
13
|
+
action?: () => void | Promise<void>;
|
|
14
|
+
menu?: TrayIconMenuItem[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TrayIconInstance {
|
|
18
|
+
setIcon?: (icon: Buffer) => void;
|
|
19
|
+
setTitle?: (title: string) => void;
|
|
20
|
+
dispose?: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function create(options: TrayIconOptions): TrayIconInstance;
|
|
24
|
+
|
|
25
|
+
const trayicon: {
|
|
26
|
+
create: typeof create;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default trayicon;
|
|
30
|
+
}
|
package/src/types/tools.ts
CHANGED
|
@@ -69,6 +69,11 @@ export interface CustomAITool {
|
|
|
69
69
|
*/
|
|
70
70
|
icon?: string;
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* 説明文(オプション)
|
|
74
|
+
*/
|
|
75
|
+
description?: string;
|
|
76
|
+
|
|
72
77
|
/**
|
|
73
78
|
* 実行方式
|
|
74
79
|
*
|
|
@@ -118,6 +123,16 @@ export interface CustomAITool {
|
|
|
118
123
|
* APIキーや設定ファイルパスなどを指定。
|
|
119
124
|
*/
|
|
120
125
|
env?: Record<string, string>;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 作成日時(ISO8601)。tools.jsonのメタデータとして使用。
|
|
129
|
+
*/
|
|
130
|
+
createdAt?: string;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 更新日時(ISO8601)。tools.jsonのメタデータとして使用。
|
|
134
|
+
*/
|
|
135
|
+
updatedAt?: string;
|
|
121
136
|
}
|
|
122
137
|
|
|
123
138
|
/**
|
package/src/utils/prompt.ts
CHANGED
|
@@ -22,7 +22,9 @@ export async function waitForEnter(promptMessage: string): Promise<void> {
|
|
|
22
22
|
|
|
23
23
|
if ((stdin as NodeJS.ReadStream & { isRaw?: boolean }).isRaw) {
|
|
24
24
|
try {
|
|
25
|
-
(
|
|
25
|
+
(
|
|
26
|
+
stdin as NodeJS.ReadStream & { setRawMode?: (flag: boolean) => void }
|
|
27
|
+
).setRawMode?.(false);
|
|
26
28
|
} catch {
|
|
27
29
|
// Ignore raw mode errors
|
|
28
30
|
}
|
|
@@ -35,17 +37,21 @@ export async function waitForEnter(promptMessage: string): Promise<void> {
|
|
|
35
37
|
rl.removeAllListeners();
|
|
36
38
|
rl.close();
|
|
37
39
|
const remover = (method: "off" | "removeListener") =>
|
|
38
|
-
(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
(
|
|
41
|
+
stdin as unknown as Record<
|
|
42
|
+
string,
|
|
43
|
+
(event: string, fn: () => void) => void
|
|
44
|
+
>
|
|
45
|
+
)[method]?.("end", onEnd);
|
|
42
46
|
remover("off");
|
|
43
47
|
remover("removeListener");
|
|
44
48
|
const removerErr = (method: "off" | "removeListener") =>
|
|
45
|
-
(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
(
|
|
50
|
+
stdin as unknown as Record<
|
|
51
|
+
string,
|
|
52
|
+
(event: string, fn: () => void) => void
|
|
53
|
+
>
|
|
54
|
+
)[method]?.("error", onEnd);
|
|
49
55
|
removerErr("off");
|
|
50
56
|
removerErr("removeListener");
|
|
51
57
|
if (typeof stdin.pause === "function") {
|
package/src/utils/session.ts
CHANGED
|
@@ -11,7 +11,9 @@ const UUID_REGEX =
|
|
|
11
11
|
* @returns true if the string is a valid UUID format
|
|
12
12
|
*/
|
|
13
13
|
export function isValidUuidSessionId(id: string): boolean {
|
|
14
|
-
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
14
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
15
|
+
id,
|
|
16
|
+
);
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
function pickSessionIdFromObject(obj: unknown): string | null {
|
|
@@ -34,7 +36,13 @@ function pickSessionIdFromObject(obj: unknown): string | null {
|
|
|
34
36
|
function pickCwdFromObject(obj: unknown): string | null {
|
|
35
37
|
if (!obj || typeof obj !== "object") return null;
|
|
36
38
|
const candidate = obj as Record<string, unknown>;
|
|
37
|
-
const keys = [
|
|
39
|
+
const keys = [
|
|
40
|
+
"cwd",
|
|
41
|
+
"workingDirectory",
|
|
42
|
+
"workdir",
|
|
43
|
+
"directory",
|
|
44
|
+
"projectPath",
|
|
45
|
+
];
|
|
38
46
|
for (const key of keys) {
|
|
39
47
|
const value = candidate[key];
|
|
40
48
|
if (typeof value === "string" && value.trim().length > 0) {
|
|
@@ -118,7 +126,12 @@ async function findLatestFile(
|
|
|
118
126
|
async function findNewestSessionIdFromDir(
|
|
119
127
|
dir: string,
|
|
120
128
|
recursive: boolean,
|
|
121
|
-
options: {
|
|
129
|
+
options: {
|
|
130
|
+
since?: number;
|
|
131
|
+
until?: number;
|
|
132
|
+
preferClosestTo?: number;
|
|
133
|
+
windowMs?: number;
|
|
134
|
+
} = {},
|
|
122
135
|
): Promise<{ id: string; mtime: number } | null> {
|
|
123
136
|
try {
|
|
124
137
|
const files: { fullPath: string; mtime: number }[] = [];
|
|
@@ -423,7 +436,12 @@ function generateClaudeProjectPathCandidates(cwd: string): string[] {
|
|
|
423
436
|
|
|
424
437
|
export async function findLatestClaudeSessionId(
|
|
425
438
|
cwd: string,
|
|
426
|
-
options: {
|
|
439
|
+
options: {
|
|
440
|
+
since?: number;
|
|
441
|
+
until?: number;
|
|
442
|
+
preferClosestTo?: number;
|
|
443
|
+
windowMs?: number;
|
|
444
|
+
} = {},
|
|
427
445
|
): Promise<string | null> {
|
|
428
446
|
const found = await findLatestClaudeSession(cwd, options);
|
|
429
447
|
return found?.id ?? null;
|
|
@@ -436,7 +454,12 @@ export interface ClaudeSessionInfo {
|
|
|
436
454
|
|
|
437
455
|
export async function findLatestClaudeSession(
|
|
438
456
|
cwd: string,
|
|
439
|
-
options: {
|
|
457
|
+
options: {
|
|
458
|
+
since?: number;
|
|
459
|
+
until?: number;
|
|
460
|
+
preferClosestTo?: number;
|
|
461
|
+
windowMs?: number;
|
|
462
|
+
} = {},
|
|
440
463
|
): Promise<ClaudeSessionInfo | null> {
|
|
441
464
|
const rootCandidates: string[] = [];
|
|
442
465
|
if (process.env.CLAUDE_CONFIG_DIR) {
|
|
@@ -481,9 +504,15 @@ export async function findLatestClaudeSession(
|
|
|
481
504
|
try {
|
|
482
505
|
const line = lines[i] ?? "";
|
|
483
506
|
const parsed = JSON.parse(line) as Record<string, unknown>;
|
|
484
|
-
const project =
|
|
485
|
-
|
|
486
|
-
|
|
507
|
+
const project =
|
|
508
|
+
typeof parsed.project === "string" ? parsed.project : null;
|
|
509
|
+
const sessionId =
|
|
510
|
+
typeof parsed.sessionId === "string" ? parsed.sessionId : null;
|
|
511
|
+
if (
|
|
512
|
+
project &&
|
|
513
|
+
sessionId &&
|
|
514
|
+
(project === cwd || cwd.startsWith(project))
|
|
515
|
+
) {
|
|
487
516
|
return { id: sessionId, mtime: Date.now() };
|
|
488
517
|
}
|
|
489
518
|
} catch {
|
|
@@ -513,10 +542,16 @@ export async function waitForClaudeSessionId(
|
|
|
513
542
|
const deadline = Date.now() + timeoutMs;
|
|
514
543
|
|
|
515
544
|
while (Date.now() < deadline) {
|
|
516
|
-
const opt: {
|
|
545
|
+
const opt: {
|
|
546
|
+
since?: number;
|
|
547
|
+
until?: number;
|
|
548
|
+
preferClosestTo?: number;
|
|
549
|
+
windowMs?: number;
|
|
550
|
+
} = {};
|
|
517
551
|
if (options.since !== undefined) opt.since = options.since;
|
|
518
552
|
if (options.until !== undefined) opt.until = options.until;
|
|
519
|
-
if (options.preferClosestTo !== undefined)
|
|
553
|
+
if (options.preferClosestTo !== undefined)
|
|
554
|
+
opt.preferClosestTo = options.preferClosestTo;
|
|
520
555
|
if (options.windowMs !== undefined) opt.windowMs = options.windowMs;
|
|
521
556
|
|
|
522
557
|
const found = await findLatestClaudeSession(cwd, opt);
|
|
@@ -560,7 +595,13 @@ async function findLatestNestedSessionFile(
|
|
|
560
595
|
|
|
561
596
|
export async function findLatestGeminiSession(
|
|
562
597
|
_cwd: string,
|
|
563
|
-
options: {
|
|
598
|
+
options: {
|
|
599
|
+
since?: number;
|
|
600
|
+
until?: number;
|
|
601
|
+
preferClosestTo?: number;
|
|
602
|
+
windowMs?: number;
|
|
603
|
+
cwd?: string | null;
|
|
604
|
+
} = {},
|
|
564
605
|
): Promise<GeminiSessionInfo | null> {
|
|
565
606
|
// Gemini stores sessions/logs under ~/.gemini/tmp/<project_hash>/(chats|logs).json
|
|
566
607
|
const baseDir = path.join(homedir(), ".gemini", "tmp");
|
|
@@ -588,17 +629,15 @@ export async function findLatestGeminiSession(
|
|
|
588
629
|
|
|
589
630
|
const ref = options.preferClosestTo;
|
|
590
631
|
const window = options.windowMs ?? 30 * 60 * 1000;
|
|
591
|
-
pool = pool
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
return b.mtime - a.mtime;
|
|
601
|
-
});
|
|
632
|
+
pool = pool.slice().sort((a, b) => {
|
|
633
|
+
if (typeof ref === "number") {
|
|
634
|
+
const da = Math.abs(a.mtime - ref);
|
|
635
|
+
const db = Math.abs(b.mtime - ref);
|
|
636
|
+
if (da === db) return b.mtime - a.mtime;
|
|
637
|
+
if (da <= window || db <= window) return da - db;
|
|
638
|
+
}
|
|
639
|
+
return b.mtime - a.mtime;
|
|
640
|
+
});
|
|
602
641
|
|
|
603
642
|
for (const file of pool) {
|
|
604
643
|
const info = await readSessionInfoFromFile(file.fullPath);
|
|
@@ -620,16 +659,31 @@ export async function findLatestGeminiSession(
|
|
|
620
659
|
|
|
621
660
|
export async function findLatestGeminiSessionId(
|
|
622
661
|
cwd: string,
|
|
623
|
-
options: {
|
|
662
|
+
options: {
|
|
663
|
+
since?: number;
|
|
664
|
+
until?: number;
|
|
665
|
+
preferClosestTo?: number;
|
|
666
|
+
windowMs?: number;
|
|
667
|
+
cwd?: string | null;
|
|
668
|
+
} = {},
|
|
624
669
|
): Promise<string | null> {
|
|
625
|
-
const normalized: {
|
|
670
|
+
const normalized: {
|
|
671
|
+
since?: number;
|
|
672
|
+
until?: number;
|
|
673
|
+
preferClosestTo?: number;
|
|
674
|
+
windowMs?: number;
|
|
675
|
+
} = {};
|
|
626
676
|
if (options.since !== undefined) normalized.since = options.since as number;
|
|
627
677
|
if (options.until !== undefined) normalized.until = options.until as number;
|
|
628
678
|
if (options.preferClosestTo !== undefined)
|
|
629
679
|
normalized.preferClosestTo = options.preferClosestTo as number;
|
|
630
|
-
if (options.windowMs !== undefined)
|
|
680
|
+
if (options.windowMs !== undefined)
|
|
681
|
+
normalized.windowMs = options.windowMs as number;
|
|
631
682
|
|
|
632
|
-
const found = await findLatestGeminiSession(cwd, {
|
|
683
|
+
const found = await findLatestGeminiSession(cwd, {
|
|
684
|
+
...normalized,
|
|
685
|
+
cwd: options.cwd ?? cwd,
|
|
686
|
+
});
|
|
633
687
|
return found?.id ?? null;
|
|
634
688
|
}
|
|
635
689
|
|