@roj-ai/standalone-server 0.1.10 → 0.1.12
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/files-upload-route.d.ts +36 -0
- package/dist/files-upload-route.d.ts.map +1 -0
- package/dist/files-upload-route.js +50 -0
- package/dist/files-upload-route.js.map +1 -0
- package/dist/instance.d.ts +6 -0
- package/dist/instance.d.ts.map +1 -1
- package/dist/instance.js +5 -0
- package/dist/instance.js.map +1 -1
- package/dist/local-registry.d.ts +109 -0
- package/dist/local-registry.d.ts.map +1 -0
- package/dist/local-registry.js +252 -0
- package/dist/local-registry.js.map +1 -0
- package/dist/main.js +7 -1
- package/dist/main.js.map +1 -1
- package/dist/platform-api.d.ts +8 -1
- package/dist/platform-api.d.ts.map +1 -1
- package/dist/platform-api.js +164 -13
- package/dist/platform-api.js.map +1 -1
- package/dist/server.d.ts +7 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +27 -1
- package/dist/server.js.map +1 -1
- package/dist/session-file-route.d.ts +24 -0
- package/dist/session-file-route.d.ts.map +1 -0
- package/dist/session-file-route.js +72 -0
- package/dist/session-file-route.js.map +1 -0
- package/dist/signed-token.d.ts +25 -0
- package/dist/signed-token.d.ts.map +1 -0
- package/dist/signed-token.js +50 -0
- package/dist/signed-token.js.map +1 -0
- package/dist/user-config-loader.d.ts.map +1 -1
- package/dist/user-config-loader.js +37 -1
- package/dist/user-config-loader.js.map +1 -1
- package/package.json +5 -5
- package/src/files-upload-route.ts +64 -0
- package/src/instance.ts +12 -1
- package/src/local-registry.ts +334 -0
- package/src/main.ts +7 -1
- package/src/platform-api.ts +234 -13
- package/src/server.ts +42 -2
- package/src/session-file-route.ts +99 -0
- package/src/signed-token.ts +68 -0
- package/src/user-config-loader.ts +41 -2
package/dist/platform-api.js
CHANGED
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
* - sessions.publish — publishing requires the CF platform
|
|
14
14
|
* - instances.archive — no-op; shutdown the server instead
|
|
15
15
|
*/
|
|
16
|
+
import { SessionId } from '@roj-ai/sdk';
|
|
16
17
|
import { Hono } from 'hono';
|
|
18
|
+
import { signFileToken } from './signed-token.js';
|
|
17
19
|
export function createPlatformApi(deps) {
|
|
18
20
|
const app = new Hono();
|
|
19
21
|
app.post('/rpc', async (c) => {
|
|
@@ -48,11 +50,132 @@ async function dispatch(deps, method, input) {
|
|
|
48
50
|
return { ok: false, error: { type: 'handler_error', message } };
|
|
49
51
|
}
|
|
50
52
|
}
|
|
53
|
+
// Shared by `instances.create.autoCreateSession` and `sessions.create`. Creates
|
|
54
|
+
// a session via the SDK session manager, injects any matching local resources,
|
|
55
|
+
// then (if set) pushes `initialPrompt` as a user-chat message — order matters:
|
|
56
|
+
// resources must land in the workspace before the agent's first inference so
|
|
57
|
+
// the agent sees a non-empty workspace. Mirrors roj-platform's project-init
|
|
58
|
+
// (inject-resources → activatePendingSession with initialPrompt).
|
|
59
|
+
async function startSession(deps, input) {
|
|
60
|
+
const created = await deps.sessionManager.callManagerMethod('sessions.create', {
|
|
61
|
+
presetId: input.presetId,
|
|
62
|
+
});
|
|
63
|
+
if (!created.ok)
|
|
64
|
+
throw new Error(created.error.message);
|
|
65
|
+
const { sessionId } = created.value;
|
|
66
|
+
const resources = resolveSessionResources(deps, input.presetId, input.resourceIds);
|
|
67
|
+
for (const resource of resources) {
|
|
68
|
+
await injectRegistryResource(deps, sessionId, resource);
|
|
69
|
+
}
|
|
70
|
+
if (input.initialPrompt) {
|
|
71
|
+
const sent = await deps.sessionManager.callPluginMethod(SessionId(sessionId), 'user-chat.sendMessage', {
|
|
72
|
+
content: input.initialPrompt,
|
|
73
|
+
});
|
|
74
|
+
if (!sent.ok) {
|
|
75
|
+
deps.logger.warn('Failed to deliver initialPrompt', {
|
|
76
|
+
sessionId,
|
|
77
|
+
error: sent.error,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { sessionId };
|
|
82
|
+
}
|
|
83
|
+
// Resolution mirrors roj-platform's project-init.ts:206:
|
|
84
|
+
// 1. explicit input.resourceIds — matched against the local registry by ID
|
|
85
|
+
// first, then by slug (since local-dev callers often pass slugs as ids).
|
|
86
|
+
// 2. fallback to preset.defaultResourceSlugs (looked up by slug).
|
|
87
|
+
function resolveSessionResources(deps, presetId, inputResourceIds) {
|
|
88
|
+
if (inputResourceIds && inputResourceIds.length > 0) {
|
|
89
|
+
const matched = [];
|
|
90
|
+
const unmatched = [];
|
|
91
|
+
for (const id of inputResourceIds) {
|
|
92
|
+
const resource = deps.registry.getResource({ resourceId: id }) ?? deps.registry.getResource({ resourceSlug: id });
|
|
93
|
+
const resolved = toResolvedResource(resource);
|
|
94
|
+
if (resolved)
|
|
95
|
+
matched.push(resolved);
|
|
96
|
+
else
|
|
97
|
+
unmatched.push(id);
|
|
98
|
+
}
|
|
99
|
+
if (unmatched.length > 0) {
|
|
100
|
+
deps.logger.warn('Some input resourceIds did not match the local registry; ignoring', {
|
|
101
|
+
unmatched,
|
|
102
|
+
availableSlugs: deps.registry.listResources().map(r => r.slug),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (matched.length > 0)
|
|
106
|
+
return matched;
|
|
107
|
+
}
|
|
108
|
+
const preset = deps.presets.find(p => p.id === presetId);
|
|
109
|
+
const slugs = preset?.defaultResourceSlugs ?? [];
|
|
110
|
+
const resolved = [];
|
|
111
|
+
const missing = [];
|
|
112
|
+
for (const slug of slugs) {
|
|
113
|
+
const r = toResolvedResource(deps.registry.getResource({ resourceSlug: slug }));
|
|
114
|
+
if (r)
|
|
115
|
+
resolved.push(r);
|
|
116
|
+
else
|
|
117
|
+
missing.push(slug);
|
|
118
|
+
}
|
|
119
|
+
if (missing.length > 0) {
|
|
120
|
+
deps.logger.warn("Preset declares defaultResourceSlugs not present in the local registry; those slots will be skipped", { presetId, missing, availableSlugs: deps.registry.listResources().map(r => r.slug) });
|
|
121
|
+
}
|
|
122
|
+
return resolved;
|
|
123
|
+
}
|
|
124
|
+
function toResolvedResource(resource) {
|
|
125
|
+
if (!resource || !resource.latestRevision)
|
|
126
|
+
return null;
|
|
127
|
+
const file = resource.latestRevision.file;
|
|
128
|
+
return {
|
|
129
|
+
resourceId: resource.id,
|
|
130
|
+
slug: resource.slug,
|
|
131
|
+
name: resource.name,
|
|
132
|
+
fileId: file.id,
|
|
133
|
+
filename: file.filename,
|
|
134
|
+
mimeType: file.mimeType,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
async function injectRegistryResource(deps, sessionId, resource) {
|
|
138
|
+
const file = await deps.registry.readFileById(resource.fileId);
|
|
139
|
+
if (!file) {
|
|
140
|
+
throw new Error(`Registry file not found for resource ${resource.slug} (fileId=${resource.fileId})`);
|
|
141
|
+
}
|
|
142
|
+
const result = await deps.sessionManager.callPluginMethod(SessionId(sessionId), 'resources.inject', {
|
|
143
|
+
sessionId,
|
|
144
|
+
filename: resource.filename,
|
|
145
|
+
mimeType: resource.mimeType,
|
|
146
|
+
size: file.buffer.length,
|
|
147
|
+
fileBuffer: file.buffer,
|
|
148
|
+
metadata: { slug: resource.slug, name: resource.name ?? resource.slug },
|
|
149
|
+
});
|
|
150
|
+
if (!result.ok) {
|
|
151
|
+
deps.logger.error('Registry resource injection failed', undefined, {
|
|
152
|
+
sessionId,
|
|
153
|
+
slug: resource.slug,
|
|
154
|
+
error: result.error,
|
|
155
|
+
});
|
|
156
|
+
throw new Error(`Failed to inject resource '${resource.slug}': ${result.error.message ?? result.error.type}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
51
159
|
const handlers = {
|
|
52
|
-
'instances.create': async (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
160
|
+
'instances.create': async (deps, input) => {
|
|
161
|
+
if (input.metadata !== undefined) {
|
|
162
|
+
deps.instance.metadata = input.metadata;
|
|
163
|
+
}
|
|
164
|
+
let sessionId;
|
|
165
|
+
if (input.autoCreateSession?.presetId) {
|
|
166
|
+
const result = await startSession(deps, {
|
|
167
|
+
presetId: input.autoCreateSession.presetId,
|
|
168
|
+
initialPrompt: input.autoCreateSession.initialPrompt,
|
|
169
|
+
resourceIds: input.autoCreateSession.resourceIds,
|
|
170
|
+
});
|
|
171
|
+
sessionId = result.sessionId;
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
instanceId: deps.instance.id,
|
|
175
|
+
status: 'ready',
|
|
176
|
+
...(sessionId ? { sessionId } : {}),
|
|
177
|
+
};
|
|
178
|
+
},
|
|
56
179
|
'instances.list': async ({ instance }) => ({
|
|
57
180
|
instances: [instanceSummary(instance)],
|
|
58
181
|
total: 1,
|
|
@@ -75,14 +198,7 @@ const handlers = {
|
|
|
75
198
|
};
|
|
76
199
|
},
|
|
77
200
|
'instances.archive': async () => ({ ok: true }),
|
|
78
|
-
'sessions.create': async (
|
|
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
|
-
},
|
|
201
|
+
'sessions.create': async (deps, input) => startSession(deps, input),
|
|
86
202
|
'sessions.list': async ({ sessionManager }) => {
|
|
87
203
|
const result = await sessionManager.callManagerMethod('sessions.list', {});
|
|
88
204
|
if (!result.ok)
|
|
@@ -90,6 +206,41 @@ const handlers = {
|
|
|
90
206
|
return result.value;
|
|
91
207
|
},
|
|
92
208
|
'tokens.create': async () => ({ token: '' }),
|
|
209
|
+
'resources.create': async (deps, input) => deps.registry.createResource(input),
|
|
210
|
+
'resources.addRevision': async (deps, input) => deps.registry.addRevision(input),
|
|
211
|
+
'resources.get': async (deps, input) => {
|
|
212
|
+
const resource = deps.registry.getResource(input);
|
|
213
|
+
if (!resource)
|
|
214
|
+
throw new Error('Resource not found');
|
|
215
|
+
return resource;
|
|
216
|
+
},
|
|
217
|
+
'resources.list': async (deps, _input) => ({
|
|
218
|
+
resources: deps.registry.listResources(),
|
|
219
|
+
}),
|
|
220
|
+
'resources.delete': async (deps, input) => deps.registry.deleteResource(input.resourceId),
|
|
221
|
+
'sessionFiles.createDownloadUrl': async (deps, input) => {
|
|
222
|
+
if (input.scope !== 'workspace' && input.scope !== 'session') {
|
|
223
|
+
throw new Error(`Invalid scope: ${input.scope}`);
|
|
224
|
+
}
|
|
225
|
+
if (!input.path || input.path.includes('..')) {
|
|
226
|
+
throw new Error('Path traversal not allowed');
|
|
227
|
+
}
|
|
228
|
+
const ttl = Math.min(Math.max(input.ttlSeconds ?? 300, 1), 3600);
|
|
229
|
+
const expiresAt = Date.now() + ttl * 1000;
|
|
230
|
+
const token = signFileToken(deps.tokenSecret, {
|
|
231
|
+
instanceId: input.instanceId,
|
|
232
|
+
sessionId: input.sessionId,
|
|
233
|
+
scope: input.scope,
|
|
234
|
+
path: input.path,
|
|
235
|
+
expiresAt,
|
|
236
|
+
});
|
|
237
|
+
const encodedPath = input.path
|
|
238
|
+
.split('/')
|
|
239
|
+
.map(seg => encodeURIComponent(seg))
|
|
240
|
+
.join('/');
|
|
241
|
+
const url = `${deps.publicBaseUrl}/api/v1/instances/${input.instanceId}/sessions/${input.sessionId}/files/${input.scope}/${encodedPath}?token=${encodeURIComponent(token)}`;
|
|
242
|
+
return { url, expiresAt: new Date(expiresAt).toISOString() };
|
|
243
|
+
},
|
|
93
244
|
};
|
|
94
245
|
function instanceSummary(instance) {
|
|
95
246
|
return {
|
|
@@ -100,7 +251,7 @@ function instanceSummary(instance) {
|
|
|
100
251
|
bundleSlug: 'standalone',
|
|
101
252
|
bundleRevisionId: '',
|
|
102
253
|
vcsType: 'none',
|
|
103
|
-
metadata:
|
|
254
|
+
metadata: instance.metadata,
|
|
104
255
|
createdAt: instance.createdAt,
|
|
105
256
|
};
|
|
106
257
|
}
|
package/dist/platform-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-api.js","sourceRoot":"","sources":["../src/platform-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"platform-api.js","sourceRoot":"","sources":["../src/platform-api.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* POST /api/v1/instances/{id}/exchange — noop (no auth locally)
|
|
12
12
|
* GET /health — health check
|
|
13
13
|
*/
|
|
14
|
-
import type { Config, LLMMiddleware, Logger, Preset, SessionManager } from '@roj-ai/sdk';
|
|
14
|
+
import type { Config, LLMMiddleware, LocalResource, Logger, Preset, SessionManager } from '@roj-ai/sdk';
|
|
15
15
|
import { type InstanceState } from './instance.js';
|
|
16
16
|
export interface StartStandaloneOptions {
|
|
17
17
|
presets: Preset[];
|
|
@@ -19,6 +19,12 @@ export interface StartStandaloneOptions {
|
|
|
19
19
|
instanceId?: string;
|
|
20
20
|
instanceName?: string;
|
|
21
21
|
llmMiddleware?: LLMMiddleware[];
|
|
22
|
+
/**
|
|
23
|
+
* Local resource registry — files (typically ZIPs) on disk addressable by slug.
|
|
24
|
+
* Injected into new sessions via `resources.inject` whenever the preset's
|
|
25
|
+
* `defaultResourceSlugs` (or input.resourceIds) match a slug here.
|
|
26
|
+
*/
|
|
27
|
+
localResources?: LocalResource[];
|
|
22
28
|
onShutdown?: () => Promise<void> | void;
|
|
23
29
|
onBeforeStart?: (ctx: {
|
|
24
30
|
config: Config;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAa,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAa,cAAc,EAAE,MAAM,aAAa,CAAA;AASlH,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAOlE,MAAM,WAAW,sBAAsB;IACtC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACxB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,aAAa,EAAE,CAAA;IAC/B;;;;OAIG;IACH,cAAc,CAAC,EAAE,aAAa,EAAE,CAAA;IAChC,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACvC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACjF;AAED,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,aAAa,CAAA;IACvB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAA;IACZ,0FAA0F;IAC1F,cAAc,EAAE,cAAc,CAAA;IAC9B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8ItG"}
|
package/dist/server.js
CHANGED
|
@@ -18,9 +18,13 @@ import { createBunPlatform } from '@roj-ai/sdk/bun-platform';
|
|
|
18
18
|
import { createBunWebSocketHandlers } from '@roj-ai/transport/bun';
|
|
19
19
|
import { Hono } from 'hono';
|
|
20
20
|
import { cors } from 'hono/cors';
|
|
21
|
+
import { createFilesUploadRoute } from './files-upload-route.js';
|
|
21
22
|
import { createInstance } from './instance.js';
|
|
23
|
+
import { LocalRegistry } from './local-registry.js';
|
|
22
24
|
import { createPlatformApi } from './platform-api.js';
|
|
23
25
|
import { proxyPreview } from './preview-proxy.js';
|
|
26
|
+
import { createSessionFileRoute } from './session-file-route.js';
|
|
27
|
+
import { generateTokenSecret } from './signed-token.js';
|
|
24
28
|
export async function startStandaloneServer(options) {
|
|
25
29
|
const envConfig = loadConfig();
|
|
26
30
|
const config = options.config ? { ...envConfig, ...options.config } : envConfig;
|
|
@@ -54,13 +58,32 @@ export async function startStandaloneServer(options) {
|
|
|
54
58
|
...services,
|
|
55
59
|
sessionRuntime: sessionManager,
|
|
56
60
|
});
|
|
57
|
-
const
|
|
61
|
+
const tokenSecret = generateTokenSecret();
|
|
62
|
+
const publicHost = config.host === '0.0.0.0' ? 'localhost' : config.host;
|
|
63
|
+
const publicBaseUrl = `http://${publicHost}:${config.port}`;
|
|
64
|
+
const registry = new LocalRegistry(config.dataPath, logger);
|
|
65
|
+
await registry.init();
|
|
66
|
+
if (options.localResources?.length) {
|
|
67
|
+
await registry.bootstrapLocalResources(options.localResources);
|
|
68
|
+
}
|
|
69
|
+
const platformApp = createPlatformApi({
|
|
70
|
+
instance,
|
|
71
|
+
sessionManager,
|
|
72
|
+
logger,
|
|
73
|
+
presets,
|
|
74
|
+
registry,
|
|
75
|
+
tokenSecret,
|
|
76
|
+
publicBaseUrl,
|
|
77
|
+
});
|
|
58
78
|
const outerApp = new Hono();
|
|
59
79
|
// Reflect the request origin and allow credentials — the SPA uses
|
|
60
80
|
// `credentials: 'include'` on its RPC fetches, which requires a specific
|
|
61
81
|
// origin header (not `*`) plus `Access-Control-Allow-Credentials: true`.
|
|
62
82
|
outerApp.use('*', cors({ origin: (origin) => origin ?? '*', credentials: true }));
|
|
63
83
|
outerApp.get('/health', (c) => c.json({ status: 'ok', timestamp: Date.now() }));
|
|
84
|
+
// Multipart file upload — must be registered BEFORE the /api/v1 RPC mount
|
|
85
|
+
// so it isn't treated as JSON RPC.
|
|
86
|
+
outerApp.post('/api/v1/files/upload', createFilesUploadRoute({ registry }));
|
|
64
87
|
outerApp.route('/api/v1', platformApp);
|
|
65
88
|
// Path-based preview proxy (must come BEFORE the catch-all instance route)
|
|
66
89
|
outerApp.all('/api/v1/instances/:id/preview/*', (c) => {
|
|
@@ -68,6 +91,9 @@ export async function startStandaloneServer(options) {
|
|
|
68
91
|
const prefix = `/api/v1/instances/${id}/preview/`;
|
|
69
92
|
return proxyPreview(c.req.raw, prefix, sessionManager, logger);
|
|
70
93
|
});
|
|
94
|
+
// Token-authenticated download URL minted by `sessionFiles.createDownloadUrl`.
|
|
95
|
+
// Must come BEFORE the agent catch-all so the token gate runs.
|
|
96
|
+
outerApp.get('/api/v1/instances/:id/sessions/:sid/files/:scope{workspace|session}/*', createSessionFileRoute({ tokenSecret, agentApp, logger }));
|
|
71
97
|
// Noop auth-exchange (platform has real cookies; standalone is open)
|
|
72
98
|
outerApp.post('/api/v1/instances/:id/exchange', (c) => c.json({ ok: true }));
|
|
73
99
|
// Instance-scoped agent routes — strip the instance prefix and delegate to createApp
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,SAAS,EAAE,wBAAwB,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC7F,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AACnF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAA;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,cAAc,EAAsB,MAAM,eAAe,CAAA;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-authenticated session file download route.
|
|
3
|
+
*
|
|
4
|
+
* GET /api/v1/instances/:id/sessions/:sid/files/:scope/{path}?token=...
|
|
5
|
+
*
|
|
6
|
+
* Validates the HMAC token minted by `sessionFiles.createDownloadUrl` against
|
|
7
|
+
* the URL's (instanceId, sessionId, scope, path), then proxies to the SDK's
|
|
8
|
+
* existing `/sessions/:sid/workspace/*` or `/sessions/:sid/files/*` route on
|
|
9
|
+
* the agent app — same shape as roj-platform's `session-file-route.ts`, just
|
|
10
|
+
* served in-process.
|
|
11
|
+
*/
|
|
12
|
+
import type { Logger } from '@roj-ai/sdk';
|
|
13
|
+
import type { Context } from 'hono';
|
|
14
|
+
interface AgentAppLike {
|
|
15
|
+
fetch: (req: Request) => Response | Promise<Response>;
|
|
16
|
+
}
|
|
17
|
+
interface Deps {
|
|
18
|
+
tokenSecret: string;
|
|
19
|
+
agentApp: AgentAppLike;
|
|
20
|
+
logger: Logger;
|
|
21
|
+
}
|
|
22
|
+
export declare function createSessionFileRoute(deps: Deps): (c: Context) => Promise<Response>;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=session-file-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-file-route.d.ts","sourceRoot":"","sources":["../src/session-file-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAMnC,UAAU,YAAY;IACrB,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CACrD;AAED,UAAU,IAAI;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,YAAY,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;CACd;AAOD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI,IAClC,GAAG,OAAO,uBA2DxB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-authenticated session file download route.
|
|
3
|
+
*
|
|
4
|
+
* GET /api/v1/instances/:id/sessions/:sid/files/:scope/{path}?token=...
|
|
5
|
+
*
|
|
6
|
+
* Validates the HMAC token minted by `sessionFiles.createDownloadUrl` against
|
|
7
|
+
* the URL's (instanceId, sessionId, scope, path), then proxies to the SDK's
|
|
8
|
+
* existing `/sessions/:sid/workspace/*` or `/sessions/:sid/files/*` route on
|
|
9
|
+
* the agent app — same shape as roj-platform's `session-file-route.ts`, just
|
|
10
|
+
* served in-process.
|
|
11
|
+
*/
|
|
12
|
+
import { verifyFileToken } from './signed-token.js';
|
|
13
|
+
const SCOPE_TO_SDK_PREFIX = {
|
|
14
|
+
workspace: 'workspace',
|
|
15
|
+
session: 'files',
|
|
16
|
+
};
|
|
17
|
+
export function createSessionFileRoute(deps) {
|
|
18
|
+
return async (c) => {
|
|
19
|
+
const instanceId = c.req.param('id');
|
|
20
|
+
const sessionId = c.req.param('sid');
|
|
21
|
+
const scope = c.req.param('scope');
|
|
22
|
+
const url = new URL(c.req.url);
|
|
23
|
+
const tokenStr = url.searchParams.get('token');
|
|
24
|
+
if (!tokenStr) {
|
|
25
|
+
return c.json({ error: { type: 'unauthorized', message: 'Missing token' } }, 401);
|
|
26
|
+
}
|
|
27
|
+
const verified = verifyFileToken(deps.tokenSecret, tokenStr);
|
|
28
|
+
if (!verified.ok) {
|
|
29
|
+
return c.json({ error: { type: 'unauthorized', message: `Invalid token (${verified.error})` } }, 401);
|
|
30
|
+
}
|
|
31
|
+
// Recover the file path the token was signed for. Hono `*` capture is
|
|
32
|
+
// available via the URL pathname; decode each segment back to its
|
|
33
|
+
// original form so the comparison against the signed payload is exact.
|
|
34
|
+
const pathPrefix = `/api/v1/instances/${instanceId}/sessions/${sessionId}/files/${scope}/`;
|
|
35
|
+
if (!url.pathname.startsWith(pathPrefix)) {
|
|
36
|
+
return c.json({ error: { type: 'invalid_request', message: 'Path mismatch' } }, 400);
|
|
37
|
+
}
|
|
38
|
+
const requestedPath = url.pathname
|
|
39
|
+
.slice(pathPrefix.length)
|
|
40
|
+
.split('/')
|
|
41
|
+
.map(seg => decodeURIComponent(seg))
|
|
42
|
+
.join('/');
|
|
43
|
+
const p = verified.payload;
|
|
44
|
+
if (p.instanceId !== instanceId ||
|
|
45
|
+
p.sessionId !== sessionId ||
|
|
46
|
+
p.scope !== scope ||
|
|
47
|
+
p.path !== requestedPath) {
|
|
48
|
+
return c.json({ error: { type: 'unauthorized', message: 'Token does not match requested file' } }, 401);
|
|
49
|
+
}
|
|
50
|
+
// Proxy to the SDK's underlying file route. Note the path/scope mapping:
|
|
51
|
+
// public `workspace` → SDK `/sessions/:sid/workspace/*`,
|
|
52
|
+
// public `session` → SDK `/sessions/:sid/files/*`.
|
|
53
|
+
const sdkPrefix = SCOPE_TO_SDK_PREFIX[scope];
|
|
54
|
+
const innerUrl = `${url.origin}/sessions/${sessionId}/${sdkPrefix}/${encodePathSegments(requestedPath)}`;
|
|
55
|
+
try {
|
|
56
|
+
return await deps.agentApp.fetch(new Request(innerUrl, { method: 'GET' }));
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
deps.logger.error('Session file proxy failed', err instanceof Error ? err : new Error(String(err)), {
|
|
60
|
+
instanceId,
|
|
61
|
+
sessionId,
|
|
62
|
+
scope,
|
|
63
|
+
path: requestedPath,
|
|
64
|
+
});
|
|
65
|
+
return c.json({ error: { type: 'proxy_error', message: 'File proxy failed' } }, 502);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function encodePathSegments(path) {
|
|
70
|
+
return path.split('/').map(seg => encodeURIComponent(seg)).join('/');
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=session-file-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-file-route.js","sourceRoot":"","sources":["../src/session-file-route.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny HMAC-signed download token used by `sessionFiles.createDownloadUrl`.
|
|
3
|
+
*
|
|
4
|
+
* The token binds (instanceId, sessionId, scope, path, expiresAt) so a leaked
|
|
5
|
+
* URL can only fetch the exact file it was minted for, and only briefly. Same
|
|
6
|
+
* shape as roj-platform's `session-file-token.ts`, just pruned for local use.
|
|
7
|
+
*/
|
|
8
|
+
export interface SignedFilePayload {
|
|
9
|
+
instanceId: string;
|
|
10
|
+
sessionId: string;
|
|
11
|
+
scope: 'workspace' | 'session';
|
|
12
|
+
path: string;
|
|
13
|
+
expiresAt: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function generateTokenSecret(): string;
|
|
16
|
+
export declare function signFileToken(secret: string, payload: SignedFilePayload): string;
|
|
17
|
+
export type VerifyResult = {
|
|
18
|
+
ok: true;
|
|
19
|
+
payload: SignedFilePayload;
|
|
20
|
+
} | {
|
|
21
|
+
ok: false;
|
|
22
|
+
error: 'malformed' | 'bad_signature' | 'expired';
|
|
23
|
+
};
|
|
24
|
+
export declare function verifyFileToken(secret: string, token: string): VerifyResult;
|
|
25
|
+
//# sourceMappingURL=signed-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signed-token.d.ts","sourceRoot":"","sources":["../src/signed-token.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,iBAAiB;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,WAAW,GAAG,SAAS,CAAA;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAIhF;AAED,MAAM,MAAM,YAAY,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,GACxC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,WAAW,GAAG,eAAe,GAAG,SAAS,CAAA;CAAE,CAAA;AAElE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAsB3E"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny HMAC-signed download token used by `sessionFiles.createDownloadUrl`.
|
|
3
|
+
*
|
|
4
|
+
* The token binds (instanceId, sessionId, scope, path, expiresAt) so a leaked
|
|
5
|
+
* URL can only fetch the exact file it was minted for, and only briefly. Same
|
|
6
|
+
* shape as roj-platform's `session-file-token.ts`, just pruned for local use.
|
|
7
|
+
*/
|
|
8
|
+
import { randomBytes, createHmac, timingSafeEqual } from 'node:crypto';
|
|
9
|
+
export function generateTokenSecret() {
|
|
10
|
+
return randomBytes(32).toString('hex');
|
|
11
|
+
}
|
|
12
|
+
export function signFileToken(secret, payload) {
|
|
13
|
+
const body = base64UrlEncode(Buffer.from(JSON.stringify(payload), 'utf8'));
|
|
14
|
+
const sig = base64UrlEncode(hmac(secret, body));
|
|
15
|
+
return `${body}.${sig}`;
|
|
16
|
+
}
|
|
17
|
+
export function verifyFileToken(secret, token) {
|
|
18
|
+
const dot = token.indexOf('.');
|
|
19
|
+
if (dot < 0)
|
|
20
|
+
return { ok: false, error: 'malformed' };
|
|
21
|
+
const body = token.slice(0, dot);
|
|
22
|
+
const sigEncoded = token.slice(dot + 1);
|
|
23
|
+
const expectedSig = hmac(secret, body);
|
|
24
|
+
const givenSig = base64UrlDecode(sigEncoded);
|
|
25
|
+
if (givenSig.length !== expectedSig.length || !timingSafeEqual(givenSig, expectedSig)) {
|
|
26
|
+
return { ok: false, error: 'bad_signature' };
|
|
27
|
+
}
|
|
28
|
+
let payload;
|
|
29
|
+
try {
|
|
30
|
+
payload = JSON.parse(base64UrlDecode(body).toString('utf8'));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return { ok: false, error: 'malformed' };
|
|
34
|
+
}
|
|
35
|
+
if (typeof payload.expiresAt !== 'number' || payload.expiresAt < Date.now()) {
|
|
36
|
+
return { ok: false, error: 'expired' };
|
|
37
|
+
}
|
|
38
|
+
return { ok: true, payload };
|
|
39
|
+
}
|
|
40
|
+
function hmac(secret, body) {
|
|
41
|
+
return createHmac('sha256', secret).update(body).digest();
|
|
42
|
+
}
|
|
43
|
+
function base64UrlEncode(buf) {
|
|
44
|
+
return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
45
|
+
}
|
|
46
|
+
function base64UrlDecode(s) {
|
|
47
|
+
const pad = '='.repeat((4 - (s.length % 4)) % 4);
|
|
48
|
+
return Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/') + pad, 'base64');
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=signed-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signed-token.js","sourceRoot":"","sources":["../src/signed-token.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-config-loader.d.ts","sourceRoot":"","sources":["../src/user-config-loader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"user-config-loader.d.ts","sourceRoot":"","sources":["../src/user-config-loader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAyB,SAAS,EAAE,MAAM,aAAa,CAAA;AAKnE,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CA8D3E"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { validatePreset } from '@roj-ai/sdk';
|
|
8
8
|
import { existsSync } from 'node:fs';
|
|
9
|
-
import { resolve } from 'node:path';
|
|
9
|
+
import { dirname, isAbsolute, resolve } from 'node:path';
|
|
10
10
|
export async function loadUserConfig(configPath) {
|
|
11
11
|
const absolutePath = resolve(process.cwd(), configPath);
|
|
12
12
|
if (!existsSync(absolutePath)) {
|
|
@@ -50,10 +50,46 @@ export async function loadUserConfig(configPath) {
|
|
|
50
50
|
if (duplicates.length > 0) {
|
|
51
51
|
throw new Error(`Duplicate preset IDs: ${[...new Set(duplicates)].join(', ')}`);
|
|
52
52
|
}
|
|
53
|
+
const configDir = dirname(absolutePath);
|
|
54
|
+
const localResources = parseLocalResources(typedConfig.localResources, configDir, absolutePath);
|
|
53
55
|
return {
|
|
54
56
|
presets,
|
|
55
57
|
sandboxed: typedConfig.sandboxed,
|
|
56
58
|
snapshotter: typedConfig.snapshotter,
|
|
59
|
+
localResources,
|
|
57
60
|
};
|
|
58
61
|
}
|
|
62
|
+
function parseLocalResources(raw, configDir, configPath) {
|
|
63
|
+
if (raw === undefined)
|
|
64
|
+
return undefined;
|
|
65
|
+
if (!Array.isArray(raw)) {
|
|
66
|
+
throw new Error(`'localResources' must be an array: ${configPath}`);
|
|
67
|
+
}
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
return raw.map((entry, i) => {
|
|
70
|
+
if (typeof entry !== 'object' || entry === null) {
|
|
71
|
+
throw new Error(`localResources[${i}] must be an object: ${configPath}`);
|
|
72
|
+
}
|
|
73
|
+
const r = entry;
|
|
74
|
+
if (typeof r.slug !== 'string' || !r.slug) {
|
|
75
|
+
throw new Error(`localResources[${i}] missing required 'slug': ${configPath}`);
|
|
76
|
+
}
|
|
77
|
+
if (typeof r.path !== 'string' || !r.path) {
|
|
78
|
+
throw new Error(`localResources[${i}] missing required 'path': ${configPath}`);
|
|
79
|
+
}
|
|
80
|
+
if (seen.has(r.slug)) {
|
|
81
|
+
throw new Error(`Duplicate localResources slug '${r.slug}': ${configPath}`);
|
|
82
|
+
}
|
|
83
|
+
seen.add(r.slug);
|
|
84
|
+
const resolvedPath = isAbsolute(r.path) ? r.path : resolve(configDir, r.path);
|
|
85
|
+
if (!existsSync(resolvedPath)) {
|
|
86
|
+
throw new Error(`localResources[${i}] file not found: ${resolvedPath} (config: ${configPath})`);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
slug: r.slug,
|
|
90
|
+
path: resolvedPath,
|
|
91
|
+
name: typeof r.name === 'string' ? r.name : undefined,
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
59
95
|
//# sourceMappingURL=user-config-loader.js.map
|