@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.
Files changed (72) hide show
  1. package/README.md +13 -0
  2. package/dist/bin.js +1332 -528
  3. package/dist/client/assets/AgentDetailView-B3KAsP2O.js +18 -0
  4. package/dist/client/assets/{AgentsView-Dvf_xUkx.js → AgentsView-DoXb_amw.js} +4 -4
  5. package/dist/client/assets/ChatView-BJ2c7wvd.js +1 -0
  6. package/dist/client/assets/{DevServerView-C2qTJch7.js → DevServerView-DbgM4tlT.js} +1 -1
  7. package/dist/client/assets/{DirectoryPicker-DRfhg9zz.js → DirectoryPicker-DfmtfMiu.js} +1 -1
  8. package/dist/client/assets/{DocumentsView-j8ic1xUw.js → DocumentsView-_-Efkx_W.js} +1 -1
  9. package/dist/client/assets/{InsightsView-CpAz3o0i.js → InsightsView-DUjcfW53.js} +1 -1
  10. package/dist/client/assets/{MemoryView-BcQsi_JK.js → MemoryView-DxMPBb0q.js} +1 -1
  11. package/dist/client/assets/{NodesView-Bo_Yhr4N.js → NodesView-BEBTI15s.js} +1 -1
  12. package/dist/client/assets/PiExtensionsManager-BpMYhHH_.js +11 -0
  13. package/dist/client/assets/PluginManager-CPv7yQd3.js +1 -0
  14. package/dist/client/assets/PluginManager-DA_T0GHn.css +1 -0
  15. package/dist/client/assets/{ResearchView-CLyyqAWE.js → ResearchView-BrFvdyXT.js} +1 -1
  16. package/dist/client/assets/{RoadmapsView-tG7IdOoc.js → RoadmapsView-BDjLrtcj.js} +1 -1
  17. package/dist/client/assets/SettingsModal-Cd-QGB0C.js +31 -0
  18. package/dist/client/assets/{SettingsModal-CXUGeZ0_.js → SettingsModal-CxDxiTRy.js} +1 -1
  19. package/dist/client/assets/SettingsModal-D_AFkDJa.css +1 -0
  20. package/dist/client/assets/{SetupWizardModal-BMJL6eNR.js → SetupWizardModal-DFUA4X3z.js} +1 -1
  21. package/dist/client/assets/{SkillMultiselect-ILMft-Kz.js → SkillMultiselect-BUWe5ujb.js} +1 -1
  22. package/dist/client/assets/{SkillsView-x4_YwBz6.js → SkillsView-RAkqGX3y.js} +1 -1
  23. package/dist/client/assets/TodoView-Ceb0wrg1.js +6 -0
  24. package/dist/client/assets/TodoView-SeO9o7km.css +1 -0
  25. package/dist/client/assets/{folder-open-DDdJt8aE.js → folder-open-DcM-Vd6r.js} +1 -1
  26. package/dist/client/assets/index-C1prPuSl.css +1 -0
  27. package/dist/client/assets/index-DH3aprf6.js +661 -0
  28. package/dist/client/assets/{list-checks-DFxQ9biT.js → list-checks-ByGHVQpZ.js} +1 -1
  29. package/dist/client/assets/{star-BKs1bgJN.js → star-DlEYI8GL.js} +1 -1
  30. package/dist/client/assets/{upload-Bb5Pidne.js → upload-DKshabz-.js} +1 -1
  31. package/dist/client/assets/{users-BImNn91Q.js → users-X6tYPPBV.js} +1 -1
  32. package/dist/client/index.html +2 -2
  33. package/dist/client/sw.js +6 -0
  34. package/dist/client/version.json +1 -1
  35. package/dist/droid-cli/index.ts +127 -0
  36. package/dist/droid-cli/package.json +37 -0
  37. package/dist/droid-cli/src/__tests__/control-handler.test.ts +164 -0
  38. package/dist/droid-cli/src/__tests__/event-bridge.test.ts +1318 -0
  39. package/dist/droid-cli/src/__tests__/mcp-config.test.ts +310 -0
  40. package/dist/droid-cli/src/__tests__/process-manager.test.ts +818 -0
  41. package/dist/droid-cli/src/__tests__/prompt-builder.test.ts +1206 -0
  42. package/dist/droid-cli/src/__tests__/provider.test.ts +1894 -0
  43. package/dist/droid-cli/src/__tests__/setup-test-isolation.test.ts +32 -0
  44. package/dist/droid-cli/src/__tests__/setup-test-isolation.ts +14 -0
  45. package/dist/droid-cli/src/__tests__/stream-parser.test.ts +188 -0
  46. package/dist/droid-cli/src/__tests__/thinking-config.test.ts +141 -0
  47. package/dist/droid-cli/src/__tests__/tool-mapping.test.ts +253 -0
  48. package/dist/droid-cli/src/control-handler.ts +82 -0
  49. package/dist/droid-cli/src/event-bridge.ts +397 -0
  50. package/dist/droid-cli/src/mcp-config.ts +144 -0
  51. package/dist/droid-cli/src/mcp-schema-server.cjs +49 -0
  52. package/dist/droid-cli/src/process-manager.ts +358 -0
  53. package/dist/droid-cli/src/prompt-builder.ts +629 -0
  54. package/dist/droid-cli/src/provider.ts +447 -0
  55. package/dist/droid-cli/src/stream-parser.ts +37 -0
  56. package/dist/droid-cli/src/thinking-config.ts +83 -0
  57. package/dist/droid-cli/src/tool-mapping.ts +147 -0
  58. package/dist/droid-cli/src/types.ts +87 -0
  59. package/dist/extension.js +555 -125
  60. package/dist/pi-claude-cli/package.json +1 -1
  61. package/package.json +2 -1
  62. package/dist/client/assets/AgentDetailView-B7j297GT.js +0 -18
  63. package/dist/client/assets/ChatView-BgUt38ty.js +0 -1
  64. package/dist/client/assets/PiExtensionsManager-DHt2zFg8.js +0 -11
  65. package/dist/client/assets/PluginManager-BQhBHWrB.js +0 -1
  66. package/dist/client/assets/PluginManager-jyNkJZSz.css +0 -1
  67. package/dist/client/assets/SettingsModal-9HS8MnmW.css +0 -1
  68. package/dist/client/assets/SettingsModal-UziTDnLh.js +0 -31
  69. package/dist/client/assets/TodoView-BBYcMbXE.js +0 -6
  70. package/dist/client/assets/TodoView-C1g65hJo.css +0 -1
  71. package/dist/client/assets/index-B15xwijw.css +0 -1
  72. 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
+ });