@theaiinc/yggdrasil-ratatoskr 0.2.3 → 0.3.0

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.
Files changed (52) hide show
  1. package/README.md +39 -0
  2. package/dist/src/handlers/android-handler.d.ts +11 -0
  3. package/dist/src/handlers/android-handler.d.ts.map +1 -0
  4. package/dist/src/handlers/android-handler.js +22 -0
  5. package/dist/src/handlers/android-handler.js.map +1 -0
  6. package/dist/src/handlers/automation-loop.d.ts +33 -0
  7. package/dist/src/handlers/automation-loop.d.ts.map +1 -0
  8. package/dist/src/handlers/automation-loop.js +189 -0
  9. package/dist/src/handlers/automation-loop.js.map +1 -0
  10. package/dist/src/handlers/cu-handler.d.ts +8 -0
  11. package/dist/src/handlers/cu-handler.d.ts.map +1 -0
  12. package/dist/src/handlers/cu-handler.js +9 -0
  13. package/dist/src/handlers/cu-handler.js.map +1 -0
  14. package/dist/src/handlers/realm-client.d.ts +70 -0
  15. package/dist/src/handlers/realm-client.d.ts.map +1 -0
  16. package/dist/src/handlers/realm-client.js +199 -0
  17. package/dist/src/handlers/realm-client.js.map +1 -0
  18. package/dist/src/index.d.ts +2 -2
  19. package/dist/src/index.d.ts.map +1 -1
  20. package/dist/src/index.js +1 -1
  21. package/dist/src/index.js.map +1 -1
  22. package/dist/src/presets/builtins.d.ts +19 -0
  23. package/dist/src/presets/builtins.d.ts.map +1 -1
  24. package/dist/src/presets/builtins.js +117 -1
  25. package/dist/src/presets/builtins.js.map +1 -1
  26. package/dist/src/presets/index.d.ts +1 -1
  27. package/dist/src/presets/index.d.ts.map +1 -1
  28. package/dist/src/presets/index.js +1 -1
  29. package/dist/src/presets/index.js.map +1 -1
  30. package/dist/src/ratatoskr.d.ts.map +1 -1
  31. package/dist/src/ratatoskr.js +2 -1
  32. package/dist/src/ratatoskr.js.map +1 -1
  33. package/dist/src/services/registrar.d.ts +3 -2
  34. package/dist/src/services/registrar.d.ts.map +1 -1
  35. package/dist/src/services/registrar.js +4 -1
  36. package/dist/src/services/registrar.js.map +1 -1
  37. package/dist/src/services/session-authorizer.d.ts +34 -0
  38. package/dist/src/services/session-authorizer.d.ts.map +1 -0
  39. package/dist/src/services/session-authorizer.js +28 -0
  40. package/dist/src/services/session-authorizer.js.map +1 -0
  41. package/dist/src/services/session-manager.d.ts +75 -0
  42. package/dist/src/services/session-manager.d.ts.map +1 -0
  43. package/dist/src/services/session-manager.js +251 -0
  44. package/dist/src/services/session-manager.js.map +1 -0
  45. package/dist/src/transports/http-transport.d.ts +14 -1
  46. package/dist/src/transports/http-transport.d.ts.map +1 -1
  47. package/dist/src/transports/http-transport.js +20 -0
  48. package/dist/src/transports/http-transport.js.map +1 -1
  49. package/dist/src/types/index.d.ts +156 -0
  50. package/dist/src/types/index.d.ts.map +1 -1
  51. package/dist/src/types/index.js.map +1 -1
  52. package/package.json +14 -2
package/README.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @theaiinc/yggdrasil-ratatoskr
2
2
 
3
+ <p align="center">
4
+ <a href="https://github.com/theaiinc/yggdrasil"><img alt="GitHub Repo" src="https://img.shields.io/badge/github-theaiinc%2Fyggdrasil-181717?style=flat-square&logo=github"/></a>
5
+ <a href="https://www.npmjs.com/package/@theaiinc/yggdrasil-ratatoskr"><img alt="npm" src="https://img.shields.io/npm/v/@theaiinc/yggdrasil-ratatoskr?style=flat-square&logo=npm"/></a>
6
+ <a href="https://github.com/theaiinc/yggdrasil/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/theaiinc/yggdrasil?style=flat-square"/></a>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <img src="./ratatoskr.svg" alt="Ratatoskr" width="300" />
11
+ </p>
12
+
3
13
  Lightweight runner daemon for Yggdrasil — registers, heartbeats, and executes tasks with configurable capability presets.
