@roj-ai/standalone-server 0.1.1 → 0.1.4
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/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/instance.js +16 -0
- package/dist/instance.js.map +1 -0
- package/dist/main.js +32 -0
- package/dist/main.js.map +1 -0
- package/dist/platform-api.js +107 -0
- package/dist/platform-api.js.map +1 -0
- package/dist/preview-proxy.js +67 -0
- package/dist/preview-proxy.js.map +1 -0
- package/dist/server.js +150 -0
- package/dist/server.js.map +1 -0
- package/dist/user-config-loader.js +59 -0
- package/dist/user-config-loader.js.map +1 -0
- package/package.json +9 -9
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @roj-ai/standalone-server — run a single roj instance on your machine.
|
|
3
|
+
*
|
|
4
|
+
* Exposes the same REST + WebSocket URL shape as the Cloudflare-hosted
|
|
5
|
+
* platform (the subset that makes sense for a local single-instance setup),
|
|
6
|
+
* so the same client SDK and React hooks work unchanged.
|
|
7
|
+
*/
|
|
8
|
+
export { startStandaloneServer } from './server.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/instance.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Singleton instance state for the standalone server.
|
|
3
|
+
*
|
|
4
|
+
* A standalone server hosts exactly one "instance" — a workspace containing
|
|
5
|
+
* sessions. The instance id is either supplied via config or auto-generated
|
|
6
|
+
* on startup and remains stable for the process lifetime.
|
|
7
|
+
*/
|
|
8
|
+
export function createInstance(options) {
|
|
9
|
+
return {
|
|
10
|
+
id: options.id ?? 'default',
|
|
11
|
+
name: options.name ?? 'standalone',
|
|
12
|
+
createdAt: new Date().toISOString(),
|
|
13
|
+
presetIds: options.presetIds,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=instance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,MAAM,UAAU,cAAc,CAAC,OAA4D;IAC1F,OAAO;QACN,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,SAAS;QAC3B,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,YAAY;QAClC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,SAAS,EAAE,OAAO,CAAC,SAAS;KAC5B,CAAA;AACF,CAAC"}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry: run the standalone roj server from a `roj.config.ts` file.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bun standalone-server [config-path] (default: roj.config.ts)
|
|
7
|
+
* bunx roj-standalone [config-path]
|
|
8
|
+
*/
|
|
9
|
+
import { resolve } from 'node:path';
|
|
10
|
+
import { startStandaloneServer } from './server.js';
|
|
11
|
+
import { loadUserConfig } from './user-config-loader.js';
|
|
12
|
+
async function main() {
|
|
13
|
+
const configPath = process.argv[2] ?? 'roj.config.ts';
|
|
14
|
+
const absoluteConfigPath = resolve(process.cwd(), configPath);
|
|
15
|
+
let userConfig;
|
|
16
|
+
try {
|
|
17
|
+
userConfig = await loadUserConfig(absoluteConfigPath);
|
|
18
|
+
console.log(`Loaded config from: ${absoluteConfigPath}`);
|
|
19
|
+
console.log(` Presets: ${userConfig.presets.map(p => p.id).join(', ')}`);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error(`Failed to load config from ${absoluteConfigPath}:`);
|
|
23
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
await startStandaloneServer({ presets: userConfig.presets });
|
|
27
|
+
}
|
|
28
|
+
main().catch((error) => {
|
|
29
|
+
console.error('Fatal error:', error);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAExD,KAAK,UAAU,IAAI;IAClB,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,eAAe,CAAA;IACrD,MAAM,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAA;IAE7D,IAAI,UAAsD,CAAA;IAC1D,IAAI,CAAC;QACJ,UAAU,GAAG,MAAM,cAAc,CAAC,kBAAkB,CAAC,CAAA;QACrD,OAAO,CAAC,GAAG,CAAC,uBAAuB,kBAAkB,EAAE,CAAC,CAAA;QACxD,OAAO,CAAC,GAAG,CAAC,cAAc,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC1E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,8BAA8B,kBAAkB,GAAG,CAAC,CAAA;QAClE,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAChB,CAAC;IAED,MAAM,qBAAqB,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAA;AAC7D,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACtB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;IACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAChB,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform REST API for the standalone server.
|
|
3
|
+
*
|
|
4
|
+
* Implements the subset of @roj-ai/client/platform method contract that
|
|
5
|
+
* makes sense for a single-machine, single-instance deployment:
|
|
6
|
+
*
|
|
7
|
+
* - instances.* — singleton, always returns the one instance
|
|
8
|
+
* - sessions.* — delegates to the SDK session manager
|
|
9
|
+
* - tokens.create — noop (no auth)
|
|
10
|
+
*
|
|
11
|
+
* NOT implemented (return method_not_found):
|
|
12
|
+
* - bundles.* — presets are imported directly, not uploaded
|
|
13
|
+
* - sessions.publish — publishing requires the CF platform
|
|
14
|
+
* - instances.archive — no-op; shutdown the server instead
|
|
15
|
+
*/
|
|
16
|
+
import { Hono } from 'hono';
|
|
17
|
+
export function createPlatformApi(deps) {
|
|
18
|
+
const app = new Hono();
|
|
19
|
+
app.post('/rpc', async (c) => {
|
|
20
|
+
const body = await c.req.json().catch(() => ({}));
|
|
21
|
+
if (Array.isArray(body.batch)) {
|
|
22
|
+
const results = [];
|
|
23
|
+
for (const call of body.batch) {
|
|
24
|
+
results.push(await dispatch(deps, call.method, call.input));
|
|
25
|
+
}
|
|
26
|
+
return c.json({ results });
|
|
27
|
+
}
|
|
28
|
+
if (typeof body.method !== 'string') {
|
|
29
|
+
return c.json({ ok: false, error: { type: 'invalid_request', message: 'Missing method' } }, 400);
|
|
30
|
+
}
|
|
31
|
+
const result = await dispatch(deps, body.method, body.input);
|
|
32
|
+
return c.json(result);
|
|
33
|
+
});
|
|
34
|
+
return app;
|
|
35
|
+
}
|
|
36
|
+
async function dispatch(deps, method, input) {
|
|
37
|
+
const handler = handlers[method];
|
|
38
|
+
if (!handler) {
|
|
39
|
+
return { ok: false, error: { type: 'method_not_found', message: `Method not supported in standalone: ${method}` } };
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const value = await handler(deps, input ?? {});
|
|
43
|
+
return { ok: true, value };
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
47
|
+
deps.logger.error(`Platform RPC handler failed: ${method}`, err instanceof Error ? err : new Error(message));
|
|
48
|
+
return { ok: false, error: { type: 'handler_error', message } };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const handlers = {
|
|
52
|
+
'instances.create': async ({ instance }) => ({
|
|
53
|
+
instanceId: instance.id,
|
|
54
|
+
status: 'ready',
|
|
55
|
+
}),
|
|
56
|
+
'instances.list': async ({ instance }) => ({
|
|
57
|
+
instances: [instanceSummary(instance)],
|
|
58
|
+
total: 1,
|
|
59
|
+
}),
|
|
60
|
+
'instances.get': async ({ instance }, _input) => instanceSummary(instance),
|
|
61
|
+
'instances.status': async ({ instance, sessionManager }) => {
|
|
62
|
+
const stats = await sessionManager.getStats();
|
|
63
|
+
return {
|
|
64
|
+
instanceId: instance.id,
|
|
65
|
+
status: 'ready',
|
|
66
|
+
sandbox: { state: 'running' },
|
|
67
|
+
sessions: stats.sessions.map(s => ({
|
|
68
|
+
id: s.id,
|
|
69
|
+
presetId: s.presetId,
|
|
70
|
+
status: s.status,
|
|
71
|
+
createdAt: new Date().toISOString(),
|
|
72
|
+
})),
|
|
73
|
+
lifecycleEvents: [],
|
|
74
|
+
serviceUrls: [],
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
'instances.archive': async () => ({ ok: true }),
|
|
78
|
+
'sessions.create': async ({ sessionManager }, input) => {
|
|
79
|
+
const result = await sessionManager.callManagerMethod('sessions.create', {
|
|
80
|
+
presetId: input.presetId,
|
|
81
|
+
});
|
|
82
|
+
if (!result.ok)
|
|
83
|
+
throw new Error(result.error.message);
|
|
84
|
+
return result.value;
|
|
85
|
+
},
|
|
86
|
+
'sessions.list': async ({ sessionManager }) => {
|
|
87
|
+
const result = await sessionManager.callManagerMethod('sessions.list', {});
|
|
88
|
+
if (!result.ok)
|
|
89
|
+
throw new Error(result.error.message);
|
|
90
|
+
return result.value;
|
|
91
|
+
},
|
|
92
|
+
'tokens.create': async () => ({ token: '' }),
|
|
93
|
+
};
|
|
94
|
+
function instanceSummary(instance) {
|
|
95
|
+
return {
|
|
96
|
+
instanceId: instance.id,
|
|
97
|
+
name: instance.name,
|
|
98
|
+
status: 'ready',
|
|
99
|
+
templateSlug: 'standalone',
|
|
100
|
+
bundleSlug: 'standalone',
|
|
101
|
+
bundleRevisionId: '',
|
|
102
|
+
vcsType: 'none',
|
|
103
|
+
metadata: null,
|
|
104
|
+
createdAt: instance.createdAt,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=platform-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-api.js","sourceRoot":"","sources":["../src/platform-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preview proxy — forwards requests from
|
|
3
|
+
* /api/v1/instances/{id}/preview/{code}/{...path}
|
|
4
|
+
* to the localhost port of a running dev service.
|
|
5
|
+
*
|
|
6
|
+
* The {code} segment is treated as a service type (e.g. "dev"). The first
|
|
7
|
+
* running session that has a service of that type wins. For single-session
|
|
8
|
+
* flows this is deterministic; for multi-session flows the caller should
|
|
9
|
+
* pick a more specific code if needed.
|
|
10
|
+
*/
|
|
11
|
+
import { selectPluginState } from '@roj-ai/sdk';
|
|
12
|
+
export async function resolveServicePort(sessionManager, serviceType) {
|
|
13
|
+
const stats = await sessionManager.getStats();
|
|
14
|
+
for (const { id } of stats.sessions) {
|
|
15
|
+
const result = await sessionManager.getSession(id);
|
|
16
|
+
if (!result.ok)
|
|
17
|
+
continue;
|
|
18
|
+
const services = selectPluginState(result.value.state, 'services');
|
|
19
|
+
const entry = services?.get(serviceType);
|
|
20
|
+
// Accept any non-failed status as long as a port is assigned. The
|
|
21
|
+
// services plugin emits `ready` (not `running`); the state projection
|
|
22
|
+
// can also briefly lag the WS `serviceStatus` broadcast that clients
|
|
23
|
+
// use to decide when to render the preview iframe.
|
|
24
|
+
if (entry?.port && entry.status !== 'failed' && entry.status !== 'stopped') {
|
|
25
|
+
return entry.port;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
export async function proxyPreview(req, prefix, sessionManager, logger) {
|
|
31
|
+
const url = new URL(req.url);
|
|
32
|
+
const rest = url.pathname.slice(prefix.length);
|
|
33
|
+
const [code, ...restParts] = rest.split('/').filter(Boolean);
|
|
34
|
+
if (!code) {
|
|
35
|
+
return new Response('preview code required', { status: 404 });
|
|
36
|
+
}
|
|
37
|
+
const port = await resolveServicePort(sessionManager, code);
|
|
38
|
+
if (!port) {
|
|
39
|
+
return new Response(`No service running for code "${code}"`, { status: 503 });
|
|
40
|
+
}
|
|
41
|
+
const targetPath = '/' + restParts.join('/');
|
|
42
|
+
const targetUrl = `http://127.0.0.1:${port}${targetPath}${url.search}`;
|
|
43
|
+
const headers = new Headers(req.headers);
|
|
44
|
+
headers.delete('host');
|
|
45
|
+
try {
|
|
46
|
+
const upstream = await fetch(targetUrl, {
|
|
47
|
+
method: req.method,
|
|
48
|
+
headers,
|
|
49
|
+
body: req.method === 'GET' || req.method === 'HEAD' ? undefined : req.body,
|
|
50
|
+
redirect: 'manual',
|
|
51
|
+
});
|
|
52
|
+
return new Response(upstream.body, {
|
|
53
|
+
status: upstream.status,
|
|
54
|
+
statusText: upstream.statusText,
|
|
55
|
+
headers: upstream.headers,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
logger.warn('Preview proxy upstream failed', {
|
|
60
|
+
code,
|
|
61
|
+
port,
|
|
62
|
+
error: err instanceof Error ? err.message : String(err),
|
|
63
|
+
});
|
|
64
|
+
return new Response('Preview upstream unreachable', { status: 502 });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=preview-proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-proxy.js","sourceRoot":"","sources":["../src/preview-proxy.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone roj server — hosts the SDK agent and exposes a platform-
|
|
3
|
+
* compatible REST + WebSocket surface for a single instance on localhost.
|
|
4
|
+
*
|
|
5
|
+
* URL shape (matches @roj-ai/client/platform):
|
|
6
|
+
* POST /api/v1/rpc — platform RPC (singleton)
|
|
7
|
+
* POST /api/v1/instances/{id}/rpc — agent RPC
|
|
8
|
+
* POST /api/v1/instances/{id}/sessions/{sid}/upload — file upload (session)
|
|
9
|
+
* WS /api/v1/instances/{id}/ws — live events
|
|
10
|
+
* ANY /api/v1/instances/{id}/preview/{code}/* — dev service proxy
|
|
11
|
+
* POST /api/v1/instances/{id}/exchange — noop (no auth locally)
|
|
12
|
+
* GET /health — health check
|
|
13
|
+
*/
|
|
14
|
+
import { bootstrap, createSystemFromServices, loadConfig, validateConfig } from '@roj-ai/sdk';
|
|
15
|
+
import { createApp } from '@roj-ai/sdk/transport/http/app';
|
|
16
|
+
import { createAgentTransport, ServerAdapter } from '@roj-ai/sdk/transport/adapter';
|
|
17
|
+
import { createBunPlatform } from '@roj-ai/sdk/bun-platform';
|
|
18
|
+
import { createBunWebSocketHandlers } from '@roj-ai/transport/bun';
|
|
19
|
+
import { Hono } from 'hono';
|
|
20
|
+
import { cors } from 'hono/cors';
|
|
21
|
+
import { createInstance } from './instance.js';
|
|
22
|
+
import { createPlatformApi } from './platform-api.js';
|
|
23
|
+
import { proxyPreview } from './preview-proxy.js';
|
|
24
|
+
export async function startStandaloneServer(options) {
|
|
25
|
+
const envConfig = loadConfig();
|
|
26
|
+
const config = options.config ? { ...envConfig, ...options.config } : envConfig;
|
|
27
|
+
const errors = validateConfig(config);
|
|
28
|
+
if (errors.length > 0) {
|
|
29
|
+
throw new Error(`Configuration errors:\n${errors.map(e => ` - ${e}`).join('\n')}`);
|
|
30
|
+
}
|
|
31
|
+
const presets = options.llmMiddleware?.length
|
|
32
|
+
? options.presets.map(p => ({
|
|
33
|
+
...p,
|
|
34
|
+
llmMiddleware: [...options.llmMiddleware, ...(p.llmMiddleware ?? [])],
|
|
35
|
+
}))
|
|
36
|
+
: options.presets;
|
|
37
|
+
const services = bootstrap(config, { presets }, createBunPlatform());
|
|
38
|
+
const { logger } = services;
|
|
39
|
+
const instance = createInstance({
|
|
40
|
+
id: options.instanceId,
|
|
41
|
+
name: options.instanceName,
|
|
42
|
+
presetIds: presets.map(p => p.id),
|
|
43
|
+
});
|
|
44
|
+
if (options.onBeforeStart) {
|
|
45
|
+
await options.onBeforeStart({ config, logger });
|
|
46
|
+
}
|
|
47
|
+
const transport = createAgentTransport({ mode: 'standalone', logger });
|
|
48
|
+
const serverAdapter = transport instanceof ServerAdapter ? transport : null;
|
|
49
|
+
const system = createSystemFromServices(services, {
|
|
50
|
+
onUserOutput: (message) => transport.broadcast(message),
|
|
51
|
+
});
|
|
52
|
+
const sessionManager = system.sessionManager;
|
|
53
|
+
const agentApp = createApp({
|
|
54
|
+
...services,
|
|
55
|
+
sessionRuntime: sessionManager,
|
|
56
|
+
});
|
|
57
|
+
const platformApp = createPlatformApi({ instance, sessionManager, logger });
|
|
58
|
+
const outerApp = new Hono();
|
|
59
|
+
// Reflect the request origin and allow credentials — the SPA uses
|
|
60
|
+
// `credentials: 'include'` on its RPC fetches, which requires a specific
|
|
61
|
+
// origin header (not `*`) plus `Access-Control-Allow-Credentials: true`.
|
|
62
|
+
outerApp.use('*', cors({ origin: (origin) => origin ?? '*', credentials: true }));
|
|
63
|
+
outerApp.get('/health', (c) => c.json({ status: 'ok', timestamp: Date.now() }));
|
|
64
|
+
outerApp.route('/api/v1', platformApp);
|
|
65
|
+
// Path-based preview proxy (must come BEFORE the catch-all instance route)
|
|
66
|
+
outerApp.all('/api/v1/instances/:id/preview/*', (c) => {
|
|
67
|
+
const id = c.req.param('id');
|
|
68
|
+
const prefix = `/api/v1/instances/${id}/preview/`;
|
|
69
|
+
return proxyPreview(c.req.raw, prefix, sessionManager, logger);
|
|
70
|
+
});
|
|
71
|
+
// Noop auth-exchange (platform has real cookies; standalone is open)
|
|
72
|
+
outerApp.post('/api/v1/instances/:id/exchange', (c) => c.json({ ok: true }));
|
|
73
|
+
// Instance-scoped agent routes — strip the instance prefix and delegate to createApp
|
|
74
|
+
outerApp.all('/api/v1/instances/:id/*', async (c) => {
|
|
75
|
+
const id = c.req.param('id');
|
|
76
|
+
const prefix = `/api/v1/instances/${id}`;
|
|
77
|
+
const url = new URL(c.req.url);
|
|
78
|
+
const innerPath = url.pathname.slice(prefix.length) || '/';
|
|
79
|
+
const innerUrl = `${url.origin}${innerPath}${url.search}`;
|
|
80
|
+
return agentApp.fetch(new Request(innerUrl, c.req.raw));
|
|
81
|
+
});
|
|
82
|
+
const server = startBunServer(config, outerApp, serverAdapter);
|
|
83
|
+
try {
|
|
84
|
+
await sessionManager.loadAllSessions();
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
logger.error('Failed to load persisted sessions', err instanceof Error ? err : new Error(String(err)));
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
await transport.start();
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
logger.error('Transport start failed', err instanceof Error ? err : new Error(String(err)));
|
|
94
|
+
}
|
|
95
|
+
logger.info('Standalone server started', {
|
|
96
|
+
host: config.host,
|
|
97
|
+
port: config.port,
|
|
98
|
+
instanceId: instance.id,
|
|
99
|
+
url: `http://${config.host === '0.0.0.0' ? 'localhost' : config.host}:${config.port}`,
|
|
100
|
+
});
|
|
101
|
+
const shutdown = async () => {
|
|
102
|
+
logger.info('Shutting down standalone server...');
|
|
103
|
+
if (options.onShutdown) {
|
|
104
|
+
await options.onShutdown();
|
|
105
|
+
}
|
|
106
|
+
await sessionManager.shutdown();
|
|
107
|
+
await transport.stop();
|
|
108
|
+
server.stop();
|
|
109
|
+
};
|
|
110
|
+
process.on('SIGINT', () => {
|
|
111
|
+
void shutdown().then(() => process.exit(0));
|
|
112
|
+
});
|
|
113
|
+
process.on('SIGTERM', () => {
|
|
114
|
+
void shutdown().then(() => process.exit(0));
|
|
115
|
+
});
|
|
116
|
+
return { config, logger, instance, port: server.port ?? config.port, sessionManager, shutdown };
|
|
117
|
+
}
|
|
118
|
+
function startBunServer(config, app, serverAdapter) {
|
|
119
|
+
const wsHandlers = serverAdapter
|
|
120
|
+
? createBunWebSocketHandlers({
|
|
121
|
+
onOpen: (ws) => serverAdapter.handleOpen(ws, ws.data.sessionId ?? undefined),
|
|
122
|
+
onClose: (ws, code, reason) => serverAdapter.handleClose(ws, code, reason),
|
|
123
|
+
onMessage: (ws, message) => serverAdapter.handleMessage(ws, message),
|
|
124
|
+
onError: (ws, error) => serverAdapter.handleError(ws, error),
|
|
125
|
+
getData: (ws) => ws.data,
|
|
126
|
+
})
|
|
127
|
+
: null;
|
|
128
|
+
return Bun.serve({
|
|
129
|
+
port: config.port,
|
|
130
|
+
hostname: config.host,
|
|
131
|
+
fetch(req, server) {
|
|
132
|
+
const url = new URL(req.url);
|
|
133
|
+
const wsMatch = url.pathname.match(/^\/api\/v1\/instances\/[^/]+\/ws$/);
|
|
134
|
+
if (wsMatch && serverAdapter) {
|
|
135
|
+
const sessionId = url.searchParams.get('sessionId');
|
|
136
|
+
const upgraded = server.upgrade(req, {
|
|
137
|
+
data: {
|
|
138
|
+
clientId: crypto.randomUUID(),
|
|
139
|
+
sessionId,
|
|
140
|
+
subscribedSessions: new Set(),
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
return upgraded ? undefined : new Response('WebSocket upgrade failed', { status: 400 });
|
|
144
|
+
}
|
|
145
|
+
return app.fetch(req);
|
|
146
|
+
},
|
|
147
|
+
websocket: wsHandlers ?? { open() { }, close() { }, message() { } },
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime loader for the user `roj.config.ts` file.
|
|
3
|
+
*
|
|
4
|
+
* Uses dynamic `import()` — kept out of `@roj-ai/sdk` (which stays free of
|
|
5
|
+
* runtime-specific I/O) and out of `@roj-ai/sandbox-runtime` (Bun-specific).
|
|
6
|
+
*/
|
|
7
|
+
import { validatePreset } from '@roj-ai/sdk';
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
9
|
+
import { resolve } from 'node:path';
|
|
10
|
+
export async function loadUserConfig(configPath) {
|
|
11
|
+
const absolutePath = resolve(process.cwd(), configPath);
|
|
12
|
+
if (!existsSync(absolutePath)) {
|
|
13
|
+
throw new Error(`Configuration file not found: ${absolutePath}`);
|
|
14
|
+
}
|
|
15
|
+
let mod;
|
|
16
|
+
try {
|
|
17
|
+
mod = await import(absolutePath);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
throw new Error(`Failed to load configuration file: ${absolutePath}\n${error instanceof Error ? error.message : String(error)}`);
|
|
21
|
+
}
|
|
22
|
+
const config = mod.default;
|
|
23
|
+
if (!config) {
|
|
24
|
+
throw new Error(`Configuration file must have a default export: ${absolutePath}`);
|
|
25
|
+
}
|
|
26
|
+
if (typeof config !== 'object' || config === null) {
|
|
27
|
+
throw new Error(`Configuration must be an object: ${absolutePath}`);
|
|
28
|
+
}
|
|
29
|
+
const typedConfig = config;
|
|
30
|
+
if (!Array.isArray(typedConfig.presets)) {
|
|
31
|
+
throw new Error(`Configuration must have a 'presets' array: ${absolutePath}`);
|
|
32
|
+
}
|
|
33
|
+
const presets = typedConfig.presets;
|
|
34
|
+
const allErrors = [];
|
|
35
|
+
for (const preset of presets) {
|
|
36
|
+
if (!preset.id || typeof preset.id !== 'string') {
|
|
37
|
+
allErrors.push(`Preset missing required 'id' field`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const errors = validatePreset(preset);
|
|
41
|
+
if (errors.length > 0) {
|
|
42
|
+
allErrors.push(`Preset '${preset.id}': ${errors.join(', ')}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (allErrors.length > 0) {
|
|
46
|
+
throw new Error(`Preset validation errors:\n${allErrors.map(e => ` - ${e}`).join('\n')}`);
|
|
47
|
+
}
|
|
48
|
+
const ids = presets.map(p => p.id);
|
|
49
|
+
const duplicates = ids.filter((id, index) => ids.indexOf(id) !== index);
|
|
50
|
+
if (duplicates.length > 0) {
|
|
51
|
+
throw new Error(`Duplicate preset IDs: ${[...new Set(duplicates)].join(', ')}`);
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
presets,
|
|
55
|
+
sandboxed: typedConfig.sandboxed,
|
|
56
|
+
snapshotter: typedConfig.snapshotter,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=user-config-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-config-loader.js","sourceRoot":"","sources":["../src/user-config-loader.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roj-ai/standalone-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "./
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"roj-standalone": "./
|
|
7
|
+
"roj-standalone": "./dist/main.js"
|
|
8
8
|
},
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
|
-
"import": "./
|
|
13
|
-
"default": "./
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"type-check": "tsc --noEmit"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@roj-ai/client": "^0.1.
|
|
37
|
-
"@roj-ai/sdk": "^0.1.
|
|
38
|
-
"@roj-ai/shared": "^0.1.
|
|
39
|
-
"@roj-ai/transport": "^0.1.
|
|
36
|
+
"@roj-ai/client": "^0.1.4",
|
|
37
|
+
"@roj-ai/sdk": "^0.1.4",
|
|
38
|
+
"@roj-ai/shared": "^0.1.4",
|
|
39
|
+
"@roj-ai/transport": "^0.1.4",
|
|
40
40
|
"hono": "^4.10.7"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|