@roj-ai/client 0.0.2

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.
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Instance-scoped RPC method definitions.
3
+ *
4
+ * These methods are called from the SPA via POST /api/v1/instances/:id/rpc
5
+ * with instance token or cookie auth. The instance is already resolved by the route,
6
+ * so inputs do NOT contain instanceId.
7
+ *
8
+ * Shared between worker (server) and client SDK.
9
+ */
10
+ import { defineMethods, method } from './rpc-definition'
11
+ import type { PublishSessionOutput, GetAgentLogsOutput } from './methods'
12
+
13
+ // ============================================================================
14
+ // Sandbox methods
15
+ // ============================================================================
16
+
17
+ export type InstanceCreateSandboxInput = {}
18
+ export interface InstanceCreateSandboxOutput {
19
+ sandboxId: string
20
+ }
21
+
22
+ export interface InstancePauseSandboxInput {
23
+ sandboxId: string
24
+ }
25
+
26
+ export type InstanceResumeSandboxInput = {}
27
+
28
+ export interface InstanceTerminateSandboxInput {
29
+ sandboxId: string
30
+ }
31
+
32
+ export interface InstanceRestartAgentInput {
33
+ sandboxId: string
34
+ }
35
+
36
+ export interface InstanceGetAgentLogsInput {
37
+ sandboxId: string
38
+ lines?: number
39
+ }
40
+
41
+ export interface InstanceExecInSandboxInput {
42
+ command: string
43
+ }
44
+
45
+ export interface InstanceExecInSandboxOutput {
46
+ stdout: string
47
+ stderr: string
48
+ success: boolean
49
+ }
50
+
51
+ export interface InstanceValidateSandboxTokenInput {
52
+ sandboxId: string
53
+ token: string
54
+ }
55
+
56
+ export interface InstanceValidateSandboxTokenOutput {
57
+ value: boolean
58
+ }
59
+
60
+ // ============================================================================
61
+ // Session methods
62
+ // ============================================================================
63
+
64
+ export interface InstanceCreateSessionInput {
65
+ presetId: string
66
+ }
67
+
68
+ export interface InstanceCreateSessionOutput {
69
+ sessionId: string
70
+ }
71
+
72
+ export type InstanceListSessionsInput = {}
73
+ export interface InstanceListSessionsOutput {
74
+ sessions: Array<{
75
+ id: string
76
+ presetId: string | null
77
+ status: string
78
+ createdAt: number
79
+ }>
80
+ }
81
+
82
+ export interface InstancePublishSessionInput {
83
+ sessionId: string
84
+ }
85
+
86
+ // ============================================================================
87
+ // Service methods
88
+ // ============================================================================
89
+
90
+ export interface InstanceGetServiceUrlInput {
91
+ sessionId: string
92
+ serviceType: string
93
+ }
94
+
95
+ export interface InstanceGetServiceUrlOutput {
96
+ url: string | null
97
+ }
98
+
99
+ export interface InstanceGetServiceUrlsInput {
100
+ sessionId: string
101
+ }
102
+
103
+ export interface InstanceGetServiceUrlsOutput {
104
+ services: Array<{
105
+ serviceType: string
106
+ code: string
107
+ port: number
108
+ }>
109
+ }
110
+
111
+ export interface InstanceEnsureSandboxOutput {
112
+ sandboxId: string
113
+ state: string
114
+ }
115
+
116
+ // ============================================================================
117
+ // Shared output for void actions
118
+ // ============================================================================
119
+
120
+ export interface InstanceOkOutput {
121
+ ok: boolean
122
+ }
123
+
124
+ // ============================================================================
125
+ // Method registry
126
+ // ============================================================================
127
+
128
+ export const instanceMethods = defineMethods({
129
+ // Sandbox
130
+ createSandbox: method<InstanceCreateSandboxInput, InstanceCreateSandboxOutput>(),
131
+ pauseSandbox: method<InstancePauseSandboxInput, InstanceOkOutput>(),
132
+ resumeSandbox: method<InstanceResumeSandboxInput, InstanceOkOutput>(),
133
+ terminateSandbox: method<InstanceTerminateSandboxInput, InstanceOkOutput>(),
134
+ restartAgent: method<InstanceRestartAgentInput, InstanceOkOutput>(),
135
+ getAgentLogs: method<InstanceGetAgentLogsInput, GetAgentLogsOutput>(),
136
+ execInSandbox: method<InstanceExecInSandboxInput, InstanceExecInSandboxOutput>(),
137
+ validateSandboxToken: method<InstanceValidateSandboxTokenInput, InstanceValidateSandboxTokenOutput>(),
138
+
139
+ // Sessions
140
+ createSession: method<InstanceCreateSessionInput, InstanceCreateSessionOutput>(),
141
+ listSessions: method<InstanceListSessionsInput, InstanceListSessionsOutput>(),
142
+ publishSession: method<InstancePublishSessionInput, PublishSessionOutput>(),
143
+
144
+ // Services
145
+ getServiceUrl: method<InstanceGetServiceUrlInput, InstanceGetServiceUrlOutput>(),
146
+ getServiceUrls: method<InstanceGetServiceUrlsInput, InstanceGetServiceUrlsOutput>(),
147
+
148
+ // Idempotent sandbox access
149
+ ensureSandbox: method<{}, InstanceEnsureSandboxOutput>(),
150
+ })
151
+
152
+ export type InstanceMethods = typeof instanceMethods
153
+ export type InstanceMethodName = keyof InstanceMethods
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Platform RPC method definitions.
3
+ *
4
+ * Single source of truth for all platform API method types.
5
+ * Shared between worker (server) and client SDK.
6
+ */
7
+ import { defineMethods, method } from './rpc-definition'
8
+ import type { SandboxState } from './sandbox-state'
9
+
10
+ // ============================================================================
11
+ // Instance methods
12
+ // ============================================================================
13
+
14
+ export interface CreateInstanceInput {
15
+ templateSlug: string
16
+ bundleSlug?: string
17
+ bundleRevisionId?: string
18
+ name: string
19
+ vcsType?: 'github' | 'gitLocal' | 'none'
20
+ metadata?: Record<string, unknown>
21
+ autoCreateSession?: {
22
+ presetId: string
23
+ blocking?: boolean
24
+ initialPrompt?: string
25
+ resourceIds?: string[]
26
+ fileIds?: string[]
27
+ }
28
+ }
29
+
30
+ export interface CreateInstanceOutput {
31
+ instanceId: string
32
+ status: 'created' | 'initializing' | 'ready'
33
+ sessionId?: string
34
+ wsToken?: string
35
+ }
36
+
37
+ export interface GetInstanceInput {
38
+ instanceId: string
39
+ }
40
+
41
+ export interface GetInstanceOutput {
42
+ instanceId: string
43
+ name: string
44
+ status: string
45
+ templateSlug: string
46
+ bundleSlug: string
47
+ bundleRevisionId: string
48
+ vcsType: string
49
+ metadata: Record<string, unknown> | null
50
+ createdAt: string
51
+ }
52
+
53
+ export interface GetInstanceStatusInput {
54
+ instanceId: string
55
+ }
56
+
57
+ export interface GetInstanceStatusOutput {
58
+ instanceId: string
59
+ status: string
60
+ sandbox: {
61
+ state: SandboxState
62
+ e2bId?: string
63
+ lastActivityAt?: string
64
+ } | null
65
+ sessions: Array<{
66
+ id: string
67
+ presetId: string | null
68
+ status: string
69
+ createdAt: string
70
+ }>
71
+ lifecycleEvents: Array<{
72
+ event: string
73
+ detail?: string
74
+ createdAt: string
75
+ }>
76
+ serviceUrls: Array<{
77
+ code: string
78
+ sessionId: string | null
79
+ serviceType: string | null
80
+ port: number
81
+ }>
82
+ }
83
+
84
+ export interface ArchiveInstanceInput {
85
+ instanceId: string
86
+ }
87
+
88
+ export interface ArchiveInstanceOutput {
89
+ ok: boolean
90
+ }
91
+
92
+ export interface ListInstancesInput {
93
+ limit?: number
94
+ offset?: number
95
+ }
96
+
97
+ export interface ListInstancesOutput {
98
+ instances: GetInstanceOutput[]
99
+ total: number
100
+ }
101
+
102
+ // ============================================================================
103
+ // Session methods
104
+ // ============================================================================
105
+
106
+ export interface CreateSessionInput {
107
+ instanceId: string
108
+ presetId: string
109
+ blocking?: boolean
110
+ origin?: string
111
+ expiresIn?: number // token TTL in seconds (default 24h, max 7d)
112
+ }
113
+
114
+ export interface CreateSessionOutput {
115
+ sessionId: string
116
+ status: 'creating' | 'active'
117
+ wsToken?: string
118
+ }
119
+
120
+ export interface ListSessionsInput {
121
+ instanceId: string
122
+ }
123
+
124
+ export interface ListSessionsOutput {
125
+ sessions: Array<{
126
+ id: string
127
+ presetId: string | null
128
+ status: string
129
+ createdAt: string
130
+ }>
131
+ }
132
+
133
+ export interface PublishSessionInput {
134
+ instanceId: string
135
+ sessionId: string
136
+ }
137
+
138
+ export interface PublishSessionOutput {
139
+ ok: boolean
140
+ pushed: boolean
141
+ commitSha?: string
142
+ error?: string
143
+ }
144
+
145
+ // ============================================================================
146
+ // Token methods
147
+ // ============================================================================
148
+
149
+ export interface CreateInstanceTokenInput {
150
+ instanceId: string
151
+ origin?: string
152
+ expiresIn?: number // token TTL in seconds (default 24h, max 7d)
153
+ meta?: Record<string, unknown> // custom claims propagated to plugin method caller context
154
+ }
155
+
156
+ export interface CreateInstanceTokenOutput {
157
+ token: string
158
+ expiresAt: string
159
+ }
160
+
161
+ // ============================================================================
162
+ // Bundle methods
163
+ // ============================================================================
164
+
165
+ export interface ListBundlesInput {
166
+ limit?: number
167
+ offset?: number
168
+ }
169
+
170
+ export interface ListBundlesOutput {
171
+ bundles: Array<{
172
+ id: string
173
+ slug: string
174
+ name: string | null
175
+ description: string | null
176
+ latestRevision: { id: string; version: string | null; r2Key: string; createdAt: string } | null
177
+ createdAt: string
178
+ }>
179
+ }
180
+
181
+ export interface DeleteBundleInput {
182
+ bundleId?: string
183
+ bundleSlug?: string
184
+ }
185
+
186
+ export interface DeleteBundleOutput {
187
+ ok: boolean
188
+ }
189
+
190
+ // ============================================================================
191
+ // Resource methods
192
+ // ============================================================================
193
+
194
+ export interface CreateResourceInput {
195
+ slug: string
196
+ name?: string
197
+ description?: string
198
+ fileId: string
199
+ label?: string
200
+ }
201
+
202
+ export interface CreateResourceOutput {
203
+ resourceId: string
204
+ revisionId: string
205
+ }
206
+
207
+ export interface AddResourceRevisionInput {
208
+ resourceId?: string
209
+ resourceSlug?: string
210
+ fileId: string
211
+ label?: string
212
+ }
213
+
214
+ export interface AddResourceRevisionOutput {
215
+ revisionId: string
216
+ }
217
+
218
+ export interface GetResourceInput {
219
+ resourceId?: string
220
+ resourceSlug?: string
221
+ }
222
+
223
+ export interface GetResourceOutput {
224
+ id: string
225
+ slug: string
226
+ name: string | null
227
+ description: string | null
228
+ latestRevision: {
229
+ id: string
230
+ label: string | null
231
+ file: { id: string; filename: string; mimeType: string; size: number }
232
+ createdAt: string
233
+ } | null
234
+ createdAt: string
235
+ }
236
+
237
+ export interface ListResourcesInput {
238
+ limit?: number
239
+ offset?: number
240
+ }
241
+
242
+ export interface ListResourcesOutput {
243
+ resources: GetResourceOutput[]
244
+ }
245
+
246
+ export interface DeleteResourceInput {
247
+ resourceId: string
248
+ }
249
+
250
+ export interface DeleteResourceOutput {
251
+ ok: boolean
252
+ }
253
+
254
+ // ============================================================================
255
+ // Sandbox methods (admin/debug)
256
+ // ============================================================================
257
+
258
+ export interface PauseSandboxInput {
259
+ instanceId: string
260
+ sandboxId: string
261
+ }
262
+
263
+ export interface ResumeSandboxInput {
264
+ instanceId: string
265
+ sandboxId: string
266
+ }
267
+
268
+ export interface TerminateSandboxInput {
269
+ instanceId: string
270
+ sandboxId: string
271
+ }
272
+
273
+ export interface RestartAgentInput {
274
+ instanceId: string
275
+ sandboxId: string
276
+ }
277
+
278
+ export interface GetAgentLogsInput {
279
+ instanceId: string
280
+ sandboxId: string
281
+ lines?: number
282
+ }
283
+
284
+ export interface GetAgentLogsOutput {
285
+ logs: string
286
+ truncated: boolean
287
+ }
288
+
289
+ export interface SandboxActionOutput {
290
+ ok: boolean
291
+ }
292
+
293
+ // ============================================================================
294
+ // Services
295
+ // ============================================================================
296
+
297
+ export interface GetServiceUrlInput {
298
+ instanceId: string
299
+ sessionId: string
300
+ serviceType: string
301
+ }
302
+
303
+ export interface GetServiceUrlOutput {
304
+ url: string | null
305
+ }
306
+
307
+ // ============================================================================
308
+ // Method registry
309
+ // ============================================================================
310
+
311
+ export const platformMethods = defineMethods({
312
+ // Instances
313
+ 'instances.create': method<CreateInstanceInput, CreateInstanceOutput>(),
314
+ 'instances.get': method<GetInstanceInput, GetInstanceOutput>(),
315
+ 'instances.list': method<ListInstancesInput, ListInstancesOutput>(),
316
+ 'instances.status': method<GetInstanceStatusInput, GetInstanceStatusOutput>(),
317
+ 'instances.archive': method<ArchiveInstanceInput, ArchiveInstanceOutput>(),
318
+
319
+ // Sessions
320
+ 'sessions.create': method<CreateSessionInput, CreateSessionOutput>(),
321
+ 'sessions.list': method<ListSessionsInput, ListSessionsOutput>(),
322
+ 'sessions.publish': method<PublishSessionInput, PublishSessionOutput>(),
323
+
324
+ // Tokens
325
+ 'tokens.create': method<CreateInstanceTokenInput, CreateInstanceTokenOutput>(),
326
+
327
+ // Services
328
+ 'services.getUrl': method<GetServiceUrlInput, GetServiceUrlOutput>(),
329
+
330
+ // Bundles
331
+ 'bundles.list': method<ListBundlesInput, ListBundlesOutput>(),
332
+ 'bundles.delete': method<DeleteBundleInput, DeleteBundleOutput>(),
333
+
334
+ // Resources
335
+ 'resources.create': method<CreateResourceInput, CreateResourceOutput>(),
336
+ 'resources.addRevision': method<AddResourceRevisionInput, AddResourceRevisionOutput>(),
337
+ 'resources.get': method<GetResourceInput, GetResourceOutput>(),
338
+ 'resources.list': method<ListResourcesInput, ListResourcesOutput>(),
339
+ 'resources.delete': method<DeleteResourceInput, DeleteResourceOutput>(),
340
+ })
341
+
342
+ export type PlatformMethods = typeof platformMethods
343
+ export type PlatformMethodName = keyof PlatformMethods
@@ -0,0 +1,131 @@
1
+ import { createRpcClient } from './rpc-client'
2
+ import type { MethodInput, MethodOutput } from './rpc-definition'
3
+ import type { PlatformMethods, PlatformMethodName } from './methods'
4
+ import type {
5
+ CreateInstanceInput,
6
+ CreateInstanceOutput,
7
+ GetInstanceOutput,
8
+ GetInstanceStatusOutput,
9
+ ListInstancesInput,
10
+ ListInstancesOutput,
11
+ ArchiveInstanceOutput,
12
+ CreateSessionInput,
13
+ CreateSessionOutput,
14
+ ListSessionsOutput,
15
+ PublishSessionInput,
16
+ PublishSessionOutput,
17
+ CreateInstanceTokenInput,
18
+ CreateInstanceTokenOutput,
19
+ ListBundlesInput,
20
+ ListBundlesOutput,
21
+ DeleteBundleOutput,
22
+ CreateResourceInput,
23
+ CreateResourceOutput,
24
+ AddResourceRevisionInput,
25
+ AddResourceRevisionOutput,
26
+ GetResourceInput,
27
+ GetResourceOutput,
28
+ ListResourcesInput,
29
+ ListResourcesOutput,
30
+ DeleteResourceOutput,
31
+ } from './methods'
32
+ import { RojApiError } from './errors'
33
+
34
+ export interface RojClientOptions {
35
+ /** Platform URL (e.g. https://roj.example.com) */
36
+ url: string
37
+ /** Platform API key */
38
+ apiKey: string
39
+ }
40
+
41
+ export interface RojClient {
42
+ instances: {
43
+ create(input: CreateInstanceInput): Promise<CreateInstanceOutput>
44
+ get(instanceId: string): Promise<GetInstanceOutput>
45
+ getStatus(instanceId: string): Promise<GetInstanceStatusOutput>
46
+ list(input?: ListInstancesInput): Promise<ListInstancesOutput>
47
+ archive(instanceId: string): Promise<ArchiveInstanceOutput>
48
+ }
49
+ sessions: {
50
+ create(input: CreateSessionInput): Promise<CreateSessionOutput>
51
+ list(instanceId: string): Promise<ListSessionsOutput>
52
+ publish(input: PublishSessionInput): Promise<PublishSessionOutput>
53
+ }
54
+ tokens: {
55
+ create(input: CreateInstanceTokenInput): Promise<CreateInstanceTokenOutput>
56
+ }
57
+ bundles: {
58
+ list(input?: ListBundlesInput): Promise<ListBundlesOutput>
59
+ delete(input: { bundleId?: string; bundleSlug?: string }): Promise<DeleteBundleOutput>
60
+ }
61
+ files: {
62
+ upload(file: File | Blob, filename?: string): Promise<{ ok: true; fileId: string; filename: string; mimeType: string; size: number; r2Key: string }>
63
+ }
64
+ resources: {
65
+ create(input: CreateResourceInput): Promise<CreateResourceOutput>
66
+ addRevision(input: AddResourceRevisionInput): Promise<AddResourceRevisionOutput>
67
+ get(input: GetResourceInput): Promise<GetResourceOutput>
68
+ list(input?: ListResourcesInput): Promise<ListResourcesOutput>
69
+ delete(resourceId: string): Promise<DeleteResourceOutput>
70
+ }
71
+ }
72
+
73
+ export function createRojClient(options: RojClientOptions): RojClient {
74
+ const rpc = createRpcClient<PlatformMethods>(`${options.url}/api/v1`, {
75
+ headers: { Authorization: `Bearer ${options.apiKey}` },
76
+ })
77
+
78
+ async function call<M extends PlatformMethodName>(
79
+ method: M,
80
+ input: MethodInput<PlatformMethods, M>,
81
+ ): Promise<MethodOutput<PlatformMethods, M>> {
82
+ const result = await rpc.call(method, input)
83
+ if (!result.ok) throw new RojApiError(result.error)
84
+ return result.value
85
+ }
86
+
87
+ return {
88
+ instances: {
89
+ create: (input) => call('instances.create', input),
90
+ get: (instanceId) => call('instances.get', { instanceId }),
91
+ getStatus: (instanceId) => call('instances.status', { instanceId }),
92
+ list: (input) => call('instances.list', input ?? {}),
93
+ archive: (instanceId) => call('instances.archive', { instanceId }),
94
+ },
95
+ sessions: {
96
+ create: (input) => call('sessions.create', input),
97
+ list: (instanceId) => call('sessions.list', { instanceId }),
98
+ publish: (input) => call('sessions.publish', input),
99
+ },
100
+ tokens: {
101
+ create: (input) => call('tokens.create', input),
102
+ },
103
+ bundles: {
104
+ list: (input) => call('bundles.list', input ?? {}),
105
+ delete: (input) => call('bundles.delete', input),
106
+ },
107
+ files: {
108
+ upload: async (file, filename) => {
109
+ const formData = new FormData()
110
+ formData.append('file', file, filename)
111
+ const response = await fetch(`${options.url}/api/v1/files/upload`, {
112
+ method: 'POST',
113
+ headers: { Authorization: `Bearer ${options.apiKey}` },
114
+ body: formData,
115
+ })
116
+ if (!response.ok) {
117
+ const body = await response.json().catch(() => ({ message: response.statusText }))
118
+ throw new RojApiError({ type: 'http_error', message: body.error ?? body.message ?? response.statusText })
119
+ }
120
+ return response.json()
121
+ },
122
+ },
123
+ resources: {
124
+ create: (input) => call('resources.create', input),
125
+ addRevision: (input) => call('resources.addRevision', input),
126
+ get: (input) => call('resources.get', input),
127
+ list: (input) => call('resources.list', input ?? {}),
128
+ delete: (resourceId) => call('resources.delete', { resourceId }),
129
+ },
130
+ }
131
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Type-safe RPC client.
3
+ * Mirrors the method definitions for compile-time safety.
4
+ *
5
+ * Usage:
6
+ * const client = createRpcClient<PlatformMethods>('https://api.roj.cloud/rpc', {
7
+ * headers: { Authorization: `Bearer ${apiKey}` },
8
+ * })
9
+ * const result = await client.call('instances.create', { ... })
10
+ */
11
+ import type { MethodDef, MethodInput, MethodOutput, RpcError, RpcResponse } from './rpc-definition'
12
+
13
+ export interface RpcClientOptions {
14
+ headers?: Record<string, string>
15
+ credentials?: RequestCredentials
16
+ }
17
+
18
+ export interface RpcClient<Methods extends Record<string, MethodDef>> {
19
+ call<M extends string & keyof Methods>(
20
+ method: M,
21
+ input: MethodInput<Methods, M>,
22
+ ): Promise<RpcResult<MethodOutput<Methods, M>>>
23
+ }
24
+
25
+ export type RpcResult<T> =
26
+ | { ok: true; value: T }
27
+ | { ok: false; error: RpcError }
28
+
29
+ export function createRpcClient<Methods extends Record<string, MethodDef>>(
30
+ baseUrl: string,
31
+ options?: RpcClientOptions,
32
+ ): RpcClient<Methods> {
33
+ return {
34
+ async call<M extends string & keyof Methods>(
35
+ method: M,
36
+ input: MethodInput<Methods, M>,
37
+ ): Promise<RpcResult<MethodOutput<Methods, M>>> {
38
+ const response = await fetch(`${baseUrl}/rpc`, {
39
+ method: 'POST',
40
+ headers: {
41
+ 'Content-Type': 'application/json',
42
+ ...options?.headers,
43
+ },
44
+ credentials: options?.credentials,
45
+ body: JSON.stringify({ method, input }),
46
+ })
47
+
48
+ const data = (await response.json()) as RpcResponse
49
+
50
+ if (!response.ok) {
51
+ return {
52
+ ok: false,
53
+ error: data.error ?? { type: 'transport_error', message: `HTTP ${response.status}` },
54
+ }
55
+ }
56
+
57
+ if (data.ok) {
58
+ return { ok: true, value: data.value as MethodOutput<Methods, M> }
59
+ }
60
+
61
+ return {
62
+ ok: false,
63
+ error: data.error ?? { type: 'unknown_error', message: 'Unknown error' },
64
+ }
65
+ },
66
+ }
67
+ }