@ash-ai/server 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/__tests__/auth.test.d.ts +2 -0
- package/dist/__tests__/auth.test.d.ts.map +1 -0
- package/dist/__tests__/auth.test.js +111 -0
- package/dist/__tests__/auth.test.js.map +1 -0
- package/dist/__tests__/backpressure.test.d.ts +2 -0
- package/dist/__tests__/backpressure.test.d.ts.map +1 -0
- package/dist/__tests__/backpressure.test.js +81 -0
- package/dist/__tests__/backpressure.test.js.map +1 -0
- package/dist/__tests__/db.test.d.ts +2 -0
- package/dist/__tests__/db.test.d.ts.map +1 -0
- package/dist/__tests__/db.test.js +111 -0
- package/dist/__tests__/db.test.js.map +1 -0
- package/dist/__tests__/files.test.d.ts +2 -0
- package/dist/__tests__/files.test.d.ts.map +1 -0
- package/dist/__tests__/files.test.js +171 -0
- package/dist/__tests__/files.test.js.map +1 -0
- package/dist/__tests__/openapi.test.d.ts +2 -0
- package/dist/__tests__/openapi.test.d.ts.map +1 -0
- package/dist/__tests__/openapi.test.js +88 -0
- package/dist/__tests__/openapi.test.js.map +1 -0
- package/dist/__tests__/pool.test.d.ts +2 -0
- package/dist/__tests__/pool.test.d.ts.map +1 -0
- package/dist/__tests__/pool.test.js +352 -0
- package/dist/__tests__/pool.test.js.map +1 -0
- package/dist/__tests__/resource-limits.test.d.ts +2 -0
- package/dist/__tests__/resource-limits.test.d.ts.map +1 -0
- package/dist/__tests__/resource-limits.test.js +119 -0
- package/dist/__tests__/resource-limits.test.js.map +1 -0
- package/dist/__tests__/sandbox-env.test.d.ts +2 -0
- package/dist/__tests__/sandbox-env.test.d.ts.map +1 -0
- package/dist/__tests__/sandbox-env.test.js +40 -0
- package/dist/__tests__/sandbox-env.test.js.map +1 -0
- package/dist/__tests__/snapshot-store.test.d.ts +2 -0
- package/dist/__tests__/snapshot-store.test.d.ts.map +1 -0
- package/dist/__tests__/snapshot-store.test.js +101 -0
- package/dist/__tests__/snapshot-store.test.js.map +1 -0
- package/dist/__tests__/state-persistence.test.d.ts +2 -0
- package/dist/__tests__/state-persistence.test.d.ts.map +1 -0
- package/dist/__tests__/state-persistence.test.js +116 -0
- package/dist/__tests__/state-persistence.test.js.map +1 -0
- package/dist/auth.d.ts +10 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +30 -0
- package/dist/auth.js.map +1 -0
- package/dist/db/index.d.ts +54 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +91 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/pg.d.ts +31 -0
- package/dist/db/pg.d.ts.map +1 -0
- package/dist/db/pg.js +214 -0
- package/dist/db/pg.js.map +1 -0
- package/dist/db/sqlite.d.ts +30 -0
- package/dist/db/sqlite.d.ts.map +1 -0
- package/dist/db/sqlite.js +195 -0
- package/dist/db/sqlite.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +122 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/agents.d.ts +3 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +103 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/files.d.ts +4 -0
- package/dist/routes/files.d.ts.map +1 -0
- package/dist/routes/files.js +189 -0
- package/dist/routes/files.js.map +1 -0
- package/dist/routes/health.d.ts +5 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +72 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/runners.d.ts +8 -0
- package/dist/routes/runners.d.ts.map +1 -0
- package/dist/routes/runners.js +33 -0
- package/dist/routes/runners.js.map +1 -0
- package/dist/routes/sessions.d.ts +10 -0
- package/dist/routes/sessions.d.ts.map +1 -0
- package/dist/routes/sessions.js +392 -0
- package/dist/routes/sessions.js.map +1 -0
- package/dist/runner/coordinator.d.ts +52 -0
- package/dist/runner/coordinator.d.ts.map +1 -0
- package/dist/runner/coordinator.js +157 -0
- package/dist/runner/coordinator.js.map +1 -0
- package/dist/runner/local-backend.d.ts +26 -0
- package/dist/runner/local-backend.d.ts.map +1 -0
- package/dist/runner/local-backend.js +79 -0
- package/dist/runner/local-backend.js.map +1 -0
- package/dist/runner/remote-backend.d.ts +32 -0
- package/dist/runner/remote-backend.d.ts.map +1 -0
- package/dist/runner/remote-backend.js +81 -0
- package/dist/runner/remote-backend.js.map +1 -0
- package/dist/runner/runner-client.d.ts +53 -0
- package/dist/runner/runner-client.d.ts.map +1 -0
- package/dist/runner/runner-client.js +157 -0
- package/dist/runner/runner-client.js.map +1 -0
- package/dist/runner/types.d.ts +37 -0
- package/dist/runner/types.d.ts.map +1 -0
- package/dist/runner/types.js +2 -0
- package/dist/runner/types.js.map +1 -0
- package/dist/schemas.d.ts +3 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +73 -0
- package/dist/schemas.js.map +1 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ash Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/auth.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import Fastify from 'fastify';
|
|
3
|
+
import { registerAuth } from '../auth.js';
|
|
4
|
+
/** Build a minimal Fastify app with auth + a test route. */
|
|
5
|
+
async function buildApp(apiKey) {
|
|
6
|
+
const app = Fastify({ logger: false });
|
|
7
|
+
registerAuth(app, apiKey);
|
|
8
|
+
// Stub routes to test against
|
|
9
|
+
app.get('/health', async () => ({ status: 'ok' }));
|
|
10
|
+
app.get('/docs/json', async () => ({ spec: true }));
|
|
11
|
+
app.get('/api/agents', async () => ({ agents: [] }));
|
|
12
|
+
app.post('/api/sessions', async () => ({ session: {} }));
|
|
13
|
+
await app.ready();
|
|
14
|
+
return app;
|
|
15
|
+
}
|
|
16
|
+
describe('API key auth middleware', () => {
|
|
17
|
+
describe('when ASH_API_KEY is set', () => {
|
|
18
|
+
const API_KEY = 'test-secret-key-12345';
|
|
19
|
+
let app;
|
|
20
|
+
beforeAll(async () => { app = await buildApp(API_KEY); });
|
|
21
|
+
afterAll(async () => { await app.close(); });
|
|
22
|
+
it('rejects requests without Authorization header', async () => {
|
|
23
|
+
const res = await app.inject({ method: 'GET', url: '/api/agents' });
|
|
24
|
+
expect(res.statusCode).toBe(401);
|
|
25
|
+
expect(res.json().error).toBe('Missing Authorization header');
|
|
26
|
+
});
|
|
27
|
+
it('rejects requests with wrong API key', async () => {
|
|
28
|
+
const res = await app.inject({
|
|
29
|
+
method: 'GET',
|
|
30
|
+
url: '/api/agents',
|
|
31
|
+
headers: { authorization: 'Bearer wrong-key' },
|
|
32
|
+
});
|
|
33
|
+
expect(res.statusCode).toBe(401);
|
|
34
|
+
expect(res.json().error).toBe('Invalid API key');
|
|
35
|
+
});
|
|
36
|
+
it('rejects requests with malformed Authorization header', async () => {
|
|
37
|
+
const res = await app.inject({
|
|
38
|
+
method: 'GET',
|
|
39
|
+
url: '/api/agents',
|
|
40
|
+
headers: { authorization: `Basic ${API_KEY}` },
|
|
41
|
+
});
|
|
42
|
+
expect(res.statusCode).toBe(401);
|
|
43
|
+
expect(res.json().error).toBe('Invalid API key');
|
|
44
|
+
});
|
|
45
|
+
it('allows requests with correct API key', async () => {
|
|
46
|
+
const res = await app.inject({
|
|
47
|
+
method: 'GET',
|
|
48
|
+
url: '/api/agents',
|
|
49
|
+
headers: { authorization: `Bearer ${API_KEY}` },
|
|
50
|
+
});
|
|
51
|
+
expect(res.statusCode).toBe(200);
|
|
52
|
+
expect(res.json()).toEqual({ agents: [] });
|
|
53
|
+
});
|
|
54
|
+
it('allows /health without auth', async () => {
|
|
55
|
+
const res = await app.inject({ method: 'GET', url: '/health' });
|
|
56
|
+
expect(res.statusCode).toBe(200);
|
|
57
|
+
expect(res.json().status).toBe('ok');
|
|
58
|
+
});
|
|
59
|
+
it('allows /docs without auth', async () => {
|
|
60
|
+
const res = await app.inject({ method: 'GET', url: '/docs/json' });
|
|
61
|
+
expect(res.statusCode).toBe(200);
|
|
62
|
+
});
|
|
63
|
+
it('protects POST routes too', async () => {
|
|
64
|
+
const res = await app.inject({ method: 'POST', url: '/api/sessions' });
|
|
65
|
+
expect(res.statusCode).toBe(401);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe('when ASH_API_KEY is not set', () => {
|
|
69
|
+
let app;
|
|
70
|
+
beforeAll(async () => { app = await buildApp(undefined); });
|
|
71
|
+
afterAll(async () => { await app.close(); });
|
|
72
|
+
it('allows all requests without auth', async () => {
|
|
73
|
+
const res = await app.inject({ method: 'GET', url: '/api/agents' });
|
|
74
|
+
expect(res.statusCode).toBe(200);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe('real HTTP requests against auth-protected server', () => {
|
|
78
|
+
const API_KEY = 'test-secret-key-12345';
|
|
79
|
+
let app;
|
|
80
|
+
let serverUrl;
|
|
81
|
+
beforeAll(async () => {
|
|
82
|
+
app = await buildApp(API_KEY);
|
|
83
|
+
const address = await app.listen({ port: 0, host: '127.0.0.1' });
|
|
84
|
+
serverUrl = address;
|
|
85
|
+
});
|
|
86
|
+
afterAll(async () => { await app.close(); });
|
|
87
|
+
it('rejects when no API key is provided', async () => {
|
|
88
|
+
const res = await fetch(`${serverUrl}/api/agents`);
|
|
89
|
+
expect(res.status).toBe(401);
|
|
90
|
+
const body = await res.json();
|
|
91
|
+
expect(body.error).toBe('Missing Authorization header');
|
|
92
|
+
});
|
|
93
|
+
it('rejects when wrong API key is provided', async () => {
|
|
94
|
+
const res = await fetch(`${serverUrl}/api/agents`, {
|
|
95
|
+
headers: { authorization: 'Bearer wrong-key' },
|
|
96
|
+
});
|
|
97
|
+
expect(res.status).toBe(401);
|
|
98
|
+
const body = await res.json();
|
|
99
|
+
expect(body.error).toBe('Invalid API key');
|
|
100
|
+
});
|
|
101
|
+
it('succeeds when correct API key is provided', async () => {
|
|
102
|
+
const res = await fetch(`${serverUrl}/api/agents`, {
|
|
103
|
+
headers: { authorization: `Bearer ${API_KEY}` },
|
|
104
|
+
});
|
|
105
|
+
expect(res.status).toBe(200);
|
|
106
|
+
const body = await res.json();
|
|
107
|
+
expect(body.agents).toEqual([]);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=auth.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.test.js","sourceRoot":"","sources":["../../src/__tests__/auth.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backpressure.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/backpressure.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
2
|
+
import { PassThrough } from 'node:stream';
|
|
3
|
+
import { writeSSE } from '../routes/sessions.js';
|
|
4
|
+
describe('writeSSE backpressure', () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
vi.useRealTimers();
|
|
7
|
+
});
|
|
8
|
+
it('writes immediately when buffer has room', async () => {
|
|
9
|
+
const written = [];
|
|
10
|
+
const fakeRaw = {
|
|
11
|
+
write: (data) => {
|
|
12
|
+
written.push(data);
|
|
13
|
+
return true; // buffer has room
|
|
14
|
+
},
|
|
15
|
+
once: () => { },
|
|
16
|
+
};
|
|
17
|
+
await writeSSE(fakeRaw, 'event: message\ndata: {"hello":"world"}\n\n');
|
|
18
|
+
expect(written).toHaveLength(1);
|
|
19
|
+
expect(written[0]).toContain('hello');
|
|
20
|
+
});
|
|
21
|
+
it('waits for drain when write returns false', async () => {
|
|
22
|
+
const written = [];
|
|
23
|
+
let drainCb = null;
|
|
24
|
+
const fakeRaw = {
|
|
25
|
+
write: (data) => {
|
|
26
|
+
written.push(data);
|
|
27
|
+
return false; // buffer full
|
|
28
|
+
},
|
|
29
|
+
once: (event, cb) => {
|
|
30
|
+
if (event === 'drain')
|
|
31
|
+
drainCb = cb;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
let resolved = false;
|
|
35
|
+
const writePromise = writeSSE(fakeRaw, 'event: test\ndata: {}\n\n').then(() => {
|
|
36
|
+
resolved = true;
|
|
37
|
+
});
|
|
38
|
+
// Give the microtask queue a tick — writeSSE should be waiting for drain
|
|
39
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
40
|
+
expect(resolved).toBe(false);
|
|
41
|
+
expect(drainCb).not.toBeNull();
|
|
42
|
+
// Simulate drain
|
|
43
|
+
drainCb();
|
|
44
|
+
await writePromise;
|
|
45
|
+
expect(resolved).toBe(true);
|
|
46
|
+
expect(written).toHaveLength(1);
|
|
47
|
+
});
|
|
48
|
+
it('blocks when client never drains (timeout behavior)', async () => {
|
|
49
|
+
const fakeRaw = {
|
|
50
|
+
write: () => false,
|
|
51
|
+
once: (_event, _cb) => {
|
|
52
|
+
// Never call the callback — simulates a dead client
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
// writeSSE will wait up to SSE_WRITE_TIMEOUT_MS (30s) — verify it doesn't
|
|
56
|
+
// resolve within a short window, proving it's blocked on drain
|
|
57
|
+
const result = await Promise.race([
|
|
58
|
+
writeSSE(fakeRaw, 'event: test\ndata: {}\n\n').then(() => 'resolved').catch(() => 'rejected'),
|
|
59
|
+
new Promise((resolve) => setTimeout(() => resolve('still-waiting'), 200)),
|
|
60
|
+
]);
|
|
61
|
+
expect(result).toBe('still-waiting');
|
|
62
|
+
});
|
|
63
|
+
it('handles multiple sequential writes when buffer drains', async () => {
|
|
64
|
+
// Use a PassThrough with a large enough buffer and consume output
|
|
65
|
+
const stream = new PassThrough({ highWaterMark: 64 * 1024 });
|
|
66
|
+
const raw = stream;
|
|
67
|
+
const chunks = [];
|
|
68
|
+
// Consume output so the buffer stays drained
|
|
69
|
+
stream.on('data', (chunk) => {
|
|
70
|
+
chunks.push(chunk.toString());
|
|
71
|
+
});
|
|
72
|
+
for (let i = 0; i < 10; i++) {
|
|
73
|
+
await writeSSE(raw, `event: message\ndata: {"i":${i}}\n\n`);
|
|
74
|
+
}
|
|
75
|
+
expect(chunks).toHaveLength(10);
|
|
76
|
+
expect(chunks[0]).toContain('"i":0');
|
|
77
|
+
expect(chunks[9]).toContain('"i":9');
|
|
78
|
+
stream.destroy();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=backpressure.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backpressure.test.js","sourceRoot":"","sources":["../../src/__tests__/backpressure.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/db.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { initDb, closeDb, upsertAgent, getAgent, listAgents, deleteAgent, insertSession, getSession, listSessions, updateSessionStatus, updateSessionSandbox, touchSession, } from '../db/index.js';
|
|
6
|
+
describe('database', () => {
|
|
7
|
+
let dataDir;
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
dataDir = mkdtempSync(join(tmpdir(), 'ash-test-db-'));
|
|
10
|
+
await initDb({ dataDir });
|
|
11
|
+
});
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await closeDb();
|
|
14
|
+
rmSync(dataDir, { recursive: true, force: true });
|
|
15
|
+
});
|
|
16
|
+
it('uses SQLite by default when no databaseUrl is provided', async () => {
|
|
17
|
+
// initDb was already called in beforeEach without databaseUrl — verify it works
|
|
18
|
+
const agent = await upsertAgent('default-backend-test', '/tmp/test');
|
|
19
|
+
expect(agent.name).toBe('default-backend-test');
|
|
20
|
+
});
|
|
21
|
+
// -- Agents -----------------------------------------------------------------
|
|
22
|
+
describe('agents', () => {
|
|
23
|
+
it('creates an agent', async () => {
|
|
24
|
+
const agent = await upsertAgent('test-agent', '/tmp/agent');
|
|
25
|
+
expect(agent.name).toBe('test-agent');
|
|
26
|
+
expect(agent.version).toBe(1);
|
|
27
|
+
expect(agent.path).toBe('/tmp/agent');
|
|
28
|
+
});
|
|
29
|
+
it('increments version on upsert', async () => {
|
|
30
|
+
await upsertAgent('test-agent', '/tmp/v1');
|
|
31
|
+
const v2 = await upsertAgent('test-agent', '/tmp/v2');
|
|
32
|
+
expect(v2.version).toBe(2);
|
|
33
|
+
expect(v2.path).toBe('/tmp/v2');
|
|
34
|
+
});
|
|
35
|
+
it('gets an agent by name', async () => {
|
|
36
|
+
await upsertAgent('my-agent', '/tmp/path');
|
|
37
|
+
const agent = await getAgent('my-agent');
|
|
38
|
+
expect(agent).not.toBeNull();
|
|
39
|
+
expect(agent.name).toBe('my-agent');
|
|
40
|
+
});
|
|
41
|
+
it('returns null for nonexistent agent', async () => {
|
|
42
|
+
expect(await getAgent('ghost')).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
it('lists all agents', async () => {
|
|
45
|
+
await upsertAgent('a', '/tmp/a');
|
|
46
|
+
await upsertAgent('b', '/tmp/b');
|
|
47
|
+
const agents = await listAgents();
|
|
48
|
+
expect(agents).toHaveLength(2);
|
|
49
|
+
expect(agents.map((a) => a.name)).toEqual(['a', 'b']);
|
|
50
|
+
});
|
|
51
|
+
it('deletes an agent', async () => {
|
|
52
|
+
await upsertAgent('doomed', '/tmp/doomed');
|
|
53
|
+
expect(await deleteAgent('doomed')).toBe(true);
|
|
54
|
+
expect(await getAgent('doomed')).toBeNull();
|
|
55
|
+
});
|
|
56
|
+
it('returns false when deleting nonexistent agent', async () => {
|
|
57
|
+
expect(await deleteAgent('ghost')).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
// -- Sessions ---------------------------------------------------------------
|
|
61
|
+
describe('sessions', () => {
|
|
62
|
+
beforeEach(async () => {
|
|
63
|
+
await upsertAgent('test-agent', '/tmp/agent');
|
|
64
|
+
});
|
|
65
|
+
it('creates a session', async () => {
|
|
66
|
+
const session = await insertSession('s1', 'test-agent', 'sandbox-1');
|
|
67
|
+
expect(session.id).toBe('s1');
|
|
68
|
+
expect(session.agentName).toBe('test-agent');
|
|
69
|
+
expect(session.status).toBe('starting');
|
|
70
|
+
});
|
|
71
|
+
it('updates session status', async () => {
|
|
72
|
+
await insertSession('s1', 'test-agent', 'sandbox-1');
|
|
73
|
+
await updateSessionStatus('s1', 'active');
|
|
74
|
+
const session = await getSession('s1');
|
|
75
|
+
expect(session.status).toBe('active');
|
|
76
|
+
});
|
|
77
|
+
it('follows status lifecycle: starting → active → ended', async () => {
|
|
78
|
+
await insertSession('s1', 'test-agent', 'sandbox-1');
|
|
79
|
+
expect((await getSession('s1')).status).toBe('starting');
|
|
80
|
+
await updateSessionStatus('s1', 'active');
|
|
81
|
+
expect((await getSession('s1')).status).toBe('active');
|
|
82
|
+
await updateSessionStatus('s1', 'ended');
|
|
83
|
+
expect((await getSession('s1')).status).toBe('ended');
|
|
84
|
+
});
|
|
85
|
+
it('lists sessions (newest first)', async () => {
|
|
86
|
+
await insertSession('s1', 'test-agent', 'sb1');
|
|
87
|
+
await insertSession('s2', 'test-agent', 'sb2');
|
|
88
|
+
const sessions = await listSessions();
|
|
89
|
+
expect(sessions).toHaveLength(2);
|
|
90
|
+
});
|
|
91
|
+
it('returns null for nonexistent session', async () => {
|
|
92
|
+
expect(await getSession('ghost')).toBeNull();
|
|
93
|
+
});
|
|
94
|
+
it('touch updates lastActiveAt', async () => {
|
|
95
|
+
await insertSession('s1', 'test-agent', 'sb1');
|
|
96
|
+
const before = (await getSession('s1')).lastActiveAt;
|
|
97
|
+
// Small delay to ensure timestamp difference
|
|
98
|
+
await touchSession('s1');
|
|
99
|
+
const after = (await getSession('s1')).lastActiveAt;
|
|
100
|
+
expect(after).toBeTruthy();
|
|
101
|
+
});
|
|
102
|
+
it('updates sandbox ID', async () => {
|
|
103
|
+
await insertSession('s1', 'test-agent', 'sb-old');
|
|
104
|
+
expect((await getSession('s1')).sandboxId).toBe('sb-old');
|
|
105
|
+
await updateSessionSandbox('s1', 'sb-new');
|
|
106
|
+
const session = (await getSession('s1'));
|
|
107
|
+
expect(session.sandboxId).toBe('sb-new');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=db.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.test.js","sourceRoot":"","sources":["../../src/__tests__/db.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/files.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import Fastify from 'fastify';
|
|
6
|
+
import { registerSchemas } from '../schemas.js';
|
|
7
|
+
import { fileRoutes } from '../routes/files.js';
|
|
8
|
+
import { initDb, closeDb, upsertAgent, insertSession, updateSessionStatus } from '../db/index.js';
|
|
9
|
+
// Minimal mock coordinator that returns a mock backend pointing at a workspace dir
|
|
10
|
+
function mockCoordinator(workspaceDir) {
|
|
11
|
+
const handle = workspaceDir
|
|
12
|
+
? { sandboxId: 'sbx-1', workspaceDir }
|
|
13
|
+
: undefined;
|
|
14
|
+
const backend = {
|
|
15
|
+
getSandbox: () => handle,
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
getBackendForRunner: () => backend,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
describe('file routes', () => {
|
|
22
|
+
let dataDir;
|
|
23
|
+
let workspaceDir;
|
|
24
|
+
beforeEach(async () => {
|
|
25
|
+
dataDir = mkdtempSync(join(tmpdir(), 'ash-files-test-'));
|
|
26
|
+
workspaceDir = mkdtempSync(join(tmpdir(), 'ash-files-ws-'));
|
|
27
|
+
await initDb({ dataDir });
|
|
28
|
+
});
|
|
29
|
+
afterEach(async () => {
|
|
30
|
+
await closeDb();
|
|
31
|
+
rmSync(dataDir, { recursive: true, force: true });
|
|
32
|
+
rmSync(workspaceDir, { recursive: true, force: true });
|
|
33
|
+
});
|
|
34
|
+
function populateWorkspace() {
|
|
35
|
+
writeFileSync(join(workspaceDir, 'CLAUDE.md'), '# Agent');
|
|
36
|
+
mkdirSync(join(workspaceDir, 'src'), { recursive: true });
|
|
37
|
+
writeFileSync(join(workspaceDir, 'src', 'index.ts'), 'console.log("hello")');
|
|
38
|
+
writeFileSync(join(workspaceDir, 'src', 'util.ts'), 'export const x = 1;');
|
|
39
|
+
// Filtered dirs
|
|
40
|
+
mkdirSync(join(workspaceDir, 'node_modules', 'pkg'), { recursive: true });
|
|
41
|
+
writeFileSync(join(workspaceDir, 'node_modules', 'pkg', 'index.js'), '');
|
|
42
|
+
mkdirSync(join(workspaceDir, '.git'), { recursive: true });
|
|
43
|
+
writeFileSync(join(workspaceDir, '.git', 'HEAD'), 'ref');
|
|
44
|
+
}
|
|
45
|
+
async function buildApp(coordinator) {
|
|
46
|
+
const app = Fastify();
|
|
47
|
+
registerSchemas(app);
|
|
48
|
+
fileRoutes(app, coordinator, dataDir);
|
|
49
|
+
await app.ready();
|
|
50
|
+
return app;
|
|
51
|
+
}
|
|
52
|
+
async function createTestSession() {
|
|
53
|
+
await upsertAgent('test-agent', '/tmp/agent');
|
|
54
|
+
const session = await insertSession('11111111-1111-1111-1111-111111111111', 'test-agent', 'sbx-1');
|
|
55
|
+
await updateSessionStatus(session.id, 'active');
|
|
56
|
+
return session;
|
|
57
|
+
}
|
|
58
|
+
describe('GET /api/sessions/:id/files', () => {
|
|
59
|
+
it('lists files from live sandbox workspace', async () => {
|
|
60
|
+
populateWorkspace();
|
|
61
|
+
const session = await createTestSession();
|
|
62
|
+
const app = await buildApp(mockCoordinator(workspaceDir));
|
|
63
|
+
const res = await app.inject({
|
|
64
|
+
method: 'GET',
|
|
65
|
+
url: `/api/sessions/${session.id}/files`,
|
|
66
|
+
});
|
|
67
|
+
expect(res.statusCode).toBe(200);
|
|
68
|
+
const body = res.json();
|
|
69
|
+
expect(body.source).toBe('sandbox');
|
|
70
|
+
expect(body.files).toBeInstanceOf(Array);
|
|
71
|
+
const paths = body.files.map((f) => f.path).sort();
|
|
72
|
+
expect(paths).toContain('CLAUDE.md');
|
|
73
|
+
expect(paths).toContain('src/index.ts');
|
|
74
|
+
expect(paths).toContain('src/util.ts');
|
|
75
|
+
// Filtered dirs should not appear
|
|
76
|
+
expect(paths.find((p) => p.includes('node_modules'))).toBeUndefined();
|
|
77
|
+
expect(paths.find((p) => p.includes('.git'))).toBeUndefined();
|
|
78
|
+
});
|
|
79
|
+
it('returns file sizes and modifiedAt', async () => {
|
|
80
|
+
populateWorkspace();
|
|
81
|
+
const session = await createTestSession();
|
|
82
|
+
const app = await buildApp(mockCoordinator(workspaceDir));
|
|
83
|
+
const res = await app.inject({
|
|
84
|
+
method: 'GET',
|
|
85
|
+
url: `/api/sessions/${session.id}/files`,
|
|
86
|
+
});
|
|
87
|
+
const body = res.json();
|
|
88
|
+
const claude = body.files.find((f) => f.path === 'CLAUDE.md');
|
|
89
|
+
expect(claude).toBeDefined();
|
|
90
|
+
expect(claude.size).toBe(7); // '# Agent'
|
|
91
|
+
expect(claude.modifiedAt).toBeTruthy();
|
|
92
|
+
});
|
|
93
|
+
it('falls back to persisted snapshot when sandbox is gone', async () => {
|
|
94
|
+
populateWorkspace();
|
|
95
|
+
const session = await createTestSession();
|
|
96
|
+
// Persist state to snapshot location
|
|
97
|
+
const snapshotDir = join(dataDir, 'sessions', session.sandboxId, 'workspace');
|
|
98
|
+
mkdirSync(snapshotDir, { recursive: true });
|
|
99
|
+
writeFileSync(join(snapshotDir, 'snapshot-file.txt'), 'from snapshot');
|
|
100
|
+
// Coordinator returns no sandbox (sandbox gone)
|
|
101
|
+
const app = await buildApp(mockCoordinator(null));
|
|
102
|
+
const res = await app.inject({
|
|
103
|
+
method: 'GET',
|
|
104
|
+
url: `/api/sessions/${session.id}/files`,
|
|
105
|
+
});
|
|
106
|
+
expect(res.statusCode).toBe(200);
|
|
107
|
+
const body = res.json();
|
|
108
|
+
expect(body.source).toBe('snapshot');
|
|
109
|
+
expect(body.files.map((f) => f.path)).toContain('snapshot-file.txt');
|
|
110
|
+
});
|
|
111
|
+
it('returns 404 for unknown session', async () => {
|
|
112
|
+
const app = await buildApp(mockCoordinator(null));
|
|
113
|
+
const res = await app.inject({
|
|
114
|
+
method: 'GET',
|
|
115
|
+
url: '/api/sessions/99999999-9999-9999-9999-999999999999/files',
|
|
116
|
+
});
|
|
117
|
+
expect(res.statusCode).toBe(404);
|
|
118
|
+
});
|
|
119
|
+
it('returns 404 when no workspace available', async () => {
|
|
120
|
+
const session = await createTestSession();
|
|
121
|
+
const app = await buildApp(mockCoordinator(null));
|
|
122
|
+
const res = await app.inject({
|
|
123
|
+
method: 'GET',
|
|
124
|
+
url: `/api/sessions/${session.id}/files`,
|
|
125
|
+
});
|
|
126
|
+
expect(res.statusCode).toBe(404);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe('GET /api/sessions/:id/files/*path', () => {
|
|
130
|
+
it('reads a single file', async () => {
|
|
131
|
+
populateWorkspace();
|
|
132
|
+
const session = await createTestSession();
|
|
133
|
+
const app = await buildApp(mockCoordinator(workspaceDir));
|
|
134
|
+
const res = await app.inject({
|
|
135
|
+
method: 'GET',
|
|
136
|
+
url: `/api/sessions/${session.id}/files/src/index.ts`,
|
|
137
|
+
});
|
|
138
|
+
expect(res.statusCode).toBe(200);
|
|
139
|
+
const body = res.json();
|
|
140
|
+
expect(body.path).toBe('src/index.ts');
|
|
141
|
+
expect(body.content).toBe('console.log("hello")');
|
|
142
|
+
expect(body.size).toBe(20);
|
|
143
|
+
expect(body.source).toBe('sandbox');
|
|
144
|
+
});
|
|
145
|
+
it('returns 404 for nonexistent file', async () => {
|
|
146
|
+
populateWorkspace();
|
|
147
|
+
const session = await createTestSession();
|
|
148
|
+
const app = await buildApp(mockCoordinator(workspaceDir));
|
|
149
|
+
const res = await app.inject({
|
|
150
|
+
method: 'GET',
|
|
151
|
+
url: `/api/sessions/${session.id}/files/nope.txt`,
|
|
152
|
+
});
|
|
153
|
+
expect(res.statusCode).toBe(404);
|
|
154
|
+
});
|
|
155
|
+
it('rejects path traversal with ..', async () => {
|
|
156
|
+
populateWorkspace();
|
|
157
|
+
const session = await createTestSession();
|
|
158
|
+
const app = await buildApp(mockCoordinator(workspaceDir));
|
|
159
|
+
// Fastify normalizes ../ in URLs before the handler runs, so the path
|
|
160
|
+
// resolves to something that doesn't exist (404). Either way, the
|
|
161
|
+
// traversal is blocked — the file can't be read.
|
|
162
|
+
const res = await app.inject({
|
|
163
|
+
method: 'GET',
|
|
164
|
+
url: `/api/sessions/${session.id}/files/../../../etc/passwd`,
|
|
165
|
+
});
|
|
166
|
+
expect([400, 404]).toContain(res.statusCode);
|
|
167
|
+
expect(res.json().content).toBeUndefined();
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
//# sourceMappingURL=files.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.test.js","sourceRoot":"","sources":["../../src/__tests__/files.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/openapi.test.ts"],"names":[],"mappings":""}
|