@elizaos/plugin-capacitor-bridge 2.0.3-beta.2 → 2.0.3-beta.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.
@@ -0,0 +1,106 @@
1
+ import { Server } from 'node:http';
2
+ import { AgentRuntime, GenerateTextParams } from '@elizaos/core';
3
+
4
+ /**
5
+ * Stock Capacitor mobile local-inference bridge.
6
+ *
7
+ * AOSP builds run llama.cpp inside the agent process via bun:ffi. Stock
8
+ * Capacitor Android/iOS builds cannot do that: llama.cpp is exposed to the
9
+ * WebView through the native Capacitor plugin. This module is the agent-side
10
+ * half of that path. It accepts a loopback WebSocket from the WebView,
11
+ * forwards TEXT_SMALL / TEXT_LARGE requests to the device, and lets the
12
+ * normal conversation routes keep using runtime model handlers.
13
+ */
14
+
15
+ interface LocalInferenceLoadArgs {
16
+ modelPath: string;
17
+ contextSize?: number;
18
+ useGpu?: boolean;
19
+ maxThreads?: number;
20
+ draftModelPath?: string;
21
+ draftContextSize?: number;
22
+ draftMin?: number;
23
+ draftMax?: number;
24
+ speculativeSamples?: number;
25
+ mobileSpeculative?: boolean;
26
+ cacheTypeK?: string;
27
+ cacheTypeV?: string;
28
+ disableThinking?: boolean;
29
+ }
30
+ interface DeviceCapabilities {
31
+ platform: "ios" | "android" | "web";
32
+ deviceModel: string;
33
+ totalRamGb: number;
34
+ cpuCores: number;
35
+ gpu: {
36
+ backend: "metal" | "vulkan" | "gpu-delegate";
37
+ available: boolean;
38
+ } | null;
39
+ }
40
+ interface MobileDeviceBridgeStatus {
41
+ enabled: boolean;
42
+ connected: boolean;
43
+ devices: Array<{
44
+ deviceId: string;
45
+ capabilities: DeviceCapabilities;
46
+ loadedPath: string | null;
47
+ connectedSince: string;
48
+ }>;
49
+ primaryDeviceId: string | null;
50
+ pendingRequests: number;
51
+ modelPath: string | null;
52
+ }
53
+ declare class MobileDeviceBridge {
54
+ private wss;
55
+ private readonly devices;
56
+ private readonly pendingLoads;
57
+ private readonly pendingUnloads;
58
+ private readonly pendingGenerates;
59
+ private readonly pendingEmbeds;
60
+ private readonly pendingFormatChats;
61
+ private readonly expectedPairingToken;
62
+ status(): MobileDeviceBridgeStatus;
63
+ attachToHttpServer(server: Server): Promise<void>;
64
+ private handleConnection;
65
+ private handleDeviceMessage;
66
+ private primaryDevice;
67
+ private sendToPrimary;
68
+ loadModel(args: LocalInferenceLoadArgs): Promise<void>;
69
+ unloadModel(): Promise<void>;
70
+ generate(args: {
71
+ prompt: string;
72
+ stopSequences?: string[];
73
+ maxTokens?: number;
74
+ temperature?: number;
75
+ }): Promise<string>;
76
+ embed(args: {
77
+ input: string;
78
+ }): Promise<number[]>;
79
+ /**
80
+ * Apply the model's native chat template (Jinja, from the GGUF) to the
81
+ * given message list. Round-trips to the WebView so the Capacitor
82
+ * `LlamaCpp.getFormattedChat()` plugin call can invoke llama.cpp's
83
+ * `llama_chat_apply_template`. Returns the fully tokenized chat
84
+ * prompt string ready to feed back into `generate()`. Returns `null`
85
+ * when the loaded model has no chat template baked in (caller should
86
+ * fall back to a manual flatten in that case).
87
+ */
88
+ formatChat(messages: {
89
+ role: string;
90
+ content: string;
91
+ }[]): Promise<string | null>;
92
+ }
93
+ declare const mobileDeviceBridge: MobileDeviceBridge;
94
+ declare function buildLoadArgsFromRegistryModel(model: {
95
+ id: string;
96
+ path: string;
97
+ }): LocalInferenceLoadArgs;
98
+ /** Gemma fallback prompt for bionic paths built without device-bridge templating. */
99
+ declare function buildGemmaBionicPrompt(params: GenerateTextParams): string;
100
+ declare function getMobileDeviceBridgeStatus(): MobileDeviceBridgeStatus;
101
+ declare function loadMobileDeviceBridgeModel(modelPath: string, modelId?: string): Promise<void>;
102
+ declare function unloadMobileDeviceBridgeModel(): Promise<void>;
103
+ declare function attachMobileDeviceBridgeToServer(server: Server): Promise<void>;
104
+ declare function ensureMobileDeviceBridgeInferenceHandlers(runtime: AgentRuntime): Promise<boolean>;
105
+
106
+ export { type MobileDeviceBridgeStatus, attachMobileDeviceBridgeToServer, buildGemmaBionicPrompt, buildLoadArgsFromRegistryModel, ensureMobileDeviceBridgeInferenceHandlers, getMobileDeviceBridgeStatus, loadMobileDeviceBridgeModel, mobileDeviceBridge, unloadMobileDeviceBridgeModel };
@@ -0,0 +1,21 @@
1
+ import {
2
+ attachMobileDeviceBridgeToServer,
3
+ buildGemmaBionicPrompt,
4
+ buildLoadArgsFromRegistryModel,
5
+ ensureMobileDeviceBridgeInferenceHandlers,
6
+ getMobileDeviceBridgeStatus,
7
+ loadMobileDeviceBridgeModel,
8
+ mobileDeviceBridge,
9
+ unloadMobileDeviceBridgeModel
10
+ } from "./chunk-Q2XW27TY.js";
11
+ import "./chunk-MLKGABMK.js";
12
+ export {
13
+ attachMobileDeviceBridgeToServer,
14
+ buildGemmaBionicPrompt,
15
+ buildLoadArgsFromRegistryModel,
16
+ ensureMobileDeviceBridgeInferenceHandlers,
17
+ getMobileDeviceBridgeStatus,
18
+ loadMobileDeviceBridgeModel,
19
+ mobileDeviceBridge,
20
+ unloadMobileDeviceBridgeModel
21
+ };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * mobile-fs-shim.ts — Sandboxed virtual filesystem for the mobile IDE use case.
3
+ *
4
+ * PURPOSE
5
+ * -------
6
+ * The elizaOS agent bundle runs on-device in Bun (iOS via ElizaBunEngine.xcframework)
7
+ * and Node.js (Android via nodejs-mobile). It uses `node:fs` and `node:path` heavily
8
+ * for workspace reads/writes, PGlite data, skill files, and trajectory logs.
9
+ *
10
+ * This shim installs a deny-by-default interceptor over `node:fs` / `node:path`
11
+ * at process start so that:
12
+ * 1. Every path is resolved relative to a known workspace root on-device.
13
+ * 2. Path traversal outside the root (e.g. `../../etc/passwd`) is rejected.
14
+ * 3. System paths (`/etc`, `/usr`, `/System`, `/private`, kernel sockets, etc.)
15
+ * are blocked unconditionally — even when the caller passes an absolute path.
16
+ * 4. Dynamic code loading is blocked: `require()` and `import()` of files that
17
+ * aren't bundled are rejected.
18
+ * 5. Network-sourced code execution is blocked: `fetch + eval/Function` is not
19
+ * prevented here (that's handled at the JS engine level), but writing fetched
20
+ * bytes to an executable path is caught at the fs layer.
21
+ *
22
+ * APP STORE COMPLIANCE
23
+ * --------------------
24
+ * iOS App Store:
25
+ * - No JIT entitlement is required. Bun on iOS runs in interpreter mode
26
+ * (LLVM AOT + Bun's bytecode interpreter), never dlopen'd JIT pages.
27
+ * - No code is downloaded and executed at runtime. All JS is bundled into
28
+ * `agent-bundle.js` at build time via `Bun.build`. The shim adds a runtime
29
+ * guard to enforce this invariant.
30
+ * - File access is confined to the app's sandbox (Application Support/Eliza/).
31
+ * iOS enforces this at the kernel level too, but the shim provides an
32
+ * explicit JS-layer defence-in-depth.
33
+ *
34
+ * Android Play Store:
35
+ * - Play Store policy allows JIT for nodejs-mobile (V8 JIT is a documented
36
+ * exception for scripting runtimes).
37
+ * - Same "no downloaded code execution" guarantee as iOS — bundle-only JS.
38
+ * - Access restricted to app's internal storage (`getFilesDir()` / equivalent).
39
+ *
40
+ * USAGE
41
+ * -----
42
+ * import { installMobileFsShim } from "./mobile-fs-shim.ts";
43
+ * installMobileFsShim(process.env.MOBILE_WORKSPACE_ROOT!);
44
+ *
45
+ * The shim must be installed before any other module that touches `node:fs`.
46
+ * In `ios-bridge.ts` / `ios-android.ts` entry points, call it as the very
47
+ * first statement — before `bootElizaRuntime()` is imported.
48
+ *
49
+ * DESIGN NOTES
50
+ * ------------
51
+ * - Bun (iOS) and nodejs-mobile (Android) both expose `node:fs` as the
52
+ * canonical CJS module. We patch the live module object returned by
53
+ * `require("node:fs")` / `require("fs")` so every subsequent `import fs`
54
+ * or `require("fs")` in the bundle sees the sandboxed version.
55
+ * - `node:path` is not patched — path utilities themselves are safe; only the
56
+ * final resolved path fed to an fs operation needs guarding. We export
57
+ * `sandboxedPath()` for callers that assemble absolute paths externally.
58
+ * - The shim is idempotent: calling `installMobileFsShim` a second time with
59
+ * the same root returns the already-installed shim state. Calling it with a
60
+ * different root after install throws to prevent accidental escalation.
61
+ * - All blocked operations throw `EACCES`-coded errors so callers that check
62
+ * `err.code` behave the same as if the OS rejected the call.
63
+ */
64
+ /**
65
+ * Exported for callers that assemble absolute paths outside fs calls.
66
+ * Returns the sandbox-validated absolute path or throws `EACCES`.
67
+ */
68
+ declare function sandboxedPath(inputPath: string): string;
69
+ /**
70
+ * Whether the shim is currently active.
71
+ */
72
+ declare function isMobileFsShimInstalled(): boolean;
73
+ /**
74
+ * The workspace root the shim was installed with (empty string if not yet
75
+ * installed).
76
+ */
77
+ declare function getMobileWorkspaceRoot(): string;
78
+ /**
79
+ * Install the mobile filesystem sandbox.
80
+ *
81
+ * @param workspaceRoot Absolute path to the app's writable workspace directory.
82
+ * Typically `SandboxPaths.appSupport + "/workspace"` on iOS,
83
+ * equivalent to `getFilesDir()/workspace` on Android.
84
+ * May be passed directly from the native host via
85
+ * `process.env.MOBILE_WORKSPACE_ROOT`.
86
+ *
87
+ * The function is idempotent: a second call with the same root is silently
88
+ * ignored. A second call with a different root throws to prevent accidental
89
+ * privilege escalation.
90
+ */
91
+ declare function installMobileFsShim(workspaceRoot: string): void;
92
+
93
+ export { getMobileWorkspaceRoot, installMobileFsShim, isMobileFsShimInstalled, sandboxedPath };
@@ -0,0 +1,13 @@
1
+ import {
2
+ getMobileWorkspaceRoot,
3
+ installMobileFsShim,
4
+ isMobileFsShimInstalled,
5
+ sandboxedPath
6
+ } from "../chunk-E7Y447TQ.js";
7
+ import "../chunk-MLKGABMK.js";
8
+ export {
9
+ getMobileWorkspaceRoot,
10
+ installMobileFsShim,
11
+ isMobileFsShimInstalled,
12
+ sandboxedPath
13
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/plugin-capacitor-bridge",
3
- "version": "2.0.3-beta.2",
3
+ "version": "2.0.3-beta.4",
4
4
  "description": "Capacitor WebSocket bridge to device llama for stock mobile (non-AOSP) Eliza builds.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -54,11 +54,11 @@
54
54
  "typecheck": "tsc --noEmit"
55
55
  },
56
56
  "dependencies": {
57
- "@elizaos/core": "2.0.3-beta.2",
57
+ "@elizaos/core": "2.0.3-beta.4",
58
58
  "ws": "^8.18.0"
59
59
  },
60
60
  "peerDependencies": {
61
- "@elizaos/core": "2.0.3-beta.2"
61
+ "@elizaos/core": "2.0.3-beta.4"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@biomejs/biome": "^2.4.14",
@@ -72,5 +72,5 @@
72
72
  "publishConfig": {
73
73
  "access": "public"
74
74
  },
75
- "gitHead": "82fe0f44215954c2417328203f5bd6510985c1fc"
75
+ "gitHead": "f76f55793a0fb8d6b869dd8ddce03970de061e34"
76
76
  }