@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.
Files changed (106) hide show
  1. package/LICENSE +21 -0
  2. package/dist/__tests__/auth.test.d.ts +2 -0
  3. package/dist/__tests__/auth.test.d.ts.map +1 -0
  4. package/dist/__tests__/auth.test.js +111 -0
  5. package/dist/__tests__/auth.test.js.map +1 -0
  6. package/dist/__tests__/backpressure.test.d.ts +2 -0
  7. package/dist/__tests__/backpressure.test.d.ts.map +1 -0
  8. package/dist/__tests__/backpressure.test.js +81 -0
  9. package/dist/__tests__/backpressure.test.js.map +1 -0
  10. package/dist/__tests__/db.test.d.ts +2 -0
  11. package/dist/__tests__/db.test.d.ts.map +1 -0
  12. package/dist/__tests__/db.test.js +111 -0
  13. package/dist/__tests__/db.test.js.map +1 -0
  14. package/dist/__tests__/files.test.d.ts +2 -0
  15. package/dist/__tests__/files.test.d.ts.map +1 -0
  16. package/dist/__tests__/files.test.js +171 -0
  17. package/dist/__tests__/files.test.js.map +1 -0
  18. package/dist/__tests__/openapi.test.d.ts +2 -0
  19. package/dist/__tests__/openapi.test.d.ts.map +1 -0
  20. package/dist/__tests__/openapi.test.js +88 -0
  21. package/dist/__tests__/openapi.test.js.map +1 -0
  22. package/dist/__tests__/pool.test.d.ts +2 -0
  23. package/dist/__tests__/pool.test.d.ts.map +1 -0
  24. package/dist/__tests__/pool.test.js +352 -0
  25. package/dist/__tests__/pool.test.js.map +1 -0
  26. package/dist/__tests__/resource-limits.test.d.ts +2 -0
  27. package/dist/__tests__/resource-limits.test.d.ts.map +1 -0
  28. package/dist/__tests__/resource-limits.test.js +119 -0
  29. package/dist/__tests__/resource-limits.test.js.map +1 -0
  30. package/dist/__tests__/sandbox-env.test.d.ts +2 -0
  31. package/dist/__tests__/sandbox-env.test.d.ts.map +1 -0
  32. package/dist/__tests__/sandbox-env.test.js +40 -0
  33. package/dist/__tests__/sandbox-env.test.js.map +1 -0
  34. package/dist/__tests__/snapshot-store.test.d.ts +2 -0
  35. package/dist/__tests__/snapshot-store.test.d.ts.map +1 -0
  36. package/dist/__tests__/snapshot-store.test.js +101 -0
  37. package/dist/__tests__/snapshot-store.test.js.map +1 -0
  38. package/dist/__tests__/state-persistence.test.d.ts +2 -0
  39. package/dist/__tests__/state-persistence.test.d.ts.map +1 -0
  40. package/dist/__tests__/state-persistence.test.js +116 -0
  41. package/dist/__tests__/state-persistence.test.js.map +1 -0
  42. package/dist/auth.d.ts +10 -0
  43. package/dist/auth.d.ts.map +1 -0
  44. package/dist/auth.js +30 -0
  45. package/dist/auth.js.map +1 -0
  46. package/dist/db/index.d.ts +54 -0
  47. package/dist/db/index.d.ts.map +1 -0
  48. package/dist/db/index.js +91 -0
  49. package/dist/db/index.js.map +1 -0
  50. package/dist/db/pg.d.ts +31 -0
  51. package/dist/db/pg.d.ts.map +1 -0
  52. package/dist/db/pg.js +214 -0
  53. package/dist/db/pg.js.map +1 -0
  54. package/dist/db/sqlite.d.ts +30 -0
  55. package/dist/db/sqlite.d.ts.map +1 -0
  56. package/dist/db/sqlite.js +195 -0
  57. package/dist/db/sqlite.js.map +1 -0
  58. package/dist/index.d.ts +2 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +122 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/routes/agents.d.ts +3 -0
  63. package/dist/routes/agents.d.ts.map +1 -0
  64. package/dist/routes/agents.js +103 -0
  65. package/dist/routes/agents.js.map +1 -0
  66. package/dist/routes/files.d.ts +4 -0
  67. package/dist/routes/files.d.ts.map +1 -0
  68. package/dist/routes/files.js +189 -0
  69. package/dist/routes/files.js.map +1 -0
  70. package/dist/routes/health.d.ts +5 -0
  71. package/dist/routes/health.d.ts.map +1 -0
  72. package/dist/routes/health.js +72 -0
  73. package/dist/routes/health.js.map +1 -0
  74. package/dist/routes/runners.d.ts +8 -0
  75. package/dist/routes/runners.d.ts.map +1 -0
  76. package/dist/routes/runners.js +33 -0
  77. package/dist/routes/runners.js.map +1 -0
  78. package/dist/routes/sessions.d.ts +10 -0
  79. package/dist/routes/sessions.d.ts.map +1 -0
  80. package/dist/routes/sessions.js +392 -0
  81. package/dist/routes/sessions.js.map +1 -0
  82. package/dist/runner/coordinator.d.ts +52 -0
  83. package/dist/runner/coordinator.d.ts.map +1 -0
  84. package/dist/runner/coordinator.js +157 -0
  85. package/dist/runner/coordinator.js.map +1 -0
  86. package/dist/runner/local-backend.d.ts +26 -0
  87. package/dist/runner/local-backend.d.ts.map +1 -0
  88. package/dist/runner/local-backend.js +79 -0
  89. package/dist/runner/local-backend.js.map +1 -0
  90. package/dist/runner/remote-backend.d.ts +32 -0
  91. package/dist/runner/remote-backend.d.ts.map +1 -0
  92. package/dist/runner/remote-backend.js +81 -0
  93. package/dist/runner/remote-backend.js.map +1 -0
  94. package/dist/runner/runner-client.d.ts +53 -0
  95. package/dist/runner/runner-client.d.ts.map +1 -0
  96. package/dist/runner/runner-client.js +157 -0
  97. package/dist/runner/runner-client.js.map +1 -0
  98. package/dist/runner/types.d.ts +37 -0
  99. package/dist/runner/types.d.ts.map +1 -0
  100. package/dist/runner/types.js +2 -0
  101. package/dist/runner/types.js.map +1 -0
  102. package/dist/schemas.d.ts +3 -0
  103. package/dist/schemas.d.ts.map +1 -0
  104. package/dist/schemas.js +73 -0
  105. package/dist/schemas.js.map +1 -0
  106. package/package.json +44 -0
