@elizaos/app-core 2.0.3-beta.3 → 2.0.3-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/api/i18n-locale-routes.d.ts +7 -0
  2. package/api/i18n-locale-routes.d.ts.map +1 -0
  3. package/api/i18n-locale-routes.js +58 -0
  4. package/api/ios-local-agent-transport.d.ts +3 -0
  5. package/api/ios-local-agent-transport.d.ts.map +1 -1
  6. package/api/ios-local-agent-transport.js +22 -1
  7. package/api/server.d.ts.map +1 -1
  8. package/api/server.js +5 -0
  9. package/benchmark/cerebras-autowire.d.ts.map +1 -1
  10. package/benchmark/cerebras-autowire.js +9 -2
  11. package/package.json +36 -36
  12. package/packaging/debian/rules +1 -1
  13. package/packaging/flatpak/generate-sources.sh +6 -1
  14. package/packaging/snap/snapcraft.yaml +15 -11
  15. package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaAgentService.java +2 -1
  16. package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaNativeBridge.java +5 -0
  17. package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaStartupTrace.java +28 -0
  18. package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaVoicePlugin.java +1 -1
  19. package/platforms/android/app/src/main/java/ai/elizaos/app/MainActivity.java +1 -0
  20. package/platforms/electrobun/package.json +10 -10
  21. package/platforms/electrobun/remotes/local-model/README.md +2 -5
  22. package/platforms/electrobun/remotes/local-model/src/bun/eliza1-catalog.ts +0 -16
  23. package/platforms/electrobun/remotes/local-model/src/dev/phase8-smoke.ts +0 -2
  24. package/platforms/electrobun/scripts/smoke-test.sh +8 -2
  25. package/platforms/electrobun/scripts/stage-macos-release-artifacts.sh +8 -2
  26. package/platforms/electrobun/src/bridge/electrobun-boot-config.ts +8 -0
  27. package/platforms/electrobun/src/bridge/electrobun-crypto-ready.ts +5 -1
  28. package/platforms/electrobun/src/bridge/electrobun-direct-rpc.ts +5 -11
  29. package/platforms/electrobun/src/preload.js +1 -1
  30. package/platforms/ios/App/App/Base.lproj/Main.storyboard +1 -1
  31. package/platforms/ios/App/App/ElizaBridgeViewController.swift +22 -0
  32. package/platforms/ios/App/App/ElizaStartupTrace.swift +12 -0
  33. package/platforms/ios/App/App.xcodeproj/project.pbxproj +8 -0
  34. package/runtime/eliza.d.ts.map +1 -1
  35. package/runtime/eliza.js +18 -0
  36. package/runtime/voice-warmup.d.ts +4 -4
  37. package/runtime/voice-warmup.d.ts.map +1 -1
  38. package/runtime/voice-warmup.js +4 -4
  39. package/scripts/aosp/compile-libllama-paths.mjs +110 -0
  40. package/scripts/aosp/compile-libllama.mjs +35 -106
  41. package/scripts/aosp/compile-libllama.test.mjs +18 -2
  42. package/scripts/assert-required-bundled-packages.test.ts +24 -2
  43. package/scripts/build-bundled-agent-skills-artifact.mjs +32 -8
  44. package/scripts/build-bundled-orchestrator-artifact.mjs +34 -12
  45. package/scripts/build-llama-cpp-mtp.mjs +27 -3
  46. package/scripts/build-patched-electrobun-cli.mjs +30 -2
  47. package/scripts/bun-riscv64/bun-version.json +1 -1
  48. package/scripts/bun-riscv64/validate.sh +21 -8
  49. package/scripts/check-real-local-reset.ts +13 -1
  50. package/scripts/check-sms-gateway-completion-audit.mjs +96 -31
  51. package/scripts/check-sms-gateway-readiness.mjs +54 -17
  52. package/scripts/clean-repo.mjs +21 -3
  53. package/scripts/copy-runtime-node-modules.ts +68 -63
  54. package/scripts/desktop-build.mjs +46 -5
  55. package/scripts/dev-platform-no-install.test.mjs +19 -0
  56. package/scripts/dev-platform.mjs +6 -2
  57. package/scripts/disable-local-eliza-workspace.mjs +23 -1
  58. package/scripts/docker-ci-smoke.sh +6 -5
  59. package/scripts/ensure-avatars.mjs +18 -5
  60. package/scripts/ensure-bundled-workspaces.mjs +4 -15
  61. package/scripts/ensure-type-package-aliases.mjs +47 -3
  62. package/scripts/ffi-stub/Makefile +5 -2
  63. package/scripts/ios-xcframework/build-xcframework.mjs +22 -11
  64. package/scripts/ios-xcframework/run-physical-device-smoke.mjs +99 -6
  65. package/scripts/ios-xcframework/runtime-symbol-shim.c +560 -105
  66. package/scripts/lib/agent-source-watcher.test.mjs +24 -4
  67. package/scripts/lib/apple-entitlement-audit.test.mjs +19 -2
  68. package/scripts/lib/artifact-staleness.test.mts +18 -1
  69. package/scripts/lib/patch-bun-exports.mjs +20 -2
  70. package/scripts/lib/patch-bun-exports.test.mjs +108 -4
  71. package/scripts/lib/renderer-build-manifest.test.mts +18 -1
  72. package/scripts/lib/stage-android-agent.mjs +16 -3
  73. package/scripts/lib/verify-ondevice-artifact.test.mts +18 -1
  74. package/scripts/link-docker-local-app-packages.mjs +26 -1
  75. package/scripts/mas-smoke.test.mjs +25 -9
  76. package/scripts/omnivoice-fuse/prepare.mjs +27 -4
  77. package/scripts/package-electrobun-linux.mjs +22 -7
  78. package/scripts/patch-deps.mjs +18 -5
  79. package/scripts/playwright-ui-live-stack.ts +16 -3
  80. package/scripts/playwright-ui-smoke-api-stub.mjs +167 -2
  81. package/scripts/prepare-ios-cocoapods.sh +5 -1
  82. package/scripts/prune-cdn-local-assets.mjs +52 -5
  83. package/scripts/run-mobile-build-android-app-actions.test.mjs +19 -1
  84. package/scripts/run-mobile-build-mtp-staleness.test.mts +20 -4
  85. package/scripts/run-mobile-build.mjs +63 -2
  86. package/scripts/stage-android-agent.test.mjs +19 -1
  87. package/scripts/stage-desktop-fused-lib.mjs +26 -1
  88. package/scripts/sync-desktop-renderer.mjs +53 -12
  89. package/scripts/test-sms-gateway-software.mjs +20 -4
  90. package/cli/command-format.d.ts +0 -2
  91. package/cli/command-format.d.ts.map +0 -1
  92. package/cli/command-format.js +0 -20
  93. package/register-runtime-hooks.d.ts +0 -2
  94. package/register-runtime-hooks.d.ts.map +0 -1
  95. package/register-runtime-hooks.js +0 -19
