@agentmeshhq/agent 0.2.0 → 0.3.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 (147) hide show
  1. package/README.md +39 -0
  2. package/dist/__tests__/context-template.test.d.ts +4 -0
  3. package/dist/__tests__/context-template.test.js +233 -0
  4. package/dist/__tests__/context-template.test.js.map +1 -0
  5. package/dist/__tests__/loader.test.js +140 -28
  6. package/dist/__tests__/loader.test.js.map +1 -1
  7. package/dist/__tests__/no-respawn.test.d.ts +1 -0
  8. package/dist/__tests__/no-respawn.test.js +254 -0
  9. package/dist/__tests__/no-respawn.test.js.map +1 -0
  10. package/dist/__tests__/onboard.test.d.ts +5 -0
  11. package/dist/__tests__/onboard.test.js +341 -0
  12. package/dist/__tests__/onboard.test.js.map +1 -0
  13. package/dist/__tests__/orphan-process.test.d.ts +11 -0
  14. package/dist/__tests__/orphan-process.test.js +286 -0
  15. package/dist/__tests__/orphan-process.test.js.map +1 -0
  16. package/dist/__tests__/runner.test.js +16 -0
  17. package/dist/__tests__/runner.test.js.map +1 -1
  18. package/dist/__tests__/shared-resource-guards.test.d.ts +7 -0
  19. package/dist/__tests__/shared-resource-guards.test.js +260 -0
  20. package/dist/__tests__/shared-resource-guards.test.js.map +1 -0
  21. package/dist/__tests__/watchdog.test.js +138 -12
  22. package/dist/__tests__/watchdog.test.js.map +1 -1
  23. package/dist/cli/status.js +11 -0
  24. package/dist/cli/status.js.map +1 -1
  25. package/dist/cli/stop.js +7 -2
  26. package/dist/cli/stop.js.map +1 -1
  27. package/dist/config/loader.d.ts +0 -4
  28. package/dist/config/loader.js +102 -42
  29. package/dist/config/loader.js.map +1 -1
  30. package/dist/config/schema.d.ts +6 -4
  31. package/dist/core/daemon/assignment-message.d.ts +12 -0
  32. package/dist/core/daemon/assignment-message.js +36 -0
  33. package/dist/core/daemon/assignment-message.js.map +1 -0
  34. package/dist/core/daemon/bootstrap.d.ts +35 -0
  35. package/dist/core/daemon/bootstrap.js +52 -0
  36. package/dist/core/daemon/bootstrap.js.map +1 -0
  37. package/dist/core/daemon/context-template.d.ts +11 -0
  38. package/dist/core/daemon/context-template.js +144 -0
  39. package/dist/core/daemon/context-template.js.map +1 -0
  40. package/dist/core/daemon/crash-log.d.ts +14 -0
  41. package/dist/core/daemon/crash-log.js +23 -0
  42. package/dist/core/daemon/crash-log.js.map +1 -0
  43. package/dist/core/daemon/git-auth.d.ts +18 -0
  44. package/dist/core/daemon/git-auth.js +88 -0
  45. package/dist/core/daemon/git-auth.js.map +1 -0
  46. package/dist/core/daemon/health-policy.d.ts +17 -0
  47. package/dist/core/daemon/health-policy.js +24 -0
  48. package/dist/core/daemon/health-policy.js.map +1 -0
  49. package/dist/core/daemon/sandbox-config.d.ts +9 -0
  50. package/dist/core/daemon/sandbox-config.js +17 -0
  51. package/dist/core/daemon/sandbox-config.js.map +1 -0
  52. package/dist/core/daemon/state.d.ts +33 -0
  53. package/dist/core/daemon/state.js +78 -0
  54. package/dist/core/daemon/state.js.map +1 -0
  55. package/dist/core/daemon/tmux-session.d.ts +17 -0
  56. package/dist/core/daemon/tmux-session.js +34 -0
  57. package/dist/core/daemon/tmux-session.js.map +1 -0
  58. package/dist/core/daemon/workspace.d.ts +23 -0
  59. package/dist/core/daemon/workspace.js +90 -0
  60. package/dist/core/daemon/workspace.js.map +1 -0
  61. package/dist/core/daemon.d.ts +9 -12
  62. package/dist/core/daemon.js +293 -393
  63. package/dist/core/daemon.js.map +1 -1
  64. package/dist/core/injector.d.ts +5 -1
  65. package/dist/core/injector.js +83 -0
  66. package/dist/core/injector.js.map +1 -1
  67. package/dist/core/registry.d.ts +62 -0
  68. package/dist/core/registry.js +18 -0
  69. package/dist/core/registry.js.map +1 -1
  70. package/dist/core/runner/build.d.ts +9 -0
  71. package/dist/core/runner/build.js +53 -0
  72. package/dist/core/runner/build.js.map +1 -0
  73. package/dist/core/runner/detect.d.ts +5 -0
  74. package/dist/core/runner/detect.js +14 -0
  75. package/dist/core/runner/detect.js.map +1 -0
  76. package/dist/core/runner/index.d.ts +5 -0
  77. package/dist/core/runner/index.js +5 -0
  78. package/dist/core/runner/index.js.map +1 -0
  79. package/dist/core/runner/model.d.ts +5 -0
  80. package/dist/core/runner/model.js +7 -0
  81. package/dist/core/runner/model.js.map +1 -0
  82. package/dist/core/runner/opencode-models.d.ts +15 -0
  83. package/dist/core/runner/opencode-models.js +70 -0
  84. package/dist/core/runner/opencode-models.js.map +1 -0
  85. package/dist/core/runner/types.d.ts +19 -0
  86. package/dist/core/runner/types.js +8 -0
  87. package/dist/core/runner/types.js.map +1 -0
  88. package/dist/core/runner.d.ts +5 -47
  89. package/dist/core/runner.js +5 -167
  90. package/dist/core/runner.js.map +1 -1
  91. package/dist/core/tmux-runtime.d.ts +13 -0
  92. package/dist/core/tmux-runtime.js +72 -0
  93. package/dist/core/tmux-runtime.js.map +1 -0
  94. package/dist/core/tmux.d.ts +7 -1
  95. package/dist/core/tmux.js +75 -45
  96. package/dist/core/tmux.js.map +1 -1
  97. package/dist/core/watchdog.d.ts +18 -1
  98. package/dist/core/watchdog.js +78 -29
  99. package/dist/core/watchdog.js.map +1 -1
  100. package/package.json +24 -4
  101. package/src/__tests__/context.test.ts +0 -464
  102. package/src/__tests__/injector.test.ts +0 -29
  103. package/src/__tests__/jwt.test.ts +0 -112
  104. package/src/__tests__/loader.test.ts +0 -239
  105. package/src/__tests__/runner.test.ts +0 -104
  106. package/src/__tests__/sandbox.test.ts +0 -435
  107. package/src/__tests__/watchdog.test.ts +0 -368
  108. package/src/cli/attach.ts +0 -22
  109. package/src/cli/build.ts +0 -145
  110. package/src/cli/config.ts +0 -148
  111. package/src/cli/context.ts +0 -231
  112. package/src/cli/deploy.ts +0 -155
  113. package/src/cli/index.ts +0 -376
  114. package/src/cli/init.ts +0 -75
  115. package/src/cli/list.ts +0 -70
  116. package/src/cli/local.ts +0 -183
  117. package/src/cli/logs.ts +0 -64
  118. package/src/cli/migrate.ts +0 -212
  119. package/src/cli/nudge.ts +0 -81
  120. package/src/cli/restart.ts +0 -59
  121. package/src/cli/slack.ts +0 -70
  122. package/src/cli/start.ts +0 -118
  123. package/src/cli/status.ts +0 -91
  124. package/src/cli/stop.ts +0 -48
  125. package/src/cli/test.ts +0 -143
  126. package/src/cli/token.ts +0 -188
  127. package/src/cli/whoami.ts +0 -142
  128. package/src/config/loader.ts +0 -121
  129. package/src/config/schema.ts +0 -68
  130. package/src/context/handoff.ts +0 -122
  131. package/src/context/index.ts +0 -8
  132. package/src/context/schema.ts +0 -111
  133. package/src/context/storage.ts +0 -197
  134. package/src/core/daemon.ts +0 -1317
  135. package/src/core/heartbeat.ts +0 -129
  136. package/src/core/injector.ts +0 -292
  137. package/src/core/registry.ts +0 -159
  138. package/src/core/runner.ts +0 -225
  139. package/src/core/sandbox.ts +0 -547
  140. package/src/core/session-id.ts +0 -111
  141. package/src/core/tmux.ts +0 -405
  142. package/src/core/watchdog.ts +0 -238
  143. package/src/core/websocket.ts +0 -94
  144. package/src/index.ts +0 -10
  145. package/src/utils/jwt.ts +0 -87
  146. package/tsconfig.json +0 -8
  147. package/vitest.config.ts +0 -12
