@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,57 @@
1
+ /**
2
+ * Type-safe RPC definition system.
3
+ * No external dependencies — just TypeScript types + minimal runtime.
4
+ *
5
+ * Usage:
6
+ * const methods = defineMethods({
7
+ * 'instances.create': method<CreateInput, CreateOutput>(),
8
+ * 'sessions.list': method<ListInput, ListOutput>(),
9
+ * })
10
+ */
11
+
12
+ /** Marker type for a method definition (input → output). */
13
+ export interface MethodDef<I = unknown, O = unknown> {
14
+ readonly _input: I
15
+ readonly _output: O
16
+ }
17
+
18
+ /** Define a method type. Zero runtime cost — just a type marker. */
19
+ export function method<I, O>(): MethodDef<I, O> {
20
+ return {} as MethodDef<I, O>
21
+ }
22
+
23
+ /** Define a set of methods. Returns the definition object as-is (typed). */
24
+ export function defineMethods<T extends Record<string, MethodDef>>(methods: T): T {
25
+ return methods
26
+ }
27
+
28
+ /** Extract input type from a method name. */
29
+ export type MethodInput<
30
+ Methods extends Record<string, MethodDef>,
31
+ M extends keyof Methods,
32
+ > = Methods[M]['_input']
33
+
34
+ /** Extract output type from a method name. */
35
+ export type MethodOutput<
36
+ Methods extends Record<string, MethodDef>,
37
+ M extends keyof Methods,
38
+ > = Methods[M]['_output']
39
+
40
+ /** RPC request envelope. */
41
+ export interface RpcRequest<M extends string = string> {
42
+ method: M
43
+ input: unknown
44
+ }
45
+
46
+ /** RPC response envelope. */
47
+ export interface RpcResponse<T = unknown> {
48
+ ok: boolean
49
+ value?: T
50
+ error?: RpcError
51
+ }
52
+
53
+ export interface RpcError {
54
+ type: string
55
+ message: string
56
+ details?: unknown
57
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Type-safe RPC server handler.
3
+ * Maps method names to handler functions, dispatches incoming requests.
4
+ *
5
+ * Usage:
6
+ * const router = createRpcRouter(platformMethods, {
7
+ * 'instances.create': async (input, ctx) => { ... },
8
+ * 'instances.get': async (input, ctx) => { ... },
9
+ * })
10
+ * // In Hono route:
11
+ * app.post('/rpc', (c) => router.handle(c.req.json(), ctx))
12
+ */
13
+ import type { MethodDef, MethodInput, MethodOutput, RpcError, RpcRequest, RpcResponse } from './rpc-definition'
14
+
15
+ export type MethodHandler<
16
+ Methods extends Record<string, MethodDef>,
17
+ M extends keyof Methods,
18
+ Ctx,
19
+ > = (input: MethodInput<Methods, M>, ctx: Ctx) => Promise<MethodOutput<Methods, M>>
20
+
21
+ export type MethodHandlers<
22
+ Methods extends Record<string, MethodDef>,
23
+ Ctx,
24
+ > = {
25
+ [M in keyof Methods]: MethodHandler<Methods, M, Ctx>
26
+ }
27
+
28
+ export interface RpcRouter<Ctx> {
29
+ handle(request: RpcRequest, ctx: Ctx): Promise<RpcResponse>
30
+ }
31
+
32
+ export function createRpcRouter<
33
+ Methods extends Record<string, MethodDef>,
34
+ Ctx,
35
+ >(
36
+ _methods: Methods,
37
+ handlers: MethodHandlers<Methods, Ctx>,
38
+ ): RpcRouter<Ctx> {
39
+ return {
40
+ async handle(request: RpcRequest, ctx: Ctx): Promise<RpcResponse> {
41
+ const handler = handlers[request.method as keyof Methods]
42
+ if (!handler) {
43
+ return {
44
+ ok: false,
45
+ error: { type: 'method_not_found', message: `Unknown method: ${request.method}` },
46
+ }
47
+ }
48
+
49
+ try {
50
+ const result = await handler(request.input as any, ctx)
51
+ return { ok: true, value: result }
52
+ } catch (error) {
53
+ const rpcError: RpcError = error instanceof Error
54
+ ? { type: 'handler_error', message: error.message }
55
+ : { type: 'unknown_error', message: String(error) }
56
+ return { ok: false, error: rpcError }
57
+ }
58
+ },
59
+ }
60
+ }
@@ -0,0 +1 @@
1
+ export type SandboxState = 'stopped' | 'starting' | 'running' | 'pausing' | 'paused' | 'failed'
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Convert an instance UUID to its full 32-char hex representation (no hyphens, lowercase).
3
+ */
4
+ export function instanceIdToHex(instanceId: string): string {
5
+ return instanceId.replace(/-/g, '').toLowerCase()
6
+ }
7
+
8
+ export interface BuildPreviewUrlOptions {
9
+ instanceId: string
10
+ code: string
11
+ baseDomain: string
12
+ /** Optional instance token appended as ?token= for auth */
13
+ token?: string
14
+ /** Platform URL for path-based preview (used when subdomains aren't available) */
15
+ platformUrl?: string
16
+ }
17
+
18
+ /**
19
+ * Build a dev preview URL for a service running in a roj sandbox.
20
+ *
21
+ * Production: https://dev-{slug}-{code}.{baseDomain}/
22
+ * Local dev: {platformUrl}/api/v1/instances/{id}/preview/{code}/
23
+ */
24
+ export function buildPreviewUrl({ instanceId, code, baseDomain, token, platformUrl }: BuildPreviewUrlOptions): string {
25
+ // Cloudflare quick tunnels are already subdomains — can't nest further
26
+ if (baseDomain.includes('trycloudflare.com') && platformUrl) {
27
+ const base = `${platformUrl}/api/v1/instances/${instanceId}/preview/${code}`
28
+ return token ? `${base}/?token=${encodeURIComponent(token)}` : base
29
+ }
30
+
31
+ // Subdomain-based: dev-{hex}-{code}.{baseDomain}
32
+ // Works on localhost (*.localhost resolves to 127.0.0.1) and production
33
+ const hex = instanceIdToHex(instanceId)
34
+ const protocol = baseDomain.includes('localhost') ? 'http' : 'https'
35
+ const base = `${protocol}://dev-${hex}-${code}.${baseDomain}`
36
+ return token ? `${base}/?token=${encodeURIComponent(token)}` : base
37
+ }
38
+
39
+ export interface BuildWsUrlOptions {
40
+ platformUrl: string
41
+ instanceId: string
42
+ sessionId: string
43
+ token: string
44
+ }
45
+
46
+ /**
47
+ * Build a WebSocket URL for connecting to a roj instance.
48
+ */
49
+ export function buildWsUrl({ platformUrl, instanceId, sessionId, token }: BuildWsUrlOptions): string {
50
+ const url = new URL(`/api/v1/instances/${instanceId}/ws`, platformUrl)
51
+ url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'
52
+ url.searchParams.set('token', token)
53
+ url.searchParams.set('sessionId', sessionId)
54
+ return url.toString()
55
+ }
56
+
57
+ /**
58
+ * Build the RPC base URL for a roj instance.
59
+ */
60
+ export function buildApiBaseUrl(platformUrl: string, instanceId: string): string {
61
+ return `${platformUrl}/api/v1/instances/${instanceId}`
62
+ }