@hyperdrive.bot/fleet-server 0.3.157 → 0.3.158

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 (22) hide show
  1. package/dist/server/utils/git-command-cache.d.ts +67 -0
  2. package/dist/server/utils/git-command-cache.js +436 -0
  3. package/dist/server/utils/run-git-command.js +14 -4
  4. package/dist/server/utils/spawn-broker-child.mjs +156 -0
  5. package/dist/server/utils/spawn-broker.d.ts +120 -0
  6. package/dist/server/utils/spawn-broker.js +299 -0
  7. package/dist/server/utils/spawn.d.ts +21 -0
  8. package/dist/server/utils/spawn.js +144 -10
  9. package/dist/server/web-ui/_expo/static/js/web/{index-7ca8a9a256b79485a77556fab01588ca.js → index-461552a3716a48e99f87634c93c7b220.js} +4 -4
  10. package/dist/server/web-ui/_expo/static/js/web/index-461552a3716a48e99f87634c93c7b220.js.br +0 -0
  11. package/dist/server/web-ui/_expo/static/js/web/index-461552a3716a48e99f87634c93c7b220.js.gz +0 -0
  12. package/dist/server/web-ui/_expo/static/js/web/{index-7ca8a9a256b79485a77556fab01588ca.js.map.br → index-461552a3716a48e99f87634c93c7b220.js.map.br} +0 -0
  13. package/dist/server/web-ui/_expo/static/js/web/{index-7ca8a9a256b79485a77556fab01588ca.js.map.gz → index-461552a3716a48e99f87634c93c7b220.js.map.gz} +0 -0
  14. package/dist/server/web-ui/index.html +1 -1
  15. package/dist/server/web-ui/index.html.br +0 -0
  16. package/dist/server/web-ui/index.html.gz +0 -0
  17. package/dist/src/utils/spawn-broker-child.mjs +156 -0
  18. package/dist/src/utils/spawn-broker.js +299 -0
  19. package/dist/src/utils/spawn.js +144 -10
  20. package/package.json +8 -8
  21. package/dist/server/web-ui/_expo/static/js/web/index-7ca8a9a256b79485a77556fab01588ca.js.br +0 -0
  22. package/dist/server/web-ui/_expo/static/js/web/index-7ca8a9a256b79485a77556fab01588ca.js.gz +0 -0
@@ -2,6 +2,7 @@ import { execFile, spawn } from "node:child_process";
2
2
  import { extname } from "node:path";
3
3
  import { promisify } from "node:util";
4
4
  import { createExternalCommandProcessEnv } from "../server/paseo-env.js";
5
+ import { getSharedSpawnBroker, isSpawnBrokerEnabled, } from "./spawn-broker.js";
5
6
  import { isWindowsCommandScript, quoteWindowsArgument, quoteWindowsCommand, } from "./windows-command.js";
6
7
  const execFileAsync = promisify(execFile);
7
8
  function hasPathSeparator(value) {
@@ -16,17 +17,21 @@ function shouldUseWindowsShell(command, requestedShell) {
16
17
  }
17
18
  return process.platform === "win32" && !hasPathSeparator(command) && !extname(command);
18
19
  }
