@dyyz1993/create-agent 2.0.0 → 2.1.0

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 (165) hide show
  1. package/package.json +1 -1
  2. package/src/commands/create.ts +31 -31
  3. package/src/commands/workspace.ts +112 -105
  4. package/src/lib/copy.ts +24 -1
  5. package/templates/agent/electron/main.js +46 -0
  6. package/templates/agent/electron/preload.js +5 -0
  7. package/templates/agent/electron-builder.json +40 -0
  8. package/templates/agent/eslint.config.mjs +17 -2
  9. package/templates/agent/package.json +69 -3
  10. package/templates/agent/src/__tests__/hybrid-mode.test.ts +126 -0
  11. package/templates/agent/src/__tests__/server-config-security.test.ts +55 -0
  12. package/templates/agent/src/bun/index.ts +96 -41
  13. package/templates/agent/src/bun/three.d.ts +1 -0
  14. package/templates/agent/src/gateway/__tests__/ws-handler-token.test.ts +118 -0
  15. package/templates/agent/src/gateway/http-routes.ts +1 -1
  16. package/templates/agent/src/gateway/ws-handler.ts +84 -56
  17. package/templates/agent/src/mainview/App.tsx +29 -26
  18. package/templates/agent/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
  19. package/templates/agent/src/mainview/__tests__/setup.ts +32 -29
  20. package/templates/agent/src/mainview/__tests__/theme-variables.test.ts +36 -0
  21. package/templates/agent/src/mainview/components/chat/ChatPanel.tsx +88 -88
  22. package/templates/agent/src/mainview/components/common/ErrorBoundary.tsx +1 -58
  23. package/templates/agent/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
  24. package/templates/agent/src/mainview/components/common/ThemeToggle.tsx +1 -0
  25. package/templates/agent/src/mainview/components/feed/FeedPanel.tsx +232 -210
  26. package/templates/agent/src/mainview/components/file-preview/VirtualizedCodeView.tsx +105 -81
  27. package/templates/agent/src/mainview/components/search/SearchPanel.tsx +427 -378
  28. package/templates/agent/src/mainview/components/todo/TodoPanel.tsx +149 -122
  29. package/templates/agent/src/mainview/hooks/use-input-history.ts +70 -61
  30. package/templates/agent/src/mainview/lib/api-client.ts +179 -172
  31. package/templates/agent/src/mainview/main.tsx +4 -10
  32. package/templates/agent/src/mainview/stores/use-feed-store.ts +107 -107
  33. package/templates/agent/src/mainview/utils/drop-handler.ts +114 -115
  34. package/templates/agent/src/server-config.ts +34 -32
  35. package/templates/agent/src/server.ts +42 -54
  36. package/templates/agent/src/shared/handlers/__tests__/chat-concurrency.test.ts +127 -0
  37. package/templates/agent/src/shared/handlers/__tests__/handler-isolation.test.ts +244 -0
  38. package/templates/agent/src/shared/handlers/bash.ts +65 -65
  39. package/templates/agent/src/shared/handlers/chat.ts +189 -155
  40. package/templates/agent/src/shared/handlers/debug.ts +12 -0
  41. package/templates/agent/src/shared/handlers/feed.ts +31 -32
  42. package/templates/agent/src/shared/handlers/git.ts +286 -243
  43. package/templates/agent/src/shared/handlers/index.ts +1 -0
  44. package/templates/agent/src/shared/handlers/rules.ts +31 -31
  45. package/templates/agent/src/shared/handlers/todo.ts +31 -31
  46. package/templates/agent/src/shared/http-routes.ts +250 -0
  47. package/templates/agent/src/shared/lib/__tests__/web-server.test.ts +190 -0
  48. package/templates/agent/src/shared/lib/bash-security.ts +43 -43
  49. package/templates/agent/src/shared/lib/logger.ts +117 -95
  50. package/templates/agent/src/shared/lib/web-server.ts +128 -0
  51. package/templates/agent/src/shared/modules/debug.ts +8 -0
  52. package/templates/agent/src/shared/rpc-schema.ts +16 -3
  53. package/templates/agent/test-upload.txt +0 -0
  54. package/templates/agent/tsconfig.ipc.json +14 -0
  55. package/templates/agent/tsconfig.json +13 -3
  56. package/templates/chat/eslint.config.mjs +15 -2
  57. package/templates/chat/package.json +13 -2
  58. package/templates/chat/src/__tests__/hybrid-mode.test.ts +125 -0
  59. package/templates/chat/src/__tests__/server-config-security.test.ts +55 -0
  60. package/templates/chat/src/__tests__/server-config.test.ts +46 -44
  61. package/templates/chat/src/bun/index.ts +94 -44
  62. package/templates/chat/src/bun/three.d.ts +1 -0
  63. package/templates/chat/src/gateway/http-routes.ts +1 -1
  64. package/templates/chat/src/gateway/ws-handler.ts +84 -56
  65. package/templates/chat/src/mainview/App.tsx +6 -0
  66. package/templates/chat/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
  67. package/templates/chat/src/mainview/__tests__/theme-variables.test.ts +36 -0
  68. package/templates/chat/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
  69. package/templates/chat/src/mainview/components/common/ThemeToggle.tsx +1 -0
  70. package/templates/chat/src/mainview/hooks/use-input-history.ts +70 -61
  71. package/templates/chat/src/mainview/lib/api-client.ts +180 -156
  72. package/templates/chat/src/mainview/lib/rpc-cache.ts +84 -0
  73. package/templates/chat/src/mainview/main.tsx +10 -6
  74. package/templates/chat/src/server-config.ts +33 -31
  75. package/templates/chat/src/server.ts +42 -60
  76. package/templates/chat/src/shared/handlers/chat.ts +196 -162
  77. package/templates/chat/src/shared/handlers/debug.ts +12 -0
  78. package/templates/chat/src/shared/handlers/index.ts +1 -0
  79. package/templates/chat/src/shared/http-routes.ts +250 -0
  80. package/templates/chat/src/shared/lib/__tests__/logger.test.ts +92 -0
  81. package/templates/chat/src/shared/lib/logger.ts +108 -95
  82. package/templates/chat/src/shared/lib/web-server.ts +128 -0
  83. package/templates/chat/src/shared/modules/debug.ts +8 -0
  84. package/templates/chat/src/shared/rpc-schema.ts +3 -2
  85. package/templates/chat/test-upload.txt +0 -0
  86. package/templates/chat/tsconfig.ipc.json +14 -0
  87. package/templates/chat/tsconfig.json +12 -2
  88. package/templates/general/eslint.config.mjs +15 -2
  89. package/templates/general/package.json +13 -2
  90. package/templates/general/src/__tests__/hybrid-mode.test.ts +125 -0
  91. package/templates/general/src/__tests__/server-config-security.test.ts +55 -0
  92. package/templates/general/src/__tests__/server-config.test.ts +46 -44
  93. package/templates/general/src/bun/index.ts +94 -44
  94. package/templates/general/src/bun/three.d.ts +1 -0
  95. package/templates/general/src/gateway/http-routes.ts +1 -1
  96. package/templates/general/src/gateway/ws-handler.ts +84 -56
  97. package/templates/general/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
  98. package/templates/general/src/mainview/__tests__/theme-variables.test.ts +36 -0
  99. package/templates/general/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
  100. package/templates/general/src/mainview/components/common/ThemeToggle.tsx +1 -0
  101. package/templates/general/src/mainview/components/feed/FeedPanel.tsx +241 -219
  102. package/templates/general/src/mainview/components/file-preview/VirtualizedCodeView.tsx +101 -81
  103. package/templates/general/src/mainview/components/layout/AppLayout.tsx +6 -0
  104. package/templates/general/src/mainview/components/search/SearchPanel.tsx +429 -378
  105. package/templates/general/src/mainview/hooks/use-input-history.ts +70 -61
  106. package/templates/general/src/mainview/lib/api-client.ts +180 -156
  107. package/templates/general/src/mainview/lib/rpc-cache.ts +84 -0
  108. package/templates/general/src/mainview/main.tsx +10 -6
  109. package/templates/general/src/mainview/stores/use-feed-store.ts +107 -107
  110. package/templates/general/src/mainview/utils/drop-handler.ts +114 -115
  111. package/templates/general/src/server-config.ts +33 -31
  112. package/templates/general/src/server.ts +42 -60
  113. package/templates/general/src/shared/handlers/__tests__/chat.test.ts +165 -160
  114. package/templates/general/src/shared/handlers/chat.ts +189 -155
  115. package/templates/general/src/shared/handlers/debug.ts +12 -0
  116. package/templates/general/src/shared/handlers/feed.ts +38 -40
  117. package/templates/general/src/shared/handlers/git.ts +286 -243
  118. package/templates/general/src/shared/handlers/index.ts +1 -0
  119. package/templates/general/src/shared/http-routes.ts +250 -0
  120. package/templates/general/src/shared/lib/__tests__/logger.test.ts +92 -0
  121. package/templates/general/src/shared/lib/logger.ts +117 -95
  122. package/templates/general/src/shared/lib/web-server.ts +128 -0
  123. package/templates/general/src/shared/modules/debug.ts +8 -0
  124. package/templates/general/src/shared/rpc-schema.ts +12 -2
  125. package/templates/general/test-upload.txt +0 -0
  126. package/templates/general/tsconfig.ipc.json +14 -0
  127. package/templates/general/tsconfig.json +12 -2
  128. package/templates/shared/__tests__/cors-security.test.ts +165 -0
  129. package/templates/shared/__tests__/file-path-security.test.ts +260 -0
  130. package/templates/shared/__tests__/http-routes.test.ts +174 -0
  131. package/templates/shared/__tests__/logger.test.ts +186 -0
  132. package/templates/shared/components/ErrorBoundary.tsx +59 -0
  133. package/templates/shared/env.d.ts +11 -0
  134. package/templates/shared/http-routes.ts +250 -0
  135. package/templates/shared/logger.ts +129 -0
  136. package/templates/shared/test-upload.txt +0 -0
  137. package/templates/shared/vite-base.config.ts +92 -0
  138. package/templates/shared/vitest-base.config.ts +42 -0
  139. package/templates/agent/eslint-plugin-rpc/index.js +0 -50
  140. package/templates/agent/eslint-plugin-rpc/package.json +0 -6
  141. package/templates/agent/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
  142. package/templates/agent/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
  143. package/templates/agent/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
  144. package/templates/agent/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
  145. package/templates/agent/eslint-plugin-rpc/rules/require-api-client.js +0 -58
  146. package/templates/agent/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
  147. package/templates/agent/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
  148. package/templates/chat/eslint-plugin-rpc/index.js +0 -50
  149. package/templates/chat/eslint-plugin-rpc/package.json +0 -6
  150. package/templates/chat/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
  151. package/templates/chat/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
  152. package/templates/chat/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
  153. package/templates/chat/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
  154. package/templates/chat/eslint-plugin-rpc/rules/require-api-client.js +0 -58
  155. package/templates/chat/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
  156. package/templates/chat/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
  157. package/templates/general/eslint-plugin-rpc/index.js +0 -50
  158. package/templates/general/eslint-plugin-rpc/package.json +0 -6
  159. package/templates/general/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
  160. package/templates/general/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
  161. package/templates/general/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
  162. package/templates/general/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
  163. package/templates/general/eslint-plugin-rpc/rules/require-api-client.js +0 -58
  164. package/templates/general/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
  165. package/templates/general/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
