@elizaos/app-core 2.0.0-beta.1 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/platforms/electrobun/native/macos/window-effects.mm +103 -0
- package/platforms/electrobun/package.json +9 -0
- package/platforms/electrobun/src/__stubs__/bun-ffi.ts +16 -0
- package/platforms/electrobun/src/libMacWindowEffects.dylib +0 -0
- package/platforms/electrobun/src/native/agent.ts +74 -3
- package/platforms/electrobun/src/native/desktop.ts +39 -6
- package/platforms/electrobun/src/native/mac-window-effects.ts +61 -1
- package/platforms/electrobun/src/native/permissions-shared.ts +3 -2
- package/platforms/electrobun/src/native/permissions.ts +11 -6
- package/platforms/electrobun/src/rpc-handlers.ts +7 -0
- package/platforms/electrobun/src/rpc-schema.ts +39 -4
- package/platforms/electrobun/src/runtime-permissions.ts +7 -1
- package/runtime/ensure-local-inference-handler.d.ts +1 -0
- package/runtime/ensure-local-inference-handler.d.ts.map +1 -1
- package/runtime/ensure-local-inference-handler.js +9 -0
- package/runtime/mode/remote-forwarder.d.ts.map +1 -1
- package/runtime/mode/remote-forwarder.js +1 -1
- package/runtime/mode/runtime-mode.d.ts +20 -2
- package/runtime/mode/runtime-mode.d.ts.map +1 -1
- package/runtime/mode/runtime-mode.js +69 -1
- package/scripts/aosp/stage-default-models.mjs +2 -2
- package/scripts/build-llama-cpp-dflash.mjs +75 -40
- package/scripts/kernel-patches/metal-kernels.mjs +357 -337
- package/scripts/lib/read-app-identity.mjs +5 -1
- package/services/local-inference/catalog.d.ts +2 -1
- package/services/local-inference/catalog.d.ts.map +1 -1
- package/services/local-inference/catalog.js +131 -12
- package/services/local-inference/downloader.d.ts +2 -0
- package/services/local-inference/downloader.d.ts.map +1 -1
- package/services/local-inference/downloader.js +300 -1
- package/services/local-inference/manifest/validator.d.ts.map +1 -1
- package/services/local-inference/manifest/validator.js +48 -0
- package/services/local-inference/providers.d.ts +1 -1
- package/services/local-inference/providers.js +6 -6
- package/services/local-inference/registry.d.ts.map +1 -1
- package/services/local-inference/registry.js +10 -1
- package/services/local-inference/types.d.ts +6 -0
- package/services/local-inference/types.d.ts.map +1 -1
- package/test/helpers/real-runtime.ts +21 -20
- package/platforms/electrobun/src/native/permissions-darwin.ts +0 -342
- package/platforms/electrobun/src/native/permissions-linux.ts +0 -34
- package/platforms/electrobun/src/native/permissions-win32.ts +0 -56
|
@@ -113,6 +113,54 @@ function collectContractErrors(m) {
|
|
|
113
113
|
errors.push("evals.e2eLoopOk: false");
|
|
114
114
|
if (!m.evals.thirtyTurnOk)
|
|
115
115
|
errors.push("evals.thirtyTurnOk: false");
|
|
116
|
+
// Optional component slots must be internally consistent: a shipped
|
|
117
|
+
// component needs auditable lineage, and lineage may not point at a
|
|
118
|
+
// component absent from the bundle. Components that affect runtime quality
|
|
119
|
+
// also require their own publish gate to pass.
|
|
120
|
+
for (const slot of ["asr", "embedding", "vision", "vad", "wakeword"]) {
|
|
121
|
+
const files = m.files[slot] ?? [];
|
|
122
|
+
const lineage = m.lineage[slot];
|
|
123
|
+
if (files.length > 0 && !lineage) {
|
|
124
|
+
errors.push(`lineage.${slot}: required when files.${slot} is non-empty`);
|
|
125
|
+
}
|
|
126
|
+
if (lineage && files.length === 0) {
|
|
127
|
+
errors.push(`files.${slot}: required when lineage.${slot} is present`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if ((m.files.asr ?? []).length > 0) {
|
|
131
|
+
if (!m.evals.asrWer) {
|
|
132
|
+
errors.push("evals.asrWer: required when files.asr is non-empty");
|
|
133
|
+
}
|
|
134
|
+
else if (!m.evals.asrWer.passed) {
|
|
135
|
+
errors.push("evals.asrWer.passed: false");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if ((m.files.embedding ?? []).length > 0) {
|
|
139
|
+
if (!m.evals.embedMteb) {
|
|
140
|
+
errors.push("evals.embedMteb: required when files.embedding is non-empty");
|
|
141
|
+
}
|
|
142
|
+
else if (!m.evals.embedMteb.passed) {
|
|
143
|
+
errors.push("evals.embedMteb.passed: false");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if ((m.files.vad ?? []).length > 0) {
|
|
147
|
+
if (!m.evals.vadLatencyMs) {
|
|
148
|
+
errors.push("evals.vadLatencyMs: required when files.vad is non-empty");
|
|
149
|
+
}
|
|
150
|
+
else if (!m.evals.vadLatencyMs.passed) {
|
|
151
|
+
errors.push("evals.vadLatencyMs.passed: false");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const expressiveVoice = m.voice?.capabilities.includes("emotion-tags") ||
|
|
155
|
+
m.voice?.capabilities.includes("singing");
|
|
156
|
+
if (expressiveVoice) {
|
|
157
|
+
if (!m.evals.expressive) {
|
|
158
|
+
errors.push("evals.expressive: required when voice capabilities include emotion-tags or singing");
|
|
159
|
+
}
|
|
160
|
+
else if (!m.evals.expressive.passed) {
|
|
161
|
+
errors.push("evals.expressive.passed: false");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
116
164
|
// The strongest claim: defaultEligible. If anything above failed, this
|
|
117
165
|
// flag must be false. (Contract errors are already accumulated; we add
|
|
118
166
|
// an explicit message so callers can identify the violation cleanly.)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Provider registry.
|
|
3
3
|
*
|
|
4
4
|
* Treats every inference source the same way — cloud subscription, cloud
|
|
5
|
-
* API, local
|
|
5
|
+
* API, Eliza-1 local runtime, paired-device bridge, Capacitor on-device
|
|
6
6
|
* — each is a `ProviderDefinition` with an `id`, a human label, a set of
|
|
7
7
|
* supported model slots, and a pluggable `getEnableState()` that inspects
|
|
8
8
|
* whatever underlying gate controls it (API key presence, subscription
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Provider registry.
|
|
3
3
|
*
|
|
4
4
|
* Treats every inference source the same way — cloud subscription, cloud
|
|
5
|
-
* API, local
|
|
5
|
+
* API, Eliza-1 local runtime, paired-device bridge, Capacitor on-device
|
|
6
6
|
* — each is a `ProviderDefinition` with an `id`, a human label, a set of
|
|
7
7
|
* supported model slots, and a pluggable `getEnableState()` that inspects
|
|
8
8
|
* whatever underlying gate controls it (API key presence, subscription
|
|
@@ -34,9 +34,9 @@ export function getRegisteredSlotsForProvider(providerId) {
|
|
|
34
34
|
// ── Built-in provider definitions ────────────────────────────────────
|
|
35
35
|
const LOCAL_PROVIDER = {
|
|
36
36
|
id: "eliza-local-inference",
|
|
37
|
-
label: "
|
|
37
|
+
label: "Eliza-1 local runtime",
|
|
38
38
|
kind: "local",
|
|
39
|
-
description: "On-device inference
|
|
39
|
+
description: "On-device Eliza-1 inference with the optimized local runtime when the managed binary and companion files are installed. The local embedding companion serves the TEXT_EMBEDDING slot from the same runtime.",
|
|
40
40
|
// TEXT_EMBEDDING is served by the plugin-local-embedding plugin, which
|
|
41
41
|
// registers its own model handler against the runtime. We advertise the
|
|
42
42
|
// slot here so the providers panel reports it as supported by the local
|
|
@@ -56,7 +56,7 @@ const LOCAL_PROVIDER = {
|
|
|
56
56
|
return { enabled: false, reason: "No local model installed" };
|
|
57
57
|
const dflash = getDflashRuntimeStatus();
|
|
58
58
|
return dflash.enabled
|
|
59
|
-
? { enabled: true, reason: "
|
|
59
|
+
? { enabled: true, reason: "Eliza-1 model installed; local acceleration available" }
|
|
60
60
|
: { enabled: true, reason: "GGUF model installed" };
|
|
61
61
|
}
|
|
62
62
|
catch {
|
|
@@ -98,9 +98,9 @@ const DEVICE_BRIDGE_PROVIDER = {
|
|
|
98
98
|
};
|
|
99
99
|
const CAPACITOR_LLAMA_PROVIDER = {
|
|
100
100
|
id: "capacitor-llama",
|
|
101
|
-
label: "
|
|
101
|
+
label: "Eliza-1 mobile runtime",
|
|
102
102
|
kind: "local",
|
|
103
|
-
description: "Runs
|
|
103
|
+
description: "Runs Eliza-1 natively on iOS or Android via Capacitor. Only available in mobile builds.",
|
|
104
104
|
supportedSlots: ["TEXT_SMALL", "TEXT_LARGE"],
|
|
105
105
|
async getEnableState() {
|
|
106
106
|
const cap = globalThis.Capacitor;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../../../../src/services/local-inference/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAmC9C;;;;GAIG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAcrE;AAED,wEAAwE;AACxE,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../../../../src/services/local-inference/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAmC9C;;;;GAIG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAcrE;AAED,wEAAwE;AACxE,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3E;AAED,gEAAgE;AAChE,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/D;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;CACnC,CAAC,CA6BD"}
|
|
@@ -62,6 +62,12 @@ export async function upsertElizaModel(model) {
|
|
|
62
62
|
if (!isWithinElizaRoot(model.path)) {
|
|
63
63
|
throw new Error("[local-inference] Eliza-owned models must live under the local-inference root");
|
|
64
64
|
}
|
|
65
|
+
if (model.bundleRoot && !isWithinElizaRoot(model.bundleRoot)) {
|
|
66
|
+
throw new Error("[local-inference] Eliza-owned bundle roots must live under the local-inference root");
|
|
67
|
+
}
|
|
68
|
+
if (model.manifestPath && !isWithinElizaRoot(model.manifestPath)) {
|
|
69
|
+
throw new Error("[local-inference] Eliza-owned manifests must live under the local-inference root");
|
|
70
|
+
}
|
|
65
71
|
const owned = await readElizaOwned();
|
|
66
72
|
const withoutCurrent = owned.filter((m) => m.id !== model.id);
|
|
67
73
|
withoutCurrent.push(model);
|
|
@@ -97,8 +103,11 @@ export async function removeElizaModel(id) {
|
|
|
97
103
|
if (!isWithinElizaRoot(target.path)) {
|
|
98
104
|
return { removed: false, reason: "external" };
|
|
99
105
|
}
|
|
106
|
+
const removePath = target.bundleRoot && isWithinElizaRoot(target.bundleRoot)
|
|
107
|
+
? target.bundleRoot
|
|
108
|
+
: target.path;
|
|
100
109
|
try {
|
|
101
|
-
await fs.rm(
|
|
110
|
+
await fs.rm(removePath, { recursive: true, force: true });
|
|
102
111
|
}
|
|
103
112
|
catch {
|
|
104
113
|
// If the file was already gone we still want to clear the registry entry.
|
|
@@ -131,6 +131,12 @@ export interface CatalogModel {
|
|
|
131
131
|
hfRepo: string;
|
|
132
132
|
/** Exact GGUF filename in the repo. */
|
|
133
133
|
ggufFile: string;
|
|
134
|
+
/**
|
|
135
|
+
* Optional Eliza-1 bundle manifest in the same HF repo. When present, the
|
|
136
|
+
* downloader installs every file listed in the manifest and uses
|
|
137
|
+
* `ggufFile` as the primary text GGUF inside that bundle.
|
|
138
|
+
*/
|
|
139
|
+
bundleManifestFile?: string;
|
|
134
140
|
params: "360M" | "1B" | "1.7B" | "2B" | "3B" | "4B" | "7B" | "8B" | "9B" | "14B" | "16B" | "22B" | "24B" | "27B" | "32B";
|
|
135
141
|
quant: string;
|
|
136
142
|
sizeGb: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/services/local-inference/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,IAAI,CAAC;AAE3D,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,GAAG,cAAc,CAAC;AAEpE;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,YAAY,GACZ,UAAU,GACV,YAAY,CAAC;AAEjB;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACxC,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC5B,0CAA0C;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gDAAgD;IAChD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,kBAAkB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC,8EAA8E;IAC9E,aAAa,CAAC,EAAE,yBAAyB,CAAC;IAC1C,MAAM,CAAC,EAAE;QACP,uDAAuD;QACvD,cAAc,EAAE,MAAM,CAAC;QACvB,QAAQ,EAAE,QAAQ,CAAC;QACnB,iDAAiD;QACjD,WAAW,EAAE,MAAM,CAAC;QACpB,4CAA4C;QAC5C,gBAAgB,EAAE,MAAM,CAAC;QACzB,kDAAkD;QAClD,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,4DAA4D;QAC5D,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;QAC3B,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC;QAChC,kEAAkE;QAClE,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,OAAO,CAAC,EAAE;QACR;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,oBAAoB,GAAG,gBAAgB,CAAC;KACxD,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/services/local-inference/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -317,26 +317,6 @@ export async function createRealTestRuntime(
|
|
|
317
317
|
// Always register plugin-sql for PGLite database.
|
|
318
318
|
await runtime.registerPlugin(await importPluginSql());
|
|
319
319
|
|
|
320
|
-
if (
|
|
321
|
-
options?.withLLM &&
|
|
322
|
-
process.env.ELIZA_DISABLE_LOCAL_EMBEDDING_PLUGIN !== "1"
|
|
323
|
-
) {
|
|
324
|
-
try {
|
|
325
|
-
const { default: localEmbeddingPlugin } = await import(
|
|
326
|
-
"@elizaos/plugin-local-embedding"
|
|
327
|
-
);
|
|
328
|
-
configureLocalEmbeddingPlugin(localEmbeddingPlugin as Plugin);
|
|
329
|
-
await runtime.registerPlugin(localEmbeddingPlugin as Plugin);
|
|
330
|
-
logger.info(
|
|
331
|
-
"[real-runtime] Registered local embedding plugin for TEXT_EMBEDDING",
|
|
332
|
-
);
|
|
333
|
-
} catch (err) {
|
|
334
|
-
logger.warn(
|
|
335
|
-
`[real-runtime] Failed to register local embedding plugin: ${err}`,
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
320
|
// Register LLM plugin if requested
|
|
341
321
|
let providerName: LiveProviderName | null = null;
|
|
342
322
|
let providerConfig: LiveProviderConfig | null = null;
|
|
@@ -391,6 +371,27 @@ export async function createRealTestRuntime(
|
|
|
391
371
|
}
|
|
392
372
|
}
|
|
393
373
|
|
|
374
|
+
if (
|
|
375
|
+
options?.withLLM &&
|
|
376
|
+
!providerConfig &&
|
|
377
|
+
process.env.ELIZA_DISABLE_LOCAL_EMBEDDING_PLUGIN !== "1"
|
|
378
|
+
) {
|
|
379
|
+
try {
|
|
380
|
+
const { default: localEmbeddingPlugin } = await import(
|
|
381
|
+
"@elizaos/plugin-local-embedding"
|
|
382
|
+
);
|
|
383
|
+
configureLocalEmbeddingPlugin(localEmbeddingPlugin as Plugin);
|
|
384
|
+
await runtime.registerPlugin(localEmbeddingPlugin as Plugin);
|
|
385
|
+
logger.info(
|
|
386
|
+
"[real-runtime] Registered local embedding plugin for TEXT_EMBEDDING",
|
|
387
|
+
);
|
|
388
|
+
} catch (err) {
|
|
389
|
+
logger.warn(
|
|
390
|
+
`[real-runtime] Failed to register local embedding plugin: ${err}`,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
394
395
|
// Register Discord plugin if requested and token available
|
|
395
396
|
if (options?.withDiscord && process.env.DISCORD_BOT_TOKEN?.trim()) {
|
|
396
397
|
try {
|
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
import { dlopen, FFIType } from "bun:ffi";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { getBrandConfig } from "../brand-config";
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
PermissionCheckResult,
|
|
9
|
-
PermissionStatus,
|
|
10
|
-
SystemPermissionId,
|
|
11
|
-
} from "./permissions-shared";
|
|
12
|
-
|
|
13
|
-
interface NativePermissionsLib {
|
|
14
|
-
requestAccessibilityPermission: () => boolean;
|
|
15
|
-
checkAccessibilityPermission: () => boolean;
|
|
16
|
-
requestScreenRecordingPermission: () => boolean;
|
|
17
|
-
checkScreenRecordingPermission: () => boolean;
|
|
18
|
-
checkMicrophonePermission: () => number;
|
|
19
|
-
checkCameraPermission: () => number;
|
|
20
|
-
requestCameraPermission: () => void;
|
|
21
|
-
requestMicrophonePermission: () => void;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
type TccPermissionService =
|
|
25
|
-
| "kTCCServiceAccessibility"
|
|
26
|
-
| "kTCCServiceScreenCapture";
|
|
27
|
-
|
|
28
|
-
const DEFAULT_APP_BUNDLE_ID = getBrandConfig().appId;
|
|
29
|
-
const sessionPromptedPermissions = new Set<SystemPermissionId>();
|
|
30
|
-
|
|
31
|
-
let nativeLib: NativePermissionsLib | null = null;
|
|
32
|
-
/** After the first load attempt (success or failure), do not call `dlopen` again. */
|
|
33
|
-
let nativeLibResolved = false;
|
|
34
|
-
|
|
35
|
-
function getNativeLib(): NativePermissionsLib | null {
|
|
36
|
-
if (nativeLibResolved) {
|
|
37
|
-
return nativeLib;
|
|
38
|
-
}
|
|
39
|
-
nativeLibResolved = true;
|
|
40
|
-
|
|
41
|
-
const dylibPath = path.join(import.meta.dir, "../libMacWindowEffects.dylib");
|
|
42
|
-
if (!existsSync(dylibPath)) {
|
|
43
|
-
console.warn(
|
|
44
|
-
`[Permissions] Native permission dylib missing at ${dylibPath}. Preflight uses safe fallbacks. Build with: (cd apps/app/electrobun && bun run build:native-effects)`,
|
|
45
|
-
);
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
const { symbols } = dlopen(dylibPath, {
|
|
51
|
-
requestAccessibilityPermission: { args: [], returns: FFIType.bool },
|
|
52
|
-
checkAccessibilityPermission: { args: [], returns: FFIType.bool },
|
|
53
|
-
requestScreenRecordingPermission: { args: [], returns: FFIType.bool },
|
|
54
|
-
checkScreenRecordingPermission: { args: [], returns: FFIType.bool },
|
|
55
|
-
checkMicrophonePermission: { args: [], returns: FFIType.i32 },
|
|
56
|
-
checkCameraPermission: { args: [], returns: FFIType.i32 },
|
|
57
|
-
requestCameraPermission: { args: [], returns: FFIType.void },
|
|
58
|
-
requestMicrophonePermission: { args: [], returns: FFIType.void },
|
|
59
|
-
});
|
|
60
|
-
nativeLib = symbols as NativePermissionsLib;
|
|
61
|
-
return nativeLib;
|
|
62
|
-
} catch (error) {
|
|
63
|
-
console.warn("[Permissions] Failed to load native dylib:", error);
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function resetPermissionSessionState(): void {
|
|
69
|
-
sessionPromptedPermissions.clear();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function markPermissionInteraction(id: SystemPermissionId): void {
|
|
73
|
-
if (id === "accessibility" || id === "screen-recording") {
|
|
74
|
-
sessionPromptedPermissions.add(id);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function mapAvAuthorizationStatus(value: number): PermissionStatus {
|
|
79
|
-
if (value === 2) {
|
|
80
|
-
return "granted";
|
|
81
|
-
}
|
|
82
|
-
if (value === 1) {
|
|
83
|
-
return "denied";
|
|
84
|
-
}
|
|
85
|
-
if (value === 3) {
|
|
86
|
-
return "restricted";
|
|
87
|
-
}
|
|
88
|
-
return "not-determined";
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function resolveSessionPermissionStatus(options: {
|
|
92
|
-
granted: boolean;
|
|
93
|
-
promptedThisSession: boolean;
|
|
94
|
-
tccStatus?: PermissionStatus | null;
|
|
95
|
-
}): PermissionStatus {
|
|
96
|
-
if (options.granted || options.tccStatus === "granted") {
|
|
97
|
-
return "granted";
|
|
98
|
-
}
|
|
99
|
-
if (options.tccStatus === "denied" || options.promptedThisSession) {
|
|
100
|
-
return "denied";
|
|
101
|
-
}
|
|
102
|
-
return "not-determined";
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function shouldOpenSettingsAfterMediaRequest(
|
|
106
|
-
status: PermissionStatus,
|
|
107
|
-
): boolean {
|
|
108
|
-
return status !== "granted";
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function extractBundleIdentifierFromInfoPlist(
|
|
112
|
-
infoPlistText: string,
|
|
113
|
-
): string | null {
|
|
114
|
-
const match = infoPlistText.match(
|
|
115
|
-
/<key>\s*CFBundleIdentifier\s*<\/key>\s*<string>([^<]+)<\/string>/s,
|
|
116
|
-
);
|
|
117
|
-
return match?.[1]?.trim() ?? null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function resolveInfoPlistPath(execPath: string): string | null {
|
|
121
|
-
const macOsDir = path.dirname(path.resolve(execPath));
|
|
122
|
-
const contentsDir = path.resolve(macOsDir, "..");
|
|
123
|
-
const infoPlistPath = path.join(contentsDir, "Info.plist");
|
|
124
|
-
|
|
125
|
-
if (existsSync(infoPlistPath)) {
|
|
126
|
-
return infoPlistPath;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function resolveRuntimeBundleIdentifier(
|
|
133
|
-
execPath = process.execPath,
|
|
134
|
-
): string {
|
|
135
|
-
try {
|
|
136
|
-
const infoPlistPath = resolveInfoPlistPath(execPath);
|
|
137
|
-
if (!infoPlistPath) {
|
|
138
|
-
return DEFAULT_APP_BUNDLE_ID;
|
|
139
|
-
}
|
|
140
|
-
const infoPlistText = readFileSync(infoPlistPath, "utf8");
|
|
141
|
-
return (
|
|
142
|
-
extractBundleIdentifierFromInfoPlist(infoPlistText) ??
|
|
143
|
-
DEFAULT_APP_BUNDLE_ID
|
|
144
|
-
);
|
|
145
|
-
} catch {
|
|
146
|
-
return DEFAULT_APP_BUNDLE_ID;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async function queryTccPermissionStatus(
|
|
151
|
-
service: TccPermissionService,
|
|
152
|
-
bundleIdentifier: string,
|
|
153
|
-
): Promise<PermissionStatus | null> {
|
|
154
|
-
try {
|
|
155
|
-
const tccDb = path.join(
|
|
156
|
-
os.homedir(),
|
|
157
|
-
"Library/Application Support/com.apple.TCC/TCC.db",
|
|
158
|
-
);
|
|
159
|
-
if (!existsSync(tccDb)) {
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const proc = Bun.spawn(
|
|
164
|
-
[
|
|
165
|
-
"sqlite3",
|
|
166
|
-
tccDb,
|
|
167
|
-
`SELECT auth_value FROM access WHERE service='${service}' AND client='${bundleIdentifier}'`,
|
|
168
|
-
],
|
|
169
|
-
{ stdout: "pipe", stderr: "pipe" },
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
173
|
-
new Response(proc.stdout).text(),
|
|
174
|
-
new Response(proc.stderr).text(),
|
|
175
|
-
proc.exited,
|
|
176
|
-
]);
|
|
177
|
-
|
|
178
|
-
if (exitCode !== 0 || stderr.includes("authorization denied")) {
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const authValue = stdout.trim();
|
|
183
|
-
if (authValue === "2") {
|
|
184
|
-
return "granted";
|
|
185
|
-
}
|
|
186
|
-
if (authValue === "0") {
|
|
187
|
-
return "denied";
|
|
188
|
-
}
|
|
189
|
-
return null;
|
|
190
|
-
} catch {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async function checkSessionAwarePermission(args: {
|
|
196
|
-
id: "accessibility" | "screen-recording";
|
|
197
|
-
granted: boolean;
|
|
198
|
-
service: TccPermissionService;
|
|
199
|
-
}): Promise<PermissionCheckResult> {
|
|
200
|
-
const bundleIdentifier = resolveRuntimeBundleIdentifier();
|
|
201
|
-
const tccStatus = args.granted
|
|
202
|
-
? null
|
|
203
|
-
: await queryTccPermissionStatus(args.service, bundleIdentifier);
|
|
204
|
-
const status = resolveSessionPermissionStatus({
|
|
205
|
-
granted: args.granted,
|
|
206
|
-
promptedThisSession: sessionPromptedPermissions.has(args.id),
|
|
207
|
-
tccStatus,
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
status,
|
|
212
|
-
canRequest: status === "not-determined",
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export async function checkPermission(
|
|
217
|
-
id: SystemPermissionId,
|
|
218
|
-
): Promise<PermissionCheckResult> {
|
|
219
|
-
switch (id) {
|
|
220
|
-
case "accessibility": {
|
|
221
|
-
const granted = getNativeLib()?.checkAccessibilityPermission() ?? false;
|
|
222
|
-
return checkSessionAwarePermission({
|
|
223
|
-
id,
|
|
224
|
-
granted,
|
|
225
|
-
service: "kTCCServiceAccessibility",
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
case "screen-recording": {
|
|
230
|
-
const granted = getNativeLib()?.checkScreenRecordingPermission() ?? false;
|
|
231
|
-
return checkSessionAwarePermission({
|
|
232
|
-
id,
|
|
233
|
-
granted,
|
|
234
|
-
service: "kTCCServiceScreenCapture",
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
case "microphone": {
|
|
239
|
-
const status = mapAvAuthorizationStatus(
|
|
240
|
-
getNativeLib()?.checkMicrophonePermission() ?? 0,
|
|
241
|
-
);
|
|
242
|
-
return {
|
|
243
|
-
status,
|
|
244
|
-
canRequest: status === "not-determined",
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
case "camera": {
|
|
249
|
-
const status = mapAvAuthorizationStatus(
|
|
250
|
-
getNativeLib()?.checkCameraPermission() ?? 0,
|
|
251
|
-
);
|
|
252
|
-
return {
|
|
253
|
-
status,
|
|
254
|
-
canRequest: status === "not-determined",
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
case "shell":
|
|
259
|
-
return { status: "granted", canRequest: false };
|
|
260
|
-
|
|
261
|
-
default:
|
|
262
|
-
return { status: "not-applicable", canRequest: false };
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export async function requestPermission(
|
|
267
|
-
id: SystemPermissionId,
|
|
268
|
-
): Promise<PermissionCheckResult> {
|
|
269
|
-
switch (id) {
|
|
270
|
-
case "accessibility": {
|
|
271
|
-
markPermissionInteraction(id);
|
|
272
|
-
const granted = getNativeLib()?.requestAccessibilityPermission() ?? false;
|
|
273
|
-
if (!granted) {
|
|
274
|
-
await openPrivacySettings(id);
|
|
275
|
-
}
|
|
276
|
-
return checkPermission(id);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
case "screen-recording": {
|
|
280
|
-
markPermissionInteraction(id);
|
|
281
|
-
const granted =
|
|
282
|
-
getNativeLib()?.requestScreenRecordingPermission() ?? false;
|
|
283
|
-
if (!granted) {
|
|
284
|
-
await openPrivacySettings(id);
|
|
285
|
-
}
|
|
286
|
-
return checkPermission(id);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
case "camera":
|
|
290
|
-
getNativeLib()?.requestCameraPermission();
|
|
291
|
-
{
|
|
292
|
-
const result = await checkPermission(id);
|
|
293
|
-
if (shouldOpenSettingsAfterMediaRequest(result.status)) {
|
|
294
|
-
await openPrivacySettings(id);
|
|
295
|
-
return checkPermission(id);
|
|
296
|
-
}
|
|
297
|
-
return result;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
case "microphone":
|
|
301
|
-
getNativeLib()?.requestMicrophonePermission();
|
|
302
|
-
{
|
|
303
|
-
const result = await checkPermission(id);
|
|
304
|
-
if (shouldOpenSettingsAfterMediaRequest(result.status)) {
|
|
305
|
-
await openPrivacySettings(id);
|
|
306
|
-
return checkPermission(id);
|
|
307
|
-
}
|
|
308
|
-
return result;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
case "shell":
|
|
312
|
-
return { status: "granted", canRequest: false };
|
|
313
|
-
|
|
314
|
-
default:
|
|
315
|
-
return { status: "not-applicable", canRequest: false };
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
export async function openPrivacySettings(
|
|
320
|
-
id: SystemPermissionId,
|
|
321
|
-
): Promise<void> {
|
|
322
|
-
markPermissionInteraction(id);
|
|
323
|
-
|
|
324
|
-
const paneMap: Partial<Record<SystemPermissionId, string>> = {
|
|
325
|
-
accessibility:
|
|
326
|
-
"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility",
|
|
327
|
-
"screen-recording":
|
|
328
|
-
"x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture",
|
|
329
|
-
microphone:
|
|
330
|
-
"x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone",
|
|
331
|
-
camera:
|
|
332
|
-
"x-apple.systempreferences:com.apple.preference.security?Privacy_Camera",
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
const url = paneMap[id];
|
|
336
|
-
if (!url) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const proc = Bun.spawn(["open", url], { stdout: "pipe", stderr: "pipe" });
|
|
341
|
-
await proc.exited;
|
|
342
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
PermissionCheckResult,
|
|
3
|
-
SystemPermissionId,
|
|
4
|
-
} from "./permissions-shared";
|
|
5
|
-
|
|
6
|
-
export async function checkPermission(
|
|
7
|
-
id: SystemPermissionId,
|
|
8
|
-
): Promise<PermissionCheckResult> {
|
|
9
|
-
switch (id) {
|
|
10
|
-
case "microphone":
|
|
11
|
-
case "camera":
|
|
12
|
-
case "shell":
|
|
13
|
-
return { status: "granted", canRequest: false };
|
|
14
|
-
|
|
15
|
-
case "accessibility":
|
|
16
|
-
case "screen-recording":
|
|
17
|
-
return { status: "not-applicable", canRequest: false };
|
|
18
|
-
|
|
19
|
-
default:
|
|
20
|
-
return { status: "not-applicable", canRequest: false };
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export async function requestPermission(
|
|
25
|
-
id: SystemPermissionId,
|
|
26
|
-
): Promise<PermissionCheckResult> {
|
|
27
|
-
return checkPermission(id);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export async function openPrivacySettings(
|
|
31
|
-
_id: SystemPermissionId,
|
|
32
|
-
): Promise<void> {
|
|
33
|
-
// No standard privacy settings on Linux
|
|
34
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
PermissionCheckResult,
|
|
3
|
-
SystemPermissionId,
|
|
4
|
-
} from "./permissions-shared";
|
|
5
|
-
|
|
6
|
-
export async function checkPermission(
|
|
7
|
-
id: SystemPermissionId,
|
|
8
|
-
): Promise<PermissionCheckResult> {
|
|
9
|
-
switch (id) {
|
|
10
|
-
case "microphone":
|
|
11
|
-
case "camera":
|
|
12
|
-
// Windows desktop privacy state is not reliably observable here.
|
|
13
|
-
// Report requestable until runtime capture actually proves access.
|
|
14
|
-
return { status: "not-determined", canRequest: true };
|
|
15
|
-
|
|
16
|
-
case "shell":
|
|
17
|
-
return { status: "granted", canRequest: false };
|
|
18
|
-
|
|
19
|
-
case "accessibility":
|
|
20
|
-
case "screen-recording":
|
|
21
|
-
return { status: "not-applicable", canRequest: false };
|
|
22
|
-
|
|
23
|
-
default:
|
|
24
|
-
return { status: "not-applicable", canRequest: false };
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export async function requestPermission(
|
|
29
|
-
id: SystemPermissionId,
|
|
30
|
-
): Promise<PermissionCheckResult> {
|
|
31
|
-
if (id === "microphone" || id === "camera") {
|
|
32
|
-
await openPrivacySettings(id);
|
|
33
|
-
}
|
|
34
|
-
return checkPermission(id);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export async function openPrivacySettings(
|
|
38
|
-
id: SystemPermissionId,
|
|
39
|
-
): Promise<void> {
|
|
40
|
-
const settingsMap: Record<string, string> = {
|
|
41
|
-
microphone: "ms-settings:privacy-microphone",
|
|
42
|
-
camera: "ms-settings:privacy-webcam",
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const uri = settingsMap[id];
|
|
46
|
-
if (uri) {
|
|
47
|
-
try {
|
|
48
|
-
Bun.spawn(["cmd", "/c", "start", uri], {
|
|
49
|
-
stdout: "ignore",
|
|
50
|
-
stderr: "ignore",
|
|
51
|
-
});
|
|
52
|
-
} catch {
|
|
53
|
-
// Settings unavailable
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|