wave-code 0.6.5 → 0.7.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/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/commands/plugin/disable.d.ts.map +1 -1
- package/dist/commands/plugin/disable.js +3 -10
- package/dist/commands/plugin/enable.d.ts.map +1 -1
- package/dist/commands/plugin/enable.js +3 -10
- package/dist/commands/plugin/install.d.ts.map +1 -1
- package/dist/commands/plugin/install.js +4 -11
- package/dist/commands/plugin/list.d.ts.map +1 -1
- package/dist/commands/plugin/list.js +5 -39
- package/dist/commands/plugin/marketplace.js +9 -9
- package/dist/commands/plugin/uninstall.d.ts.map +1 -1
- package/dist/commands/plugin/uninstall.js +4 -17
- package/dist/commands/plugin/update.js +3 -3
- package/dist/components/App.d.ts +1 -0
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +4 -4
- package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
- package/dist/components/BackgroundTaskManager.js +34 -18
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +28 -15
- package/dist/components/ConfirmationDetails.d.ts +1 -0
- package/dist/components/ConfirmationDetails.d.ts.map +1 -1
- package/dist/components/ConfirmationDetails.js +7 -14
- package/dist/components/ConfirmationSelector.d.ts +1 -0
- package/dist/components/ConfirmationSelector.d.ts.map +1 -1
- package/dist/components/ConfirmationSelector.js +164 -117
- package/dist/components/DiffDisplay.d.ts +1 -0
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +94 -36
- package/dist/components/HistorySearch.d.ts.map +1 -1
- package/dist/components/HistorySearch.js +26 -20
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/Markdown.js +3 -1
- package/dist/components/McpManager.d.ts.map +1 -1
- package/dist/components/McpManager.js +49 -52
- package/dist/components/MessageBlockItem.d.ts +9 -0
- package/dist/components/MessageBlockItem.d.ts.map +1 -0
- package/dist/components/MessageBlockItem.js +11 -0
- package/dist/components/MessageList.d.ts +2 -4
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +28 -23
- package/dist/components/PluginDetail.d.ts.map +1 -1
- package/dist/components/PluginDetail.js +19 -22
- package/dist/components/SessionSelector.d.ts.map +1 -1
- package/dist/components/SessionSelector.js +8 -5
- package/dist/components/TaskList.d.ts.map +1 -1
- package/dist/components/TaskList.js +2 -5
- package/dist/components/ToolDisplay.d.ts.map +1 -1
- package/dist/components/ToolDisplay.js +1 -1
- package/dist/contexts/useChat.d.ts +1 -0
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +20 -3
- package/dist/hooks/usePluginManager.d.ts.map +1 -1
- package/dist/hooks/usePluginManager.js +20 -39
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -0
- package/dist/print-cli.d.ts +1 -0
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +2 -1
- package/dist/utils/highlightUtils.d.ts +2 -0
- package/dist/utils/highlightUtils.d.ts.map +1 -0
- package/dist/utils/highlightUtils.js +69 -0
- package/dist/utils/toolParameterTransforms.d.ts +2 -6
- package/dist/utils/toolParameterTransforms.d.ts.map +1 -1
- package/dist/utils/toolParameterTransforms.js +10 -14
- package/package.json +4 -2
- package/src/cli.tsx +3 -0
- package/src/commands/plugin/disable.ts +3 -17
- package/src/commands/plugin/enable.ts +3 -17
- package/src/commands/plugin/install.ts +4 -18
- package/src/commands/plugin/list.ts +5 -55
- package/src/commands/plugin/marketplace.ts +9 -9
- package/src/commands/plugin/uninstall.ts +4 -26
- package/src/commands/plugin/update.ts +3 -3
- package/src/components/App.tsx +10 -2
- package/src/components/BackgroundTaskManager.tsx +69 -44
- package/src/components/ChatInterface.tsx +35 -23
- package/src/components/ConfirmationDetails.tsx +13 -15
- package/src/components/ConfirmationSelector.tsx +207 -128
- package/src/components/DiffDisplay.tsx +164 -75
- package/src/components/HistorySearch.tsx +31 -25
- package/src/components/Markdown.tsx +3 -1
- package/src/components/McpManager.tsx +51 -59
- package/src/components/MessageBlockItem.tsx +83 -0
- package/src/components/MessageList.tsx +55 -52
- package/src/components/PluginDetail.tsx +30 -31
- package/src/components/SessionSelector.tsx +8 -5
- package/src/components/TaskList.tsx +2 -5
- package/src/components/ToolDisplay.tsx +5 -1
- package/src/contexts/useChat.tsx +22 -2
- package/src/hooks/usePluginManager.ts +21 -57
- package/src/index.ts +17 -0
- package/src/print-cli.ts +3 -0
- package/src/utils/highlightUtils.ts +76 -0
- package/src/utils/toolParameterTransforms.ts +11 -20
- package/dist/components/MessageItem.d.ts +0 -8
- package/dist/components/MessageItem.d.ts.map +0 -1
- package/dist/components/MessageItem.js +0 -13
- package/src/components/MessageItem.tsx +0 -81
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import type { Message, MessageBlock } from "wave-agent-sdk";
|
|
4
|
+
import { MessageSource } from "wave-agent-sdk";
|
|
5
|
+
import { CommandOutputDisplay } from "./CommandOutputDisplay.js";
|
|
6
|
+
import { ToolDisplay } from "./ToolDisplay.js";
|
|
7
|
+
import { CompressDisplay } from "./CompressDisplay.js";
|
|
8
|
+
import { ReasoningDisplay } from "./ReasoningDisplay.js";
|
|
9
|
+
import { Markdown } from "./Markdown.js";
|
|
10
|
+
|
|
11
|
+
export interface MessageBlockItemProps {
|
|
12
|
+
block: MessageBlock;
|
|
13
|
+
message: Message;
|
|
14
|
+
isExpanded: boolean;
|
|
15
|
+
paddingTop?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const MessageBlockItem = ({
|
|
19
|
+
block,
|
|
20
|
+
message,
|
|
21
|
+
isExpanded,
|
|
22
|
+
paddingTop = 0,
|
|
23
|
+
}: MessageBlockItemProps) => {
|
|
24
|
+
return (
|
|
25
|
+
<Box flexDirection="column" paddingTop={paddingTop}>
|
|
26
|
+
{block.type === "text" && block.content.trim() && (
|
|
27
|
+
<Box>
|
|
28
|
+
{block.customCommandContent && (
|
|
29
|
+
<Text color="cyan" bold>
|
|
30
|
+
${" "}
|
|
31
|
+
</Text>
|
|
32
|
+
)}
|
|
33
|
+
{block.source === MessageSource.HOOK && (
|
|
34
|
+
<Text color="magenta" bold>
|
|
35
|
+
~{" "}
|
|
36
|
+
</Text>
|
|
37
|
+
)}
|
|
38
|
+
{message.role === "user" ? (
|
|
39
|
+
<Text backgroundColor="gray" color="white">
|
|
40
|
+
{block.content}
|
|
41
|
+
</Text>
|
|
42
|
+
) : (
|
|
43
|
+
<Markdown>{block.content}</Markdown>
|
|
44
|
+
)}
|
|
45
|
+
</Box>
|
|
46
|
+
)}
|
|
47
|
+
|
|
48
|
+
{block.type === "error" && (
|
|
49
|
+
<Box>
|
|
50
|
+
<Text color="red">Error: {block.content}</Text>
|
|
51
|
+
</Box>
|
|
52
|
+
)}
|
|
53
|
+
|
|
54
|
+
{block.type === "command_output" && (
|
|
55
|
+
<CommandOutputDisplay block={block} isExpanded={isExpanded} />
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
{block.type === "tool" && (
|
|
59
|
+
<ToolDisplay block={block} isExpanded={isExpanded} />
|
|
60
|
+
)}
|
|
61
|
+
|
|
62
|
+
{block.type === "image" && (
|
|
63
|
+
<Box>
|
|
64
|
+
<Text color="magenta" bold>
|
|
65
|
+
# Image
|
|
66
|
+
</Text>
|
|
67
|
+
{block.imageUrls && block.imageUrls.length > 0 && (
|
|
68
|
+
<Text color="gray" dimColor>
|
|
69
|
+
{" "}
|
|
70
|
+
({block.imageUrls.length})
|
|
71
|
+
</Text>
|
|
72
|
+
)}
|
|
73
|
+
</Box>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
{block.type === "compress" && (
|
|
77
|
+
<CompressDisplay block={block} isExpanded={isExpanded} />
|
|
78
|
+
)}
|
|
79
|
+
|
|
80
|
+
{block.type === "reasoning" && <ReasoningDisplay block={block} />}
|
|
81
|
+
</Box>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Text, Static } from "ink";
|
|
3
3
|
import type { Message } from "wave-agent-sdk";
|
|
4
|
-
import {
|
|
4
|
+
import { MessageBlockItem } from "./MessageBlockItem.js";
|
|
5
5
|
|
|
6
6
|
export interface MessageListProps {
|
|
7
7
|
messages: Message[];
|
|
8
|
-
isLoading?: boolean;
|
|
9
|
-
isCommandRunning?: boolean;
|
|
10
8
|
isExpanded?: boolean;
|
|
11
|
-
|
|
9
|
+
hideDynamicBlocks?: boolean;
|
|
12
10
|
}
|
|
13
11
|
|
|
14
12
|
export const MessageList = React.memo(
|
|
15
13
|
({
|
|
16
14
|
messages,
|
|
17
|
-
isLoading = false,
|
|
18
|
-
isCommandRunning = false,
|
|
19
15
|
isExpanded = false,
|
|
20
|
-
|
|
16
|
+
hideDynamicBlocks = false,
|
|
21
17
|
}: MessageListProps) => {
|
|
22
18
|
// Empty message state
|
|
23
19
|
if (messages.length === 0) {
|
|
@@ -38,59 +34,66 @@ export const MessageList = React.memo(
|
|
|
38
34
|
? messages.slice(-maxExpandedMessages)
|
|
39
35
|
: messages;
|
|
40
36
|
|
|
41
|
-
//
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
37
|
+
// Flatten messages into blocks with metadata
|
|
38
|
+
const allBlocks = displayMessages.flatMap((message, index) => {
|
|
39
|
+
const messageIndex = shouldLimitMessages
|
|
40
|
+
? messages.length - maxExpandedMessages + index
|
|
41
|
+
: index;
|
|
42
|
+
return message.blocks.map((block, blockIndex) => ({
|
|
43
|
+
block,
|
|
44
|
+
message,
|
|
45
|
+
isLastMessage: messageIndex === messages.length - 1,
|
|
46
|
+
// Unique key for each block to help Static component
|
|
47
|
+
key: `${message.id || messageIndex}-${blockIndex}`,
|
|
48
|
+
}));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Determine which blocks are static vs dynamic
|
|
52
|
+
const blocksWithStatus = allBlocks.map((item) => {
|
|
53
|
+
const { block, isLastMessage } = item;
|
|
54
|
+
const isDynamic =
|
|
55
|
+
isLastMessage &&
|
|
56
|
+
((block.type === "tool" && block.stage !== "end") ||
|
|
57
|
+
(block.type === "command_output" && block.isRunning));
|
|
58
|
+
return { ...item, isDynamic };
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const staticBlocks = blocksWithStatus.filter((b) => !b.isDynamic);
|
|
62
|
+
const dynamicBlocks = hideDynamicBlocks
|
|
63
|
+
? []
|
|
64
|
+
: blocksWithStatus.filter((b) => b.isDynamic);
|
|
59
65
|
|
|
60
66
|
return (
|
|
61
67
|
<Box flexDirection="column" paddingBottom={1}>
|
|
62
|
-
{/* Static
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
key={key}
|
|
71
|
-
message={message}
|
|
72
|
-
shouldShowHeader={previousMessage?.role !== message.role}
|
|
68
|
+
{/* Static blocks */}
|
|
69
|
+
{staticBlocks.length > 0 && (
|
|
70
|
+
<Static items={staticBlocks}>
|
|
71
|
+
{(item) => (
|
|
72
|
+
<MessageBlockItem
|
|
73
|
+
key={item.key}
|
|
74
|
+
block={item.block}
|
|
75
|
+
message={item.message}
|
|
73
76
|
isExpanded={isExpanded}
|
|
77
|
+
paddingTop={1}
|
|
74
78
|
/>
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
)}
|
|
80
|
+
</Static>
|
|
81
|
+
)}
|
|
78
82
|
|
|
79
|
-
{/* Dynamic
|
|
80
|
-
{
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
message={message}
|
|
88
|
-
shouldShowHeader={previousMessage?.role !== message.role}
|
|
83
|
+
{/* Dynamic blocks */}
|
|
84
|
+
{dynamicBlocks.length > 0 && (
|
|
85
|
+
<Box flexDirection="column">
|
|
86
|
+
{dynamicBlocks.map((item) => (
|
|
87
|
+
<MessageBlockItem
|
|
88
|
+
key={item.key}
|
|
89
|
+
block={item.block}
|
|
90
|
+
message={item.message}
|
|
89
91
|
isExpanded={isExpanded}
|
|
92
|
+
paddingTop={1}
|
|
90
93
|
/>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
))}
|
|
95
|
+
</Box>
|
|
96
|
+
)}
|
|
94
97
|
</Box>
|
|
95
98
|
);
|
|
96
99
|
},
|
|
@@ -36,42 +36,41 @@ export const PluginDetail: React.FC = () => {
|
|
|
36
36
|
);
|
|
37
37
|
actions.setView(isFromDiscover ? "DISCOVER" : "INSTALLED");
|
|
38
38
|
} else if (key.upArrow) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
prev > 0 ? prev - 1 : SCOPES.length - 1,
|
|
46
|
-
);
|
|
47
|
-
}
|
|
39
|
+
setSelectedActionIndex((prev) =>
|
|
40
|
+
prev > 0 ? prev - 1 : INSTALLED_ACTIONS.length - 1,
|
|
41
|
+
);
|
|
42
|
+
setSelectedScopeIndex((prev) =>
|
|
43
|
+
prev > 0 ? prev - 1 : SCOPES.length - 1,
|
|
44
|
+
);
|
|
48
45
|
} else if (key.downArrow) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
prev < SCOPES.length - 1 ? prev + 1 : 0,
|
|
56
|
-
);
|
|
57
|
-
}
|
|
46
|
+
setSelectedActionIndex((prev) =>
|
|
47
|
+
prev < INSTALLED_ACTIONS.length - 1 ? prev + 1 : 0,
|
|
48
|
+
);
|
|
49
|
+
setSelectedScopeIndex((prev) =>
|
|
50
|
+
prev < SCOPES.length - 1 ? prev + 1 : 0,
|
|
51
|
+
);
|
|
58
52
|
} else if (key.return && plugin) {
|
|
59
53
|
if (isInstalledAndEnabled) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
setSelectedActionIndex((prev) => {
|
|
55
|
+
const action = INSTALLED_ACTIONS[prev].id;
|
|
56
|
+
if (action === "uninstall") {
|
|
57
|
+
actions.uninstallPlugin(plugin.name, plugin.marketplace);
|
|
58
|
+
} else {
|
|
59
|
+
actions.updatePlugin(plugin.name, plugin.marketplace);
|
|
60
|
+
}
|
|
61
|
+
return prev;
|
|
62
|
+
});
|
|
67
63
|
} else {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
setSelectedScopeIndex((prev) => {
|
|
65
|
+
actions.installPlugin(
|
|
66
|
+
plugin.name,
|
|
67
|
+
plugin.marketplace,
|
|
68
|
+
SCOPES[prev].id,
|
|
69
|
+
);
|
|
70
|
+
return prev;
|
|
71
|
+
});
|
|
74
72
|
}
|
|
73
|
+
actions.setView("INSTALLED");
|
|
75
74
|
}
|
|
76
75
|
});
|
|
77
76
|
|
|
@@ -17,9 +17,12 @@ export const SessionSelector: React.FC<SessionSelectorProps> = ({
|
|
|
17
17
|
|
|
18
18
|
useInput((input, key) => {
|
|
19
19
|
if (key.return) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
setSelectedIndex((prev) => {
|
|
21
|
+
if (sessions.length > 0 && prev < sessions.length) {
|
|
22
|
+
onSelect(sessions[prev].id);
|
|
23
|
+
}
|
|
24
|
+
return prev;
|
|
25
|
+
});
|
|
23
26
|
return;
|
|
24
27
|
}
|
|
25
28
|
|
|
@@ -29,12 +32,12 @@ export const SessionSelector: React.FC<SessionSelectorProps> = ({
|
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
if (key.upArrow) {
|
|
32
|
-
setSelectedIndex(Math.max(0,
|
|
35
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
33
36
|
return;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
if (key.downArrow) {
|
|
37
|
-
setSelectedIndex(Math.min(sessions.length - 1,
|
|
40
|
+
setSelectedIndex((prev) => Math.min(sessions.length - 1, prev + 1));
|
|
38
41
|
return;
|
|
39
42
|
}
|
|
40
43
|
});
|
|
@@ -11,10 +11,7 @@ export const TaskList: React.FC = () => {
|
|
|
11
11
|
return null;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const getStatusIcon = (status: string
|
|
15
|
-
if (isBlocked) {
|
|
16
|
-
return <Text color="red">🔒</Text>;
|
|
17
|
-
}
|
|
14
|
+
const getStatusIcon = (status: string) => {
|
|
18
15
|
switch (status) {
|
|
19
16
|
case "pending":
|
|
20
17
|
return <Text color="gray">□</Text>;
|
|
@@ -48,7 +45,7 @@ export const TaskList: React.FC = () => {
|
|
|
48
45
|
|
|
49
46
|
return (
|
|
50
47
|
<Box key={task.id} gap={1}>
|
|
51
|
-
{getStatusIcon(task.status
|
|
48
|
+
{getStatusIcon(task.status)}
|
|
52
49
|
<Text dimColor={isDimmed}>{fullText}</Text>
|
|
53
50
|
</Box>
|
|
54
51
|
);
|
|
@@ -131,7 +131,11 @@ export const ToolDisplay: React.FC<ToolDisplayProps> = ({
|
|
|
131
131
|
|
|
132
132
|
{/* Diff display - only show after tool execution completes and was successful */}
|
|
133
133
|
{stage === "end" && success && (
|
|
134
|
-
<DiffDisplay
|
|
134
|
+
<DiffDisplay
|
|
135
|
+
toolName={name}
|
|
136
|
+
parameters={parameters}
|
|
137
|
+
startLineNumber={block.startLineNumber}
|
|
138
|
+
/>
|
|
135
139
|
)}
|
|
136
140
|
</Box>
|
|
137
141
|
);
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -108,12 +108,14 @@ export interface ChatProviderProps {
|
|
|
108
108
|
children: React.ReactNode;
|
|
109
109
|
bypassPermissions?: boolean;
|
|
110
110
|
pluginDirs?: string[];
|
|
111
|
+
tools?: string[];
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
114
115
|
children,
|
|
115
116
|
bypassPermissions,
|
|
116
117
|
pluginDirs,
|
|
118
|
+
tools,
|
|
117
119
|
}) => {
|
|
118
120
|
const { restoreSessionId, continueLastSession } = useAppConfig();
|
|
119
121
|
|
|
@@ -129,6 +131,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
129
131
|
|
|
130
132
|
// AI State
|
|
131
133
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
134
|
+
const messagesUpdateTimerRef = useRef<NodeJS.Timeout | null>(null);
|
|
135
|
+
|
|
132
136
|
const [isLoading, setIsLoading] = useState(false);
|
|
133
137
|
const [latestTotalTokens, setlatestTotalTokens] = useState(0);
|
|
134
138
|
const [sessionId, setSessionId] = useState("");
|
|
@@ -225,9 +229,16 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
225
229
|
useEffect(() => {
|
|
226
230
|
const initializeAgent = async () => {
|
|
227
231
|
const callbacks: AgentCallbacks = {
|
|
228
|
-
onMessagesChange: (
|
|
232
|
+
onMessagesChange: () => {
|
|
229
233
|
if (!isExpandedRef.current) {
|
|
230
|
-
|
|
234
|
+
if (!messagesUpdateTimerRef.current) {
|
|
235
|
+
messagesUpdateTimerRef.current = setTimeout(() => {
|
|
236
|
+
if (agentRef.current) {
|
|
237
|
+
setMessages([...agentRef.current.messages]);
|
|
238
|
+
}
|
|
239
|
+
messagesUpdateTimerRef.current = null;
|
|
240
|
+
}, 50);
|
|
241
|
+
}
|
|
231
242
|
}
|
|
232
243
|
},
|
|
233
244
|
onServersChange: (servers) => {
|
|
@@ -304,6 +315,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
304
315
|
canUseTool: permissionCallback,
|
|
305
316
|
stream: false, // 关闭流式模式
|
|
306
317
|
plugins: pluginDirs?.map((path) => ({ type: "local", path })),
|
|
318
|
+
tools,
|
|
307
319
|
});
|
|
308
320
|
|
|
309
321
|
agentRef.current = agent;
|
|
@@ -336,11 +348,15 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
336
348
|
bypassPermissions,
|
|
337
349
|
showConfirmation,
|
|
338
350
|
pluginDirs,
|
|
351
|
+
tools,
|
|
339
352
|
]);
|
|
340
353
|
|
|
341
354
|
// Cleanup on unmount
|
|
342
355
|
useEffect(() => {
|
|
343
356
|
return () => {
|
|
357
|
+
if (messagesUpdateTimerRef.current) {
|
|
358
|
+
clearTimeout(messagesUpdateTimerRef.current);
|
|
359
|
+
}
|
|
344
360
|
if (agentRef.current) {
|
|
345
361
|
try {
|
|
346
362
|
// Display usage summary before cleanup
|
|
@@ -526,6 +542,10 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
526
542
|
setIsExpanded((prev) => {
|
|
527
543
|
const newExpanded = !prev;
|
|
528
544
|
if (newExpanded) {
|
|
545
|
+
if (messagesUpdateTimerRef.current) {
|
|
546
|
+
clearTimeout(messagesUpdateTimerRef.current);
|
|
547
|
+
messagesUpdateTimerRef.current = null;
|
|
548
|
+
}
|
|
529
549
|
// Transitioning to EXPANDED: Freeze the current view
|
|
530
550
|
// Deep copy the last message to ensure it doesn't update if the agent is still writing to it
|
|
531
551
|
setMessages((currentMessages) => {
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
PluginScopeManager,
|
|
5
|
-
ConfigurationService,
|
|
6
|
-
PluginManager,
|
|
3
|
+
PluginCore,
|
|
7
4
|
KnownMarketplace,
|
|
8
5
|
InstalledPlugin,
|
|
9
6
|
MarketplacePluginEntry,
|
|
@@ -35,25 +32,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
35
32
|
})[]
|
|
36
33
|
>([]);
|
|
37
34
|
|
|
38
|
-
const
|
|
39
|
-
const configurationService = useMemo(() => new ConfigurationService(), []);
|
|
40
|
-
const pluginManager = useMemo(
|
|
41
|
-
() =>
|
|
42
|
-
new PluginManager({
|
|
43
|
-
workdir: process.cwd(),
|
|
44
|
-
configurationService,
|
|
45
|
-
}),
|
|
46
|
-
[configurationService],
|
|
47
|
-
);
|
|
48
|
-
const pluginScopeManager = useMemo(
|
|
49
|
-
() =>
|
|
50
|
-
new PluginScopeManager({
|
|
51
|
-
workdir: process.cwd(),
|
|
52
|
-
configurationService,
|
|
53
|
-
pluginManager,
|
|
54
|
-
}),
|
|
55
|
-
[configurationService, pluginManager],
|
|
56
|
-
);
|
|
35
|
+
const pluginCore = useMemo(() => new PluginCore(), []);
|
|
57
36
|
|
|
58
37
|
const refresh = useCallback(async () => {
|
|
59
38
|
setState((prev: PluginManagerState) => ({
|
|
@@ -63,9 +42,9 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
63
42
|
}));
|
|
64
43
|
try {
|
|
65
44
|
const [mks, installed, enabledMap] = await Promise.all([
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Promise.resolve(
|
|
45
|
+
pluginCore.listMarketplaces(),
|
|
46
|
+
pluginCore.getInstalledPlugins(),
|
|
47
|
+
Promise.resolve(pluginCore.getMergedEnabledPlugins()),
|
|
69
48
|
]);
|
|
70
49
|
|
|
71
50
|
setMarketplaces(mks);
|
|
@@ -74,7 +53,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
74
53
|
return {
|
|
75
54
|
...p,
|
|
76
55
|
enabled: !!enabledMap[pluginId],
|
|
77
|
-
scope:
|
|
56
|
+
scope: pluginCore.findPluginScope(pluginId) || undefined,
|
|
78
57
|
};
|
|
79
58
|
});
|
|
80
59
|
|
|
@@ -88,8 +67,8 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
88
67
|
})[] = [];
|
|
89
68
|
for (const mk of mks) {
|
|
90
69
|
try {
|
|
91
|
-
const manifest = await
|
|
92
|
-
|
|
70
|
+
const manifest = await pluginCore.loadMarketplaceManifest(
|
|
71
|
+
pluginCore.getMarketplacePath(mk),
|
|
93
72
|
);
|
|
94
73
|
manifest.plugins.forEach((p) => {
|
|
95
74
|
const pluginId = `${p.name}@${mk.name}`;
|
|
@@ -120,7 +99,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
120
99
|
error: error instanceof Error ? error.message : String(error),
|
|
121
100
|
}));
|
|
122
101
|
}
|
|
123
|
-
}, [
|
|
102
|
+
}, [pluginCore]);
|
|
124
103
|
|
|
125
104
|
useEffect(() => {
|
|
126
105
|
refresh();
|
|
@@ -142,7 +121,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
142
121
|
error: null,
|
|
143
122
|
}));
|
|
144
123
|
try {
|
|
145
|
-
await
|
|
124
|
+
await pluginCore.addMarketplace(source);
|
|
146
125
|
await refresh();
|
|
147
126
|
} catch (error) {
|
|
148
127
|
setState((prev: PluginManagerState) => ({
|
|
@@ -152,7 +131,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
152
131
|
}));
|
|
153
132
|
}
|
|
154
133
|
},
|
|
155
|
-
[
|
|
134
|
+
[pluginCore, refresh],
|
|
156
135
|
);
|
|
157
136
|
|
|
158
137
|
const removeMarketplace = useCallback(
|
|
@@ -163,7 +142,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
163
142
|
error: null,
|
|
164
143
|
}));
|
|
165
144
|
try {
|
|
166
|
-
await
|
|
145
|
+
await pluginCore.removeMarketplace(name);
|
|
167
146
|
await refresh();
|
|
168
147
|
} catch (error) {
|
|
169
148
|
setState((prev: PluginManagerState) => ({
|
|
@@ -173,7 +152,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
173
152
|
}));
|
|
174
153
|
}
|
|
175
154
|
},
|
|
176
|
-
[
|
|
155
|
+
[pluginCore, refresh],
|
|
177
156
|
);
|
|
178
157
|
|
|
179
158
|
const updateMarketplace = useCallback(
|
|
@@ -184,7 +163,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
184
163
|
error: null,
|
|
185
164
|
}));
|
|
186
165
|
try {
|
|
187
|
-
await
|
|
166
|
+
await pluginCore.updateMarketplace(name);
|
|
188
167
|
await refresh();
|
|
189
168
|
} catch (error) {
|
|
190
169
|
setState((prev: PluginManagerState) => ({
|
|
@@ -194,7 +173,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
194
173
|
}));
|
|
195
174
|
}
|
|
196
175
|
},
|
|
197
|
-
[
|
|
176
|
+
[pluginCore, refresh],
|
|
198
177
|
);
|
|
199
178
|
|
|
200
179
|
const installPlugin = useCallback(
|
|
@@ -210,9 +189,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
210
189
|
}));
|
|
211
190
|
try {
|
|
212
191
|
const pluginId = `${name}@${marketplace}`;
|
|
213
|
-
|
|
214
|
-
await marketplaceService.installPlugin(pluginId, workdir);
|
|
215
|
-
await pluginScopeManager.enablePlugin(scope, pluginId);
|
|
192
|
+
await pluginCore.installPlugin(pluginId, scope);
|
|
216
193
|
await refresh();
|
|
217
194
|
} catch (error) {
|
|
218
195
|
setState((prev: PluginManagerState) => ({
|
|
@@ -222,7 +199,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
222
199
|
}));
|
|
223
200
|
}
|
|
224
201
|
},
|
|
225
|
-
[
|
|
202
|
+
[pluginCore, refresh],
|
|
226
203
|
);
|
|
227
204
|
|
|
228
205
|
const uninstallPlugin = useCallback(
|
|
@@ -234,20 +211,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
234
211
|
}));
|
|
235
212
|
try {
|
|
236
213
|
const pluginId = `${name}@${marketplace}`;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// 1. Remove from global registry and potentially clean up cache
|
|
240
|
-
await marketplaceService.uninstallPlugin(pluginId, workdir);
|
|
241
|
-
|
|
242
|
-
// 2. Find the scope where it's currently enabled and remove it from there
|
|
243
|
-
const scope = pluginScopeManager.findPluginScope(pluginId);
|
|
244
|
-
if (scope) {
|
|
245
|
-
await configurationService.removeEnabledPlugin(
|
|
246
|
-
workdir,
|
|
247
|
-
scope,
|
|
248
|
-
pluginId,
|
|
249
|
-
);
|
|
250
|
-
}
|
|
214
|
+
await pluginCore.uninstallPlugin(pluginId);
|
|
251
215
|
await refresh();
|
|
252
216
|
} catch (error) {
|
|
253
217
|
setState((prev: PluginManagerState) => ({
|
|
@@ -257,7 +221,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
257
221
|
}));
|
|
258
222
|
}
|
|
259
223
|
},
|
|
260
|
-
[
|
|
224
|
+
[pluginCore, refresh],
|
|
261
225
|
);
|
|
262
226
|
|
|
263
227
|
const updatePlugin = useCallback(
|
|
@@ -269,7 +233,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
269
233
|
}));
|
|
270
234
|
try {
|
|
271
235
|
const pluginId = `${name}@${marketplace}`;
|
|
272
|
-
await
|
|
236
|
+
await pluginCore.updatePlugin(pluginId);
|
|
273
237
|
await refresh();
|
|
274
238
|
} catch (error) {
|
|
275
239
|
setState((prev: PluginManagerState) => ({
|
|
@@ -279,7 +243,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
279
243
|
}));
|
|
280
244
|
}
|
|
281
245
|
},
|
|
282
|
-
[
|
|
246
|
+
[pluginCore, refresh],
|
|
283
247
|
);
|
|
284
248
|
|
|
285
249
|
return {
|