@@ -0,0 +1,244 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import type { RPCServer } from "@dyyz1993/rpc-core";
3
+
4
+ vi.mock("../../lib/logger", () => ({
5
+ createLogger: () => ({
6
+ info: vi.fn(),
7
+ error: vi.fn(),
8
+ warn: vi.fn(),
9
+ debug: vi.fn(),
10
+ }),
11
+ }));
12
+
13
+ function createMockServer() {
14
+ const handlers: Record<string, Function> = {};
15
+ return {
16
+ handlers,
17
+ server: {
18
+ register: vi.fn((method: string, handler: Function) => {
19
+ handlers[method] = handler;
20
+ }),
21
+ emitEvent: vi.fn(),
22
+ } as unknown as RPCServer,
23
+ };
24
+ }
25
+
26
+ // --- Timer ---
27
+ describe("Handler 隔离性 - timer", () => {
28
+ beforeEach(() => {
29
+ vi.useFakeTimers();
30
+ vi.resetModules();
31
+ });
32
+
33
+ afterEach(() => {
34
+ vi.useRealTimers();
35
+ });
36
+
37
+ it("两个独立的 RPCServer 各自维护独立的定时器状态", async () => {
38
+ const a = createMockServer();
39
+ const b = createMockServer();
40
+
41
+ const { register } = await import("../timer");
42
+ register(a.server, { platform: "web" });
43
+ register(b.server, { platform: "web" });
44
+
45
+ const resultA = await a.handlers["timer.start"]({});
46
+ expect(resultA).toEqual({ started: true });
47
+
48
+ const resultB = await b.handlers["timer.start"]({});
49
+ expect(resultB).toEqual({ started: true });
50
+
51
+ await a.handlers["timer.stop"]({});
52
+ const resultB2 = await b.handlers["timer.start"]({});
53
+ expect(resultB2).toEqual({ alreadyRunning: true });
54
+ });
55
+
56
+ it("server A 停止定时器不影响 server B 的定时器", async () => {
57
+ const a = createMockServer();
58
+ const b = createMockServer();
59
+
60
+ const { register } = await import("../timer");
61
+ register(a.server, { platform: "web" });
62
+ register(b.server, { platform: "web" });
63
+
64
+ await a.handlers["timer.start"]({});
65
+ await b.handlers["timer.start"]({});
66
+
67
+ await a.handlers["timer.stop"]({});
68
+
69
+ vi.advanceTimersByTime(2000);
70
+ expect(b.server.emitEvent).toHaveBeenCalled();
71
+ });
72
+ });
73
+
74
+ // --- Bash ---
75
+ describe("Handler 隔离性 - bash", () => {
76
+ beforeEach(() => {
77
+ vi.resetModules();
78
+ vi.stubGlobal("Bun", { spawn: vi.fn() });
79
+ vi.stubGlobal(
80
+ "Response",
81
+ class {
82
+ private src: any;
83
+ constructor(src: any) {
84
+ this.src = src;
85
+ }
86
+ async text() {
87
+ if (this.src && typeof this.src.text === "function") return this.src.text();
88
+ return String(this.src);
89
+ }
90
+ }
91
+ );
92
+ });
93
+
94
+ it("两个独立的 RPCServer 各自维护独立的进程列表", async () => {
95
+ const a = createMockServer();
96
+ const b = createMockServer();
97
+
98
+ const { register } = await import("../bash");
99
+ register(a.server, { platform: "web" });
100
+ register(b.server, { platform: "web" });
101
+
102
+ const mockSub = {
103
+ pid: 100,
104
+ stdout: { text: () => Promise.resolve("ok") },
105
+ stderr: { text: () => Promise.resolve("") },
106
+ exited: Promise.resolve(0),
107
+ };
108
+ (Bun.spawn as ReturnType<typeof vi.fn>).mockReturnValue(mockSub);
109
+
110
+ const resultA = await a.handlers["bash.execute"]({ command: "echo A" });
111
+ const resultB = await b.handlers["bash.execute"]({ command: "echo B" });
112
+
113
+ expect(resultA.pid).toBe(1);
114
+ expect(resultB.pid).toBe(1);
115
+ });
116
+
117
+ it("server A 的 pidCounter 不影响 server B", async () => {
118
+ const a = createMockServer();
119
+ const b = createMockServer();
120
+
121
+ const { register } = await import("../bash");
122
+ register(a.server, { platform: "web" });
123
+ register(b.server, { platform: "web" });
124
+
125
+ const mockSub = {
126
+ pid: 100,
127
+ stdout: { text: () => Promise.resolve("ok") },
128
+ stderr: { text: () => Promise.resolve("") },
129
+ exited: Promise.resolve(0),
130
+ };
131
+ (Bun.spawn as ReturnType<typeof vi.fn>).mockReturnValue(mockSub);
132
+
133
+ await a.handlers["bash.execute"]({ command: "ls" });
134
+ await a.handlers["bash.execute"]({ command: "ls" });
135
+ await a.handlers["bash.execute"]({ command: "ls" });
136
+
137
+ const resultB = await b.handlers["bash.execute"]({ command: "ls" });
138
+ expect(resultB.pid).toBe(1);
139
+ });
140
+ });
141
+
142
+ // --- Feed ---
143
+ describe("Handler 隔离性 - feed", () => {
144
+ beforeEach(() => {
145
+ vi.resetModules();
146
+ });
147
+
148
+ it("两个独立的 RPCServer 各自维护独立的帖子列表", async () => {
149
+ const a = createMockServer();
150
+ const b = createMockServer();
151
+
152
+ const { register } = await import("../feed");
153
+ register(a.server, { platform: "web" });
154
+ register(b.server, { platform: "web" });
155
+
156
+ await a.handlers["feed.post"]({ content: "post from A", category: "tech" });
157
+
158
+ const listB = await b.handlers["feed.list"]({});
159
+ expect(listB.posts).toHaveLength(0);
160
+
161
+ const listA = await a.handlers["feed.list"]({});
162
+ expect(listA.posts).toHaveLength(1);
163
+ expect(listA.posts[0].content).toBe("post from A");
164
+ });
165
+ });
166
+
167
+ // --- Todo ---
168
+ describe("Handler 隔离性 - todo", () => {
169
+ beforeEach(() => {
170
+ vi.resetModules();
171
+ });
172
+
173
+ it("两个独立的 RPCServer 各自维护独立的 todo 列表", async () => {
174
+ const a = createMockServer();
175
+ const b = createMockServer();
176
+
177
+ const { register } = await import("../todo");
178
+ register(a.server, { platform: "web" });
179
+ register(b.server, { platform: "web" });
180
+
181
+ await a.handlers["todo.add"]({ content: "task from A" });
182
+
183
+ const listB = await b.handlers["todo.list"]({});
184
+ expect(listB.items).toHaveLength(0);
185
+
186
+ const listA = await a.handlers["todo.list"]({});
187
+ expect(listA.items).toHaveLength(1);
188
+ });
189
+
190
+ it("server A 的 todoIdCounter 不影响 server B", async () => {
191
+ const a = createMockServer();
192
+ const b = createMockServer();
193
+
194
+ const { register } = await import("../todo");
195
+ register(a.server, { platform: "web" });
196
+ register(b.server, { platform: "web" });
197
+
198
+ await a.handlers["todo.add"]({ content: "A1" });
199
+ await a.handlers["todo.add"]({ content: "A2" });
200
+ await a.handlers["todo.add"]({ content: "A3" });
201
+
202
+ const resultB = await b.handlers["todo.add"]({ content: "B1" });
203
+ expect(resultB.item.id).toBe("todo-1");
204
+ });
205
+ });
206
+
207
+ // --- Rules ---
208
+ describe("Handler 隔离性 - rules", () => {
209
+ beforeEach(() => {
210
+ vi.resetModules();
211
+ });
212
+
213
+ it("两个独立的 RPCServer 各自维护独立的规则列表", async () => {
214
+ const a = createMockServer();
215
+ const b = createMockServer();
216
+
217
+ const { register } = await import("../rules");
218
+ register(a.server, { platform: "web" });
219
+ register(b.server, { platform: "web" });
220
+
221
+ await a.handlers["rules.add"]({ name: "rule A", pattern: "*.ts" });
222
+
223
+ const listB = await b.handlers["rules.list"]({});
224
+ expect(listB.rules).toHaveLength(0);
225
+
226
+ const listA = await a.handlers["rules.list"]({});
227
+ expect(listA.rules).toHaveLength(1);
228
+ });
229
+
230
+ it("server A 的 ruleIdCounter 不影响 server B", async () => {
231
+ const a = createMockServer();
232
+ const b = createMockServer();
233
+
234
+ const { register } = await import("../rules");
235
+ register(a.server, { platform: "web" });
236
+ register(b.server, { platform: "web" });
237
+
238
+ await a.handlers["rules.add"]({ name: "A1", pattern: "*.ts" });
239
+ await a.handlers["rules.add"]({ name: "A2", pattern: "*.js" });
240
+
241
+ const resultB = await b.handlers["rules.add"]({ name: "B1", pattern: "*.css" });
242
+ expect(resultB.rule.id).toBe("rule-1");
243
+ });
244
+ });
@@ -4,86 +4,86 @@ import type { RPCMethods, HandlerOptions } from "../rpc-schema";
4
4
  import { validateCommand, setCommandPolicy } from "../lib/bash-security";
