@ash-ai/runner 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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/mem-db.d.ts +21 -0
- package/dist/mem-db.d.ts.map +1 -0
- package/dist/mem-db.js +79 -0
- package/dist/mem-db.js.map +1 -0
- package/dist/registration.d.ts +16 -0
- package/dist/registration.d.ts.map +1 -0
- package/dist/registration.js +53 -0
- package/dist/registration.js.map +1 -0
- package/dist/routes/health.d.ts +4 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +17 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/sandboxes.d.ts +4 -0
- package/dist/routes/sandboxes.d.ts.map +1 -0
- package/dist/routes/sandboxes.js +133 -0
- package/dist/routes/sandboxes.js.map +1 -0
- package/package.json +37 -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.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import Fastify from 'fastify';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { DEFAULT_DATA_DIR, DEFAULT_MAX_SANDBOXES, DEFAULT_IDLE_TIMEOUT_MS, DEFAULT_RUNNER_PORT } from '@ash-ai/shared';
|
|
4
|
+
import { SandboxManager, SandboxPool, persistSessionState, syncStateToCloud } from '@ash-ai/sandbox';
|
|
5
|
+
import { sandboxRoutes } from './routes/sandboxes.js';
|
|
6
|
+
import { healthRoutes } from './routes/health.js';
|
|
7
|
+
import { startRegistration, stopRegistration } from './registration.js';
|
|
8
|
+
// Config from env
|
|
9
|
+
const runnerId = process.env.ASH_RUNNER_ID || `runner-${process.pid}`;
|
|
10
|
+
const port = parseInt(process.env.ASH_RUNNER_PORT || String(DEFAULT_RUNNER_PORT), 10);
|
|
11
|
+
const host = process.env.ASH_RUNNER_HOST || '0.0.0.0';
|
|
12
|
+
const dataDir = resolve(process.env.ASH_DATA_DIR || DEFAULT_DATA_DIR);
|
|
13
|
+
const bridgeEntry = process.env.ASH_BRIDGE_ENTRY
|
|
14
|
+
? resolve(process.env.ASH_BRIDGE_ENTRY)
|
|
15
|
+
: join(resolve('.'), 'packages', 'bridge', 'dist', 'index.js');
|
|
16
|
+
const serverUrl = process.env.ASH_SERVER_URL; // e.g., http://server:4100
|
|
17
|
+
// Initialize sandbox infrastructure (runner uses a simple in-memory DB for sandboxes)
|
|
18
|
+
const sandboxManager = new SandboxManager({
|
|
19
|
+
sandboxesDir: join(dataDir, 'sandboxes'),
|
|
20
|
+
bridgeEntry,
|
|
21
|
+
});
|
|
22
|
+
const maxCapacity = parseInt(process.env.ASH_MAX_SANDBOXES || String(DEFAULT_MAX_SANDBOXES), 10);
|
|
23
|
+
const idleTimeoutMs = parseInt(process.env.ASH_IDLE_TIMEOUT_MS || String(DEFAULT_IDLE_TIMEOUT_MS), 10);
|
|
24
|
+
// Runner uses a lightweight in-memory sandbox DB (no SQLite needed for pool tracking)
|
|
25
|
+
import { InMemorySandboxDb } from './mem-db.js';
|
|
26
|
+
const sandboxDb = new InMemorySandboxDb();
|
|
27
|
+
const pool = new SandboxPool({
|
|
28
|
+
manager: sandboxManager,
|
|
29
|
+
db: sandboxDb,
|
|
30
|
+
dataDir,
|
|
31
|
+
maxCapacity,
|
|
32
|
+
idleTimeoutMs,
|
|
33
|
+
onBeforeEvict: async (entry) => {
|
|
34
|
+
if (entry.sessionId) {
|
|
35
|
+
persistSessionState(dataDir, entry.sessionId, entry.sandbox.workspaceDir, entry.agentName);
|
|
36
|
+
syncStateToCloud(dataDir, entry.sessionId).catch((err) => console.error(`[runner] Cloud sync failed for ${entry.sessionId}:`, err));
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
await pool.init();
|
|
41
|
+
pool.startIdleSweep();
|
|
42
|
+
const app = Fastify({ logger: true });
|
|
43
|
+
// Routes
|
|
44
|
+
sandboxRoutes(app, pool, dataDir);
|
|
45
|
+
healthRoutes(app, pool, runnerId, maxCapacity);
|
|
46
|
+
// Graceful shutdown
|
|
47
|
+
async function shutdown() {
|
|
48
|
+
app.log.info('Runner shutting down...');
|
|
49
|
+
stopRegistration();
|
|
50
|
+
pool.stopIdleSweep();
|
|
51
|
+
await pool.destroyAll();
|
|
52
|
+
await app.close();
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
process.on('SIGTERM', shutdown);
|
|
56
|
+
process.on('SIGINT', shutdown);
|
|
57
|
+
// Start
|
|
58
|
+
try {
|
|
59
|
+
await app.listen({ port, host });
|
|
60
|
+
app.log.info(`Ash runner ${runnerId} listening on ${host}:${port}`);
|
|
61
|
+
app.log.info(`Data directory: ${dataDir}`);
|
|
62
|
+
// Register with control plane if ASH_SERVER_URL is set
|
|
63
|
+
if (serverUrl) {
|
|
64
|
+
startRegistration({
|
|
65
|
+
runnerId,
|
|
66
|
+
host: process.env.ASH_RUNNER_ADVERTISE_HOST || host,
|
|
67
|
+
port,
|
|
68
|
+
maxSandboxes: maxCapacity,
|
|
69
|
+
serverUrl,
|
|
70
|
+
pool,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
app.log.error(err);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACvH,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACrG,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAExE,kBAAkB;AAClB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,UAAU,OAAO,CAAC,GAAG,EAAE,CAAC;AACtE,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,MAAM,CAAC,mBAAmB,CAAC,EAAE,EAAE,CAAC,CAAC;AACtF,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,SAAS,CAAC;AACtD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,gBAAgB,CAAC,CAAC;AACtE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB;IAC9C,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACvC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AACjE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,2BAA2B;AAEzE,sFAAsF;AACtF,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC;IACxC,YAAY,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;IACxC,WAAW;CACZ,CAAC,CAAC;AAEH,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,MAAM,CAAC,qBAAqB,CAAC,EAAE,EAAE,CAAC,CAAC;AACjG,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,MAAM,CAAC,uBAAuB,CAAC,EAAE,EAAE,CAAC,CAAC;AAEvG,sFAAsF;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,MAAM,SAAS,GAAG,IAAI,iBAAiB,EAAE,CAAC;AAE1C,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC;IAC3B,OAAO,EAAE,cAAc;IACvB,EAAE,EAAE,SAAS;IACb,OAAO;IACP,WAAW;IACX,aAAa;IACb,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC7B,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACpB,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;YAC3F,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CACvD,OAAO,CAAC,KAAK,CAAC,kCAAkC,KAAK,CAAC,SAAS,GAAG,EAAE,GAAG,CAAC,CACzE,CAAC;QACJ,CAAC;IACH,CAAC;CACF,CAAC,CAAC;AACH,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;AAClB,IAAI,CAAC,cAAc,EAAE,CAAC;AAEtB,MAAM,GAAG,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAEtC,SAAS;AACT,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAClC,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAE/C,oBAAoB;AACpB,KAAK,UAAU,QAAQ;IACrB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACxC,gBAAgB,EAAE,CAAC;IACnB,IAAI,CAAC,aAAa,EAAE,CAAC;IACrB,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;IACxB,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;IAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAChC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAE/B,QAAQ;AACR,IAAI,CAAC;IACH,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACjC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,QAAQ,iBAAiB,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;IACpE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,mBAAmB,OAAO,EAAE,CAAC,CAAC;IAE3C,uDAAuD;IACvD,IAAI,SAAS,EAAE,CAAC;QACd,iBAAiB,CAAC;YAChB,QAAQ;YACR,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,IAAI;YACnD,IAAI;YACJ,YAAY,EAAE,WAAW;YACzB,SAAS;YACT,IAAI;SACL,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAAC,OAAO,GAAG,EAAE,CAAC;IACb,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
package/dist/mem-db.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SandboxState, SandboxRecord } from '@ash-ai/shared';
|
|
2
|
+
import type { SandboxDb } from '@ash-ai/sandbox';
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight in-memory implementation of SandboxDb for the runner process.
|
|
5
|
+
* The runner doesn't persist sandbox state across restarts — the coordinator
|
|
6
|
+
* handles that. This is just for pool tracking during the runner's lifetime.
|
|
7
|
+
*/
|
|
8
|
+
export declare class InMemorySandboxDb implements SandboxDb {
|
|
9
|
+
private sandboxes;
|
|
10
|
+
insertSandbox(id: string, agentName: string, workspaceDir: string, sessionId?: string): Promise<void>;
|
|
11
|
+
updateSandboxState(id: string, state: SandboxState): Promise<void>;
|
|
12
|
+
updateSandboxSession(id: string, sessionId: string | null): Promise<void>;
|
|
13
|
+
touchSandbox(id: string): Promise<void>;
|
|
14
|
+
getSandbox(id: string): Promise<SandboxRecord | null>;
|
|
15
|
+
countSandboxes(): Promise<number>;
|
|
16
|
+
getBestEvictionCandidate(): Promise<SandboxRecord | null>;
|
|
17
|
+
getIdleSandboxes(olderThan: string): Promise<SandboxRecord[]>;
|
|
18
|
+
deleteSandbox(id: string): Promise<void>;
|
|
19
|
+
markAllSandboxesCold(): Promise<number>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=mem-db.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mem-db.d.ts","sourceRoot":"","sources":["../src/mem-db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,SAAS;IACjD,OAAO,CAAC,SAAS,CAAoC;IAE/C,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAarG,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlE,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzE,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAIrD,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAIjC,wBAAwB,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAiBzD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAU7D,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;CAU9C"}
|
package/dist/mem-db.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight in-memory implementation of SandboxDb for the runner process.
|
|
3
|
+
* The runner doesn't persist sandbox state across restarts — the coordinator
|
|
4
|
+
* handles that. This is just for pool tracking during the runner's lifetime.
|
|
5
|
+
*/
|
|
6
|
+
export class InMemorySandboxDb {
|
|
7
|
+
sandboxes = new Map();
|
|
8
|
+
async insertSandbox(id, agentName, workspaceDir, sessionId) {
|
|
9
|
+
const now = new Date().toISOString();
|
|
10
|
+
this.sandboxes.set(id, {
|
|
11
|
+
id,
|
|
12
|
+
sessionId: sessionId ?? null,
|
|
13
|
+
agentName,
|
|
14
|
+
state: 'warming',
|
|
15
|
+
workspaceDir,
|
|
16
|
+
createdAt: now,
|
|
17
|
+
lastUsedAt: now,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async updateSandboxState(id, state) {
|
|
21
|
+
const record = this.sandboxes.get(id);
|
|
22
|
+
if (record)
|
|
23
|
+
record.state = state;
|
|
24
|
+
}
|
|
25
|
+
async updateSandboxSession(id, sessionId) {
|
|
26
|
+
const record = this.sandboxes.get(id);
|
|
27
|
+
if (record)
|
|
28
|
+
record.sessionId = sessionId;
|
|
29
|
+
}
|
|
30
|
+
async touchSandbox(id) {
|
|
31
|
+
const record = this.sandboxes.get(id);
|
|
32
|
+
if (record)
|
|
33
|
+
record.lastUsedAt = new Date().toISOString();
|
|
34
|
+
}
|
|
35
|
+
async getSandbox(id) {
|
|
36
|
+
return this.sandboxes.get(id) ?? null;
|
|
37
|
+
}
|
|
38
|
+
async countSandboxes() {
|
|
39
|
+
return this.sandboxes.size;
|
|
40
|
+
}
|
|
41
|
+
async getBestEvictionCandidate() {
|
|
42
|
+
const statePriority = { cold: 0, warm: 1, waiting: 2 };
|
|
43
|
+
let best = null;
|
|
44
|
+
let bestPriority = Infinity;
|
|
45
|
+
for (const record of this.sandboxes.values()) {
|
|
46
|
+
const priority = statePriority[record.state];
|
|
47
|
+
if (priority === undefined)
|
|
48
|
+
continue; // running — not evictable
|
|
49
|
+
if (priority < bestPriority || (priority === bestPriority && best && record.lastUsedAt < best.lastUsedAt)) {
|
|
50
|
+
best = record;
|
|
51
|
+
bestPriority = priority;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return best;
|
|
55
|
+
}
|
|
56
|
+
async getIdleSandboxes(olderThan) {
|
|
57
|
+
const result = [];
|
|
58
|
+
for (const record of this.sandboxes.values()) {
|
|
59
|
+
if (record.state === 'waiting' && record.lastUsedAt < olderThan) {
|
|
60
|
+
result.push(record);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return result.sort((a, b) => a.lastUsedAt.localeCompare(b.lastUsedAt));
|
|
64
|
+
}
|
|
65
|
+
async deleteSandbox(id) {
|
|
66
|
+
this.sandboxes.delete(id);
|
|
67
|
+
}
|
|
68
|
+
async markAllSandboxesCold() {
|
|
69
|
+
let count = 0;
|
|
70
|
+
for (const record of this.sandboxes.values()) {
|
|
71
|
+
if (record.state !== 'cold') {
|
|
72
|
+
record.state = 'cold';
|
|
73
|
+
count++;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return count;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=mem-db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mem-db.js","sourceRoot":"","sources":["../src/mem-db.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,OAAO,iBAAiB;IACpB,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;IAErD,KAAK,CAAC,aAAa,CAAC,EAAU,EAAE,SAAiB,EAAE,YAAoB,EAAE,SAAkB;QACzF,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE;YACrB,EAAE;YACF,SAAS,EAAE,SAAS,IAAI,IAAI;YAC5B,SAAS;YACT,KAAK,EAAE,SAAS;YAChB,YAAY;YACZ,SAAS,EAAE,GAAG;YACd,UAAU,EAAE,GAAG;SAChB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,EAAU,EAAE,KAAmB;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM;YAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,EAAU,EAAE,SAAwB;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM;YAAE,MAAM,CAAC,SAAS,GAAG,SAAS,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,EAAU;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM;YAAE,MAAM,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU;QACzB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,wBAAwB;QAC5B,MAAM,aAAa,GAA2B,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAC/E,IAAI,IAAI,GAAyB,IAAI,CAAC;QACtC,IAAI,YAAY,GAAG,QAAQ,CAAC;QAE5B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC7C,IAAI,QAAQ,KAAK,SAAS;gBAAE,SAAS,CAAC,0BAA0B;YAChE,IAAI,QAAQ,GAAG,YAAY,IAAI,CAAC,QAAQ,KAAK,YAAY,IAAI,IAAI,IAAI,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC1G,IAAI,GAAG,MAAM,CAAC;gBACd,YAAY,GAAG,QAAQ,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,SAAiB;QACtC,MAAM,MAAM,GAAoB,EAAE,CAAC;QACnC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,UAAU,GAAG,SAAS,EAAE,CAAC;gBAChE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,EAAU;QAC5B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,oBAAoB;QACxB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,IAAI,MAAM,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;gBAC5B,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC;gBACtB,KAAK,EAAE,CAAC;YACV,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SandboxPool } from '@ash-ai/sandbox';
|
|
2
|
+
interface RegistrationOpts {
|
|
3
|
+
runnerId: string;
|
|
4
|
+
host: string;
|
|
5
|
+
port: number;
|
|
6
|
+
maxSandboxes: number;
|
|
7
|
+
serverUrl: string;
|
|
8
|
+
pool: SandboxPool;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Register this runner with the control plane and begin heartbeat loop.
|
|
12
|
+
*/
|
|
13
|
+
export declare function startRegistration(opts: RegistrationOpts): void;
|
|
14
|
+
export declare function stopRegistration(): void;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=registration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../src/registration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,UAAU,gBAAgB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,WAAW,CAAC;CACnB;AAID;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAyC9D;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { RUNNER_HEARTBEAT_INTERVAL_MS } from '@ash-ai/shared';
|
|
2
|
+
let heartbeatTimer = null;
|
|
3
|
+
/**
|
|
4
|
+
* Register this runner with the control plane and begin heartbeat loop.
|
|
5
|
+
*/
|
|
6
|
+
export function startRegistration(opts) {
|
|
7
|
+
const { runnerId, host, port, maxSandboxes, serverUrl, pool } = opts;
|
|
8
|
+
const registerUrl = `${serverUrl}/api/internal/runners/register`;
|
|
9
|
+
const heartbeatUrl = `${serverUrl}/api/internal/runners/heartbeat`;
|
|
10
|
+
// Initial registration
|
|
11
|
+
const register = async () => {
|
|
12
|
+
try {
|
|
13
|
+
const resp = await fetch(registerUrl, {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: { 'Content-Type': 'application/json' },
|
|
16
|
+
body: JSON.stringify({ runnerId, host, port, maxSandboxes }),
|
|
17
|
+
});
|
|
18
|
+
if (!resp.ok) {
|
|
19
|
+
console.error(`[runner] Registration failed: ${resp.status} ${await resp.text()}`);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
console.log(`[runner] Registered with control plane at ${serverUrl}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
console.error(`[runner] Failed to register with ${serverUrl}:`, err);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
// Heartbeat
|
|
30
|
+
const heartbeat = async () => {
|
|
31
|
+
try {
|
|
32
|
+
const stats = await pool.statsAsync();
|
|
33
|
+
await fetch(heartbeatUrl, {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: { 'Content-Type': 'application/json' },
|
|
36
|
+
body: JSON.stringify({ runnerId, stats, activeCount: pool.activeCount }),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error('[runner] Heartbeat failed:', err);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
register();
|
|
44
|
+
heartbeatTimer = setInterval(heartbeat, RUNNER_HEARTBEAT_INTERVAL_MS);
|
|
45
|
+
heartbeatTimer.unref();
|
|
46
|
+
}
|
|
47
|
+
export function stopRegistration() {
|
|
48
|
+
if (heartbeatTimer) {
|
|
49
|
+
clearInterval(heartbeatTimer);
|
|
50
|
+
heartbeatTimer = null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=registration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration.js","sourceRoot":"","sources":["../src/registration.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAW9D,IAAI,cAAc,GAA0B,IAAI,CAAC;AAEjD;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAsB;IACtD,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IACrE,MAAM,WAAW,GAAG,GAAG,SAAS,gCAAgC,CAAC;IACjE,MAAM,YAAY,GAAG,GAAG,SAAS,iCAAiC,CAAC;IAEnE,uBAAuB;IACvB,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,WAAW,EAAE;gBACpC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;aAC7D,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,iCAAiC,IAAI,CAAC,MAAM,IAAI,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACrF,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,6CAA6C,SAAS,EAAE,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,oCAAoC,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC;QACvE,CAAC;IACH,CAAC,CAAC;IAEF,YAAY;IACZ,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE;QAC3B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YACtC,MAAM,KAAK,CAAC,YAAY,EAAE;gBACxB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;aACzE,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;QACnD,CAAC;IACH,CAAC,CAAC;IAEF,QAAQ,EAAE,CAAC;IAEX,cAAc,GAAG,WAAW,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC;IACtE,cAAc,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,cAAc,EAAE,CAAC;QACnB,aAAa,CAAC,cAAc,CAAC,CAAC;QAC9B,cAAc,GAAG,IAAI,CAAC;IACxB,CAAC;AACH,CAAC"}
|
|
@@ -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,wBAAgB,YAAY,CAC1B,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,IAAI,CAeN"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function healthRoutes(app, pool, runnerId, maxSandboxes) {
|
|
2
|
+
app.get('/runner/health', async (_req, reply) => {
|
|
3
|
+
const stats = await pool.statsAsync();
|
|
4
|
+
return reply.send({
|
|
5
|
+
runnerId,
|
|
6
|
+
status: 'ok',
|
|
7
|
+
capacity: {
|
|
8
|
+
max: maxSandboxes,
|
|
9
|
+
active: pool.activeCount,
|
|
10
|
+
available: maxSandboxes - pool.activeCount,
|
|
11
|
+
},
|
|
12
|
+
pool: stats,
|
|
13
|
+
uptime: process.uptime(),
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,YAAY,CAC1B,GAAoB,EACpB,IAAiB,EACjB,QAAgB,EAChB,YAAoB;IAEpB,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QAC9C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACtC,OAAO,KAAK,CAAC,IAAI,CAAC;YAChB,QAAQ;YACR,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE;gBACR,GAAG,EAAE,YAAY;gBACjB,MAAM,EAAE,IAAI,CAAC,WAAW;gBACxB,SAAS,EAAE,YAAY,GAAG,IAAI,CAAC,WAAW;aAC3C;YACD,IAAI,EAAE,KAAK;YACX,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;SACzB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandboxes.d.ts","sourceRoot":"","sources":["../../src/routes/sandboxes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAgCnD,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CA4H5F"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { persistSessionState, syncStateToCloud } from '@ash-ai/sandbox';
|
|
2
|
+
import { SSE_WRITE_TIMEOUT_MS } from '@ash-ai/shared';
|
|
3
|
+
/**
|
|
4
|
+
* Write an SSE frame with backpressure.
|
|
5
|
+
*/
|
|
6
|
+
async function writeSSE(raw, frame) {
|
|
7
|
+
const canWrite = raw.write(frame);
|
|
8
|
+
if (!canWrite) {
|
|
9
|
+
let timer;
|
|
10
|
+
let onDrain;
|
|
11
|
+
const drained = await Promise.race([
|
|
12
|
+
new Promise((resolve) => {
|
|
13
|
+
onDrain = () => resolve(true);
|
|
14
|
+
raw.once('drain', onDrain);
|
|
15
|
+
}),
|
|
16
|
+
new Promise((resolve) => {
|
|
17
|
+
timer = setTimeout(() => resolve(false), SSE_WRITE_TIMEOUT_MS);
|
|
18
|
+
}),
|
|
19
|
+
]);
|
|
20
|
+
if (drained) {
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
raw.removeListener('drain', onDrain);
|
|
25
|
+
throw new Error('Client write timeout — closing stream');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function sandboxRoutes(app, pool, dataDir) {
|
|
30
|
+
// Create sandbox
|
|
31
|
+
app.post('/runner/sandboxes', async (req, reply) => {
|
|
32
|
+
const body = req.body;
|
|
33
|
+
try {
|
|
34
|
+
const sandbox = await pool.create({
|
|
35
|
+
agentDir: body.agentDir,
|
|
36
|
+
sessionId: body.sessionId,
|
|
37
|
+
id: body.sandboxId,
|
|
38
|
+
agentName: body.agentName,
|
|
39
|
+
skipAgentCopy: body.skipAgentCopy,
|
|
40
|
+
limits: body.limits,
|
|
41
|
+
});
|
|
42
|
+
return reply.status(201).send({
|
|
43
|
+
sandboxId: sandbox.id,
|
|
44
|
+
workspaceDir: sandbox.workspaceDir,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
49
|
+
if (msg.includes('capacity reached')) {
|
|
50
|
+
return reply.status(503).send({ error: msg });
|
|
51
|
+
}
|
|
52
|
+
return reply.status(500).send({ error: `Failed to create sandbox: ${msg}` });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
// Destroy sandbox
|
|
56
|
+
app.delete('/runner/sandboxes/:id', async (req, reply) => {
|
|
57
|
+
await pool.destroy(req.params.id);
|
|
58
|
+
return reply.status(204).send();
|
|
59
|
+
});
|
|
60
|
+
// Send command to sandbox — returns SSE stream
|
|
61
|
+
app.post('/runner/sandboxes/:id/cmd', async (req, reply) => {
|
|
62
|
+
const sandbox = pool.get(req.params.id);
|
|
63
|
+
if (!sandbox) {
|
|
64
|
+
return reply.status(404).send({ error: 'Sandbox not found' });
|
|
65
|
+
}
|
|
66
|
+
const body = req.body;
|
|
67
|
+
pool.markRunning(req.params.id);
|
|
68
|
+
reply.raw.writeHead(200, {
|
|
69
|
+
'Content-Type': 'text/event-stream',
|
|
70
|
+
'Cache-Control': 'no-cache',
|
|
71
|
+
'Connection': 'keep-alive',
|
|
72
|
+
});
|
|
73
|
+
try {
|
|
74
|
+
const events = sandbox.client.sendCommand(body);
|
|
75
|
+
for await (const event of events) {
|
|
76
|
+
if (event.ev === 'message') {
|
|
77
|
+
await writeSSE(reply.raw, `event: message\ndata: ${JSON.stringify(event.data)}\n\n`);
|
|
78
|
+
}
|
|
79
|
+
else if (event.ev === 'error') {
|
|
80
|
+
await writeSSE(reply.raw, `event: error\ndata: ${JSON.stringify({ error: event.error })}\n\n`);
|
|
81
|
+
}
|
|
82
|
+
else if (event.ev === 'done') {
|
|
83
|
+
await writeSSE(reply.raw, `event: done\ndata: ${JSON.stringify({ sessionId: event.sessionId })}\n\n`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
89
|
+
reply.raw.write(`event: error\ndata: ${JSON.stringify({ error: msg })}\n\n`);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
pool.markWaiting(req.params.id);
|
|
93
|
+
}
|
|
94
|
+
reply.raw.end();
|
|
95
|
+
});
|
|
96
|
+
// Persist state on runner's filesystem
|
|
97
|
+
app.post('/runner/sandboxes/:id/persist', async (req, reply) => {
|
|
98
|
+
const sandbox = pool.get(req.params.id);
|
|
99
|
+
if (!sandbox) {
|
|
100
|
+
return reply.status(404).send({ error: 'Sandbox not found' });
|
|
101
|
+
}
|
|
102
|
+
const { sessionId, agentName } = req.body;
|
|
103
|
+
const success = persistSessionState(dataDir, sessionId, sandbox.workspaceDir, agentName);
|
|
104
|
+
if (success) {
|
|
105
|
+
syncStateToCloud(dataDir, sessionId).catch((err) => console.error(`[runner] Cloud sync failed for ${sessionId}:`, err));
|
|
106
|
+
}
|
|
107
|
+
return reply.send({ success });
|
|
108
|
+
});
|
|
109
|
+
// Mark sandbox running/waiting
|
|
110
|
+
app.post('/runner/sandboxes/:id/mark', async (req, reply) => {
|
|
111
|
+
const { state } = req.body;
|
|
112
|
+
if (state === 'running') {
|
|
113
|
+
pool.markRunning(req.params.id);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
pool.markWaiting(req.params.id);
|
|
117
|
+
}
|
|
118
|
+
return reply.send({ ok: true });
|
|
119
|
+
});
|
|
120
|
+
// Get sandbox info
|
|
121
|
+
app.get('/runner/sandboxes/:id', async (req, reply) => {
|
|
122
|
+
const sandbox = pool.get(req.params.id);
|
|
123
|
+
if (!sandbox) {
|
|
124
|
+
return reply.status(404).send({ error: 'Sandbox not found' });
|
|
125
|
+
}
|
|
126
|
+
return reply.send({
|
|
127
|
+
sandboxId: sandbox.id,
|
|
128
|
+
workspaceDir: sandbox.workspaceDir,
|
|
129
|
+
alive: sandbox.process.exitCode === null,
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=sandboxes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandboxes.js","sourceRoot":"","sources":["../../src/routes/sandboxes.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ash-ai/runner",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/ash-ai/ash.git",
|
|
16
|
+
"directory": "packages/runner"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"description": "Ash runner process — manages sandboxes and exposes them over HTTP/2",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@aws-sdk/client-s3": "^3.700.0",
|
|
22
|
+
"better-sqlite3": "^11.0.0",
|
|
23
|
+
"fastify": "^5.2.0",
|
|
24
|
+
"@ash-ai/sandbox": "0.0.1",
|
|
25
|
+
"@ash-ai/shared": "0.0.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/better-sqlite3": "^7.6.0"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc",
|
|
32
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
33
|
+
"typecheck": "tsc --noEmit",
|
|
34
|
+
"start": "node dist/index.js",
|
|
35
|
+
"dev": "tsx src/index.ts"
|
|
36
|
+
}
|
|
37
|
+
}
|