@@ -0,0 +1,103 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join, isAbsolute } from 'node:path';
3
+ import { upsertAgent, getAgent, listAgents, deleteAgent } from '../db/index.js';
4
+ const nameParam = {
5
+ type: 'object',
6
+ properties: { name: { type: 'string' } },
7
+ required: ['name'],
8
+ };
9
+ export function agentRoutes(app, dataDir) {
10
+ // Deploy agent (provide local path to agent directory)
11
+ app.post('/api/agents', {
12
+ schema: {
13
+ tags: ['agents'],
14
+ body: {
15
+ type: 'object',
16
+ properties: {
17
+ name: { type: 'string' },
18
+ path: { type: 'string' },
19
+ },
20
+ required: ['name', 'path'],
21
+ },
22
+ response: {
23
+ 201: {
24
+ type: 'object',
25
+ properties: { agent: { $ref: 'Agent#' } },
26
+ required: ['agent'],
27
+ },
28
+ 400: { $ref: 'ApiError#' },
29
+ },
30
+ },
31
+ }, async (req, reply) => {
32
+ const { name, path } = req.body;
33
+ // Resolve relative paths against dataDir
34
+ const resolvedPath = isAbsolute(path) ? path : join(dataDir, path);
35
+ // Validate: CLAUDE.md must exist
36
+ if (!existsSync(join(resolvedPath, 'CLAUDE.md'))) {
37
+ return reply.status(400).send({ error: 'Agent directory must contain CLAUDE.md', statusCode: 400 });
38
+ }
39
+ const agent = await upsertAgent(name, resolvedPath);
40
+ return reply.status(201).send({ agent });
41
+ });
42
+ // List agents
43
+ app.get('/api/agents', {
44
+ schema: {
45
+ tags: ['agents'],
46
+ response: {
47
+ 200: {
48
+ type: 'object',
49
+ properties: {
50
+ agents: { type: 'array', items: { $ref: 'Agent#' } },
51
+ },
52
+ required: ['agents'],
53
+ },
54
+ },
55
+ },
56
+ }, async (_req, reply) => {
57
+ const agents = await listAgents();
58
+ return reply.send({ agents });
59
+ });
60
+ // Get agent
61
+ app.get('/api/agents/:name', {
62
+ schema: {
63
+ tags: ['agents'],
64
+ params: nameParam,
65
+ response: {
66
+ 200: {
67
+ type: 'object',
68
+ properties: { agent: { $ref: 'Agent#' } },
69
+ required: ['agent'],
70
+ },
71
+ 404: { $ref: 'ApiError#' },
72
+ },
73
+ },
74
+ }, async (req, reply) => {
75
+ const agent = await getAgent(req.params.name);
76
+ if (!agent) {
77
+ return reply.status(404).send({ error: 'Agent not found', statusCode: 404 });
78
+ }
79
+ return reply.send({ agent });
80
+ });
81
+ // Delete agent
82
+ app.delete('/api/agents/:name', {
83
+ schema: {
84
+ tags: ['agents'],
85
+ params: nameParam,
86
+ response: {
87
+ 200: {
88
+ type: 'object',
89
+ properties: { ok: { type: 'boolean' } },
90
+ required: ['ok'],
91
+ },
92
+ 404: { $ref: 'ApiError#' },
93
+ },
94
+ },
95
+ }, async (req, reply) => {
96
+ const deleted = await deleteAgent(req.params.name);
97
+ if (!deleted) {
98
+ return reply.status(404).send({ error: 'Agent not found', statusCode: 404 });
99
+ }
100
+ return reply.send({ ok: true });
101
+ });
102
+ }
103
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { RunnerCoordinator } from '../runner/coordinator.js';
3
+ export declare function fileRoutes(app: FastifyInstance, coordinator: RunnerCoordinator, dataDir: string): void;
4
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/routes/files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAuFlE,wBAAgB,UAAU,CAAC,GAAG,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CA0HtG"}
@@ -0,0 +1,189 @@
1
+ import { readdirSync, statSync, readFileSync, existsSync } from 'node:fs';
2
+ import { join, relative } from 'node:path';
3
+ import { getSession } from '../db/index.js';
4
+ // Same skip list as state-persistence.ts — no value showing these to clients
5
+ const SKIP_NAMES = new Set([
6
+ 'node_modules', '.git', '__pycache__', '.cache', '.npm',
7
+ '.pnpm-store', '.yarn', '.venv', 'venv', '.tmp', 'tmp',
8
+ ]);
9
+ const SKIP_EXTENSIONS = new Set(['.sock', '.lock', '.pid']);
10
+ // Max file size we'll return inline (1 MB)
11
+ const MAX_FILE_SIZE = 1_048_576;
12
+ /**
13
+ * Recursively list files in a directory, returning flat paths relative to root.
14
+ */
15
+ function listFiles(dir, root) {
16
+ const entries = [];
17
+ let items;
18
+ try {
19
+ items = readdirSync(dir);
20
+ }
21
+ catch {
22
+ return entries;
23
+ }
24
+ for (const name of items) {
25
+ if (SKIP_NAMES.has(name))
26
+ continue;
27
+ if (SKIP_EXTENSIONS.has(name.slice(name.lastIndexOf('.'))))
28
+ continue;
29
+ const fullPath = join(dir, name);
30
+ let st;
31
+ try {
32
+ st = statSync(fullPath);
33
+ }
34
+ catch {
35
+ continue;
36
+ }
37
+ if (st.isDirectory()) {
38
+ entries.push(...listFiles(fullPath, root));
39
+ }
40
+ else if (st.isFile()) {
41
+ entries.push({
42
+ path: relative(root, fullPath),
43
+ size: st.size,
44
+ modifiedAt: st.mtime.toISOString(),
45
+ });
46
+ }
47
+ }
48
+ return entries;
49
+ }
50
+ /**
51
+ * Resolve the workspace directory for a session.
52
+ * Prefers the live sandbox workspace; falls back to persisted snapshot.
53
+ * Returns { dir, source } or null if neither exists.
54
+ */
55
+ function resolveWorkspace(coordinator, dataDir, session) {
56
+ // Try live sandbox first
57
+ try {
58
+ const backend = coordinator.getBackendForRunner(session.runnerId);
59
+ const sandbox = backend.getSandbox(session.sandboxId);
60
+ if (sandbox && existsSync(sandbox.workspaceDir)) {
61
+ return { dir: sandbox.workspaceDir, source: 'sandbox' };
62
+ }
63
+ }
64
+ catch { /* runner gone */ }
65
+ // Fall back to persisted snapshot
66
+ const snapshotDir = join(dataDir, 'sessions', session.sandboxId, 'workspace');
67
+ if (existsSync(snapshotDir)) {
68
+ return { dir: snapshotDir, source: 'snapshot' };
69
+ }
70
+ return null;
71
+ }
72
+ const idParam = {
73
+ type: 'object',
74
+ properties: { id: { type: 'string', format: 'uuid' } },
75
+ required: ['id'],
76
+ };
77
+ export function fileRoutes(app, coordinator, dataDir) {
78
+ // List files in session workspace
79
+ app.get('/api/sessions/:id/files', {
80
+ schema: {
81
+ tags: ['sessions'],
82
+ params: idParam,
83
+ response: {
84
+ 200: {
85
+ type: 'object',
86
+ properties: {
87
+ files: {
88
+ type: 'array',
89
+ items: {
90
+ type: 'object',
91
+ properties: {
92
+ path: { type: 'string' },
93
+ size: { type: 'integer' },
94
+ modifiedAt: { type: 'string', format: 'date-time' },
95
+ },
96
+ required: ['path', 'size', 'modifiedAt'],
97
+ },
98
+ },
99
+ source: { type: 'string', enum: ['sandbox', 'snapshot'] },
100
+ },
101
+ required: ['files', 'source'],
102
+ },
103
+ 404: { $ref: 'ApiError#' },
104
+ },
105
+ },
106
+ }, async (req, reply) => {
107
+ const session = await getSession(req.params.id);
108
+ if (!session) {
109
+ return reply.status(404).send({ error: 'Session not found', statusCode: 404 });
110
+ }
111
+ const workspace = resolveWorkspace(coordinator, dataDir, session);
112
+ if (!workspace) {
113
+ return reply.status(404).send({ error: 'No workspace available for this session', statusCode: 404 });
114
+ }
115
+ const files = listFiles(workspace.dir, workspace.dir);
116
+ return reply.send({ files, source: workspace.source });
117
+ });
118
+ // Get single file content
119
+ app.get('/api/sessions/:id/files/*', {
120
+ schema: {
121
+ tags: ['sessions'],
122
+ params: {
123
+ type: 'object',
124
+ properties: {
125
+ id: { type: 'string', format: 'uuid' },
126
+ '*': { type: 'string' },
127
+ },
128
+ required: ['id', '*'],
129
+ },
130
+ response: {
131
+ 200: {
132
+ type: 'object',
133
+ properties: {
134
+ path: { type: 'string' },
135
+ content: { type: 'string' },
136
+ size: { type: 'integer' },
137
+ source: { type: 'string', enum: ['sandbox', 'snapshot'] },
138
+ },
139
+ required: ['path', 'content', 'size', 'source'],
140
+ },
141
+ 400: { $ref: 'ApiError#' },
142
+ 404: { $ref: 'ApiError#' },
143
+ },
144
+ },
145
+ }, async (req, reply) => {
146
+ const session = await getSession(req.params.id);
147
+ if (!session) {
148
+ return reply.status(404).send({ error: 'Session not found', statusCode: 404 });
149
+ }
150
+ const filePath = req.params['*'];
151
+ if (!filePath) {
152
+ return reply.status(400).send({ error: 'File path required', statusCode: 400 });
153
+ }
154
+ // Path traversal protection
155
+ if (filePath.includes('..') || filePath.startsWith('/')) {
156
+ return reply.status(400).send({ error: 'Invalid file path', statusCode: 400 });
157
+ }
158
+ const workspace = resolveWorkspace(coordinator, dataDir, session);
159
+ if (!workspace) {
160
+ return reply.status(404).send({ error: 'No workspace available for this session', statusCode: 404 });
161
+ }
162
+ const fullPath = join(workspace.dir, filePath);
163
+ // Ensure resolved path is still within workspace (belt and suspenders)
164
+ if (!fullPath.startsWith(workspace.dir)) {
165
+ return reply.status(400).send({ error: 'Invalid file path', statusCode: 400 });
166
+ }
167
+ let st;
168
+ try {
169
+ st = statSync(fullPath);
170
+ }
171
+ catch {
172
+ return reply.status(404).send({ error: 'File not found', statusCode: 404 });
173
+ }
174
+ if (!st.isFile()) {
175
+ return reply.status(400).send({ error: 'Path is not a file', statusCode: 400 });
176
+ }
177
+ if (st.size > MAX_FILE_SIZE) {
178
+ return reply.status(400).send({ error: `File too large (${st.size} bytes, max ${MAX_FILE_SIZE})`, statusCode: 400 });
179
+ }
180
+ const content = readFileSync(fullPath, 'utf-8');
181
+ return reply.send({
182
+ path: filePath,
183
+ content,
184
+ size: st.size,
185
+ source: workspace.source,
186
+ });
187
+ });
188
+ }
189
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/routes/files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { SandboxPool } from '@ash-ai/sandbox';
3
+ import type { RunnerCoordinator } from '../runner/coordinator.js';
4
+ export declare function healthRoutes(app: FastifyInstance, coordinator: RunnerCoordinator, localPool: SandboxPool | null): void;
5
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAUlE,wBAAgB,YAAY,CAAC,GAAG,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAoEtH"}
@@ -0,0 +1,72 @@
1
+ import { listSessions } from '../db/index.js';
2
+ const startTime = Date.now();
3
+ const EMPTY_POOL = {
4
+ total: 0, cold: 0, warming: 0, warm: 0, waiting: 0, running: 0,
5
+ maxCapacity: 0, resumeWarmHits: 0, resumeColdHits: 0,
6
+ };
7
+ export function healthRoutes(app, coordinator, localPool) {
8
+ app.get('/health', {
9
+ schema: {
10
+ tags: ['health'],
11
+ response: {
12
+ 200: { $ref: 'HealthResponse#' },
13
+ },
14
+ },
15
+ }, async (_req, reply) => {
16
+ const sessions = (await listSessions()).filter((s) => s.status === 'active');
17
+ const poolStats = localPool ? await localPool.statsAsync() : EMPTY_POOL;
18
+ return reply.send({
19
+ status: 'ok',
20
+ activeSessions: sessions.length,
21
+ activeSandboxes: localPool?.activeCount ?? 0,
22
+ uptime: Math.floor((Date.now() - startTime) / 1000),
23
+ pool: poolStats,
24
+ });
25
+ });
26
+ // Prometheus text format — no library needed
27
+ app.get('/metrics', {
28
+ schema: { tags: ['health'], hide: true },
29
+ }, async (_req, reply) => {
30
+ const sessions = (await listSessions()).filter((s) => s.status === 'active');
31
+ const pool = localPool ? await localPool.statsAsync() : EMPTY_POOL;
32
+ const uptime = Math.floor((Date.now() - startTime) / 1000);
33
+ const lines = [
34
+ '# HELP ash_up Whether the Ash server is up (always 1 if reachable).',
35
+ '# TYPE ash_up gauge',
36
+ 'ash_up 1',
37
+ '',
38
+ '# HELP ash_uptime_seconds Seconds since server start.',
39
+ '# TYPE ash_uptime_seconds gauge',
40
+ `ash_uptime_seconds ${uptime}`,
41
+ '',
42
+ '# HELP ash_active_sessions Number of active sessions.',
43
+ '# TYPE ash_active_sessions gauge',
44
+ `ash_active_sessions ${sessions.length}`,
45
+ '',
46
+ '# HELP ash_active_sandboxes Number of live sandbox processes.',
47
+ '# TYPE ash_active_sandboxes gauge',
48
+ `ash_active_sandboxes ${localPool?.activeCount ?? 0}`,
49
+ '',
50
+ '# HELP ash_pool_sandboxes Sandbox count by state.',
51
+ '# TYPE ash_pool_sandboxes gauge',
52
+ `ash_pool_sandboxes{state="cold"} ${pool.cold}`,
53
+ `ash_pool_sandboxes{state="warming"} ${pool.warming}`,
54
+ `ash_pool_sandboxes{state="warm"} ${pool.warm}`,
55
+ `ash_pool_sandboxes{state="waiting"} ${pool.waiting}`,
56
+ `ash_pool_sandboxes{state="running"} ${pool.running}`,
57
+ '',
58
+ '# HELP ash_pool_max_capacity Maximum sandbox capacity.',
59
+ '# TYPE ash_pool_max_capacity gauge',
60
+ `ash_pool_max_capacity ${pool.maxCapacity}`,
61
+ '',
62
+ '# HELP ash_resume_total Total session resumes by path (warm=sandbox alive, cold=new sandbox).',
63
+ '# TYPE ash_resume_total counter',
64
+ `ash_resume_total{path="warm"} ${pool.resumeWarmHits}`,
65
+ `ash_resume_total{path="cold"} ${pool.resumeColdHits}`,
66
+ '',
67
+ ];
68
+ reply.header('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');
69
+ return reply.send(lines.join('\n'));
70
+ });
71
+ }
72
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { RunnerCoordinator } from '../runner/coordinator.js';
3
+ /**
4
+ * Internal endpoints for runner registration and heartbeat.
5
+ * These are called by runner processes, not by clients.
6
+ */
7
+ export declare function runnerRoutes(app: FastifyInstance, coordinator: RunnerCoordinator): void;
8
+ //# sourceMappingURL=runners.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runners.d.ts","sourceRoot":"","sources":["../../src/routes/runners.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAyCvF"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Internal endpoints for runner registration and heartbeat.
3
+ * These are called by runner processes, not by clients.
4
+ */
5
+ export function runnerRoutes(app, coordinator) {
6
+ // Register a runner
7
+ app.post('/api/internal/runners/register', async (req, reply) => {
8
+ const { runnerId, host, port, maxSandboxes } = req.body;
9
+ if (!runnerId || !host || !port) {
10
+ return reply.status(400).send({ error: 'Missing required fields: runnerId, host, port' });
11
+ }
12
+ coordinator.registerRunner({ runnerId, host, port, maxSandboxes: maxSandboxes ?? 100 });
13
+ return reply.send({ ok: true });
14
+ });
15
+ // Runner heartbeat
16
+ app.post('/api/internal/runners/heartbeat', async (req, reply) => {
17
+ const { runnerId, stats } = req.body;
18
+ if (!runnerId) {
19
+ return reply.status(400).send({ error: 'Missing runnerId' });
20
+ }
21
+ coordinator.heartbeat(runnerId, stats);
22
+ return reply.send({ ok: true });
23
+ });
24
+ // List runners (for monitoring)
25
+ app.get('/api/internal/runners', async (_req, reply) => {
26
+ return reply.send({
27
+ runners: coordinator.getRunnerInfo(),
28
+ count: coordinator.runnerCount,
29
+ hasLocal: coordinator.hasLocalBackend,
30
+ });
31
+ });
32
+ }
33
+ //# sourceMappingURL=runners.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runners.js","sourceRoot":"","sources":["../../src/routes/runners.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAoB,EAAE,WAA8B;IAC/E,oBAAoB;IACpB,GAAG,CAAC,IAAI,CAAC,gCAAgC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;QAC9D,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC,IAKlD,CAAC;QAEF,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+CAA+C,EAAE,CAAC,CAAC;QAC5F,CAAC;QAED,WAAW,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,IAAI,GAAG,EAAE,CAAC,CAAC;QACxF,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,mBAAmB;IACnB,GAAG,CAAC,IAAI,CAAC,iCAAiC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;QAC/D,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,IAG/B,CAAC;QAEF,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACvC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,gCAAgC;IAChC,GAAG,CAAC,GAAG,CAAC,uBAAuB,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QACrD,OAAO,KAAK,CAAC,IAAI,CAAC;YAChB,OAAO,EAAE,WAAW,CAAC,aAAa,EAAE;YACpC,KAAK,EAAE,WAAW,CAAC,WAAW;YAC9B,QAAQ,EAAE,WAAW,CAAC,eAAe;SACtC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { ServerResponse } from 'node:http';
3
+ import type { RunnerCoordinator } from '../runner/coordinator.js';
4
+ /**
5
+ * Write an SSE frame with backpressure. If the kernel TCP send buffer is full,
6
+ * waits for `drain` up to SSE_WRITE_TIMEOUT_MS before giving up.
7
+ */
8
+ export declare function writeSSE(raw: ServerResponse, frame: string): Promise<void>;
9
+ export declare function sessionRoutes(app: FastifyInstance, coordinator: RunnerCoordinator, dataDir: string): void;
10
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/routes/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AA0BlE;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhF;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAgXzG"}