miladyai 2.0.0-alpha.27

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 (241) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +7 -0
  2. package/dist/actions/emote.js +64 -0
  3. package/dist/actions/restart.js +81 -0
  4. package/dist/actions/send-message.js +152 -0
  5. package/dist/agent-admin-routes.js +82 -0
  6. package/dist/agent-lifecycle-routes.js +79 -0
  7. package/dist/agent-transfer-routes.js +102 -0
  8. package/dist/api/agent-admin-routes.js +82 -0
  9. package/dist/api/agent-lifecycle-routes.js +79 -0
  10. package/dist/api/agent-transfer-routes.js +102 -0
  11. package/dist/api/apps-hyperscape-routes.js +58 -0
  12. package/dist/api/apps-routes.js +114 -0
  13. package/dist/api/auth-routes.js +56 -0
  14. package/dist/api/autonomy-routes.js +44 -0
  15. package/dist/api/bug-report-routes.js +111 -0
  16. package/dist/api/character-routes.js +195 -0
  17. package/dist/api/cloud-routes.js +330 -0
  18. package/dist/api/cloud-status-routes.js +155 -0
  19. package/dist/api/compat-utils.js +111 -0
  20. package/dist/api/database.js +735 -0
  21. package/dist/api/diagnostics-routes.js +205 -0
  22. package/dist/api/drop-service.js +134 -0
  23. package/dist/api/early-logs.js +86 -0
  24. package/dist/api/http-helpers.js +131 -0
  25. package/dist/api/knowledge-routes.js +534 -0
  26. package/dist/api/memory-bounds.js +71 -0
  27. package/dist/api/models-routes.js +28 -0
  28. package/dist/api/og-tracker.js +36 -0
  29. package/dist/api/permissions-routes.js +109 -0
  30. package/dist/api/plugin-validation.js +198 -0
  31. package/dist/api/provider-switch-config.js +41 -0
  32. package/dist/api/registry-routes.js +86 -0
  33. package/dist/api/registry-service.js +164 -0
  34. package/dist/api/sandbox-routes.js +1112 -0
  35. package/dist/api/server.js +7949 -0
  36. package/dist/api/subscription-routes.js +172 -0
  37. package/dist/api/terminal-run-limits.js +24 -0
  38. package/dist/api/training-routes.js +158 -0
  39. package/dist/api/trajectory-routes.js +300 -0
  40. package/dist/api/trigger-routes.js +246 -0
  41. package/dist/api/twitter-verify.js +134 -0
  42. package/dist/api/tx-service.js +108 -0
  43. package/dist/api/wallet-routes.js +266 -0
  44. package/dist/api/wallet.js +568 -0
  45. package/dist/api/whatsapp-routes.js +182 -0
  46. package/dist/api/zip-utils.js +109 -0
  47. package/dist/apps-hyperscape-routes.js +58 -0
  48. package/dist/apps-routes.js +114 -0
  49. package/dist/ascii.js +20 -0
  50. package/dist/auth/anthropic.js +44 -0
  51. package/dist/auth/apply-stealth.js +41 -0
  52. package/dist/auth/claude-code-stealth.js +78 -0
  53. package/dist/auth/credentials.js +156 -0
  54. package/dist/auth/index.js +5 -0
  55. package/dist/auth/openai-codex.js +66 -0
  56. package/dist/auth/types.js +9 -0
  57. package/dist/auth-routes.js +56 -0
  58. package/dist/autonomy-routes.js +44 -0
  59. package/dist/bug-report-routes.js +111 -0
  60. package/dist/build-info.json +6 -0
  61. package/dist/character-routes.js +195 -0
  62. package/dist/cli/argv.js +63 -0
  63. package/dist/cli/banner.js +34 -0
  64. package/dist/cli/cli-name.js +21 -0
  65. package/dist/cli/cli-utils.js +16 -0
  66. package/dist/cli/git-commit.js +78 -0
  67. package/dist/cli/parse-duration.js +15 -0
  68. package/dist/cli/plugins-cli.js +590 -0
  69. package/dist/cli/profile-utils.js +9 -0
  70. package/dist/cli/profile.js +95 -0
  71. package/dist/cli/program/build-program.js +17 -0
  72. package/dist/cli/program/command-registry.js +23 -0
  73. package/dist/cli/program/help.js +47 -0
  74. package/dist/cli/program/preaction.js +33 -0
  75. package/dist/cli/program/register.config.js +106 -0
  76. package/dist/cli/program/register.configure.js +20 -0
  77. package/dist/cli/program/register.dashboard.js +124 -0
  78. package/dist/cli/program/register.models.js +23 -0
  79. package/dist/cli/program/register.setup.js +36 -0
  80. package/dist/cli/program/register.start.js +22 -0
  81. package/dist/cli/program/register.subclis.js +70 -0
  82. package/dist/cli/program/register.tui.js +163 -0
  83. package/dist/cli/program/register.update.js +154 -0
  84. package/dist/cli/program.js +3 -0
  85. package/dist/cli/run-main.js +37 -0
  86. package/dist/cli/version.js +7 -0
  87. package/dist/cloud/validate-url.js +93 -0
  88. package/dist/cloud-routes.js +330 -0
  89. package/dist/cloud-status-routes.js +155 -0
  90. package/dist/compat-utils.js +111 -0
  91. package/dist/config/config.js +69 -0
  92. package/dist/config/env-vars.js +19 -0
  93. package/dist/config/includes.js +121 -0
  94. package/dist/config/object-utils.js +7 -0
  95. package/dist/config/paths.js +38 -0
  96. package/dist/config/plugin-auto-enable.js +231 -0
  97. package/dist/config/schema.js +864 -0
  98. package/dist/config/telegram-custom-commands.js +76 -0
  99. package/dist/config/zod-schema.agent-runtime.js +519 -0
  100. package/dist/config/zod-schema.core.js +538 -0
  101. package/dist/config/zod-schema.hooks.js +103 -0
  102. package/dist/config/zod-schema.js +488 -0
  103. package/dist/config/zod-schema.providers-core.js +785 -0
  104. package/dist/config/zod-schema.session.js +73 -0
  105. package/dist/core-plugins.js +37 -0
  106. package/dist/custom-actions.js +250 -0
  107. package/dist/database.js +735 -0
  108. package/dist/diagnostics/integration-observability.js +57 -0
  109. package/dist/diagnostics-routes.js +205 -0
  110. package/dist/drop-service.js +134 -0
  111. package/dist/early-logs.js +24 -0
  112. package/dist/eliza.js +2061 -0
  113. package/dist/emotes/catalog.js +271 -0
  114. package/dist/entry.js +40 -0
  115. package/dist/hooks/discovery.js +167 -0
  116. package/dist/hooks/eligibility.js +64 -0
  117. package/dist/hooks/index.js +4 -0
  118. package/dist/hooks/loader.js +147 -0
  119. package/dist/hooks/registry.js +55 -0
  120. package/dist/http-helpers.js +131 -0
  121. package/dist/index.js +49 -0
  122. package/dist/knowledge-routes.js +534 -0
  123. package/dist/memory-bounds.js +71 -0
  124. package/dist/milady-plugin.js +90 -0
  125. package/dist/models-routes.js +28 -0
  126. package/dist/onboarding-names.js +78 -0
  127. package/dist/onboarding-presets.js +922 -0
  128. package/dist/package.json +1 -0
  129. package/dist/permissions-routes.js +109 -0
  130. package/dist/plugin-validation.js +107 -0
  131. package/dist/plugins/whatsapp/actions.js +91 -0
  132. package/dist/plugins/whatsapp/index.js +16 -0
  133. package/dist/plugins/whatsapp/service.js +270 -0
  134. package/dist/provider-switch-config.js +41 -0
  135. package/dist/providers/admin-trust.js +46 -0
  136. package/dist/providers/autonomous-state.js +101 -0
  137. package/dist/providers/session-bridge.js +86 -0
  138. package/dist/providers/session-utils.js +36 -0
  139. package/dist/providers/simple-mode.js +50 -0
  140. package/dist/providers/ui-catalog.js +15 -0
  141. package/dist/providers/workspace-provider.js +93 -0
  142. package/dist/providers/workspace.js +348 -0
  143. package/dist/registry-routes.js +86 -0
  144. package/dist/registry-service.js +164 -0
  145. package/dist/restart.js +40 -0
  146. package/dist/runtime/core-plugins.js +37 -0
  147. package/dist/runtime/custom-actions.js +250 -0
  148. package/dist/runtime/eliza.js +2061 -0
  149. package/dist/runtime/embedding-manager-support.js +185 -0
  150. package/dist/runtime/embedding-manager.js +193 -0
  151. package/dist/runtime/embedding-presets.js +54 -0
  152. package/dist/runtime/embedding-state.js +8 -0
  153. package/dist/runtime/milady-plugin.js +90 -0
  154. package/dist/runtime/onboarding-names.js +78 -0
  155. package/dist/runtime/restart.js +40 -0
  156. package/dist/runtime/version.js +7 -0
  157. package/dist/sandbox-routes.js +1112 -0
  158. package/dist/security/audit-log.js +149 -0
  159. package/dist/security/network-policy.js +70 -0
  160. package/dist/server.js +7949 -0
  161. package/dist/services/agent-export.js +559 -0
  162. package/dist/services/app-manager.js +389 -0
  163. package/dist/services/browser-capture.js +86 -0
  164. package/dist/services/fallback-training-service.js +128 -0
  165. package/dist/services/mcp-marketplace.js +134 -0
  166. package/dist/services/plugin-installer.js +396 -0
  167. package/dist/services/plugin-manager-types.js +15 -0
  168. package/dist/services/registry-client-app-meta.js +144 -0
  169. package/dist/services/registry-client-endpoints.js +166 -0
  170. package/dist/services/registry-client-local.js +271 -0
  171. package/dist/services/registry-client-network.js +93 -0
  172. package/dist/services/registry-client-queries.js +70 -0
  173. package/dist/services/registry-client.js +157 -0
  174. package/dist/services/sandbox-engine.js +511 -0
  175. package/dist/services/sandbox-manager.js +297 -0
  176. package/dist/services/self-updater.js +175 -0
  177. package/dist/services/skill-catalog-client.js +119 -0
  178. package/dist/services/skill-marketplace.js +521 -0
  179. package/dist/services/stream-manager.js +236 -0
  180. package/dist/services/update-checker.js +121 -0
  181. package/dist/services/update-notifier.js +29 -0
  182. package/dist/services/version-compat.js +78 -0
  183. package/dist/services/whatsapp-pairing.js +196 -0
  184. package/dist/shared/ui-catalog-prompt.js +728 -0
  185. package/dist/subscription-routes.js +172 -0
  186. package/dist/terminal/links.js +19 -0
  187. package/dist/terminal/palette.js +14 -0
  188. package/dist/terminal/theme.js +25 -0
  189. package/dist/terminal-run-limits.js +24 -0
  190. package/dist/training-routes.js +158 -0
  191. package/dist/trajectory-routes.js +300 -0
  192. package/dist/trigger-routes.js +246 -0
  193. package/dist/triggers/action.js +218 -0
  194. package/dist/triggers/runtime.js +281 -0
  195. package/dist/triggers/scheduling.js +295 -0
  196. package/dist/triggers/types.js +5 -0
  197. package/dist/tui/components/assistant-message.js +76 -0
  198. package/dist/tui/components/chat-editor.js +34 -0
  199. package/dist/tui/components/embeddings-overlay.js +46 -0
  200. package/dist/tui/components/footer.js +60 -0
  201. package/dist/tui/components/index.js +15 -0
  202. package/dist/tui/components/modal-frame.js +45 -0
  203. package/dist/tui/components/modal-style.js +15 -0
  204. package/dist/tui/components/model-selector.js +70 -0
  205. package/dist/tui/components/pinned-chat-layout.js +46 -0
  206. package/dist/tui/components/plugins-endpoints-tab.js +196 -0
  207. package/dist/tui/components/plugins-installed-tab-view.js +69 -0
  208. package/dist/tui/components/plugins-installed-tab.js +319 -0
  209. package/dist/tui/components/plugins-overlay-catalog.js +81 -0
  210. package/dist/tui/components/plugins-overlay-data-api.js +21 -0
  211. package/dist/tui/components/plugins-overlay-data-shared.js +20 -0
  212. package/dist/tui/components/plugins-overlay-data.js +323 -0
  213. package/dist/tui/components/plugins-overlay.js +117 -0
  214. package/dist/tui/components/plugins-store-tab.js +148 -0
  215. package/dist/tui/components/settings-overlay.js +61 -0
  216. package/dist/tui/components/status-bar.js +64 -0
  217. package/dist/tui/components/tool-execution.js +68 -0
  218. package/dist/tui/components/user-message.js +22 -0
  219. package/dist/tui/eliza-tui-bridge.js +606 -0
  220. package/dist/tui/index.js +370 -0
  221. package/dist/tui/modal-presets.js +33 -0
  222. package/dist/tui/model-spec.js +46 -0
  223. package/dist/tui/sse-parser.js +78 -0
  224. package/dist/tui/theme.js +110 -0
  225. package/dist/tui/titlebar-spinner.js +62 -0
  226. package/dist/tui/tui-app.js +311 -0
  227. package/dist/tui/ws-client.js +215 -0
  228. package/dist/twitter-verify.js +134 -0
  229. package/dist/tx-service.js +108 -0
  230. package/dist/utils/exec-safety.js +17 -0
  231. package/dist/utils/globals.js +20 -0
  232. package/dist/utils/milady-root.js +61 -0
  233. package/dist/utils/number-parsing.js +37 -0
  234. package/dist/version-resolver.js +37 -0
  235. package/dist/version.js +7 -0
  236. package/dist/wallet-routes.js +266 -0
  237. package/dist/wallet.js +568 -0
  238. package/dist/whatsapp-routes.js +182 -0
  239. package/dist/zip-utils.js +109 -0
  240. package/milady.mjs +14 -0
  241. package/package.json +111 -0
