@dvai-bridge/capacitor 4.0.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.
- package/LICENSE +51 -0
- package/README.md +199 -0
- package/dist/index.cjs +203 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +297 -0
- package/dist/index.d.ts +297 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Deep Voice Ai Limited - Software License Agreement
|
|
2
|
+
|
|
3
|
+
**Version 1.0.0**
|
|
4
|
+
|
|
5
|
+
This License Agreement governs the use of the DVAI-Bridge software (the "Software"). By downloading, installing, or using the Software, you agree to be bound by the terms of this License.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. LICENSE GRANTS
|
|
10
|
+
|
|
11
|
+
### 1.1 Development and Personal Use (Free Tier)
|
|
12
|
+
Deep Voice Ai Limited ("Licensor") grants you a non-exclusive, non-transferable, royalty-free license to use the Software solely for:
|
|
13
|
+
- Internal development and testing purposes.
|
|
14
|
+
- Non-commercial personal projects.
|
|
15
|
+
- Academic and non-profit research.
|
|
16
|
+
|
|
17
|
+
### 1.2 Commercial Use (Paid Tier)
|
|
18
|
+
Any use of the Software for **Commercial Purposes** requires a separate, paid Commercial License from Licensor. "Commercial Purposes" include:
|
|
19
|
+
- Use in production environments.
|
|
20
|
+
- Integration into revenue-generating products or services.
|
|
21
|
+
- Distribution to third-party customers for a fee.
|
|
22
|
+
- Use by an entity with more than $100,000 USD in annual revenue.
|
|
23
|
+
|
|
24
|
+
To obtain a Commercial License, contact `info@deepvoiceai.co` or visit `https://deepvoiceai.co/licensing`.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. RESTRICTIONS
|
|
29
|
+
Except as expressly permitted, you may not:
|
|
30
|
+
- Sublicense, rent, lease, or resell the Software without express permission.
|
|
31
|
+
- Remove any proprietary notices or branding from the Software.
|
|
32
|
+
- Use the Software for any illegal or malicious purposes.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 3. INTELLECTUAL PROPERTY
|
|
37
|
+
The Software is owned by **Deep Voice Ai Limited** and is protected by copyright and intellectual property laws. This agreement does not transfer ownership of the Software.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 4. NO WARRANTY
|
|
42
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL THE LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 5. GOVERNING LAW
|
|
47
|
+
This License shall be governed by and construed in accordance with the laws of the jurisdiction where Deep Voice Ai Limited is registered.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
© 2026 Deep Voice Ai Limited. All rights reserved.
|
package/README.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# DVAI-Bridge
|
|
4
|
+
|
|
5
|
+
<!-- [](https://github.com/Westenets/dvai-bridge/actions/workflows/smoke-real-models.yml) -->
|
|
6
|
+
|
|
7
|
+
[](LICENSE)      
|
|
8
|
+
|
|
9
|
+
> **The local OpenAI server you embed inside your app.**
|
|
10
|
+
> One library. One HTTP wire. Every platform. Zero install for your users.
|
|
11
|
+
|
|
12
|
+
**Docs:** [dvai-bridge.deepvoiceai.co](https://dvai-bridge.deepvoiceai.co)
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { DVAI } from "@dvai-bridge/core";
|
|
16
|
+
import OpenAI from "openai";
|
|
17
|
+
|
|
18
|
+
const dvai = new DVAI({ backend: "transformers" });
|
|
19
|
+
await dvai.initialize();
|
|
20
|
+
|
|
21
|
+
const openai = new OpenAI({ baseURL: dvai.baseUrl, apiKey: "ignored" });
|
|
22
|
+
await openai.chat.completions.create({
|
|
23
|
+
model: dvai.transformersModelId,
|
|
24
|
+
messages: [{ role: "user", content: "Hello!" }],
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
That's it. A real OpenAI-compatible server is now running inside your app's
|
|
29
|
+
own process. Point any OpenAI client — LangChain, the OpenAI SDK, the Vercel
|
|
30
|
+
AI SDK, anything — at `dvai.baseUrl` and your agent code keeps working.
|
|
31
|
+
|
|
32
|
+
Built by **[Deep Voice AI](https://deepvoiceai.co)**.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Why it exists
|
|
37
|
+
|
|
38
|
+
Local AI works beautifully on a laptop with **Ollama + LangChain**. Then you
|
|
39
|
+
try to ship the app and your users don't have Ollama. Mobile can't run it.
|
|
40
|
+
Corporate IT won't add another daemon. So you reinvent the same plumbing —
|
|
41
|
+
spawn an inference engine, bind a port, translate to OpenAI HTTP, handle
|
|
42
|
+
CORS, manage lifecycle, wrap the accelerator of the day per platform — and
|
|
43
|
+
do it all over again for every target OS.
|
|
44
|
+
|
|
45
|
+
DVAI-Bridge is that plumbing, packaged as a library, for every client
|
|
46
|
+
platform.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## What you get
|
|
51
|
+
|
|
52
|
+
- **One OpenAI HTTP surface.** Bound on `127.0.0.1` (or `0.0.0.0` for
|
|
53
|
+
device-to-device). Streaming, embeddings, models, recovery — all built in.
|
|
54
|
+
- **Six SDKs.** `@dvai-bridge/core` + `react` + `vanilla` + `capacitor`,
|
|
55
|
+
`DVAIBridge` (Swift / iOS), `co.deepvoiceai:dvai-bridge` (Kotlin / Android),
|
|
56
|
+
`@dvai-bridge/react-native`, `dvai_bridge` (Flutter), `co.deepvoiceai.dvai-bridge` (.NET).
|
|
57
|
+
- **Nine backends.** WebLLM, Transformers.js, llama.cpp, Apple Foundation
|
|
58
|
+
Models, MLX, CoreML / ANE, MediaPipe LLM, LiteRT, ONNX Runtime GenAI —
|
|
59
|
+
selected per-platform, invisible to your agent code.
|
|
60
|
+
- **Native acceleration** wherever it runs: WebGPU in browsers, CUDA / Metal
|
|
61
|
+
/ Vulkan / DirectML on desktop, ANE / Metal / MLX on iOS, NNAPI / QNN
|
|
62
|
+
Hexagon / GPU delegate on Android.
|
|
63
|
+
- **Multimodal.** Text, image, audio, video — declarative loader for
|
|
64
|
+
cutting-edge models (Gemma 4, LLaVA, Idefics) without waiting for library
|
|
65
|
+
updates.
|
|
66
|
+
- **Distributed inference (v3.0+).** Phone too slow? Offload to your laptop
|
|
67
|
+
on the same Wi-Fi via mDNS pairing — same OpenAI wire, transparent to
|
|
68
|
+
your code. Internet path via a self-hostable rendezvous server.
|
|
69
|
+
- **DVAI Hub (v3.1+).** A first-party desktop utility that turns any device
|
|
70
|
+
into a strong-peer for the rest of your fleet. Brand-neutral install via
|
|
71
|
+
Homebrew / winget / GitHub Releases, OR fork it for your own branded
|
|
72
|
+
companion. Routes through Ollama / LM Studio / vLLM / llama-server /
|
|
73
|
+
llamafile if you've already got those running.
|
|
74
|
+
- **Zero user install.** It's a library, not a daemon. `npm install`,
|
|
75
|
+
`cocoapods`, gradle — your CI already has the muscle for it.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Supported platforms
|
|
80
|
+
|
|
81
|
+
| Stack | Package | Backends |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| Browser (React, Vue, Svelte, vanilla JS) | `@dvai-bridge/core` + `react` / `vanilla` | WebLLM (WebGPU), Transformers.js (WebGPU / WASM SIMD) |
|
|
84
|
+
| Node / Bun / Electron | `@dvai-bridge/core` | Transformers.js, native llama.cpp |
|
|
85
|
+
| Capacitor hybrid mobile | `@dvai-bridge/capacitor` + backend slice | Native llama.cpp (Metal iOS, Vulkan / CPU Android) |
|
|
86
|
+
| iOS native (Swift) | `DVAIBridge` (SPM / CocoaPods) | llama.cpp (Metal), CoreML / ANE, Apple Foundation Models, MLX |
|
|
87
|
+
| Android native (Kotlin / Java) | `co.deepvoiceai:dvai-bridge` (AAR) | llama.cpp, MediaPipe LLM, LiteRT, NNAPI / QNN |
|
|
88
|
+
| React Native (≥0.77, TurboModule) | `@dvai-bridge/react-native` | All iOS + Android backends (delegates) |
|
|
89
|
+
| Flutter (≥3.39) | `dvai_bridge` (pub.dev) | All iOS + Android backends (Pigeon channels) |
|
|
90
|
+
| .NET 10 LTS (MAUI / Avalonia / WinUI / Catalyst / desktop) | `co.deepvoiceai.dvai-bridge*` (NuGet) | iOS / Android delegate to native; desktop = llama.cpp + ONNX Runtime GenAI + ML.NET |
|
|
91
|
+
|
|
92
|
+
Full quickstart per platform: [dvai-bridge.deepvoiceai.co/guide/getting-started](https://dvai-bridge.deepvoiceai.co/guide/getting-started)
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
// React
|
|
100
|
+
import { DVAIProvider, useDVAI } from "@dvai-bridge/react";
|
|
101
|
+
<DVAIProvider config={{ backend: "transformers" }}>
|
|
102
|
+
<Chat />
|
|
103
|
+
</DVAIProvider>;
|
|
104
|
+
function Chat() {
|
|
105
|
+
const { isReady, baseUrl } = useDVAI();
|
|
106
|
+
return isReady ? <div>Local AI live at {baseUrl}</div> : <Loading />;
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```swift
|
|
111
|
+
// iOS
|
|
112
|
+
let server = try await DVAIBridge.shared.start()
|
|
113
|
+
// server.baseUrl = "http://127.0.0.1:38883/v1"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```kotlin
|
|
117
|
+
// Android
|
|
118
|
+
val server = DVAIBridge.start(context)
|
|
119
|
+
// server.baseUrl = "http://127.0.0.1:38883/v1"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```dart
|
|
123
|
+
// Flutter
|
|
124
|
+
final state = await DVAIBridge.instance.start(
|
|
125
|
+
backend: BackendKind.auto,
|
|
126
|
+
modelPath: '/path/to/model.gguf',
|
|
127
|
+
);
|
|
128
|
+
// state.baseUrl = "http://127.0.0.1:38883/v1"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```csharp
|
|
132
|
+
// .NET
|
|
133
|
+
var server = await DVAIBridge.Shared.StartAsync(new StartOptions {
|
|
134
|
+
Backend = BackendKind.Auto,
|
|
135
|
+
ModelPath = "/path/to/model.gguf",
|
|
136
|
+
});
|
|
137
|
+
// server.BaseUrl = "http://127.0.0.1:38883/v1"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Multimodal, streaming, embeddings, distributed offload, the Hub —
|
|
141
|
+
everything's at the [docs site](https://dvai-bridge.deepvoiceai.co).
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## What's new in v3.1
|
|
146
|
+
|
|
147
|
+
- **DVAI Hub** — Tauri desktop utility that's the strong-peer side of v3
|
|
148
|
+
distributed inference. `brew install deepvoiceai/dvai-hub/dvai-hub` (or
|
|
149
|
+
`winget install DeepVoiceAI.DVAIHub`) → mobile apps on the same Wi-Fi
|
|
150
|
+
pair with it and offload heavy inference. [Guide →](https://dvai-bridge.deepvoiceai.co/guide/dvai-hub)
|
|
151
|
+
- **External-engine bridge.** Hub surfaces Ollama / LM Studio / vLLM /
|
|
152
|
+
llama-server / llamafile as additional backend pools so paired apps
|
|
153
|
+
serve from whatever's already cached. Opt-in per engine.
|
|
154
|
+
- **Strict substitution policy.** Models with mismatched family / version /
|
|
155
|
+
size / type are refused by default; quant-only mismatches gated behind a
|
|
156
|
+
per-pairing `preferBetterQuant` flag. No silent mis-routing.
|
|
157
|
+
- **HMAC-signed identity** on `/v1/chat/completions`. Per-app audit logs
|
|
158
|
+
surface who served what, with structured `(appId, peerDeviceId,
|
|
159
|
+
engine, requestedModel, servedModel, outcome)` rows.
|
|
160
|
+
- **Library finalization.** `httpBindHost` (LAN bind), `chatCompletionInterceptor`
|
|
161
|
+
(extension point), HMAC primitives re-exported, `/v1/dvai/*` routes
|
|
162
|
+
actually dispatched, TransformersBackend Node-mode device fix.
|
|
163
|
+
[Migration v3.0 → v3.1 →](https://dvai-bridge.deepvoiceai.co/migration/v3.0-to-v3.1)
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Robustness
|
|
168
|
+
|
|
169
|
+
Streaming-correct (SSE passthrough + blank-chunk detection), generation
|
|
170
|
+
timeout, automatic engine-state recovery on fatal errors, port fallback,
|
|
171
|
+
worker offloading, Private Network Access ready, CORS configured. The
|
|
172
|
+
boring substrate so your agent code never has to think about it.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Licensing
|
|
177
|
+
|
|
178
|
+
Dual: **free for development & personal use** on `localhost` (verified at
|
|
179
|
+
runtime). **Commercial use** requires a license key — `info@deepvoiceai.co`.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Contributing
|
|
184
|
+
|
|
185
|
+
PRs welcome.
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pnpm install
|
|
189
|
+
pnpm build
|
|
190
|
+
bash scripts/build-all.sh # full matrix (auto-skips per-host)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
[`CONTRIBUTING.md`](./CONTRIBUTING.md) for the PR flow. Per-platform
|
|
194
|
+
contributor docs (iOS / Android / RN / Flutter / .NET) under
|
|
195
|
+
[`docs/development/`](./docs/development/).
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
© Deep Voice AI Limited. All rights reserved.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
DVAIBridge: () => DVAIBridge
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/dispatch.ts
|
|
38
|
+
var import_core = require("@capacitor/core");
|
|
39
|
+
var PLUGIN_NAME_BY_BACKEND = {
|
|
40
|
+
llama: "DVAIBridgeLlama",
|
|
41
|
+
foundation: "DVAIBridgeFoundation",
|
|
42
|
+
mediapipe: "DVAIBridgeMediaPipe",
|
|
43
|
+
mlx: "DVAIBridgeMLX"
|
|
44
|
+
};
|
|
45
|
+
var activePlugin = null;
|
|
46
|
+
var activeBackend = null;
|
|
47
|
+
function pluginFor(backend) {
|
|
48
|
+
const name = PLUGIN_NAME_BY_BACKEND[backend];
|
|
49
|
+
return (0, import_core.registerPlugin)(name);
|
|
50
|
+
}
|
|
51
|
+
function isPluginNotImplementedError(err) {
|
|
52
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
53
|
+
return /not implemented|not available|UNIMPLEMENTED/i.test(msg);
|
|
54
|
+
}
|
|
55
|
+
function assertBackendCompatibleWithPlatform(backend) {
|
|
56
|
+
if (backend === "foundation" || backend === "mlx") {
|
|
57
|
+
let platform;
|
|
58
|
+
try {
|
|
59
|
+
platform = import_core.Capacitor?.getPlatform?.();
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
if (platform === "android") {
|
|
63
|
+
const label = backend === "foundation" ? "Apple Foundation Models" : "MLX";
|
|
64
|
+
throw new Error(
|
|
65
|
+
`[DVAI] ${label} is iOS-only. Use backend: "llama" or "mediapipe" on Android.`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
var dispatch = {
|
|
71
|
+
async start(opts) {
|
|
72
|
+
assertBackendCompatibleWithPlatform(opts.backend);
|
|
73
|
+
const native = pluginFor(opts.backend);
|
|
74
|
+
try {
|
|
75
|
+
const result = await native.start(opts);
|
|
76
|
+
activePlugin = native;
|
|
77
|
+
activeBackend = opts.backend;
|
|
78
|
+
return result;
|
|
79
|
+
} catch (err) {
|
|
80
|
+
if (isPluginNotImplementedError(err)) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`[DVAI] Backend "${opts.backend}" selected but the corresponding plugin is not installed. Run: npm install @dvai-bridge/capacitor-${opts.backend} && npx cap sync`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
async stop() {
|
|
89
|
+
if (!activePlugin) return;
|
|
90
|
+
try {
|
|
91
|
+
await activePlugin.stop();
|
|
92
|
+
} finally {
|
|
93
|
+
activePlugin = null;
|
|
94
|
+
activeBackend = null;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
async status() {
|
|
98
|
+
if (!activePlugin) return { running: false };
|
|
99
|
+
return activePlugin.status();
|
|
100
|
+
},
|
|
101
|
+
__reset() {
|
|
102
|
+
activePlugin = null;
|
|
103
|
+
activeBackend = null;
|
|
104
|
+
},
|
|
105
|
+
__activePlugin() {
|
|
106
|
+
return activePlugin;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// src/index.ts
|
|
111
|
+
var DVAIBridge = {
|
|
112
|
+
/** Start the embedded HTTP server with the chosen backend. Returns the URL. */
|
|
113
|
+
async start(opts) {
|
|
114
|
+
return dispatch.start(opts);
|
|
115
|
+
},
|
|
116
|
+
/** Stop the server and unload the model. Idempotent. */
|
|
117
|
+
async stop() {
|
|
118
|
+
return dispatch.stop();
|
|
119
|
+
},
|
|
120
|
+
/** Status snapshot — useful for UI reactivity. */
|
|
121
|
+
async status() {
|
|
122
|
+
return dispatch.status();
|
|
123
|
+
},
|
|
124
|
+
/** Subscribe to load/progress events. */
|
|
125
|
+
async addProgressListener(cb) {
|
|
126
|
+
const native = dispatch.__activePlugin();
|
|
127
|
+
if (!native) {
|
|
128
|
+
throw new Error("[DVAI] addProgressListener called before start()");
|
|
129
|
+
}
|
|
130
|
+
return native.addListener("progress", cb);
|
|
131
|
+
},
|
|
132
|
+
/**
|
|
133
|
+
* v3.0+ — distributed inference. Subscribe to one of the bridge's
|
|
134
|
+
* named event channels. Currently:
|
|
135
|
+
*
|
|
136
|
+
* - `"pairingRequest"`: emitted when an inbound peer requests pairing.
|
|
137
|
+
* The handler receives a {@link PairingRequest}; respond via
|
|
138
|
+
* {@link respondToPairing}. Default behaviour without a listener
|
|
139
|
+
* is to deny inbound pairing requests.
|
|
140
|
+
*
|
|
141
|
+
* Must be called after a successful {@link start} — the listener is
|
|
142
|
+
* dispatched on the active backend plugin, which is established by
|
|
143
|
+
* `start()`. Calling before `start()` throws.
|
|
144
|
+
*/
|
|
145
|
+
async addListener(eventName, cb) {
|
|
146
|
+
if (eventName !== "pairingRequest") {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`[DVAI] addListener: unknown event name "${eventName}". Valid: "pairingRequest".`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const native = dispatch.__activePlugin();
|
|
152
|
+
if (!native) {
|
|
153
|
+
throw new Error("[DVAI] addListener called before start()");
|
|
154
|
+
}
|
|
155
|
+
return native.addListener("pairingRequest", cb);
|
|
156
|
+
},
|
|
157
|
+
/**
|
|
158
|
+
* v3.0+ — distributed inference. Resolve a pending {@link PairingRequest}
|
|
159
|
+
* received via the `"pairingRequest"` event. Pass the request `id` and
|
|
160
|
+
* the user's decision; the native side records the decision and either
|
|
161
|
+
* lets the pairing proceed or rejects it.
|
|
162
|
+
*
|
|
163
|
+
* Idempotent — responding twice to the same `requestId` resolves
|
|
164
|
+
* cleanly on subsequent calls.
|
|
165
|
+
*/
|
|
166
|
+
async respondToPairing(requestId, approved) {
|
|
167
|
+
const native = dispatch.__activePlugin();
|
|
168
|
+
if (!native) {
|
|
169
|
+
throw new Error("[DVAI] respondToPairing called before start()");
|
|
170
|
+
}
|
|
171
|
+
await native.respondToPairing({ requestId, approved });
|
|
172
|
+
},
|
|
173
|
+
/** Resumable, checksum-verified, app-data-cached download. */
|
|
174
|
+
async downloadModel(opts) {
|
|
175
|
+
const native = await modelManagementPlugin();
|
|
176
|
+
return native.downloadModel(opts);
|
|
177
|
+
},
|
|
178
|
+
async listCachedModels() {
|
|
179
|
+
const native = await modelManagementPlugin();
|
|
180
|
+
const result = await native.listCachedModels();
|
|
181
|
+
return result.models;
|
|
182
|
+
},
|
|
183
|
+
async deleteCachedModel(filename) {
|
|
184
|
+
const native = await modelManagementPlugin();
|
|
185
|
+
await native.deleteCachedModel({ filename });
|
|
186
|
+
},
|
|
187
|
+
async cacheDir() {
|
|
188
|
+
const native = await modelManagementPlugin();
|
|
189
|
+
const result = await native.cacheDir();
|
|
190
|
+
return result.path;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
async function modelManagementPlugin() {
|
|
194
|
+
const active = dispatch.__activePlugin();
|
|
195
|
+
if (active) return active;
|
|
196
|
+
const { registerPlugin: registerPlugin2 } = await import("@capacitor/core");
|
|
197
|
+
return registerPlugin2("DVAIBridgeLlama");
|
|
198
|
+
}
|
|
199
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
200
|
+
0 && (module.exports = {
|
|
201
|
+
DVAIBridge
|
|
202
|
+
});
|
|
203
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/dispatch.ts"],"sourcesContent":["import { dispatch } from \"./dispatch.js\";\nimport type {\n StartOptions,\n StartResult,\n StatusInfo,\n ProgressEvent,\n DownloadOptions,\n CachedModelInfo,\n CapacitorBackend,\n NativePluginInterface,\n OffloadConfig,\n PairingRequest,\n Peer,\n} from \"./types.js\";\n\nexport type {\n CapacitorBackend,\n StartOptions,\n StartResult,\n StatusInfo,\n ProgressEvent,\n DownloadOptions,\n CachedModelInfo,\n NativePluginInterface,\n OffloadConfig,\n PairingRequest,\n Peer,\n};\n\nexport const DVAIBridge = {\n /** Start the embedded HTTP server with the chosen backend. Returns the URL. */\n async start(opts: StartOptions): Promise<StartResult> {\n return dispatch.start(opts);\n },\n\n /** Stop the server and unload the model. Idempotent. */\n async stop(): Promise<void> {\n return dispatch.stop();\n },\n\n /** Status snapshot — useful for UI reactivity. */\n async status(): Promise<StatusInfo> {\n return dispatch.status();\n },\n\n /** Subscribe to load/progress events. */\n async addProgressListener(\n cb: (e: ProgressEvent) => void,\n ): Promise<{ remove: () => Promise<void> }> {\n const native = dispatch.__activePlugin();\n if (!native) {\n throw new Error(\"[DVAI] addProgressListener called before start()\");\n }\n return native.addListener(\"progress\", cb);\n },\n\n /**\n * v3.0+ — distributed inference. Subscribe to one of the bridge's\n * named event channels. Currently:\n *\n * - `\"pairingRequest\"`: emitted when an inbound peer requests pairing.\n * The handler receives a {@link PairingRequest}; respond via\n * {@link respondToPairing}. Default behaviour without a listener\n * is to deny inbound pairing requests.\n *\n * Must be called after a successful {@link start} — the listener is\n * dispatched on the active backend plugin, which is established by\n * `start()`. Calling before `start()` throws.\n */\n async addListener(\n eventName: \"pairingRequest\",\n cb: (req: PairingRequest) => void,\n ): Promise<{ remove: () => Promise<void> }> {\n if (eventName !== \"pairingRequest\") {\n throw new Error(\n `[DVAI] addListener: unknown event name \"${eventName}\". Valid: \"pairingRequest\".`,\n );\n }\n const native = dispatch.__activePlugin();\n if (!native) {\n throw new Error(\"[DVAI] addListener called before start()\");\n }\n return native.addListener(\"pairingRequest\", cb);\n },\n\n /**\n * v3.0+ — distributed inference. Resolve a pending {@link PairingRequest}\n * received via the `\"pairingRequest\"` event. Pass the request `id` and\n * the user's decision; the native side records the decision and either\n * lets the pairing proceed or rejects it.\n *\n * Idempotent — responding twice to the same `requestId` resolves\n * cleanly on subsequent calls.\n */\n async respondToPairing(requestId: string, approved: boolean): Promise<void> {\n const native = dispatch.__activePlugin();\n if (!native) {\n throw new Error(\"[DVAI] respondToPairing called before start()\");\n }\n await native.respondToPairing({ requestId, approved });\n },\n\n /** Resumable, checksum-verified, app-data-cached download. */\n async downloadModel(opts: DownloadOptions): Promise<{ path: string; cached: boolean }> {\n const native = await modelManagementPlugin();\n return native.downloadModel(opts);\n },\n\n async listCachedModels(): Promise<CachedModelInfo[]> {\n const native = await modelManagementPlugin();\n const result = await native.listCachedModels();\n return result.models;\n },\n\n async deleteCachedModel(filename: string): Promise<void> {\n const native = await modelManagementPlugin();\n await native.deleteCachedModel({ filename });\n },\n\n async cacheDir(): Promise<string> {\n const native = await modelManagementPlugin();\n const result = await native.cacheDir();\n return result.path;\n },\n};\n\n/**\n * Resolve the plugin that owns model-management methods (downloadModel,\n * listCachedModels, deleteCachedModel, cacheDir). In Phase 1, only the\n * `llama` backend implements these — `foundation` rejects (Apple manages\n * models internally) and `mediapipe` rejects (developer-managed paths).\n *\n * If a non-llama plugin is currently active, model management still routes\n * to the llama plugin (caller can install + use it without `start()`).\n * The native side may reject if the plugin isn't installed; the resulting\n * Capacitor \"plugin not implemented\" error is the right surface.\n */\nasync function modelManagementPlugin(): Promise<NativePluginInterface> {\n const active = dispatch.__activePlugin();\n if (active) return active;\n const { registerPlugin } = await import(\"@capacitor/core\");\n return registerPlugin<NativePluginInterface>(\"DVAIBridgeLlama\");\n}\n","import { Capacitor, registerPlugin } from \"@capacitor/core\";\r\nimport type {\r\n CapacitorBackend,\r\n NativePluginInterface,\r\n StartOptions,\r\n StartResult,\r\n StatusInfo,\r\n} from \"./types.js\";\r\n\r\nconst PLUGIN_NAME_BY_BACKEND: Record<CapacitorBackend, string> = {\r\n llama: \"DVAIBridgeLlama\",\r\n foundation: \"DVAIBridgeFoundation\",\r\n mediapipe: \"DVAIBridgeMediaPipe\",\r\n mlx: \"DVAIBridgeMLX\",\r\n};\r\n\r\nlet activePlugin: NativePluginInterface | null = null;\r\nlet activeBackend: CapacitorBackend | null = null;\r\n\r\nfunction pluginFor(backend: CapacitorBackend): NativePluginInterface {\r\n const name = PLUGIN_NAME_BY_BACKEND[backend];\r\n return registerPlugin<NativePluginInterface>(name);\r\n}\r\n\r\nfunction isPluginNotImplementedError(err: unknown): boolean {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n return /not implemented|not available|UNIMPLEMENTED/i.test(msg);\r\n}\r\n\r\n/**\r\n * Apple Foundation Models is iOS-only. On Android, no amount of plugin-\r\n * install will help — surface a clear error rather than the generic\r\n * \"install the plugin\" wording from the not-implemented fallback.\r\n */\r\nfunction assertBackendCompatibleWithPlatform(backend: CapacitorBackend): void {\r\n if (backend === \"foundation\" || backend === \"mlx\") {\r\n let platform: string | undefined;\r\n try {\r\n platform = Capacitor?.getPlatform?.();\r\n } catch {\r\n // Capacitor.getPlatform() can throw in non-runtime contexts (SSR,\r\n // unit tests without the Capacitor shim). Fall through silently —\r\n // the native call will then surface its own error.\r\n }\r\n if (platform === \"android\") {\r\n const label = backend === \"foundation\" ? \"Apple Foundation Models\" : \"MLX\";\r\n throw new Error(\r\n `[DVAI] ${label} is iOS-only. Use backend: \"llama\" or \"mediapipe\" on Android.`,\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport const dispatch = {\r\n async start(opts: StartOptions): Promise<StartResult> {\r\n assertBackendCompatibleWithPlatform(opts.backend);\r\n const native = pluginFor(opts.backend);\r\n try {\r\n const result = await native.start(opts);\r\n activePlugin = native;\r\n activeBackend = opts.backend;\r\n return result;\r\n } catch (err) {\r\n if (isPluginNotImplementedError(err)) {\r\n throw new Error(\r\n `[DVAI] Backend \"${opts.backend}\" selected but the corresponding plugin is not installed. ` +\r\n `Run: npm install @dvai-bridge/capacitor-${opts.backend} && npx cap sync`,\r\n );\r\n }\r\n throw err;\r\n }\r\n },\r\n\r\n async stop(): Promise<void> {\r\n if (!activePlugin) return;\r\n try {\r\n await activePlugin.stop();\r\n } finally {\r\n activePlugin = null;\r\n activeBackend = null;\r\n }\r\n },\r\n\r\n async status(): Promise<StatusInfo> {\r\n if (!activePlugin) return { running: false };\r\n return activePlugin.status();\r\n },\r\n\r\n __reset(): void {\r\n activePlugin = null;\r\n activeBackend = null;\r\n },\r\n\r\n __activePlugin(): NativePluginInterface | null {\r\n return activePlugin;\r\n },\r\n};\r\n"],"mappings":"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","names":["registerPlugin"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @dvai-bridge/capacitor — public type definitions.
|
|
3
|
+
* These types are also imported by backend plugin packages
|
|
4
|
+
* (capacitor-llama, capacitor-foundation, capacitor-mediapipe)
|
|
5
|
+
* to keep the JS↔native contract consistent.
|
|
6
|
+
*/
|
|
7
|
+
type CapacitorBackend = "llama" | "foundation" | "mediapipe" | "mlx";
|
|
8
|
+
interface StartOptions {
|
|
9
|
+
/** Which native backend plugin to dispatch to. */
|
|
10
|
+
backend: CapacitorBackend;
|
|
11
|
+
/** Path to the GGUF model file (llama backend) or .task file (mediapipe). Not used by foundation. */
|
|
12
|
+
modelPath?: string;
|
|
13
|
+
/** Optional path to mmproj (vision projector) for llama vision models. */
|
|
14
|
+
mmprojPath?: string;
|
|
15
|
+
/** Llama: GPU layers offloaded (default 99 = max). */
|
|
16
|
+
gpuLayers?: number;
|
|
17
|
+
/** Llama / mediapipe: context window. */
|
|
18
|
+
contextSize?: number;
|
|
19
|
+
/** Llama: CPU threads. */
|
|
20
|
+
threads?: number;
|
|
21
|
+
/** Llama: initialize in embedding mode (chat will not work; embeddings will). */
|
|
22
|
+
embeddingMode?: boolean;
|
|
23
|
+
/** HTTP server base port; retries +1 up to httpMaxPortAttempts on EADDRINUSE. Default 38883. */
|
|
24
|
+
httpBasePort?: number;
|
|
25
|
+
/** Default 16. */
|
|
26
|
+
httpMaxPortAttempts?: number;
|
|
27
|
+
/** CORS Access-Control-Allow-Origin. "*", a single origin, or a list. Default "*". */
|
|
28
|
+
corsOrigin?: string | string[];
|
|
29
|
+
/** Auto-unload the model when OS emits low-memory warning. Default false. */
|
|
30
|
+
autoUnloadOnLowMemory?: boolean;
|
|
31
|
+
/** Native log verbosity. Default "info". */
|
|
32
|
+
logLevel?: "silent" | "info" | "debug";
|
|
33
|
+
/**
|
|
34
|
+
* v3.0+ — distributed inference / device offload.
|
|
35
|
+
*
|
|
36
|
+
* When `offload.enabled` is true, the native side runs mDNS discovery
|
|
37
|
+
* (and optionally a rendezvous WebSocket) to find peer dvai-bridge
|
|
38
|
+
* instances and offloads inference requests when the local device
|
|
39
|
+
* can't serve the model fast enough. See
|
|
40
|
+
* [the distributed-inference guide](https://dvai-bridge.deepvoiceai.co/guide/distributed-inference)
|
|
41
|
+
* for the full contract.
|
|
42
|
+
*
|
|
43
|
+
* Pairing-request UI is surfaced via the `"pairingRequest"` event
|
|
44
|
+
* (see {@link DVAIBridge.addListener}); the function callback in the
|
|
45
|
+
* JS-side `OffloadConfig.onPairingRequest` cannot cross the Capacitor
|
|
46
|
+
* plugin boundary, so consumers use the event surface instead.
|
|
47
|
+
*/
|
|
48
|
+
offload?: OffloadConfig;
|
|
49
|
+
/**
|
|
50
|
+
* v3.2.2+ — path (or fetchable URL) to your DVAI-Bridge license JWT.
|
|
51
|
+
*
|
|
52
|
+
* The Capacitor bridge forwards this string to the native backend
|
|
53
|
+
* plugin (`DVAIBridgeLlama`, `DVAIBridgeFoundation`,
|
|
54
|
+
* `DVAIBridgeMediaPipe`, `DVAIBridgeMLX`). Each native validator runs
|
|
55
|
+
* its own offline JWT verification (signature + expiry + audience +
|
|
56
|
+
* platform binding) against the iOS bundle identifier or Android
|
|
57
|
+
* package name — see `@dvai-bridge/core/license` for the canonical
|
|
58
|
+
* JWT format. The validators are authoritative; this JS-side
|
|
59
|
+
* forwarding is the only plumbing needed.
|
|
60
|
+
*
|
|
61
|
+
* Override priority on the native side (matches `DVAIConfig`):
|
|
62
|
+
* 1. `licenseToken` (below) — inline JWT string, highest priority
|
|
63
|
+
* 2. `licenseKeyPath` (this field) — explicit path or URL
|
|
64
|
+
* 3. Platform default locations + env-var fallbacks
|
|
65
|
+
*
|
|
66
|
+
* Free-tier behaviour (no license, expired, invalid) is enforced by
|
|
67
|
+
* the native validator and surfaced via the platform's standard
|
|
68
|
+
* error channel. The JS layer never inspects this value beyond
|
|
69
|
+
* forwarding it.
|
|
70
|
+
*/
|
|
71
|
+
licenseKeyPath?: string;
|
|
72
|
+
/**
|
|
73
|
+
* v3.2.2+ — inline DVAI-Bridge license JWT (the full token string).
|
|
74
|
+
*
|
|
75
|
+
* Use when injecting the license via runtime config rather than a
|
|
76
|
+
* bundled file — typical for over-the-air license refresh or when
|
|
77
|
+
* the same Capacitor binary serves multiple licensees that each
|
|
78
|
+
* receive their own token from your backend.
|
|
79
|
+
*
|
|
80
|
+
* If both `licenseToken` and `licenseKeyPath` are set, `licenseToken`
|
|
81
|
+
* wins on the native side.
|
|
82
|
+
*/
|
|
83
|
+
licenseToken?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* v3.0+ — distributed inference / device offload config. Wire-friendly
|
|
87
|
+
* subset of the JS-side `@dvai-bridge/core` `OffloadConfig` — function
|
|
88
|
+
* callbacks (`onPairingRequest`, `onOffload`, `customDiscovery`) are not
|
|
89
|
+
* representable across the Capacitor plugin boundary, so they're surfaced
|
|
90
|
+
* via {@link DVAIBridge.addListener}'s `"pairingRequest"` channel instead.
|
|
91
|
+
*
|
|
92
|
+
* See [the distributed-inference guide](https://dvai-bridge.deepvoiceai.co/guide/distributed-inference)
|
|
93
|
+
* for the full feature description.
|
|
94
|
+
*/
|
|
95
|
+
interface OffloadConfig {
|
|
96
|
+
/** Master switch. Default false; offload is opt-in at v3.0. */
|
|
97
|
+
enabled: boolean;
|
|
98
|
+
/** Run mDNS to discover LAN peers. Default: true when `enabled`. */
|
|
99
|
+
discoverLAN?: boolean;
|
|
100
|
+
/** Below this tok/s, look for a peer. Default: 10. */
|
|
101
|
+
minLocalCapability?: number;
|
|
102
|
+
/** Optional rendezvous-server URL — enables internet path if set. */
|
|
103
|
+
rendezvousUrl?: string;
|
|
104
|
+
/** Optional pre-known peers (skip discovery). */
|
|
105
|
+
knownPeers?: Peer[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Peer dvai-bridge instance discovered on the LAN or via rendezvous.
|
|
109
|
+
* Mirrors `@dvai-bridge/core` `Peer` 1:1 across SDKs. Surfaced via
|
|
110
|
+
* {@link PairingRequest.peer} and consumed via {@link OffloadConfig.knownPeers}.
|
|
111
|
+
*/
|
|
112
|
+
interface Peer {
|
|
113
|
+
/** Stable per-install device ID of the peer. */
|
|
114
|
+
deviceId: string;
|
|
115
|
+
/** Human-readable hint (iOS device name, hostname, etc.). */
|
|
116
|
+
deviceName: string;
|
|
117
|
+
/** Library SemVer the peer is running. */
|
|
118
|
+
dvaiVersion: string;
|
|
119
|
+
/** OpenAI-compatible base URL the peer's local server exposes. */
|
|
120
|
+
baseUrl: string;
|
|
121
|
+
/** Models the peer claims to have loaded right now. */
|
|
122
|
+
loadedModels: string[];
|
|
123
|
+
/** Peer-reported capability map: `{ modelId → tok/s }`. Advisory. */
|
|
124
|
+
capability: Record<string, number>;
|
|
125
|
+
/** Discovery source. */
|
|
126
|
+
via: "mdns" | "static" | "rendezvous" | "custom";
|
|
127
|
+
/** Whether the peer's URL uses TLS. */
|
|
128
|
+
secure: boolean;
|
|
129
|
+
/** Last-seen unix ms. */
|
|
130
|
+
lastSeenAt: number;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* A request for the consumer app to approve (or deny) pairing with a
|
|
134
|
+
* remote peer. Emitted on the `"pairingRequest"` channel
|
|
135
|
+
* (see {@link DVAIBridge.addListener}). The consumer calls
|
|
136
|
+
* {@link DVAIBridge.respondToPairing} with the {@link PairingRequest.id}
|
|
137
|
+
* and the user's decision.
|
|
138
|
+
*/
|
|
139
|
+
interface PairingRequest {
|
|
140
|
+
/** Stable id used to correlate the response via {@link DVAIBridge.respondToPairing}. */
|
|
141
|
+
id: string;
|
|
142
|
+
/** The peer requesting to pair. */
|
|
143
|
+
peer: Peer;
|
|
144
|
+
/**
|
|
145
|
+
* Convenience accessor for `peer.deviceName`. The migration guide and
|
|
146
|
+
* iOS `PairingRequest.peerDeviceName` use this name; surfacing it
|
|
147
|
+
* directly keeps consumer-facing code shorter.
|
|
148
|
+
*/
|
|
149
|
+
peerDeviceName: string;
|
|
150
|
+
/** Unix-ms deadline after which the pending request is auto-denied. */
|
|
151
|
+
expiresAt: number;
|
|
152
|
+
}
|
|
153
|
+
interface StartResult {
|
|
154
|
+
/** URL the host app passes to its OpenAI SDK. e.g. "http://127.0.0.1:38883/v1". */
|
|
155
|
+
baseUrl: string;
|
|
156
|
+
/** Bound HTTP port. */
|
|
157
|
+
port: number;
|
|
158
|
+
/** Resolved backend. */
|
|
159
|
+
backend: CapacitorBackend;
|
|
160
|
+
/** Model identifier echoed in /v1/models responses. */
|
|
161
|
+
modelId: string;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Lifecycle progress event emitted by `addProgressListener`.
|
|
165
|
+
*
|
|
166
|
+
* Phase semantics:
|
|
167
|
+
* - `"download"`: bytes streaming from a remote URL into the on-disk
|
|
168
|
+
* `.partial` file. `bytesReceived` / `bytesTotal` / `percent` populated.
|
|
169
|
+
* - `"verify"`: final sha256 check after download completes (or after a
|
|
170
|
+
* resumed `.partial` is rehashed). Usually no byte fields.
|
|
171
|
+
* - `"load"`: native plugin loading the model into engine memory
|
|
172
|
+
* (mmap / GPU upload / etc.). Usually no byte fields; some backends
|
|
173
|
+
* may report `percent`.
|
|
174
|
+
* - `"ready"`: terminal state, model is live and serving.
|
|
175
|
+
* - `"error"`: terminal state, populated `message` describes the failure.
|
|
176
|
+
*/
|
|
177
|
+
interface ProgressEvent {
|
|
178
|
+
phase: "download" | "verify" | "load" | "ready" | "error";
|
|
179
|
+
bytesReceived?: number;
|
|
180
|
+
bytesTotal?: number;
|
|
181
|
+
percent?: number;
|
|
182
|
+
message?: string;
|
|
183
|
+
}
|
|
184
|
+
interface StatusInfo {
|
|
185
|
+
running: boolean;
|
|
186
|
+
backend?: CapacitorBackend;
|
|
187
|
+
baseUrl?: string;
|
|
188
|
+
}
|
|
189
|
+
interface DownloadOptions {
|
|
190
|
+
/** Source URL (HTTP or HTTPS). */
|
|
191
|
+
url: string;
|
|
192
|
+
/** Required SHA-256 of the final file (lowercase hex). */
|
|
193
|
+
sha256: string;
|
|
194
|
+
/** Override destination filename. Default: URL basename. */
|
|
195
|
+
destFilename?: string;
|
|
196
|
+
/** Extra request headers (e.g. for HuggingFace gated repos). */
|
|
197
|
+
headers?: Record<string, string>;
|
|
198
|
+
/** Progress callback. Throttled to ~10 calls/sec. */
|
|
199
|
+
onProgress?: (e: ProgressEvent) => void;
|
|
200
|
+
}
|
|
201
|
+
interface CachedModelInfo {
|
|
202
|
+
filename: string;
|
|
203
|
+
path: string;
|
|
204
|
+
bytes: number;
|
|
205
|
+
sha256: string;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Native plugin interface — what each backend plugin (llama, foundation,
|
|
209
|
+
* mediapipe) implements on the native side. The JS shim calls these.
|
|
210
|
+
*/
|
|
211
|
+
interface NativePluginInterface {
|
|
212
|
+
start(options: StartOptions): Promise<StartResult>;
|
|
213
|
+
stop(): Promise<void>;
|
|
214
|
+
status(): Promise<StatusInfo>;
|
|
215
|
+
downloadModel(options: DownloadOptions): Promise<{
|
|
216
|
+
path: string;
|
|
217
|
+
cached: boolean;
|
|
218
|
+
}>;
|
|
219
|
+
listCachedModels(): Promise<{
|
|
220
|
+
models: CachedModelInfo[];
|
|
221
|
+
}>;
|
|
222
|
+
deleteCachedModel(options: {
|
|
223
|
+
filename: string;
|
|
224
|
+
}): Promise<void>;
|
|
225
|
+
cacheDir(): Promise<{
|
|
226
|
+
path: string;
|
|
227
|
+
}>;
|
|
228
|
+
addListener(eventName: "progress", listenerFunc: (e: ProgressEvent) => void): Promise<{
|
|
229
|
+
remove: () => Promise<void>;
|
|
230
|
+
}>;
|
|
231
|
+
/**
|
|
232
|
+
* v3.0+ — distributed inference. Subscribe to inbound pairing
|
|
233
|
+
* requests emitted when a remote peer wants to pair with this device.
|
|
234
|
+
* Consumers respond by calling {@link respondToPairing} with the
|
|
235
|
+
* request's `id` and a boolean decision.
|
|
236
|
+
*/
|
|
237
|
+
addListener(eventName: "pairingRequest", listenerFunc: (req: PairingRequest) => void): Promise<{
|
|
238
|
+
remove: () => Promise<void>;
|
|
239
|
+
}>;
|
|
240
|
+
/**
|
|
241
|
+
* v3.0+ — distributed inference. Resolve a pending {@link PairingRequest}
|
|
242
|
+
* by `id`. Idempotent — responding twice resolves cleanly.
|
|
243
|
+
*/
|
|
244
|
+
respondToPairing(options: {
|
|
245
|
+
requestId: string;
|
|
246
|
+
approved: boolean;
|
|
247
|
+
}): Promise<void>;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
declare const DVAIBridge: {
|
|
251
|
+
/** Start the embedded HTTP server with the chosen backend. Returns the URL. */
|
|
252
|
+
start(opts: StartOptions): Promise<StartResult>;
|
|
253
|
+
/** Stop the server and unload the model. Idempotent. */
|
|
254
|
+
stop(): Promise<void>;
|
|
255
|
+
/** Status snapshot — useful for UI reactivity. */
|
|
256
|
+
status(): Promise<StatusInfo>;
|
|
257
|
+
/** Subscribe to load/progress events. */
|
|
258
|
+
addProgressListener(cb: (e: ProgressEvent) => void): Promise<{
|
|
259
|
+
remove: () => Promise<void>;
|
|
260
|
+
}>;
|
|
261
|
+
/**
|
|
262
|
+
* v3.0+ — distributed inference. Subscribe to one of the bridge's
|
|
263
|
+
* named event channels. Currently:
|
|
264
|
+
*
|
|
265
|
+
* - `"pairingRequest"`: emitted when an inbound peer requests pairing.
|
|
266
|
+
* The handler receives a {@link PairingRequest}; respond via
|
|
267
|
+
* {@link respondToPairing}. Default behaviour without a listener
|
|
268
|
+
* is to deny inbound pairing requests.
|
|
269
|
+
*
|
|
270
|
+
* Must be called after a successful {@link start} — the listener is
|
|
271
|
+
* dispatched on the active backend plugin, which is established by
|
|
272
|
+
* `start()`. Calling before `start()` throws.
|
|
273
|
+
*/
|
|
274
|
+
addListener(eventName: "pairingRequest", cb: (req: PairingRequest) => void): Promise<{
|
|
275
|
+
remove: () => Promise<void>;
|
|
276
|
+
}>;
|
|
277
|
+
/**
|
|
278
|
+
* v3.0+ — distributed inference. Resolve a pending {@link PairingRequest}
|
|
279
|
+
* received via the `"pairingRequest"` event. Pass the request `id` and
|
|
280
|
+
* the user's decision; the native side records the decision and either
|
|
281
|
+
* lets the pairing proceed or rejects it.
|
|
282
|
+
*
|
|
283
|
+
* Idempotent — responding twice to the same `requestId` resolves
|
|
284
|
+
* cleanly on subsequent calls.
|
|
285
|
+
*/
|
|
286
|
+
respondToPairing(requestId: string, approved: boolean): Promise<void>;
|
|
287
|
+
/** Resumable, checksum-verified, app-data-cached download. */
|
|
288
|
+
downloadModel(opts: DownloadOptions): Promise<{
|
|
289
|
+
path: string;
|
|
290
|
+
cached: boolean;
|
|
291
|
+
}>;
|
|
292
|
+
listCachedModels(): Promise<CachedModelInfo[]>;
|
|
293
|
+
deleteCachedModel(filename: string): Promise<void>;
|
|
294
|
+
cacheDir(): Promise<string>;
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
export { type CachedModelInfo, type CapacitorBackend, DVAIBridge, type DownloadOptions, type NativePluginInterface, type OffloadConfig, type PairingRequest, type Peer, type ProgressEvent, type StartOptions, type StartResult, type StatusInfo };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @dvai-bridge/capacitor — public type definitions.
|
|
3
|
+
* These types are also imported by backend plugin packages
|
|
4
|
+
* (capacitor-llama, capacitor-foundation, capacitor-mediapipe)
|
|
5
|
+
* to keep the JS↔native contract consistent.
|
|
6
|
+
*/
|
|
7
|
+
type CapacitorBackend = "llama" | "foundation" | "mediapipe" | "mlx";
|
|
8
|
+
interface StartOptions {
|
|
9
|
+
/** Which native backend plugin to dispatch to. */
|
|
10
|
+
backend: CapacitorBackend;
|
|
11
|
+
/** Path to the GGUF model file (llama backend) or .task file (mediapipe). Not used by foundation. */
|
|
12
|
+
modelPath?: string;
|
|
13
|
+
/** Optional path to mmproj (vision projector) for llama vision models. */
|
|
14
|
+
mmprojPath?: string;
|
|
15
|
+
/** Llama: GPU layers offloaded (default 99 = max). */
|
|
16
|
+
gpuLayers?: number;
|
|
17
|
+
/** Llama / mediapipe: context window. */
|
|
18
|
+
contextSize?: number;
|
|
19
|
+
/** Llama: CPU threads. */
|
|
20
|
+
threads?: number;
|
|
21
|
+
/** Llama: initialize in embedding mode (chat will not work; embeddings will). */
|
|
22
|
+
embeddingMode?: boolean;
|
|
23
|
+
/** HTTP server base port; retries +1 up to httpMaxPortAttempts on EADDRINUSE. Default 38883. */
|
|
24
|
+
httpBasePort?: number;
|
|
25
|
+
/** Default 16. */
|
|
26
|
+
httpMaxPortAttempts?: number;
|
|
27
|
+
/** CORS Access-Control-Allow-Origin. "*", a single origin, or a list. Default "*". */
|
|
28
|
+
corsOrigin?: string | string[];
|
|
29
|
+
/** Auto-unload the model when OS emits low-memory warning. Default false. */
|
|
30
|
+
autoUnloadOnLowMemory?: boolean;
|
|
31
|
+
/** Native log verbosity. Default "info". */
|
|
32
|
+
logLevel?: "silent" | "info" | "debug";
|
|
33
|
+
/**
|
|
34
|
+
* v3.0+ — distributed inference / device offload.
|
|
35
|
+
*
|
|
36
|
+
* When `offload.enabled` is true, the native side runs mDNS discovery
|
|
37
|
+
* (and optionally a rendezvous WebSocket) to find peer dvai-bridge
|
|
38
|
+
* instances and offloads inference requests when the local device
|
|
39
|
+
* can't serve the model fast enough. See
|
|
40
|
+
* [the distributed-inference guide](https://dvai-bridge.deepvoiceai.co/guide/distributed-inference)
|
|
41
|
+
* for the full contract.
|
|
42
|
+
*
|
|
43
|
+
* Pairing-request UI is surfaced via the `"pairingRequest"` event
|
|
44
|
+
* (see {@link DVAIBridge.addListener}); the function callback in the
|
|
45
|
+
* JS-side `OffloadConfig.onPairingRequest` cannot cross the Capacitor
|
|
46
|
+
* plugin boundary, so consumers use the event surface instead.
|
|
47
|
+
*/
|
|
48
|
+
offload?: OffloadConfig;
|
|
49
|
+
/**
|
|
50
|
+
* v3.2.2+ — path (or fetchable URL) to your DVAI-Bridge license JWT.
|
|
51
|
+
*
|
|
52
|
+
* The Capacitor bridge forwards this string to the native backend
|
|
53
|
+
* plugin (`DVAIBridgeLlama`, `DVAIBridgeFoundation`,
|
|
54
|
+
* `DVAIBridgeMediaPipe`, `DVAIBridgeMLX`). Each native validator runs
|
|
55
|
+
* its own offline JWT verification (signature + expiry + audience +
|
|
56
|
+
* platform binding) against the iOS bundle identifier or Android
|
|
57
|
+
* package name — see `@dvai-bridge/core/license` for the canonical
|
|
58
|
+
* JWT format. The validators are authoritative; this JS-side
|
|
59
|
+
* forwarding is the only plumbing needed.
|
|
60
|
+
*
|
|
61
|
+
* Override priority on the native side (matches `DVAIConfig`):
|
|
62
|
+
* 1. `licenseToken` (below) — inline JWT string, highest priority
|
|
63
|
+
* 2. `licenseKeyPath` (this field) — explicit path or URL
|
|
64
|
+
* 3. Platform default locations + env-var fallbacks
|
|
65
|
+
*
|
|
66
|
+
* Free-tier behaviour (no license, expired, invalid) is enforced by
|
|
67
|
+
* the native validator and surfaced via the platform's standard
|
|
68
|
+
* error channel. The JS layer never inspects this value beyond
|
|
69
|
+
* forwarding it.
|
|
70
|
+
*/
|
|
71
|
+
licenseKeyPath?: string;
|
|
72
|
+
/**
|
|
73
|
+
* v3.2.2+ — inline DVAI-Bridge license JWT (the full token string).
|
|
74
|
+
*
|
|
75
|
+
* Use when injecting the license via runtime config rather than a
|
|
76
|
+
* bundled file — typical for over-the-air license refresh or when
|
|
77
|
+
* the same Capacitor binary serves multiple licensees that each
|
|
78
|
+
* receive their own token from your backend.
|
|
79
|
+
*
|
|
80
|
+
* If both `licenseToken` and `licenseKeyPath` are set, `licenseToken`
|
|
81
|
+
* wins on the native side.
|
|
82
|
+
*/
|
|
83
|
+
licenseToken?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* v3.0+ — distributed inference / device offload config. Wire-friendly
|
|
87
|
+
* subset of the JS-side `@dvai-bridge/core` `OffloadConfig` — function
|
|
88
|
+
* callbacks (`onPairingRequest`, `onOffload`, `customDiscovery`) are not
|
|
89
|
+
* representable across the Capacitor plugin boundary, so they're surfaced
|
|
90
|
+
* via {@link DVAIBridge.addListener}'s `"pairingRequest"` channel instead.
|
|
91
|
+
*
|
|
92
|
+
* See [the distributed-inference guide](https://dvai-bridge.deepvoiceai.co/guide/distributed-inference)
|
|
93
|
+
* for the full feature description.
|
|
94
|
+
*/
|
|
95
|
+
interface OffloadConfig {
|
|
96
|
+
/** Master switch. Default false; offload is opt-in at v3.0. */
|
|
97
|
+
enabled: boolean;
|
|
98
|
+
/** Run mDNS to discover LAN peers. Default: true when `enabled`. */
|
|
99
|
+
discoverLAN?: boolean;
|
|
100
|
+
/** Below this tok/s, look for a peer. Default: 10. */
|
|
101
|
+
minLocalCapability?: number;
|
|
102
|
+
/** Optional rendezvous-server URL — enables internet path if set. */
|
|
103
|
+
rendezvousUrl?: string;
|
|
104
|
+
/** Optional pre-known peers (skip discovery). */
|
|
105
|
+
knownPeers?: Peer[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Peer dvai-bridge instance discovered on the LAN or via rendezvous.
|
|
109
|
+
* Mirrors `@dvai-bridge/core` `Peer` 1:1 across SDKs. Surfaced via
|
|
110
|
+
* {@link PairingRequest.peer} and consumed via {@link OffloadConfig.knownPeers}.
|
|
111
|
+
*/
|
|
112
|
+
interface Peer {
|
|
113
|
+
/** Stable per-install device ID of the peer. */
|
|
114
|
+
deviceId: string;
|
|
115
|
+
/** Human-readable hint (iOS device name, hostname, etc.). */
|
|
116
|
+
deviceName: string;
|
|
117
|
+
/** Library SemVer the peer is running. */
|
|
118
|
+
dvaiVersion: string;
|
|
119
|
+
/** OpenAI-compatible base URL the peer's local server exposes. */
|
|
120
|
+
baseUrl: string;
|
|
121
|
+
/** Models the peer claims to have loaded right now. */
|
|
122
|
+
loadedModels: string[];
|
|
123
|
+
/** Peer-reported capability map: `{ modelId → tok/s }`. Advisory. */
|
|
124
|
+
capability: Record<string, number>;
|
|
125
|
+
/** Discovery source. */
|
|
126
|
+
via: "mdns" | "static" | "rendezvous" | "custom";
|
|
127
|
+
/** Whether the peer's URL uses TLS. */
|
|
128
|
+
secure: boolean;
|
|
129
|
+
/** Last-seen unix ms. */
|
|
130
|
+
lastSeenAt: number;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* A request for the consumer app to approve (or deny) pairing with a
|
|
134
|
+
* remote peer. Emitted on the `"pairingRequest"` channel
|
|
135
|
+
* (see {@link DVAIBridge.addListener}). The consumer calls
|
|
136
|
+
* {@link DVAIBridge.respondToPairing} with the {@link PairingRequest.id}
|
|
137
|
+
* and the user's decision.
|
|
138
|
+
*/
|
|
139
|
+
interface PairingRequest {
|
|
140
|
+
/** Stable id used to correlate the response via {@link DVAIBridge.respondToPairing}. */
|
|
141
|
+
id: string;
|
|
142
|
+
/** The peer requesting to pair. */
|
|
143
|
+
peer: Peer;
|
|
144
|
+
/**
|
|
145
|
+
* Convenience accessor for `peer.deviceName`. The migration guide and
|
|
146
|
+
* iOS `PairingRequest.peerDeviceName` use this name; surfacing it
|
|
147
|
+
* directly keeps consumer-facing code shorter.
|
|
148
|
+
*/
|
|
149
|
+
peerDeviceName: string;
|
|
150
|
+
/** Unix-ms deadline after which the pending request is auto-denied. */
|
|
151
|
+
expiresAt: number;
|
|
152
|
+
}
|
|
153
|
+
interface StartResult {
|
|
154
|
+
/** URL the host app passes to its OpenAI SDK. e.g. "http://127.0.0.1:38883/v1". */
|
|
155
|
+
baseUrl: string;
|
|
156
|
+
/** Bound HTTP port. */
|
|
157
|
+
port: number;
|
|
158
|
+
/** Resolved backend. */
|
|
159
|
+
backend: CapacitorBackend;
|
|
160
|
+
/** Model identifier echoed in /v1/models responses. */
|
|
161
|
+
modelId: string;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Lifecycle progress event emitted by `addProgressListener`.
|
|
165
|
+
*
|
|
166
|
+
* Phase semantics:
|
|
167
|
+
* - `"download"`: bytes streaming from a remote URL into the on-disk
|
|
168
|
+
* `.partial` file. `bytesReceived` / `bytesTotal` / `percent` populated.
|
|
169
|
+
* - `"verify"`: final sha256 check after download completes (or after a
|
|
170
|
+
* resumed `.partial` is rehashed). Usually no byte fields.
|
|
171
|
+
* - `"load"`: native plugin loading the model into engine memory
|
|
172
|
+
* (mmap / GPU upload / etc.). Usually no byte fields; some backends
|
|
173
|
+
* may report `percent`.
|
|
174
|
+
* - `"ready"`: terminal state, model is live and serving.
|
|
175
|
+
* - `"error"`: terminal state, populated `message` describes the failure.
|
|
176
|
+
*/
|
|
177
|
+
interface ProgressEvent {
|
|
178
|
+
phase: "download" | "verify" | "load" | "ready" | "error";
|
|
179
|
+
bytesReceived?: number;
|
|
180
|
+
bytesTotal?: number;
|
|
181
|
+
percent?: number;
|
|
182
|
+
message?: string;
|
|
183
|
+
}
|
|
184
|
+
interface StatusInfo {
|
|
185
|
+
running: boolean;
|
|
186
|
+
backend?: CapacitorBackend;
|
|
187
|
+
baseUrl?: string;
|
|
188
|
+
}
|
|
189
|
+
interface DownloadOptions {
|
|
190
|
+
/** Source URL (HTTP or HTTPS). */
|
|
191
|
+
url: string;
|
|
192
|
+
/** Required SHA-256 of the final file (lowercase hex). */
|
|
193
|
+
sha256: string;
|
|
194
|
+
/** Override destination filename. Default: URL basename. */
|
|
195
|
+
destFilename?: string;
|
|
196
|
+
/** Extra request headers (e.g. for HuggingFace gated repos). */
|
|
197
|
+
headers?: Record<string, string>;
|
|
198
|
+
/** Progress callback. Throttled to ~10 calls/sec. */
|
|
199
|
+
onProgress?: (e: ProgressEvent) => void;
|
|
200
|
+
}
|
|
201
|
+
interface CachedModelInfo {
|
|
202
|
+
filename: string;
|
|
203
|
+
path: string;
|
|
204
|
+
bytes: number;
|
|
205
|
+
sha256: string;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Native plugin interface — what each backend plugin (llama, foundation,
|
|
209
|
+
* mediapipe) implements on the native side. The JS shim calls these.
|
|
210
|
+
*/
|
|
211
|
+
interface NativePluginInterface {
|
|
212
|
+
start(options: StartOptions): Promise<StartResult>;
|
|
213
|
+
stop(): Promise<void>;
|
|
214
|
+
status(): Promise<StatusInfo>;
|
|
215
|
+
downloadModel(options: DownloadOptions): Promise<{
|
|
216
|
+
path: string;
|
|
217
|
+
cached: boolean;
|
|
218
|
+
}>;
|
|
219
|
+
listCachedModels(): Promise<{
|
|
220
|
+
models: CachedModelInfo[];
|
|
221
|
+
}>;
|
|
222
|
+
deleteCachedModel(options: {
|
|
223
|
+
filename: string;
|
|
224
|
+
}): Promise<void>;
|
|
225
|
+
cacheDir(): Promise<{
|
|
226
|
+
path: string;
|
|
227
|
+
}>;
|
|
228
|
+
addListener(eventName: "progress", listenerFunc: (e: ProgressEvent) => void): Promise<{
|
|
229
|
+
remove: () => Promise<void>;
|
|
230
|
+
}>;
|
|
231
|
+
/**
|
|
232
|
+
* v3.0+ — distributed inference. Subscribe to inbound pairing
|
|
233
|
+
* requests emitted when a remote peer wants to pair with this device.
|
|
234
|
+
* Consumers respond by calling {@link respondToPairing} with the
|
|
235
|
+
* request's `id` and a boolean decision.
|
|
236
|
+
*/
|
|
237
|
+
addListener(eventName: "pairingRequest", listenerFunc: (req: PairingRequest) => void): Promise<{
|
|
238
|
+
remove: () => Promise<void>;
|
|
239
|
+
}>;
|
|
240
|
+
/**
|
|
241
|
+
* v3.0+ — distributed inference. Resolve a pending {@link PairingRequest}
|
|
242
|
+
* by `id`. Idempotent — responding twice resolves cleanly.
|
|
243
|
+
*/
|
|
244
|
+
respondToPairing(options: {
|
|
245
|
+
requestId: string;
|
|
246
|
+
approved: boolean;
|
|
247
|
+
}): Promise<void>;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
declare const DVAIBridge: {
|
|
251
|
+
/** Start the embedded HTTP server with the chosen backend. Returns the URL. */
|
|
252
|
+
start(opts: StartOptions): Promise<StartResult>;
|
|
253
|
+
/** Stop the server and unload the model. Idempotent. */
|
|
254
|
+
stop(): Promise<void>;
|
|
255
|
+
/** Status snapshot — useful for UI reactivity. */
|
|
256
|
+
status(): Promise<StatusInfo>;
|
|
257
|
+
/** Subscribe to load/progress events. */
|
|
258
|
+
addProgressListener(cb: (e: ProgressEvent) => void): Promise<{
|
|
259
|
+
remove: () => Promise<void>;
|
|
260
|
+
}>;
|
|
261
|
+
/**
|
|
262
|
+
* v3.0+ — distributed inference. Subscribe to one of the bridge's
|
|
263
|
+
* named event channels. Currently:
|
|
264
|
+
*
|
|
265
|
+
* - `"pairingRequest"`: emitted when an inbound peer requests pairing.
|
|
266
|
+
* The handler receives a {@link PairingRequest}; respond via
|
|
267
|
+
* {@link respondToPairing}. Default behaviour without a listener
|
|
268
|
+
* is to deny inbound pairing requests.
|
|
269
|
+
*
|
|
270
|
+
* Must be called after a successful {@link start} — the listener is
|
|
271
|
+
* dispatched on the active backend plugin, which is established by
|
|
272
|
+
* `start()`. Calling before `start()` throws.
|
|
273
|
+
*/
|
|
274
|
+
addListener(eventName: "pairingRequest", cb: (req: PairingRequest) => void): Promise<{
|
|
275
|
+
remove: () => Promise<void>;
|
|
276
|
+
}>;
|
|
277
|
+
/**
|
|
278
|
+
* v3.0+ — distributed inference. Resolve a pending {@link PairingRequest}
|
|
279
|
+
* received via the `"pairingRequest"` event. Pass the request `id` and
|
|
280
|
+
* the user's decision; the native side records the decision and either
|
|
281
|
+
* lets the pairing proceed or rejects it.
|
|
282
|
+
*
|
|
283
|
+
* Idempotent — responding twice to the same `requestId` resolves
|
|
284
|
+
* cleanly on subsequent calls.
|
|
285
|
+
*/
|
|
286
|
+
respondToPairing(requestId: string, approved: boolean): Promise<void>;
|
|
287
|
+
/** Resumable, checksum-verified, app-data-cached download. */
|
|
288
|
+
downloadModel(opts: DownloadOptions): Promise<{
|
|
289
|
+
path: string;
|
|
290
|
+
cached: boolean;
|
|
291
|
+
}>;
|
|
292
|
+
listCachedModels(): Promise<CachedModelInfo[]>;
|
|
293
|
+
deleteCachedModel(filename: string): Promise<void>;
|
|
294
|
+
cacheDir(): Promise<string>;
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
export { type CachedModelInfo, type CapacitorBackend, DVAIBridge, type DownloadOptions, type NativePluginInterface, type OffloadConfig, type PairingRequest, type Peer, type ProgressEvent, type StartOptions, type StartResult, type StatusInfo };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// src/dispatch.ts
|
|
2
|
+
import { Capacitor, registerPlugin } from "@capacitor/core";
|
|
3
|
+
var PLUGIN_NAME_BY_BACKEND = {
|
|
4
|
+
llama: "DVAIBridgeLlama",
|
|
5
|
+
foundation: "DVAIBridgeFoundation",
|
|
6
|
+
mediapipe: "DVAIBridgeMediaPipe",
|
|
7
|
+
mlx: "DVAIBridgeMLX"
|
|
8
|
+
};
|
|
9
|
+
var activePlugin = null;
|
|
10
|
+
var activeBackend = null;
|
|
11
|
+
function pluginFor(backend) {
|
|
12
|
+
const name = PLUGIN_NAME_BY_BACKEND[backend];
|
|
13
|
+
return registerPlugin(name);
|
|
14
|
+
}
|
|
15
|
+
function isPluginNotImplementedError(err) {
|
|
16
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
17
|
+
return /not implemented|not available|UNIMPLEMENTED/i.test(msg);
|
|
18
|
+
}
|
|
19
|
+
function assertBackendCompatibleWithPlatform(backend) {
|
|
20
|
+
if (backend === "foundation" || backend === "mlx") {
|
|
21
|
+
let platform;
|
|
22
|
+
try {
|
|
23
|
+
platform = Capacitor?.getPlatform?.();
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
26
|
+
if (platform === "android") {
|
|
27
|
+
const label = backend === "foundation" ? "Apple Foundation Models" : "MLX";
|
|
28
|
+
throw new Error(
|
|
29
|
+
`[DVAI] ${label} is iOS-only. Use backend: "llama" or "mediapipe" on Android.`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
var dispatch = {
|
|
35
|
+
async start(opts) {
|
|
36
|
+
assertBackendCompatibleWithPlatform(opts.backend);
|
|
37
|
+
const native = pluginFor(opts.backend);
|
|
38
|
+
try {
|
|
39
|
+
const result = await native.start(opts);
|
|
40
|
+
activePlugin = native;
|
|
41
|
+
activeBackend = opts.backend;
|
|
42
|
+
return result;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (isPluginNotImplementedError(err)) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`[DVAI] Backend "${opts.backend}" selected but the corresponding plugin is not installed. Run: npm install @dvai-bridge/capacitor-${opts.backend} && npx cap sync`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
async stop() {
|
|
53
|
+
if (!activePlugin) return;
|
|
54
|
+
try {
|
|
55
|
+
await activePlugin.stop();
|
|
56
|
+
} finally {
|
|
57
|
+
activePlugin = null;
|
|
58
|
+
activeBackend = null;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
async status() {
|
|
62
|
+
if (!activePlugin) return { running: false };
|
|
63
|
+
return activePlugin.status();
|
|
64
|
+
},
|
|
65
|
+
__reset() {
|
|
66
|
+
activePlugin = null;
|
|
67
|
+
activeBackend = null;
|
|
68
|
+
},
|
|
69
|
+
__activePlugin() {
|
|
70
|
+
return activePlugin;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/index.ts
|
|
75
|
+
var DVAIBridge = {
|
|
76
|
+
/** Start the embedded HTTP server with the chosen backend. Returns the URL. */
|
|
77
|
+
async start(opts) {
|
|
78
|
+
return dispatch.start(opts);
|
|
79
|
+
},
|
|
80
|
+
/** Stop the server and unload the model. Idempotent. */
|
|
81
|
+
async stop() {
|
|
82
|
+
return dispatch.stop();
|
|
83
|
+
},
|
|
84
|
+
/** Status snapshot — useful for UI reactivity. */
|
|
85
|
+
async status() {
|
|
86
|
+
return dispatch.status();
|
|
87
|
+
},
|
|
88
|
+
/** Subscribe to load/progress events. */
|
|
89
|
+
async addProgressListener(cb) {
|
|
90
|
+
const native = dispatch.__activePlugin();
|
|
91
|
+
if (!native) {
|
|
92
|
+
throw new Error("[DVAI] addProgressListener called before start()");
|
|
93
|
+
}
|
|
94
|
+
return native.addListener("progress", cb);
|
|
95
|
+
},
|
|
96
|
+
/**
|
|
97
|
+
* v3.0+ — distributed inference. Subscribe to one of the bridge's
|
|
98
|
+
* named event channels. Currently:
|
|
99
|
+
*
|
|
100
|
+
* - `"pairingRequest"`: emitted when an inbound peer requests pairing.
|
|
101
|
+
* The handler receives a {@link PairingRequest}; respond via
|
|
102
|
+
* {@link respondToPairing}. Default behaviour without a listener
|
|
103
|
+
* is to deny inbound pairing requests.
|
|
104
|
+
*
|
|
105
|
+
* Must be called after a successful {@link start} — the listener is
|
|
106
|
+
* dispatched on the active backend plugin, which is established by
|
|
107
|
+
* `start()`. Calling before `start()` throws.
|
|
108
|
+
*/
|
|
109
|
+
async addListener(eventName, cb) {
|
|
110
|
+
if (eventName !== "pairingRequest") {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`[DVAI] addListener: unknown event name "${eventName}". Valid: "pairingRequest".`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const native = dispatch.__activePlugin();
|
|
116
|
+
if (!native) {
|
|
117
|
+
throw new Error("[DVAI] addListener called before start()");
|
|
118
|
+
}
|
|
119
|
+
return native.addListener("pairingRequest", cb);
|
|
120
|
+
},
|
|
121
|
+
/**
|
|
122
|
+
* v3.0+ — distributed inference. Resolve a pending {@link PairingRequest}
|
|
123
|
+
* received via the `"pairingRequest"` event. Pass the request `id` and
|
|
124
|
+
* the user's decision; the native side records the decision and either
|
|
125
|
+
* lets the pairing proceed or rejects it.
|
|
126
|
+
*
|
|
127
|
+
* Idempotent — responding twice to the same `requestId` resolves
|
|
128
|
+
* cleanly on subsequent calls.
|
|
129
|
+
*/
|
|
130
|
+
async respondToPairing(requestId, approved) {
|
|
131
|
+
const native = dispatch.__activePlugin();
|
|
132
|
+
if (!native) {
|
|
133
|
+
throw new Error("[DVAI] respondToPairing called before start()");
|
|
134
|
+
}
|
|
135
|
+
await native.respondToPairing({ requestId, approved });
|
|
136
|
+
},
|
|
137
|
+
/** Resumable, checksum-verified, app-data-cached download. */
|
|
138
|
+
async downloadModel(opts) {
|
|
139
|
+
const native = await modelManagementPlugin();
|
|
140
|
+
return native.downloadModel(opts);
|
|
141
|
+
},
|
|
142
|
+
async listCachedModels() {
|
|
143
|
+
const native = await modelManagementPlugin();
|
|
144
|
+
const result = await native.listCachedModels();
|
|
145
|
+
return result.models;
|
|
146
|
+
},
|
|
147
|
+
async deleteCachedModel(filename) {
|
|
148
|
+
const native = await modelManagementPlugin();
|
|
149
|
+
await native.deleteCachedModel({ filename });
|
|
150
|
+
},
|
|
151
|
+
async cacheDir() {
|
|
152
|
+
const native = await modelManagementPlugin();
|
|
153
|
+
const result = await native.cacheDir();
|
|
154
|
+
return result.path;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
async function modelManagementPlugin() {
|
|
158
|
+
const active = dispatch.__activePlugin();
|
|
159
|
+
if (active) return active;
|
|
160
|
+
const { registerPlugin: registerPlugin2 } = await import("@capacitor/core");
|
|
161
|
+
return registerPlugin2("DVAIBridgeLlama");
|
|
162
|
+
}
|
|
163
|
+
export {
|
|
164
|
+
DVAIBridge
|
|
165
|
+
};
|
|
166
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dispatch.ts","../src/index.ts"],"sourcesContent":["import { Capacitor, registerPlugin } from \"@capacitor/core\";\r\nimport type {\r\n CapacitorBackend,\r\n NativePluginInterface,\r\n StartOptions,\r\n StartResult,\r\n StatusInfo,\r\n} from \"./types.js\";\r\n\r\nconst PLUGIN_NAME_BY_BACKEND: Record<CapacitorBackend, string> = {\r\n llama: \"DVAIBridgeLlama\",\r\n foundation: \"DVAIBridgeFoundation\",\r\n mediapipe: \"DVAIBridgeMediaPipe\",\r\n mlx: \"DVAIBridgeMLX\",\r\n};\r\n\r\nlet activePlugin: NativePluginInterface | null = null;\r\nlet activeBackend: CapacitorBackend | null = null;\r\n\r\nfunction pluginFor(backend: CapacitorBackend): NativePluginInterface {\r\n const name = PLUGIN_NAME_BY_BACKEND[backend];\r\n return registerPlugin<NativePluginInterface>(name);\r\n}\r\n\r\nfunction isPluginNotImplementedError(err: unknown): boolean {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n return /not implemented|not available|UNIMPLEMENTED/i.test(msg);\r\n}\r\n\r\n/**\r\n * Apple Foundation Models is iOS-only. On Android, no amount of plugin-\r\n * install will help — surface a clear error rather than the generic\r\n * \"install the plugin\" wording from the not-implemented fallback.\r\n */\r\nfunction assertBackendCompatibleWithPlatform(backend: CapacitorBackend): void {\r\n if (backend === \"foundation\" || backend === \"mlx\") {\r\n let platform: string | undefined;\r\n try {\r\n platform = Capacitor?.getPlatform?.();\r\n } catch {\r\n // Capacitor.getPlatform() can throw in non-runtime contexts (SSR,\r\n // unit tests without the Capacitor shim). Fall through silently —\r\n // the native call will then surface its own error.\r\n }\r\n if (platform === \"android\") {\r\n const label = backend === \"foundation\" ? \"Apple Foundation Models\" : \"MLX\";\r\n throw new Error(\r\n `[DVAI] ${label} is iOS-only. Use backend: \"llama\" or \"mediapipe\" on Android.`,\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport const dispatch = {\r\n async start(opts: StartOptions): Promise<StartResult> {\r\n assertBackendCompatibleWithPlatform(opts.backend);\r\n const native = pluginFor(opts.backend);\r\n try {\r\n const result = await native.start(opts);\r\n activePlugin = native;\r\n activeBackend = opts.backend;\r\n return result;\r\n } catch (err) {\r\n if (isPluginNotImplementedError(err)) {\r\n throw new Error(\r\n `[DVAI] Backend \"${opts.backend}\" selected but the corresponding plugin is not installed. ` +\r\n `Run: npm install @dvai-bridge/capacitor-${opts.backend} && npx cap sync`,\r\n );\r\n }\r\n throw err;\r\n }\r\n },\r\n\r\n async stop(): Promise<void> {\r\n if (!activePlugin) return;\r\n try {\r\n await activePlugin.stop();\r\n } finally {\r\n activePlugin = null;\r\n activeBackend = null;\r\n }\r\n },\r\n\r\n async status(): Promise<StatusInfo> {\r\n if (!activePlugin) return { running: false };\r\n return activePlugin.status();\r\n },\r\n\r\n __reset(): void {\r\n activePlugin = null;\r\n activeBackend = null;\r\n },\r\n\r\n __activePlugin(): NativePluginInterface | null {\r\n return activePlugin;\r\n },\r\n};\r\n","import { dispatch } from \"./dispatch.js\";\nimport type {\n StartOptions,\n StartResult,\n StatusInfo,\n ProgressEvent,\n DownloadOptions,\n CachedModelInfo,\n CapacitorBackend,\n NativePluginInterface,\n OffloadConfig,\n PairingRequest,\n Peer,\n} from \"./types.js\";\n\nexport type {\n CapacitorBackend,\n StartOptions,\n StartResult,\n StatusInfo,\n ProgressEvent,\n DownloadOptions,\n CachedModelInfo,\n NativePluginInterface,\n OffloadConfig,\n PairingRequest,\n Peer,\n};\n\nexport const DVAIBridge = {\n /** Start the embedded HTTP server with the chosen backend. Returns the URL. */\n async start(opts: StartOptions): Promise<StartResult> {\n return dispatch.start(opts);\n },\n\n /** Stop the server and unload the model. Idempotent. */\n async stop(): Promise<void> {\n return dispatch.stop();\n },\n\n /** Status snapshot — useful for UI reactivity. */\n async status(): Promise<StatusInfo> {\n return dispatch.status();\n },\n\n /** Subscribe to load/progress events. */\n async addProgressListener(\n cb: (e: ProgressEvent) => void,\n ): Promise<{ remove: () => Promise<void> }> {\n const native = dispatch.__activePlugin();\n if (!native) {\n throw new Error(\"[DVAI] addProgressListener called before start()\");\n }\n return native.addListener(\"progress\", cb);\n },\n\n /**\n * v3.0+ — distributed inference. Subscribe to one of the bridge's\n * named event channels. Currently:\n *\n * - `\"pairingRequest\"`: emitted when an inbound peer requests pairing.\n * The handler receives a {@link PairingRequest}; respond via\n * {@link respondToPairing}. Default behaviour without a listener\n * is to deny inbound pairing requests.\n *\n * Must be called after a successful {@link start} — the listener is\n * dispatched on the active backend plugin, which is established by\n * `start()`. Calling before `start()` throws.\n */\n async addListener(\n eventName: \"pairingRequest\",\n cb: (req: PairingRequest) => void,\n ): Promise<{ remove: () => Promise<void> }> {\n if (eventName !== \"pairingRequest\") {\n throw new Error(\n `[DVAI] addListener: unknown event name \"${eventName}\". Valid: \"pairingRequest\".`,\n );\n }\n const native = dispatch.__activePlugin();\n if (!native) {\n throw new Error(\"[DVAI] addListener called before start()\");\n }\n return native.addListener(\"pairingRequest\", cb);\n },\n\n /**\n * v3.0+ — distributed inference. Resolve a pending {@link PairingRequest}\n * received via the `\"pairingRequest\"` event. Pass the request `id` and\n * the user's decision; the native side records the decision and either\n * lets the pairing proceed or rejects it.\n *\n * Idempotent — responding twice to the same `requestId` resolves\n * cleanly on subsequent calls.\n */\n async respondToPairing(requestId: string, approved: boolean): Promise<void> {\n const native = dispatch.__activePlugin();\n if (!native) {\n throw new Error(\"[DVAI] respondToPairing called before start()\");\n }\n await native.respondToPairing({ requestId, approved });\n },\n\n /** Resumable, checksum-verified, app-data-cached download. */\n async downloadModel(opts: DownloadOptions): Promise<{ path: string; cached: boolean }> {\n const native = await modelManagementPlugin();\n return native.downloadModel(opts);\n },\n\n async listCachedModels(): Promise<CachedModelInfo[]> {\n const native = await modelManagementPlugin();\n const result = await native.listCachedModels();\n return result.models;\n },\n\n async deleteCachedModel(filename: string): Promise<void> {\n const native = await modelManagementPlugin();\n await native.deleteCachedModel({ filename });\n },\n\n async cacheDir(): Promise<string> {\n const native = await modelManagementPlugin();\n const result = await native.cacheDir();\n return result.path;\n },\n};\n\n/**\n * Resolve the plugin that owns model-management methods (downloadModel,\n * listCachedModels, deleteCachedModel, cacheDir). In Phase 1, only the\n * `llama` backend implements these — `foundation` rejects (Apple manages\n * models internally) and `mediapipe` rejects (developer-managed paths).\n *\n * If a non-llama plugin is currently active, model management still routes\n * to the llama plugin (caller can install + use it without `start()`).\n * The native side may reject if the plugin isn't installed; the resulting\n * Capacitor \"plugin not implemented\" error is the right surface.\n */\nasync function modelManagementPlugin(): Promise<NativePluginInterface> {\n const active = dispatch.__activePlugin();\n if (active) return active;\n const { registerPlugin } = await import(\"@capacitor/core\");\n return registerPlugin<NativePluginInterface>(\"DVAIBridgeLlama\");\n}\n"],"mappings":"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","names":["registerPlugin"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dvai-bridge/capacitor",
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"registry": "https://registry.npmjs.org/",
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"description": "JS routing shim for DVAI-Bridge Capacitor backend plugins. Dispatches to capacitor-llama, capacitor-foundation, or capacitor-mediapipe.",
|
|
9
|
+
"main": "dist/index.cjs",
|
|
10
|
+
"module": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"keywords": [
|
|
26
|
+
"dvai-bridge",
|
|
27
|
+
"capacitor",
|
|
28
|
+
"local-ai",
|
|
29
|
+
"openai-compatible"
|
|
30
|
+
],
|
|
31
|
+
"author": "Deep Chakraborty <https://github.com/dk013>",
|
|
32
|
+
"license": "Custom (See LICENSE)",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/westenets/dvai-bridge.git"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@capacitor/core": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"dev": "tsup --watch"
|
|
43
|
+
}
|
|
44
|
+
}
|