@shareai-lab/kode 1.1.14 → 1.1.16-dev.1
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/cli.js +77 -82
- package/dist/entrypoints/cli.js +59 -38
- package/dist/entrypoints/cli.js.map +3 -3
- package/dist/index.js +5 -26
- package/dist/package.json +4 -1
- package/package.json +11 -104
- package/dist/test/testAdapters.js +0 -88
- package/dist/test/testAdapters.js.map +0 -1
- package/src/ProjectOnboarding.tsx +0 -198
- package/src/Tool.ts +0 -83
- package/src/commands/agents.tsx +0 -3416
- package/src/commands/approvedTools.ts +0 -53
- package/src/commands/bug.tsx +0 -20
- package/src/commands/clear.ts +0 -43
- package/src/commands/compact.ts +0 -120
- package/src/commands/config.tsx +0 -19
- package/src/commands/cost.ts +0 -18
- package/src/commands/ctx_viz.ts +0 -209
- package/src/commands/doctor.ts +0 -24
- package/src/commands/help.tsx +0 -19
- package/src/commands/init.ts +0 -37
- package/src/commands/listen.ts +0 -42
- package/src/commands/login.tsx +0 -51
- package/src/commands/logout.tsx +0 -40
- package/src/commands/mcp.ts +0 -41
- package/src/commands/model.tsx +0 -40
- package/src/commands/modelstatus.tsx +0 -20
- package/src/commands/onboarding.tsx +0 -34
- package/src/commands/pr_comments.ts +0 -59
- package/src/commands/refreshCommands.ts +0 -54
- package/src/commands/release-notes.ts +0 -34
- package/src/commands/resume.tsx +0 -31
- package/src/commands/review.ts +0 -49
- package/src/commands/terminalSetup.ts +0 -221
- package/src/commands.ts +0 -139
- package/src/components/ApproveApiKey.tsx +0 -93
- package/src/components/AsciiLogo.tsx +0 -13
- package/src/components/AutoUpdater.tsx +0 -148
- package/src/components/Bug.tsx +0 -367
- package/src/components/Config.tsx +0 -293
- package/src/components/ConsoleOAuthFlow.tsx +0 -327
- package/src/components/Cost.tsx +0 -23
- package/src/components/CostThresholdDialog.tsx +0 -46
- package/src/components/CustomSelect/option-map.ts +0 -42
- package/src/components/CustomSelect/select-option.tsx +0 -78
- package/src/components/CustomSelect/select.tsx +0 -152
- package/src/components/CustomSelect/theme.ts +0 -45
- package/src/components/CustomSelect/use-select-state.ts +0 -414
- package/src/components/CustomSelect/use-select.ts +0 -35
- package/src/components/FallbackToolUseRejectedMessage.tsx +0 -15
- package/src/components/FileEditToolUpdatedMessage.tsx +0 -66
- package/src/components/Help.tsx +0 -215
- package/src/components/HighlightedCode.tsx +0 -33
- package/src/components/InvalidConfigDialog.tsx +0 -113
- package/src/components/Link.tsx +0 -32
- package/src/components/LogSelector.tsx +0 -86
- package/src/components/Logo.tsx +0 -170
- package/src/components/MCPServerApprovalDialog.tsx +0 -100
- package/src/components/MCPServerDialogCopy.tsx +0 -25
- package/src/components/MCPServerMultiselectDialog.tsx +0 -109
- package/src/components/Message.tsx +0 -221
- package/src/components/MessageResponse.tsx +0 -15
- package/src/components/MessageSelector.tsx +0 -211
- package/src/components/ModeIndicator.tsx +0 -88
- package/src/components/ModelConfig.tsx +0 -301
- package/src/components/ModelListManager.tsx +0 -227
- package/src/components/ModelSelector.tsx +0 -3387
- package/src/components/ModelStatusDisplay.tsx +0 -230
- package/src/components/Onboarding.tsx +0 -274
- package/src/components/PressEnterToContinue.tsx +0 -11
- package/src/components/PromptInput.tsx +0 -760
- package/src/components/SentryErrorBoundary.ts +0 -39
- package/src/components/Spinner.tsx +0 -129
- package/src/components/StickerRequestForm.tsx +0 -16
- package/src/components/StructuredDiff.tsx +0 -191
- package/src/components/TextInput.tsx +0 -259
- package/src/components/TodoItem.tsx +0 -47
- package/src/components/TokenWarning.tsx +0 -31
- package/src/components/ToolUseLoader.tsx +0 -40
- package/src/components/TrustDialog.tsx +0 -106
- package/src/components/binary-feedback/BinaryFeedback.tsx +0 -63
- package/src/components/binary-feedback/BinaryFeedbackOption.tsx +0 -111
- package/src/components/binary-feedback/BinaryFeedbackView.tsx +0 -172
- package/src/components/binary-feedback/utils.ts +0 -220
- package/src/components/messages/AssistantBashOutputMessage.tsx +0 -22
- package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +0 -49
- package/src/components/messages/AssistantRedactedThinkingMessage.tsx +0 -19
- package/src/components/messages/AssistantTextMessage.tsx +0 -144
- package/src/components/messages/AssistantThinkingMessage.tsx +0 -40
- package/src/components/messages/AssistantToolUseMessage.tsx +0 -132
- package/src/components/messages/TaskProgressMessage.tsx +0 -32
- package/src/components/messages/TaskToolMessage.tsx +0 -58
- package/src/components/messages/UserBashInputMessage.tsx +0 -28
- package/src/components/messages/UserCommandMessage.tsx +0 -30
- package/src/components/messages/UserKodingInputMessage.tsx +0 -28
- package/src/components/messages/UserPromptMessage.tsx +0 -35
- package/src/components/messages/UserTextMessage.tsx +0 -39
- package/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +0 -12
- package/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +0 -36
- package/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +0 -31
- package/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +0 -57
- package/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +0 -35
- package/src/components/messages/UserToolResultMessage/utils.tsx +0 -56
- package/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +0 -121
- package/src/components/permissions/FallbackPermissionRequest.tsx +0 -153
- package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +0 -182
- package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +0 -77
- package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +0 -164
- package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +0 -83
- package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +0 -240
- package/src/components/permissions/PermissionRequest.tsx +0 -101
- package/src/components/permissions/PermissionRequestTitle.tsx +0 -69
- package/src/components/permissions/hooks.ts +0 -44
- package/src/components/permissions/toolUseOptions.ts +0 -59
- package/src/components/permissions/utils.ts +0 -23
- package/src/constants/betas.ts +0 -5
- package/src/constants/claude-asterisk-ascii-art.tsx +0 -238
- package/src/constants/figures.ts +0 -4
- package/src/constants/keys.ts +0 -3
- package/src/constants/macros.ts +0 -11
- package/src/constants/modelCapabilities.ts +0 -179
- package/src/constants/models.ts +0 -1025
- package/src/constants/oauth.ts +0 -18
- package/src/constants/product.ts +0 -17
- package/src/constants/prompts.ts +0 -168
- package/src/constants/releaseNotes.ts +0 -7
- package/src/context/PermissionContext.tsx +0 -149
- package/src/context.ts +0 -278
- package/src/cost-tracker.ts +0 -84
- package/src/entrypoints/cli.tsx +0 -1561
- package/src/entrypoints/mcp.ts +0 -175
- package/src/history.ts +0 -25
- package/src/hooks/useApiKeyVerification.ts +0 -59
- package/src/hooks/useArrowKeyHistory.ts +0 -55
- package/src/hooks/useCanUseTool.ts +0 -138
- package/src/hooks/useCancelRequest.ts +0 -39
- package/src/hooks/useDoublePress.ts +0 -41
- package/src/hooks/useExitOnCtrlCD.ts +0 -31
- package/src/hooks/useInterval.ts +0 -25
- package/src/hooks/useLogMessages.ts +0 -16
- package/src/hooks/useLogStartupTime.ts +0 -12
- package/src/hooks/useNotifyAfterTimeout.ts +0 -65
- package/src/hooks/usePermissionRequestLogging.ts +0 -44
- package/src/hooks/useTerminalSize.ts +0 -49
- package/src/hooks/useTextInput.ts +0 -317
- package/src/hooks/useUnifiedCompletion.ts +0 -1405
- package/src/index.ts +0 -34
- package/src/messages.ts +0 -38
- package/src/permissions.ts +0 -268
- package/src/query.ts +0 -720
- package/src/screens/ConfigureNpmPrefix.tsx +0 -197
- package/src/screens/Doctor.tsx +0 -219
- package/src/screens/LogList.tsx +0 -68
- package/src/screens/REPL.tsx +0 -813
- package/src/screens/ResumeConversation.tsx +0 -68
- package/src/services/adapters/base.ts +0 -38
- package/src/services/adapters/chatCompletions.ts +0 -90
- package/src/services/adapters/responsesAPI.ts +0 -170
- package/src/services/browserMocks.ts +0 -66
- package/src/services/claude.ts +0 -2197
- package/src/services/customCommands.ts +0 -704
- package/src/services/fileFreshness.ts +0 -377
- package/src/services/gpt5ConnectionTest.ts +0 -340
- package/src/services/mcpClient.ts +0 -564
- package/src/services/mcpServerApproval.tsx +0 -50
- package/src/services/mentionProcessor.ts +0 -273
- package/src/services/modelAdapterFactory.ts +0 -69
- package/src/services/notifier.ts +0 -40
- package/src/services/oauth.ts +0 -357
- package/src/services/openai.ts +0 -1359
- package/src/services/responseStateManager.ts +0 -90
- package/src/services/sentry.ts +0 -3
- package/src/services/statsig.ts +0 -172
- package/src/services/statsigStorage.ts +0 -86
- package/src/services/systemReminder.ts +0 -507
- package/src/services/vcr.ts +0 -161
- package/src/test/testAdapters.ts +0 -96
- package/src/tools/ArchitectTool/ArchitectTool.tsx +0 -135
- package/src/tools/ArchitectTool/prompt.ts +0 -15
- package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +0 -576
- package/src/tools/BashTool/BashTool.tsx +0 -243
- package/src/tools/BashTool/BashToolResultMessage.tsx +0 -38
- package/src/tools/BashTool/OutputLine.tsx +0 -49
- package/src/tools/BashTool/prompt.ts +0 -174
- package/src/tools/BashTool/utils.ts +0 -56
- package/src/tools/FileEditTool/FileEditTool.tsx +0 -319
- package/src/tools/FileEditTool/prompt.ts +0 -51
- package/src/tools/FileEditTool/utils.ts +0 -58
- package/src/tools/FileReadTool/FileReadTool.tsx +0 -404
- package/src/tools/FileReadTool/prompt.ts +0 -7
- package/src/tools/FileWriteTool/FileWriteTool.tsx +0 -301
- package/src/tools/FileWriteTool/prompt.ts +0 -10
- package/src/tools/GlobTool/GlobTool.tsx +0 -119
- package/src/tools/GlobTool/prompt.ts +0 -8
- package/src/tools/GrepTool/GrepTool.tsx +0 -147
- package/src/tools/GrepTool/prompt.ts +0 -11
- package/src/tools/MCPTool/MCPTool.tsx +0 -107
- package/src/tools/MCPTool/prompt.ts +0 -3
- package/src/tools/MemoryReadTool/MemoryReadTool.tsx +0 -127
- package/src/tools/MemoryReadTool/prompt.ts +0 -3
- package/src/tools/MemoryWriteTool/MemoryWriteTool.tsx +0 -89
- package/src/tools/MemoryWriteTool/prompt.ts +0 -3
- package/src/tools/MultiEditTool/MultiEditTool.tsx +0 -388
- package/src/tools/MultiEditTool/prompt.ts +0 -45
- package/src/tools/NotebookEditTool/NotebookEditTool.tsx +0 -298
- package/src/tools/NotebookEditTool/prompt.ts +0 -3
- package/src/tools/NotebookReadTool/NotebookReadTool.tsx +0 -258
- package/src/tools/NotebookReadTool/prompt.ts +0 -3
- package/src/tools/StickerRequestTool/StickerRequestTool.tsx +0 -107
- package/src/tools/StickerRequestTool/prompt.ts +0 -19
- package/src/tools/TaskTool/TaskTool.tsx +0 -438
- package/src/tools/TaskTool/constants.ts +0 -1
- package/src/tools/TaskTool/prompt.ts +0 -92
- package/src/tools/ThinkTool/ThinkTool.tsx +0 -54
- package/src/tools/ThinkTool/prompt.ts +0 -12
- package/src/tools/TodoWriteTool/TodoWriteTool.tsx +0 -313
- package/src/tools/TodoWriteTool/prompt.ts +0 -63
- package/src/tools/URLFetcherTool/URLFetcherTool.tsx +0 -178
- package/src/tools/URLFetcherTool/cache.ts +0 -55
- package/src/tools/URLFetcherTool/htmlToMarkdown.ts +0 -55
- package/src/tools/URLFetcherTool/prompt.ts +0 -17
- package/src/tools/WebSearchTool/WebSearchTool.tsx +0 -103
- package/src/tools/WebSearchTool/prompt.ts +0 -13
- package/src/tools/WebSearchTool/searchProviders.ts +0 -66
- package/src/tools/lsTool/lsTool.tsx +0 -272
- package/src/tools/lsTool/prompt.ts +0 -2
- package/src/tools.ts +0 -67
- package/src/types/PermissionMode.ts +0 -120
- package/src/types/RequestContext.ts +0 -72
- package/src/types/common.d.ts +0 -2
- package/src/types/conversation.ts +0 -51
- package/src/types/logs.ts +0 -58
- package/src/types/modelCapabilities.ts +0 -64
- package/src/types/notebook.ts +0 -87
- package/src/utils/Cursor.ts +0 -436
- package/src/utils/PersistentShell.ts +0 -552
- package/src/utils/advancedFuzzyMatcher.ts +0 -290
- package/src/utils/agentLoader.ts +0 -278
- package/src/utils/agentStorage.ts +0 -97
- package/src/utils/array.ts +0 -3
- package/src/utils/ask.tsx +0 -99
- package/src/utils/auth.ts +0 -13
- package/src/utils/autoCompactCore.ts +0 -223
- package/src/utils/autoUpdater.ts +0 -458
- package/src/utils/betas.ts +0 -20
- package/src/utils/browser.ts +0 -14
- package/src/utils/cleanup.ts +0 -72
- package/src/utils/commands.ts +0 -261
- package/src/utils/commonUnixCommands.ts +0 -161
- package/src/utils/config.ts +0 -945
- package/src/utils/conversationRecovery.ts +0 -55
- package/src/utils/debugLogger.ts +0 -1235
- package/src/utils/diff.ts +0 -42
- package/src/utils/env.ts +0 -57
- package/src/utils/errors.ts +0 -21
- package/src/utils/exampleCommands.ts +0 -109
- package/src/utils/execFileNoThrow.ts +0 -51
- package/src/utils/expertChatStorage.ts +0 -136
- package/src/utils/file.ts +0 -405
- package/src/utils/fileRecoveryCore.ts +0 -71
- package/src/utils/format.tsx +0 -44
- package/src/utils/fuzzyMatcher.ts +0 -328
- package/src/utils/generators.ts +0 -62
- package/src/utils/git.ts +0 -92
- package/src/utils/globalLogger.ts +0 -77
- package/src/utils/http.ts +0 -10
- package/src/utils/imagePaste.ts +0 -38
- package/src/utils/json.ts +0 -13
- package/src/utils/log.ts +0 -382
- package/src/utils/markdown.ts +0 -213
- package/src/utils/messageContextManager.ts +0 -294
- package/src/utils/messages.tsx +0 -945
- package/src/utils/model.ts +0 -914
- package/src/utils/permissions/filesystem.ts +0 -127
- package/src/utils/responseState.ts +0 -23
- package/src/utils/ripgrep.ts +0 -167
- package/src/utils/secureFile.ts +0 -564
- package/src/utils/sessionState.ts +0 -49
- package/src/utils/state.ts +0 -25
- package/src/utils/style.ts +0 -29
- package/src/utils/terminal.ts +0 -50
- package/src/utils/theme.ts +0 -127
- package/src/utils/thinking.ts +0 -144
- package/src/utils/todoStorage.ts +0 -431
- package/src/utils/tokens.ts +0 -43
- package/src/utils/toolExecutionController.ts +0 -163
- package/src/utils/unaryLogging.ts +0 -26
- package/src/utils/user.ts +0 -37
- package/src/utils/validate.ts +0 -165
package/src/utils/config.ts
DELETED
|
@@ -1,945 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
|
2
|
-
import { resolve, join } from 'path'
|
|
3
|
-
import { cloneDeep, memoize, pick } from 'lodash-es'
|
|
4
|
-
import { homedir } from 'os'
|
|
5
|
-
import { GLOBAL_CLAUDE_FILE } from './env'
|
|
6
|
-
import { getCwd } from './state'
|
|
7
|
-
import { randomBytes } from 'crypto'
|
|
8
|
-
import { safeParseJSON } from './json'
|
|
9
|
-
import { checkGate, logEvent } from '../services/statsig'
|
|
10
|
-
import { GATE_USE_EXTERNAL_UPDATER } from '../constants/betas'
|
|
11
|
-
import { ConfigParseError } from './errors'
|
|
12
|
-
import type { ThemeNames } from './theme'
|
|
13
|
-
import { debug as debugLogger } from './debugLogger'
|
|
14
|
-
import { getSessionState, setSessionState } from './sessionState'
|
|
15
|
-
|
|
16
|
-
export type McpStdioServerConfig = {
|
|
17
|
-
type?: 'stdio' // Optional for backwards compatibility
|
|
18
|
-
command: string
|
|
19
|
-
args: string[]
|
|
20
|
-
env?: Record<string, string>
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type McpSSEServerConfig = {
|
|
24
|
-
type: 'sse'
|
|
25
|
-
url: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export type McpServerConfig = McpStdioServerConfig | McpSSEServerConfig
|
|
29
|
-
|
|
30
|
-
export type ProjectConfig = {
|
|
31
|
-
allowedTools: string[]
|
|
32
|
-
context: Record<string, string>
|
|
33
|
-
contextFiles?: string[]
|
|
34
|
-
history: string[]
|
|
35
|
-
dontCrawlDirectory?: boolean
|
|
36
|
-
enableArchitectTool?: boolean
|
|
37
|
-
mcpContextUris: string[]
|
|
38
|
-
mcpServers?: Record<string, McpServerConfig>
|
|
39
|
-
approvedMcprcServers?: string[]
|
|
40
|
-
rejectedMcprcServers?: string[]
|
|
41
|
-
lastAPIDuration?: number
|
|
42
|
-
lastCost?: number
|
|
43
|
-
lastDuration?: number
|
|
44
|
-
lastSessionId?: string
|
|
45
|
-
exampleFiles?: string[]
|
|
46
|
-
exampleFilesGeneratedAt?: number
|
|
47
|
-
hasTrustDialogAccepted?: boolean
|
|
48
|
-
hasCompletedProjectOnboarding?: boolean
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const DEFAULT_PROJECT_CONFIG: ProjectConfig = {
|
|
52
|
-
allowedTools: [],
|
|
53
|
-
context: {},
|
|
54
|
-
history: [],
|
|
55
|
-
dontCrawlDirectory: false,
|
|
56
|
-
enableArchitectTool: false,
|
|
57
|
-
mcpContextUris: [],
|
|
58
|
-
mcpServers: {},
|
|
59
|
-
approvedMcprcServers: [],
|
|
60
|
-
rejectedMcprcServers: [],
|
|
61
|
-
hasTrustDialogAccepted: false,
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function defaultConfigForProject(projectPath: string): ProjectConfig {
|
|
65
|
-
const config = { ...DEFAULT_PROJECT_CONFIG }
|
|
66
|
-
if (projectPath === homedir()) {
|
|
67
|
-
config.dontCrawlDirectory = true
|
|
68
|
-
}
|
|
69
|
-
return config
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export type AutoUpdaterStatus =
|
|
73
|
-
| 'disabled'
|
|
74
|
-
| 'enabled'
|
|
75
|
-
| 'no_permissions'
|
|
76
|
-
| 'not_configured'
|
|
77
|
-
|
|
78
|
-
export function isAutoUpdaterStatus(value: string): value is AutoUpdaterStatus {
|
|
79
|
-
return ['disabled', 'enabled', 'no_permissions', 'not_configured'].includes(
|
|
80
|
-
value as AutoUpdaterStatus,
|
|
81
|
-
)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export type NotificationChannel =
|
|
85
|
-
| 'iterm2'
|
|
86
|
-
| 'terminal_bell'
|
|
87
|
-
| 'iterm2_with_bell'
|
|
88
|
-
| 'notifications_disabled'
|
|
89
|
-
|
|
90
|
-
export type ProviderType =
|
|
91
|
-
| 'anthropic'
|
|
92
|
-
| 'openai'
|
|
93
|
-
| 'mistral'
|
|
94
|
-
| 'deepseek'
|
|
95
|
-
| 'kimi'
|
|
96
|
-
| 'qwen'
|
|
97
|
-
| 'glm'
|
|
98
|
-
| 'minimax'
|
|
99
|
-
| 'baidu-qianfan'
|
|
100
|
-
| 'siliconflow'
|
|
101
|
-
| 'bigdream'
|
|
102
|
-
| 'opendev'
|
|
103
|
-
| 'xai'
|
|
104
|
-
| 'groq'
|
|
105
|
-
| 'gemini'
|
|
106
|
-
| 'ollama'
|
|
107
|
-
| 'azure'
|
|
108
|
-
| 'custom'
|
|
109
|
-
| 'custom-openai'
|
|
110
|
-
|
|
111
|
-
// New model system types
|
|
112
|
-
export type ModelProfile = {
|
|
113
|
-
name: string // User-friendly name
|
|
114
|
-
provider: ProviderType // Provider type
|
|
115
|
-
modelName: string // Primary key - actual model identifier
|
|
116
|
-
baseURL?: string // Custom endpoint
|
|
117
|
-
apiKey: string
|
|
118
|
-
maxTokens: number // Output token limit (for GPT-5, this maps to max_completion_tokens)
|
|
119
|
-
contextLength: number // Context window size
|
|
120
|
-
reasoningEffort?: 'low' | 'medium' | 'high' | 'minimal' | 'medium'
|
|
121
|
-
isActive: boolean // Whether profile is enabled
|
|
122
|
-
createdAt: number // Creation timestamp
|
|
123
|
-
lastUsed?: number // Last usage timestamp
|
|
124
|
-
// 🔥 GPT-5 specific metadata
|
|
125
|
-
isGPT5?: boolean // Auto-detected GPT-5 model flag
|
|
126
|
-
validationStatus?: 'valid' | 'needs_repair' | 'auto_repaired' // Configuration status
|
|
127
|
-
lastValidation?: number // Last validation timestamp
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export type ModelPointerType = 'main' | 'task' | 'reasoning' | 'quick'
|
|
131
|
-
|
|
132
|
-
export type ModelPointers = {
|
|
133
|
-
main: string // Main dialog model ID
|
|
134
|
-
task: string // Task tool model ID
|
|
135
|
-
reasoning: string // Reasoning model ID
|
|
136
|
-
quick: string // Quick model ID
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export type AccountInfo = {
|
|
140
|
-
accountUuid: string
|
|
141
|
-
emailAddress: string
|
|
142
|
-
organizationUuid?: string
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export type GlobalConfig = {
|
|
146
|
-
projects?: Record<string, ProjectConfig>
|
|
147
|
-
numStartups: number
|
|
148
|
-
autoUpdaterStatus?: AutoUpdaterStatus
|
|
149
|
-
userID?: string
|
|
150
|
-
theme: ThemeNames
|
|
151
|
-
hasCompletedOnboarding?: boolean
|
|
152
|
-
// Tracks the last version that reset onboarding, used with MIN_VERSION_REQUIRING_ONBOARDING_RESET
|
|
153
|
-
lastOnboardingVersion?: string
|
|
154
|
-
// Tracks the last version for which release notes were seen, used for managing release notes
|
|
155
|
-
lastReleaseNotesSeen?: string
|
|
156
|
-
mcpServers?: Record<string, McpServerConfig>
|
|
157
|
-
preferredNotifChannel: NotificationChannel
|
|
158
|
-
verbose: boolean
|
|
159
|
-
customApiKeyResponses?: {
|
|
160
|
-
approved?: string[]
|
|
161
|
-
rejected?: string[]
|
|
162
|
-
}
|
|
163
|
-
primaryProvider?: ProviderType
|
|
164
|
-
maxTokens?: number
|
|
165
|
-
hasAcknowledgedCostThreshold?: boolean
|
|
166
|
-
oauthAccount?: AccountInfo
|
|
167
|
-
iterm2KeyBindingInstalled?: boolean // Legacy - keeping for backward compatibility
|
|
168
|
-
shiftEnterKeyBindingInstalled?: boolean
|
|
169
|
-
proxy?: string
|
|
170
|
-
stream?: boolean
|
|
171
|
-
|
|
172
|
-
// New model system
|
|
173
|
-
modelProfiles?: ModelProfile[] // Model configuration list
|
|
174
|
-
modelPointers?: ModelPointers // Model pointer system
|
|
175
|
-
defaultModelName?: string // Default model
|
|
176
|
-
// Update notifications
|
|
177
|
-
lastDismissedUpdateVersion?: string
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export const DEFAULT_GLOBAL_CONFIG: GlobalConfig = {
|
|
181
|
-
numStartups: 0,
|
|
182
|
-
autoUpdaterStatus: 'not_configured',
|
|
183
|
-
theme: 'dark' as ThemeNames,
|
|
184
|
-
preferredNotifChannel: 'iterm2',
|
|
185
|
-
verbose: false,
|
|
186
|
-
primaryProvider: 'anthropic' as ProviderType,
|
|
187
|
-
customApiKeyResponses: {
|
|
188
|
-
approved: [],
|
|
189
|
-
rejected: [],
|
|
190
|
-
},
|
|
191
|
-
stream: true,
|
|
192
|
-
|
|
193
|
-
// New model system defaults
|
|
194
|
-
modelProfiles: [],
|
|
195
|
-
modelPointers: {
|
|
196
|
-
main: '',
|
|
197
|
-
task: '',
|
|
198
|
-
reasoning: '',
|
|
199
|
-
quick: '',
|
|
200
|
-
},
|
|
201
|
-
lastDismissedUpdateVersion: undefined,
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
export const GLOBAL_CONFIG_KEYS = [
|
|
205
|
-
'autoUpdaterStatus',
|
|
206
|
-
'theme',
|
|
207
|
-
'hasCompletedOnboarding',
|
|
208
|
-
'lastOnboardingVersion',
|
|
209
|
-
'lastReleaseNotesSeen',
|
|
210
|
-
'verbose',
|
|
211
|
-
'customApiKeyResponses',
|
|
212
|
-
'primaryProvider',
|
|
213
|
-
'preferredNotifChannel',
|
|
214
|
-
'shiftEnterKeyBindingInstalled',
|
|
215
|
-
'maxTokens',
|
|
216
|
-
] as const
|
|
217
|
-
|
|
218
|
-
export type GlobalConfigKey = (typeof GLOBAL_CONFIG_KEYS)[number]
|
|
219
|
-
|
|
220
|
-
export function isGlobalConfigKey(key: string): key is GlobalConfigKey {
|
|
221
|
-
return GLOBAL_CONFIG_KEYS.includes(key as GlobalConfigKey)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export const PROJECT_CONFIG_KEYS = [
|
|
225
|
-
'dontCrawlDirectory',
|
|
226
|
-
'enableArchitectTool',
|
|
227
|
-
'hasTrustDialogAccepted',
|
|
228
|
-
'hasCompletedProjectOnboarding',
|
|
229
|
-
] as const
|
|
230
|
-
|
|
231
|
-
export type ProjectConfigKey = (typeof PROJECT_CONFIG_KEYS)[number]
|
|
232
|
-
|
|
233
|
-
export function checkHasTrustDialogAccepted(): boolean {
|
|
234
|
-
let currentPath = getCwd()
|
|
235
|
-
const config = getConfig(GLOBAL_CLAUDE_FILE, DEFAULT_GLOBAL_CONFIG)
|
|
236
|
-
|
|
237
|
-
while (true) {
|
|
238
|
-
const projectConfig = config.projects?.[currentPath]
|
|
239
|
-
if (projectConfig?.hasTrustDialogAccepted) {
|
|
240
|
-
return true
|
|
241
|
-
}
|
|
242
|
-
const parentPath = resolve(currentPath, '..')
|
|
243
|
-
// Stop if we've reached the root (when parent is same as current)
|
|
244
|
-
if (parentPath === currentPath) {
|
|
245
|
-
break
|
|
246
|
-
}
|
|
247
|
-
currentPath = parentPath
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return false
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// We have to put this test code here because Jest doesn't support mocking ES modules :O
|
|
254
|
-
const TEST_GLOBAL_CONFIG_FOR_TESTING: GlobalConfig = {
|
|
255
|
-
...DEFAULT_GLOBAL_CONFIG,
|
|
256
|
-
autoUpdaterStatus: 'disabled',
|
|
257
|
-
}
|
|
258
|
-
const TEST_PROJECT_CONFIG_FOR_TESTING: ProjectConfig = {
|
|
259
|
-
...DEFAULT_PROJECT_CONFIG,
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export function isProjectConfigKey(key: string): key is ProjectConfigKey {
|
|
263
|
-
return PROJECT_CONFIG_KEYS.includes(key as ProjectConfigKey)
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export function saveGlobalConfig(config: GlobalConfig): void {
|
|
267
|
-
if (process.env.NODE_ENV === 'test') {
|
|
268
|
-
for (const key in config) {
|
|
269
|
-
TEST_GLOBAL_CONFIG_FOR_TESTING[key] = config[key]
|
|
270
|
-
}
|
|
271
|
-
return
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// 直接保存配置(无需清除缓存,因为已移除缓存)
|
|
275
|
-
saveConfig(
|
|
276
|
-
GLOBAL_CLAUDE_FILE,
|
|
277
|
-
{
|
|
278
|
-
...config,
|
|
279
|
-
projects: getConfig(GLOBAL_CLAUDE_FILE, DEFAULT_GLOBAL_CONFIG).projects,
|
|
280
|
-
},
|
|
281
|
-
DEFAULT_GLOBAL_CONFIG,
|
|
282
|
-
)
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// 临时移除缓存,确保总是获取最新配置
|
|
286
|
-
export function getGlobalConfig(): GlobalConfig {
|
|
287
|
-
if (process.env.NODE_ENV === 'test') {
|
|
288
|
-
return TEST_GLOBAL_CONFIG_FOR_TESTING
|
|
289
|
-
}
|
|
290
|
-
const config = getConfig(GLOBAL_CLAUDE_FILE, DEFAULT_GLOBAL_CONFIG)
|
|
291
|
-
return migrateModelProfilesRemoveId(config)
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export function getAnthropicApiKey(): null | string {
|
|
295
|
-
return process.env.ANTHROPIC_API_KEY || null
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
export function normalizeApiKeyForConfig(apiKey: string): string {
|
|
299
|
-
return apiKey?.slice(-20) ?? ''
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export function getCustomApiKeyStatus(
|
|
303
|
-
truncatedApiKey: string,
|
|
304
|
-
): 'approved' | 'rejected' | 'new' {
|
|
305
|
-
const config = getGlobalConfig()
|
|
306
|
-
if (config.customApiKeyResponses?.approved?.includes(truncatedApiKey)) {
|
|
307
|
-
return 'approved'
|
|
308
|
-
}
|
|
309
|
-
if (config.customApiKeyResponses?.rejected?.includes(truncatedApiKey)) {
|
|
310
|
-
return 'rejected'
|
|
311
|
-
}
|
|
312
|
-
return 'new'
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function saveConfig<A extends object>(
|
|
316
|
-
file: string,
|
|
317
|
-
config: A,
|
|
318
|
-
defaultConfig: A,
|
|
319
|
-
): void {
|
|
320
|
-
// Filter out any values that match the defaults
|
|
321
|
-
const filteredConfig = Object.fromEntries(
|
|
322
|
-
Object.entries(config).filter(
|
|
323
|
-
([key, value]) =>
|
|
324
|
-
JSON.stringify(value) !== JSON.stringify(defaultConfig[key as keyof A]),
|
|
325
|
-
),
|
|
326
|
-
)
|
|
327
|
-
writeFileSync(file, JSON.stringify(filteredConfig, null, 2), 'utf-8')
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Flag to track if config reading is allowed
|
|
331
|
-
let configReadingAllowed = false
|
|
332
|
-
|
|
333
|
-
export function enableConfigs(): void {
|
|
334
|
-
// Any reads to configuration before this flag is set show an console warning
|
|
335
|
-
// to prevent us from adding config reading during module initialization
|
|
336
|
-
configReadingAllowed = true
|
|
337
|
-
// We only check the global config because currently all the configs share a file
|
|
338
|
-
getConfig(
|
|
339
|
-
GLOBAL_CLAUDE_FILE,
|
|
340
|
-
DEFAULT_GLOBAL_CONFIG,
|
|
341
|
-
true /* throw on invalid */,
|
|
342
|
-
)
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function getConfig<A>(
|
|
346
|
-
file: string,
|
|
347
|
-
defaultConfig: A,
|
|
348
|
-
throwOnInvalid?: boolean,
|
|
349
|
-
): A {
|
|
350
|
-
// 简化配置访问逻辑,移除复杂的时序检查
|
|
351
|
-
|
|
352
|
-
debugLogger.state('CONFIG_LOAD_START', {
|
|
353
|
-
file,
|
|
354
|
-
fileExists: String(existsSync(file)),
|
|
355
|
-
throwOnInvalid: String(!!throwOnInvalid),
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
if (!existsSync(file)) {
|
|
359
|
-
debugLogger.state('CONFIG_LOAD_DEFAULT', {
|
|
360
|
-
file,
|
|
361
|
-
reason: 'file_not_exists',
|
|
362
|
-
defaultConfigKeys: Object.keys(defaultConfig as object).join(', '),
|
|
363
|
-
})
|
|
364
|
-
return cloneDeep(defaultConfig)
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
try {
|
|
368
|
-
const fileContent = readFileSync(file, 'utf-8')
|
|
369
|
-
debugLogger.state('CONFIG_FILE_READ', {
|
|
370
|
-
file,
|
|
371
|
-
contentLength: String(fileContent.length),
|
|
372
|
-
contentPreview:
|
|
373
|
-
fileContent.substring(0, 100) + (fileContent.length > 100 ? '...' : ''),
|
|
374
|
-
})
|
|
375
|
-
|
|
376
|
-
try {
|
|
377
|
-
const parsedConfig = JSON.parse(fileContent)
|
|
378
|
-
debugLogger.state('CONFIG_JSON_PARSED', {
|
|
379
|
-
file,
|
|
380
|
-
parsedKeys: Object.keys(parsedConfig).join(', '),
|
|
381
|
-
})
|
|
382
|
-
|
|
383
|
-
// Handle backward compatibility - remove logic for deleted fields
|
|
384
|
-
const finalConfig = {
|
|
385
|
-
...cloneDeep(defaultConfig),
|
|
386
|
-
...parsedConfig,
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
debugLogger.state('CONFIG_LOAD_SUCCESS', {
|
|
390
|
-
file,
|
|
391
|
-
finalConfigKeys: Object.keys(finalConfig as object).join(', '),
|
|
392
|
-
})
|
|
393
|
-
|
|
394
|
-
return finalConfig
|
|
395
|
-
} catch (error) {
|
|
396
|
-
// Throw a ConfigParseError with the file path and default config
|
|
397
|
-
const errorMessage =
|
|
398
|
-
error instanceof Error ? error.message : String(error)
|
|
399
|
-
|
|
400
|
-
debugLogger.error('CONFIG_JSON_PARSE_ERROR', {
|
|
401
|
-
file,
|
|
402
|
-
errorMessage,
|
|
403
|
-
errorType:
|
|
404
|
-
error instanceof Error ? error.constructor.name : typeof error,
|
|
405
|
-
contentLength: String(fileContent.length),
|
|
406
|
-
})
|
|
407
|
-
|
|
408
|
-
throw new ConfigParseError(errorMessage, file, defaultConfig)
|
|
409
|
-
}
|
|
410
|
-
} catch (error: unknown) {
|
|
411
|
-
// Re-throw ConfigParseError if throwOnInvalid is true
|
|
412
|
-
if (error instanceof ConfigParseError && throwOnInvalid) {
|
|
413
|
-
debugLogger.error('CONFIG_PARSE_ERROR_RETHROWN', {
|
|
414
|
-
file,
|
|
415
|
-
throwOnInvalid: String(throwOnInvalid),
|
|
416
|
-
errorMessage: error.message,
|
|
417
|
-
})
|
|
418
|
-
throw error
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
debugLogger.warn('CONFIG_FALLBACK_TO_DEFAULT', {
|
|
422
|
-
file,
|
|
423
|
-
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
424
|
-
errorMessage: error instanceof Error ? error.message : String(error),
|
|
425
|
-
action: 'using_default_config',
|
|
426
|
-
})
|
|
427
|
-
|
|
428
|
-
return cloneDeep(defaultConfig)
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
export function getCurrentProjectConfig(): ProjectConfig {
|
|
433
|
-
if (process.env.NODE_ENV === 'test') {
|
|
434
|
-
return TEST_PROJECT_CONFIG_FOR_TESTING
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
const absolutePath = resolve(getCwd())
|
|
438
|
-
const config = getConfig(GLOBAL_CLAUDE_FILE, DEFAULT_GLOBAL_CONFIG)
|
|
439
|
-
|
|
440
|
-
if (!config.projects) {
|
|
441
|
-
return defaultConfigForProject(absolutePath)
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
const projectConfig =
|
|
445
|
-
config.projects[absolutePath] ?? defaultConfigForProject(absolutePath)
|
|
446
|
-
// Not sure how this became a string
|
|
447
|
-
// TODO: Fix upstream
|
|
448
|
-
if (typeof projectConfig.allowedTools === 'string') {
|
|
449
|
-
projectConfig.allowedTools =
|
|
450
|
-
(safeParseJSON(projectConfig.allowedTools) as string[]) ?? []
|
|
451
|
-
}
|
|
452
|
-
return projectConfig
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
export function saveCurrentProjectConfig(projectConfig: ProjectConfig): void {
|
|
456
|
-
if (process.env.NODE_ENV === 'test') {
|
|
457
|
-
for (const key in projectConfig) {
|
|
458
|
-
TEST_PROJECT_CONFIG_FOR_TESTING[key] = projectConfig[key]
|
|
459
|
-
}
|
|
460
|
-
return
|
|
461
|
-
}
|
|
462
|
-
const config = getConfig(GLOBAL_CLAUDE_FILE, DEFAULT_GLOBAL_CONFIG)
|
|
463
|
-
saveConfig(
|
|
464
|
-
GLOBAL_CLAUDE_FILE,
|
|
465
|
-
{
|
|
466
|
-
...config,
|
|
467
|
-
projects: {
|
|
468
|
-
...config.projects,
|
|
469
|
-
[resolve(getCwd())]: projectConfig,
|
|
470
|
-
},
|
|
471
|
-
},
|
|
472
|
-
DEFAULT_GLOBAL_CONFIG,
|
|
473
|
-
)
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
export async function isAutoUpdaterDisabled(): Promise<boolean> {
|
|
477
|
-
const useExternalUpdater = await checkGate(GATE_USE_EXTERNAL_UPDATER)
|
|
478
|
-
return (
|
|
479
|
-
useExternalUpdater || getGlobalConfig().autoUpdaterStatus === 'disabled'
|
|
480
|
-
)
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
export const TEST_MCPRC_CONFIG_FOR_TESTING: Record<string, McpServerConfig> = {}
|
|
484
|
-
|
|
485
|
-
export function clearMcprcConfigForTesting(): void {
|
|
486
|
-
if (process.env.NODE_ENV === 'test') {
|
|
487
|
-
Object.keys(TEST_MCPRC_CONFIG_FOR_TESTING).forEach(key => {
|
|
488
|
-
delete TEST_MCPRC_CONFIG_FOR_TESTING[key]
|
|
489
|
-
})
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
export function addMcprcServerForTesting(
|
|
494
|
-
name: string,
|
|
495
|
-
server: McpServerConfig,
|
|
496
|
-
): void {
|
|
497
|
-
if (process.env.NODE_ENV === 'test') {
|
|
498
|
-
TEST_MCPRC_CONFIG_FOR_TESTING[name] = server
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
export function removeMcprcServerForTesting(name: string): void {
|
|
503
|
-
if (process.env.NODE_ENV === 'test') {
|
|
504
|
-
if (!TEST_MCPRC_CONFIG_FOR_TESTING[name]) {
|
|
505
|
-
throw new Error(`No MCP server found with name: ${name} in .mcprc`)
|
|
506
|
-
}
|
|
507
|
-
delete TEST_MCPRC_CONFIG_FOR_TESTING[name]
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
export const getMcprcConfig = memoize(
|
|
512
|
-
(): Record<string, McpServerConfig> => {
|
|
513
|
-
if (process.env.NODE_ENV === 'test') {
|
|
514
|
-
return TEST_MCPRC_CONFIG_FOR_TESTING
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
const mcprcPath = join(getCwd(), '.mcprc')
|
|
518
|
-
if (!existsSync(mcprcPath)) {
|
|
519
|
-
return {}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
try {
|
|
523
|
-
const mcprcContent = readFileSync(mcprcPath, 'utf-8')
|
|
524
|
-
const config = safeParseJSON(mcprcContent)
|
|
525
|
-
if (config && typeof config === 'object') {
|
|
526
|
-
logEvent('tengu_mcprc_found', {
|
|
527
|
-
numServers: Object.keys(config).length.toString(),
|
|
528
|
-
})
|
|
529
|
-
return config as Record<string, McpServerConfig>
|
|
530
|
-
}
|
|
531
|
-
} catch {
|
|
532
|
-
// Ignore errors reading/parsing .mcprc (they're logged in safeParseJSON)
|
|
533
|
-
}
|
|
534
|
-
return {}
|
|
535
|
-
},
|
|
536
|
-
// This function returns the same value as long as the cwd and mcprc file content remain the same
|
|
537
|
-
() => {
|
|
538
|
-
const cwd = getCwd()
|
|
539
|
-
const mcprcPath = join(cwd, '.mcprc')
|
|
540
|
-
if (existsSync(mcprcPath)) {
|
|
541
|
-
try {
|
|
542
|
-
const stat = readFileSync(mcprcPath, 'utf-8')
|
|
543
|
-
return `${cwd}:${stat}`
|
|
544
|
-
} catch {
|
|
545
|
-
return cwd
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
return cwd
|
|
549
|
-
},
|
|
550
|
-
)
|
|
551
|
-
|
|
552
|
-
export function getOrCreateUserID(): string {
|
|
553
|
-
const config = getGlobalConfig()
|
|
554
|
-
if (config.userID) {
|
|
555
|
-
return config.userID
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
const userID = randomBytes(32).toString('hex')
|
|
559
|
-
saveGlobalConfig({ ...config, userID })
|
|
560
|
-
return userID
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
export function getConfigForCLI(key: string, global: boolean): unknown {
|
|
564
|
-
logEvent('tengu_config_get', {
|
|
565
|
-
key,
|
|
566
|
-
global: global?.toString() ?? 'false',
|
|
567
|
-
})
|
|
568
|
-
if (global) {
|
|
569
|
-
if (!isGlobalConfigKey(key)) {
|
|
570
|
-
console.error(
|
|
571
|
-
`Error: '${key}' is not a valid config key. Valid keys are: ${GLOBAL_CONFIG_KEYS.join(', ')}`,
|
|
572
|
-
)
|
|
573
|
-
process.exit(1)
|
|
574
|
-
}
|
|
575
|
-
return getGlobalConfig()[key]
|
|
576
|
-
} else {
|
|
577
|
-
if (!isProjectConfigKey(key)) {
|
|
578
|
-
console.error(
|
|
579
|
-
`Error: '${key}' is not a valid config key. Valid keys are: ${PROJECT_CONFIG_KEYS.join(', ')}`,
|
|
580
|
-
)
|
|
581
|
-
process.exit(1)
|
|
582
|
-
}
|
|
583
|
-
return getCurrentProjectConfig()[key]
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
export function setConfigForCLI(
|
|
588
|
-
key: string,
|
|
589
|
-
value: unknown,
|
|
590
|
-
global: boolean,
|
|
591
|
-
): void {
|
|
592
|
-
logEvent('tengu_config_set', {
|
|
593
|
-
key,
|
|
594
|
-
global: global?.toString() ?? 'false',
|
|
595
|
-
})
|
|
596
|
-
if (global) {
|
|
597
|
-
if (!isGlobalConfigKey(key)) {
|
|
598
|
-
console.error(
|
|
599
|
-
`Error: Cannot set '${key}'. Only these keys can be modified: ${GLOBAL_CONFIG_KEYS.join(', ')}`,
|
|
600
|
-
)
|
|
601
|
-
process.exit(1)
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
if (key === 'autoUpdaterStatus' && !isAutoUpdaterStatus(value as string)) {
|
|
605
|
-
console.error(
|
|
606
|
-
`Error: Invalid value for autoUpdaterStatus. Must be one of: disabled, enabled, no_permissions, not_configured`,
|
|
607
|
-
)
|
|
608
|
-
process.exit(1)
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
const currentConfig = getGlobalConfig()
|
|
612
|
-
saveGlobalConfig({
|
|
613
|
-
...currentConfig,
|
|
614
|
-
[key]: value,
|
|
615
|
-
})
|
|
616
|
-
} else {
|
|
617
|
-
if (!isProjectConfigKey(key)) {
|
|
618
|
-
console.error(
|
|
619
|
-
`Error: Cannot set '${key}'. Only these keys can be modified: ${PROJECT_CONFIG_KEYS.join(', ')}. Did you mean --global?`,
|
|
620
|
-
)
|
|
621
|
-
process.exit(1)
|
|
622
|
-
}
|
|
623
|
-
const currentConfig = getCurrentProjectConfig()
|
|
624
|
-
saveCurrentProjectConfig({
|
|
625
|
-
...currentConfig,
|
|
626
|
-
[key]: value,
|
|
627
|
-
})
|
|
628
|
-
}
|
|
629
|
-
// Wait for the output to be flushed, to avoid clearing the screen.
|
|
630
|
-
setTimeout(() => {
|
|
631
|
-
// Without this we hang indefinitely.
|
|
632
|
-
process.exit(0)
|
|
633
|
-
}, 100)
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
export function deleteConfigForCLI(key: string, global: boolean): void {
|
|
637
|
-
logEvent('tengu_config_delete', {
|
|
638
|
-
key,
|
|
639
|
-
global: global?.toString() ?? 'false',
|
|
640
|
-
})
|
|
641
|
-
if (global) {
|
|
642
|
-
if (!isGlobalConfigKey(key)) {
|
|
643
|
-
console.error(
|
|
644
|
-
`Error: Cannot delete '${key}'. Only these keys can be modified: ${GLOBAL_CONFIG_KEYS.join(', ')}`,
|
|
645
|
-
)
|
|
646
|
-
process.exit(1)
|
|
647
|
-
}
|
|
648
|
-
const currentConfig = getGlobalConfig()
|
|
649
|
-
delete currentConfig[key]
|
|
650
|
-
saveGlobalConfig(currentConfig)
|
|
651
|
-
} else {
|
|
652
|
-
if (!isProjectConfigKey(key)) {
|
|
653
|
-
console.error(
|
|
654
|
-
`Error: Cannot delete '${key}'. Only these keys can be modified: ${PROJECT_CONFIG_KEYS.join(', ')}. Did you mean --global?`,
|
|
655
|
-
)
|
|
656
|
-
process.exit(1)
|
|
657
|
-
}
|
|
658
|
-
const currentConfig = getCurrentProjectConfig()
|
|
659
|
-
delete currentConfig[key]
|
|
660
|
-
saveCurrentProjectConfig(currentConfig)
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
export function listConfigForCLI(global: true): GlobalConfig
|
|
665
|
-
export function listConfigForCLI(global: false): ProjectConfig
|
|
666
|
-
export function listConfigForCLI(global: boolean): object {
|
|
667
|
-
logEvent('tengu_config_list', {
|
|
668
|
-
global: global?.toString() ?? 'false',
|
|
669
|
-
})
|
|
670
|
-
if (global) {
|
|
671
|
-
const currentConfig = pick(getGlobalConfig(), GLOBAL_CONFIG_KEYS)
|
|
672
|
-
return currentConfig
|
|
673
|
-
} else {
|
|
674
|
-
return pick(getCurrentProjectConfig(), PROJECT_CONFIG_KEYS)
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
export function getOpenAIApiKey(): string | undefined {
|
|
679
|
-
return process.env.OPENAI_API_KEY
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
// Configuration migration utility functions
|
|
683
|
-
function migrateModelProfilesRemoveId(config: GlobalConfig): GlobalConfig {
|
|
684
|
-
if (!config.modelProfiles) return config
|
|
685
|
-
|
|
686
|
-
// 1. Remove id field from ModelProfile objects and build ID to modelName mapping
|
|
687
|
-
const idToModelNameMap = new Map<string, string>()
|
|
688
|
-
const migratedProfiles = config.modelProfiles.map(profile => {
|
|
689
|
-
// Build mapping before removing id field
|
|
690
|
-
if ((profile as any).id && profile.modelName) {
|
|
691
|
-
idToModelNameMap.set((profile as any).id, profile.modelName)
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// Remove id field, keep everything else
|
|
695
|
-
const { id, ...profileWithoutId } = profile as any
|
|
696
|
-
return profileWithoutId as ModelProfile
|
|
697
|
-
})
|
|
698
|
-
|
|
699
|
-
// 2. Migrate ModelPointers from IDs to modelNames
|
|
700
|
-
const migratedPointers: ModelPointers = {
|
|
701
|
-
main: '',
|
|
702
|
-
task: '',
|
|
703
|
-
reasoning: '',
|
|
704
|
-
quick: '',
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
if (config.modelPointers) {
|
|
708
|
-
Object.entries(config.modelPointers).forEach(([pointer, value]) => {
|
|
709
|
-
if (value) {
|
|
710
|
-
// If value looks like an old ID (model_xxx), map it to modelName
|
|
711
|
-
const modelName = idToModelNameMap.get(value) || value
|
|
712
|
-
migratedPointers[pointer as ModelPointerType] = modelName
|
|
713
|
-
}
|
|
714
|
-
})
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// 3. Migrate legacy config fields
|
|
718
|
-
let defaultModelName: string | undefined
|
|
719
|
-
if ((config as any).defaultModelId) {
|
|
720
|
-
defaultModelName =
|
|
721
|
-
idToModelNameMap.get((config as any).defaultModelId) ||
|
|
722
|
-
(config as any).defaultModelId
|
|
723
|
-
} else if ((config as any).defaultModelName) {
|
|
724
|
-
defaultModelName = (config as any).defaultModelName
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
// 4. Remove legacy fields and return migrated config
|
|
728
|
-
const migratedConfig = { ...config }
|
|
729
|
-
delete (migratedConfig as any).defaultModelId
|
|
730
|
-
delete (migratedConfig as any).currentSelectedModelId
|
|
731
|
-
delete (migratedConfig as any).mainAgentModelId
|
|
732
|
-
delete (migratedConfig as any).taskToolModelId
|
|
733
|
-
|
|
734
|
-
return {
|
|
735
|
-
...migratedConfig,
|
|
736
|
-
modelProfiles: migratedProfiles,
|
|
737
|
-
modelPointers: migratedPointers,
|
|
738
|
-
defaultModelName,
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
// New model system utility functions
|
|
743
|
-
|
|
744
|
-
export function setAllPointersToModel(modelName: string): void {
|
|
745
|
-
const config = getGlobalConfig()
|
|
746
|
-
const updatedConfig = {
|
|
747
|
-
...config,
|
|
748
|
-
modelPointers: {
|
|
749
|
-
main: modelName,
|
|
750
|
-
task: modelName,
|
|
751
|
-
reasoning: modelName,
|
|
752
|
-
quick: modelName,
|
|
753
|
-
},
|
|
754
|
-
defaultModelName: modelName,
|
|
755
|
-
}
|
|
756
|
-
saveGlobalConfig(updatedConfig)
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
export function setModelPointer(
|
|
760
|
-
pointer: ModelPointerType,
|
|
761
|
-
modelName: string,
|
|
762
|
-
): void {
|
|
763
|
-
const config = getGlobalConfig()
|
|
764
|
-
const updatedConfig = {
|
|
765
|
-
...config,
|
|
766
|
-
modelPointers: {
|
|
767
|
-
...config.modelPointers,
|
|
768
|
-
[pointer]: modelName,
|
|
769
|
-
},
|
|
770
|
-
}
|
|
771
|
-
saveGlobalConfig(updatedConfig)
|
|
772
|
-
|
|
773
|
-
// 🔧 Fix: Force ModelManager reload after config change
|
|
774
|
-
// Import here to avoid circular dependency
|
|
775
|
-
import('./model').then(({ reloadModelManager }) => {
|
|
776
|
-
reloadModelManager()
|
|
777
|
-
})
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// 🔥 GPT-5 Configuration Validation and Auto-Repair Functions
|
|
781
|
-
|
|
782
|
-
/**
|
|
783
|
-
* Check if a model name represents a GPT-5 model
|
|
784
|
-
*/
|
|
785
|
-
export function isGPT5ModelName(modelName: string): boolean {
|
|
786
|
-
if (!modelName || typeof modelName !== 'string') return false
|
|
787
|
-
const lowerName = modelName.toLowerCase()
|
|
788
|
-
return lowerName.startsWith('gpt-5') || lowerName.includes('gpt-5')
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
/**
|
|
792
|
-
* Validate and auto-repair GPT-5 model configuration
|
|
793
|
-
*/
|
|
794
|
-
export function validateAndRepairGPT5Profile(profile: ModelProfile): ModelProfile {
|
|
795
|
-
const isGPT5 = isGPT5ModelName(profile.modelName)
|
|
796
|
-
const now = Date.now()
|
|
797
|
-
|
|
798
|
-
// Create a working copy
|
|
799
|
-
const repairedProfile: ModelProfile = { ...profile }
|
|
800
|
-
let wasRepaired = false
|
|
801
|
-
|
|
802
|
-
// 🔧 Set GPT-5 detection flag
|
|
803
|
-
if (isGPT5 !== profile.isGPT5) {
|
|
804
|
-
repairedProfile.isGPT5 = isGPT5
|
|
805
|
-
wasRepaired = true
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
if (isGPT5) {
|
|
809
|
-
// 🔧 GPT-5 Parameter Validation and Repair
|
|
810
|
-
|
|
811
|
-
// 1. Reasoning effort validation
|
|
812
|
-
const validReasoningEfforts = ['minimal', 'low', 'medium', 'high']
|
|
813
|
-
if (!profile.reasoningEffort || !validReasoningEfforts.includes(profile.reasoningEffort)) {
|
|
814
|
-
repairedProfile.reasoningEffort = 'medium' // Default for coding tasks
|
|
815
|
-
wasRepaired = true
|
|
816
|
-
console.log(`🔧 GPT-5 Config: Set reasoning effort to 'medium' for ${profile.modelName}`)
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
// 2. Context length validation (GPT-5 models typically have 128k context)
|
|
820
|
-
if (profile.contextLength < 128000) {
|
|
821
|
-
repairedProfile.contextLength = 128000
|
|
822
|
-
wasRepaired = true
|
|
823
|
-
console.log(`🔧 GPT-5 Config: Updated context length to 128k for ${profile.modelName}`)
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
// 3. Output tokens validation (reasonable defaults for GPT-5)
|
|
827
|
-
if (profile.maxTokens < 4000) {
|
|
828
|
-
repairedProfile.maxTokens = 8192 // Good default for coding tasks
|
|
829
|
-
wasRepaired = true
|
|
830
|
-
console.log(`🔧 GPT-5 Config: Updated max tokens to 8192 for ${profile.modelName}`)
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
// 4. Provider validation
|
|
834
|
-
if (profile.provider !== 'openai' && profile.provider !== 'custom-openai' && profile.provider !== 'azure') {
|
|
835
|
-
console.warn(`⚠️ GPT-5 Config: Unexpected provider '${profile.provider}' for GPT-5 model ${profile.modelName}. Consider using 'openai' or 'custom-openai'.`)
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
// 5. Base URL validation for official models
|
|
839
|
-
if (profile.modelName.includes('gpt-5') && !profile.baseURL) {
|
|
840
|
-
repairedProfile.baseURL = 'https://api.openai.com/v1'
|
|
841
|
-
wasRepaired = true
|
|
842
|
-
console.log(`🔧 GPT-5 Config: Set default base URL for ${profile.modelName}`)
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// Update validation metadata
|
|
847
|
-
repairedProfile.validationStatus = wasRepaired ? 'auto_repaired' : 'valid'
|
|
848
|
-
repairedProfile.lastValidation = now
|
|
849
|
-
|
|
850
|
-
if (wasRepaired) {
|
|
851
|
-
console.log(`✅ GPT-5 Config: Auto-repaired configuration for ${profile.modelName}`)
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
return repairedProfile
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
/**
|
|
858
|
-
* Validate and repair all GPT-5 profiles in the global configuration
|
|
859
|
-
*/
|
|
860
|
-
export function validateAndRepairAllGPT5Profiles(): { repaired: number; total: number } {
|
|
861
|
-
const config = getGlobalConfig()
|
|
862
|
-
if (!config.modelProfiles) {
|
|
863
|
-
return { repaired: 0, total: 0 }
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
let repairCount = 0
|
|
867
|
-
const repairedProfiles = config.modelProfiles.map(profile => {
|
|
868
|
-
const repairedProfile = validateAndRepairGPT5Profile(profile)
|
|
869
|
-
if (repairedProfile.validationStatus === 'auto_repaired') {
|
|
870
|
-
repairCount++
|
|
871
|
-
}
|
|
872
|
-
return repairedProfile
|
|
873
|
-
})
|
|
874
|
-
|
|
875
|
-
// Save the repaired configuration
|
|
876
|
-
if (repairCount > 0) {
|
|
877
|
-
const updatedConfig = {
|
|
878
|
-
...config,
|
|
879
|
-
modelProfiles: repairedProfiles,
|
|
880
|
-
}
|
|
881
|
-
saveGlobalConfig(updatedConfig)
|
|
882
|
-
console.log(`🔧 GPT-5 Config: Auto-repaired ${repairCount} model profiles`)
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
return { repaired: repairCount, total: config.modelProfiles.length }
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
/**
|
|
889
|
-
* Get GPT-5 configuration recommendations for a specific model
|
|
890
|
-
*/
|
|
891
|
-
export function getGPT5ConfigRecommendations(modelName: string): Partial<ModelProfile> {
|
|
892
|
-
if (!isGPT5ModelName(modelName)) {
|
|
893
|
-
return {}
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
const recommendations: Partial<ModelProfile> = {
|
|
897
|
-
contextLength: 128000, // GPT-5 standard context length
|
|
898
|
-
maxTokens: 8192, // Good default for coding tasks
|
|
899
|
-
reasoningEffort: 'medium', // Balanced for most coding tasks
|
|
900
|
-
isGPT5: true,
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
// Model-specific optimizations
|
|
904
|
-
if (modelName.includes('gpt-5-mini')) {
|
|
905
|
-
recommendations.maxTokens = 4096 // Smaller default for mini
|
|
906
|
-
recommendations.reasoningEffort = 'low' // Faster for simple tasks
|
|
907
|
-
} else if (modelName.includes('gpt-5-nano')) {
|
|
908
|
-
recommendations.maxTokens = 2048 // Even smaller for nano
|
|
909
|
-
recommendations.reasoningEffort = 'minimal' // Fastest option
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
return recommendations
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
/**
|
|
916
|
-
* Create a properly configured GPT-5 model profile
|
|
917
|
-
*/
|
|
918
|
-
export function createGPT5ModelProfile(
|
|
919
|
-
name: string,
|
|
920
|
-
modelName: string,
|
|
921
|
-
apiKey: string,
|
|
922
|
-
baseURL?: string,
|
|
923
|
-
provider: ProviderType = 'openai'
|
|
924
|
-
): ModelProfile {
|
|
925
|
-
const recommendations = getGPT5ConfigRecommendations(modelName)
|
|
926
|
-
|
|
927
|
-
const profile: ModelProfile = {
|
|
928
|
-
name,
|
|
929
|
-
provider,
|
|
930
|
-
modelName,
|
|
931
|
-
baseURL: baseURL || 'https://api.openai.com/v1',
|
|
932
|
-
apiKey,
|
|
933
|
-
maxTokens: recommendations.maxTokens || 8192,
|
|
934
|
-
contextLength: recommendations.contextLength || 128000,
|
|
935
|
-
reasoningEffort: recommendations.reasoningEffort || 'medium',
|
|
936
|
-
isActive: true,
|
|
937
|
-
createdAt: Date.now(),
|
|
938
|
-
isGPT5: true,
|
|
939
|
-
validationStatus: 'valid',
|
|
940
|
-
lastValidation: Date.now(),
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
console.log(`✅ Created GPT-5 model profile: ${name} (${modelName})`)
|
|
944
|
-
return profile
|
|
945
|
-
}
|