19
- export function spawnProcess(command, args, options) {
20
- const { baseEnv, env, envOverlay, ...spawnOptions } = options ?? {};
20
+ function resolveChildEnv(command, options) {
21
+ const { baseEnv, env, envOverlay } = options ?? {};
21
22
  const resolvedBaseEnv = env ?? baseEnv ?? process.env;
23
+ return options?.envMode === "internal"
24
+ ? { ...resolvedBaseEnv, ...envOverlay }
25
+ : createExternalCommandProcessEnv(command, resolvedBaseEnv, ...(envOverlay ? [envOverlay] : []));
26
+ }
27
+ export function spawnProcess(command, args, options) {
28
+ const { baseEnv: _baseEnv, env: _env, envOverlay: _envOverlay, ...spawnOptions } = options ?? {};
22
29
  const isWindows = process.platform === "win32";
23
30
  const shell = shouldUseWindowsShell(command, spawnOptions.shell);
24
31
  const shouldQuoteForShell = isWindows && shell !== false;
25
32
  const resolvedCommand = shouldQuoteForShell ? quoteWindowsCommand(command) : command;
26
33
  const resolvedArgs = shouldQuoteForShell ? args.map(quoteWindowsArgument) : args;
27
- const childEnv = options?.envMode === "internal"
28
- ? { ...resolvedBaseEnv, ...envOverlay }
29
- : createExternalCommandProcessEnv(command, resolvedBaseEnv, ...(envOverlay ? [envOverlay] : []));
34
+ const childEnv = resolveChildEnv(command, options);
30
35
  return spawn(resolvedCommand, resolvedArgs, {
31
36
  ...spawnOptions,
32
37
  env: childEnv,
@@ -34,17 +39,53 @@ export function spawnProcess(command, args, options) {
34
39
  windowsHide: true,
35
40
  });
36
41
  }
42
+ /**
43
+ * Spawn a short-lived command whose stdin is ignored and whose stdout/stderr are
44
+ * piped. Runs through the spawn broker helper process when available (so the
45
+ * daemon does not fork its own large image), and falls back to spawnProcess.
46
+ * Use spawnProcess instead when you need stdin, detached, a pty or a real
47
+ * ChildProcess handle.
48
+ */
49
+ export function spawnNonInteractive(command, args, options) {
50
+ const direct = () => spawnProcess(command, args, {
51
+ cwd: options?.cwd,
52
+ env: options?.env,
53
+ baseEnv: options?.baseEnv,
54
+ envOverlay: options?.envOverlay,
55
+ envMode: options?.envMode,
56
+ shell: options?.shell,
57
+ stdio: ["ignore", "pipe", "pipe"],
58
+ });
59
+ const shell = shouldUseWindowsShell(command, options?.shell);
60
+ if (!isSpawnBrokerEnabled() || shell !== false) {
61
+ return direct();
62
+ }
63
+ return getSharedSpawnBroker().spawn({
64
+ command,
65
+ args,
66
+ cwd: options?.cwd,
67
+ env: resolveChildEnv(command, options),
68
+ shell: false,
69
+ maxStdoutBytes: options?.maxStdoutBytes,
70
+ maxStderrBytes: options?.maxStderrBytes,
71
+ }, direct, { retryOnDirect: options?.retryOnDirect ?? false });
72
+ }
73
+ const DEFAULT_EXEC_MAX_BUFFER = 1024 * 1024;
37
74
  export async function execCommand(command, args, options) {
38
- const { baseEnv, env, envOverlay } = options ?? {};
39
- const resolvedBaseEnv = env ?? baseEnv ?? process.env;
40
75
  const isWindows = process.platform === "win32";
41
76
  const shell = shouldUseWindowsShell(command, options?.shell);
42
77
  const shouldQuoteForShell = isWindows && shell !== false;
78
+ if (!isSpawnBrokerEnabled() || shell !== false || shouldQuoteForShell) {
79
+ return execCommandDirect(command, args, options, shell);
80
+ }
81
+ return execCommandNonInteractive(command, args, options);
82
+ }
83
+ function execCommandDirect(command, args, options, shell) {
84
+ const isWindows = process.platform === "win32";
85
+ const shouldQuoteForShell = isWindows && shell !== false;
43
86
  const resolvedCommand = shouldQuoteForShell ? quoteWindowsCommand(command) : command;
44
87
  const resolvedArgs = shouldQuoteForShell ? args.map(quoteWindowsArgument) : args;
45
- const childEnv = options?.envMode === "internal"
46
- ? { ...resolvedBaseEnv, ...envOverlay }
47
- : createExternalCommandProcessEnv(command, resolvedBaseEnv, ...(envOverlay ? [envOverlay] : []));
88
+ const childEnv = resolveChildEnv(command, options);
48
89
  return execFileAsync(resolvedCommand, resolvedArgs, {
49
90
  cwd: options?.cwd,
50
91
  env: childEnv,
@@ -56,6 +97,99 @@ export async function execCommand(command, args, options) {
56
97
  windowsHide: true,
57
98
  });
58
99
  }
100
+ /**
101
+ * execFile semantics (timeout + killSignal, maxBuffer, "Command failed" errors
102
+ * carrying code/killed/signal/stdout/stderr) on top of spawnNonInteractive.
103
+ */
104
+ function execCommandNonInteractive(command, args, options) {
105
+ const encoding = options?.encoding ?? "utf8";
106
+ const maxBuffer = options?.maxBuffer ?? DEFAULT_EXEC_MAX_BUFFER;
107
+ const killSignal = options?.killSignal ?? "SIGTERM";
108
+ const cmd = [command, ...args].join(" ");
109
+ return new Promise((resolve, reject) => {
110
+ const child = spawnNonInteractive(command, args, {
111
+ cwd: options?.cwd,
112
+ env: options?.env,
113
+ baseEnv: options?.baseEnv,
114
+ envOverlay: options?.envOverlay,
115
+ envMode: options?.envMode,
116
+ shell: false,
117
+ // One byte over the cap so the overflow is still detected here.
118
+ maxStdoutBytes: maxBuffer + 1,
119
+ maxStderrBytes: maxBuffer + 1,
120
+ });
121
+ const stdoutChunks = [];
122
+ const stderrChunks = [];
123
+ let stdoutLength = 0;
124
+ let stderrLength = 0;
125
+ let killed = false;
126
+ let settled = false;
127
+ let exError = null;
128
+ let timer;
129
+ const decode = (chunks) => Buffer.concat(chunks).toString(encoding);
130
+ const kill = () => {
131
+ killed = true;
132
+ try {
133
+ child.kill(killSignal);
134
+ }
135
+ catch (error) {
136
+ exError = error;
137
+ }
138
+ };
139
+ const finish = (code, signal) => {
140
+ if (settled)
141
+ return;
142
+ settled = true;
143
+ if (timer)
144
+ clearTimeout(timer);
145
+ const stdout = decode(stdoutChunks);
146
+ const stderr = decode(stderrChunks);
147
+ if (!exError && code === 0 && signal === null) {
148
+ resolve({ stdout, stderr });
149
+ return;
150
+ }
151
+ const error = exError ?? new Error(`Command failed: ${cmd}\n${stderr}`);
152
+ if (!exError) {
153
+ error.code = code !== null && code < 0 ? String(code) : code;
154
+ error.killed = killed;
155
+ error.signal = signal;
156
+ }
157
+ error.cmd = cmd;
158
+ error.stdout = stdout;
159
+ error.stderr = stderr;
160
+ reject(error);
161
+ };
162
+ const onData = (chunks, stream) => (chunk) => {
163
+ if (settled)
164
+ return;
165
+ const length = stream === "stdout" ? stdoutLength : stderrLength;
166
+ const room = maxBuffer - length;
167
+ if (chunk.length > room) {
168
+ chunks.push(chunk.subarray(0, Math.max(0, room)));
169
+ const error = new RangeError(`${stream} maxBuffer length exceeded`);
170
+ error.code = "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
171
+ exError = error;
172
+ kill();
173
+ return;
174
+ }
175
+ chunks.push(chunk);
176
+ if (stream === "stdout")
177
+ stdoutLength += chunk.length;
178
+ else
179
+ stderrLength += chunk.length;
180
+ };
181
+ child.stdout?.on("data", onData(stdoutChunks, "stdout"));
182
+ child.stderr?.on("data", onData(stderrChunks, "stderr"));
183
+ child.on("error", (error) => {
184
+ exError = error;
185
+ finish(null, null);
186
+ });
187
+ child.on("close", (code, signal) => finish(code, signal));
188
+ if (options?.timeout && options.timeout > 0) {
189
+ timer = setTimeout(kill, options.timeout);
190
+ }
191
+ });
192
+ }
59
193
  export function platformShell() {
60
194
  if (process.platform === "win32") {
61
195
  return { command: "cmd.exe", flag: ["/c"] };
@@ -849,7 +849,7 @@ __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,"__esModule",{v
849
849
  __d(function(g,r,i,a,m,e,d){"use strict";var t=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0}),e.shouldAppendSitemap=u,e.shouldAppendNotFound=s,e.getRootStackRouteNames=function(){const t=[n.INTERNAL_SLOT_NAME];s()&&t.push(n.NOT_FOUND_ROUTE_NAME);u()&&t.push(n.SITEMAP_ROUTE_NAME);return t};const o=t(r(d[0])),n=r(d[1]);function u(){const t=o.default.expoConfig?.extra?.router;return!1!==t?.sitemap}function s(){const t=o.default.expoConfig?.extra?.router;return!1!==t?.notFound}},717,[718,721]);
850
850
  __d(function(g,r,i,a,m,_e,d){"use strict";function e(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(_e,'__esModule',{value:!0}),Object.defineProperty(_e,"default",{enumerable:!0,get:function(){return N}}),Object.defineProperty(_e,"AppOwnership",{enumerable:!0,get:function(){return l.AppOwnership}}),Object.defineProperty(_e,"ExecutionEnvironment",{enumerable:!0,get:function(){return l.ExecutionEnvironment}}),Object.defineProperty(_e,"UserInterfaceIdiom",{enumerable:!0,get:function(){return l.UserInterfaceIdiom}});var n=e(r(d[0])),t=r(d[1]);r(d[2]);var u=e(r(d[3])),l=r(d[4]),o=e(r(d[5]));o.default||console.warn("No native ExponentConstants module found, are you sure the expo-constants's module is linked properly?");const s=(0,t.requireOptionalNativeModule)('ExpoUpdates');let f=null;if(s){let e;s.manifest?e=s.manifest:s.manifestString&&(e=JSON.parse(s.manifestString)),e&&Object.keys(e).length>0&&(f=e)}let c=null;if(u.default.EXDevLauncher){let e;u.default.EXDevLauncher.manifestString&&(e=JSON.parse(u.default.EXDevLauncher.manifestString)),e&&Object.keys(e).length>0&&(c=e)}let p=null;if(o.default&&o.default.manifest){const e=o.default.manifest;p='string'==typeof e?JSON.parse(e):e}let b=f??c??p;const E=o.default||{},{appOwnership:O}=E,x=(0,n.default)(E,["name","appOwnership"]),v=Object.assign({},x,{appOwnership:O??null});function _(e){return!h(e)}function h(e){return'metadata'in e}function S(e=!1){if(!b){const e=null===b?'null':'undefined';if(x.executionEnvironment,l.ExecutionEnvironment.Bare,x.executionEnvironment===l.ExecutionEnvironment.StoreClient||x.executionEnvironment===l.ExecutionEnvironment.Standalone)throw new t.CodedError('ERR_CONSTANTS_MANIFEST_UNAVAILABLE',`Constants.manifest is ${e}, must be an object.`)}return b}Object.defineProperties(v,{__unsafeNoWarnManifest:{get(){const e=S(!0);return e&&_(e)?e:null},enumerable:!1},__unsafeNoWarnManifest2:{get(){const e=S(!0);return e&&h(e)?e:null},enumerable:!1},manifest:{get(){const e=S();return e&&_(e)?e:null},enumerable:!0},manifest2:{get(){const e=S();return e&&h(e)?e:null},enumerable:!0},expoConfig:{get(){const e=S(!0);return e?s&&s.isEmbeddedLaunch?p:h(e)?e.extra?.expoClient??null:_(e)?e:null:null},enumerable:!0},expoGoConfig:{get(){const e=S(!0);return e?h(e)?e.extra?.expoGo??null:_(e)?e:null:null},enumerable:!0},easConfig:{get(){const e=S(!0);return e?h(e)?e.extra?.eas??null:_(e)?e:null:null},enumerable:!0},__rawManifest_TEST:{get:()=>b,set(e){b=e},enumerable:!1}});var N=v},718,[35,4,25,637,719,720]);
851
851
  __d(function(g,r,i,a,m,e,d){"use strict";var t,n,o;Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"AppOwnership",{enumerable:!0,get:function(){return t}}),Object.defineProperty(e,"ExecutionEnvironment",{enumerable:!0,get:function(){return n}}),Object.defineProperty(e,"UserInterfaceIdiom",{enumerable:!0,get:function(){return o}}),(function(t){t.Expo="expo"})(t||(t={})),(function(t){t.Bare="bare",t.Standalone="standalone",t.StoreClient="storeClient"})(n||(n={})),(function(t){t.Handset="handset",t.Tablet="tablet",t.Desktop="desktop",t.TV="tv",t.Unsupported="unsupported"})(o||(o={}))},719,[]);
852
- __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return s}});var t=r(d[0]);const n=(Date.now()+'-'+Math.floor(1e9*Math.random())).toString();function o(){if('undefined'!=typeof navigator&&'string'==typeof navigator.userAgent){const t=navigator.userAgent.toLowerCase();if(t.includes('edge'))return'Edge';if(t.includes('edg'))return'Chromium Edge';if(t.includes('opr')&&'opr'in window&&window.opr)return'Opera';if(t.includes('chrome')&&'chrome'in window&&window.chrome)return'Chrome';if(t.includes('trident'))return'IE';if(t.includes('firefox'))return'Firefox';if(t.includes('safari'))return'Safari'}}var s={get appOwnership(){return null},get executionEnvironment(){return t.ExecutionEnvironment.Bare},get sessionId(){return n},get isHeadless(){return'undefined'==typeof navigator||/\bHeadlessChrome\//.test(navigator.userAgent)},get expoVersion(){return this.manifest.sdkVersion||null},get linkingUri(){return'undefined'!=typeof location?location.origin:''},get expoRuntimeVersion(){return this.expoVersion},get deviceName(){return o()},get systemFonts(){return[]},get statusBarHeight(){return 0},get deviceYearClass(){return null},get manifest(){return"{\"name\":\"Paseo\",\"slug\":\"paseo-hyperdrive\",\"version\":\"0.3.157\",\"orientation\":\"portrait\",\"icon\":\"./assets/images/icon.png\",\"scheme\":\"paseo\",\"userInterfaceStyle\":\"automatic\",\"newArchEnabled\":true,\"web\":{\"output\":\"single\",\"favicon\":\"./assets/images/favicon.png\",\"shortName\":\"Paseo\",\"orientation\":\"portrait\",\"name\":\"Paseo\"},\"autolinking\":{\"searchPaths\":[\"../../node_modules\",\"./node_modules\"]},\"experiments\":{\"typedRoutes\":true,\"reactCompiler\":true,\"autolinkingModuleResolution\":true},\"extra\":{\"router\":{},\"eas\":{\"build\":{\"experimental\":{\"ios\":{\"appExtensions\":[{\"bundleIdentifier\":\"bot.hyperdrive.paseo.AgentActivity\",\"targetName\":\"AgentActivity\"}]}}}}},\"sdkVersion\":\"54.0.0\",\"platforms\":[\"ios\",\"android\",\"web\"]}"},get manifest2(){return null},get experienceUrl(){return'undefined'!=typeof location?location.origin:''},get debugMode(){return!1},getWebViewUserAgentAsync:async()=>'undefined'!=typeof navigator?navigator.userAgent:null}},720,[719]);
852
+ __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return s}});var t=r(d[0]);const n=(Date.now()+'-'+Math.floor(1e9*Math.random())).toString();function o(){if('undefined'!=typeof navigator&&'string'==typeof navigator.userAgent){const t=navigator.userAgent.toLowerCase();if(t.includes('edge'))return'Edge';if(t.includes('edg'))return'Chromium Edge';if(t.includes('opr')&&'opr'in window&&window.opr)return'Opera';if(t.includes('chrome')&&'chrome'in window&&window.chrome)return'Chrome';if(t.includes('trident'))return'IE';if(t.includes('firefox'))return'Firefox';if(t.includes('safari'))return'Safari'}}var s={get appOwnership(){return null},get executionEnvironment(){return t.ExecutionEnvironment.Bare},get sessionId(){return n},get isHeadless(){return'undefined'==typeof navigator||/\bHeadlessChrome\//.test(navigator.userAgent)},get expoVersion(){return this.manifest.sdkVersion||null},get linkingUri(){return'undefined'!=typeof location?location.origin:''},get expoRuntimeVersion(){return this.expoVersion},get deviceName(){return o()},get systemFonts(){return[]},get statusBarHeight(){return 0},get deviceYearClass(){return null},get manifest(){return"{\"name\":\"Paseo\",\"slug\":\"paseo-hyperdrive\",\"version\":\"0.3.158\",\"orientation\":\"portrait\",\"icon\":\"./assets/images/icon.png\",\"scheme\":\"paseo\",\"userInterfaceStyle\":\"automatic\",\"newArchEnabled\":true,\"web\":{\"output\":\"single\",\"favicon\":\"./assets/images/favicon.png\",\"shortName\":\"Paseo\",\"orientation\":\"portrait\",\"name\":\"Paseo\"},\"autolinking\":{\"searchPaths\":[\"../../node_modules\",\"./node_modules\"]},\"experiments\":{\"typedRoutes\":true,\"reactCompiler\":true,\"autolinkingModuleResolution\":true},\"extra\":{\"router\":{},\"eas\":{\"build\":{\"experimental\":{\"ios\":{\"appExtensions\":[{\"bundleIdentifier\":\"bot.hyperdrive.paseo.AgentActivity\",\"targetName\":\"AgentActivity\"}]}}}}},\"sdkVersion\":\"54.0.0\",\"platforms\":[\"ios\",\"android\",\"web\"]}"},get manifest2(){return null},get experienceUrl(){return'undefined'!=typeof location?location.origin:''},get debugMode(){return!1},getWebViewUserAgentAsync:async()=>'undefined'!=typeof navigator?navigator.userAgent:null}},720,[719]);
853
853
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.SITEMAP_ROUTE_NAME=e.NOT_FOUND_ROUTE_NAME=e.INTERNAL_SLOT_NAME=void 0,e.INTERNAL_SLOT_NAME='__root',e.NOT_FOUND_ROUTE_NAME='+not-found',e.SITEMAP_ROUTE_NAME='_sitemap'},721,[]);
854
854
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.resolveHref=void 0,e.resolveHrefStringWithSegments=function(t,{segments:n=[],params:s={}}={},{relativeToDirectory:o}={}){if(t.startsWith('.')){let c=n?.map(t=>{if(!t.startsWith('['))return t;if(t.startsWith('[...')){t=t.slice(4,-1);const n=s[t];return Array.isArray(n)?n.join('/'):n?.split(',')?.join('/')??''}return t=t.slice(1,-1),s[t]}).filter(Boolean).join('/')??'/';o&&(c=`${c}/`);const f=new URL(t,`http://hostname/${c}`);t=`${f.pathname}${f.search}`}return t};function t(t,s){for(const[o,c=""]of Object.entries(s)){const f=`[${o}]`,l=`[...${o}]`;if(t.includes(f))t=t.replace(f,n(c));else{if(!t.includes(l))continue;t=t.replace(l,n(c))}delete s[o]}return{pathname:t,params:s}}function n(t){return Array.isArray(t)?t.map(t=>n(t)).join('/'):encodeURIComponent(t.toString())}function s(t){return Object.entries(t).filter(([,t])=>null!=t).map(([t,n])=>`${t}=${encodeURIComponent(n.toString())}`).join('&')}e.resolveHref=n=>{if('string'==typeof n)return(0,e.resolveHref)({pathname:n});const o=n.pathname??'';if(!n?.params)return o;const{pathname:c,params:f}=t(o,Object.assign({},n.params)),l=s(f);return c+(l?`?${l}`:'')}},722,[]);
855
855
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.isRoutePreloadedInStack=function(t,o){if(!t||'stack'!==t.type)return!1;return t.preloadedRoutes.some(t=>t.key===o.key)}},723,[]);
@@ -15057,7 +15057,7 @@ __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{v
15057
15057
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"decodeOfferFragmentPayload",{enumerable:!0,get:function(){return n.decodeOfferFragmentPayload}}),Object.defineProperty(e,"buildDaemonWebSocketUrl",{enumerable:!0,get:function(){return t.buildDaemonWebSocketUrl}}),Object.defineProperty(e,"deriveLabelFromEndpoint",{enumerable:!0,get:function(){return t.deriveLabelFromEndpoint}}),Object.defineProperty(e,"extractHostPortFromWebSocketUrl",{enumerable:!0,get:function(){return t.extractHostPortFromWebSocketUrl}}),Object.defineProperty(e,"normalizeHostPort",{enumerable:!0,get:function(){return t.normalizeHostPort}}),Object.defineProperty(e,"parseConnectionUri",{enumerable:!0,get:function(){return t.parseConnectionUri}}),Object.defineProperty(e,"parseHostPort",{enumerable:!0,get:function(){return t.parseHostPort}}),Object.defineProperty(e,"serializeConnectionUri",{enumerable:!0,get:function(){return t.serializeConnectionUri}}),Object.defineProperty(e,"serializeConnectionUriForStorage",{enumerable:!0,get:function(){return t.serializeConnectionUriForStorage}}),Object.defineProperty(e,"shouldUseTlsForDefaultHostedRelay",{enumerable:!0,get:function(){return t.shouldUseTlsForDefaultHostedRelay}}),e.buildRelayWebSocketUrl=function(n){return(0,t.buildRelayWebSocketUrl)(Object.assign({},n,{role:"client"}))};var t=r(d[0]),n=r(d[1])},3412,[3392,3413]);
15058
15058
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"ConnectionOfferV2Schema",{enumerable:!0,get:function(){return t}}),Object.defineProperty(e,"ConnectionOfferSchema",{enumerable:!0,get:function(){return o}}),e.decodeOfferFragmentPayload=l,e.parseConnectionOfferFromUrl=function(n){const t=f(n);if(!t)return null;const c=l(t);return o.parse(c)};var n=r(d[0]);const t=n.z.object({v:n.z.literal(2),serverId:n.z.string().min(1),daemonPublicKeyB64:n.z.string().min(1),relay:n.z.object({endpoint:n.z.string().min(1),useTls:n.z.boolean().optional()})}),o=t;function c(n){const t=n.replace(/-/g,"+").replace(/_/g,"/"),o=t.padEnd(t.length+(4-t.length%4)%4,"="),c=globalThis.atob(o),l=Uint8Array.from(c,n=>n.charCodeAt(0));return new TextDecoder("utf-8",{fatal:!0}).decode(l)}function l(n){const t=c(n);return JSON.parse(t)}const u="#offer=";function f(n){const t=n.trim();if(!t)return null;const o=t.indexOf(u);if(-1===o)return null;const c=t.slice(o+u.length).trim();return c.length>0?c:null}},3413,[3285]);
15059
15059
  __d(function(g,r,i,a,m,_e,d){"use strict";function e(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(_e,'__esModule',{value:!0}),_e.resolveAppVersion=function(){const e=u(t.default?.version);if(e)return e;const o=u(n.default.expoConfig?.version);if(o)return o;const f=u(n.default.manifest?.version);if(f)return f;return null};var n=e(r(d[0])),t=e(r(d[1]));function u(e){if("string"!=typeof e)return null;const n=e.trim();return 0===n.length?null:n}},3414,[718,3415]);
15060
- __d(function(e,t,r,a,o,i,n){o.exports={name:"@hyperdrive.bot/fleet-app",version:"0.3.157",private:!0,main:"index.ts",scripts:{start:"npm run start:expo","start:expo":"cross-env APP_VARIANT=development expo start","reset-project":"node ./scripts/reset-project.js","build:terminal-webview":"node ./scripts/build-terminal-webview-html.mjs","eas-build-post-install":"npm --prefix ../.. run build:app-deps && npm run build:terminal-webview",android:"npm run android:development","android:development":"npm --prefix ../.. run build:client && cross-env APP_VARIANT=development expo prebuild --platform android --non-interactive && cross-env APP_VARIANT=development expo run:android --variant=debug","android:production":"npm --prefix ../.. run build:client && cross-env APP_VARIANT=production expo prebuild --platform android --non-interactive && cross-env APP_VARIANT=production expo run:android --variant=release","android:release":"npm run android:production","android:clear":"node -e \"require('node:fs').rmSync('android', { recursive: true, force: true })\"",ios:"npm --prefix ../.. run build:client && expo run:ios","ios:release":"npm --prefix ../.. run build:client && expo run:ios --configuration Release",web:"npm --prefix ../.. run build:client && concurrently --kill-others --names protocol,client,expo --prefix-colors yellow,blue,magenta \"npm --prefix ../.. run watch:protocol\" \"npm --prefix ../.. run watch:client\" \"npm run web:expo\"","web:expo":"expo start --web",lint:"expo lint",typecheck:"tsgo --noEmit",test:"vitest run","test:browser":"vitest run --project browser","test:e2e":"playwright test --project='Desktop Chrome'","test:e2e:reels":"cross-env NODE_ENV=development playwright test --config playwright.reels.config.ts","test:e2e:real":"cross-env E2E_FORK_PASEO_HOME_FROM=../../.dev/paseo-home playwright test --project=real-provider","test:e2e:ui":"playwright test --ui","test:coverage":"vitest run --project unit --coverage",build:"npm run build:web","build:web":"npm --prefix ../.. run build:app-deps && expo export --platform web --source-maps","profile:workspace-tabs":"node ./scripts/profile-workspace-tabs.mjs","deploy:web":"npm run build:web && wrangler pages deploy dist --project-name paseo-app --branch main"},dependencies:{"@bacons/apple-targets":"4.0.6","@datadog/browser-rum":"^6.23.0","@datadog/browser-rum-react":"^6.23.0","@datadog/mobile-react-native":"^2.7.0","@datadog/mobile-react-native-session-replay":"^2.14.8","@dnd-kit/core":"^6.3.1","@dnd-kit/sortable":"^10.0.0","@dnd-kit/utilities":"^3.2.2","@expo/image-utils":"0.8.8","@expo/plist":"0.4.9","@expo/prebuild-config":"54.0.8","@floating-ui/react-native":"^0.10.7","@gorhom/bottom-sheet":"^5.2.14","@gorhom/portal":"^1.0.14","@hyperdrive.bot/fleet-client":"*","@hyperdrive.bot/fleet-expo-two-way-audio":"*","@hyperdrive.bot/fleet-extension-sdk":"*","@hyperdrive.bot/fleet-highlight":"*","@react-native-async-storage/async-storage":"2.2.0","@react-native-masked-view/masked-view":"^0.3.2","@react-native/normalize-colors":"^0.81.5","@react-navigation/native":"^7.1.8","@sentry/electron":"^6.11.0","@sentry/react-native":"^6.20.0","@tanstack/react-query":"^5.90.11","@tanstack/react-virtual":"^3.13.21","@xterm/addon-clipboard":"^0.3.0-beta.213","@xterm/addon-fit":"^0.12.0-beta.213","@xterm/addon-image":"^0.10.0-beta.213","@xterm/addon-ligatures":"0.11.0-beta.213","@xterm/addon-search":"^0.17.0-beta.213","@xterm/addon-unicode11":"^0.10.0-beta.213","@xterm/addon-web-links":"^0.13.0-beta.213","@xterm/addon-webgl":"^0.20.0-beta.212","@xterm/xterm":"^6.1.0-beta.213",buffer:"^6.0.3",expo:"^54.0.18","expo-asset":"~12.0.12","expo-audio":"~1.0.13","expo-background-fetch":"~14.0.9","expo-build-properties":"^1.0.9","expo-camera":"~17.0.10","expo-clipboard":"~8.0.7","expo-constants":"~18.0.9","expo-crypto":"^15.0.8","expo-dev-client":"^6.0.15","expo-document-picker":"~14.0.8","expo-file-system":"~19.0.17","expo-haptics":"~15.0.7","expo-image":"~3.0.10","expo-image-manipulator":"~14.0.8","expo-image-picker":"^17.0.8","expo-keep-awake":"^15.0.7","expo-linking":"~8.0.8","expo-localization":"~17.0.9","expo-notifications":"^0.32.16","expo-router":"~6.0.13","expo-sharing":"^14.0.8","expo-speech":"~14.0.8","expo-speech-recognition":"^56.0.1","expo-splash-screen":"~31.0.10","expo-system-ui":"~6.0.7","expo-task-manager":"~14.0.9","expo-video":"~3.0.16","fast-deep-equal":"^3.1.3",htmlparser2:"^12.0.0",i18next:"^26.3.0","lucide-react-native":"^0.546.0","markdown-it":"^10.0.0","mnemonic-id":"^3.2.7","posthog-js":"^1.431.2","posthog-react-native":"^4.72.0",qrcode:"^1.5.4",react:"19.1.0","react-dom":"19.1.0","react-i18next":"^17.0.8","react-native":"0.81.5","react-native-draggable-flatlist":"^4.0.3","react-native-edge-to-edge":"^1.7.0","react-native-gesture-handler":"~2.28.0","react-native-keyboard-controller":"^1.21.7","react-native-markdown-display":"^7.0.2","react-native-nitro-modules":"0.35.5","react-native-reanimated":"~4.3.1","react-native-safe-area-context":"~5.6.0","react-native-screens":"~4.16.0","react-native-svg":"^15.14.0","react-native-uitextview":"^2.2.0","react-native-unistyles":"^3.2.4","react-native-web":"~0.21.0","react-native-webview":"^13.16.0","react-native-worklets":"~0.8.3","tiny-invariant":"^1.3.3","use-sync-external-store":"^1.6.0",zod:"^4.4.3",zustand:"^5.0.9"},devDependencies:{"@playwright/test":"^1.56.1","@testing-library/dom":"^10.4.1","@testing-library/react":"^16.3.2","@types/chai":"^5.2.2","@types/markdown-it":"^14.1.2","@types/qrcode":"^1.5.6","@types/react":"~19.2.0","@types/ws":"^8.18.1","@vitest/browser":"^4.1.7","@vitest/browser-playwright":"^4.1.7","@vitest/coverage-v8":"^4.1.7","@xterm/headless":"^6.1.0-beta.213",dotenv:"^17.2.3",eslint:"^9.25.0","eslint-config-expo":"~10.0.0",jsdom:"^20.0.3","material-icon-theme":"^5.32.0",playwright:"^1.56.1","serve-sim":"^0.1.40",typescript:"~5.9.2",vitest:"^4.1.6",wrangler:"^4.105.0",ws:"^8.20.0"}}},3415,[]);
15060
+ __d(function(e,t,r,a,o,i,n){o.exports={name:"@hyperdrive.bot/fleet-app",version:"0.3.158",private:!0,main:"index.ts",scripts:{start:"npm run start:expo","start:expo":"cross-env APP_VARIANT=development expo start","reset-project":"node ./scripts/reset-project.js","build:terminal-webview":"node ./scripts/build-terminal-webview-html.mjs","eas-build-post-install":"npm --prefix ../.. run build:app-deps && npm run build:terminal-webview",android:"npm run android:development","android:development":"npm --prefix ../.. run build:client && cross-env APP_VARIANT=development expo prebuild --platform android --non-interactive && cross-env APP_VARIANT=development expo run:android --variant=debug","android:production":"npm --prefix ../.. run build:client && cross-env APP_VARIANT=production expo prebuild --platform android --non-interactive && cross-env APP_VARIANT=production expo run:android --variant=release","android:release":"npm run android:production","android:clear":"node -e \"require('node:fs').rmSync('android', { recursive: true, force: true })\"",ios:"npm --prefix ../.. run build:client && expo run:ios","ios:release":"npm --prefix ../.. run build:client && expo run:ios --configuration Release",web:"npm --prefix ../.. run build:client && concurrently --kill-others --names protocol,client,expo --prefix-colors yellow,blue,magenta \"npm --prefix ../.. run watch:protocol\" \"npm --prefix ../.. run watch:client\" \"npm run web:expo\"","web:expo":"expo start --web",lint:"expo lint",typecheck:"tsgo --noEmit",test:"vitest run","test:browser":"vitest run --project browser","test:e2e":"playwright test --project='Desktop Chrome'","test:e2e:reels":"cross-env NODE_ENV=development playwright test --config playwright.reels.config.ts","test:e2e:real":"cross-env E2E_FORK_PASEO_HOME_FROM=../../.dev/paseo-home playwright test --project=real-provider","test:e2e:ui":"playwright test --ui","test:coverage":"vitest run --project unit --coverage",build:"npm run build:web","build:web":"npm --prefix ../.. run build:app-deps && expo export --platform web --source-maps","profile:workspace-tabs":"node ./scripts/profile-workspace-tabs.mjs","deploy:web":"npm run build:web && wrangler pages deploy dist --project-name paseo-app --branch main"},dependencies:{"@bacons/apple-targets":"4.0.6","@datadog/browser-rum":"^6.23.0","@datadog/browser-rum-react":"^6.23.0","@datadog/mobile-react-native":"^2.7.0","@datadog/mobile-react-native-session-replay":"^2.14.8","@dnd-kit/core":"^6.3.1","@dnd-kit/sortable":"^10.0.0","@dnd-kit/utilities":"^3.2.2","@expo/image-utils":"0.8.8","@expo/plist":"0.4.9","@expo/prebuild-config":"54.0.8","@floating-ui/react-native":"^0.10.7","@gorhom/bottom-sheet":"^5.2.14","@gorhom/portal":"^1.0.14","@hyperdrive.bot/fleet-client":"*","@hyperdrive.bot/fleet-expo-two-way-audio":"*","@hyperdrive.bot/fleet-extension-sdk":"*","@hyperdrive.bot/fleet-highlight":"*","@react-native-async-storage/async-storage":"2.2.0","@react-native-masked-view/masked-view":"^0.3.2","@react-native/normalize-colors":"^0.81.5","@react-navigation/native":"^7.1.8","@sentry/electron":"^6.11.0","@sentry/react-native":"^6.20.0","@tanstack/react-query":"^5.90.11","@tanstack/react-virtual":"^3.13.21","@xterm/addon-clipboard":"^0.3.0-beta.213","@xterm/addon-fit":"^0.12.0-beta.213","@xterm/addon-image":"^0.10.0-beta.213","@xterm/addon-ligatures":"0.11.0-beta.213","@xterm/addon-search":"^0.17.0-beta.213","@xterm/addon-unicode11":"^0.10.0-beta.213","@xterm/addon-web-links":"^0.13.0-beta.213","@xterm/addon-webgl":"^0.20.0-beta.212","@xterm/xterm":"^6.1.0-beta.213",buffer:"^6.0.3",expo:"^54.0.18","expo-asset":"~12.0.12","expo-audio":"~1.0.13","expo-background-fetch":"~14.0.9","expo-build-properties":"^1.0.9","expo-camera":"~17.0.10","expo-clipboard":"~8.0.7","expo-constants":"~18.0.9","expo-crypto":"^15.0.8","expo-dev-client":"^6.0.15","expo-document-picker":"~14.0.8","expo-file-system":"~19.0.17","expo-haptics":"~15.0.7","expo-image":"~3.0.10","expo-image-manipulator":"~14.0.8","expo-image-picker":"^17.0.8","expo-keep-awake":"^15.0.7","expo-linking":"~8.0.8","expo-localization":"~17.0.9","expo-notifications":"^0.32.16","expo-router":"~6.0.13","expo-sharing":"^14.0.8","expo-speech":"~14.0.8","expo-speech-recognition":"^56.0.1","expo-splash-screen":"~31.0.10","expo-system-ui":"~6.0.7","expo-task-manager":"~14.0.9","expo-video":"~3.0.16","fast-deep-equal":"^3.1.3",htmlparser2:"^12.0.0",i18next:"^26.3.0","lucide-react-native":"^0.546.0","markdown-it":"^10.0.0","mnemonic-id":"^3.2.7","posthog-js":"^1.431.2","posthog-react-native":"^4.72.0",qrcode:"^1.5.4",react:"19.1.0","react-dom":"19.1.0","react-i18next":"^17.0.8","react-native":"0.81.5","react-native-draggable-flatlist":"^4.0.3","react-native-edge-to-edge":"^1.7.0","react-native-gesture-handler":"~2.28.0","react-native-keyboard-controller":"^1.21.7","react-native-markdown-display":"^7.0.2","react-native-nitro-modules":"0.35.5","react-native-reanimated":"~4.3.1","react-native-safe-area-context":"~5.6.0","react-native-screens":"~4.16.0","react-native-svg":"^15.14.0","react-native-uitextview":"^2.2.0","react-native-unistyles":"^3.2.4","react-native-web":"~0.21.0","react-native-webview":"^13.16.0","react-native-worklets":"~0.8.3","tiny-invariant":"^1.3.3","use-sync-external-store":"^1.6.0",zod:"^4.4.3",zustand:"^5.0.9"},devDependencies:{"@playwright/test":"^1.56.1","@testing-library/dom":"^10.4.1","@testing-library/react":"^16.3.2","@types/chai":"^5.2.2","@types/markdown-it":"^14.1.2","@types/qrcode":"^1.5.6","@types/react":"~19.2.0","@types/ws":"^8.18.1","@vitest/browser":"^4.1.7","@vitest/browser-playwright":"^4.1.7","@vitest/coverage-v8":"^4.1.7","@xterm/headless":"^6.1.0-beta.213",dotenv:"^17.2.3",eslint:"^9.25.0","eslint-config-expo":"~10.0.0",jsdom:"^20.0.3","material-icon-theme":"^5.32.0",playwright:"^1.56.1","serve-sim":"^0.1.40",typescript:"~5.9.2",vitest:"^4.1.6",wrangler:"^4.105.0",ws:"^8.20.0"}}},3415,[]);
15061
15061
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.shouldUseDesktopDaemon=function(){return(0,n.isElectronRuntime)()},e.getDesktopDaemonStatus=async function(){return c(await(0,t.invokeDesktopCommand)("desktop_daemon_status"))},e.startDesktopDaemon=async function(){return c(await(0,t.invokeDesktopCommand)("start_desktop_daemon"))},e.stopDesktopDaemon=async function(n="manual_ipc"){return c(await(0,t.invokeDesktopCommand)("stop_desktop_daemon",{reason:n}))},e.restartDesktopDaemon=async function(){return c(await(0,t.invokeDesktopCommand)("restart_desktop_daemon"))},e.getDesktopDaemonLogs=async function(){return p(await(0,t.invokeDesktopCommand)("desktop_daemon_logs"))},e.getDesktopDaemonPairing=async function(){return k(await(0,t.invokeDesktopCommand)("desktop_daemon_pairing"))},e.getCliDaemonStatus=async function(){const n=await(0,t.invokeDesktopCommand)("cli_daemon_status");if("string"!=typeof n)throw new Error("Unexpected CLI daemon status response.");return n},e.listenToLocalTransportEvents=async function(t){const u=(0,n.getDesktopHost)()?.events?.on;if("function"!=typeof u)throw new Error("Desktop events API is unavailable.");const c=await u("local-daemon-transport-event",n=>{o(n)&&t({sessionId:s(n.sessionId)??"",kind:s(n.kind)??"error",text:s(n.text),binaryBase64:s(n.binaryBase64),code:l(n.code),reason:s(n.reason),error:s(n.error)})});return"function"==typeof c?c:()=>{}},e.openLocalTransportSession=async function(n){const o=await(0,t.invokeDesktopCommand)("open_local_daemon_transport",n);if("string"!=typeof o||0===o.trim().length)throw new Error("Unexpected local transport session response.");return o},e.sendLocalTransportMessage=async function(n){await(0,t.invokeDesktopCommand)("send_local_daemon_transport_message",Object.assign({sessionId:n.sessionId},n.text?{text:n.text}:{},n.binaryBase64?{binaryBase64:n.binaryBase64}:{}))},e.closeLocalTransportSession=async function(n){await(0,t.invokeDesktopCommand)("close_local_daemon_transport",{sessionId:n})},e.getCliInstallStatus=async function(){return f(await(0,t.invokeDesktopCommand)("get_cli_install_status"))},e.installCli=async function(){return f(await(0,t.invokeDesktopCommand)("install_cli"))},e.getSkillsStatus=async function(){return y(await(0,t.invokeDesktopCommand)("get_skills_status"))},e.installSkills=async function(){return y(await(0,t.invokeDesktopCommand)("install_skills"))},e.updateSkills=async function(){return y(await(0,t.invokeDesktopCommand)("update_skills"))},e.uninstallSkills=async function(){return y(await(0,t.invokeDesktopCommand)("uninstall_skills"))};var n=r(d[0]),t=r(d[1]);function o(n){return"object"==typeof n&&null!==n}function s(n){return"string"==typeof n&&n.trim().length>0?n:null}function l(n){return"number"==typeof n&&Number.isFinite(n)?n:null}function u(n){const t=s(n)?.toLowerCase();switch(t){case"starting":return"starting";case"running":return"running";case"errored":case"error":return"errored";default:return"stopped"}}function c(n){if(!o(n))throw new Error("Unexpected desktop daemon status response.");return{serverId:s(n.serverId)??"",status:u(n.status),listen:s(n.listen),hostname:s(n.hostname),pid:l(n.pid),home:s(n.home)??"",version:s(n.version),desktopManaged:!0===n.desktopManaged,error:s(n.error)}}function p(n){if(!o(n))throw new Error("Unexpected desktop daemon logs response.");return{logPath:s(n.logPath)??"",contents:"string"==typeof n.contents?n.contents:""}}function k(n){if(!o(n))throw new Error("Unexpected desktop daemon pairing response.");return{relayEnabled:!0===n.relayEnabled,url:s(n.url),qr:s(n.qr)}}function f(n){if(!o(n))throw new Error("Unexpected install status response.");return{installed:!0===n.installed}}function w(n){switch(n){case"not-installed":case"up-to-date":case"drift":return n;default:throw new Error(`Unexpected skills status state: ${String(n)}`)}}function _(n){if(!o(n))throw new Error("Unexpected skill op response.");const t=s(n.name);if(!t)throw new Error("Skill op missing name.");switch(n.kind){case"add":return{kind:"add",name:t};case"update":return{kind:"update",name:t};case"delete":return{kind:"delete",name:t};default:throw new Error(`Unexpected skill op kind: ${String(n.kind)}`)}}function y(n){if(!o(n))throw new Error("Unexpected skills status response.");const t=Array.isArray(n.ops)?n.ops.map(_):[];return{state:w(n.state),ops:t}}},3416,[3417,3419]);
15062
15062
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.getDesktopHost=n,e.isElectronRuntime=o,e.isElectronRuntimeMac=function(){if(!o())return!1;if("undefined"==typeof navigator)return!1;const t=n()?.platform?.toLowerCase();if("darwin"===t||"mac"===t||"macos"===t)return!0;const u=navigator.userAgent;return u.includes("Mac OS")||u.includes("Macintosh")},r(d[0]);var t=r(d[1]);function n(){return(0,t.getElectronHost)()}function o(){return null!==n()}},3417,[25,3418]);
15063
15063
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.getElectronHost=function(){if("undefined"==typeof window)return null;const t=window.paseoDesktop;if(!t||"object"!=typeof t)return null;return t}},3418,[]);
@@ -16708,5 +16708,5 @@ __d(function(g,r,_i,a,_m,_e,d){"use strict";var e,t=r(d[0]),n=this&&this.__creat
16708
16708
  __r(694);
16709
16709
  __r(341);
16710
16710
  __r(0);
16711
- //# sourceMappingURL=/_expo/static/js/web/index-7ca8a9a256b79485a77556fab01588ca.js.map
16712
- //# debugId=cd303ef6-1fd0-4c46-869b-20f8e964297e
16711
+ //# sourceMappingURL=/_expo/static/js/web/index-461552a3716a48e99f87634c93c7b220.js.map
16712
+ //# debugId=761b09c0-f320-4cd2-a3d9-075d30b5fbb7
@@ -191,6 +191,6 @@
191
191
  <body>
192
192
  <noscript>You need to enable JavaScript to run this app.</noscript>
193
193
  <div id="root"></div>
194
- <script src="/_expo/static/js/web/index-7ca8a9a256b79485a77556fab01588ca.js" defer></script>
194
+ <script src="/_expo/static/js/web/index-461552a3716a48e99f87634c93c7b220.js" defer></script>
195
195
  </body>
196
196
  </html>
Binary file
Binary file
@@ -0,0 +1,156 @@
1
+ // Spawn broker helper process. The daemon forks this small Node process once and
2
+ // asks it to run non-interactive subprocesses (git, gh, which, ...) over IPC, so
3
+ // the daemon itself never forks its multi-GB image: on WSL2 every direct spawn()
4
+ // from the daemon blocked its event loop for about 100ms.
5
+ //
6
+ // Protocol (JSON over the IPC channel):
7
+ // parent -> child { type: "spawn", id, command, args, cwd, env, shell }
8
+ // { type: "kill", id, signal }
9
+ // child -> parent { type: "spawned", id, pid }
10
+ // { type: "data", id, fd: 1 | 2, data: base64 }
11
+ // { type: "error", id, error: { message, code, errno, syscall, path } }
12
+ // { type: "close", id, exitCode, signal }
13
+ //
14
+ // Timeouts, output caps and exit-code policy stay in the parent, which kills the
15
+ // child through the "kill" message exactly like it would kill a direct ChildProcess.
16
+ import { spawn } from "node:child_process";
17
+
18
+ const children = new Map();
19
+
20
+ function send(message) {
21
+ // Returns false when the channel is backed up (or gone; the disconnect handler
22
+ // cleans up in that case).
23
+ return sendRaw(message);
24
+ }
25
+
26
+ function serializeError(error) {
27
+ return {
28
+ message: error?.message ?? String(error),
29
+ code: error?.code,
30
+ errno: error?.errno,
31
+ syscall: error?.syscall,
32
+ path: error?.path,
33
+ };
34
+ }
35
+
36
+ const CHUNK_BYTES = 64 * 1024;
37
+
38
+ // Forward a stream in bounded chunks, stop forwarding once the caller's cap is
39
+ // reached (the caller decides whether to kill), and pause the source while the
40
+ // IPC channel is backed up so output never piles up in memory on either side.
41
+ function forward(stream, id, fd, maxBytes) {
42
+ const cap = Number.isFinite(maxBytes) && maxBytes >= 0 ? maxBytes : Infinity;
43
+ let forwarded = 0;
44
+ stream.on("data", (chunk) => {
45
+ if (forwarded >= cap) return;
46
+ const slice = chunk.length > cap - forwarded ? chunk.subarray(0, cap - forwarded) : chunk;
47
+ forwarded += slice.length;
48
+ for (let offset = 0; offset < slice.length; offset += CHUNK_BYTES) {
49
+ const part = slice.subarray(offset, offset + CHUNK_BYTES);
50
+ const ok = send({ type: "data", id, fd, data: part.toString("base64") });
51
+ if (!ok) {
52
+ stream.pause();
53
+ waitForDrain(() => stream.resume());
54
+ }
55
+ }
56
+ });
57
+ }
58
+
59
+ const drainWaiters = [];
60
+ function waitForDrain(callback) {
61
+ drainWaiters.push(callback);
62
+ if (drainWaiters.length > 1) return;
63
+ // An empty message's send callback fires once everything queued before it
64
+ // has been written to the channel.
65
+ sendRaw({ type: "noop" }, () => {
66
+ const waiters = drainWaiters.splice(0);
67
+ for (const waiter of waiters) waiter();
68
+ });
69
+ }
70
+
71
+ function sendRaw(message, callback) {
72
+ if (!process.connected) return false;
73
+ try {
74
+ return process.send(message, undefined, undefined, callback);
75
+ } catch {
76
+ return false;
77
+ }
78
+ }
79
+
80
+ function handleSpawn(message) {
81
+ const { id } = message;
82
+ let child;
83
+ try {
84
+ child = spawn(message.command, message.args, {
85
+ cwd: message.cwd,
86
+ env: message.env,
87
+ shell: message.shell ?? false,
88
+ stdio: ["ignore", "pipe", "pipe"],
89
+ windowsHide: true,
90
+ });
91
+ } catch (error) {
92
+ send({ type: "error", id, error: serializeError(error) });
93
+ return;
94
+ }
95
+ children.set(id, child);
96
+ if (child.pid !== undefined) {
97
+ send({ type: "spawned", id, pid: child.pid });
98
+ }
99
+ forward(child.stdout, id, 1, message.maxStdoutBytes);
100
+ forward(child.stderr, id, 2, message.maxStderrBytes);
101
+ child.on("error", (error) => {
102
+ children.delete(id);
103
+ send({ type: "error", id, error: serializeError(error) });
104
+ });
105
+ child.on("close", (exitCode, signal) => {
106
+ children.delete(id);
107
+ send({ type: "close", id, exitCode, signal });
108
+ });
109
+ }
110
+
111
+ function killAll() {
112
+ for (const child of children.values()) {
113
+ try {
114
+ child.kill("SIGKILL");
115
+ } catch {
116
+ // already gone
117
+ }
118
+ }
119
+ children.clear();
120
+ }
121
+
122
+ process.on("message", (message) => {
123
+ if (!message || typeof message !== "object") return;
124
+ if (message.type === "spawn") {
125
+ handleSpawn(message);
126
+ return;
127
+ }
128
+ if (message.type === "kill") {
129
+ const child = children.get(message.id);
130
+ if (child) {
131
+ try {
132
+ child.kill(message.signal ?? "SIGTERM");
133
+ } catch {
134
+ // already gone
135
+ }
136
+ }
137
+ return;
138
+ }
139
+ if (message.type === "crash-for-test" && process.env.PASEO_SPAWN_BROKER_TEST_HOOKS === "1") {
140
+ process.exit(1);
141
+ }
142
+ });
143
+
144
+ // The daemon owns us: when its IPC channel closes (clean shutdown or crash) we take
145
+ // our children down and exit, so nothing is orphaned.
146
+ process.on("disconnect", () => {
147
+ killAll();
148
+ process.exit(0);
149
+ });
150
+
151
+ for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"]) {
152
+ process.on(signal, () => {
153
+ killAll();
154
+ process.exit(0);
155
+ });
156
+ }