@elizaos/app-core 2.0.3-beta.5 → 2.0.3-beta.7
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 +37 -36
- package/platforms/electrobun/electrobun.config.ts +31 -1
- package/platforms/electrobun/package.json +10 -10
- package/platforms/electrobun/remotes/fs/README.md +0 -1
- package/platforms/electrobun/remotes/fs/package.json +0 -1
- package/platforms/electrobun/remotes/git/README.md +1 -2
- package/platforms/electrobun/remotes/git/package.json +0 -1
- package/platforms/electrobun/remotes/local-model/README.md +0 -1
- package/platforms/electrobun/remotes/local-model/package.json +0 -1
- package/platforms/electrobun/remotes/pty/README.md +1 -2
- package/platforms/electrobun/remotes/pty/package.json +0 -1
- package/platforms/electrobun/remotes/runtime/README.md +1 -5
- package/platforms/electrobun/remotes/runtime/package.json +0 -1
- package/platforms/electrobun/remotes/surface/README.md +1 -2
- package/platforms/electrobun/remotes/surface/package.json +0 -1
- package/platforms/electrobun/scripts/sync-web-assets.mjs +31 -2
- package/platforms/electrobun/src/api-base.ts +11 -2
- package/platforms/electrobun/src/bridge/electrobun-direct-rpc.ts +15 -1
- package/platforms/electrobun/src/index.ts +25 -28
- package/platforms/electrobun/src/lifecycle/api-base-owner.test.ts +67 -5
- package/platforms/electrobun/src/lifecycle/api-base-owner.ts +36 -6
- package/platforms/electrobun/src/native/desktop.ts +1 -1
- package/platforms/electrobun/src/onboarding-overlay-window.ts +7 -8
- package/platforms/electrobun/src/preload.js +1 -1
- package/platforms/electrobun/src/rpc-schema.ts +5 -1
- package/runtime/voice-warmup.d.ts.map +1 -1
- package/runtime/voice-warmup.js +8 -2
- package/scripts/benchmark-preflight.mjs +31 -2
- package/scripts/build-image.sh +5 -2
- package/scripts/bun-riscv64/build.sh +12 -6
- package/scripts/bun-riscv64/run-build.sh +10 -1
- package/scripts/dev-platform.mjs +19 -10
- package/scripts/ios-xcframework/README.md +15 -9
- package/scripts/ios-xcframework/run-physical-device-smoke.mjs +5 -3
- package/scripts/playwright-ui-live-stack.ts +32 -8
- package/scripts/run-mobile-build.mjs +11 -14
- package/platforms/electrobun/src/desktop-pill-config.test.ts +0 -27
- package/platforms/electrobun/src/desktop-pill-config.ts +0 -40
- package/platforms/electrobun/src/pill-window.test.ts +0 -91
- package/platforms/electrobun/src/pill-window.ts +0 -99
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
import {
|
|
33
|
+
normalizeApiBase,
|
|
33
34
|
pushApiBaseToRenderer,
|
|
34
35
|
resolveDesktopRuntimeMode,
|
|
35
36
|
resolveDesktopRuntimeModeSignal,
|
|
@@ -51,6 +52,31 @@ function resolveStartupTraceId(): string | null {
|
|
|
51
52
|
return getStartupTraceConfig().sessionId;
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
function resolveCurrentExternalApiBase(): string | null {
|
|
56
|
+
const runtime = resolveDesktopRuntimeMode(process.env);
|
|
57
|
+
if (runtime.mode === "external" && runtime.externalApi.base) {
|
|
58
|
+
return runtime.externalApi.base;
|
|
59
|
+
}
|
|
60
|
+
const currentBase = normalizeApiBase(current.base ?? undefined);
|
|
61
|
+
if (!currentBase) return null;
|
|
62
|
+
try {
|
|
63
|
+
const parsed = new URL(currentBase);
|
|
64
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
65
|
+
if (
|
|
66
|
+
(parsed.protocol === "http:" || parsed.protocol === "https:") &&
|
|
67
|
+
hostname !== "localhost" &&
|
|
68
|
+
hostname !== "127.0.0.1" &&
|
|
69
|
+
hostname !== "::1" &&
|
|
70
|
+
hostname !== "[::1]"
|
|
71
|
+
) {
|
|
72
|
+
return parsed.origin;
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
54
80
|
/**
|
|
55
81
|
* Update the singleton with the latest known API base + token. Subsequent
|
|
56
82
|
* `injectIntoHtml(...)` and `pushToWindow(...)` calls read this state.
|
|
@@ -107,11 +133,10 @@ export function injectIntoHtml(html: string): string {
|
|
|
107
133
|
const runtimeModeInject = runtimeModeSignal
|
|
108
134
|
? `window.__ELIZA_DESKTOP_RUNTIME_MODE__=${safeJsonForHtml(runtimeModeSignal)};`
|
|
109
135
|
: "";
|
|
110
|
-
const
|
|
111
|
-
const externalApiBaseInject =
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
: "";
|
|
136
|
+
const externalApiBase = resolveCurrentExternalApiBase();
|
|
137
|
+
const externalApiBaseInject = externalApiBase
|
|
138
|
+
? `window.__ELIZA_DESKTOP_EXTERNAL_API_BASE__=${safeJsonForHtml(externalApiBase)};`
|
|
139
|
+
: "";
|
|
115
140
|
apiBaseInject = `window.__ELIZA_API_BASE__=${baseLiteral};${runtimeModeInject}${externalApiBaseInject}${tokenInject}${bootConfigInject}`;
|
|
116
141
|
}
|
|
117
142
|
|
|
@@ -133,7 +158,12 @@ export function injectIntoHtml(html: string): string {
|
|
|
133
158
|
*/
|
|
134
159
|
export function pushToWindow(win: { webview: { rpc?: unknown } }): void {
|
|
135
160
|
if (!current.base) return;
|
|
136
|
-
pushApiBaseToRenderer(
|
|
161
|
+
pushApiBaseToRenderer(
|
|
162
|
+
win,
|
|
163
|
+
current.base,
|
|
164
|
+
current.token || undefined,
|
|
165
|
+
resolveCurrentExternalApiBase(),
|
|
166
|
+
);
|
|
137
167
|
}
|
|
138
168
|
|
|
139
169
|
/**
|
|
@@ -2252,7 +2252,7 @@ X-GNOME-Autostart-enabled=true
|
|
|
2252
2252
|
error instanceof Error ? error.message : error ? String(error) : null;
|
|
2253
2253
|
|
|
2254
2254
|
try {
|
|
2255
|
-
const localInfo = await Updater.
|
|
2255
|
+
const localInfo = await Updater.getLocalInfo();
|
|
2256
2256
|
currentVersion = localInfo.version;
|
|
2257
2257
|
currentHash = localInfo.hash;
|
|
2258
2258
|
channel = localInfo.channel;
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* onboarding completes the overlay is closed and the normal dashboard window
|
|
19
19
|
* opens.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
21
|
+
* Uses the same small borderless/transparent-window constraints as the other
|
|
22
|
+
* focused native overlay surfaces.
|
|
22
23
|
*/
|
|
23
24
|
|
|
24
25
|
import { type BrowserWindow, Screen } from "electrobun/bun";
|
|
@@ -51,11 +52,10 @@ export function createOnboardingOverlayWindow(args: {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
// Use the full work area so the renderer can place UI elements anywhere on
|
|
54
|
-
// screen — the onboarding card is pinned top-right via CSS
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
// (buttons, pill) receive clicks.
|
|
55
|
+
// screen — the onboarding card is pinned top-right via CSS. The window is
|
|
56
|
+
// transparent + passthrough, so clicks on empty regions fall through to the
|
|
57
|
+
// desktop. The makeKeyAndOrderFront call after dom-ready ensures the
|
|
58
|
+
// interactive elements receive clicks.
|
|
59
59
|
const workArea = Screen.getPrimaryDisplay().workArea;
|
|
60
60
|
const frame = {
|
|
61
61
|
x: workArea.x,
|
|
@@ -72,8 +72,7 @@ export function createOnboardingOverlayWindow(args: {
|
|
|
72
72
|
frame,
|
|
73
73
|
titleBarStyle: "hidden",
|
|
74
74
|
transparent: true,
|
|
75
|
-
//
|
|
76
|
-
// transparent window only showed reliably with activate:false — a
|
|
75
|
+
// A small, borderless, transparent window only showed reliably with activate:false — a
|
|
77
76
|
// borderless NSWindow cannot become key, and activate:true left the small
|
|
78
77
|
// window unshown (the full-screen variant happened to paint regardless).
|
|
79
78
|
// No passthrough: the window is small, so clicks outside it already reach
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{function
|
|
1
|
+
(()=>{function _(e,t){let r=e.map((n)=>`"${n}"`).join(", ");return Error(`This RPC instance cannot ${t} because the transport did not provide one or more of these methods: ${r}`)}function I(e={}){let t={},r={},n=void 0;function o(s){if(r.unregisterHandler)r.unregisterHandler();r=s,r.registerHandler?.($)}function c(s){if(typeof s==="function"){n=s;return}n=(i,u)=>{let p=s[i];if(p)return p(u);let g=s._;if(!g)throw Error(`The requested method has no handler: ${String(i)}`);return g(i,u)}}let{maxRequestTime:a=1000}=e;if(e.transport)o(e.transport);if(e.requestHandler)c(e.requestHandler);if(e._debugHooks)t=e._debugHooks;let R=0;function m(){if(R<=10000000000)return++R;return R=0}let d=new Map,l=new Map;function S(s,...i){let u=i[0];return new Promise((p,g)=>{if(!r.send)throw _(["send"],"make requests");let f=m(),B={type:"request",id:f,method:s,params:u};if(d.set(f,{resolve:p,reject:g}),a!==1/0)l.set(f,setTimeout(()=>{l.delete(f),d.delete(f),g(Error("RPC request timed out."))},a));t.onSend?.(B),r.send(B)})}let q=new Proxy(S,{get:(s,i,u)=>{if(i in s)return Reflect.get(s,i,u);return(p)=>S(i,p)}}),T=q;function x(s,...i){let u=i[0];if(!r.send)throw _(["send"],"send messages");let p={type:"message",id:s,payload:u};t.onSend?.(p),r.send(p)}let v=new Proxy(x,{get:(s,i,u)=>{if(i in s)return Reflect.get(s,i,u);return(p)=>x(i,p)}}),O=v,h=new Map,b=new Set;function Y(s,i){if(!r.registerHandler)throw _(["registerHandler"],"register message listeners");if(s==="*"){b.add(i);return}if(!h.has(s))h.set(s,new Set);h.get(s).add(i)}function U(s,i){if(s==="*"){b.delete(i);return}if(h.get(s)?.delete(i),h.get(s)?.size===0)h.delete(s)}async function $(s){if(t.onReceive?.(s),!("type"in s))throw Error("Message does not contain a type.");if(s.type==="request"){if(!r.send||!n)throw _(["send","requestHandler"],"handle requests");let{id:i,method:u,params:p}=s,g;try{g={type:"response",id:i,success:!0,payload:await n(u,p)}}catch(f){if(!(f instanceof Error))throw f;g={type:"response",id:i,success:!1,error:f.message}}t.onSend?.(g),r.send(g);return}if(s.type==="response"){let i=l.get(s.id);if(i!=null)clearTimeout(i);l.delete(s.id);let{resolve:u,reject:p}=d.get(s.id)??{};if(d.delete(s.id),!s.success)p?.(Error(s.error));else u?.(s.payload);return}if(s.type==="message"){for(let u of b)u(s.id,s.payload);let i=h.get(s.id);if(!i)return;for(let u of i)u(s.payload);return}throw Error(`Unexpected RPC message type: ${s.type}`)}return{setTransport:o,setRequestHandler:c,request:q,requestProxy:T,send:v,sendProxy:O,addMessageListener:Y,removeMessageListener:U,proxy:{send:O,request:T}}}function H(e,t){let r={maxRequestTime:t.maxRequestTime,requestHandler:{...t.handlers.requests,...t.extraRequestHandlers},transport:{registerHandler:()=>{}}},n=I(r),o=t.handlers.messages;if(o)n.addMessageListener("*",(c,a)=>{let R=o["*"];if(R)R(c,a);let m=o[c];if(m)m(a)});return n}var{__electrobunWebviewId:L,__electrobunRpcSocketPort:A}=window;class w{bunSocket;rpc;rpcHandler;constructor(e){this.rpc=e.rpc,this.init()}init(){if(this.initSocketToBun(),window.__electrobun.receiveMessageFromBun=this.receiveMessageFromBun.bind(this),this.rpc)this.rpc.setTransport(this.createTransport())}initSocketToBun(){if(!A||!L)return;let e=new WebSocket(`ws://localhost:${A}/socket?webviewId=${L}`);this.bunSocket=e,e.addEventListener("open",()=>{}),e.addEventListener("message",async(t)=>{let r=t.data;if(typeof r==="string")try{let n=JSON.parse(r),o=await window.__electrobun_decrypt(n.encryptedData,n.iv,n.tag);this.rpcHandler?.(JSON.parse(o))}catch(n){console.error("Error parsing bun message:",n)}else if(r instanceof Blob);else console.error("UNKNOWN DATA TYPE RECEIVED:",t.data)}),e.addEventListener("error",(t)=>{console.error("Socket error:",t)}),e.addEventListener("close",(t)=>{})}createTransport(){let e=this;return{send(t){try{let r=JSON.stringify(t);e.bunBridge(r)}catch(r){console.error("bun: failed to serialize message to webview",r)}},registerHandler(t){e.rpcHandler=t}}}async bunBridge(e){if(this.bunSocket?.readyState===WebSocket.OPEN)try{let{encryptedData:t,iv:r,tag:n}=await window.__electrobun_encrypt(e),c=JSON.stringify({encryptedData:t,iv:r,tag:n});this.bunSocket.send(c);return}catch(t){console.error("Error sending message to bun via socket:",t)}window.__electrobunBunBridge?.postMessage(e)}receiveMessageFromBun(e){if(this.rpcHandler)this.rpcHandler(e)}static defineRPC(e){return H("webview",{...e,extraRequestHandlers:{evaluateJavascriptWithResponse:({script:t})=>new Promise((r)=>{try{let o=Function(t)();if(o instanceof Promise)o.then((c)=>{r(c)}).catch((c)=>{console.error("bun: async script execution failed",c),r(String(c))});else r(o)}catch(n){console.error("bun: failed to eval script",n),r(String(n))}})}})}}function C(e,t){if(e===404&&Z(t))return null;return e>=500?"error":"warn"}function Z(e){let t=e?.method?.toUpperCase()??"GET";if(t!=="GET"&&t!=="HEAD")return!1;let r=e?.url;if(!r)return!1;let n=r;try{n=new URL(r,"http://localhost").pathname}catch{n=r.split("?")[0]??r}return n==="/api/vincent/status"}var W={evaluate:async(e)=>({ok:!1,error:`BrowserWorkspaceView is not mounted — cannot evaluate tab ${e}`}),getTabRect:async()=>null};function E(){if(typeof window>"u")return W;return window.__ELIZA_BROWSER_TABS_REGISTRY__??W}var z=Symbol.for("elizaos.app.boot-config"),N=z;function F(e,t){let n={...e.__ELIZAOS_APP_BOOT_CONFIG__??e.__ELIZA_APP_BOOT_CONFIG__??e[N]?.current??{},...t};return e.__ELIZAOS_APP_BOOT_CONFIG__=n,e.__ELIZA_APP_BOOT_CONFIG__=n,e[N]={current:n},n}function D(){if(typeof window.__electrobun>"u")window.__electrobun={receiveMessageFromBun:(e)=>{},receiveInternalMessageFromBun:(e)=>{}}}var y={},K="__ELIZA_ELECTROBUN_LOG_MIRROR__";function G(e){if(!e||typeof e!=="object")throw Error("Electrobun RPC params must be an object");return e}function k(e,t){let r=e[t];if(typeof r!=="string")throw Error(`Electrobun RPC param "${t}" must be a string`);return r}function X(e,t){let r=e[t];if(typeof r!=="number"||!Number.isFinite(r))throw Error(`Electrobun RPC param "${t}" must be a finite number`);return r}D();function j(e,t){if(e==="apiBaseUpdate"){let n=t;if(window.__ELIZA_API_BASE__=n.base,typeof n.externalApiBase==="string"&&n.externalApiBase.trim())window.__ELIZA_DESKTOP_EXTERNAL_API_BASE__=n.externalApiBase.trim();else Reflect.deleteProperty(window,"__ELIZA_DESKTOP_EXTERNAL_API_BASE__");if(n.token)Object.defineProperty(window,"__ELIZA_API_TOKEN__",{value:n.token,configurable:!0,writable:!0,enumerable:!1});F(window,{apiBase:n.base,...n.token?{apiToken:n.token}:{}})}let r=y[e];if(!r)return;for(let n of Array.from(r))try{n(t)}catch(o){console.error(`[ElectrobunBridge] Listener error for ${e}:`,o)}}function J(e,t){if(typeof e==="string")j(e,t)}var P=w.defineRPC({maxRequestTime:600000,handlers:{requests:{browserWorkspaceRendererEvaluate:async(e)=>{let t=G(e),r=k(t,"id"),n=k(t,"script"),o=X(t,"timeoutMs");return await E().evaluate(r,n,o)},browserWorkspaceRendererGetTabRect:async(e)=>{let t=G(e);return E().getTabRect(k(t,"id"))}},messages:{"*":J}}});new w({rpc:P});function M(e){if(e instanceof Error)return{name:e.name,message:e.message,stack:e.stack};return e}var V=new Proxy(P.request,{get(e,t,r){let n=Reflect.get(e,t,r);if(typeof n!=="function")return n;return async(o)=>{try{return await n.call(e,o)}catch(c){throw P.request.rendererReportDiagnostic({level:"error",source:"rpc",message:`Electrobun RPC request failed: ${String(t)}`,details:M(c)}).catch(()=>{}),c}}}}),Q={request:V,onMessage:(e,t)=>{if(!y[e])y[e]=new Set;y[e].add(t)},offMessage:(e,t)=>{if(y[e]?.delete(t),y[e]?.size===0)delete y[e]}};window.__ELIZA_ELECTROBUN_RPC__=Q;function ee(){let e=window;if(e[K])return;e[K]=!0;let t=(n,o,c,a)=>{P.request.rendererReportDiagnostic({level:n,source:o,message:c,details:a}).catch(()=>{})},r=["log","info","warn","error"];for(let n of r){let o=console[n].bind(console);console[n]=(...c)=>{o(...c),t(n,"console",c.map((a)=>{if(typeof a==="string")return a;try{return JSON.stringify(a)}catch{return String(a)}}).join(" "))}}if(window.addEventListener("error",(n)=>{let o=n.target;if(o&&(o.src||o.href)){t("error","resource","Failed to load resource",{tagName:o.tagName,src:o.src,href:o.href});return}t("error","window.onerror",n.message||"Unhandled window error",{filename:n.filename,lineno:n.lineno,colno:n.colno})},!0),window.addEventListener("unhandledrejection",(n)=>{t("error","unhandledrejection","Unhandled promise rejection",M(n.reason))}),typeof window.fetch==="function"){let n=window.fetch.bind(window);window.fetch=async(...o)=>{let c=Date.now(),a=o[0],R=o[1],m=typeof a==="string"?a:a instanceof Request?a.url:String(a),d=R?.method??(a instanceof Request?a.method:void 0)??"GET";try{let l=await n(...o),S=l.ok?null:C(l.status,{url:m,method:d});if(S)t(S,"fetch",`HTTP ${l.status} ${l.statusText}`,{url:m,method:d,durationMs:Date.now()-c});return l}catch(l){throw t("error","fetch","Fetch failed",{url:m,method:d,durationMs:Date.now()-c,error:M(l)}),l}}}if(typeof XMLHttpRequest<"u"){let n=XMLHttpRequest.prototype.open,o=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(c,a,...R){return this.__elizaDiag={method:c,url:String(a),startedAt:Date.now()},n.call(this,c,a,...R)},XMLHttpRequest.prototype.send=function(...c){let a=this,R=()=>{let d=a.__elizaDiag;if(!d)return;let l=a.status>=400?C(a.status,{url:d.url,method:d.method}):null;if(l)t(l,"xhr",`HTTP ${a.status}`,{url:d.url,method:d.method,durationMs:Date.now()-d.startedAt})},m=()=>{let d=a.__elizaDiag;t("error","xhr","XMLHttpRequest failed",{url:d?.url,method:d?.method,durationMs:d?Date.now()-d.startedAt:void 0})};return a.addEventListener("loadend",R,{once:!0}),a.addEventListener("error",m,{once:!0}),o.call(this,...c)}}}ee();})();
|
|
@@ -2363,7 +2363,11 @@ export type ElizaDesktopRPCSchema = {
|
|
|
2363
2363
|
floatingChatStatusChanged: FloatingChatStatus;
|
|
2364
2364
|
|
|
2365
2365
|
// API Base injection
|
|
2366
|
-
apiBaseUpdate: {
|
|
2366
|
+
apiBaseUpdate: {
|
|
2367
|
+
base: string;
|
|
2368
|
+
token?: string;
|
|
2369
|
+
externalApiBase?: string | null;
|
|
2370
|
+
};
|
|
2367
2371
|
|
|
2368
2372
|
// Share target
|
|
2369
2373
|
shareTargetReceived: { url: string; text?: string };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-warmup.d.ts","sourceRoot":"","sources":["../../../../../src/runtime/voice-warmup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjE;AAED,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC;IAChB,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAMhE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAmB7C;AAED,MAAM,WAAW,qBAAqB;IACpC,+EAA+E;IAC/E,OAAO,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,KAAK,OAAO,GAAG;IACb,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B,CAAC;
|
|
1
|
+
{"version":3,"file":"voice-warmup.d.ts","sourceRoot":"","sources":["../../../../../src/runtime/voice-warmup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjE;AAED,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC;IAChB,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAMhE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAmB7C;AAED,MAAM,WAAW,qBAAqB;IACpC,+EAA+E;IAC/E,OAAO,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,KAAK,OAAO,GAAG;IACb,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B,CAAC;AAmCF;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,kBAAkB,EAC3B,KAAK,EAAE,qBAAqB,EAC5B,GAAG,GAAE,OAAiB,GACrB,OAAO,CAAC,IAAI,CAAC,CAiCf"}
|
package/runtime/voice-warmup.js
CHANGED
|
@@ -52,6 +52,10 @@ export function buildSilentWarmupWav() {
|
|
|
52
52
|
return buf;
|
|
53
53
|
}
|
|
54
54
|
const noopLog = { info: () => { }, warn: () => { } };
|
|
55
|
+
function isMissingModelHandlerError(err) {
|
|
56
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
57
|
+
return message.includes("No handler found for delegate type");
|
|
58
|
+
}
|
|
55
59
|
/**
|
|
56
60
|
* Retry a warmup call up to `maxRetries` times when the error message
|
|
57
61
|
* indicates a transient HTTP issue (429 / 503). Waits `delayMs` between
|
|
@@ -89,13 +93,15 @@ export async function warmVoiceModels(runtime, types, log = noopLog) {
|
|
|
89
93
|
log.info("[eliza] Voice TTS model: ready");
|
|
90
94
|
}
|
|
91
95
|
catch (err) {
|
|
92
|
-
|
|
96
|
+
const logMethod = isMissingModelHandlerError(err) ? log.info : log.warn;
|
|
97
|
+
logMethod(`[eliza] Voice TTS warmup skipped (will load on first use): ${err instanceof Error ? err.message : String(err)}`);
|
|
93
98
|
}
|
|
94
99
|
try {
|
|
95
100
|
await withRetry(() => runtime.useModel(types.transcriptionType, buildSilentWarmupWav()));
|
|
96
101
|
log.info("[eliza] Voice STT model: ready");
|
|
97
102
|
}
|
|
98
103
|
catch (err) {
|
|
99
|
-
|
|
104
|
+
const logMethod = isMissingModelHandlerError(err) ? log.info : log.warn;
|
|
105
|
+
logMethod(`[eliza] Voice STT warmup skipped (will load on first use): ${err instanceof Error ? err.message : String(err)}`);
|
|
100
106
|
}
|
|
101
107
|
}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
|
-
import { mkdir
|
|
4
|
+
import { mkdir } from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const rmPathRecursiveScript = path.resolve(
|
|
10
|
+
scriptDir,
|
|
11
|
+
"..",
|
|
12
|
+
"..",
|
|
13
|
+
"scripts",
|
|
14
|
+
"rm-path-recursive.mjs",
|
|
15
|
+
);
|
|
6
16
|
|
|
7
17
|
function parseArgs(argv) {
|
|
8
18
|
const args = {
|
|
@@ -48,6 +58,25 @@ function runCommand(command, commandArgs, cwd, dryRun = false) {
|
|
|
48
58
|
}
|
|
49
59
|
}
|
|
50
60
|
|
|
61
|
+
function rmRecursive(pathToRemove) {
|
|
62
|
+
const result = spawnSync(
|
|
63
|
+
process.execPath,
|
|
64
|
+
[rmPathRecursiveScript, path.resolve(pathToRemove)],
|
|
65
|
+
{
|
|
66
|
+
encoding: "utf8",
|
|
67
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
if (result.status !== 0) {
|
|
71
|
+
const reason =
|
|
72
|
+
result.stderr?.trim() ||
|
|
73
|
+
result.stdout?.trim() ||
|
|
74
|
+
result.error?.message ||
|
|
75
|
+
`exit status ${String(result.status)}`;
|
|
76
|
+
fail(`failed to recursively remove ${pathToRemove}: ${reason}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
51
80
|
function shellQuote(value) {
|
|
52
81
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
53
82
|
}
|
|
@@ -127,7 +156,7 @@ async function main() {
|
|
|
127
156
|
if (args.dryRun) {
|
|
128
157
|
console.log(`[dry-run] rm -rf ${venvPath}`);
|
|
129
158
|
} else {
|
|
130
|
-
|
|
159
|
+
rmRecursive(venvPath);
|
|
131
160
|
}
|
|
132
161
|
}
|
|
133
162
|
|
package/scripts/build-image.sh
CHANGED
|
@@ -395,9 +395,11 @@ if $REMOTE; then
|
|
|
395
395
|
for arg in "${BUILD_ARGS[@]}"; do
|
|
396
396
|
REMOTE_BUILD_ARGS+=" $(printf '%q' "$arg")"
|
|
397
397
|
done
|
|
398
|
+
REMOTE_BUILD_DIR_QUOTED="$(printf '%q' "${REMOTE_BUILD_DIR}")"
|
|
399
|
+
REMOTE_CLEANUP_HELPER="$(printf '%q' "${REMOTE_BUILD_DIR}/packages/scripts/rm-path-recursive.mjs")"
|
|
398
400
|
REMOTE_SCRIPT=$(cat <<SCRIPT
|
|
399
401
|
set -e
|
|
400
|
-
cd ${
|
|
402
|
+
cd ${REMOTE_BUILD_DIR_QUOTED}
|
|
401
403
|
tar -xzf build.tar.gz
|
|
402
404
|
rm build.tar.gz
|
|
403
405
|
echo "[remote] Extracted build context"
|
|
@@ -406,7 +408,8 @@ docker build -f $(printf '%q' "${DOCKERFILE}")${REMOTE_BUILD_ARGS} \
|
|
|
406
408
|
. 2>&1 | tail -30
|
|
407
409
|
echo "[remote] Build complete"
|
|
408
410
|
docker images '${IMAGE_NAME}' --format 'Image ready: {{.Repository}}:{{.Tag}} ({{.Size}})'
|
|
409
|
-
|
|
411
|
+
cd /tmp
|
|
412
|
+
node ${REMOTE_CLEANUP_HELPER} ${REMOTE_BUILD_DIR_QUOTED}
|
|
410
413
|
SCRIPT
|
|
411
414
|
)
|
|
412
415
|
run "ssh ${SSH_OPTS} -i '${SSH_KEY}' ${BUILD_SERVER} \"${REMOTE_SCRIPT}\""
|
|
@@ -43,6 +43,12 @@ export CARGO_HOME="/home/builder/.cargo"
|
|
|
43
43
|
log() { printf '[bun-riscv64] %s\n' "$*"; }
|
|
44
44
|
die() { printf '[bun-riscv64][FATAL] %s\n' "$*" >&2; exit 1; }
|
|
45
45
|
|
|
46
|
+
RM_PATH_RECURSIVE="${RM_PATH_RECURSIVE:-/opt/rm-path-recursive.mjs}"
|
|
47
|
+
remove_path_recursive() {
|
|
48
|
+
[ -r "$RM_PATH_RECURSIVE" ] || die "recursive cleanup helper not mounted at $RM_PATH_RECURSIVE"
|
|
49
|
+
bun "$RM_PATH_RECURSIVE" "$@"
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
prepare_ninja_object_dirs() {
|
|
47
53
|
local ninja_file="$1"
|
|
48
54
|
local ninja_dir
|
|
@@ -175,7 +181,7 @@ if compgen -G "/opt/webkit-patches/*.patch" >/dev/null; then
|
|
|
175
181
|
cd "$SRC_ROOT/WebKit"
|
|
176
182
|
git config user.email "bun-riscv64@eliza.local"
|
|
177
183
|
git config user.name "bun-riscv64 build"
|
|
178
|
-
|
|
184
|
+
while IFS= read -r p; do
|
|
179
185
|
# In C_LOOP mode, the 0003-disable-dfg-ftl-on-riscv64 patch is
|
|
180
186
|
# unnecessary — ENABLE_C_LOOP=ON in CMake forcibly disables every
|
|
181
187
|
# JIT tier, so the upstream PlatformEnable.h ifdefs never reach
|
|
@@ -198,7 +204,7 @@ if compgen -G "/opt/webkit-patches/*.patch" >/dev/null; then
|
|
|
198
204
|
# 3-way merge is tolerant of context drift; on hard conflict, fail
|
|
199
205
|
# rather than silently skipping.
|
|
200
206
|
git apply --3way "$p" || die "WebKit patch failed: $p — see webkit-patches/README.md for rebase guidance"
|
|
201
|
-
done
|
|
207
|
+
done < <(find /opt/webkit-patches -maxdepth 1 -type f -name '*.patch' | sort)
|
|
202
208
|
cd "$SRC_ROOT"
|
|
203
209
|
else
|
|
204
210
|
log "No webkit-patches/*.patch present; building WebKit @ ${WEBKIT_COMMIT} as-is."
|
|
@@ -230,14 +236,14 @@ if compgen -G "/opt/bun-patches/*.patch" >/dev/null; then
|
|
|
230
236
|
cd "$SRC_ROOT/bun"
|
|
231
237
|
git config user.email "bun-riscv64@eliza.local"
|
|
232
238
|
git config user.name "bun-riscv64 build"
|
|
233
|
-
|
|
239
|
+
while IFS= read -r p; do
|
|
234
240
|
log " -> $p"
|
|
235
241
|
if git apply --reverse --check "$p" >/dev/null 2>&1; then
|
|
236
242
|
log " already applied; skipping"
|
|
237
243
|
continue
|
|
238
244
|
fi
|
|
239
245
|
git apply --3way "$p" || die "Bun patch failed: $p"
|
|
240
|
-
done
|
|
246
|
+
done < <(find /opt/bun-patches -maxdepth 1 -type f -name '*.patch' | sort)
|
|
241
247
|
cd "$SRC_ROOT"
|
|
242
248
|
else
|
|
243
249
|
die "No bun-patches/*.patch present — Bun's build system needs riscv64 awareness (Arch type, cpu flags, WebKit pin, etc.). Populate bun-patches/ before running build.sh."
|
|
@@ -370,7 +376,7 @@ export BUN_SYSROOT=/sysroot
|
|
|
370
376
|
export BUN_DISABLE_TINYCC=1
|
|
371
377
|
|
|
372
378
|
BUN_BUILD_DIR="$SRC_ROOT/bun/build/release"
|
|
373
|
-
|
|
379
|
+
remove_path_recursive "$BUN_BUILD_DIR"
|
|
374
380
|
mkdir -p "$BUN_BUILD_DIR/deps"
|
|
375
381
|
ln -s "$WEBKIT_BUILD_DIR" "$BUN_BUILD_DIR/deps/WebKit"
|
|
376
382
|
|
|
@@ -436,7 +442,7 @@ log " → eval reports: $QEMU_EVAL_OUT"
|
|
|
436
442
|
|
|
437
443
|
log "Smoke test: qemu-riscv64-static bun <script.js>"
|
|
438
444
|
SMOKE_DIR="$(mktemp -d /tmp/bun-riscv64-smoke.XXXXXX)"
|
|
439
|
-
trap '
|
|
445
|
+
trap 'remove_path_recursive "$SMOKE_DIR"' EXIT
|
|
440
446
|
SMOKE_JS="$SMOKE_DIR/entrypoint.js"
|
|
441
447
|
printf '%s\n' 'console.log("bun-riscv64-script-ok", process.arch);' > "$SMOKE_JS"
|
|
442
448
|
QEMU_SCRIPT_OUT="$(qemu-riscv64-static -L /sysroot "$BUN_BIN" "$SMOKE_JS" 2>&1)" || \
|
|
@@ -19,8 +19,15 @@
|
|
|
19
19
|
set -euo pipefail
|
|
20
20
|
|
|
21
21
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
22
|
+
SCRIPT_FILE="$HERE/$(basename "${BASH_SOURCE[0]}")"
|
|
22
23
|
cd "$HERE"
|
|
23
24
|
|
|
25
|
+
RM_PATH_RECURSIVE_HOST="$(cd "$HERE/../../.." && pwd)/scripts/rm-path-recursive.mjs"
|
|
26
|
+
[ -r "$RM_PATH_RECURSIVE_HOST" ] || {
|
|
27
|
+
echo "FATAL: cleanup helper not found at $RM_PATH_RECURSIVE_HOST" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
IMAGE_TAG="eliza/bun-riscv64-builder"
|
|
25
32
|
NO_CACHE=""
|
|
26
33
|
FORCE_CLOOP="1"
|
|
@@ -44,7 +51,7 @@ while [ $# -gt 0 ]; do
|
|
|
44
51
|
-h|--help)
|
|
45
52
|
# Usage block lives in the file header (lines 3-17 of the
|
|
46
53
|
# leading comment, just below the shebang).
|
|
47
|
-
grep '^#' "$
|
|
54
|
+
grep '^#' "$SCRIPT_FILE" | sed -n '3,17p' | sed 's|^# \?||'
|
|
48
55
|
exit 0 ;;
|
|
49
56
|
*)
|
|
50
57
|
echo "Unknown arg: $1" >&2
|
|
@@ -100,6 +107,7 @@ if [ "$SHELL_MODE" = "1" ]; then
|
|
|
100
107
|
exec docker run --rm -it \
|
|
101
108
|
--platform "${PLATFORM}" \
|
|
102
109
|
-v "$HERE:/work-host:rw" \
|
|
110
|
+
-v "$RM_PATH_RECURSIVE_HOST:/opt/rm-path-recursive.mjs:ro" \
|
|
103
111
|
--entrypoint /bin/bash \
|
|
104
112
|
"${IMAGE_TAG}"
|
|
105
113
|
fi
|
|
@@ -120,6 +128,7 @@ DOCKER_RUN_ARGS=(
|
|
|
120
128
|
--rm
|
|
121
129
|
--platform "${PLATFORM}"
|
|
122
130
|
-v "$HERE/build.sh:/opt/build.sh:ro"
|
|
131
|
+
-v "$RM_PATH_RECURSIVE_HOST:/opt/rm-path-recursive.mjs:ro"
|
|
123
132
|
-v "$HERE/bun-version.json:/opt/bun-version.json:ro"
|
|
124
133
|
-v "$PATCH_MOUNT:/opt/bun-patches:ro"
|
|
125
134
|
-v "$HERE/webkit-patches:/opt/webkit-patches:ro"
|
package/scripts/dev-platform.mjs
CHANGED
|
@@ -324,17 +324,12 @@ const rendererBuildSkipRequested =
|
|
|
324
324
|
process.env.ELIZA_DESKTOP_RENDERER_BUILD === "skip";
|
|
325
325
|
const viteWatch = process.env.ELIZA_DESKTOP_VITE_WATCH === "1";
|
|
326
326
|
const viteDepForceCli = process.argv.includes("--vite-force");
|
|
327
|
-
const viteDepForce =
|
|
328
|
-
viteDepForceCli ||
|
|
329
|
-
process.env.ELIZA_VITE_FORCE === "1" ||
|
|
330
|
-
process.env.ELIZA_VITE_FORCE === "1";
|
|
327
|
+
const viteDepForce = viteDepForceCli || process.env.ELIZA_VITE_FORCE === "1";
|
|
331
328
|
const viteRollupWatchCli = process.argv.includes("--rollup-watch");
|
|
332
329
|
/** Legacy: Rollup `vite build --watch` (tens of seconds per edit on large graphs). */
|
|
333
330
|
const viteRollupWatch =
|
|
334
331
|
viteWatch &&
|
|
335
|
-
(viteRollupWatchCli ||
|
|
336
|
-
process.env.ELIZA_DESKTOP_VITE_BUILD_WATCH === "1" ||
|
|
337
|
-
process.env.ELIZA_DESKTOP_VITE_BUILD_WATCH === "1");
|
|
332
|
+
(viteRollupWatchCli || process.env.ELIZA_DESKTOP_VITE_BUILD_WATCH === "1");
|
|
338
333
|
/** Default when VITE_WATCH: Vite dev server + Electrobun ELIZA_RENDERER_URL (fast HMR). */
|
|
339
334
|
const viteDevServer = viteWatch && !viteRollupWatch;
|
|
340
335
|
/** On by default for `dev:desktop` / `dev:desktop:watch`; set to 0/false/no/off to disable. */
|
|
@@ -373,7 +368,7 @@ const desktopCefWorkaroundEnv = (() => {
|
|
|
373
368
|
return explicit;
|
|
374
369
|
}
|
|
375
370
|
|
|
376
|
-
return
|
|
371
|
+
return null;
|
|
377
372
|
})();
|
|
378
373
|
const desktopUnsafeDevtoolsEnv = (() => {
|
|
379
374
|
if (process.platform !== "darwin") {
|
|
@@ -673,12 +668,22 @@ const CHILD_COLORS = {
|
|
|
673
668
|
default: chalk.white,
|
|
674
669
|
};
|
|
675
670
|
|
|
671
|
+
function shouldSuppressExpectedDevLine(line) {
|
|
672
|
+
return (
|
|
673
|
+
line.includes("Notification authorization error:") &&
|
|
674
|
+
line.includes("falling back to legacy API")
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
676
678
|
function prefixStream(name, stream) {
|
|
677
679
|
const plainTag = `[${name.padEnd(PREFIX_PAD)}]`;
|
|
678
680
|
const colorFn = CHILD_COLORS[name] ?? CHILD_COLORS.default;
|
|
679
681
|
stream.on("data", (chunk) => {
|
|
680
682
|
for (const line of chunk.toString().split("\n")) {
|
|
681
683
|
if (line.trim()) {
|
|
684
|
+
if (shouldSuppressExpectedDevLine(line)) {
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
682
687
|
// Use the child color for both streams. Many tools (including Electrobun)
|
|
683
688
|
// write normal startup logs to stderr; red prefixes read as errors.
|
|
684
689
|
const coloredTag = colorFn(plainTag);
|
|
@@ -695,12 +700,16 @@ function prefixStream(name, stream) {
|
|
|
695
700
|
});
|
|
696
701
|
}
|
|
697
702
|
|
|
703
|
+
function childColorEnv() {
|
|
704
|
+
return process.env.NO_COLOR === undefined ? { FORCE_COLOR: "1" } : {};
|
|
705
|
+
}
|
|
706
|
+
|
|
698
707
|
function pushChild(name, cmd, args, cwd, extraEnv = {}) {
|
|
699
708
|
const resolvedCmd = cmd === "bun" ? BUN_EXECUTABLE : cmd;
|
|
700
709
|
const child = spawn(resolvedCmd, args, {
|
|
701
710
|
cwd,
|
|
702
711
|
env: extendNodePathEnv(
|
|
703
|
-
{ ...process.env, ...extraEnv,
|
|
712
|
+
{ ...process.env, ...extraEnv, ...childColorEnv() },
|
|
704
713
|
bundleRoot,
|
|
705
714
|
),
|
|
706
715
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -901,7 +910,7 @@ async function launch() {
|
|
|
901
910
|
...apiEnv,
|
|
902
911
|
[API_PROCESS_SPAWNED_AT_ENV]: apiProcessSpawnedAtMs,
|
|
903
912
|
[PROCESS_SPAWNED_AT_ENV]: apiProcessSpawnedAtMs,
|
|
904
|
-
|
|
913
|
+
...childColorEnv(),
|
|
905
914
|
},
|
|
906
915
|
bundleRoot,
|
|
907
916
|
),
|
|
@@ -177,14 +177,15 @@ ELIZA_IOS_INCLUDE_LLAMA=1 \
|
|
|
177
177
|
|
|
178
178
|
## Known gaps
|
|
179
179
|
|
|
180
|
-
### iOS runtime bridge
|
|
180
|
+
### iOS runtime bridge: text-benchmark ready, voice symbol-ready
|
|
181
181
|
|
|
182
182
|
The iOS slices now embed the same shipped Metal kernel payload as desktop
|
|
183
183
|
Metal and `build-xcframework.mjs --verify` passes the kernel-symbol and
|
|
184
184
|
runtime-symbol audits. The added `libeliza-ios-runtime-shim.a` is a link
|
|
185
|
-
and smoke-test bridge
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
and smoke-test bridge. Its text entrypoint is wired to a live llama context for
|
|
186
|
+
the physical-device `--benchmark-model` path; its voice entrypoints still
|
|
187
|
+
refuse real TTS/ASR with structured unsupported/not-loaded errors until the
|
|
188
|
+
mobile runtime is wired to real OmniVoice GGUF assets.
|
|
188
189
|
|
|
189
190
|
### Real iPhone hardware verification
|
|
190
191
|
|
|
@@ -214,11 +215,16 @@ If no physical device is attached, unlocked, trusted, and in Developer
|
|
|
214
215
|
Mode, the script exits non-zero and prints the offline device list
|
|
215
216
|
reported by `xcrun xctrace list devices`.
|
|
216
217
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
218
|
+
By default, this is still a symbol/ABI smoke and no model weights are bundled.
|
|
219
|
+
With `--benchmark-model <path-to.gguf>`, the generated host app bundles that
|
|
220
|
+
GGUF and runs a real CPU + Metal text-generation benchmark on the physical
|
|
221
|
+
device. The report captures prompt/generation throughput, token counts, process
|
|
222
|
+
memory footprint before/after generation, and thermal state before/after.
|
|
223
|
+
|
|
224
|
+
This does **not** claim voice numerical generation or first-audio latency yet.
|
|
225
|
+
The iOS voice ABI currently links and fail-closes with structured errors for
|
|
226
|
+
missing TTS/ASR assets; first-audio requires a future full-bundle or app-shell
|
|
227
|
+
smoke that stages real OmniVoice assets and exercises the mobile voice runtime.
|
|
222
228
|
|
|
223
229
|
### Why no fallback to the npm-bundled framework
|
|
224
230
|
|
|
@@ -21,9 +21,11 @@
|
|
|
21
21
|
* - libelizainference voice ABI symbols resolve, unless explicitly disabled
|
|
22
22
|
* with --skip-voice-abi for diagnosis.
|
|
23
23
|
*
|
|
24
|
-
* No model weights are bundled
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* No model weights are bundled by default. When --benchmark-model is supplied,
|
|
25
|
+
* the smoke also bundles that GGUF and records real physical-device text
|
|
26
|
+
* throughput plus memory/thermal telemetry for CPU and Metal. Voice and
|
|
27
|
+
* first-audio latency remain out of scope until the mobile voice runtime is
|
|
28
|
+
* wired to real OmniVoice assets instead of the current fail-closed ABI shim.
|
|
27
29
|
*/
|
|
28
30
|
|
|
29
31
|
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
mkdir,
|
|
11
11
|
mkdtemp,
|
|
12
12
|
readFile,
|
|
13
|
-
rm,
|
|
14
13
|
writeFile,
|
|
15
14
|
} from "node:fs/promises";
|
|
16
15
|
import {
|
|
@@ -139,12 +138,37 @@ function markStateDirOwnedByStack(stateDir: string): void {
|
|
|
139
138
|
ownedStateDirs.add(stateDir);
|
|
140
139
|
}
|
|
141
140
|
|
|
141
|
+
async function removePathRecursive(targetPath: string): Promise<void> {
|
|
142
|
+
await new Promise<void>((resolve, reject) => {
|
|
143
|
+
const child = spawn(process.execPath, [CLEANUP_HELPER_SCRIPT, targetPath], {
|
|
144
|
+
cwd: REPO_ROOT,
|
|
145
|
+
stdio: "inherit",
|
|
146
|
+
});
|
|
147
|
+
child.on("error", reject);
|
|
148
|
+
child.on("exit", (code, signal) => {
|
|
149
|
+
if (signal) {
|
|
150
|
+
reject(new Error(`rm-path-recursive exited due to signal ${signal}`));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if ((code ?? 1) !== 0) {
|
|
154
|
+
reject(
|
|
155
|
+
new Error(
|
|
156
|
+
`rm-path-recursive failed for ${targetPath} with status ${
|
|
157
|
+
code ?? 1
|
|
158
|
+
}`,
|
|
159
|
+
),
|
|
160
|
+
);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
resolve();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
142
168
|
async function cleanupPendingStateDirs(): Promise<void> {
|
|
143
169
|
const stateDirs = Array.from(pendingStateDirs);
|
|
144
170
|
pendingStateDirs.clear();
|
|
145
|
-
await Promise.all(
|
|
146
|
-
stateDirs.map((stateDir) => rm(stateDir, { force: true, recursive: true })),
|
|
147
|
-
);
|
|
171
|
+
await Promise.all(stateDirs.map(removePathRecursive));
|
|
148
172
|
}
|
|
149
173
|
|
|
150
174
|
function cleanupKnownStateDirsSync(): void {
|
|
@@ -694,7 +718,7 @@ async function ensureUiDistReady(): Promise<void> {
|
|
|
694
718
|
return;
|
|
695
719
|
}
|
|
696
720
|
|
|
697
|
-
await
|
|
721
|
+
await removePathRecursive(path.join(APP_DIR, ".vite"));
|
|
698
722
|
|
|
699
723
|
const logs: string[] = [];
|
|
700
724
|
const child = spawn(resolveBunCommand(), ["run", "build:web"], {
|
|
@@ -931,7 +955,7 @@ async function startStubStack(): Promise<StartedStack> {
|
|
|
931
955
|
} catch (error) {
|
|
932
956
|
await closeUiServer(uiServer);
|
|
933
957
|
await stopApiChild(apiChild);
|
|
934
|
-
await
|
|
958
|
+
await removePathRecursive(stateDir);
|
|
935
959
|
pendingStateDirs.delete(stateDir);
|
|
936
960
|
throw error;
|
|
937
961
|
}
|
|
@@ -1046,7 +1070,7 @@ async function startRealStack(): Promise<StartedStack> {
|
|
|
1046
1070
|
} catch (error) {
|
|
1047
1071
|
await closeUiServer(uiServer);
|
|
1048
1072
|
await stopApiChild(apiChild);
|
|
1049
|
-
await
|
|
1073
|
+
await removePathRecursive(stateDir);
|
|
1050
1074
|
pendingStateDirs.delete(stateDir);
|
|
1051
1075
|
throw error;
|
|
1052
1076
|
}
|
|
@@ -1060,7 +1084,7 @@ async function stopRealStack(stack: StartedStack | null): Promise<void> {
|
|
|
1060
1084
|
await closeUiServer(stack.uiServer);
|
|
1061
1085
|
await stopApiChild(stack.apiChild);
|
|
1062
1086
|
|
|
1063
|
-
await
|
|
1087
|
+
await removePathRecursive(stack.stateDir);
|
|
1064
1088
|
ownedStateDirs.delete(stack.stateDir);
|
|
1065
1089
|
}
|
|
1066
1090
|
|