@townco/cli 0.1.84 → 0.1.87
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/commands/create.js +0 -1
- package/dist/commands/deploy.js +2 -25
- package/package.json +9 -9
- package/dist/commands/delete.d.ts +0 -1
- package/dist/commands/delete.js +0 -60
- package/dist/commands/edit.d.ts +0 -1
- package/dist/commands/edit.js +0 -92
- package/dist/commands/list.d.ts +0 -1
- package/dist/commands/list.js +0 -55
- package/dist/commands/mcp-add.d.ts +0 -14
- package/dist/commands/mcp-add.js +0 -494
- package/dist/commands/mcp-list.d.ts +0 -3
- package/dist/commands/mcp-list.js +0 -63
- package/dist/commands/mcp-remove.d.ts +0 -3
- package/dist/commands/mcp-remove.js +0 -120
- package/dist/commands/tool-add.d.ts +0 -6
- package/dist/commands/tool-add.js +0 -349
- package/dist/commands/tool-list.d.ts +0 -3
- package/dist/commands/tool-list.js +0 -61
- package/dist/commands/tool-register.d.ts +0 -7
- package/dist/commands/tool-register.js +0 -291
- package/dist/commands/tool-remove.d.ts +0 -3
- package/dist/commands/tool-remove.js +0 -202
- package/dist/components/MergedLogsPane.d.ts +0 -11
- package/dist/components/MergedLogsPane.js +0 -205
- package/dist/lib/auth-storage.d.ts +0 -38
- package/dist/lib/auth-storage.js +0 -89
- package/dist/lib/mcp-storage.d.ts +0 -32
- package/dist/lib/mcp-storage.js +0 -111
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { copyFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { getAgentPath, listAgents } from "@townco/agent/storage";
|
|
5
|
-
import { MultiSelect } from "@townco/ui/tui";
|
|
6
|
-
import { Box, render, Text, useApp, useInput } from "ink";
|
|
7
|
-
import TextInput from "ink-text-input";
|
|
8
|
-
import { useEffect, useState } from "react";
|
|
9
|
-
import { ensureToolsDir, getToolsDir, saveToolConfig, toolConfigExists, } from "../lib/tool-storage";
|
|
10
|
-
// ============================================================================
|
|
11
|
-
// Helper Functions
|
|
12
|
-
// ============================================================================
|
|
13
|
-
/**
|
|
14
|
-
* Normalize a tool name to kebab-case for use as a filename
|
|
15
|
-
*/
|
|
16
|
-
function normalizeToolName(name) {
|
|
17
|
-
return name
|
|
18
|
-
.trim()
|
|
19
|
-
.toLowerCase()
|
|
20
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
21
|
-
.replace(/^-+|-+$/g, "");
|
|
22
|
-
}
|
|
23
|
-
function TextInputStage({ title, value, onChange, onSubmit, onCancel, placeholder, }) {
|
|
24
|
-
useInput((_input, key) => {
|
|
25
|
-
if (key.escape) {
|
|
26
|
-
onCancel();
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: title }) }), _jsxs(Box, { children: [_jsxs(Text, { children: [">", " "] }), _jsx(TextInput, { value: value, onChange: onChange, onSubmit: onSubmit, ...(placeholder && { placeholder }) })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: Continue \u2022 Esc: Back" }) })] }));
|
|
30
|
-
}
|
|
31
|
-
function NameInputStage({ value, onChange, onNext, onBack, }) {
|
|
32
|
-
const [localError, setLocalError] = useState(null);
|
|
33
|
-
const handleSubmit = () => {
|
|
34
|
-
const trimmed = value.trim();
|
|
35
|
-
if (!trimmed) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
// Check for duplicate
|
|
39
|
-
if (toolConfigExists(trimmed)) {
|
|
40
|
-
setLocalError(`Tool "${trimmed}" already exists`);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
onNext(trimmed);
|
|
44
|
-
};
|
|
45
|
-
const handleChange = (newValue) => {
|
|
46
|
-
if (localError) {
|
|
47
|
-
setLocalError(null);
|
|
48
|
-
}
|
|
49
|
-
onChange(newValue);
|
|
50
|
-
};
|
|
51
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(TextInputStage, { title: "Enter tool name:", value: value, onChange: handleChange, onSubmit: handleSubmit, onCancel: onBack, placeholder: "my-custom-tool" }), localError && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["\u274C ", localError] }) }))] }));
|
|
52
|
-
}
|
|
53
|
-
function PathInputStage({ toolName, value, onChange, onNext, onBack, }) {
|
|
54
|
-
const [localError, setLocalError] = useState(null);
|
|
55
|
-
const handleSubmit = () => {
|
|
56
|
-
const trimmed = value.trim();
|
|
57
|
-
if (!trimmed) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
// Check if file exists
|
|
61
|
-
if (!existsSync(trimmed)) {
|
|
62
|
-
setLocalError(`File not found: ${trimmed}`);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
// Check if it's a TypeScript file
|
|
66
|
-
if (!trimmed.endsWith(".ts")) {
|
|
67
|
-
setLocalError("File must be a TypeScript file (.ts)");
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
onNext(trimmed);
|
|
71
|
-
};
|
|
72
|
-
const handleChange = (newValue) => {
|
|
73
|
-
if (localError) {
|
|
74
|
-
setLocalError(null);
|
|
75
|
-
}
|
|
76
|
-
onChange(newValue);
|
|
77
|
-
};
|
|
78
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(TextInputStage, { title: `Enter path to TypeScript file for tool: ${toolName}`, value: value, onChange: handleChange, onSubmit: handleSubmit, onCancel: onBack, placeholder: "/path/to/my-tool.ts" }), localError && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["\u274C ", localError] }) }))] }));
|
|
79
|
-
}
|
|
80
|
-
function NoAgentsMessage({ onNext }) {
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
const timer = setTimeout(() => {
|
|
83
|
-
onNext();
|
|
84
|
-
}, 1000);
|
|
85
|
-
return () => clearTimeout(timer);
|
|
86
|
-
}, [onNext]);
|
|
87
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "No agents found. Tool will be saved globally." }), _jsx(Text, { dimColor: true, children: "You can attach it to agents later." })] }));
|
|
88
|
-
}
|
|
89
|
-
function AgentSelectionStage({ selectedAgents, onSelectedAgentsChange, onNext, onBack, }) {
|
|
90
|
-
const [availableAgents, setAvailableAgents] = useState([]);
|
|
91
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
92
|
-
useEffect(() => {
|
|
93
|
-
// Fetch available agents
|
|
94
|
-
const fetchAgents = async () => {
|
|
95
|
-
try {
|
|
96
|
-
const agents = await listAgents();
|
|
97
|
-
setAvailableAgents(agents);
|
|
98
|
-
}
|
|
99
|
-
catch (error) {
|
|
100
|
-
console.error("Error fetching agents:", error);
|
|
101
|
-
setAvailableAgents([]);
|
|
102
|
-
}
|
|
103
|
-
finally {
|
|
104
|
-
setIsLoading(false);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
fetchAgents();
|
|
108
|
-
}, []);
|
|
109
|
-
// If still loading, show loading message
|
|
110
|
-
if (isLoading) {
|
|
111
|
-
return (_jsx(Box, { flexDirection: "column", children: _jsx(Text, { children: "Loading agents..." }) }));
|
|
112
|
-
}
|
|
113
|
-
// If no agents available, show info message and auto-proceed
|
|
114
|
-
if (availableAgents.length === 0) {
|
|
115
|
-
return _jsx(NoAgentsMessage, { onNext: onNext });
|
|
116
|
-
}
|
|
117
|
-
// Show agent selection UI
|
|
118
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Register tool with agents (optional):" }) }), _jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { dimColor: true, children: "Select which agents should have access to this tool." }), _jsx(Text, { dimColor: true, children: "You can skip this step and register agents later." })] }), _jsx(MultiSelect, { options: availableAgents.map((agent) => ({
|
|
119
|
-
label: agent,
|
|
120
|
-
value: agent,
|
|
121
|
-
})), selected: selectedAgents, onChange: onSelectedAgentsChange, onSubmit: onNext, onCancel: onBack })] }));
|
|
122
|
-
}
|
|
123
|
-
function DoneStage({ config, status, error, attachedAgents }) {
|
|
124
|
-
const { exit } = useApp();
|
|
125
|
-
useEffect(() => {
|
|
126
|
-
if (status === "done") {
|
|
127
|
-
setImmediate(() => {
|
|
128
|
-
exit();
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}, [status, exit]);
|
|
132
|
-
if (status === "saving") {
|
|
133
|
-
return (_jsx(Box, { flexDirection: "column", children: _jsx(Text, { children: "\u23F3 Saving tool configuration..." }) }));
|
|
134
|
-
}
|
|
135
|
-
if (status === "error") {
|
|
136
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "red", children: "\u274C Error saving tool" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: error }) })] }));
|
|
137
|
-
}
|
|
138
|
-
if (status === "done") {
|
|
139
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "green", children: "\u2705 Tool saved successfully!" }), _jsxs(Box, { marginTop: 1, children: [_jsxs(Text, { dimColor: true, children: ["Name: ", config.name] }), _jsxs(Text, { dimColor: true, children: ["Path: ", config.path] })] }), attachedAgents.length > 0 && (_jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsxs(Text, { dimColor: true, children: ["Registered with agents: ", attachedAgents.join(", ")] }) }))] }));
|
|
140
|
-
}
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
// ============================================================================
|
|
144
|
-
// Main Component
|
|
145
|
-
// ============================================================================
|
|
146
|
-
function ToolAddApp({ name: initialName, path: initialPath }) {
|
|
147
|
-
// Determine the starting stage based on what's provided
|
|
148
|
-
const determineInitialStage = () => {
|
|
149
|
-
if (initialPath && initialName) {
|
|
150
|
-
return "agentSelection";
|
|
151
|
-
}
|
|
152
|
-
if (initialName) {
|
|
153
|
-
return "path";
|
|
154
|
-
}
|
|
155
|
-
return "name";
|
|
156
|
-
};
|
|
157
|
-
const { exit } = useApp();
|
|
158
|
-
const [stage, setStage] = useState(determineInitialStage());
|
|
159
|
-
const [config, setConfig] = useState({
|
|
160
|
-
...(initialName && { name: initialName }),
|
|
161
|
-
...(initialPath && { path: initialPath }),
|
|
162
|
-
});
|
|
163
|
-
const [nameInput, setNameInput] = useState(initialName || "");
|
|
164
|
-
const [pathInput, setPathInput] = useState(initialPath || "");
|
|
165
|
-
const [saveStatus, setSaveStatus] = useState("pending");
|
|
166
|
-
const [saveError, setSaveError] = useState(null);
|
|
167
|
-
const [selectedAgents, setSelectedAgents] = useState([]);
|
|
168
|
-
const handleSave = (agentsToAttach) => {
|
|
169
|
-
setSaveStatus("saving");
|
|
170
|
-
try {
|
|
171
|
-
if (!config.name || !config.path) {
|
|
172
|
-
setSaveStatus("error");
|
|
173
|
-
setSaveError("Missing required fields");
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
// Ensure tools directory exists
|
|
177
|
-
ensureToolsDir();
|
|
178
|
-
// Normalize the tool name for the filename
|
|
179
|
-
const normalizedName = normalizeToolName(config.name);
|
|
180
|
-
const toolsDir = getToolsDir();
|
|
181
|
-
const targetPath = join(toolsDir, `${normalizedName}.ts`);
|
|
182
|
-
// Copy the file to the tools directory
|
|
183
|
-
try {
|
|
184
|
-
copyFileSync(config.path, targetPath);
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
setSaveStatus("error");
|
|
188
|
-
setSaveError(`Failed to copy file: ${error instanceof Error ? error.message : String(error)}`);
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
// Create tool config
|
|
192
|
-
const toolConfig = {
|
|
193
|
-
name: config.name,
|
|
194
|
-
path: targetPath,
|
|
195
|
-
};
|
|
196
|
-
// Save to global tool storage
|
|
197
|
-
saveToolConfig(toolConfig);
|
|
198
|
-
// Update selected agents
|
|
199
|
-
for (const agentName of agentsToAttach) {
|
|
200
|
-
try {
|
|
201
|
-
const agentPath = getAgentPath(agentName);
|
|
202
|
-
const agentJsonPath = join(agentPath, "agent.json");
|
|
203
|
-
// Read existing agent.json
|
|
204
|
-
const agentJsonContent = readFileSync(agentJsonPath, "utf-8");
|
|
205
|
-
const agentDef = JSON.parse(agentJsonContent);
|
|
206
|
-
// Add tool to tools array (create array if doesn't exist)
|
|
207
|
-
if (!agentDef.tools) {
|
|
208
|
-
agentDef.tools = [];
|
|
209
|
-
}
|
|
210
|
-
// Create custom tool object
|
|
211
|
-
const customTool = {
|
|
212
|
-
type: "custom",
|
|
213
|
-
modulePath: targetPath,
|
|
214
|
-
};
|
|
215
|
-
// Check if this tool is already in the agent's config
|
|
216
|
-
const existingIndex = agentDef.tools.findIndex((tool) => typeof tool === "object" &&
|
|
217
|
-
tool !== null &&
|
|
218
|
-
"type" in tool &&
|
|
219
|
-
tool.type === "custom" &&
|
|
220
|
-
"modulePath" in tool &&
|
|
221
|
-
tool.modulePath === targetPath);
|
|
222
|
-
if (existingIndex >= 0) {
|
|
223
|
-
// Update existing config
|
|
224
|
-
agentDef.tools[existingIndex] = customTool;
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
// Add new config
|
|
228
|
-
agentDef.tools.push(customTool);
|
|
229
|
-
}
|
|
230
|
-
// Write back to agent.json
|
|
231
|
-
writeFileSync(agentJsonPath, JSON.stringify(agentDef, null, 2));
|
|
232
|
-
}
|
|
233
|
-
catch (error) {
|
|
234
|
-
console.error(`Error updating agent ${agentName}:`, error);
|
|
235
|
-
// Continue with other agents even if one fails
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
setSaveStatus("done");
|
|
239
|
-
}
|
|
240
|
-
catch (error) {
|
|
241
|
-
setSaveStatus("error");
|
|
242
|
-
setSaveError(error instanceof Error ? error.message : "Unknown error occurred");
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
// Name input stage
|
|
246
|
-
if (stage === "name") {
|
|
247
|
-
return (_jsx(NameInputStage, { value: nameInput, onChange: setNameInput, onNext: (name) => {
|
|
248
|
-
setConfig({ ...config, name });
|
|
249
|
-
setStage("path");
|
|
250
|
-
}, onBack: () => exit() }));
|
|
251
|
-
}
|
|
252
|
-
// Path input stage
|
|
253
|
-
if (stage === "path") {
|
|
254
|
-
return (_jsx(PathInputStage, { toolName: config.name || "", value: pathInput, onChange: setPathInput, onNext: (path) => {
|
|
255
|
-
setConfig({ ...config, path });
|
|
256
|
-
setStage("agentSelection");
|
|
257
|
-
}, onBack: () => {
|
|
258
|
-
if (initialName) {
|
|
259
|
-
exit();
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
setStage("name");
|
|
263
|
-
}
|
|
264
|
-
} }));
|
|
265
|
-
}
|
|
266
|
-
// Agent selection stage
|
|
267
|
-
if (stage === "agentSelection") {
|
|
268
|
-
return (_jsx(AgentSelectionStage, { selectedAgents: selectedAgents, onSelectedAgentsChange: setSelectedAgents, onNext: () => {
|
|
269
|
-
handleSave(selectedAgents);
|
|
270
|
-
setStage("done");
|
|
271
|
-
}, onBack: () => setStage("path") }));
|
|
272
|
-
}
|
|
273
|
-
// Done stage
|
|
274
|
-
if (stage === "done") {
|
|
275
|
-
return (_jsx(DoneStage, { config: config, status: saveStatus, error: saveError, attachedAgents: selectedAgents }));
|
|
276
|
-
}
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
// ============================================================================
|
|
280
|
-
// Export and Runner
|
|
281
|
-
// ============================================================================
|
|
282
|
-
export default ToolAddApp;
|
|
283
|
-
export async function runToolRegister(props = {}) {
|
|
284
|
-
// Set stdin to raw mode to capture input
|
|
285
|
-
if (process.stdin.isTTY) {
|
|
286
|
-
process.stdin.setRawMode(true);
|
|
287
|
-
}
|
|
288
|
-
const { waitUntilExit } = render(_jsx(ToolAddApp, { ...props }));
|
|
289
|
-
// Wait for the app to exit before returning
|
|
290
|
-
await waitUntilExit();
|
|
291
|
-
}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { getAgentPath, listAgents } from "@townco/agent/storage";
|
|
5
|
-
import { MultiSelect } from "@townco/ui/tui";
|
|
6
|
-
import { Box, render, Text, useApp } from "ink";
|
|
7
|
-
import { useEffect, useState } from "react";
|
|
8
|
-
import { deleteToolConfig, listToolConfigs } from "../lib/tool-storage";
|
|
9
|
-
function NoToolsMessage({ onCancel }) {
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
const timer = setTimeout(() => {
|
|
12
|
-
onCancel();
|
|
13
|
-
}, 2000);
|
|
14
|
-
return () => clearTimeout(timer);
|
|
15
|
-
}, [onCancel]);
|
|
16
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "No custom tools found." }), _jsx(Text, { dimColor: true, children: "Use 'town tool add' to create a new tool." })] }));
|
|
17
|
-
}
|
|
18
|
-
function ToolSelectionStage({ availableTools, selectedTools, onSelectedToolsChange, onNext, onCancel, }) {
|
|
19
|
-
if (availableTools.length === 0) {
|
|
20
|
-
return _jsx(NoToolsMessage, { onCancel: onCancel });
|
|
21
|
-
}
|
|
22
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Select tools to remove:" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Use space to select, enter to confirm." }) }), _jsx(MultiSelect, { options: availableTools.map((tool) => ({
|
|
23
|
-
label: tool.name,
|
|
24
|
-
value: tool.name,
|
|
25
|
-
})), selected: selectedTools, onChange: onSelectedToolsChange, onSubmit: onNext, onCancel: onCancel })] }));
|
|
26
|
-
}
|
|
27
|
-
function ConfirmationStage({ selectedTools, onConfirm, onBack, }) {
|
|
28
|
-
const [choice, setChoice] = useState([]);
|
|
29
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "yellow", children: "\u26A0\uFE0F Confirm deletion" }) }), _jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { children: "The following tools will be permanently deleted:" }), selectedTools.map((tool) => (_jsxs(Text, { dimColor: true, children: ["\u2022 ", tool] }, tool)))] }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "This will delete the tool files and remove them from all agents." }) }), _jsx(MultiSelect, { options: [
|
|
30
|
-
{
|
|
31
|
-
label: "Yes, delete these tools",
|
|
32
|
-
value: "confirm",
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
label: "No, go back",
|
|
36
|
-
value: "cancel",
|
|
37
|
-
},
|
|
38
|
-
], selected: choice, onChange: (selected) => {
|
|
39
|
-
// Only allow single selection
|
|
40
|
-
if (selected.length > 0) {
|
|
41
|
-
setChoice([selected[selected.length - 1] || ""]);
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
setChoice([]);
|
|
45
|
-
}
|
|
46
|
-
}, onSubmit: () => {
|
|
47
|
-
if (choice[0] === "confirm") {
|
|
48
|
-
onConfirm();
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
onBack();
|
|
52
|
-
}
|
|
53
|
-
}, onCancel: onBack })] }));
|
|
54
|
-
}
|
|
55
|
-
function RemovingStage({ progress }) {
|
|
56
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "\u23F3 Removing tools..." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: progress }) })] }));
|
|
57
|
-
}
|
|
58
|
-
function DoneStage({ removedTools, errors }) {
|
|
59
|
-
const { exit } = useApp();
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
const timer = setTimeout(() => {
|
|
62
|
-
exit();
|
|
63
|
-
}, 2000);
|
|
64
|
-
return () => clearTimeout(timer);
|
|
65
|
-
}, [exit]);
|
|
66
|
-
return (_jsxs(Box, { flexDirection: "column", children: [removedTools.length > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "green", children: ["\u2705 Successfully removed ", removedTools.length, " tool(s):"] }), removedTools.map((tool) => (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["\u2022 ", tool] }) }, tool)))] })), errors.length > 0 && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "red", children: "\u274C Errors occurred:" }), errors.map((error) => (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { dimColor: true, children: error }) }, error)))] }))] }));
|
|
67
|
-
}
|
|
68
|
-
// ============================================================================
|
|
69
|
-
// Main Component
|
|
70
|
-
// ============================================================================
|
|
71
|
-
function ToolRemoveApp() {
|
|
72
|
-
const { exit } = useApp();
|
|
73
|
-
const [stage, setStage] = useState("selection");
|
|
74
|
-
const [availableTools, setAvailableTools] = useState([]);
|
|
75
|
-
const [selectedTools, setSelectedTools] = useState([]);
|
|
76
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
77
|
-
const [progress, setProgress] = useState("");
|
|
78
|
-
const [removedTools, setRemovedTools] = useState([]);
|
|
79
|
-
const [errors, setErrors] = useState([]);
|
|
80
|
-
// Load available tools on mount
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
try {
|
|
83
|
-
const tools = listToolConfigs();
|
|
84
|
-
setAvailableTools(tools);
|
|
85
|
-
}
|
|
86
|
-
catch (error) {
|
|
87
|
-
console.error("Error loading tools:", error);
|
|
88
|
-
setAvailableTools([]);
|
|
89
|
-
}
|
|
90
|
-
finally {
|
|
91
|
-
setIsLoading(false);
|
|
92
|
-
}
|
|
93
|
-
}, []);
|
|
94
|
-
const handleRemove = async () => {
|
|
95
|
-
setStage("removing");
|
|
96
|
-
const removed = [];
|
|
97
|
-
const errs = [];
|
|
98
|
-
for (const toolName of selectedTools) {
|
|
99
|
-
try {
|
|
100
|
-
setProgress(`Removing ${toolName}...`);
|
|
101
|
-
// Find the tool config to get the path
|
|
102
|
-
const toolConfig = availableTools.find((t) => t.name === toolName);
|
|
103
|
-
if (!toolConfig) {
|
|
104
|
-
errs.push(`Tool config not found: ${toolName}`);
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
// Delete the tool file if it exists
|
|
108
|
-
if (existsSync(toolConfig.path)) {
|
|
109
|
-
try {
|
|
110
|
-
unlinkSync(toolConfig.path);
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
errs.push(`Failed to delete file for ${toolName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
// Remove from tool storage
|
|
117
|
-
deleteToolConfig(toolName);
|
|
118
|
-
// Remove from all agents
|
|
119
|
-
setProgress(`Removing ${toolName} from agents...`);
|
|
120
|
-
try {
|
|
121
|
-
const agents = await listAgents();
|
|
122
|
-
for (const agentName of agents) {
|
|
123
|
-
try {
|
|
124
|
-
const agentPath = getAgentPath(agentName);
|
|
125
|
-
const agentJsonPath = join(agentPath, "agent.json");
|
|
126
|
-
if (!existsSync(agentJsonPath)) {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
// Read existing agent.json
|
|
130
|
-
const agentJsonContent = readFileSync(agentJsonPath, "utf-8");
|
|
131
|
-
const agentDef = JSON.parse(agentJsonContent);
|
|
132
|
-
// Remove tool from tools array if it exists
|
|
133
|
-
if (agentDef.tools && Array.isArray(agentDef.tools)) {
|
|
134
|
-
const originalLength = agentDef.tools.length;
|
|
135
|
-
agentDef.tools = agentDef.tools.filter((tool) => !(typeof tool === "object" &&
|
|
136
|
-
tool !== null &&
|
|
137
|
-
"type" in tool &&
|
|
138
|
-
tool.type === "custom" &&
|
|
139
|
-
"modulePath" in tool &&
|
|
140
|
-
tool.modulePath === toolConfig.path));
|
|
141
|
-
// Only write back if we actually removed something
|
|
142
|
-
if (agentDef.tools.length !== originalLength) {
|
|
143
|
-
writeFileSync(agentJsonPath, JSON.stringify(agentDef, null, 2));
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
catch (error) {
|
|
148
|
-
console.error(`Error updating agent ${agentName}:`, error);
|
|
149
|
-
// Continue with other agents even if one fails
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
errs.push(`Failed to update agents for ${toolName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
155
|
-
}
|
|
156
|
-
removed.push(toolName);
|
|
157
|
-
}
|
|
158
|
-
catch (error) {
|
|
159
|
-
errs.push(`Failed to remove ${toolName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
setRemovedTools(removed);
|
|
163
|
-
setErrors(errs);
|
|
164
|
-
setStage("done");
|
|
165
|
-
};
|
|
166
|
-
if (isLoading) {
|
|
167
|
-
return (_jsx(Box, { flexDirection: "column", children: _jsx(Text, { children: "Loading tools..." }) }));
|
|
168
|
-
}
|
|
169
|
-
if (stage === "selection") {
|
|
170
|
-
return (_jsx(ToolSelectionStage, { availableTools: availableTools, selectedTools: selectedTools, onSelectedToolsChange: setSelectedTools, onNext: () => {
|
|
171
|
-
if (selectedTools.length === 0) {
|
|
172
|
-
exit();
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
setStage("confirmation");
|
|
176
|
-
}
|
|
177
|
-
}, onCancel: () => exit() }));
|
|
178
|
-
}
|
|
179
|
-
if (stage === "confirmation") {
|
|
180
|
-
return (_jsx(ConfirmationStage, { selectedTools: selectedTools, onConfirm: handleRemove, onBack: () => setStage("selection") }));
|
|
181
|
-
}
|
|
182
|
-
if (stage === "removing") {
|
|
183
|
-
return _jsx(RemovingStage, { progress: progress });
|
|
184
|
-
}
|
|
185
|
-
if (stage === "done") {
|
|
186
|
-
return _jsx(DoneStage, { removedTools: removedTools, errors: errors });
|
|
187
|
-
}
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
// ============================================================================
|
|
191
|
-
// Export and Runner
|
|
192
|
-
// ============================================================================
|
|
193
|
-
export default ToolRemoveApp;
|
|
194
|
-
export async function runToolRemove() {
|
|
195
|
-
// Set stdin to raw mode to capture input
|
|
196
|
-
if (process.stdin.isTTY) {
|
|
197
|
-
process.stdin.setRawMode(true);
|
|
198
|
-
}
|
|
199
|
-
const { waitUntilExit } = render(_jsx(ToolRemoveApp, {}));
|
|
200
|
-
// Wait for the app to exit before returning
|
|
201
|
-
await waitUntilExit();
|
|
202
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export interface ServiceOutput {
|
|
2
|
-
service: string;
|
|
3
|
-
output: string[];
|
|
4
|
-
port: number | undefined;
|
|
5
|
-
status: "starting" | "running" | "stopped" | "error";
|
|
6
|
-
}
|
|
7
|
-
export interface MergedLogsPaneProps {
|
|
8
|
-
services: ServiceOutput[];
|
|
9
|
-
onClear?: (serviceIndex: number) => void;
|
|
10
|
-
}
|
|
11
|
-
export declare function MergedLogsPane({ services, onClear }: MergedLogsPaneProps): import("react/jsx-runtime").JSX.Element;
|