@@ -1,435 +0,0 @@
1
- import os from "node:os";
2
- import path from "node:path";
3
- import { beforeEach, describe, expect, it, vi } from "vitest";
4
- import { DockerSandbox, type SandboxConfig, type SandboxMountPolicy } from "../core/sandbox.js";
5
-
6
- // Mock child_process
7
- vi.mock("node:child_process", () => ({
8
- execSync: vi.fn(),
9
- spawn: vi.fn(() => ({
10
- on: vi.fn(),
11
- unref: vi.fn(),
12
- pid: 12345,
13
- })),
14
- spawnSync: vi.fn(() => ({
15
- status: 0,
16
- stdout: "mock-container-id\n",
17
- stderr: "",
18
- })),
19
- }));
20
-
21
- // Mock fs
22
- vi.mock("node:fs", () => ({
23
- default: {
24
- existsSync: vi.fn(() => true),
25
- },
26
- }));
27
-
28
- describe("DockerSandbox", () => {
29
- const defaultConfig: SandboxConfig = {
30
- agentName: "test-agent",
31
- image: "agentmesh/agent-sandbox:latest",
32
- workspacePath: "/tmp/workspace",
33
- };
34
-
35
- beforeEach(() => {
36
- vi.clearAllMocks();
37
- });
38
-
39
- describe("constructor", () => {
40
- it("should create sandbox with default values", () => {
41
- const sandbox = new DockerSandbox(defaultConfig);
42
-
43
- expect(sandbox.getContainerName()).toMatch(/^agentmesh-sandbox-test-agent-[a-f0-9]{8}$/);
44
- });
45
-
46
- it("should merge config with defaults", () => {
47
- const config: SandboxConfig = {
48
- ...defaultConfig,
49
- cpuLimit: "0.5",
50
- memoryLimit: "1g",
51
- };
52
-
53
- const sandbox = new DockerSandbox(config);
54
- expect(sandbox).toBeDefined();
55
- });
56
- });
57
-
58
- describe("validateMountPolicy", () => {
59
- it("should deny sensitive paths by default", () => {
60
- const sandbox = new DockerSandbox({
61
- ...defaultConfig,
62
- workspacePath: path.join(os.homedir(), ".ssh"),
63
- });
64
-
65
- expect(() => sandbox.validateMountPolicy()).toThrow(/Mount denied.*\.ssh/);
66
- });
67
-
68
- it("should deny ~/.gnupg", () => {
69
- const sandbox = new DockerSandbox({
70
- ...defaultConfig,
71
- workspacePath: path.join(os.homedir(), ".gnupg"),
72
- });
73
-
74
- expect(() => sandbox.validateMountPolicy()).toThrow(/Mount denied.*\.gnupg/);
75
- });
76
-
77
- it("should deny ~/.aws", () => {
78
- const sandbox = new DockerSandbox({
79
- ...defaultConfig,
80
- workspacePath: path.join(os.homedir(), ".aws"),
81
- });
82
-
83
- expect(() => sandbox.validateMountPolicy()).toThrow(/Mount denied.*\.aws/);
84
- });
85
-
86
- it("should deny ~/.config/gcloud", () => {
87
- const sandbox = new DockerSandbox({
88
- ...defaultConfig,
89
- workspacePath: path.join(os.homedir(), ".config/gcloud"),
90
- });
91
-
92
- expect(() => sandbox.validateMountPolicy()).toThrow(/Mount denied.*gcloud/);
93
- });
94
-
95
- it("should deny ~/.kube", () => {
96
- const sandbox = new DockerSandbox({
97
- ...defaultConfig,
98
- workspacePath: path.join(os.homedir(), ".kube"),
99
- });
100
-
101
- expect(() => sandbox.validateMountPolicy()).toThrow(/Mount denied.*\.kube/);
102
- });
103
-
104
- it("should deny ~/.docker", () => {
105
- const sandbox = new DockerSandbox({
106
- ...defaultConfig,
107
- workspacePath: path.join(os.homedir(), ".docker"),
108
- });
109
-
110
- expect(() => sandbox.validateMountPolicy()).toThrow(/Mount denied.*\.docker/);
111
- });
112
-
113
- it("should deny /var/run/docker.sock", () => {
114
- const sandbox = new DockerSandbox({
115
- ...defaultConfig,
116
- workspacePath: "/var/run/docker.sock",
117
- });
118
-
119
- expect(() => sandbox.validateMountPolicy()).toThrow(/Mount denied.*docker\.sock/);
120
- });
121
-
122
- it("should allow regular workspace paths", () => {
123
- const sandbox = new DockerSandbox({
124
- ...defaultConfig,
125
- workspacePath: "/tmp/workspace",
126
- });
127
-
128
- expect(() => sandbox.validateMountPolicy()).not.toThrow();
129
- });
130
-
131
- it("should enforce custom allowed paths when specified", () => {
132
- const sandbox = new DockerSandbox({
133
- ...defaultConfig,
134
- workspacePath: "/home/user/random",
135
- });
136
-
137
- const policy: SandboxMountPolicy = {
138
- allowedPaths: ["/home/user/allowed-only"],
139
- deniedPaths: [],
140
- };
141
-
142
- expect(() => sandbox.validateMountPolicy(policy)).toThrow(/not in allowed paths/);
143
- });
144
-
145
- it("should allow path within custom allowed paths", () => {
146
- const sandbox = new DockerSandbox({
147
- ...defaultConfig,
148
- workspacePath: "/home/user/projects/myrepo",
149
- });
150
-
151
- const policy: SandboxMountPolicy = {
152
- allowedPaths: ["/home/user/projects"],
153
- deniedPaths: [],
154
- };
155
-
156
- expect(() => sandbox.validateMountPolicy(policy)).not.toThrow();
157
- });
158
- });
159
-
160
- describe("checkDockerAvailable", () => {
161
- it("should return true when docker is available", async () => {
162
- const { spawnSync } = await import("node:child_process");
163
- vi.mocked(spawnSync).mockReturnValueOnce({
164
- status: 0,
165
- stdout: "Docker version 24.0.0",
166
- stderr: "",
167
- pid: 1,
168
- output: [],
169
- signal: null,
170
- });
171
-
172
- expect(DockerSandbox.checkDockerAvailable()).toBe(true);
173
- });
174
-
175
- it("should return false when docker is not available", async () => {
176
- const { spawnSync } = await import("node:child_process");
177
- vi.mocked(spawnSync).mockReturnValueOnce({
178
- status: 1,
179
- stdout: "",
180
- stderr: "docker: command not found",
181
- pid: 1,
182
- output: [],
183
- signal: null,
184
- });
185
-
186
- expect(DockerSandbox.checkDockerAvailable()).toBe(false);
187
- });
188
- });
189
-
190
- describe("listAll", () => {
191
- it("should parse docker ps output correctly", async () => {
192
- const { spawnSync } = await import("node:child_process");
193
- vi.mocked(spawnSync).mockReturnValueOnce({
194
- status: 0,
195
- stdout:
196
- "abc123|agentmesh-sandbox-agent1-12345678|Up 5 minutes|agentmesh/agent-sandbox:latest\ndef456|agentmesh-sandbox-agent2-87654321|Exited (0)|agentmesh/agent-sandbox:latest",
197
- stderr: "",
198
- pid: 1,
199
- output: [],
200
- signal: null,
201
- });
202
-
203
- const containers = DockerSandbox.listAll();
204
-
205
- expect(containers).toHaveLength(2);
206
- expect(containers[0]).toEqual({
207
- id: "abc123",
208
- name: "agentmesh-sandbox-agent1-12345678",
209
- status: "Up 5 minutes",
210
- image: "agentmesh/agent-sandbox:latest",
211
- });
212
- });
213
-
214
- it("should return empty array when no containers found", async () => {
215
- const { spawnSync } = await import("node:child_process");
216
- vi.mocked(spawnSync).mockReturnValueOnce({
217
- status: 0,
218
- stdout: "",
219
- stderr: "",
220
- pid: 1,
221
- output: [],
222
- signal: null,
223
- });
224
-
225
- const containers = DockerSandbox.listAll();
226
- expect(containers).toEqual([]);
227
- });
228
- });
229
-
230
- describe("findExisting", () => {
231
- it("should find existing container for agent", async () => {
232
- const { spawnSync } = await import("node:child_process");
233
- vi.mocked(spawnSync).mockReturnValueOnce({
234
- status: 0,
235
- stdout: "abc123def456\n",
236
- stderr: "",
237
- pid: 1,
238
- output: [],
239
- signal: null,
240
- });
241
-
242
- const containerId = DockerSandbox.findExisting("test-agent");
243
- expect(containerId).toBe("abc123def456");
244
- });
245
-
246
- it("should return null when no container exists", async () => {
247
- const { spawnSync } = await import("node:child_process");
248
- vi.mocked(spawnSync).mockReturnValueOnce({
249
- status: 0,
250
- stdout: "\n",
251
- stderr: "",
252
- pid: 1,
253
- output: [],
254
- signal: null,
255
- });
256
-
257
- const containerId = DockerSandbox.findExisting("nonexistent");
258
- expect(containerId).toBeNull();
259
- });
260
- });
261
-
262
- describe("container lifecycle", () => {
263
- it("should build correct docker run arguments", async () => {
264
- const { spawnSync } = await import("node:child_process");
265
- const mockSpawnSync = vi.mocked(spawnSync);
266
-
267
- // Mock image inspect (image exists)
268
- mockSpawnSync.mockReturnValueOnce({
269
- status: 0,
270
- stdout: "{}",
271
- stderr: "",
272
- pid: 1,
273
- output: [],
274
- signal: null,
275
- });
276
-
277
- // Mock docker run
278
- mockSpawnSync.mockReturnValueOnce({
279
- status: 0,
280
- stdout: "container-id-12345\n",
281
- stderr: "",
282
- pid: 1,
283
- output: [],
284
- signal: null,
285
- });
286
-
287
- const sandbox = new DockerSandbox({
288
- agentName: "test",
289
- image: "agentmesh/agent-sandbox:latest",
290
- workspacePath: "/tmp/workspace",
291
- cpuLimit: "2",
292
- memoryLimit: "4g",
293
- env: {
294
- AGENT_TOKEN: "test-token",
295
- },
296
- });
297
-
298
- await sandbox.pullImage();
299
- await sandbox.start();
300
-
301
- // Find the docker run call
302
- const runCall = mockSpawnSync.mock.calls.find(
303
- (call) => call[0] === "docker" && call[1]?.[0] === "run",
304
- );
305
-
306
- expect(runCall).toBeDefined();
307
- const args = runCall![1] as string[];
308
-
309
- // Check key arguments
310
- expect(args).toContain("-d");
311
- expect(args).toContain("--cpus");
312
- expect(args).toContain("2");
313
- expect(args).toContain("--memory");
314
- expect(args).toContain("4g");
315
- expect(args).toContain("--user");
316
- expect(args).toContain("1000:1000");
317
- expect(args).toContain("-e");
318
- expect(args).toContain("AGENT_TOKEN=test-token");
319
- });
320
-
321
- it("should get container status", async () => {
322
- const { spawnSync } = await import("node:child_process");
323
- vi.mocked(spawnSync).mockReturnValueOnce({
324
- status: 0,
325
- stdout: "true|running|healthy",
326
- stderr: "",
327
- pid: 1,
328
- output: [],
329
- signal: null,
330
- });
331
-
332
- const sandbox = new DockerSandbox(defaultConfig);
333
- // @ts-expect-error - accessing private property for testing
334
- sandbox.containerId = "test-container";
335
-
336
- const status = sandbox.getStatus();
337
-
338
- expect(status).toEqual({
339
- running: true,
340
- status: "running",
341
- health: "healthy",
342
- });
343
- });
344
-
345
- it("should return not found status when container not started", async () => {
346
- const { spawnSync } = await import("node:child_process");
347
- vi.mocked(spawnSync).mockReturnValueOnce({
348
- status: 1, // docker inspect fails for non-existent container
349
- stdout: "",
350
- stderr: "Error: No such container",
351
- pid: 1,
352
- output: [],
353
- signal: null,
354
- });
355
-
356
- const sandbox = new DockerSandbox(defaultConfig);
357
- const status = sandbox.getStatus();
358
-
359
- expect(status).toEqual({
360
- running: false,
361
- status: "not found",
362
- });
363
- });
364
- });
365
-
366
- describe("resource limits", () => {
367
- it("should apply default CPU limit of 1", () => {
368
- const sandbox = new DockerSandbox(defaultConfig);
369
- // The default is applied in constructor
370
- expect(sandbox).toBeDefined();
371
- });
372
-
373
- it("should apply default memory limit of 2g", () => {
374
- const sandbox = new DockerSandbox(defaultConfig);
375
- expect(sandbox).toBeDefined();
376
- });
377
-
378
- it("should allow custom resource limits", () => {
379
- const sandbox = new DockerSandbox({
380
- ...defaultConfig,
381
- cpuLimit: "0.5",
382
- memoryLimit: "512m",
383
- });
384
- expect(sandbox).toBeDefined();
385
- });
386
- });
387
-
388
- describe("serve mode", () => {
389
- it("should configure serve mode with port", async () => {
390
- const { spawnSync } = await import("node:child_process");
391
- const mockSpawnSync = vi.mocked(spawnSync);
392
-
393
- // Mock image inspect
394
- mockSpawnSync.mockReturnValueOnce({
395
- status: 0,
396
- stdout: "{}",
397
- stderr: "",
398
- pid: 1,
399
- output: [],
400
- signal: null,
401
- });
402
-
403
- // Mock docker run
404
- mockSpawnSync.mockReturnValueOnce({
405
- status: 0,
406
- stdout: "container-id\n",
407
- stderr: "",
408
- pid: 1,
409
- output: [],
410
- signal: null,
411
- });
412
-
413
- const sandbox = new DockerSandbox({
414
- ...defaultConfig,
415
- serveMode: true,
416
- servePort: 3001,
417
- });
418
-
419
- await sandbox.pullImage();
420
- await sandbox.start();
421
-
422
- const runCall = mockSpawnSync.mock.calls.find(
423
- (call) => call[0] === "docker" && call[1]?.[0] === "run",
424
- );
425
-
426
- expect(runCall).toBeDefined();
427
- const args = runCall![1] as string[];
428
-
429
- expect(args).toContain("-p");
430
- expect(args).toContain("3001:3001");
431
- expect(args).toContain("opencode");
432
- expect(args).toContain("serve");
433
- });
434
- });
435
- });