@@ -0,0 +1,389 @@
1
+ import { deriveEvmAddress, deriveSolanaAddress } from "../api/wallet.js";
2
+ import { getPluginInfo, getRegistryPlugins } from "./registry-client.js";
3
+ import * as path$1 from "node:path";
4
+ import { logger } from "@elizaos/core";
5
+ import * as fs$1 from "node:fs";
6
+
7
+ //#region src/services/app-manager.ts
8
+ /**
9
+ * App Manager — manages app lifecycle: discover, install plugin, show viewer.
10
+ *
11
+ * Apps are hosted services. The manager's job is:
12
+ * 1. List/search apps from the registry
13
+ * 2. Install the game's plugin onto the agent (triggers restart)
14
+ * 3. Return the viewer URL so the UI can embed the game client in an iframe
15
+ *
16
+ * @module services/app-manager
17
+ */
18
+ const LOCAL_PLUGINS_DIR = "plugins";
19
+ const DEFAULT_VIEWER_SANDBOX = "allow-scripts allow-same-origin allow-popups";
20
+ const HYPERSCAPE_APP_NAME = "@elizaos/app-hyperscape";
21
+ const HYPERSCAPE_AUTH_MESSAGE_TYPE = "HYPERSCAPE_AUTH";
22
+ const RS_2004SCAPE_APP_NAME = "@elizaos/app-2004scape";
23
+ const RS_2004SCAPE_AUTH_MESSAGE_TYPE = "RS_2004SCAPE_AUTH";
24
+ const SAFE_APP_URL_PROTOCOLS = new Set(["http:", "https:"]);
25
+ function resolvePluginPackageName(appInfo) {
26
+ const npmPackage = appInfo.npm.package.trim();
27
+ return npmPackage && npmPackage.length > 0 ? npmPackage : appInfo.name;
28
+ }
29
+ function mergeAppMeta(appInfo, meta) {
30
+ if (!meta) return;
31
+ appInfo.viewer = meta.viewer ?? appInfo.viewer;
32
+ appInfo.launchUrl = meta.launchUrl ?? appInfo.launchUrl;
33
+ appInfo.launchType = meta.launchType ?? appInfo.launchType;
34
+ appInfo.displayName = meta.displayName ?? appInfo.displayName;
35
+ appInfo.category = meta.category ?? appInfo.category;
36
+ appInfo.capabilities = meta.capabilities ?? appInfo.capabilities;
37
+ appInfo.icon = meta.icon ?? appInfo.icon;
38
+ }
39
+ function isAutoInstallable(appInfo) {
40
+ const supportsRuntime = appInfo.supports.v0 || appInfo.supports.v1 || appInfo.supports.v2;
41
+ const hasVersion = Boolean(appInfo.npm.v0Version || appInfo.npm.v1Version || appInfo.npm.v2Version);
42
+ return supportsRuntime && hasVersion;
43
+ }
44
+ /**
45
+ * Check if a plugin exists locally in the plugins/ directory.
46
+ * Local plugins don't need to be installed - they're already available.
47
+ */
48
+ function isLocalPlugin(appInfo) {
49
+ const pluginsDir = path$1.resolve(process.cwd(), LOCAL_PLUGINS_DIR);
50
+ if (!fs$1.existsSync(pluginsDir)) return false;
51
+ const possibleDirs = [appInfo.name.replace(/^@[^/]+\//, ""), appInfo.name.replace("/", "-")];
52
+ for (const dirName of possibleDirs) {
53
+ const pluginPath = path$1.join(pluginsDir, dirName);
54
+ const pluginJsonPath = path$1.join(pluginPath, "elizaos.plugin.json");
55
+ if (fs$1.existsSync(pluginJsonPath)) return true;
56
+ }
57
+ return false;
58
+ }
59
+ function getTemplateFallbackValue(key) {
60
+ if (key === "RS_SDK_BOT_NAME") {
61
+ const runtimeBotName = process.env.BOT_NAME?.trim();
62
+ if (runtimeBotName && runtimeBotName.length > 0) return runtimeBotName;
63
+ return "testbot";
64
+ }
65
+ if (key === "HYPERSCAPE_CLIENT_URL") return "http://localhost:3333";
66
+ if (key === "HYPERSCAPE_SERVER_URL") return "ws://localhost:5555/ws";
67
+ }
68
+ function substituteTemplateVars(raw) {
69
+ return raw.replace(/\{([A-Z0-9_]+)\}/g, (_full, key) => {
70
+ const value = process.env[key];
71
+ if (value && value.trim().length > 0) return value.trim();
72
+ return getTemplateFallbackValue(key) ?? "";
73
+ });
74
+ }
75
+ function buildViewerUrl(baseUrl, embedParams) {
76
+ if (!embedParams || Object.keys(embedParams).length === 0) return substituteTemplateVars(baseUrl);
77
+ const [beforeHash, hashPartRaw] = substituteTemplateVars(baseUrl).split("#", 2);
78
+ const [pathPart, queryPartRaw] = beforeHash.split("?", 2);
79
+ const queryParams = new URLSearchParams(queryPartRaw ?? "");
80
+ for (const [key, rawValue] of Object.entries(embedParams)) queryParams.set(key, substituteTemplateVars(rawValue));
81
+ const query = queryParams.toString();
82
+ const hash = hashPartRaw ? `#${hashPartRaw}` : "";
83
+ return `${pathPart}${query.length > 0 ? `?${query}` : ""}${hash}`;
84
+ }
85
+ function normalizeSafeAppUrl(url) {
86
+ const trimmed = url.trim();
87
+ if (!trimmed) return null;
88
+ if (trimmed.startsWith("/")) return trimmed.startsWith("//") ? null : trimmed;
89
+ try {
90
+ const parsed = new URL(trimmed);
91
+ if (!SAFE_APP_URL_PROTOCOLS.has(parsed.protocol)) return null;
92
+ return trimmed;
93
+ } catch {
94
+ return null;
95
+ }
96
+ }
97
+ function buildViewerAuthMessage(appName, postMessageAuth) {
98
+ if (!postMessageAuth) return void 0;
99
+ if (appName === HYPERSCAPE_APP_NAME) {
100
+ const authToken = process.env.HYPERSCAPE_AUTH_TOKEN?.trim();
101
+ const characterId = process.env.HYPERSCAPE_CHARACTER_ID?.trim();
102
+ if (!authToken && !characterId) return void 0;
103
+ const sessionToken = process.env.HYPERSCAPE_SESSION_TOKEN?.trim();
104
+ const agentId = process.env.HYPERSCAPE_EMBED_AGENT_ID?.trim();
105
+ return {
106
+ type: HYPERSCAPE_AUTH_MESSAGE_TYPE,
107
+ authToken: authToken || void 0,
108
+ characterId: characterId || void 0,
109
+ sessionToken: sessionToken && sessionToken.length > 0 ? sessionToken : void 0,
110
+ agentId: agentId && agentId.length > 0 ? agentId : void 0
111
+ };
112
+ }
113
+ if (appName === RS_2004SCAPE_APP_NAME) return {
114
+ type: RS_2004SCAPE_AUTH_MESSAGE_TYPE,
115
+ authToken: process.env.RS_SDK_BOT_NAME?.trim() || process.env.BOT_NAME?.trim() || "testbot",
116
+ sessionToken: process.env.RS_SDK_BOT_PASSWORD?.trim() || process.env.BOT_PASSWORD?.trim() || ""
117
+ };
118
+ }
119
+ function buildViewerConfig(appInfo, launchUrl) {
120
+ const viewerInfo = appInfo.viewer;
121
+ if (viewerInfo) {
122
+ const requestedPostMessageAuth = Boolean(viewerInfo.postMessageAuth);
123
+ const authMessage = buildViewerAuthMessage(appInfo.name, requestedPostMessageAuth);
124
+ const postMessageAuth = requestedPostMessageAuth && Boolean(authMessage);
125
+ if (requestedPostMessageAuth && !authMessage) if (appInfo.name === HYPERSCAPE_APP_NAME) logger.info(`[app-manager] ${appInfo.name} auth token not configured; launching embedded viewer without postMessage auth.`);
126
+ else logger.warn(`[app-manager] ${appInfo.name} requires postMessage auth but no auth payload was generated.`);
127
+ const viewerUrl = normalizeSafeAppUrl(buildViewerUrl(viewerInfo.url, viewerInfo.embedParams));
128
+ if (!viewerUrl) throw new Error(`Refusing to launch app "${appInfo.name}": unsafe viewer URL`);
129
+ return {
130
+ url: viewerUrl,
131
+ embedParams: viewerInfo.embedParams,
132
+ postMessageAuth,
133
+ sandbox: viewerInfo.sandbox ?? DEFAULT_VIEWER_SANDBOX,
134
+ authMessage
135
+ };
136
+ }
137
+ if ((appInfo.launchType === "connect" || appInfo.launchType === "local") && launchUrl) {
138
+ const viewerUrl = normalizeSafeAppUrl(launchUrl);
139
+ if (!viewerUrl) throw new Error(`Refusing to launch app "${appInfo.name}": unsafe launch URL`);
140
+ return {
141
+ url: viewerUrl,
142
+ sandbox: DEFAULT_VIEWER_SANDBOX
143
+ };
144
+ }
145
+ return null;
146
+ }
147
+ /**
148
+ * Get wallet addresses from agent runtime settings (character secrets).
149
+ * Falls back to process.env if runtime is not available.
150
+ */
151
+ function getWalletAddressesFromRuntime(runtime) {
152
+ let evmAddress = null;
153
+ let solanaAddress = null;
154
+ const evmKey = (runtime?.getSetting?.("EVM_PRIVATE_KEY"))?.trim() || process.env.EVM_PRIVATE_KEY?.trim();
155
+ if (evmKey) try {
156
+ evmAddress = deriveEvmAddress(evmKey);
157
+ } catch (e) {
158
+ logger.warn(`[app-manager] Bad EVM key: ${e}`);
159
+ }
160
+ const solKey = (runtime?.getSetting?.("SOLANA_PRIVATE_KEY"))?.trim() || process.env.SOLANA_PRIVATE_KEY?.trim();
161
+ if (solKey) try {
162
+ solanaAddress = deriveSolanaAddress(solKey);
163
+ } catch (e) {
164
+ logger.warn(`[app-manager] Bad SOL key: ${e}`);
165
+ }
166
+ return {
167
+ evmAddress,
168
+ solanaAddress
169
+ };
170
+ }
171
+ /**
172
+ * Auto-provision a hyperscape agent using wallet-based authentication.
173
+ * The agent's wallet address becomes its identity - no manual registration needed.
174
+ * Uses agent.character.settings.secrets for wallet credentials.
175
+ */
176
+ async function autoProvisionHyperscapeAgent(runtime) {
177
+ const existingCharId = (runtime?.getSetting?.("HYPERSCAPE_CHARACTER_ID"))?.trim() || process.env.HYPERSCAPE_CHARACTER_ID?.trim();
178
+ const existingToken = (runtime?.getSetting?.("HYPERSCAPE_AUTH_TOKEN"))?.trim() || process.env.HYPERSCAPE_AUTH_TOKEN?.trim();
179
+ if (existingCharId && existingToken) {
180
+ logger.info(`[app-manager] Hyperscape already configured with character: ${existingCharId}`);
181
+ return {
182
+ characterId: existingCharId,
183
+ authToken: existingToken
184
+ };
185
+ }
186
+ const walletAddresses = getWalletAddressesFromRuntime(runtime);
187
+ const walletAddress = walletAddresses.evmAddress || walletAddresses.solanaAddress;
188
+ if (!walletAddress) {
189
+ logger.warn("[app-manager] No wallet address found for hyperscape auto-auth (need EVM_PRIVATE_KEY or SOLANA_PRIVATE_KEY in character secrets)");
190
+ return null;
191
+ }
192
+ const walletType = walletAddresses.evmAddress ? "evm" : "solana";
193
+ const apiBaseUrl = ((runtime?.getSetting?.("HYPERSCAPE_SERVER_URL"))?.trim() || process.env.HYPERSCAPE_SERVER_URL?.trim() || "ws://localhost:5555/ws").replace(/^ws:/, "http:").replace(/^wss:/, "https:").replace(/\/ws$/, "");
194
+ const agentName = runtime?.character?.name || (runtime?.getSetting?.("BOT_NAME"))?.trim() || process.env.BOT_NAME || "Agent";
195
+ try {
196
+ logger.info(`[app-manager] Auto-provisioning hyperscape agent with wallet: ${walletAddress.slice(0, 10)}...`);
197
+ const response = await fetch(`${apiBaseUrl}/api/agents/wallet-auth`, {
198
+ method: "POST",
199
+ headers: { "Content-Type": "application/json" },
200
+ body: JSON.stringify({
201
+ walletAddress,
202
+ walletType,
203
+ agentName
204
+ })
205
+ });
206
+ if (!response.ok) {
207
+ const errorText = await response.text().catch(() => "Unknown error");
208
+ logger.warn(`[app-manager] Hyperscape wallet auth failed: ${response.status} ${errorText}`);
209
+ return null;
210
+ }
211
+ const result = await response.json();
212
+ if (!result.success || !result.authToken || !result.characterId) {
213
+ logger.warn("[app-manager] Hyperscape wallet auth returned failure");
214
+ return null;
215
+ }
216
+ process.env.HYPERSCAPE_CHARACTER_ID = result.characterId;
217
+ process.env.HYPERSCAPE_AUTH_TOKEN = result.authToken;
218
+ logger.info(`[app-manager] Auto-provisioned hyperscape agent: ${result.characterId}`);
219
+ return {
220
+ characterId: result.characterId,
221
+ authToken: result.authToken
222
+ };
223
+ } catch (error) {
224
+ logger.warn(`[app-manager] Failed to auto-provision hyperscape agent: ${error instanceof Error ? error.message : String(error)}`);
225
+ return null;
226
+ }
227
+ }
228
+ var AppManager = class {
229
+ constructor() {
230
+ this.activeSessions = /* @__PURE__ */ new Map();
231
+ }
232
+ async listAvailable(pluginManager) {
233
+ const registry = await pluginManager.refreshRegistry();
234
+ try {
235
+ const localRegistry = await getRegistryPlugins();
236
+ for (const [name, info] of localRegistry) if (!registry.has(name) && info.kind === "app") registry.set(name, info);
237
+ } catch {}
238
+ return Array.from(registry.values()).filter((plugin) => {
239
+ if (plugin.kind === "app") return true;
240
+ const name = plugin.name.toLowerCase();
241
+ const npmPackage = plugin.npm.package.toLowerCase();
242
+ return name.includes("/app-") || npmPackage.includes("/app-");
243
+ }).map((p) => {
244
+ const meta = p.appMeta;
245
+ if (!meta) return p;
246
+ return {
247
+ ...p,
248
+ displayName: meta.displayName,
249
+ launchType: meta.launchType,
250
+ launchUrl: meta.launchUrl,
251
+ icon: meta.icon,
252
+ category: meta.category,
253
+ capabilities: meta.capabilities,
254
+ viewer: meta.viewer
255
+ };
256
+ });
257
+ }
258
+ async search(pluginManager, query, limit = 15) {
259
+ return (await pluginManager.searchRegistry(query, limit)).filter((result) => {
260
+ const name = result.name.toLowerCase();
261
+ const npmPackage = result.npmPackage.toLowerCase();
262
+ return name.includes("/app-") || npmPackage.includes("/app-");
263
+ });
264
+ }
265
+ async getInfo(pluginManager, name) {
266
+ return pluginManager.getRegistryPlugin(name);
267
+ }
268
+ /**
269
+ * Launch an app: install its plugin (if needed) and return the viewer URL.
270
+ *
271
+ * The plugin connects the agent to the game server. The viewer URL is what
272
+ * the UI shows in an iframe so the user can watch the agent play.
273
+ *
274
+ * After installing a new plugin, the agent needs to restart. The UI should
275
+ * handle this by showing "connecting..." while the runtime restarts.
276
+ */
277
+ async launch(pluginManager, name, onProgress, runtime) {
278
+ let appInfo = await pluginManager.getRegistryPlugin(name);
279
+ try {
280
+ const localInfo = await getPluginInfo(name);
281
+ if (localInfo) {
282
+ const meta = localInfo.appMeta;
283
+ if (!appInfo) {
284
+ appInfo = { ...localInfo };
285
+ mergeAppMeta(appInfo, meta);
286
+ } else if (meta && !appInfo.viewer) {
287
+ mergeAppMeta(appInfo, meta);
288
+ appInfo.kind = localInfo.kind ?? appInfo.kind;
289
+ }
290
+ }
291
+ } catch {}
292
+ if (!appInfo) throw new Error(`App "${name}" not found in the registry.`);
293
+ const pluginName = resolvePluginPackageName(appInfo);
294
+ const isLocal = isLocalPlugin(appInfo);
295
+ const alreadyInstalled = (await pluginManager.listInstalledPlugins()).some((p) => p.name === pluginName);
296
+ let pluginInstalled = alreadyInstalled || isLocal;
297
+ let needsRestart = false;
298
+ if (isLocal) logger.info(`[app-manager] Using local plugin for ${name}: ${pluginName}`);
299
+ else if (!alreadyInstalled) if (isAutoInstallable(appInfo)) {
300
+ logger.info(`[app-manager] Installing plugin for app: ${pluginName}`);
301
+ const result = await pluginManager.installPlugin(pluginName, onProgress);
302
+ if (!result.success) throw new Error(`Failed to install plugin "${pluginName}": ${result.error}`);
303
+ pluginInstalled = true;
304
+ needsRestart = result.requiresRestart;
305
+ logger.info(`[app-manager] Plugin installed: ${pluginName} v${result.version}`);
306
+ } else logger.info(`[app-manager] Skipping plugin install for ${name}: no installable runtime package/version in registry metadata.`);
307
+ else logger.info(`[app-manager] Plugin already installed: ${pluginName}`);
308
+ if (name === HYPERSCAPE_APP_NAME) {
309
+ if (!await autoProvisionHyperscapeAgent(runtime) && !process.env.HYPERSCAPE_CHARACTER_ID?.trim() && !process.env.HYPERSCAPE_AUTH_TOKEN?.trim()) {
310
+ logger.warn("[app-manager] Hyperscape requires authentication but auto-provisioning failed. Set HYPERSCAPE_CHARACTER_ID and HYPERSCAPE_AUTH_TOKEN, or ensure the hyperscape server is running.");
311
+ throw new Error("Hyperscape authentication required. Set HYPERSCAPE_CHARACTER_ID and HYPERSCAPE_AUTH_TOKEN, or ensure the hyperscape server is running at " + (process.env.HYPERSCAPE_SERVER_URL || "localhost:5555") + " for auto-provisioning.");
312
+ }
313
+ }
314
+ const resolvedLaunchUrl = appInfo.launchUrl ? substituteTemplateVars(appInfo.launchUrl) : null;
315
+ const launchUrl = resolvedLaunchUrl ? normalizeSafeAppUrl(resolvedLaunchUrl) : null;
316
+ if (resolvedLaunchUrl && !launchUrl) throw new Error(`Refusing to launch app "${appInfo.name}": unsafe launch URL`);
317
+ const viewer = buildViewerConfig(appInfo, launchUrl);
318
+ this.activeSessions.set(name, {
319
+ appName: name,
320
+ pluginName,
321
+ launchType: appInfo.launchType ?? "connect",
322
+ launchUrl,
323
+ viewerUrl: viewer?.url ?? null,
324
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
325
+ });
326
+ return {
327
+ pluginInstalled,
328
+ needsRestart,
329
+ displayName: appInfo.displayName ?? appInfo.name,
330
+ launchType: appInfo.launchType ?? "connect",
331
+ launchUrl,
332
+ viewer
333
+ };
334
+ }
335
+ async stop(pluginManager, name) {
336
+ const appInfo = await pluginManager.getRegistryPlugin(name);
337
+ if (!appInfo) throw new Error(`App "${name}" not found in the registry.`);
338
+ const hadSession = this.activeSessions.delete(name);
339
+ const pluginName = resolvePluginPackageName(appInfo);
340
+ const isPluginInstalled = (await pluginManager.listInstalledPlugins()).some((plugin) => plugin.name === pluginName);
341
+ if (!hadSession && !isPluginInstalled) return {
342
+ success: false,
343
+ appName: name,
344
+ stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
345
+ pluginUninstalled: false,
346
+ needsRestart: false,
347
+ stopScope: "no-op",
348
+ message: `No active session or installed plugin found for "${name}".`
349
+ };
350
+ if (isPluginInstalled) {
351
+ const uninstallResult = await pluginManager.uninstallPlugin(pluginName);
352
+ if (!uninstallResult.success) throw new Error(`Failed to stop "${name}": ${uninstallResult.error ?? "plugin uninstall failed"}`);
353
+ return {
354
+ success: true,
355
+ appName: name,
356
+ stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
357
+ pluginUninstalled: true,
358
+ needsRestart: uninstallResult.requiresRestart,
359
+ stopScope: "plugin-uninstalled",
360
+ message: uninstallResult.requiresRestart ? `${name} disconnected and plugin uninstalled. Agent restart required.` : `${name} disconnected and plugin uninstalled.`
361
+ };
362
+ }
363
+ return {
364
+ success: true,
365
+ appName: name,
366
+ stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
367
+ pluginUninstalled: false,
368
+ needsRestart: false,
369
+ stopScope: "viewer-session",
370
+ message: `${name} viewer session stopped.`
371
+ };
372
+ }
373
+ /** List apps whose plugins are currently installed on the agent. */
374
+ async listInstalled(pluginManager) {
375
+ return (await pluginManager.listInstalledPlugins()).filter((p) => {
376
+ const name = p.name.toLowerCase();
377
+ return name.includes("/app-") || name === "@lunchtable/plugin-ltcg";
378
+ }).map((p) => ({
379
+ name: p.name,
380
+ displayName: p.name.replace(/^@elizaos\/(app-|plugin-)/, "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
381
+ pluginName: p.name,
382
+ version: p.version ?? "unknown",
383
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
384
+ }));
385
+ }
386
+ };
387
+
388
+ //#endregion
389
+ export { AppManager };
@@ -0,0 +1,86 @@
1
+ import { tmpdir } from "node:os";
2
+ import { join } from "node:path";
3
+ import { existsSync, writeFileSync } from "node:fs";
4
+ import puppeteer from "puppeteer-core";
5
+
6
+ //#region src/services/browser-capture.ts
7
+ /**
8
+ * Headless browser capture — opens a game URL in headless Chrome and
9
+ * saves screenshots to a temp file. FFmpeg reads the temp file using
10
+ * -loop 1 to continuously re-read the latest frame.
11
+ *
12
+ * This approach avoids the pipe bottleneck — FFmpeg reads at its own
13
+ * pace while the browser updates the file independently.
14
+ */
15
+ const CHROME_PATH = process.platform === "darwin" ? "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" : process.platform === "win32" ? "C:\\Program Files\\Google Chrome\\Application\\chrome.exe" : "/usr/bin/google-chrome-stable";
16
+ let activeBrowser = null;
17
+ let stopSignal = false;
18
+ /** Path to the temp frame file that FFmpeg reads */
19
+ const FRAME_FILE = join(tmpdir(), "milady-stream-frame.jpg");
20
+ async function startBrowserCapture(config) {
21
+ if (activeBrowser) {
22
+ console.log("[browser-capture] Already running");
23
+ return;
24
+ }
25
+ const { url, width = 1280, height = 720, quality = 70 } = config;
26
+ stopSignal = false;
27
+ console.log(`[browser-capture] Launching headless Chrome → ${url}`);
28
+ const browser = await puppeteer.launch({
29
+ executablePath: CHROME_PATH,
30
+ headless: true,
31
+ args: [
32
+ `--window-size=${width},${height}`,
33
+ "--no-sandbox",
34
+ "--disable-gpu",
35
+ "--disable-dev-shm-usage",
36
+ "--disable-extensions",
37
+ "--mute-audio"
38
+ ]
39
+ });
40
+ activeBrowser = browser;
41
+ const page = await browser.newPage();
42
+ await page.setViewport({
43
+ width,
44
+ height
45
+ });
46
+ await page.goto(url, {
47
+ waitUntil: "domcontentloaded",
48
+ timeout: 3e4
49
+ });
50
+ console.log(`[browser-capture] Page loaded, writing frames to ${FRAME_FILE}`);
51
+ const cdp = await page.createCDPSession();
52
+ let frameCount = 0;
53
+ cdp.on("Page.screencastFrame", async (params) => {
54
+ if (stopSignal) return;
55
+ try {
56
+ const buf = Buffer.from(params.data, "base64");
57
+ if (buf.length > 0) {
58
+ writeFileSync(FRAME_FILE, buf);
59
+ frameCount++;
60
+ if (frameCount % 100 === 0) console.log(`[browser-capture] ${frameCount} frames written`);
61
+ }
62
+ await cdp.send("Page.screencastFrameAck", { sessionId: params.sessionId });
63
+ } catch {}
64
+ });
65
+ await cdp.send("Page.startScreencast", {
66
+ format: "jpeg",
67
+ quality,
68
+ maxWidth: width,
69
+ maxHeight: height,
70
+ everyNthFrame: 2
71
+ });
72
+ console.log(`[browser-capture] CDP screencast active, saving to ${FRAME_FILE}`);
73
+ }
74
+ async function stopBrowserCapture() {
75
+ stopSignal = true;
76
+ if (activeBrowser) {
77
+ try {
78
+ await activeBrowser.close();
79
+ } catch {}
80
+ activeBrowser = null;
81
+ }
82
+ console.log("[browser-capture] Stopped");
83
+ }
84
+
85
+ //#endregion
86
+ export { FRAME_FILE, startBrowserCapture, stopBrowserCapture };
@@ -0,0 +1,128 @@
1
+ import crypto from "node:crypto";
2
+
3
+ //#region src/services/fallback-training-service.ts
4
+ var FallbackTrainingService = class {
5
+ constructor(options) {
6
+ this.options = options;
7
+ this.listeners = /* @__PURE__ */ new Set();
8
+ this.datasets = [];
9
+ this.jobs = [];
10
+ this.models = [];
11
+ }
12
+ async initialize() {}
13
+ subscribe(listener) {
14
+ this.listeners.add(listener);
15
+ return () => this.listeners.delete(listener);
16
+ }
17
+ emit(event) {
18
+ for (const listener of this.listeners) try {
19
+ listener(event);
20
+ } catch {}
21
+ }
22
+ getStatus() {
23
+ return {
24
+ runningJobs: this.jobs.filter((job) => job.status === "running").length,
25
+ datasetCount: this.datasets.length,
26
+ modelCount: this.models.length
27
+ };
28
+ }
29
+ async listTrajectories(options) {
30
+ const limit = options.limit ?? 100;
31
+ const offset = options.offset ?? 0;
32
+ if (!this.options.getRuntime()) return {
33
+ available: false,
34
+ reason: "runtime_not_started",
35
+ trajectories: [],
36
+ total: 0,
37
+ limit,
38
+ offset
39
+ };
40
+ return {
41
+ available: false,
42
+ reason: "trajectory_store_unavailable",
43
+ trajectories: [],
44
+ total: 0,
45
+ limit,
46
+ offset
47
+ };
48
+ }
49
+ async getTrajectoryById(_trajectoryId) {
50
+ return null;
51
+ }
52
+ listDatasets() {
53
+ return [...this.datasets];
54
+ }
55
+ async buildDataset(options) {
56
+ const dataset = {
57
+ id: `dataset-${crypto.randomUUID()}`,
58
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
59
+ limit: options.limit,
60
+ minLlmCallsPerTrajectory: options.minLlmCallsPerTrajectory
61
+ };
62
+ this.datasets.unshift(dataset);
63
+ this.emit({
64
+ kind: "dataset_built",
65
+ dataset
66
+ });
67
+ return dataset;
68
+ }
69
+ listJobs() {
70
+ return [...this.jobs];
71
+ }
72
+ async startTrainingJob(options) {
73
+ if (!options.datasetId) throw new Error("datasetId is required");
74
+ if (!this.datasets.some((dataset) => dataset.id === options.datasetId)) throw new Error("Dataset not found");
75
+ const job = {
76
+ id: `job-${crypto.randomUUID()}`,
77
+ datasetId: options.datasetId,
78
+ status: "queued",
79
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
80
+ };
81
+ this.jobs.unshift(job);
82
+ this.emit({
83
+ kind: "job_started",
84
+ job
85
+ });
86
+ return job;
87
+ }
88
+ getJob(jobId) {
89
+ return this.jobs.find((job) => job.id === jobId) ?? null;
90
+ }
91
+ async cancelJob(jobId) {
92
+ const job = this.getJob(jobId);
93
+ if (!job) throw new Error("Training job not found");
94
+ job.status = "cancelled";
95
+ this.emit({
96
+ kind: "job_cancelled",
97
+ job
98
+ });
99
+ return job;
100
+ }
101
+ listModels() {
102
+ return [...this.models];
103
+ }
104
+ async importModelToOllama(modelId, _body) {
105
+ const model = this.models.find((entry) => entry.id === modelId);
106
+ if (!model) throw new Error("Model not found");
107
+ return model;
108
+ }
109
+ async activateModel(modelId, _providerModel) {
110
+ const model = this.models.find((entry) => entry.id === modelId);
111
+ if (!model) throw new Error("Model not found");
112
+ return {
113
+ ok: true,
114
+ activeModelId: model.id
115
+ };
116
+ }
117
+ async benchmarkModel(modelId) {
118
+ const model = this.models.find((entry) => entry.id === modelId);
119
+ if (!model) throw new Error("Model not found");
120
+ return {
121
+ ok: true,
122
+ modelId: model.id
123
+ };
124
+ }
125
+ };
126
+
127
+ //#endregion
128
+ export { FallbackTrainingService };