@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.
- package/dist/__tests__/attachments.test.d.ts +2 -0
- package/dist/__tests__/attachments.test.d.ts.map +1 -0
- package/dist/__tests__/attachments.test.js +57 -0
- package/dist/__tests__/attachments.test.js.map +1 -0
- package/dist/__tests__/bundle.test.d.ts +2 -0
- package/dist/__tests__/bundle.test.d.ts.map +1 -0
- package/dist/__tests__/bundle.test.js +55 -0
- package/dist/__tests__/bundle.test.js.map +1 -0
- package/dist/__tests__/coordinator.test.d.ts +2 -0
- package/dist/__tests__/coordinator.test.d.ts.map +1 -0
- package/dist/__tests__/coordinator.test.js +283 -0
- package/dist/__tests__/coordinator.test.js.map +1 -0
- package/dist/__tests__/crypto.test.d.ts +2 -0
- package/dist/__tests__/crypto.test.d.ts.map +1 -0
- package/dist/__tests__/crypto.test.js +72 -0
- package/dist/__tests__/crypto.test.js.map +1 -0
- package/dist/__tests__/file-store.test.d.ts +2 -0
- package/dist/__tests__/file-store.test.d.ts.map +1 -0
- package/dist/__tests__/file-store.test.js +105 -0
- package/dist/__tests__/file-store.test.js.map +1 -0
- package/dist/__tests__/files.test.js +18 -5
- package/dist/__tests__/files.test.js.map +1 -1
- package/dist/__tests__/multi-tenant.test.js +15 -1
- package/dist/__tests__/multi-tenant.test.js.map +1 -1
- package/dist/__tests__/openapi.test.js +6 -3
- package/dist/__tests__/openapi.test.js.map +1 -1
- package/dist/__tests__/queue.test.d.ts +2 -0
- package/dist/__tests__/queue.test.d.ts.map +1 -0
- package/dist/__tests__/queue.test.js +151 -0
- package/dist/__tests__/queue.test.js.map +1 -0
- package/dist/__tests__/usage.test.d.ts +2 -0
- package/dist/__tests__/usage.test.d.ts.map +1 -0
- package/dist/__tests__/usage.test.js +74 -0
- package/dist/__tests__/usage.test.js.map +1 -0
- package/dist/auth.d.ts +6 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +23 -5
- package/dist/auth.js.map +1 -1
- package/dist/crypto.d.ts +14 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +45 -0
- package/dist/crypto.js.map +1 -0
- package/dist/db/drizzle-db.d.ts +129 -0
- package/dist/db/drizzle-db.d.ts.map +1 -0
- package/dist/db/drizzle-db.js +789 -0
- package/dist/db/drizzle-db.js.map +1 -0
- package/dist/db/index.d.ts +129 -3
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +147 -8
- package/dist/db/index.js.map +1 -1
- package/dist/db/schema.pg.d.ts +1642 -0
- package/dist/db/schema.pg.d.ts.map +1 -0
- package/dist/db/schema.pg.js +151 -0
- package/dist/db/schema.pg.js.map +1 -0
- package/dist/db/schema.sqlite.d.ts +1800 -0
- package/dist/db/schema.sqlite.d.ts.map +1 -0
- package/dist/db/schema.sqlite.js +151 -0
- package/dist/db/schema.sqlite.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -95
- package/dist/index.js.map +1 -1
- package/dist/queue/processor.d.ts +51 -0
- package/dist/queue/processor.d.ts.map +1 -0
- package/dist/queue/processor.js +98 -0
- package/dist/queue/processor.js.map +1 -0
- package/dist/routes/attachments.d.ts +3 -0
- package/dist/routes/attachments.d.ts.map +1 -0
- package/dist/routes/attachments.js +168 -0
- package/dist/routes/attachments.js.map +1 -0
- package/dist/routes/credentials.d.ts +11 -0
- package/dist/routes/credentials.d.ts.map +1 -0
- package/dist/routes/credentials.js +120 -0
- package/dist/routes/credentials.js.map +1 -0
- package/dist/routes/files.d.ts.map +1 -1
- package/dist/routes/files.js +97 -31
- package/dist/routes/files.js.map +1 -1
- package/dist/routes/health.d.ts.map +1 -1
- package/dist/routes/health.js +9 -1
- package/dist/routes/health.js.map +1 -1
- package/dist/routes/queue.d.ts +3 -0
- package/dist/routes/queue.d.ts.map +1 -0
- package/dist/routes/queue.js +144 -0
- package/dist/routes/queue.js.map +1 -0
- package/dist/routes/runners.d.ts +5 -0
- package/dist/routes/runners.d.ts.map +1 -1
- package/dist/routes/runners.js +42 -5
- package/dist/routes/runners.js.map +1 -1
- package/dist/routes/sessions.d.ts +2 -1
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +218 -12
- package/dist/routes/sessions.js.map +1 -1
- package/dist/routes/usage.d.ts +3 -0
- package/dist/routes/usage.d.ts.map +1 -0
- package/dist/routes/usage.js +64 -0
- package/dist/routes/usage.js.map +1 -0
- package/dist/routes/workspace.d.ts +4 -0
- package/dist/routes/workspace.d.ts.map +1 -0
- package/dist/routes/workspace.js +123 -0
- package/dist/routes/workspace.js.map +1 -0
- package/dist/runner/coordinator.d.ts +77 -9
- package/dist/runner/coordinator.d.ts.map +1 -1
- package/dist/runner/coordinator.js +163 -89
- package/dist/runner/coordinator.js.map +1 -1
- package/dist/runner/local-backend.d.ts +1 -0
- package/dist/runner/local-backend.d.ts.map +1 -1
- package/dist/runner/local-backend.js +8 -0
- package/dist/runner/local-backend.js.map +1 -1
- package/dist/runner/remote-backend.d.ts +2 -0
- package/dist/runner/remote-backend.d.ts.map +1 -1
- package/dist/runner/remote-backend.js +9 -0
- package/dist/runner/remote-backend.js.map +1 -1
- package/dist/runner/runner-client.d.ts +6 -0
- package/dist/runner/runner-client.d.ts.map +1 -1
- package/dist/runner/runner-client.js +15 -0
- package/dist/runner/runner-client.js.map +1 -1
- package/dist/runner/types.d.ts +6 -0
- package/dist/runner/types.d.ts.map +1 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +85 -1
- package/dist/schemas.js.map +1 -1
- package/dist/server.d.ts +31 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +176 -0
- package/dist/server.js.map +1 -0
- package/dist/telemetry/exporter.d.ts +16 -0
- package/dist/telemetry/exporter.d.ts.map +1 -0
- package/dist/telemetry/exporter.js +89 -0
- package/dist/telemetry/exporter.js.map +1 -0
- package/dist/usage/extractor.d.ts +18 -0
- package/dist/usage/extractor.d.ts.map +1 -0
- package/dist/usage/extractor.js +48 -0
- package/dist/usage/extractor.js.map +1 -0
- package/drizzle/pg/0000_thick_loners.sql +75 -0
- package/drizzle/pg/0001_rare_lester.sql +13 -0
- package/drizzle/pg/0002_short_shinko_yamashiro.sql +1 -0
- package/drizzle/pg/0003_remarkable_mastermind.sql +14 -0
- package/drizzle/pg/0004_warm_reaper.sql +18 -0
- package/drizzle/pg/0005_overconfident_mole_man.sql +14 -0
- package/drizzle/pg/0006_third_shiva.sql +13 -0
- package/drizzle/pg/0007_keen_shockwave.sql +2 -0
- package/drizzle/pg/meta/0000_snapshot.json +648 -0
- package/drizzle/pg/meta/0001_snapshot.json +743 -0
- package/drizzle/pg/meta/0002_snapshot.json +749 -0
- package/drizzle/pg/meta/0003_snapshot.json +841 -0
- package/drizzle/pg/meta/0004_snapshot.json +974 -0
- package/drizzle/pg/meta/0005_snapshot.json +1079 -0
- package/drizzle/pg/meta/0006_snapshot.json +1193 -0
- package/drizzle/pg/meta/0007_snapshot.json +1199 -0
- package/drizzle/pg/meta/_journal.json +62 -0
- package/drizzle/sqlite/0000_massive_kinsey_walden.sql +75 -0
- package/drizzle/sqlite/0001_quiet_phantom_reporter.sql +13 -0
- package/drizzle/sqlite/0002_broad_sheva_callister.sql +1 -0
- package/drizzle/sqlite/0003_thankful_agent_brand.sql +14 -0
- package/drizzle/sqlite/0004_productive_wolverine.sql +18 -0
- package/drizzle/sqlite/0005_chilly_carlie_cooper.sql +14 -0
- package/drizzle/sqlite/0006_workable_starfox.sql +13 -0
- package/drizzle/sqlite/0007_quick_hemingway.sql +19 -0
- package/drizzle/sqlite/meta/0000_snapshot.json +503 -0
- package/drizzle/sqlite/meta/0001_snapshot.json +587 -0
- package/drizzle/sqlite/meta/0002_snapshot.json +594 -0
- package/drizzle/sqlite/meta/0003_snapshot.json +685 -0
- package/drizzle/sqlite/meta/0004_snapshot.json +807 -0
- package/drizzle/sqlite/meta/0005_snapshot.json +897 -0
- package/drizzle/sqlite/meta/0006_snapshot.json +981 -0
- package/drizzle/sqlite/meta/0007_snapshot.json +988 -0
- package/drizzle/sqlite/meta/_journal.json +62 -0
- package/package.json +10 -5
- package/dist/__tests__/schema.test.d.ts +0 -2
- package/dist/__tests__/schema.test.d.ts.map +0 -1
- package/dist/__tests__/schema.test.js +0 -31
- package/dist/__tests__/schema.test.js.map +0 -1
- package/dist/db/dump-schema.d.ts +0 -10
- package/dist/db/dump-schema.d.ts.map +0 -1
- package/dist/db/dump-schema.js +0 -64
- package/dist/db/dump-schema.js.map +0 -1
- package/dist/db/pg.d.ts +0 -52
- package/dist/db/pg.d.ts.map +0 -1
- package/dist/db/pg.js +0 -398
- package/dist/db/pg.js.map +0 -1
- package/dist/db/sqlite.d.ts +0 -51
- package/dist/db/sqlite.d.ts.map +0 -1
- package/dist/db/sqlite.js +0 -412
- package/dist/db/sqlite.js.map +0 -1
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"file-store.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/file-store.test.ts"],"names":[],"mappings":""}
|