@@ -173,7 +173,7 @@
173
173
  // `eliza/` submodule the same algorithm finds the host repo root
174
174
  // (it stops at the first ancestor that has a `package.json`).
175
175
 
176
- import { spawnSync } from "node:child_process";
176
+ import { execFileSync, spawnSync } from "node:child_process";
177
177
  import fs from "node:fs";
178
178
  import os from "node:os";
179
179
  import path from "node:path";
@@ -187,14 +187,32 @@ import {
187
187
  import { verifyFusedSymbols } from "../build-helpers/verify-fused-symbols.mjs";
188
188
  import { patchVulkanKernels } from "../kernel-patches/vulkan-kernels.mjs";
189
189
  import { resolveRepoRootFromImportMeta } from "../lib/repo-root.mjs";
190
+ import {
191
+ compareSemver,
192
+ resolveAndroidNdkHostDir,
193
+ resolveDefaultAndroidAssetsDir as resolveDefaultAndroidAssetsDirForRoot,
194
+ resolveHomebrewFormulaIncludeDirs,
195
+ } from "./compile-libllama-paths.mjs";
190
196
  import { main as compileShimMain } from "./compile-shim.mjs";
191
197
 
198
+ export {
199
+ compareSemver,
200
+ resolveAndroidNdkHostDir,
201
+ resolveHomebrewFormulaIncludeDirs,
202
+ };
203
+
192
204
  const here = path.dirname(fileURLToPath(import.meta.url));
193
205
  // Walk up from `eliza/packages/app-core/scripts/aosp/` until we hit
194
206
  // the host repo root (the directory with a top-level `package.json`).
195
207
  // On a parent-host invocation that's `<host-root>`; when running
196
208
  // inside the elizaOS source checkout it's the elizaOS repo root.
197
209
  const repoRoot = resolveRepoRootFromImportMeta(import.meta.url);
210
+ const RECURSIVE_CLEANUP_SCRIPT = path.join(
211
+ repoRoot,
212
+ "packages",
213
+ "scripts",
214
+ "rm-path-recursive.mjs",
215
+ );
198
216
 
199
217
  // elizaOS/llama.cpp @ 33c888a7b. Composes TBQ (apothic) +
200
218
  // QJL (W1-A) + Q4_POLAR (W1-B) + Metal sources (W1-D) + MTP spec-decode
@@ -401,53 +419,18 @@ export function parseAndroidTarget(target) {
401
419
  return { target, arch, backend, fused, androidAbi };
402
420
  }
403
421
 
404
- export function resolveAndroidNdkHostDir(
405
- prebuiltRoot,
406
- { platform = os.platform(), arch = os.arch(), entries } = {},
407
- ) {
408
- const dirs =
409
- entries ??
410
- (fs.existsSync(prebuiltRoot) ? fs.readdirSync(prebuiltRoot) : []);
411
- const hostDirs = dirs
412
- .filter((d) => /^(linux|darwin|windows)-(x86_64|aarch64|arm64)$/.test(d))
413
- .sort();
414
- const hostPrefix =
415
- platform === "win32"
416
- ? "windows"
417
- : platform === "linux" || platform === "darwin"
418
- ? platform
419
- : null;
420
- if (!hostPrefix) return null;
421
-
422
- const preferredArch =
423
- arch === "x64" ? "x86_64" : arch === "arm64" ? "arm64" : arch;
424
- const archCandidates =
425
- preferredArch === "arm64"
426
- ? ["arm64", "aarch64", "x86_64"]
427
- : [preferredArch, "x86_64"];
428
- return (
429
- archCandidates
430
- .map((candidate) => `${hostPrefix}-${candidate}`)
431
- .find((candidate) => hostDirs.includes(candidate)) ??
432
- hostDirs.find((candidate) => candidate.startsWith(`${hostPrefix}-`)) ??
433
- null
434
- );
435
- }
436
-
437
- export function resolveHomebrewFormulaIncludeDirs(
438
- formula,
439
- prefixes = ["/opt/homebrew", "/usr/local"],
440
- ) {
441
- const includeDirs = [];
442
- for (const prefix of prefixes) {
443
- includeDirs.push(path.join(prefix, "opt", formula, "include"));
444
- const cellar = path.join(prefix, "Cellar", formula);
445
- if (!fs.existsSync(cellar)) continue;
446
- for (const version of fs.readdirSync(cellar).sort(compareSemver)) {
447
- includeDirs.push(path.join(cellar, version, "include"));
448
- }
422
+ function removeDirectoryRecursive(targetPath) {
423
+ try {
424
+ execFileSync("node", [RECURSIVE_CLEANUP_SCRIPT, path.resolve(targetPath)], {
425
+ encoding: "utf8",
426
+ stdio: "pipe",
427
+ });
428
+ } catch (error) {
429
+ const detail = [error?.stdout, error?.stderr].filter(Boolean).join("\n");
430
+ throw new Error(detail || error?.message || String(error), {
431
+ cause: error,
432
+ });
449
433
  }
450
- return includeDirs;
451
434
  }
452
435
 
453
436
  /**
@@ -545,7 +528,7 @@ export function resolveAndroidVulkanCmakeFlags({
545
528
  ? path.resolve(stagingDir)
546
529
  : path.join(os.tmpdir(), "eliza-vulkan-headers");
547
530
  const stagedVulkan = path.join(incRoot, "vulkan");
548
- fs.rmSync(stagedVulkan, { recursive: true, force: true });
531
+ removeDirectoryRecursive(stagedVulkan);
549
532
  fs.mkdirSync(incRoot, { recursive: true });
550
533
  fs.cpSync(hostVulkanDir, stagedVulkan, { recursive: true });
551
534
  // vulkan_core.h `#include <vk_video/...>` the video-codec extension headers,
@@ -554,7 +537,7 @@ export function resolveAndroidVulkanCmakeFlags({
554
537
  const hostVkVideoDir = path.join(path.dirname(hostVulkanDir), "vk_video");
555
538
  if (fs.existsSync(hostVkVideoDir)) {
556
539
  const stagedVkVideo = path.join(incRoot, "vk_video");
557
- fs.rmSync(stagedVkVideo, { recursive: true, force: true });
540
+ removeDirectoryRecursive(stagedVkVideo);
558
541
  fs.cpSync(hostVkVideoDir, stagedVkVideo, { recursive: true });
559
542
  }
560
543
 
@@ -584,7 +567,7 @@ export function resolveAndroidVulkanCmakeFlags({
584
567
  );
585
568
  }
586
569
  const stagedSpirv = path.join(incRoot, "spirv");
587
- fs.rmSync(stagedSpirv, { recursive: true, force: true });
570
+ removeDirectoryRecursive(stagedSpirv);
588
571
  fs.cpSync(path.join(hostSpirvRoot, "spirv"), stagedSpirv, {
589
572
  recursive: true,
590
573
  });
@@ -609,39 +592,7 @@ export function resolveAndroidVulkanCmakeFlags({
609
592
  }
610
593
 
611
594
  export function resolveDefaultAndroidAssetsDir({ root = repoRoot } = {}) {
612
- const appRelativeCandidates = [
613
- path.join("packages", "app"),
614
- path.join("apps", "app"),
615
- path.join("eliza", "packages", "app"),
616
- ];
617
- for (const appRelative of appRelativeCandidates) {
618
- const appRoot = path.join(root, appRelative);
619
- if (
620
- fs.existsSync(path.join(appRoot, "android")) ||
621
- fs.existsSync(path.join(appRoot, "package.json"))
622
- ) {
623
- return path.join(
624
- appRoot,
625
- "android",
626
- "app",
627
- "src",
628
- "main",
629
- "assets",
630
- "agent",
631
- );
632
- }
633
- }
634
- return path.join(
635
- root,
636
- "packages",
637
- "app",
638
- "android",
639
- "app",
640
- "src",
641
- "main",
642
- "assets",
643
- "agent",
644
- );
595
+ return resolveDefaultAndroidAssetsDirForRoot({ root });
645
596
  }
646
597
 
647
598
  export function parseArgs(argv) {
@@ -774,28 +725,6 @@ export function parseArgs(argv) {
774
725
  return args;
775
726
  }
776
727
 
777
- /**
778
- * Compare two semver-ish version strings (zig follows MAJOR.MINOR.PATCH for
779
- * stable releases; dev builds add `-dev.NNN+sha` which we strip).
780
- * Returns negative when `a < b`, positive when `a > b`, zero on equal.
781
- */
782
- export function compareSemver(a, b) {
783
- const norm = (v) =>
784
- String(v)
785
- .replace(/^v/, "")
786
- .split(/[-+]/)[0]
787
- .split(".")
788
- .map((n) => Number.parseInt(n, 10) || 0);
789
- const aa = norm(a);
790
- const bb = norm(b);
791
- for (let i = 0; i < Math.max(aa.length, bb.length); i += 1) {
792
- const x = aa[i] ?? 0;
793
- const y = bb[i] ?? 0;
794
- if (x !== y) return x - y;
795
- }
796
- return 0;
797
- }
798
-
799
728
  /**
800
729
  * Probe the build host for a usable zig toolchain. Returns the absolute path
801
730
  * to the zig binary on success, or throws an Error with an install hint
@@ -1091,7 +1020,7 @@ export function ensureLlamaCppCheckout({
1091
1020
  log(
1092
1021
  `[compile-libllama] Cloning llama.cpp ${LLAMA_CPP_TAG} into ${cacheDir}`,
1093
1022
  );
1094
- fs.rmSync(cacheDir, { recursive: true, force: true });
1023
+ removeDirectoryRecursive(cacheDir);
1095
1024
  fs.mkdirSync(cacheDir, { recursive: true });
1096
1025
  spawn(
1097
1026
  "git",
@@ -1,13 +1,22 @@
1
+ import { execFileSync } from "node:child_process";
1
2
  import fs from "node:fs";
2
3
  import os from "node:os";
3
4
  import path from "node:path";
4
5
  import { afterEach, describe, expect, test } from "vitest";
6
+ import { resolveRepoRootFromImportMeta } from "../lib/repo-root.mjs";
5
7
  import {
6
8
  resolveAndroidNdkHostDir,
7
9
  resolveDefaultAndroidAssetsDir,
8
10
  resolveHomebrewFormulaIncludeDirs,
9
- } from "./compile-libllama.mjs";
11
+ } from "./compile-libllama-paths.mjs";
10
12
 
13
+ const repoRoot = resolveRepoRootFromImportMeta(import.meta.url);
14
+ const cleanupHelperScript = path.join(
15
+ repoRoot,
16
+ "packages",
17
+ "scripts",
18
+ "rm-path-recursive.mjs",
19
+ );
11
20
  const tmpDirs = [];
12
21
 
13
22
  function makeTmpDir() {
@@ -16,9 +25,16 @@ function makeTmpDir() {
16
25
  return dir;
17
26
  }
18
27
 
28
+ function removePathRecursive(targetPath) {
29
+ execFileSync(process.execPath, [cleanupHelperScript, targetPath], {
30
+ cwd: repoRoot,
31
+ stdio: "inherit",
32
+ });
33
+ }
34
+
19
35
  afterEach(() => {
20
36
  while (tmpDirs.length > 0) {
21
- fs.rmSync(tmpDirs.pop(), { recursive: true, force: true });
37
+ removePathRecursive(tmpDirs.pop());
22
38
  }
23
39
  });
24
40
 
@@ -11,8 +11,10 @@
11
11
  * bundle.
12
12
  */
13
13
 
14
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
14
+ import { execFileSync } from "node:child_process";
15
+ import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
15
16
  import path from "node:path";
17
+ import { fileURLToPath } from "node:url";
16
18
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
17
19
 
18
20
  import {
@@ -28,6 +30,26 @@ import {
28
30
  let tmpDir: string;
29
31
  let nodeModulesDir: string;
30
32
 
33
+ const repoRoot = path.resolve(
34
+ path.dirname(fileURLToPath(import.meta.url)),
35
+ "..",
36
+ "..",
37
+ "..",
38
+ );
39
+ const cleanupHelperScript = path.join(
40
+ repoRoot,
41
+ "packages",
42
+ "scripts",
43
+ "rm-path-recursive.mjs",
44
+ );
45
+
46
+ function removePathRecursive(targetPath: string): void {
47
+ execFileSync(process.execPath, [cleanupHelperScript, targetPath], {
48
+ cwd: repoRoot,
49
+ stdio: "inherit",
50
+ });
51
+ }
52
+
31
53
  function writePackageJson(
32
54
  name: string,
33
55
  manifest: Record<string, unknown> = {},
@@ -51,7 +73,7 @@ describe("assertRequiredBundledPackagesLanded", () => {
51
73
  });
52
74
 
53
75
  afterEach(() => {
54
- rmSync(tmpDir, { recursive: true, force: true });
76
+ removePathRecursive(tmpDir);
55
77
  });
56
78
 
57
79
  it("passes when every required package has a package.json", () => {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
+ import { spawnSync } from "node:child_process";
3
4
  import { existsSync } from "node:fs";
4
- import { rm } from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { resolveRepoRootFromImportMeta } from "./lib/repo-root.mjs";
@@ -9,21 +9,45 @@ import { resolveRepoRootFromImportMeta } from "./lib/repo-root.mjs";
9
9
  const __filename = fileURLToPath(import.meta.url);
10
10
  const __dirname = path.dirname(__filename);
11
11
  const repoRoot = resolveRepoRootFromImportMeta(import.meta.url);
12
- const pluginDir = path.join(
12
+ const pluginDir = path.join(repoRoot, "plugins", "plugin-agent-skills");
13
+ const cleanupHelperScript = path.join(
13
14
  repoRoot,
14
- "plugins",
15
- "plugin-agent-skills",
16
- "typescript",
15
+ "packages",
16
+ "scripts",
17
+ "rm-path-recursive.mjs",
17
18
  );
18
19
 
19
20
  const externals = ["node:*", "@elizaos/core", "fflate"];
20
21
 
22
+ function removeGeneratedDist() {
23
+ if (!existsSync("dist")) {
24
+ return;
25
+ }
26
+
27
+ const result = spawnSync("node", [cleanupHelperScript, "dist"], {
28
+ cwd: pluginDir,
29
+ encoding: "utf8",
30
+ stdio: "pipe",
31
+ });
32
+
33
+ if (result.error) {
34
+ throw result.error;
35
+ }
36
+ if (result.status !== 0) {
37
+ const output = [result.stdout, result.stderr]
38
+ .filter(Boolean)
39
+ .join("\n")
40
+ .trim();
41
+ throw new Error(
42
+ `cleanup helper failed for ${path.join(pluginDir, "dist")}${output ? `:\n${output}` : ""}`,
43
+ );
44
+ }
45
+ }
46
+
21
47
  async function main() {
22
48
  process.chdir(pluginDir);
23
49
 
24
- if (existsSync("dist")) {
25
- await rm("dist", { recursive: true, force: true });
26
- }
50
+ removeGeneratedDist();
27
51
 
28
52
  const result = await Bun.build({
29
53
  entrypoints: ["./src/index.ts"],
@@ -1,18 +1,17 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
+ import { spawnSync } from "node:child_process";
3
4
  import { existsSync } from "node:fs";
4
- import { rm } from "node:fs/promises";
5
5
  import path from "node:path";
6
- import { fileURLToPath } from "node:url";
6
+ import { resolveRepoRootFromImportMeta } from "./lib/repo-root.mjs";
7
7
 
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = path.dirname(__filename);
10
- const repoRoot = path.resolve(__dirname, "..");
11
- const pluginDir = path.join(
8
+ const repoRoot = resolveRepoRootFromImportMeta(import.meta.url);
9
+ const pluginDir = path.join(repoRoot, "plugins", "plugin-agent-orchestrator");
10
+ const cleanupHelperScript = path.join(
12
11
  repoRoot,
13
- "eliza",
14
- "plugins",
15
- "plugin-agent-orchestrator",
12
+ "packages",
13
+ "scripts",
14
+ "rm-path-recursive.mjs",
16
15
  );
17
16
 
18
17
  const externals = [
@@ -26,12 +25,35 @@ const externals = [
26
25
  "zod",
27
26
  ];
28
27
 
28
+ function removeGeneratedDist() {
29
+ if (!existsSync("dist")) {
30
+ return;
31
+ }
32
+
33
+ const result = spawnSync("node", [cleanupHelperScript, "dist"], {
34
+ cwd: pluginDir,
35
+ encoding: "utf8",
36
+ stdio: "pipe",
37
+ });
38
+
39
+ if (result.error) {
40
+ throw result.error;
41
+ }
42
+ if (result.status !== 0) {
43
+ const output = [result.stdout, result.stderr]
44
+ .filter(Boolean)
45
+ .join("\n")
46
+ .trim();
47
+ throw new Error(
48
+ `cleanup helper failed for ${path.join(pluginDir, "dist")}${output ? `:\n${output}` : ""}`,
49
+ );
50
+ }
51
+ }
52
+
29
53
  async function main() {
30
54
  process.chdir(pluginDir);
31
55
 
32
- if (existsSync("dist")) {
33
- await rm("dist", { recursive: true, force: true });
34
- }
56
+ removeGeneratedDist();
35
57
 
36
58
  const result = await Bun.build({
37
59
  entrypoints: ["./src/index.ts"],
@@ -36,10 +36,13 @@
36
36
  * run-mobile-build.mjs mtpTargetOutDir()
37
37
  * ELIZA_MTP_LLAMA_CPP_SRC override fork source tree
38
38
  * ELIZA_IOS_DEPLOYMENT_TARGET iOS min version (default 16.0)
39
+ * ELIZA_IOS_METAL_STD Metal language std (default ios-metal2.4);
40
+ * the iOS min-version flag is appended here
41
+ * so embedded metallibs run on older devices
39
42
  * ELIZA_MTP_FORCE_REBUILD=1 ignore a cached slice and rebuild
40
43
  */
41
44
 
42
- import { spawnSync } from "node:child_process";
45
+ import { execFileSync, spawnSync } from "node:child_process";
43
46
  import fs from "node:fs";
44
47
  import os from "node:os";
45
48
  import path from "node:path";
@@ -49,6 +52,19 @@ import { fileURLToPath } from "node:url";
49
52
  const here = path.dirname(fileURLToPath(import.meta.url));
50
53
  // packages/app-core/scripts → repo root
51
54
  const repoRoot = path.resolve(here, "..", "..", "..");
55
+ const cleanupHelperScript = path.join(
56
+ repoRoot,
57
+ "packages",
58
+ "scripts",
59
+ "rm-path-recursive.mjs",
60
+ );
61
+
62
+ function removePathRecursive(targetPath) {
63
+ execFileSync(process.execPath, [cleanupHelperScript, targetPath], {
64
+ cwd: repoRoot,
65
+ stdio: "inherit",
66
+ });
67
+ }
52
68
 
53
69
  // ── State dir — MUST mirror run-mobile-build.mjs elizaStateDirForBuild() and
54
70
  // ios-xcframework/build-xcframework.mjs elizaStateDir() EXACTLY, otherwise
@@ -58,6 +74,7 @@ const STATE_DIR =
58
74
 
59
75
  const DEPLOYMENT_TARGET =
60
76
  process.env.ELIZA_IOS_DEPLOYMENT_TARGET?.trim() || "16.0";
77
+ const IOS_METAL_STD = process.env.ELIZA_IOS_METAL_STD?.trim() || "ios-metal2.4";
61
78
 
62
79
  // ── Fork source. The canonical fork is the in-repo submodule; the vendored
63
80
  // ios-deps tree is the historical fallback. Both carry the eliza kernels.
@@ -203,7 +220,7 @@ function requiredKernelsMissing(symbolsText) {
203
220
 
204
221
  function copyHeaders(srcDir, outDir) {
205
222
  const incOut = path.join(outDir, "include");
206
- fs.rmSync(incOut, { recursive: true, force: true });
223
+ removePathRecursive(incOut);
207
224
  fs.mkdirSync(incOut, { recursive: true });
208
225
  for (const incDir of [
209
226
  path.join(srcDir, "include"),
@@ -281,10 +298,16 @@ function buildTarget(target) {
281
298
  const revision = forkRevision(srcDir);
282
299
  log(`target=${target} sdk=${t.sdk} fork=${revision}`);
283
300
  log(`source: ${srcDir}`);
301
+ const metalDeploymentFlag = t.isSimulator
302
+ ? `-mios-simulator-version-min=${DEPLOYMENT_TARGET}`
303
+ : `-miphoneos-version-min=${DEPLOYMENT_TARGET}`;
304
+ // ggml appends GGML_METAL_STD after -std=; CMake list expansion lets this
305
+ // also pass the iOS deployment target into the embedded metallib compile.
306
+ const metalStdAndDeployment = `${IOS_METAL_STD};${metalDeploymentFlag}`;
284
307
 
285
308
  const buildDir = path.join(STATE_DIR, "local-inference", "mtp-build", target);
286
309
  if (process.env.ELIZA_MTP_FORCE_REBUILD === "1") {
287
- fs.rmSync(buildDir, { recursive: true, force: true });
310
+ removePathRecursive(buildDir);
288
311
  }
289
312
  fs.mkdirSync(buildDir, { recursive: true });
290
313
  fs.mkdirSync(outDir, { recursive: true });
@@ -309,6 +332,7 @@ function buildTarget(target) {
309
332
  // (ggml-metal/CMakeLists.txt ELIZA-KERNEL-EMBED-PATCH-V1).
310
333
  "-DGGML_METAL=ON",
311
334
  "-DGGML_METAL_EMBED_LIBRARY=ON",
335
+ `-DGGML_METAL_STD=${metalStdAndDeployment}`,
312
336
  "-DGGML_METAL_USE_BF16=ON",
313
337
  "-DGGML_ACCELERATE=ON",
314
338
  "-DGGML_BLAS=OFF",
@@ -8,7 +8,6 @@ import {
8
8
  existsSync,
9
9
  mkdirSync,
10
10
  readFileSync,
11
- rmSync,
12
11
  writeFileSync,
13
12
  } from "node:fs";
14
13
  import { createRequire } from "node:module";
@@ -16,6 +15,17 @@ import os from "node:os";
16
15
  import path from "node:path";
17
16
  import { pathToFileURL } from "node:url";
18
17
  import { resolveMainAppDir } from "./lib/app-dir.mjs";
18
+ import { resolveRepoRootFromImportMeta } from "./lib/repo-root.mjs";
19
+
20
+ const repoRoot = resolveRepoRootFromImportMeta(import.meta.url, {
21
+ fallbackToCwd: true,
22
+ });
23
+ const cleanupHelperScript = path.join(
24
+ repoRoot,
25
+ "packages",
26
+ "scripts",
27
+ "rm-path-recursive.mjs",
28
+ );
19
29
 
20
30
  function fail(message, code = 1) {
21
31
  console.error(`[build-patched-electrobun-cli] ${message}`);
@@ -47,6 +57,24 @@ function runAllowFailure(command, args, options = {}) {
47
57
  return result.status ?? 1;
48
58
  }
49
59
 
60
+ function removePathRecursive(targetPath) {
61
+ const result = spawnSync("node", [cleanupHelperScript, targetPath], {
62
+ cwd: repoRoot,
63
+ encoding: "utf8",
64
+ stdio: ["ignore", "pipe", "pipe"],
65
+ });
66
+ if (result.error) throw result.error;
67
+ if (result.status !== 0) {
68
+ const output = [result.stdout, result.stderr]
69
+ .filter(Boolean)
70
+ .join("\n")
71
+ .trim();
72
+ fail(
73
+ `cleanup helper failed for ${targetPath}${output ? `:\n${output}` : ""}`,
74
+ );
75
+ }
76
+ }
77
+
50
78
  function resolveElectrobunDir() {
51
79
  const appDir = resolveMainAppDir(process.cwd(), "app");
52
80
  const workspacePackageJson = path.join(appDir, "package.json");
@@ -394,7 +422,7 @@ function main() {
394
422
  process.env.RUNNER_TEMP ?? os.tmpdir(),
395
423
  `eliza-electrobun-src-${electrobunVersion}`,
396
424
  );
397
- rmSync(tempRoot, { recursive: true, force: true });
425
+ removePathRecursive(tempRoot);
398
426
 
399
427
  run("git", [
400
428
  "clone",
@@ -127,7 +127,7 @@
127
127
  "0005-tinycc-disable-on-riscv64.patch": "ee3e9b0c64a5ccdb78397309ce2115381c353a3dfce313aba88dd951264dc7d3",
128
128
  "0006-build-add-riscv64-cli-validation.patch": "d621bff9ae125af7674bb4ee44dad62848c1ab6e333e61d4b1f6311eb139764c",
129
129
  "0007-deps-per-dep-riscv64-checks.patch": "0d1516c4655601174f5ee91a877ad1a0fcb287ed8df42f7e96297b47b03ea5e4",
130
- "0008-source-stabilize-riscv64-musl-build.patch": "21a8ca86c77dd49140e29c0fd0a3d47a490b4d19d7617342db83567fa473b108",
130
+ "0008-source-stabilize-riscv64-musl-build.patch": "58abdc9deff750305be9176da68b7190cdcbbd1a484256100b36f7239de63824",
131
131
  "0009-disable-wasm-streaming-hooks-for-c-loop.patch": "c8658536cc46505cec35ab65f9d51daeba8cc5abac741f55496c65712c556731",
132
132
  "0010-disable-inspector-profiler-for-riscv64-c-loop.patch": "4433d1077b59c325b19194fecefa4e33b5a586099fec0f4f999c82d053826128",
133
133
  "0011-process-arch-add-riscv64.patch": "1a3e4ab498e83603a34016aab7f02d78fc21a0c7f61daa18cd282e8287d1ef1e",
@@ -17,11 +17,13 @@
17
17
  # - Exit 0 if every patch validates, non-zero otherwise.
18
18
  #
19
19
  # Disk + network: needs ~1.5 GB free for the two shallow clones into /tmp.
20
- # Network: needs HTTPS access to github.com. Tear-down: rm -rf /tmp/bun-riscv64-validate*
20
+ # Network: needs HTTPS access to github.com. Tear-down: helper removes /tmp/bun-riscv64-validate*
21
21
 
22
22
  set -euo pipefail
23
23
 
24
24
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25
+ REPO_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
26
+ RM_PATH_RECURSIVE=(node "$REPO_ROOT/packages/scripts/rm-path-recursive.mjs")
25
27
  DIST_DIR="$SCRIPT_DIR/dist"
26
28
  REPORT="$DIST_DIR/validate-report.txt"
27
29
  mkdir -p "$DIST_DIR"
@@ -144,10 +146,10 @@ fi
144
146
 
145
147
  log ""
146
148
 
147
- declare -A RECORDED_PATCH_PATHS=()
149
+ RECORDED_PATCH_PATHS=()
148
150
  if command -v bun >/dev/null 2>&1; then
149
151
  while IFS= read -r path; do
150
- RECORDED_PATCH_PATHS["$path"]=1
152
+ RECORDED_PATCH_PATHS+=("$path")
151
153
  done < <(bun -e "
152
154
  const v = JSON.parse(require('fs').readFileSync('$VERSION_FILE','utf8'));
153
155
  for (const k of Object.keys(v.patch_series.bun_patches)) console.log('bun-patches/'+k);
@@ -156,7 +158,7 @@ if command -v bun >/dev/null 2>&1; then
156
158
  ")
157
159
  else
158
160
  while IFS= read -r path; do
159
- RECORDED_PATCH_PATHS["$path"]=1
161
+ RECORDED_PATCH_PATHS+=("$path")
160
162
  done < <(python3 -c "
161
163
  import json
162
164
  v = json.load(open('$VERSION_FILE'))
@@ -165,10 +167,21 @@ for k in v['patch_series']['webkit_patches']: print('webkit-patches/'+k)
165
167
  for k in v['patch_series']['webkit_recipes']: print('webkit-patches/'+k)
166
168
  ")
167
169
  fi
170
+
171
+ is_recorded_patch_path() {
172
+ local needle="$1" recorded
173
+ for recorded in "${RECORDED_PATCH_PATHS[@]}"; do
174
+ if [ "$recorded" = "$needle" ]; then
175
+ return 0
176
+ fi
177
+ done
178
+ return 1
179
+ }
180
+
168
181
  for actual in "$SCRIPT_DIR"/bun-patches/*.patch "$SCRIPT_DIR"/webkit-patches/*.patch "$SCRIPT_DIR"/webkit-patches/*.recipe; do
169
182
  [ -e "$actual" ] || continue
170
183
  rel_path="${actual#"$SCRIPT_DIR/"}"
171
- if [ -z "${RECORDED_PATCH_PATHS[$rel_path]+x}" ]; then
184
+ if ! is_recorded_patch_path "$rel_path"; then
172
185
  fail "unrecorded patch/recipe on disk: $rel_path"
173
186
  fi
174
187
  done
@@ -185,7 +198,7 @@ fi
185
198
  CLONE_DIR="${VALIDATE_CLONE_DIR:-/tmp/bun-riscv64-validate-bun}"
186
199
  if [ ! -d "$CLONE_DIR/.git" ]; then
187
200
  log "Cloning oven-sh/bun @ $BUN_TAG into $CLONE_DIR (shallow)…"
188
- rm -rf "$CLONE_DIR"
201
+ "${RM_PATH_RECURSIVE[@]}" "$CLONE_DIR"
189
202
  if ! git clone --depth=1 --branch "$BUN_TAG" --no-recurse-submodules \
190
203
  https://github.com/oven-sh/bun.git "$CLONE_DIR" >>"$REPORT" 2>&1; then
191
204
  fail "git clone of oven-sh/bun @ $BUN_TAG failed — see $REPORT"
@@ -216,12 +229,12 @@ log "── 4. WebKit patches applicability ────────────
216
229
  WK_CLONE_DIR="${VALIDATE_WK_CLONE_DIR:-/tmp/bun-riscv64-validate-webkit}"
217
230
  if [ -d "$WK_CLONE_DIR/.git" ] && ! git -C "$WK_CLONE_DIR" rev-parse --verify "$WEBKIT_COMMIT^{commit}" >/dev/null 2>&1; then
218
231
  log "Discarding incomplete WebKit clone at $WK_CLONE_DIR"
219
- rm -rf "$WK_CLONE_DIR"
232
+ "${RM_PATH_RECURSIVE[@]}" "$WK_CLONE_DIR"
220
233
  fi
221
234
 
222
235
  if [ ! -d "$WK_CLONE_DIR/.git" ]; then
223
236
  log "Cloning oven-sh/WebKit @ $WEBKIT_COMMIT into $WK_CLONE_DIR (shallow)…"
224
- rm -rf "$WK_CLONE_DIR"
237
+ "${RM_PATH_RECURSIVE[@]}" "$WK_CLONE_DIR"
225
238
  git init --initial-branch=main "$WK_CLONE_DIR" >>"$REPORT" 2>&1
226
239
  git -C "$WK_CLONE_DIR" remote add origin https://github.com/oven-sh/WebKit.git
227
240
  if ! git -C "$WK_CLONE_DIR" fetch --depth=1 --filter=blob:none origin "$WEBKIT_COMMIT" >>"$REPORT" 2>&1; then