@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/entrypoints/mcp.ts
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
|
2
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
3
|
-
import {
|
|
4
|
-
CallToolRequestSchema,
|
|
5
|
-
CallToolResultSchema,
|
|
6
|
-
ListToolsRequestSchema,
|
|
7
|
-
ListToolsResultSchema,
|
|
8
|
-
ToolSchema,
|
|
9
|
-
} from '@modelcontextprotocol/sdk/types.js'
|
|
10
|
-
import { z } from 'zod'
|
|
11
|
-
import { zodToJsonSchema } from 'zod-to-json-schema'
|
|
12
|
-
import { TaskTool } from '../tools/TaskTool/TaskTool'
|
|
13
|
-
import { hasPermissionsToUseTool } from '../permissions'
|
|
14
|
-
import { setCwd } from '../utils/state'
|
|
15
|
-
import { getModelManager } from '../utils/model'
|
|
16
|
-
import { logError } from '../utils/log'
|
|
17
|
-
import { LSTool } from '../tools/lsTool/lsTool'
|
|
18
|
-
import { BashTool } from '../tools/BashTool/BashTool'
|
|
19
|
-
import { FileEditTool } from '../tools/FileEditTool/FileEditTool'
|
|
20
|
-
import { FileReadTool } from '../tools/FileReadTool/FileReadTool'
|
|
21
|
-
import { GlobTool } from '../tools/GlobTool/GlobTool'
|
|
22
|
-
import { GrepTool } from '../tools/GrepTool/GrepTool'
|
|
23
|
-
import { FileWriteTool } from '../tools/FileWriteTool/FileWriteTool'
|
|
24
|
-
import { Tool } from '../Tool'
|
|
25
|
-
import { Command } from '../commands'
|
|
26
|
-
import review from '../commands/review'
|
|
27
|
-
import { lastX } from '../utils/generators'
|
|
28
|
-
import { MACRO } from '../constants/macros'
|
|
29
|
-
type ToolInput = z.infer<typeof ToolSchema.shape.inputSchema>
|
|
30
|
-
|
|
31
|
-
const state: {
|
|
32
|
-
readFileTimestamps: Record<string, number>
|
|
33
|
-
} = {
|
|
34
|
-
readFileTimestamps: {},
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const MCP_COMMANDS: Command[] = [review]
|
|
38
|
-
|
|
39
|
-
const MCP_TOOLS: Tool[] = [
|
|
40
|
-
TaskTool as unknown as Tool,
|
|
41
|
-
BashTool as unknown as Tool,
|
|
42
|
-
FileEditTool as unknown as Tool,
|
|
43
|
-
FileReadTool as unknown as Tool,
|
|
44
|
-
GlobTool as unknown as Tool,
|
|
45
|
-
GrepTool as unknown as Tool,
|
|
46
|
-
FileWriteTool as unknown as Tool,
|
|
47
|
-
LSTool as unknown as Tool,
|
|
48
|
-
]
|
|
49
|
-
|
|
50
|
-
export async function startMCPServer(cwd: string): Promise<void> {
|
|
51
|
-
await setCwd(cwd)
|
|
52
|
-
const server = new Server(
|
|
53
|
-
{
|
|
54
|
-
name: 'claude/tengu',
|
|
55
|
-
version: MACRO.VERSION,
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
capabilities: {
|
|
59
|
-
tools: {},
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
server.setRequestHandler(
|
|
65
|
-
ListToolsRequestSchema,
|
|
66
|
-
async (): Promise<z.infer<typeof ListToolsResultSchema>> => {
|
|
67
|
-
const tools = await Promise.all(
|
|
68
|
-
MCP_TOOLS.map(async tool => ({
|
|
69
|
-
...tool,
|
|
70
|
-
description: await tool.description(),
|
|
71
|
-
inputSchema: zodToJsonSchema(tool.inputSchema) as ToolInput,
|
|
72
|
-
})),
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
tools,
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
server.setRequestHandler(
|
|
82
|
-
CallToolRequestSchema,
|
|
83
|
-
async (request): Promise<z.infer<typeof CallToolResultSchema>> => {
|
|
84
|
-
const { name, arguments: args } = request.params
|
|
85
|
-
const tool = MCP_TOOLS.find(_ => _.name === name)
|
|
86
|
-
if (!tool) {
|
|
87
|
-
throw new Error(`Tool ${name} not found`)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// TODO: validate input types with zod
|
|
91
|
-
try {
|
|
92
|
-
if (!(await tool.isEnabled())) {
|
|
93
|
-
throw new Error(`Tool ${name} is not enabled`)
|
|
94
|
-
}
|
|
95
|
-
const model = getModelManager().getModelName('main')
|
|
96
|
-
const validationResult = await tool.validateInput?.(
|
|
97
|
-
(args as never) ?? {},
|
|
98
|
-
{
|
|
99
|
-
abortController: new AbortController(),
|
|
100
|
-
options: {
|
|
101
|
-
commands: MCP_COMMANDS,
|
|
102
|
-
tools: MCP_TOOLS,
|
|
103
|
-
forkNumber: 0,
|
|
104
|
-
messageLogName: 'unused',
|
|
105
|
-
maxThinkingTokens: 0,
|
|
106
|
-
},
|
|
107
|
-
messageId: undefined,
|
|
108
|
-
readFileTimestamps: state.readFileTimestamps,
|
|
109
|
-
},
|
|
110
|
-
)
|
|
111
|
-
if (validationResult && !validationResult.result) {
|
|
112
|
-
throw new Error(
|
|
113
|
-
`Tool ${name} input is invalid: ${validationResult.message}`,
|
|
114
|
-
)
|
|
115
|
-
}
|
|
116
|
-
const result = tool.call(
|
|
117
|
-
(args ?? {}) as never,
|
|
118
|
-
{
|
|
119
|
-
abortController: new AbortController(),
|
|
120
|
-
messageId: undefined,
|
|
121
|
-
options: {
|
|
122
|
-
commands: MCP_COMMANDS,
|
|
123
|
-
tools: MCP_TOOLS,
|
|
124
|
-
forkNumber: 0,
|
|
125
|
-
messageLogName: 'unused',
|
|
126
|
-
maxThinkingTokens: 0,
|
|
127
|
-
},
|
|
128
|
-
readFileTimestamps: state.readFileTimestamps,
|
|
129
|
-
},
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
const finalResult = await lastX(result)
|
|
133
|
-
|
|
134
|
-
if (finalResult.type !== 'result') {
|
|
135
|
-
throw new Error(`Tool ${name} did not return a result`)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
content: Array.isArray(finalResult)
|
|
140
|
-
? finalResult.map(item => ({
|
|
141
|
-
type: 'text' as const,
|
|
142
|
-
text: 'text' in item ? item.text : JSON.stringify(item),
|
|
143
|
-
}))
|
|
144
|
-
: [
|
|
145
|
-
{
|
|
146
|
-
type: 'text' as const,
|
|
147
|
-
text:
|
|
148
|
-
typeof finalResult === 'string'
|
|
149
|
-
? finalResult
|
|
150
|
-
: JSON.stringify(finalResult.data),
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
|
-
}
|
|
154
|
-
} catch (error) {
|
|
155
|
-
logError(error)
|
|
156
|
-
return {
|
|
157
|
-
isError: true,
|
|
158
|
-
content: [
|
|
159
|
-
{
|
|
160
|
-
type: 'text',
|
|
161
|
-
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
162
|
-
},
|
|
163
|
-
],
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
async function runServer() {
|
|
170
|
-
const transport = new StdioServerTransport()
|
|
171
|
-
await server.connect(transport)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return await runServer()
|
|
175
|
-
}
|
package/src/history.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getCurrentProjectConfig,
|
|
3
|
-
saveCurrentProjectConfig,
|
|
4
|
-
} from './utils/config.js'
|
|
5
|
-
|
|
6
|
-
const MAX_HISTORY_ITEMS = 100
|
|
7
|
-
|
|
8
|
-
export function getHistory(): string[] {
|
|
9
|
-
return getCurrentProjectConfig().history ?? []
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function addToHistory(command: string): void {
|
|
13
|
-
const projectConfig = getCurrentProjectConfig()
|
|
14
|
-
const history = projectConfig.history ?? []
|
|
15
|
-
|
|
16
|
-
if (history[0] === command) {
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
history.unshift(command)
|
|
21
|
-
saveCurrentProjectConfig({
|
|
22
|
-
...projectConfig,
|
|
23
|
-
history: history.slice(0, MAX_HISTORY_ITEMS),
|
|
24
|
-
})
|
|
25
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { useCallback, useState } from 'react'
|
|
2
|
-
import { verifyApiKey } from '../services/claude'
|
|
3
|
-
import { getAnthropicApiKey } from '../utils/config'
|
|
4
|
-
|
|
5
|
-
export type VerificationStatus =
|
|
6
|
-
| 'loading'
|
|
7
|
-
| 'valid'
|
|
8
|
-
| 'invalid'
|
|
9
|
-
| 'missing'
|
|
10
|
-
| 'error'
|
|
11
|
-
|
|
12
|
-
export type ApiKeyVerificationResult = {
|
|
13
|
-
status: VerificationStatus
|
|
14
|
-
reverify: () => Promise<void>
|
|
15
|
-
error: Error | null
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function useApiKeyVerification(): ApiKeyVerificationResult {
|
|
19
|
-
// const [status, setStatus] = useState<VerificationStatus>(() => {
|
|
20
|
-
// const apiKey = getAnthropicApiKey()
|
|
21
|
-
// return apiKey ? 'loading' : 'missing'
|
|
22
|
-
// })
|
|
23
|
-
// const [error, setError] = useState<Error | null>(null)
|
|
24
|
-
|
|
25
|
-
// const verify = useCallback(async (): Promise<void> => {
|
|
26
|
-
// if (isDefaultApiKey()) {
|
|
27
|
-
// setStatus('valid')
|
|
28
|
-
// return
|
|
29
|
-
// }
|
|
30
|
-
|
|
31
|
-
// const apiKey = getAnthropicApiKey()
|
|
32
|
-
// if (!apiKey) {
|
|
33
|
-
// const newStatus = 'missing' as const
|
|
34
|
-
// setStatus(newStatus)
|
|
35
|
-
// return
|
|
36
|
-
// }
|
|
37
|
-
|
|
38
|
-
// try {
|
|
39
|
-
// const isValid = await verifyApiKey(apiKey)
|
|
40
|
-
// const newStatus = isValid ? 'valid' : 'invalid'
|
|
41
|
-
// setStatus(newStatus)
|
|
42
|
-
// return
|
|
43
|
-
// } catch (error) {
|
|
44
|
-
// // This happens when there an error response from the API but it's not an invalid API key error
|
|
45
|
-
// // In this case, we still mark the API key as invalid - but we also log the error so we can
|
|
46
|
-
// // display it to the user to be more helpful
|
|
47
|
-
// setError(error as Error)
|
|
48
|
-
// const newStatus = 'error' as const
|
|
49
|
-
// setStatus(newStatus)
|
|
50
|
-
// return
|
|
51
|
-
// }
|
|
52
|
-
// }, [])
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
status: 'valid',
|
|
56
|
-
reverify: async () => {},
|
|
57
|
-
error: null,
|
|
58
|
-
}
|
|
59
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react'
|
|
2
|
-
import { getHistory } from '../history'
|
|
3
|
-
|
|
4
|
-
export function useArrowKeyHistory(
|
|
5
|
-
onSetInput: (value: string, mode: 'bash' | 'prompt') => void,
|
|
6
|
-
currentInput: string,
|
|
7
|
-
) {
|
|
8
|
-
const [historyIndex, setHistoryIndex] = useState(0)
|
|
9
|
-
const [lastTypedInput, setLastTypedInput] = useState('')
|
|
10
|
-
|
|
11
|
-
const updateInput = (input: string | undefined) => {
|
|
12
|
-
if (input !== undefined) {
|
|
13
|
-
const mode = input.startsWith('!') ? 'bash' : 'prompt'
|
|
14
|
-
const value = mode === 'bash' ? input.slice(1) : input
|
|
15
|
-
onSetInput(value, mode)
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function onHistoryUp() {
|
|
20
|
-
const latestHistory = getHistory()
|
|
21
|
-
if (historyIndex < latestHistory.length) {
|
|
22
|
-
if (historyIndex === 0 && currentInput.trim() !== '') {
|
|
23
|
-
setLastTypedInput(currentInput)
|
|
24
|
-
}
|
|
25
|
-
const newIndex = historyIndex + 1
|
|
26
|
-
setHistoryIndex(newIndex)
|
|
27
|
-
updateInput(latestHistory[historyIndex])
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function onHistoryDown() {
|
|
32
|
-
const latestHistory = getHistory()
|
|
33
|
-
if (historyIndex > 1) {
|
|
34
|
-
const newIndex = historyIndex - 1
|
|
35
|
-
setHistoryIndex(newIndex)
|
|
36
|
-
updateInput(latestHistory[newIndex - 1])
|
|
37
|
-
} else if (historyIndex === 1) {
|
|
38
|
-
setHistoryIndex(0)
|
|
39
|
-
updateInput(lastTypedInput)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function resetHistory() {
|
|
44
|
-
setLastTypedInput('')
|
|
45
|
-
setHistoryIndex(0)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
historyIndex,
|
|
50
|
-
setHistoryIndex,
|
|
51
|
-
onHistoryUp,
|
|
52
|
-
onHistoryDown,
|
|
53
|
-
resetHistory,
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import React, { useCallback } from 'react'
|
|
2
|
-
import { hasPermissionsToUseTool } from '../permissions'
|
|
3
|
-
import { logEvent } from '../services/statsig'
|
|
4
|
-
import { BashTool, inputSchema } from '../tools/BashTool/BashTool'
|
|
5
|
-
import { getCommandSubcommandPrefix } from '../utils/commands'
|
|
6
|
-
import { REJECT_MESSAGE } from '../utils/messages'
|
|
7
|
-
import type { Tool as ToolType, ToolUseContext } from '../Tool'
|
|
8
|
-
import { AssistantMessage } from '../query'
|
|
9
|
-
import { ToolUseConfirm } from '../components/permissions/PermissionRequest'
|
|
10
|
-
import { AbortError } from '../utils/errors'
|
|
11
|
-
import { logError } from '../utils/log'
|
|
12
|
-
|
|
13
|
-
type SetState<T> = React.Dispatch<React.SetStateAction<T>>
|
|
14
|
-
|
|
15
|
-
export type CanUseToolFn = (
|
|
16
|
-
tool: ToolType,
|
|
17
|
-
input: { [key: string]: unknown },
|
|
18
|
-
toolUseContext: ToolUseContext,
|
|
19
|
-
assistantMessage: AssistantMessage,
|
|
20
|
-
) => Promise<{ result: true } | { result: false; message: string }>
|
|
21
|
-
|
|
22
|
-
function useCanUseTool(
|
|
23
|
-
setToolUseConfirm: SetState<ToolUseConfirm | null>,
|
|
24
|
-
): CanUseToolFn {
|
|
25
|
-
return useCallback<CanUseToolFn>(
|
|
26
|
-
async (tool, input, toolUseContext, assistantMessage) => {
|
|
27
|
-
return new Promise(resolve => {
|
|
28
|
-
function logCancelledEvent() {
|
|
29
|
-
logEvent('tengu_tool_use_cancelled', {
|
|
30
|
-
messageID: assistantMessage.message.id,
|
|
31
|
-
toolName: tool.name,
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function resolveWithCancelledAndAbortAllToolCalls() {
|
|
36
|
-
resolve({
|
|
37
|
-
result: false,
|
|
38
|
-
message: REJECT_MESSAGE,
|
|
39
|
-
})
|
|
40
|
-
// Trigger a synthetic assistant message in query(), to cancel
|
|
41
|
-
// any other pending tool uses and stop further requests to the
|
|
42
|
-
// API and wait for user input.
|
|
43
|
-
toolUseContext.abortController.abort()
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (toolUseContext.abortController.signal.aborted) {
|
|
47
|
-
logCancelledEvent()
|
|
48
|
-
resolveWithCancelledAndAbortAllToolCalls()
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return hasPermissionsToUseTool(
|
|
53
|
-
tool,
|
|
54
|
-
input,
|
|
55
|
-
toolUseContext,
|
|
56
|
-
assistantMessage,
|
|
57
|
-
)
|
|
58
|
-
.then(async result => {
|
|
59
|
-
// Has permissions to use tool, granted in config
|
|
60
|
-
if (result.result) {
|
|
61
|
-
logEvent('tengu_tool_use_granted_in_config', {
|
|
62
|
-
messageID: assistantMessage.message.id,
|
|
63
|
-
toolName: tool.name,
|
|
64
|
-
})
|
|
65
|
-
resolve({ result: true })
|
|
66
|
-
return
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const [description, commandPrefix] = await Promise.all([
|
|
70
|
-
tool.description(input as never),
|
|
71
|
-
tool === BashTool
|
|
72
|
-
? getCommandSubcommandPrefix(
|
|
73
|
-
inputSchema.parse(input).command, // already validated upstream, so ok to parse (as opposed to safeParse)
|
|
74
|
-
toolUseContext.abortController.signal,
|
|
75
|
-
)
|
|
76
|
-
: Promise.resolve(null),
|
|
77
|
-
])
|
|
78
|
-
|
|
79
|
-
if (toolUseContext.abortController.signal.aborted) {
|
|
80
|
-
logCancelledEvent()
|
|
81
|
-
resolveWithCancelledAndAbortAllToolCalls()
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Does not have permissions to use tool, ask the user
|
|
86
|
-
setToolUseConfirm({
|
|
87
|
-
assistantMessage,
|
|
88
|
-
tool,
|
|
89
|
-
description,
|
|
90
|
-
input,
|
|
91
|
-
commandPrefix,
|
|
92
|
-
riskScore: null,
|
|
93
|
-
onAbort() {
|
|
94
|
-
logCancelledEvent()
|
|
95
|
-
logEvent('tengu_tool_use_rejected_in_prompt', {
|
|
96
|
-
messageID: assistantMessage.message.id,
|
|
97
|
-
toolName: tool.name,
|
|
98
|
-
})
|
|
99
|
-
resolveWithCancelledAndAbortAllToolCalls()
|
|
100
|
-
},
|
|
101
|
-
onAllow(type) {
|
|
102
|
-
if (type === 'permanent') {
|
|
103
|
-
logEvent('tengu_tool_use_granted_in_prompt_permanent', {
|
|
104
|
-
messageID: assistantMessage.message.id,
|
|
105
|
-
toolName: tool.name,
|
|
106
|
-
})
|
|
107
|
-
} else {
|
|
108
|
-
logEvent('tengu_tool_use_granted_in_prompt_temporary', {
|
|
109
|
-
messageID: assistantMessage.message.id,
|
|
110
|
-
toolName: tool.name,
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
resolve({ result: true })
|
|
114
|
-
},
|
|
115
|
-
onReject() {
|
|
116
|
-
logEvent('tengu_tool_use_rejected_in_prompt', {
|
|
117
|
-
messageID: assistantMessage.message.id,
|
|
118
|
-
toolName: tool.name,
|
|
119
|
-
})
|
|
120
|
-
resolveWithCancelledAndAbortAllToolCalls()
|
|
121
|
-
},
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
.catch(error => {
|
|
125
|
-
if (error instanceof AbortError) {
|
|
126
|
-
logCancelledEvent()
|
|
127
|
-
resolveWithCancelledAndAbortAllToolCalls()
|
|
128
|
-
} else {
|
|
129
|
-
logError(error)
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
})
|
|
133
|
-
},
|
|
134
|
-
[setToolUseConfirm],
|
|
135
|
-
)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export default useCanUseTool
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { useInput } from 'ink'
|
|
2
|
-
import { ToolUseConfirm } from '../components/permissions/PermissionRequest'
|
|
3
|
-
import { logEvent } from '../services/statsig'
|
|
4
|
-
import { BinaryFeedbackContext } from '../screens/REPL'
|
|
5
|
-
import type { SetToolJSXFn } from '../Tool'
|
|
6
|
-
|
|
7
|
-
export function useCancelRequest(
|
|
8
|
-
setToolJSX: SetToolJSXFn,
|
|
9
|
-
setToolUseConfirm: (toolUseConfirm: ToolUseConfirm | null) => void,
|
|
10
|
-
setBinaryFeedbackContext: (bfContext: BinaryFeedbackContext | null) => void,
|
|
11
|
-
onCancel: () => void,
|
|
12
|
-
isLoading: boolean,
|
|
13
|
-
isMessageSelectorVisible: boolean,
|
|
14
|
-
abortSignal?: AbortSignal,
|
|
15
|
-
) {
|
|
16
|
-
useInput((_, key) => {
|
|
17
|
-
if (!key.escape) {
|
|
18
|
-
return
|
|
19
|
-
}
|
|
20
|
-
if (abortSignal?.aborted) {
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
if (!abortSignal) {
|
|
24
|
-
return
|
|
25
|
-
}
|
|
26
|
-
if (!isLoading) {
|
|
27
|
-
return
|
|
28
|
-
}
|
|
29
|
-
if (isMessageSelectorVisible) {
|
|
30
|
-
// Esc closes the message selector
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
logEvent('tengu_cancel', {})
|
|
34
|
-
setToolJSX(null)
|
|
35
|
-
setToolUseConfirm(null)
|
|
36
|
-
setBinaryFeedbackContext(null)
|
|
37
|
-
onCancel()
|
|
38
|
-
})
|
|
39
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
// Creates a function that calls one function on the first call and another
|
|
2
|
-
// function on the second call within a certain timeout
|
|
3
|
-
|
|
4
|
-
import { useRef } from 'react'
|
|
5
|
-
|
|
6
|
-
export const DOUBLE_PRESS_TIMEOUT_MS = 2000
|
|
7
|
-
|
|
8
|
-
export function useDoublePress(
|
|
9
|
-
setPending: (pending: boolean) => void,
|
|
10
|
-
onDoublePress: () => void,
|
|
11
|
-
onFirstPress?: () => void,
|
|
12
|
-
): () => void {
|
|
13
|
-
const lastPressRef = useRef<number>(0)
|
|
14
|
-
const timeoutRef = useRef<NodeJS.Timeout>()
|
|
15
|
-
|
|
16
|
-
return () => {
|
|
17
|
-
const now = Date.now()
|
|
18
|
-
const timeSinceLastPress = now - lastPressRef.current
|
|
19
|
-
|
|
20
|
-
// For this to count as a double-call, be sure to check that
|
|
21
|
-
// timeoutRef.current exists so we don't trigger on triple call
|
|
22
|
-
// (e.g. of Esc to clear the text input)
|
|
23
|
-
if (timeSinceLastPress <= DOUBLE_PRESS_TIMEOUT_MS && timeoutRef.current) {
|
|
24
|
-
if (timeoutRef.current) {
|
|
25
|
-
clearTimeout(timeoutRef.current)
|
|
26
|
-
timeoutRef.current = undefined
|
|
27
|
-
}
|
|
28
|
-
onDoublePress()
|
|
29
|
-
setPending(false)
|
|
30
|
-
} else {
|
|
31
|
-
onFirstPress?.()
|
|
32
|
-
setPending(true)
|
|
33
|
-
timeoutRef.current = setTimeout(
|
|
34
|
-
() => setPending(false),
|
|
35
|
-
DOUBLE_PRESS_TIMEOUT_MS,
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
lastPressRef.current = now
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { useInput } from 'ink'
|
|
2
|
-
import { useDoublePress } from './useDoublePress'
|
|
3
|
-
import { useState } from 'react'
|
|
4
|
-
|
|
5
|
-
type ExitState = {
|
|
6
|
-
pending: boolean
|
|
7
|
-
keyName: 'Ctrl-C' | 'Ctrl-D' | null
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function useExitOnCtrlCD(onExit: () => void): ExitState {
|
|
11
|
-
const [exitState, setExitState] = useState<ExitState>({
|
|
12
|
-
pending: false,
|
|
13
|
-
keyName: null,
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
const handleCtrlC = useDoublePress(
|
|
17
|
-
pending => setExitState({ pending, keyName: 'Ctrl-C' }),
|
|
18
|
-
onExit,
|
|
19
|
-
)
|
|
20
|
-
const handleCtrlD = useDoublePress(
|
|
21
|
-
pending => setExitState({ pending, keyName: 'Ctrl-D' }),
|
|
22
|
-
onExit,
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
useInput((input, key) => {
|
|
26
|
-
if (key.ctrl && input === 'c') handleCtrlC()
|
|
27
|
-
if (key.ctrl && input === 'd') handleCtrlD()
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
return exitState
|
|
31
|
-
}
|
package/src/hooks/useInterval.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A custom hook that runs a callback at a specified interval.
|
|
5
|
-
* The interval is cleared when the component unmounts.
|
|
6
|
-
* The interval is also cleared and restarted if the delay changes.
|
|
7
|
-
*/
|
|
8
|
-
export function useInterval(callback: () => void, delay: number): void {
|
|
9
|
-
const savedCallback = useRef(callback)
|
|
10
|
-
|
|
11
|
-
// Remember the latest callback
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
savedCallback.current = callback
|
|
14
|
-
}, [callback])
|
|
15
|
-
|
|
16
|
-
// Set up the interval
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
function tick() {
|
|
19
|
-
savedCallback.current()
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const id = setInterval(tick, delay)
|
|
23
|
-
return () => clearInterval(id)
|
|
24
|
-
}, [delay])
|
|
25
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react'
|
|
2
|
-
import { type Message } from '../query'
|
|
3
|
-
import { overwriteLog, getMessagesPath } from '../utils/log'
|
|
4
|
-
|
|
5
|
-
export function useLogMessages(
|
|
6
|
-
messages: Message[],
|
|
7
|
-
messageLogName: string,
|
|
8
|
-
forkNumber: number,
|
|
9
|
-
): void {
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
overwriteLog(
|
|
12
|
-
getMessagesPath(messageLogName, forkNumber, 0),
|
|
13
|
-
messages.filter(_ => _.type !== 'progress'),
|
|
14
|
-
)
|
|
15
|
-
}, [messages, messageLogName, forkNumber])
|
|
16
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react'
|
|
2
|
-
import { logEvent } from '../services/statsig'
|
|
3
|
-
|
|
4
|
-
export function useLogStartupTime(): void {
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
const startupTimeMs = Math.round(process.uptime() * 1000)
|
|
7
|
-
logEvent('tengu_timer', {
|
|
8
|
-
event: 'startup',
|
|
9
|
-
durationMs: String(startupTimeMs),
|
|
10
|
-
})
|
|
11
|
-
}, [])
|
|
12
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react'
|
|
2
|
-
import { sendNotification } from '../services/notifier'
|
|
3
|
-
import { memoize } from 'lodash-es'
|
|
4
|
-
|
|
5
|
-
// The time threshold in milliseconds for considering an interaction "recent" (6 seconds)
|
|
6
|
-
const DEFAULT_INTERACTION_THRESHOLD_MS = 6000
|
|
7
|
-
|
|
8
|
-
const STATE = {
|
|
9
|
-
lastInteractionTime: Date.now(),
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function updateLastInteractionTime(): void {
|
|
13
|
-
STATE.lastInteractionTime = Date.now()
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function getTimeSinceLastInteraction(): number {
|
|
17
|
-
return Date.now() - STATE.lastInteractionTime
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function hasRecentInteraction(threshold: number): boolean {
|
|
21
|
-
return getTimeSinceLastInteraction() < threshold
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function shouldNotify(threshold: number): boolean {
|
|
25
|
-
return process.env.NODE_ENV !== 'test' && !hasRecentInteraction(threshold)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Start tracking the time of the user's last interaction with the app
|
|
29
|
-
const init = memoize(() => process.stdin.on('data', updateLastInteractionTime))
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Hook that manages desktop notifications after a timeout period.
|
|
33
|
-
*
|
|
34
|
-
* Shows a notification in two cases:
|
|
35
|
-
* 1. Immediately if the app has been idle for longer than the threshold
|
|
36
|
-
* 2. After the specified timeout if the user doesn't interact within that time
|
|
37
|
-
*
|
|
38
|
-
* @param message - The notification message to display
|
|
39
|
-
* @param timeout - The timeout in milliseconds (defaults to 6000ms)
|
|
40
|
-
*/
|
|
41
|
-
export function useNotifyAfterTimeout(
|
|
42
|
-
message: string,
|
|
43
|
-
timeout: number = DEFAULT_INTERACTION_THRESHOLD_MS,
|
|
44
|
-
): void {
|
|
45
|
-
// Reset interaction time when hook is called to make sure that requests
|
|
46
|
-
// that took a long time to complete don't pop up a notification right away
|
|
47
|
-
useEffect(() => {
|
|
48
|
-
init()
|
|
49
|
-
updateLastInteractionTime()
|
|
50
|
-
}, [])
|
|
51
|
-
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
let hasNotified = false
|
|
54
|
-
const timer = setInterval(() => {
|
|
55
|
-
if (shouldNotify(timeout) && !hasNotified) {
|
|
56
|
-
hasNotified = true
|
|
57
|
-
sendNotification({
|
|
58
|
-
message,
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
}, timeout)
|
|
62
|
-
|
|
63
|
-
return () => clearTimeout(timer)
|
|
64
|
-
}, [message, timeout])
|
|
65
|
-
}
|