4
14
 
5
15
  Ratatoskr runs alongside any agent (Docker container, laptop, server) and continuously informs [Yggdrasil](https://www.npmjs.com/package/@theaiinc/yggdrasil) about runner availability, capabilities, health, and task execution. It can execute tasks itself using its built-in LLM, shell, web, code, and file handlers.
@@ -43,6 +53,8 @@ Built-in presets (available by name):
43
53
  | `python` | Python script execution | `python` | — |
44
54
  | `node_runtime` | Node.js script execution | `node` | — |
45
55
  | `github_cli` | GitHub CLI operations | `github` | `shell` |
56
+ | `computer_use` | Desktop automation via Realm ubuntu engine | `computer_use` | `llm` |
57
+ | `android` | Android emulator automation via Realm VM engine | `android` | `llm` |
46
58
 
47
59
  ```typescript
48
60
  import { Ratatoskr } from '@theaiinc/yggdrasil-ratatoskr';
@@ -202,6 +214,8 @@ All built-in handlers return structured metadata:
202
214
  | `python` | `{ stdout, stderr, code }` |
203
215
  | `node` | `{ stdout, stderr, code }` |
204
216
  | `github` | `{ stdout, stderr, code }` |
217
+ | `computer_use` | `{ goal, realmId, engine, iterations, actions }` |
218
+ | `android` | `{ goal, realmId, engine, iterations, actions }` |
205
219
 
206
220
  > **Cost tracking**: Cost is computed server-side by the orchestration layer (e.g. Oasis api-gateway's `PricingService`). Ratatoskr reports the actual model used and token counts — the server applies per-model pricing.
207
221
 
@@ -250,6 +264,31 @@ The agent has access to: `shell`, `read_file`, `write_file`, `web_search`, `web_
250
264
  | `LLM_BASE_URL` | `http://host.docker.internal:1234/v1` | OpenAI-compatible base URL |
251
265
  | `LLM_API_KEY` | `''` | API key (optional for local LM Studio) |
252
266
  | `AGENT_MAX_TOOL_ITERATIONS` | `25` | Max tool call cycles per agent task |
267
+ | `REALM_URL` | `http://localhost:8542` | Realm API server URL (for `computer_use` / `android`) |
268
+ | `REALM_ID` | `''` | Existing realm to reuse (optional) |
269
+ | `REALM_AVD` | `Pixel_9_Pro` | Android Virtual Device name (for `android`) |
270
+ | `CU_MAX_ITERATIONS` | `50` | Max screenshot-action cycles per automation task |
271
+
272
+ ## Realm Integration
273
+
274
+ `computer_use` and `android` capabilities talk to [@theaiinc/realm-api](https://github.com/theaiinc/theaiincrealm) over the universal `/api/v1/realms/:id/*` interface:
275
+
276
+ | Preset | Realm Engine | Use Case |
277
+ |--------|-------------|----------|
278
+ | `computer_use` | `ubuntu` | Desktop automation (XFCE + xdotool) |
279
+ | `android` | `vm` | Android emulator automation (ADB + Realm Agent APK) |
280
+
281
+ ```typescript
282
+ const ratatoskr = new Ratatoskr({
283
+ yggdrasilUrl: 'http://localhost:3000',
284
+ capabilities: ['android', 'llm'],
285
+ });
286
+
287
+ // Task metadata for an Android automation goal:
288
+ // { type: 'android', metadata: { goal: 'Open Settings and enable Wi-Fi' } }
289
+ ```
290
+
291
+ Start the Realm API server first (`pnpm realm:api` in theaiincrealm repo). For Android, use the `wip/android-vm` branch which registers `VMEngine`.
253
292
 
254
293
  ## Services
255
294
 
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Android handler — session-based mobile automation via Realm VM engine (Android emulator + ADB).
3
+ *
4
+ * Creates a "phone-use" session: observe → decide → act → repeat.
5
+ * Streaming is an internal Realm implementation detail.
6
+ *
7
+ * Requires @theaiinc/realm-api with VMEngine registered and an Android SDK/AVD
8
+ * configured on the host running the Realm server.
9
+ */
10
+ export declare const androidHandler: import("../index.js").TaskHandler;
11
+ //# sourceMappingURL=android-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"android-handler.d.ts","sourceRoot":"","sources":["../../../src/handlers/android-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,eAAO,MAAM,cAAc,mCAE1B,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Android handler — session-based mobile automation via Realm VM engine (Android emulator + ADB).
3
+ *
4
+ * Creates a "phone-use" session: observe → decide → act → repeat.
5
+ * Streaming is an internal Realm implementation detail.
6
+ *
7
+ * Requires @theaiinc/realm-api with VMEngine registered and an Android SDK/AVD
8
+ * configured on the host running the Realm server.
9
+ */
10
+ import { createAutomationHandler, buildAutomationConfig } from './automation-loop.js';
11
+ function androidEnvironment() {
12
+ const env = {};
13
+ if (process.env.REALM_AVD)
14
+ env.REALM_AVD = process.env.REALM_AVD;
15
+ if (process.env.ANDROID_HOME)
16
+ env.ANDROID_HOME = process.env.ANDROID_HOME;
17
+ if (process.env.REALM_HTTP_PROXY)
18
+ env.REALM_HTTP_PROXY = process.env.REALM_HTTP_PROXY;
19
+ return env;
20
+ }
21
+ export const androidHandler = createAutomationHandler(buildAutomationConfig('vm', androidEnvironment()));
22
+ //# sourceMappingURL=android-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"android-handler.js","sourceRoot":"","sources":["../../../src/handlers/android-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAEtF,SAAS,kBAAkB;IACzB,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS;QAAE,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;IACjE,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;QAAE,GAAG,CAAC,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAC1E,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAAE,GAAG,CAAC,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACtF,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,uBAAuB,CACnD,qBAAqB,CAAC,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAClD,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Shared automation loop for session-based Computer Use (desktop + Android).
3
+ *
4
+ * Flow: create session → observe → decide action → send input → observe → repeat.
5
+ *
6
+ * Cognition interacts through sessions:
7
+ * startSession({ type: "computer-use" })
8
+ * observe(sessionId) → SessionObservation
9
+ * input(sessionId, { type: "mouse", params: { x, y } })
10
+ *
11
+ * NOT through streaming:
12
+ * requestScreenStream() ✗
13
+ * startStream() ✗
14
+ * stopStream() ✗
15
+ */
16
+ import type { TaskHandler } from '../types/index.js';
17
+ import { type RealmEngineType } from './realm-client.js';
18
+ export interface AutomationConfig {
19
+ engine: RealmEngineType;
20
+ realmUrl: string;
21
+ realmApiKey?: string | undefined;
22
+ realmId?: string | undefined;
23
+ realmName?: string | undefined;
24
+ environment?: Record<string, string> | undefined;
25
+ maxIterations: number;
26
+ }
27
+ export interface AutomationAction {
28
+ action: string;
29
+ params: Record<string, unknown>;
30
+ }
31
+ export declare function createAutomationHandler(config: AutomationConfig): TaskHandler;
32
+ export declare function buildAutomationConfig(engine: RealmEngineType, extraEnv?: Record<string, string>): AutomationConfig;
33
+ //# sourceMappingURL=automation-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automation-loop.d.ts","sourceRoot":"","sources":["../../../src/handlers/automation-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AAEjE,OAAO,EAGL,KAAK,eAAe,EAGrB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACjD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAiED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,gBAAgB,GAAG,WAAW,CA6G7E;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,eAAe,EACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,gBAAgB,CAWlB"}
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Shared automation loop for session-based Computer Use (desktop + Android).
3
+ *
4
+ * Flow: create session → observe → decide action → send input → observe → repeat.
5
+ *
6
+ * Cognition interacts through sessions:
7
+ * startSession({ type: "computer-use" })
8
+ * observe(sessionId) → SessionObservation
9
+ * input(sessionId, { type: "mouse", params: { x, y } })
10
+ *
11
+ * NOT through streaming:
12
+ * requestScreenStream() ✗
13
+ * startStream() ✗
14
+ * stopStream() ✗
15
+ */
16
+ import { RealmClient, loadRealmUrl, loadRealmApiKey, } from './realm-client.js';
17
+ async function decideAction(goal, observation, previousActions) {
18
+ // Placeholder — real implementation will use a vision-capable LLM.
19
+ void goal;
20
+ void observation;
21
+ void previousActions;
22
+ return { action: 'wait', params: { ms: 500 } };
23
+ }
24
+ function isGoalComplete(result) {
25
+ const data = result.data;
26
+ if (data && typeof data === 'object' && 'goal_complete' in data) {
27
+ return Boolean(data.goal_complete);
28
+ }
29
+ return false;
30
+ }
31
+ async function executeSessionAction(client, sessionId, action, params) {
32
+ switch (action) {
33
+ case 'click':
34
+ case 'tap': {
35
+ const input = {
36
+ type: action === 'click' ? 'mouse' : 'touch',
37
+ params: { x: params.x, y: params.y },
38
+ };
39
+ const result = await client.input(sessionId, input);
40
+ return {
41
+ success: result.success,
42
+ ...(result.error !== undefined ? { error: result.error } : {}),
43
+ };
44
+ }
45
+ case 'type': {
46
+ const input = {
47
+ type: 'keyboard',
48
+ params: { text: params.text },
49
+ };
50
+ const result = await client.input(sessionId, input);
51
+ return {
52
+ success: result.success,
53
+ ...(result.error !== undefined ? { error: result.error } : {}),
54
+ };
55
+ }
56
+ case 'navigate':
57
+ // Navigate is a Realm input action.
58
+ return { success: false, error: 'Navigate not yet supported in session mode' };
59
+ case 'exec':
60
+ return { success: false, error: 'Exec not yet supported in session mode' };
61
+ case 'wait':
62
+ await new Promise((r) => setTimeout(r, params.ms ?? 500));
63
+ return { success: true };
64
+ default:
65
+ return { success: false, error: `Unknown action: ${action}` };
66
+ }
67
+ }
68
+ export function createAutomationHandler(config) {
69
+ return async (task) => {
70
+ const goal = task.metadata?.goal || '';
71
+ if (!goal) {
72
+ return { status: 'failed', metadata: { error: 'No goal specified for automation task' } };
73
+ }
74
+ const realmIdOverride = task.metadata?.realmId || config.realmId;
75
+ const clientConfig = {
76
+ baseUrl: config.realmUrl,
77
+ engine: task.metadata?.engine || config.engine,
78
+ };
79
+ if (config.realmApiKey)
80
+ clientConfig.apiKey = config.realmApiKey;
81
+ if (realmIdOverride)
82
+ clientConfig.realmId = realmIdOverride;
83
+ if (config.realmName)
84
+ clientConfig.realmName = config.realmName;
85
+ if (config.environment)
86
+ clientConfig.environment = config.environment;
87
+ const client = new RealmClient(clientConfig);
88
+ const previousActions = [];
89
+ let iteration = 0;
90
+ let sessionId;
91
+ try {
92
+ // Create a session for this automation task.
93
+ // Cognition requests: startSession({ type: "computer-use" })
94
+ const sessionType = config.engine === 'vm' ? 'phone-use' : 'computer-use';
95
+ const session = await client.createSession({
96
+ type: sessionType,
97
+ ...(realmIdOverride !== undefined ? { realmId: realmIdOverride } : {}),
98
+ ...({ metadata: { goal, engine: config.engine } }),
99
+ });
100
+ sessionId = session.sessionId;
101
+ while (iteration < config.maxIterations) {
102
+ iteration++;
103
+ // Observe the current state via the session.
104
+ // Realm decides how observation is delivered (screenshot, a11y tree, etc.).
105
+ const observation = await client.observe(sessionId);
106
+ const decision = await decideAction(goal, observation, previousActions);
107
+ const result = await executeSessionAction(client, sessionId, decision.action, decision.params);
108
+ if (!result.success) {
109
+ return {
110
+ status: 'failed',
111
+ metadata: {
112
+ error: result.error || `Action failed: ${decision.action}`,
113
+ goal,
114
+ realmId: session.descriptor?.realmId,
115
+ sessionId,
116
+ engine: config.engine,
117
+ iterations: iteration,
118
+ lastAction: decision.action,
119
+ },
120
+ };
121
+ }
122
+ previousActions.push(`${decision.action}(${JSON.stringify(decision.params)})`);
123
+ if (isGoalComplete(result)) {
124
+ return {
125
+ status: 'completed',
126
+ metadata: {
127
+ goal,
128
+ realmId: session.descriptor?.realmId,
129
+ sessionId,
130
+ engine: config.engine,
131
+ iterations: iteration,
132
+ actions: previousActions,
133
+ },
134
+ };
135
+ }
136
+ if (decision.action === 'wait') {
137
+ const ms = decision.params.ms ?? 500;
138
+ await new Promise((r) => setTimeout(r, ms));
139
+ }
140
+ }
141
+ return {
142
+ status: 'completed',
143
+ metadata: {
144
+ goal,
145
+ realmId: (await client.getSession(sessionId))?.realmId,
146
+ sessionId,
147
+ engine: config.engine,
148
+ iterations: iteration,
149
+ actions: previousActions,
150
+ note: 'Max iterations reached',
151
+ },
152
+ };
153
+ }
154
+ catch (err) {
155
+ const message = err instanceof Error ? err.message : String(err);
156
+ return {
157
+ status: 'failed',
158
+ metadata: {
159
+ error: `Automation loop error at iteration ${iteration}: ${message}`,
160
+ goal,
161
+ engine: config.engine,
162
+ iterations: iteration,
163
+ },
164
+ };
165
+ }
166
+ finally {
167
+ if (sessionId) {
168
+ const destroy = task.metadata?.destroyRealm === true || process.env.REALM_DESTROY_ON_COMPLETE === 'true';
169
+ await client.terminateSession(sessionId, destroy);
170
+ }
171
+ }
172
+ };
173
+ }
174
+ export function buildAutomationConfig(engine, extraEnv) {
175
+ const config = {
176
+ engine,
177
+ realmUrl: loadRealmUrl(),
178
+ maxIterations: parseInt(process.env.CU_MAX_ITERATIONS || '50', 10),
179
+ };
180
+ const apiKey = loadRealmApiKey();
181
+ if (apiKey)
182
+ config.realmApiKey = apiKey;
183
+ if (process.env.REALM_ID)
184
+ config.realmId = process.env.REALM_ID;
185
+ if (extraEnv && Object.keys(extraEnv).length > 0)
186
+ config.environment = extraEnv;
187
+ return config;
188
+ }
189
+ //# sourceMappingURL=automation-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automation-loop.js","sourceRoot":"","sources":["../../../src/handlers/automation-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Computer Use handler — session-based desktop automation via Realm ubuntu engine.
3
+ *
4
+ * Creates a "computer-use" session: observe → decide → act → repeat.
5
+ * Streaming is an internal Realm implementation detail.
6
+ */
7
+ export declare const computerUseHandler: import("../index.js").TaskHandler;
8
+ //# sourceMappingURL=cu-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cu-handler.d.ts","sourceRoot":"","sources":["../../../src/handlers/cu-handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,eAAO,MAAM,kBAAkB,mCAE9B,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Computer Use handler — session-based desktop automation via Realm ubuntu engine.
3
+ *
4
+ * Creates a "computer-use" session: observe → decide → act → repeat.
5
+ * Streaming is an internal Realm implementation detail.
6
+ */
7
+ import { createAutomationHandler, buildAutomationConfig } from './automation-loop.js';
8
+ export const computerUseHandler = createAutomationHandler(buildAutomationConfig('ubuntu'));
9
+ //# sourceMappingURL=cu-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cu-handler.js","sourceRoot":"","sources":["../../../src/handlers/cu-handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAEtF,MAAM,CAAC,MAAM,kBAAkB,GAAG,uBAAuB,CACvD,qBAAqB,CAAC,QAAQ,CAAC,CAChC,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Streamlined Realm API client — sessions replace direct capture/action calls.
3
+ *
4
+ * This client talks to @theaiinc/realm-api HTTP server but presents a
5
+ * session-based interface. Consumers interact through sessions:
6
+ *
7
+ * const client = new RealmClient({ baseUrl, engine: 'ubuntu' });
8
+ * const session = await client.createSession({ type: 'computer-use' });
9
+ * const observation = await client.observe(session.sessionId);
10
+ * const result = await client.input(session.sessionId, { type: 'mouse', params: { x: 100, y: 200 } });
11
+ *
12
+ * Consumers must NOT depend on transport details (WebRTC, screenshots, etc.).
13
+ */
14
+ import type { CreateSessionRequest, CreateSessionResponse, SessionDescriptor, SessionObservation, SessionInput, SessionInputResult } from '../types/index.js';
15
+ export type RealmEngineType = 'ubuntu' | 'vm' | 'container' | 'browser';
16
+ export interface RealmActionResult {
17
+ success: boolean;
18
+ data?: unknown;
19
+ error?: string;
20
+ durationMs?: number;
21
+ timestamp?: string;
22
+ }
23
+ export interface RealmClientConfig {
24
+ baseUrl: string;
25
+ apiKey?: string | undefined;
26
+ realmId?: string | undefined;
27
+ engine: RealmEngineType;
28
+ realmName?: string | undefined;
29
+ environment?: Record<string, string> | undefined;
30
+ }
31
+ export declare class RealmClient {
32
+ private readonly http;
33
+ private readonly config;
34
+ private readonly realmUrl;
35
+ private readonly sessions;
36
+ constructor(config: RealmClientConfig);
37
+ /**
38
+ * Create an interaction session.
39
+ * This replaces the old ensureRealm() + capture() pattern.
40
+ */
41
+ createSession(request: CreateSessionRequest): Promise<CreateSessionResponse>;
42
+ /**
43
+ * Get the current session descriptor.
44
+ */
45
+ getSession(sessionId: string): Promise<SessionDescriptor>;
46
+ /**
47
+ * Observe the current state of a session.
48
+ * Realm decides how observation is delivered.
49
+ */
50
+ observe(sessionId: string): Promise<SessionObservation>;
51
+ /**
52
+ * Send an input action to a session.
53
+ */
54
+ input(sessionId: string, input: SessionInput): Promise<SessionInputResult>;
55
+ /**
56
+ * Terminate a session.
57
+ */
58
+ terminateSession(sessionId: string, destroyRealm?: boolean): Promise<void>;
59
+ /**
60
+ * Legacy alias for backward compatibility — delegates to createSession.
61
+ * @deprecated Use createSession({ type: "computer-use" }) instead.
62
+ */
63
+ ensureRealm(): Promise<string>;
64
+ private startIfNeeded;
65
+ private getSessionInternal;
66
+ private getCapabilitiesForType;
67
+ }
68
+ export declare function loadRealmUrl(): string;
69
+ export declare function loadRealmApiKey(): string | undefined;
70
+ //# sourceMappingURL=realm-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realm-client.d.ts","sourceRoot":"","sources":["../../../src/handlers/realm-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EACV,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAInB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,IAAI,GAAG,WAAW,GAAG,SAAS,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CAClD;AAaD,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;gBAEnD,MAAM,EAAE,iBAAiB;IAYrC;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAyClF;;OAEG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkB/D;;;OAGG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAe7D;;OAEG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA8BhF;;OAEG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAY9E;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;YAiBtB,aAAa;IAQ3B,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,sBAAsB;CAY/B;AAED,wBAAgB,YAAY,IAAI,MAAM,CAMrC;AAED,wBAAgB,eAAe,IAAI,MAAM,GAAG,SAAS,CAEpD"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Streamlined Realm API client — sessions replace direct capture/action calls.
3
+ *
4
+ * This client talks to @theaiinc/realm-api HTTP server but presents a
5
+ * session-based interface. Consumers interact through sessions:
6
+ *
7
+ * const client = new RealmClient({ baseUrl, engine: 'ubuntu' });
8
+ * const session = await client.createSession({ type: 'computer-use' });
9
+ * const observation = await client.observe(session.sessionId);
10
+ * const result = await client.input(session.sessionId, { type: 'mouse', params: { x: 100, y: 200 } });
11
+ *
12
+ * Consumers must NOT depend on transport details (WebRTC, screenshots, etc.).
13
+ */
14
+ import axios from 'axios';
15
+ import { nanoid } from 'nanoid';
16
+ export class RealmClient {
17
+ http;
18
+ config;
19
+ realmUrl;
20
+ sessions = new Map();
21
+ constructor(config) {
22
+ this.config = config;
23
+ this.realmUrl = config.baseUrl.replace(/\/$/, '');
24
+ const headers = { 'Content-Type': 'application/json' };
25
+ if (config.apiKey)
26
+ headers['x-api-key'] = config.apiKey;
27
+ this.http = axios.create({
28
+ baseURL: this.realmUrl,
29
+ headers,
30
+ timeout: 60_000,
31
+ });
32
+ }
33
+ /**
34
+ * Create an interaction session.
35
+ * This replaces the old ensureRealm() + capture() pattern.
36
+ */
37
+ async createSession(request) {
38
+ const realmId = this.config.realmId || (await this.ensureRealm());
39
+ const sessionId = `session-${nanoid(12)}`;
40
+ const now = new Date().toISOString();
41
+ const capabilities = request.capabilities ?? this.getCapabilitiesForType(request.type);
42
+ const descriptor = {
43
+ id: sessionId,
44
+ type: request.type,
45
+ state: 'creating',
46
+ observationEndpoint: `${this.realmUrl}/api/v1/realms/${realmId}/capture`,
47
+ inputEndpoint: `${this.realmUrl}/api/v1/realms/${realmId}`,
48
+ capabilities,
49
+ observationMethod: 'screenshot',
50
+ realmId,
51
+ ...(request.ownerId !== undefined ? { ownerId: request.ownerId } : {}),
52
+ ...(request.participantIds !== undefined ? { participantIds: request.participantIds } : {}),
53
+ createdAt: now,
54
+ updatedAt: now,
55
+ ...(request.metadata !== undefined ? { metadata: request.metadata } : {}),
56
+ };
57
+ this.sessions.set(sessionId, {
58
+ id: sessionId,
59
+ type: request.type,
60
+ realmId,
61
+ state: 'creating',
62
+ capabilities,
63
+ ...(request.ownerId !== undefined ? { ownerId: request.ownerId } : {}),
64
+ ...(request.participantIds !== undefined ? { participantIds: request.participantIds } : {}),
65
+ createdAt: new Date(),
66
+ });
67
+ // Mark active after setup
68
+ descriptor.state = 'active';
69
+ this.sessions.get(sessionId).state = 'active';
70
+ return { sessionId, descriptor };
71
+ }
72
+ /**
73
+ * Get the current session descriptor.
74
+ */
75
+ async getSession(sessionId) {
76
+ const internal = this.getSessionInternal(sessionId);
77
+ return {
78
+ id: internal.id,
79
+ type: internal.type,
80
+ state: internal.state,
81
+ observationEndpoint: `${this.realmUrl}/api/v1/realms/${internal.realmId}/capture`,
82
+ inputEndpoint: `${this.realmUrl}/api/v1/realms/${internal.realmId}`,
83
+ capabilities: internal.capabilities,
84
+ observationMethod: 'screenshot',
85
+ realmId: internal.realmId,
86
+ ...(internal.ownerId !== undefined ? { ownerId: internal.ownerId } : {}),
87
+ ...(internal.participantIds !== undefined ? { participantIds: internal.participantIds } : {}),
88
+ createdAt: internal.createdAt.toISOString(),
89
+ updatedAt: new Date().toISOString(),
90
+ };
91
+ }
92
+ /**
93
+ * Observe the current state of a session.
94
+ * Realm decides how observation is delivered.
95
+ */
96
+ async observe(sessionId) {
97
+ const session = this.getSessionInternal(sessionId);
98
+ const timestamp = new Date().toISOString();
99
+ const { data } = await this.http.get(`/api/v1/realms/${session.realmId}/capture`);
100
+ return {
101
+ screenshot: data.screenshot,
102
+ ...(data.piiRedacted !== undefined ? { piiRedacted: data.piiRedacted } : {}),
103
+ timestamp,
104
+ };
105
+ }
106
+ /**
107
+ * Send an input action to a session.
108
+ */
109
+ async input(sessionId, input) {
110
+ const session = this.getSessionInternal(sessionId);
111
+ switch (input.type) {
112
+ case 'mouse':
113
+ case 'touch': {
114
+ const { data } = await this.http.post(`/api/v1/realms/${session.realmId}/click`, { x: input.params.x, y: input.params.y });
115
+ return {
116
+ success: data.success,
117
+ ...(data.error !== undefined ? { error: data.error } : {}),
118
+ };
119
+ }
120
+ case 'keyboard': {
121
+ const { data } = await this.http.post(`/api/v1/realms/${session.realmId}/type`, { text: input.params.text });
122
+ return {
123
+ success: data.success,
124
+ ...(data.error !== undefined ? { error: data.error } : {}),
125
+ };
126
+ }
127
+ default:
128
+ return { success: false, error: `Unsupported input type: ${input.type}` };
129
+ }
130
+ }
131
+ /**
132
+ * Terminate a session.
133
+ */
134
+ async terminateSession(sessionId, destroyRealm = false) {
135
+ const session = this.getSessionInternal(sessionId);
136
+ session.state = 'terminated';
137
+ if (destroyRealm && !this.config.realmId) {
138
+ try {
139
+ await this.http.delete(`/api/v1/realms/${session.realmId}`);
140
+ }
141
+ catch {
142
+ // best-effort
143
+ }
144
+ }
145
+ }
146
+ /**
147
+ * Legacy alias for backward compatibility — delegates to createSession.
148
+ * @deprecated Use createSession({ type: "computer-use" }) instead.
149
+ */
150
+ async ensureRealm() {
151
+ if (this.config.realmId) {
152
+ await this.startIfNeeded(this.config.realmId);
153
+ return this.config.realmId;
154
+ }
155
+ const name = this.config.realmName || `ratatoskr-${this.config.engine}-${Date.now()}`;
156
+ const { data } = await this.http.post('/api/v1/realms', {
157
+ name,
158
+ engine: this.config.engine,
159
+ environment: this.config.environment,
160
+ });
161
+ const realmId = data.id;
162
+ await this.startIfNeeded(realmId);
163
+ return realmId;
164
+ }
165
+ async startIfNeeded(realmId) {
166
+ const { data } = await this.http.get(`/api/v1/realms/${realmId}`);
167
+ if (data.realm?.session?.state === 'running')
168
+ return;
169
+ await this.http.post(`/api/v1/realms/${realmId}/start`);
170
+ }
171
+ getSessionInternal(sessionId) {
172
+ const session = this.sessions.get(sessionId);
173
+ if (!session)
174
+ throw new Error(`Session not found: ${sessionId}`);
175
+ if (session.state === 'terminated' || session.state === 'completed') {
176
+ throw new Error(`Session is ${session.state}: ${sessionId}`);
177
+ }
178
+ return session;
179
+ }
180
+ getCapabilitiesForType(type) {
181
+ switch (type) {
182
+ case 'computer-use':
183
+ return ['mouse', 'keyboard', 'scroll', 'clipboard'];
184
+ case 'phone-use':
185
+ return ['touch', 'keyboard', 'scroll'];
186
+ default:
187
+ return ['mouse', 'keyboard'];
188
+ }
189
+ }
190
+ }
191
+ export function loadRealmUrl() {
192
+ return (process.env.REALM_URL ||
193
+ process.env.CU_REALM_URL ||
194
+ 'http://localhost:8542');
195
+ }
196
+ export function loadRealmApiKey() {
197
+ return process.env.REALM_API_KEY || process.env.CU_REALM_API_KEY || undefined;
198
+ }
199
+ //# sourceMappingURL=realm-client.js.map