5
5
 
6
6
  type RegisterFn = <K extends keyof RPCMethods & string>(
7
- method: K,
8
- handler: (params: MethodParams<RPCMethods, K>) => Promise<MethodResult<RPCMethods, K>>,
7
+ method: K,
8
+ handler: (params: MethodParams<RPCMethods, K>) => Promise<MethodResult<RPCMethods, K>>
9
9
  ) => void;
10
10
 
11
11
  interface TrackedProcess {
12
- pid: number;
13
- command: string;
14
- running: boolean;
12
+ pid: number;
13
+ command: string;
14
+ running: boolean;
15
15
  }
16
16
 
17
- const processes = new Map<number, TrackedProcess>();
18
- let pidCounter = 1;
19
-
20
17
  export function register(server: RPCServer, _options: HandlerOptions): void {
21
- setCommandPolicy({
22
- enabled: _options.enableBash !== false,
23
- blockedPatterns: [
24
- /rm\s+-rf\s+(.*\s)?\/($|\s)/,
25
- /rm\s+-rf\s+--no-preserve-root/,
26
- /mkfs/,
27
- /dd\s+if=/,
28
- />\s*\/dev\//,
29
- /:()\s*\{.*\|.*&\s*\}/,
30
- /shutdown/,
31
- /reboot/,
32
- ],
33
- allowedCommands: null,
34
- });
18
+ const processes = new Map<number, TrackedProcess>();
19
+ let pidCounter = 1;
20
+
21
+ setCommandPolicy({
22
+ enabled: _options.enableBash !== false,
23
+ blockedPatterns: [
24
+ /rm\s+-rf\s+(.*\s)?\/($|\s)/,
25
+ /rm\s+-rf\s+--no-preserve-root/,
26
+ /mkfs/,
27
+ /dd\s+if=/,
28
+ />\s*\/dev\//,
29
+ /:()\s*\{.*\|.*&\s*\}/,
30
+ /shutdown/,
31
+ /reboot/,
32
+ ],
33
+ allowedCommands: null,
34
+ });
35
35
 
36
- const r: RegisterFn = (method, handler) => {
37
- server.register(method, handler as (params: unknown) => Promise<unknown>);
38
- };
36
+ const r: RegisterFn = (method, handler) => {
37
+ server.register(method, handler as (params: unknown) => Promise<unknown>);
38
+ };
39
39
 
40
- r("bash.execute", async (params) => {
41
- const safeCommand = validateCommand(params.command);
42
- const pid = pidCounter++;
43
- const proc: TrackedProcess = { pid, command: safeCommand, running: true };
44
- processes.set(pid, proc);
40
+ r("bash.execute", async (params) => {
41
+ const safeCommand = validateCommand(params.command);
42
+ const pid = pidCounter++;
43
+ const proc: TrackedProcess = { pid, command: safeCommand, running: true };
44
+ processes.set(pid, proc);
45
45
 
46
- try {
47
- const subprocess = Bun.spawn(safeCommand.split(" "), {
48
- cwd: params.cwd || process.cwd(),
49
- stdout: "pipe",
50
- stderr: "pipe",
51
- });
46
+ try {
47
+ const subprocess = Bun.spawn(safeCommand.split(" "), {
48
+ cwd: params.cwd || process.cwd(),
49
+ stdout: "pipe",
50
+ stderr: "pipe",
51
+ });
52
52
 
53
- proc.pid = subprocess.pid;
53
+ proc.pid = subprocess.pid;
54
54
 
55
- const stdout = await new Response(subprocess.stdout).text();
56
- const stderr = await new Response(subprocess.stderr).text();
57
- const exitCode = await subprocess.exited;
55
+ const stdout = await new Response(subprocess.stdout).text();
56
+ const stderr = await new Response(subprocess.stderr).text();
57
+ const exitCode = await subprocess.exited;
58
58
 
59
- proc.running = false;
59
+ proc.running = false;
60
60
 
61
- const output = stdout + (stderr ? `\n[stderr]\n${stderr}` : "");
61
+ const output = stdout + (stderr ? `\n[stderr]\n${stderr}` : "");
62
62
 
63
- server.emitEvent("bash.output", { pid, data: output, stream: "stdout" }, {});
64
- server.emitEvent("bash.exit", { pid, code: exitCode }, {});
63
+ server.emitEvent("bash.output", { pid, data: output, stream: "stdout" }, {});
64
+ server.emitEvent("bash.exit", { pid, code: exitCode }, {});
65
65
 
66
- return { pid, output };
67
- } catch (err) {
68
- proc.running = false;
69
- const output = `Error: ${err instanceof Error ? err.message : String(err)}`;
70
- server.emitEvent("bash.output", { pid, data: output, stream: "stderr" }, {});
71
- server.emitEvent("bash.exit", { pid, code: 1 }, {});
72
- return { pid, output };
73
- }
74
- });
66
+ return { pid, output };
67
+ } catch (err) {
68
+ proc.running = false;
69
+ const output = `Error: ${err instanceof Error ? err.message : String(err)}`;
70
+ server.emitEvent("bash.output", { pid, data: output, stream: "stderr" }, {});
71
+ server.emitEvent("bash.exit", { pid, code: 1 }, {});
72
+ return { pid, output };
73
+ }
74
+ });
75
75
 
76
- r("bash.kill", async (params) => {
77
- const proc = processes.get(params.pid);
78
- if (proc) {
79
- proc.running = false;
80
- processes.delete(params.pid);
81
- return { success: true };
82
- }
83
- return { success: false };
84
- });
76
+ r("bash.kill", async (params) => {
77
+ const proc = processes.get(params.pid);
78
+ if (proc) {
79
+ proc.running = false;
80
+ processes.delete(params.pid);
81
+ return { success: true };
82
+ }
83
+ return { success: false };
84
+ });
85
85
 
86
- r("bash.listProcesses", async () => ({
87
- processes: Array.from(processes.values()),
88
- }));
86
+ r("bash.listProcesses", async () => ({
87
+ processes: Array.from(processes.values()),
88
+ }));
89
89
  }