@ash-ai/server 0.0.3 → 0.0.5

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 (184) hide show
  1. package/dist/__tests__/attachments.test.d.ts +2 -0
  2. package/dist/__tests__/attachments.test.d.ts.map +1 -0
  3. package/dist/__tests__/attachments.test.js +57 -0
  4. package/dist/__tests__/attachments.test.js.map +1 -0
  5. package/dist/__tests__/bundle.test.d.ts +2 -0
  6. package/dist/__tests__/bundle.test.d.ts.map +1 -0
  7. package/dist/__tests__/bundle.test.js +55 -0
  8. package/dist/__tests__/bundle.test.js.map +1 -0
  9. package/dist/__tests__/coordinator.test.d.ts +2 -0
  10. package/dist/__tests__/coordinator.test.d.ts.map +1 -0
  11. package/dist/__tests__/coordinator.test.js +283 -0
  12. package/dist/__tests__/coordinator.test.js.map +1 -0
  13. package/dist/__tests__/crypto.test.d.ts +2 -0
  14. package/dist/__tests__/crypto.test.d.ts.map +1 -0
  15. package/dist/__tests__/crypto.test.js +72 -0
  16. package/dist/__tests__/crypto.test.js.map +1 -0
  17. package/dist/__tests__/file-store.test.d.ts +2 -0
  18. package/dist/__tests__/file-store.test.d.ts.map +1 -0
  19. package/dist/__tests__/file-store.test.js +105 -0
  20. package/dist/__tests__/file-store.test.js.map +1 -0
  21. package/dist/__tests__/files.test.js +18 -5
  22. package/dist/__tests__/files.test.js.map +1 -1
  23. package/dist/__tests__/multi-tenant.test.js +15 -1
  24. package/dist/__tests__/multi-tenant.test.js.map +1 -1
  25. package/dist/__tests__/openapi.test.js +6 -3
  26. package/dist/__tests__/openapi.test.js.map +1 -1
  27. package/dist/__tests__/queue.test.d.ts +2 -0
  28. package/dist/__tests__/queue.test.d.ts.map +1 -0
  29. package/dist/__tests__/queue.test.js +151 -0
  30. package/dist/__tests__/queue.test.js.map +1 -0
  31. package/dist/__tests__/usage.test.d.ts +2 -0
  32. package/dist/__tests__/usage.test.d.ts.map +1 -0
  33. package/dist/__tests__/usage.test.js +74 -0
  34. package/dist/__tests__/usage.test.js.map +1 -0
  35. package/dist/auth.d.ts +6 -2
  36. package/dist/auth.d.ts.map +1 -1
  37. package/dist/auth.js +23 -5
  38. package/dist/auth.js.map +1 -1
  39. package/dist/crypto.d.ts +14 -0
  40. package/dist/crypto.d.ts.map +1 -0
  41. package/dist/crypto.js +45 -0
  42. package/dist/crypto.js.map +1 -0
  43. package/dist/db/drizzle-db.d.ts +129 -0
  44. package/dist/db/drizzle-db.d.ts.map +1 -0
  45. package/dist/db/drizzle-db.js +789 -0
  46. package/dist/db/drizzle-db.js.map +1 -0
  47. package/dist/db/index.d.ts +129 -3
  48. package/dist/db/index.d.ts.map +1 -1
  49. package/dist/db/index.js +147 -8
  50. package/dist/db/index.js.map +1 -1
  51. package/dist/db/schema.pg.d.ts +1642 -0
  52. package/dist/db/schema.pg.d.ts.map +1 -0
  53. package/dist/db/schema.pg.js +151 -0
  54. package/dist/db/schema.pg.js.map +1 -0
  55. package/dist/db/schema.sqlite.d.ts +1800 -0
  56. package/dist/db/schema.sqlite.d.ts.map +1 -0
  57. package/dist/db/schema.sqlite.js +151 -0
  58. package/dist/db/schema.sqlite.js.map +1 -0
  59. package/dist/index.d.ts +2 -1
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +16 -95
  62. package/dist/index.js.map +1 -1
  63. package/dist/queue/processor.d.ts +51 -0
  64. package/dist/queue/processor.d.ts.map +1 -0
  65. package/dist/queue/processor.js +98 -0
  66. package/dist/queue/processor.js.map +1 -0
  67. package/dist/routes/attachments.d.ts +3 -0
  68. package/dist/routes/attachments.d.ts.map +1 -0
  69. package/dist/routes/attachments.js +168 -0
  70. package/dist/routes/attachments.js.map +1 -0
  71. package/dist/routes/credentials.d.ts +11 -0
  72. package/dist/routes/credentials.d.ts.map +1 -0
  73. package/dist/routes/credentials.js +120 -0
  74. package/dist/routes/credentials.js.map +1 -0
  75. package/dist/routes/files.d.ts.map +1 -1
  76. package/dist/routes/files.js +97 -31
  77. package/dist/routes/files.js.map +1 -1
  78. package/dist/routes/health.d.ts.map +1 -1
  79. package/dist/routes/health.js +9 -1
  80. package/dist/routes/health.js.map +1 -1
  81. package/dist/routes/queue.d.ts +3 -0
  82. package/dist/routes/queue.d.ts.map +1 -0
  83. package/dist/routes/queue.js +144 -0
  84. package/dist/routes/queue.js.map +1 -0
  85. package/dist/routes/runners.d.ts +5 -0
  86. package/dist/routes/runners.d.ts.map +1 -1
  87. package/dist/routes/runners.js +42 -5
  88. package/dist/routes/runners.js.map +1 -1
  89. package/dist/routes/sessions.d.ts +2 -1
  90. package/dist/routes/sessions.d.ts.map +1 -1
  91. package/dist/routes/sessions.js +218 -12
  92. package/dist/routes/sessions.js.map +1 -1
  93. package/dist/routes/usage.d.ts +3 -0
  94. package/dist/routes/usage.d.ts.map +1 -0
  95. package/dist/routes/usage.js +64 -0
  96. package/dist/routes/usage.js.map +1 -0
  97. package/dist/routes/workspace.d.ts +4 -0
  98. package/dist/routes/workspace.d.ts.map +1 -0
  99. package/dist/routes/workspace.js +123 -0
  100. package/dist/routes/workspace.js.map +1 -0
  101. package/dist/runner/coordinator.d.ts +77 -9
  102. package/dist/runner/coordinator.d.ts.map +1 -1
  103. package/dist/runner/coordinator.js +163 -89
  104. package/dist/runner/coordinator.js.map +1 -1
  105. package/dist/runner/local-backend.d.ts +1 -0
  106. package/dist/runner/local-backend.d.ts.map +1 -1
  107. package/dist/runner/local-backend.js +8 -0
  108. package/dist/runner/local-backend.js.map +1 -1
  109. package/dist/runner/remote-backend.d.ts +2 -0
  110. package/dist/runner/remote-backend.d.ts.map +1 -1
  111. package/dist/runner/remote-backend.js +9 -0
  112. package/dist/runner/remote-backend.js.map +1 -1
  113. package/dist/runner/runner-client.d.ts +6 -0
  114. package/dist/runner/runner-client.d.ts.map +1 -1
  115. package/dist/runner/runner-client.js +15 -0
  116. package/dist/runner/runner-client.js.map +1 -1
  117. package/dist/runner/types.d.ts +6 -0
  118. package/dist/runner/types.d.ts.map +1 -1
  119. package/dist/schemas.d.ts.map +1 -1
  120. package/dist/schemas.js +85 -1
  121. package/dist/schemas.js.map +1 -1
  122. package/dist/server.d.ts +31 -0
  123. package/dist/server.d.ts.map +1 -0
  124. package/dist/server.js +176 -0
  125. package/dist/server.js.map +1 -0
  126. package/dist/telemetry/exporter.d.ts +16 -0
  127. package/dist/telemetry/exporter.d.ts.map +1 -0
  128. package/dist/telemetry/exporter.js +89 -0
  129. package/dist/telemetry/exporter.js.map +1 -0
  130. package/dist/usage/extractor.d.ts +18 -0
  131. package/dist/usage/extractor.d.ts.map +1 -0
  132. package/dist/usage/extractor.js +48 -0
  133. package/dist/usage/extractor.js.map +1 -0
  134. package/drizzle/pg/0000_thick_loners.sql +75 -0
  135. package/drizzle/pg/0001_rare_lester.sql +13 -0
  136. package/drizzle/pg/0002_short_shinko_yamashiro.sql +1 -0
  137. package/drizzle/pg/0003_remarkable_mastermind.sql +14 -0
  138. package/drizzle/pg/0004_warm_reaper.sql +18 -0
  139. package/drizzle/pg/0005_overconfident_mole_man.sql +14 -0
  140. package/drizzle/pg/0006_third_shiva.sql +13 -0
  141. package/drizzle/pg/0007_keen_shockwave.sql +2 -0
  142. package/drizzle/pg/meta/0000_snapshot.json +648 -0
  143. package/drizzle/pg/meta/0001_snapshot.json +743 -0
  144. package/drizzle/pg/meta/0002_snapshot.json +749 -0
  145. package/drizzle/pg/meta/0003_snapshot.json +841 -0
  146. package/drizzle/pg/meta/0004_snapshot.json +974 -0
  147. package/drizzle/pg/meta/0005_snapshot.json +1079 -0
  148. package/drizzle/pg/meta/0006_snapshot.json +1193 -0
  149. package/drizzle/pg/meta/0007_snapshot.json +1199 -0
  150. package/drizzle/pg/meta/_journal.json +62 -0
  151. package/drizzle/sqlite/0000_massive_kinsey_walden.sql +75 -0
  152. package/drizzle/sqlite/0001_quiet_phantom_reporter.sql +13 -0
  153. package/drizzle/sqlite/0002_broad_sheva_callister.sql +1 -0
  154. package/drizzle/sqlite/0003_thankful_agent_brand.sql +14 -0
  155. package/drizzle/sqlite/0004_productive_wolverine.sql +18 -0
  156. package/drizzle/sqlite/0005_chilly_carlie_cooper.sql +14 -0
  157. package/drizzle/sqlite/0006_workable_starfox.sql +13 -0
  158. package/drizzle/sqlite/0007_quick_hemingway.sql +19 -0
  159. package/drizzle/sqlite/meta/0000_snapshot.json +503 -0
  160. package/drizzle/sqlite/meta/0001_snapshot.json +587 -0
  161. package/drizzle/sqlite/meta/0002_snapshot.json +594 -0
  162. package/drizzle/sqlite/meta/0003_snapshot.json +685 -0
  163. package/drizzle/sqlite/meta/0004_snapshot.json +807 -0
  164. package/drizzle/sqlite/meta/0005_snapshot.json +897 -0
  165. package/drizzle/sqlite/meta/0006_snapshot.json +981 -0
  166. package/drizzle/sqlite/meta/0007_snapshot.json +988 -0
  167. package/drizzle/sqlite/meta/_journal.json +62 -0
  168. package/package.json +10 -5
  169. package/dist/__tests__/schema.test.d.ts +0 -2
  170. package/dist/__tests__/schema.test.d.ts.map +0 -1
  171. package/dist/__tests__/schema.test.js +0 -31
  172. package/dist/__tests__/schema.test.js.map +0 -1
  173. package/dist/db/dump-schema.d.ts +0 -10
  174. package/dist/db/dump-schema.d.ts.map +0 -1
  175. package/dist/db/dump-schema.js +0 -64
  176. package/dist/db/dump-schema.js.map +0 -1
  177. package/dist/db/pg.d.ts +0 -52
  178. package/dist/db/pg.d.ts.map +0 -1
  179. package/dist/db/pg.js +0 -398
  180. package/dist/db/pg.js.map +0 -1
  181. package/dist/db/sqlite.d.ts +0 -51
  182. package/dist/db/sqlite.d.ts.map +0 -1
  183. package/dist/db/sqlite.js +0 -412
  184. package/dist/db/sqlite.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=attachments.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/attachments.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { tmpdir } from 'node:os';
