@runfusion/fusion 0.13.0 → 0.14.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/README.md +13 -0
- package/dist/bin.js +1332 -528
- package/dist/client/assets/AgentDetailView-B3KAsP2O.js +18 -0
- package/dist/client/assets/{AgentsView-Dvf_xUkx.js → AgentsView-DoXb_amw.js} +4 -4
- package/dist/client/assets/ChatView-BJ2c7wvd.js +1 -0
- package/dist/client/assets/{DevServerView-C2qTJch7.js → DevServerView-DbgM4tlT.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-DRfhg9zz.js → DirectoryPicker-DfmtfMiu.js} +1 -1
- package/dist/client/assets/{DocumentsView-j8ic1xUw.js → DocumentsView-_-Efkx_W.js} +1 -1
- package/dist/client/assets/{InsightsView-CpAz3o0i.js → InsightsView-DUjcfW53.js} +1 -1
- package/dist/client/assets/{MemoryView-BcQsi_JK.js → MemoryView-DxMPBb0q.js} +1 -1
- package/dist/client/assets/{NodesView-Bo_Yhr4N.js → NodesView-BEBTI15s.js} +1 -1
- package/dist/client/assets/PiExtensionsManager-BpMYhHH_.js +11 -0
- package/dist/client/assets/PluginManager-CPv7yQd3.js +1 -0
- package/dist/client/assets/PluginManager-DA_T0GHn.css +1 -0
- package/dist/client/assets/{ResearchView-CLyyqAWE.js → ResearchView-BrFvdyXT.js} +1 -1
- package/dist/client/assets/{RoadmapsView-tG7IdOoc.js → RoadmapsView-BDjLrtcj.js} +1 -1
- package/dist/client/assets/SettingsModal-Cd-QGB0C.js +31 -0
- package/dist/client/assets/{SettingsModal-CXUGeZ0_.js → SettingsModal-CxDxiTRy.js} +1 -1
- package/dist/client/assets/SettingsModal-D_AFkDJa.css +1 -0
- package/dist/client/assets/{SetupWizardModal-BMJL6eNR.js → SetupWizardModal-DFUA4X3z.js} +1 -1
- package/dist/client/assets/{SkillMultiselect-ILMft-Kz.js → SkillMultiselect-BUWe5ujb.js} +1 -1
- package/dist/client/assets/{SkillsView-x4_YwBz6.js → SkillsView-RAkqGX3y.js} +1 -1
- package/dist/client/assets/TodoView-Ceb0wrg1.js +6 -0
- package/dist/client/assets/TodoView-SeO9o7km.css +1 -0
- package/dist/client/assets/{folder-open-DDdJt8aE.js → folder-open-DcM-Vd6r.js} +1 -1
- package/dist/client/assets/index-C1prPuSl.css +1 -0
- package/dist/client/assets/index-DH3aprf6.js +661 -0
- package/dist/client/assets/{list-checks-DFxQ9biT.js → list-checks-ByGHVQpZ.js} +1 -1
- package/dist/client/assets/{star-BKs1bgJN.js → star-DlEYI8GL.js} +1 -1
- package/dist/client/assets/{upload-Bb5Pidne.js → upload-DKshabz-.js} +1 -1
- package/dist/client/assets/{users-BImNn91Q.js → users-X6tYPPBV.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/sw.js +6 -0
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/index.ts +127 -0
- package/dist/droid-cli/package.json +37 -0
- package/dist/droid-cli/src/__tests__/control-handler.test.ts +164 -0
- package/dist/droid-cli/src/__tests__/event-bridge.test.ts +1318 -0
- package/dist/droid-cli/src/__tests__/mcp-config.test.ts +310 -0
- package/dist/droid-cli/src/__tests__/process-manager.test.ts +818 -0
- package/dist/droid-cli/src/__tests__/prompt-builder.test.ts +1206 -0
- package/dist/droid-cli/src/__tests__/provider.test.ts +1894 -0
- package/dist/droid-cli/src/__tests__/setup-test-isolation.test.ts +32 -0
- package/dist/droid-cli/src/__tests__/setup-test-isolation.ts +14 -0
- package/dist/droid-cli/src/__tests__/stream-parser.test.ts +188 -0
- package/dist/droid-cli/src/__tests__/thinking-config.test.ts +141 -0
- package/dist/droid-cli/src/__tests__/tool-mapping.test.ts +253 -0
- package/dist/droid-cli/src/control-handler.ts +82 -0
- package/dist/droid-cli/src/event-bridge.ts +397 -0
- package/dist/droid-cli/src/mcp-config.ts +144 -0
- package/dist/droid-cli/src/mcp-schema-server.cjs +49 -0
- package/dist/droid-cli/src/process-manager.ts +358 -0
- package/dist/droid-cli/src/prompt-builder.ts +629 -0
- package/dist/droid-cli/src/provider.ts +447 -0
- package/dist/droid-cli/src/stream-parser.ts +37 -0
- package/dist/droid-cli/src/thinking-config.ts +83 -0
- package/dist/droid-cli/src/tool-mapping.ts +147 -0
- package/dist/droid-cli/src/types.ts +87 -0
- package/dist/extension.js +555 -125
- package/dist/pi-claude-cli/package.json +1 -1
- package/package.json +2 -1
- package/dist/client/assets/AgentDetailView-B7j297GT.js +0 -18
- package/dist/client/assets/ChatView-BgUt38ty.js +0 -1
- package/dist/client/assets/PiExtensionsManager-DHt2zFg8.js +0 -11
- package/dist/client/assets/PluginManager-BQhBHWrB.js +0 -1
- package/dist/client/assets/PluginManager-jyNkJZSz.css +0 -1
- package/dist/client/assets/SettingsModal-9HS8MnmW.css +0 -1
- package/dist/client/assets/SettingsModal-UziTDnLh.js +0 -31
- package/dist/client/assets/TodoView-BBYcMbXE.js +0 -6
- package/dist/client/assets/TodoView-C1g65hJo.css +0 -1
- package/dist/client/assets/index-B15xwijw.css +0 -1
- package/dist/client/assets/index-DmSs2FGE.js +0 -661
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
|
|
3
|
+
// Hoist mock references so they survive vi.mock hoisting
|
|
4
|
+
const mocks = vi.hoisted(() => ({
|
|
5
|
+
writeFileSync: vi.fn(),
|
|
6
|
+
tmpdir: vi.fn(() => "/tmp"),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
// Mock node:fs writeFileSync to avoid disk I/O
|
|
10
|
+
vi.mock("node:fs", () => ({
|
|
11
|
+
writeFileSync: mocks.writeFileSync,
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
// Mock node:os tmpdir
|
|
15
|
+
vi.mock("node:os", () => ({
|
|
16
|
+
tmpdir: mocks.tmpdir,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
import { getCustomToolDefs, writeMcpConfig, toolsFromContext } from "../mcp-config";
|
|
20
|
+
import type { McpToolDef } from "../mcp-config";
|
|
21
|
+
|
|
22
|
+
describe("getCustomToolDefs", () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("filters out all 6 built-in tools and returns only custom tools", () => {
|
|
28
|
+
const mockPi = {
|
|
29
|
+
getAllTools: vi.fn(() => [
|
|
30
|
+
{
|
|
31
|
+
name: "read",
|
|
32
|
+
description: "Read file",
|
|
33
|
+
parameters: { type: "object" },
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "write",
|
|
37
|
+
description: "Write file",
|
|
38
|
+
parameters: { type: "object" },
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "edit",
|
|
42
|
+
description: "Edit file",
|
|
43
|
+
parameters: { type: "object" },
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "bash",
|
|
47
|
+
description: "Run bash",
|
|
48
|
+
parameters: { type: "object" },
|
|
49
|
+
},
|
|
50
|
+
{ name: "grep", description: "Search", parameters: { type: "object" } },
|
|
51
|
+
{
|
|
52
|
+
name: "find",
|
|
53
|
+
description: "Find files",
|
|
54
|
+
parameters: { type: "object" },
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "search",
|
|
58
|
+
description: "Custom search tool",
|
|
59
|
+
parameters: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: { query: { type: "string" } },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "deploy",
|
|
66
|
+
description: "Deploy app",
|
|
67
|
+
parameters: {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: { target: { type: "string" } },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
]),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const result = getCustomToolDefs(mockPi);
|
|
76
|
+
|
|
77
|
+
expect(result).toHaveLength(2);
|
|
78
|
+
expect(result[0].name).toBe("search");
|
|
79
|
+
expect(result[1].name).toBe("deploy");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("returns empty array when all tools are built-in", () => {
|
|
83
|
+
const mockPi = {
|
|
84
|
+
getAllTools: vi.fn(() => [
|
|
85
|
+
{
|
|
86
|
+
name: "read",
|
|
87
|
+
description: "Read file",
|
|
88
|
+
parameters: { type: "object" },
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "write",
|
|
92
|
+
description: "Write file",
|
|
93
|
+
parameters: { type: "object" },
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "edit",
|
|
97
|
+
description: "Edit file",
|
|
98
|
+
parameters: { type: "object" },
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "bash",
|
|
102
|
+
description: "Run bash",
|
|
103
|
+
parameters: { type: "object" },
|
|
104
|
+
},
|
|
105
|
+
{ name: "grep", description: "Search", parameters: { type: "object" } },
|
|
106
|
+
{
|
|
107
|
+
name: "find",
|
|
108
|
+
description: "Find files",
|
|
109
|
+
parameters: { type: "object" },
|
|
110
|
+
},
|
|
111
|
+
]),
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const result = getCustomToolDefs(mockPi);
|
|
115
|
+
expect(result).toEqual([]);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("includes custom tool with correct name, description, inputSchema from parameters", () => {
|
|
119
|
+
const customParams = {
|
|
120
|
+
type: "object",
|
|
121
|
+
properties: {
|
|
122
|
+
query: { type: "string", description: "Search query" },
|
|
123
|
+
limit: { type: "number" },
|
|
124
|
+
},
|
|
125
|
+
required: ["query"],
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const mockPi = {
|
|
129
|
+
getAllTools: vi.fn(() => [
|
|
130
|
+
{
|
|
131
|
+
name: "custom_search",
|
|
132
|
+
description: "Search the codebase",
|
|
133
|
+
parameters: customParams,
|
|
134
|
+
},
|
|
135
|
+
]),
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const result = getCustomToolDefs(mockPi);
|
|
139
|
+
|
|
140
|
+
expect(result).toHaveLength(1);
|
|
141
|
+
expect(result[0].name).toBe("custom_search");
|
|
142
|
+
expect(result[0].description).toBe("Search the codebase");
|
|
143
|
+
expect(result[0].inputSchema).toBe(customParams);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("handles pi.getAllTools() returning empty array", () => {
|
|
147
|
+
const mockPi = {
|
|
148
|
+
getAllTools: vi.fn(() => []),
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const result = getCustomToolDefs(mockPi);
|
|
152
|
+
expect(result).toEqual([]);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("returns empty array when pi.getAllTools() returns undefined", () => {
|
|
156
|
+
const mockPi = {
|
|
157
|
+
getAllTools: vi.fn(() => undefined),
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const result = getCustomToolDefs(mockPi);
|
|
161
|
+
expect(result).toEqual([]);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("returns empty array when pi.getAllTools() returns null", () => {
|
|
165
|
+
const mockPi = {
|
|
166
|
+
getAllTools: vi.fn(() => null),
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const result = getCustomToolDefs(mockPi);
|
|
170
|
+
expect(result).toEqual([]);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe("toolsFromContext", () => {
|
|
175
|
+
it("keeps ls as a custom tool (not filtered as built-in)", () => {
|
|
176
|
+
const defs = toolsFromContext([
|
|
177
|
+
{ name: "read", description: "builtin", parameters: { type: "object" } },
|
|
178
|
+
{ name: "ls", description: "list files", parameters: { type: "object" } },
|
|
179
|
+
{
|
|
180
|
+
name: "fn_task_list",
|
|
181
|
+
description: "list tasks",
|
|
182
|
+
parameters: { type: "object", properties: {} },
|
|
183
|
+
},
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
expect(defs.map((d) => d.name)).toEqual(["ls", "fn_task_list"]);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe("writeMcpConfig", () => {
|
|
191
|
+
beforeEach(() => {
|
|
192
|
+
vi.clearAllMocks();
|
|
193
|
+
mocks.tmpdir.mockReturnValue("/tmp");
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("writes schema file to tmpdir with correct content (JSON array of tool defs)", () => {
|
|
197
|
+
const toolDefs: McpToolDef[] = [
|
|
198
|
+
{
|
|
199
|
+
name: "search",
|
|
200
|
+
description: "Search",
|
|
201
|
+
inputSchema: { type: "object" },
|
|
202
|
+
},
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
writeMcpConfig(toolDefs);
|
|
206
|
+
|
|
207
|
+
// First writeFileSync call is the schema file
|
|
208
|
+
const schemaCall = mocks.writeFileSync.mock.calls[0];
|
|
209
|
+
expect(schemaCall[0]).toMatch(/droid-cli-mcp-schemas/);
|
|
210
|
+
expect(JSON.parse(schemaCall[1])).toEqual(toolDefs);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("writes config file to tmpdir with mcpServers.custom-tools entry", () => {
|
|
214
|
+
const toolDefs: McpToolDef[] = [
|
|
215
|
+
{
|
|
216
|
+
name: "search",
|
|
217
|
+
description: "Search",
|
|
218
|
+
inputSchema: { type: "object" },
|
|
219
|
+
},
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
writeMcpConfig(toolDefs);
|
|
223
|
+
|
|
224
|
+
// Second writeFileSync call is the config file
|
|
225
|
+
const configCall = mocks.writeFileSync.mock.calls[1];
|
|
226
|
+
const config = JSON.parse(configCall[1]);
|
|
227
|
+
expect(config).toHaveProperty("mcpServers");
|
|
228
|
+
expect(config.mcpServers).toHaveProperty("custom-tools");
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("config uses 'command': 'node' format (not 'type': 'http')", () => {
|
|
232
|
+
const toolDefs: McpToolDef[] = [
|
|
233
|
+
{
|
|
234
|
+
name: "search",
|
|
235
|
+
description: "Search",
|
|
236
|
+
inputSchema: { type: "object" },
|
|
237
|
+
},
|
|
238
|
+
];
|
|
239
|
+
|
|
240
|
+
writeMcpConfig(toolDefs);
|
|
241
|
+
|
|
242
|
+
const configCall = mocks.writeFileSync.mock.calls[1];
|
|
243
|
+
const config = JSON.parse(configCall[1]);
|
|
244
|
+
const server = config.mcpServers["custom-tools"];
|
|
245
|
+
|
|
246
|
+
expect(server.command).toBe("node");
|
|
247
|
+
expect(server).not.toHaveProperty("type");
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("config args include path to mcp-schema-server.cjs and schema file path", () => {
|
|
251
|
+
const toolDefs: McpToolDef[] = [
|
|
252
|
+
{
|
|
253
|
+
name: "search",
|
|
254
|
+
description: "Search",
|
|
255
|
+
inputSchema: { type: "object" },
|
|
256
|
+
},
|
|
257
|
+
];
|
|
258
|
+
|
|
259
|
+
writeMcpConfig(toolDefs);
|
|
260
|
+
|
|
261
|
+
const configCall = mocks.writeFileSync.mock.calls[1];
|
|
262
|
+
const config = JSON.parse(configCall[1]);
|
|
263
|
+
const server = config.mcpServers["custom-tools"];
|
|
264
|
+
|
|
265
|
+
expect(server.args).toHaveLength(2);
|
|
266
|
+
// First arg should be the server .cjs path (normalize separators for Windows)
|
|
267
|
+
expect(server.args[0].replace(/\\/g, "/")).toContain(
|
|
268
|
+
"mcp-schema-server.cjs",
|
|
269
|
+
);
|
|
270
|
+
// Second arg should be the schema file path
|
|
271
|
+
expect(server.args[1]).toMatch(/droid-cli-mcp-schemas/);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("returns the config file path", () => {
|
|
275
|
+
const toolDefs: McpToolDef[] = [
|
|
276
|
+
{
|
|
277
|
+
name: "search",
|
|
278
|
+
description: "Search",
|
|
279
|
+
inputSchema: { type: "object" },
|
|
280
|
+
},
|
|
281
|
+
];
|
|
282
|
+
|
|
283
|
+
const result = writeMcpConfig(toolDefs);
|
|
284
|
+
|
|
285
|
+
expect(result).toMatch(/droid-cli-mcp-config/);
|
|
286
|
+
expect(result).toMatch(/\.json$/);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("includes cacheKey in filenames when provided so distinct tool sets do not collide", () => {
|
|
290
|
+
const toolDefs: McpToolDef[] = [
|
|
291
|
+
{
|
|
292
|
+
name: "search",
|
|
293
|
+
description: "Search",
|
|
294
|
+
inputSchema: { type: "object" },
|
|
295
|
+
},
|
|
296
|
+
];
|
|
297
|
+
|
|
298
|
+
const pathA = writeMcpConfig(toolDefs, "aaaaaaaaaaaa");
|
|
299
|
+
const pathB = writeMcpConfig(toolDefs, "bbbbbbbbbbbb");
|
|
300
|
+
|
|
301
|
+
expect(pathA).toContain("aaaaaaaaaaaa");
|
|
302
|
+
expect(pathB).toContain("bbbbbbbbbbbb");
|
|
303
|
+
expect(pathA).not.toBe(pathB);
|
|
304
|
+
|
|
305
|
+
const schemaPathA = mocks.writeFileSync.mock.calls[0][0];
|
|
306
|
+
const schemaPathB = mocks.writeFileSync.mock.calls[2][0];
|
|
307
|
+
expect(schemaPathA).toContain("aaaaaaaaaaaa");
|
|
308
|
+
expect(schemaPathB).toContain("bbbbbbbbbbbb");
|
|
309
|
+
});
|
|
310
|
+
});
|