3
+ import { mkdtempSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { initDb, closeDb, insertAttachment, getAttachment, listAttachmentsByMessage, listAttachmentsBySession, deleteAttachment } from '../db/index.js';
6
+ const tenant = 'attach-test';
7
+ let n = 0;
8
+ const uid = () => `a-${Date.now()}-${++n}`;
9
+ describe('attachments', () => {
10
+ beforeEach(async () => {
11
+ const dir = mkdtempSync(join(tmpdir(), 'ash-attach-'));
12
+ await initDb({ dataDir: dir });
13
+ });
14
+ afterEach(async () => {
15
+ await closeDb();
16
+ });
17
+ it('inserts and retrieves an attachment', async () => {
18
+ const id = uid();
19
+ const att = await insertAttachment(id, tenant, 'msg-1', 'sess-1', 'test.txt', 'text/plain', 42, '/path/to/test.txt');
20
+ expect(att.id).toBe(id);
21
+ expect(att.filename).toBe('test.txt');
22
+ expect(att.mimeType).toBe('text/plain');
23
+ expect(att.size).toBe(42);
24
+ const got = await getAttachment(id);
25
+ expect(got).not.toBeNull();
26
+ expect(got.id).toBe(id);
27
+ });
28
+ it('returns null for nonexistent attachment', async () => {
29
+ const got = await getAttachment('nope');
30
+ expect(got).toBeNull();
31
+ });
32
+ it('lists attachments by message', async () => {
33
+ const a = uid(), b = uid();
34
+ await insertAttachment(a, tenant, 'msg-1', 'sess-1', 'file1.txt', 'text/plain', 10, '/p1');
35
+ await insertAttachment(b, tenant, 'msg-2', 'sess-1', 'file2.txt', 'text/plain', 20, '/p2');
36
+ const forMsg1 = await listAttachmentsByMessage('msg-1', tenant);
37
+ expect(forMsg1.length).toBe(1);
38
+ expect(forMsg1[0].id).toBe(a);
39
+ });
40
+ it('lists attachments by session', async () => {
41
+ const a = uid(), b = uid(), c = uid();
42
+ await insertAttachment(a, tenant, 'msg-1', 'sess-1', 'f1.txt', 'text/plain', 10, '/p1');
43
+ await insertAttachment(b, tenant, 'msg-2', 'sess-1', 'f2.txt', 'text/plain', 20, '/p2');
44
+ await insertAttachment(c, tenant, 'msg-3', 'sess-2', 'f3.txt', 'text/plain', 30, '/p3');
45
+ const forSess1 = await listAttachmentsBySession('sess-1', tenant);
46
+ expect(forSess1.length).toBe(2);
47
+ });
48
+ it('deletes an attachment', async () => {
49
+ const id = uid();
50
+ await insertAttachment(id, tenant, 'msg-1', 'sess-1', 'del.txt', 'text/plain', 5, '/del');
51
+ const deleted = await deleteAttachment(id);
52
+ expect(deleted).toBe(true);
53
+ const got = await getAttachment(id);
54
+ expect(got).toBeNull();
55
+ });
56
+ });
57
+ //# sourceMappingURL=attachments.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.test.js","sourceRoot":"","sources":["../../src/__tests__/attachments.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bundle.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bundle.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,55 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { tmpdir } from 'node:os';
3
+ import { mkdtempSync, writeFileSync, readFileSync, rmSync, mkdirSync, existsSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { createBundle, extractBundle } from '@ash-ai/sandbox';
6
+ describe('workspace bundles', () => {
7
+ const dirs = [];
8
+ function tmpDir() {
9
+ const d = mkdtempSync(join(tmpdir(), 'ash-bundle-'));
10
+ dirs.push(d);
11
+ return d;
12
+ }
13
+ afterEach(() => {
14
+ for (const d of dirs) {
15
+ rmSync(d, { recursive: true, force: true });
16
+ }
17
+ dirs.length = 0;
18
+ });
19
+ it('round-trip: createBundle → extractBundle preserves files', () => {
20
+ const src = tmpDir();
21
+ writeFileSync(join(src, 'hello.txt'), 'world');
22
+ mkdirSync(join(src, 'sub'));
23
+ writeFileSync(join(src, 'sub', 'nested.txt'), 'deep');
24
+ const bundle = createBundle(src);
25
+ expect(bundle.length).toBeGreaterThan(0);
26
+ const dest = tmpDir();
27
+ extractBundle(bundle, dest);
28
+ expect(readFileSync(join(dest, 'hello.txt'), 'utf-8')).toBe('world');
29
+ expect(readFileSync(join(dest, 'sub', 'nested.txt'), 'utf-8')).toBe('deep');
30
+ });
31
+ it('createBundle excludes node_modules', () => {
32
+ const src = tmpDir();
33
+ writeFileSync(join(src, 'index.js'), 'console.log("hi")');
34
+ mkdirSync(join(src, 'node_modules', 'dep'), { recursive: true });
35
+ writeFileSync(join(src, 'node_modules', 'dep', 'index.js'), 'big module');
36
+ const bundle = createBundle(src);
37
+ const dest = tmpDir();
38
+ extractBundle(bundle, dest);
39
+ expect(readFileSync(join(dest, 'index.js'), 'utf-8')).toBe('console.log("hi")');
40
+ // node_modules should be excluded
41
+ expect(existsSync(join(dest, 'node_modules'))).toBe(false);
42
+ });
43
+ it('throws for nonexistent workspace', () => {
44
+ expect(() => createBundle('/nonexistent/path')).toThrow('does not exist');
45
+ });
46
+ it('extractBundle creates target dir if needed', () => {
47
+ const src = tmpDir();
48
+ writeFileSync(join(src, 'file.txt'), 'data');
49
+ const bundle = createBundle(src);
50
+ const dest = join(tmpDir(), 'new', 'nested', 'dir');
51
+ extractBundle(bundle, dest);
52
+ expect(readFileSync(join(dest, 'file.txt'), 'utf-8')).toBe('data');
53
+ });
54
+ });
55
+ //# sourceMappingURL=bundle.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.test.js","sourceRoot":"","sources":["../../src/__tests__/bundle.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=coordinator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/coordinator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,283 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { mkdirSync, rmSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { initDb, closeDb, insertSession, updateSessionStatus, updateSessionRunner } from '../db/index.js';
6
+ import { RunnerCoordinator } from '../runner/coordinator.js';
7
+ const TEST_DIR = join(import.meta.dirname ?? '.', '..', '..', '.test-coordinator-' + process.pid);
8
+ describe('RunnerCoordinator (DB-backed)', () => {
9
+ let coordinator;
10
+ beforeEach(async () => {
11
+ mkdirSync(TEST_DIR, { recursive: true });
12
+ await initDb({ dataDir: TEST_DIR });
13
+ coordinator = new RunnerCoordinator({});
14
+ });
15
+ afterEach(async () => {
16
+ coordinator.stopLivenessSweep();
17
+ await closeDb();
18
+ rmSync(TEST_DIR, { recursive: true, force: true });
19
+ });
20
+ describe('registerRunner', () => {
21
+ it('registers a new runner and persists to DB', async () => {
22
+ await coordinator.registerRunner({
23
+ runnerId: 'runner-1',
24
+ host: 'host-1',
25
+ port: 4200,
26
+ maxSandboxes: 50,
27
+ });
28
+ const info = await coordinator.getRunnerInfoFromDb();
29
+ expect(info).toHaveLength(1);
30
+ expect(info[0].runnerId).toBe('runner-1');
31
+ expect(info[0].host).toBe('host-1');
32
+ expect(info[0].port).toBe(4200);
33
+ expect(info[0].max).toBe(50);
34
+ });
35
+ it('re-registers an existing runner with updated connection info', async () => {
36
+ await coordinator.registerRunner({
37
+ runnerId: 'runner-1',
38
+ host: 'old-host',
39
+ port: 4200,
40
+ maxSandboxes: 50,
41
+ });
42
+ await coordinator.registerRunner({
43
+ runnerId: 'runner-1',
44
+ host: 'new-host',
45
+ port: 4201,
46
+ maxSandboxes: 100,
47
+ });
48
+ const info = await coordinator.getRunnerInfoFromDb();
49
+ expect(info).toHaveLength(1);
50
+ expect(info[0].host).toBe('new-host');
51
+ expect(info[0].port).toBe(4201);
52
+ expect(info[0].max).toBe(100);
53
+ });
54
+ });
55
+ describe('heartbeat', () => {
56
+ it('updates runner stats in DB', async () => {
57
+ await coordinator.registerRunner({
58
+ runnerId: 'runner-1',
59
+ host: 'host-1',
60
+ port: 4200,
61
+ maxSandboxes: 50,
62
+ });
63
+ await coordinator.heartbeat('runner-1', {
64
+ total: 10, cold: 0, warming: 2, warm: 3, waiting: 3, running: 5,
65
+ maxCapacity: 50, resumeWarmHits: 0, resumeColdHits: 0,
66
+ });
67
+ const info = await coordinator.getRunnerInfoFromDb();
68
+ expect(info[0].active).toBe(5); // running count
69
+ });
70
+ });
71
+ describe('selectBackend', () => {
72
+ it('throws when no runners available and no local backend', async () => {
73
+ await expect(coordinator.selectBackend()).rejects.toThrow('No runners available');
74
+ });
75
+ it('falls back to local backend when no remote runners', async () => {
76
+ const localBackend = {
77
+ createSandbox: vi.fn(),
78
+ destroySandbox: vi.fn(),
79
+ destroyAll: vi.fn(),
80
+ sendCommand: vi.fn(),
81
+ interrupt: vi.fn(),
82
+ getSandbox: vi.fn(),
83
+ isSandboxAlive: vi.fn(),
84
+ markRunning: vi.fn(),
85
+ markWaiting: vi.fn(),
86
+ recordWarmHit: vi.fn(),
87
+ recordColdHit: vi.fn(),
88
+ persistState: vi.fn(),
89
+ getStats: vi.fn(),
90
+ activeCount: 0,
91
+ };
92
+ const coordWithLocal = new RunnerCoordinator({ localBackend });
93
+ const result = await coordWithLocal.selectBackend();
94
+ expect(result.runnerId).toBe('__local__');
95
+ expect(result.backend).toBe(localBackend);
96
+ });
97
+ it('selects runner with most available capacity', async () => {
98
+ await coordinator.registerRunner({
99
+ runnerId: 'runner-1',
100
+ host: 'host-1',
101
+ port: 4200,
102
+ maxSandboxes: 50,
103
+ });
104
+ await coordinator.registerRunner({
105
+ runnerId: 'runner-2',
106
+ host: 'host-2',
107
+ port: 4201,
108
+ maxSandboxes: 100,
109
+ });
110
+ // runner-1 has 40 active sandboxes (10 available)
111
+ await coordinator.heartbeat('runner-1', {
112
+ total: 50, cold: 0, warming: 5, warm: 0, waiting: 5, running: 40,
113
+ maxCapacity: 50, resumeWarmHits: 0, resumeColdHits: 0,
114
+ });
115
+ // runner-2 has 10 active sandboxes (90 available)
116
+ await coordinator.heartbeat('runner-2', {
117
+ total: 100, cold: 0, warming: 0, warm: 0, waiting: 0, running: 10,
118
+ maxCapacity: 100, resumeWarmHits: 0, resumeColdHits: 0,
119
+ });
120
+ const result = await coordinator.selectBackend();
121
+ expect(result.runnerId).toBe('runner-2');
122
+ });
123
+ });
124
+ describe('handleDeadRunner', () => {
125
+ it('marks active sessions on dead runner as paused and removes runner', async () => {
126
+ await coordinator.registerRunner({
127
+ runnerId: 'runner-dead',
128
+ host: 'host-1',
129
+ port: 4200,
130
+ maxSandboxes: 50,
131
+ });
132
+ // Create a session on this runner
133
+ const sessionId = randomUUID();
134
+ await insertSession(sessionId, 'test-agent', sessionId);
135
+ await updateSessionRunner(sessionId, 'runner-dead');
136
+ await updateSessionStatus(sessionId, 'active');
137
+ await coordinator.handleDeadRunner('runner-dead');
138
+ // Session should be paused
139
+ const { getSession } = await import('../db/index.js');
140
+ const session = await getSession(sessionId);
141
+ expect(session?.status).toBe('paused');
142
+ // Runner should be removed from DB
143
+ const info = await coordinator.getRunnerInfoFromDb();
144
+ expect(info).toHaveLength(0);
145
+ });
146
+ it('does not re-pause already paused sessions', async () => {
147
+ await coordinator.registerRunner({
148
+ runnerId: 'runner-dead',
149
+ host: 'host-1',
150
+ port: 4200,
151
+ maxSandboxes: 50,
152
+ });
153
+ const sessionId = randomUUID();
154
+ await insertSession(sessionId, 'test-agent', sessionId);
155
+ await updateSessionRunner(sessionId, 'runner-dead');
156
+ await updateSessionStatus(sessionId, 'paused');
157
+ // Should not throw
158
+ await coordinator.handleDeadRunner('runner-dead');
159
+ const { getSession } = await import('../db/index.js');
160
+ const session = await getSession(sessionId);
161
+ expect(session?.status).toBe('paused');
162
+ });
163
+ });
164
+ describe('getRunnerInfoFromDb', () => {
165
+ it('returns all runners visible to any coordinator', async () => {
166
+ await coordinator.registerRunner({
167
+ runnerId: 'runner-1',
168
+ host: 'host-1',
169
+ port: 4200,
170
+ maxSandboxes: 50,
171
+ });
172
+ await coordinator.registerRunner({
173
+ runnerId: 'runner-2',
174
+ host: 'host-2',
175
+ port: 4201,
176
+ maxSandboxes: 100,
177
+ });
178
+ // A second coordinator can see both runners (same DB)
179
+ const coordinator2 = new RunnerCoordinator({});
180
+ const info = await coordinator2.getRunnerInfoFromDb();
181
+ expect(info).toHaveLength(2);
182
+ const runnerIds = info.map((r) => r.runnerId).sort();
183
+ expect(runnerIds).toEqual(['runner-1', 'runner-2']);
184
+ });
185
+ });
186
+ describe('deregisterRunner', () => {
187
+ it('immediately pauses sessions and removes runner from DB', async () => {
188
+ await coordinator.registerRunner({
189
+ runnerId: 'runner-shutting-down',
190
+ host: 'host-1',
191
+ port: 4200,
192
+ maxSandboxes: 50,
193
+ });
194
+ // Create two sessions: one active, one ended
195
+ const activeId = randomUUID();
196
+ const endedId = randomUUID();
197
+ await insertSession(activeId, 'test-agent', activeId);
198
+ await updateSessionRunner(activeId, 'runner-shutting-down');
199
+ await updateSessionStatus(activeId, 'active');
200
+ await insertSession(endedId, 'test-agent', endedId);
201
+ await updateSessionRunner(endedId, 'runner-shutting-down');
202
+ await updateSessionStatus(endedId, 'ended');
203
+ await coordinator.deregisterRunner('runner-shutting-down');
204
+ const { getSession } = await import('../db/index.js');
205
+ // Active session should be paused
206
+ const active = await getSession(activeId);
207
+ expect(active?.status).toBe('paused');
208
+ // Ended session should remain ended (not re-paused)
209
+ const ended = await getSession(endedId);
210
+ expect(ended?.status).toBe('ended');
211
+ // Runner should be gone
212
+ const info = await coordinator.getRunnerInfoFromDb();
213
+ expect(info).toHaveLength(0);
214
+ });
215
+ });
216
+ describe('bulkPauseSessionsByRunner', () => {
217
+ it('pauses multiple active sessions in a single operation', async () => {
218
+ await coordinator.registerRunner({
219
+ runnerId: 'runner-bulk',
220
+ host: 'host-1',
221
+ port: 4200,
222
+ maxSandboxes: 50,
223
+ });
224
+ // Create 5 sessions: 3 active, 1 paused, 1 ended
225
+ const ids = Array.from({ length: 5 }, () => randomUUID());
226
+ for (const id of ids) {
227
+ await insertSession(id, 'test-agent', id);
228
+ await updateSessionRunner(id, 'runner-bulk');
229
+ }
230
+ await updateSessionStatus(ids[0], 'active');
231
+ await updateSessionStatus(ids[1], 'active');
232
+ await updateSessionStatus(ids[2], 'active');
233
+ await updateSessionStatus(ids[3], 'paused');
234
+ await updateSessionStatus(ids[4], 'ended');
235
+ await coordinator.handleDeadRunner('runner-bulk');
236
+ const { getSession } = await import('../db/index.js');
237
+ // All 3 active sessions should be paused
238
+ for (const id of ids.slice(0, 3)) {
239
+ const session = await getSession(id);
240
+ expect(session?.status).toBe('paused');
241
+ }
242
+ // Already paused stays paused
243
+ expect((await getSession(ids[3]))?.status).toBe('paused');
244
+ // Ended stays ended
245
+ expect((await getSession(ids[4]))?.status).toBe('ended');
246
+ });
247
+ });
248
+ describe('multi-coordinator consistency', () => {
249
+ it('second coordinator can select runner registered by first', async () => {
250
+ // First coordinator registers a runner
251
+ await coordinator.registerRunner({
252
+ runnerId: 'runner-shared',
253
+ host: 'shared-host',
254
+ port: 4200,
255
+ maxSandboxes: 100,
256
+ });
257
+ // Second coordinator should be able to select it
258
+ const coordinator2 = new RunnerCoordinator({});
259
+ const result = await coordinator2.selectBackend();
260
+ expect(result.runnerId).toBe('runner-shared');
261
+ });
262
+ it('dead runner handling is idempotent across coordinators', async () => {
263
+ await coordinator.registerRunner({
264
+ runnerId: 'runner-dying',
265
+ host: 'host-1',
266
+ port: 4200,
267
+ maxSandboxes: 50,
268
+ });
269
+ const sessionId = randomUUID();
270
+ await insertSession(sessionId, 'test-agent', sessionId);
271
+ await updateSessionRunner(sessionId, 'runner-dying');
272
+ await updateSessionStatus(sessionId, 'active');
273
+ const coordinator2 = new RunnerCoordinator({});
274
+ // Both coordinators handle the dead runner — should be idempotent
275
+ await coordinator.handleDeadRunner('runner-dying');
276
+ await coordinator2.handleDeadRunner('runner-dying');
277
+ const { getSession } = await import('../db/index.js');
278
+ const session = await getSession(sessionId);
279
+ expect(session?.status).toBe('paused');
280
+ });
281
+ });
282
+ });
283
+ //# sourceMappingURL=coordinator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator.test.js","sourceRoot":"","sources":["../../src/__tests__/coordinator.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=crypto.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/crypto.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,72 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { encrypt, decrypt } from '../crypto.js';
3
+ import { createCipheriv, randomBytes, createHash } from 'node:crypto';
4
+ describe('credential encryption', () => {
5
+ const masterKey = 'test-master-key-for-unit-tests';
6
+ it('round-trips a plaintext key', () => {
7
+ const plain = 'sk-ant-abc123xyz';
8
+ const { encrypted, iv, authTag, salt } = encrypt(plain, masterKey);
9
+ expect(salt).toBeDefined();
10
+ const decrypted = decrypt(encrypted, iv, authTag, masterKey, salt);
11
+ expect(decrypted).toBe(plain);
12
+ });
13
+ it('produces different ciphertext for same plaintext (unique IV and salt)', () => {
14
+ const plain = 'sk-ant-abc123xyz';
15
+ const a = encrypt(plain, masterKey);
16
+ const b = encrypt(plain, masterKey);
17
+ expect(a.encrypted).not.toBe(b.encrypted);
18
+ expect(a.iv).not.toBe(b.iv);
19
+ expect(a.salt).not.toBe(b.salt);
20
+ });
21
+ it('fails to decrypt with wrong master key', () => {
22
+ const plain = 'sk-ant-abc123xyz';
23
+ const { encrypted, iv, authTag, salt } = encrypt(plain, masterKey);
24
+ expect(() => decrypt(encrypted, iv, authTag, 'wrong-key', salt)).toThrow();
25
+ });
26
+ it('fails to decrypt with tampered authTag', () => {
27
+ const plain = 'sk-ant-abc123xyz';
28
+ const { encrypted, iv, authTag, salt } = encrypt(plain, masterKey);
29
+ const buf = Buffer.from(authTag, 'base64');
30
+ buf[0] ^= 0xff;
31
+ const tampered = buf.toString('base64');
32
+ expect(() => decrypt(encrypted, iv, tampered, masterKey, salt)).toThrow();
33
+ });
34
+ it('handles empty string', () => {
35
+ const plain = '';
36
+ const { encrypted, iv, authTag, salt } = encrypt(plain, masterKey);
37
+ const decrypted = decrypt(encrypted, iv, authTag, masterKey, salt);
38
+ expect(decrypted).toBe('');
39
+ });
40
+ it('handles long keys', () => {
41
+ const plain = 'sk-' + 'a'.repeat(1000);
42
+ const { encrypted, iv, authTag, salt } = encrypt(plain, masterKey);
43
+ const decrypted = decrypt(encrypted, iv, authTag, masterKey, salt);
44
+ expect(decrypted).toBe(plain);
45
+ });
46
+ it('decrypts legacy credentials (no salt, SHA-256 derived key)', () => {
47
+ // Simulate legacy encryption: unsalted SHA-256 key derivation
48
+ const plain = 'sk-ant-legacy-key';
49
+ const legacyKey = createHash('sha256').update(masterKey).digest();
50
+ const iv = randomBytes(12);
51
+ const cipher = createCipheriv('aes-256-gcm', legacyKey, iv);
52
+ let encrypted = cipher.update(plain, 'utf8', 'base64');
53
+ encrypted += cipher.final('base64');
54
+ const authTag = cipher.getAuthTag();
55
+ // Decrypt with salt=null triggers legacy path
56
+ const decrypted = decrypt(encrypted, iv.toString('base64'), authTag.toString('base64'), masterKey, null);
57
+ expect(decrypted).toBe(plain);
58
+ });
59
+ it('decrypt with salt=undefined also triggers legacy path', () => {
60
+ const plain = 'sk-ant-legacy-key-2';
61
+ const legacyKey = createHash('sha256').update(masterKey).digest();
62
+ const iv = randomBytes(12);
63
+ const cipher = createCipheriv('aes-256-gcm', legacyKey, iv);
64
+ let encrypted = cipher.update(plain, 'utf8', 'base64');
65
+ encrypted += cipher.final('base64');
66
+ const authTag = cipher.getAuthTag();
67
+ // Omit salt entirely
68
+ const decrypted = decrypt(encrypted, iv.toString('base64'), authTag.toString('base64'), masterKey);
69
+ expect(decrypted).toBe(plain);
70
+ });
71
+ });
72
+ //# sourceMappingURL=crypto.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.test.js","sourceRoot":"","sources":["../../src/__tests__/crypto.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=file-store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-store.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/file-store.test.ts"],"names":[],"mappings":""}