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,330 @@
1
+ import { saveMiladyConfig } from "./config/config.js";
2
+ import { createIntegrationTelemetrySpan } from "./diagnostics/integration-observability.js";
3
+ import { readJsonBody as readJsonBody$1, sendJson, sendJsonError } from "./http-helpers.js";
4
+ import { validateCloudBaseUrl } from "./cloud/validate-url.js";
5
+ import { logger } from "@elizaos/core";
6
+
7
+ //#region src/api/cloud-routes.ts
8
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9
+ function extractAgentId(pathname) {
10
+ const id = pathname.split("/")[4];
11
+ return id && UUID_RE.test(id) ? id : null;
12
+ }
13
+ /**
14
+ * Read and parse a JSON request body with size limits and error handling.
15
+ * Returns null (and sends a 4xx response) if reading or parsing fails.
16
+ */
17
+ async function readJsonBody(req, res) {
18
+ return readJsonBody$1(req, res, {
19
+ maxBytes: 1048576,
20
+ tooLargeMessage: "Request body too large",
21
+ destroyOnTooLarge: true
22
+ });
23
+ }
24
+ const CLOUD_LOGIN_CREATE_TIMEOUT_MS = 1e4;
25
+ const CLOUD_LOGIN_POLL_TIMEOUT_MS = 1e4;
26
+ function isRedirectResponse(response) {
27
+ return response.status >= 300 && response.status < 400;
28
+ }
29
+ function isTimeoutError(error) {
30
+ if (!(error instanceof Error)) return false;
31
+ if (error.name === "TimeoutError" || error.name === "AbortError") return true;
32
+ const message = error.message.toLowerCase();
33
+ return message.includes("timed out") || message.includes("timeout");
34
+ }
35
+ async function fetchWithTimeout(input, init, timeoutMs) {
36
+ return fetch(input, {
37
+ ...init,
38
+ redirect: "manual",
39
+ signal: AbortSignal.timeout(timeoutMs)
40
+ });
41
+ }
42
+ /**
43
+ * Returns true if the request was handled, false if path didn't match.
44
+ */
45
+ async function handleCloudRoute(req, res, pathname, method, state) {
46
+ if (method === "POST" && pathname === "/api/cloud/login") {
47
+ const baseUrl = state.config.cloud?.baseUrl ?? "https://www.elizacloud.ai";
48
+ const urlError = await validateCloudBaseUrl(baseUrl);
49
+ if (urlError) {
50
+ sendJsonError(res, urlError);
51
+ return true;
52
+ }
53
+ const sessionId = crypto.randomUUID();
54
+ const loginCreateSpan = createIntegrationTelemetrySpan({
55
+ boundary: "cloud",
56
+ operation: "login_create_session",
57
+ timeoutMs: CLOUD_LOGIN_CREATE_TIMEOUT_MS
58
+ });
59
+ let createRes;
60
+ try {
61
+ createRes = await fetchWithTimeout(`${baseUrl}/api/auth/cli-session`, {
62
+ method: "POST",
63
+ headers: { "Content-Type": "application/json" },
64
+ body: JSON.stringify({ sessionId })
65
+ }, CLOUD_LOGIN_CREATE_TIMEOUT_MS);
66
+ } catch (fetchErr) {
67
+ if (isTimeoutError(fetchErr)) {
68
+ loginCreateSpan.failure({
69
+ error: fetchErr,
70
+ statusCode: 504
71
+ });
72
+ sendJsonError(res, "Eliza Cloud login request timed out", 504);
73
+ return true;
74
+ }
75
+ loginCreateSpan.failure({
76
+ error: fetchErr,
77
+ statusCode: 502
78
+ });
79
+ sendJsonError(res, "Failed to reach Eliza Cloud", 502);
80
+ return true;
81
+ }
82
+ if (isRedirectResponse(createRes)) {
83
+ loginCreateSpan.failure({
84
+ statusCode: createRes.status,
85
+ errorKind: "redirect_response"
86
+ });
87
+ sendJsonError(res, "Eliza Cloud login request was redirected; redirects are not allowed", 502);
88
+ return true;
89
+ }
90
+ if (!createRes.ok) {
91
+ loginCreateSpan.failure({
92
+ statusCode: createRes.status,
93
+ errorKind: "http_error"
94
+ });
95
+ sendJsonError(res, "Failed to create auth session with Eliza Cloud", 502);
96
+ return true;
97
+ }
98
+ loginCreateSpan.success({ statusCode: createRes.status });
99
+ sendJson(res, {
100
+ ok: true,
101
+ sessionId,
102
+ browserUrl: `${baseUrl}/auth/cli-login?session=${encodeURIComponent(sessionId)}`
103
+ });
104
+ return true;
105
+ }
106
+ if (method === "GET" && pathname.startsWith("/api/cloud/login/status")) {
107
+ const sessionId = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`).searchParams.get("sessionId");
108
+ if (!sessionId) {
109
+ sendJsonError(res, "sessionId query parameter is required");
110
+ return true;
111
+ }
112
+ const baseUrl = state.config.cloud?.baseUrl ?? "https://www.elizacloud.ai";
113
+ const urlError = await validateCloudBaseUrl(baseUrl);
114
+ if (urlError) {
115
+ sendJsonError(res, urlError);
116
+ return true;
117
+ }
118
+ const loginPollSpan = createIntegrationTelemetrySpan({
119
+ boundary: "cloud",
120
+ operation: "login_poll_status",
121
+ timeoutMs: CLOUD_LOGIN_POLL_TIMEOUT_MS
122
+ });
123
+ let pollRes;
124
+ try {
125
+ pollRes = await fetchWithTimeout(`${baseUrl}/api/auth/cli-session/${encodeURIComponent(sessionId)}`, {}, CLOUD_LOGIN_POLL_TIMEOUT_MS);
126
+ } catch (fetchErr) {
127
+ if (isTimeoutError(fetchErr)) {
128
+ loginPollSpan.failure({
129
+ error: fetchErr,
130
+ statusCode: 504
131
+ });
132
+ sendJson(res, {
133
+ status: "error",
134
+ error: "Eliza Cloud status request timed out"
135
+ }, 504);
136
+ return true;
137
+ }
138
+ loginPollSpan.failure({
139
+ error: fetchErr,
140
+ statusCode: 502
141
+ });
142
+ sendJson(res, {
143
+ status: "error",
144
+ error: "Failed to reach Eliza Cloud"
145
+ }, 502);
146
+ return true;
147
+ }
148
+ if (isRedirectResponse(pollRes)) {
149
+ loginPollSpan.failure({
150
+ statusCode: pollRes.status,
151
+ errorKind: "redirect_response"
152
+ });
153
+ sendJson(res, {
154
+ status: "error",
155
+ error: "Eliza Cloud status request was redirected; redirects are not allowed"
156
+ }, 502);
157
+ return true;
158
+ }
159
+ if (!pollRes.ok) {
160
+ loginPollSpan.failure({
161
+ statusCode: pollRes.status,
162
+ errorKind: "http_error"
163
+ });
164
+ sendJson(res, pollRes.status === 404 ? {
165
+ status: "expired",
166
+ error: "Session not found or expired"
167
+ } : {
168
+ status: "error",
169
+ error: `Eliza Cloud returned HTTP ${pollRes.status}`
170
+ });
171
+ return true;
172
+ }
173
+ let data;
174
+ try {
175
+ data = await pollRes.json();
176
+ } catch (parseErr) {
177
+ loginPollSpan.failure({
178
+ error: parseErr,
179
+ statusCode: pollRes.status
180
+ });
181
+ throw parseErr;
182
+ }
183
+ loginPollSpan.success({ statusCode: pollRes.status });
184
+ if (data.status === "authenticated" && data.apiKey) {
185
+ const cloud = state.config.cloud ?? {};
186
+ cloud.enabled = true;
187
+ cloud.apiKey = data.apiKey;
188
+ state.config.cloud = cloud;
189
+ try {
190
+ saveMiladyConfig(state.config);
191
+ logger.info("[cloud-login] API key saved to config file");
192
+ } catch (saveErr) {
193
+ logger.error(`[cloud-login] Failed to save config: ${saveErr instanceof Error ? saveErr.message : saveErr}`);
194
+ }
195
+ process.env.ELIZAOS_CLOUD_API_KEY = data.apiKey;
196
+ process.env.ELIZAOS_CLOUD_ENABLED = "true";
197
+ if (state.runtime) try {
198
+ if (!state.runtime.character.secrets) state.runtime.character.secrets = {};
199
+ const secrets = state.runtime.character.secrets;
200
+ secrets.ELIZAOS_CLOUD_API_KEY = data.apiKey;
201
+ secrets.ELIZAOS_CLOUD_ENABLED = "true";
202
+ await state.runtime.updateAgent(state.runtime.agentId, { secrets: { ...secrets } });
203
+ logger.info("[cloud-login] API key persisted to agent DB record");
204
+ } catch (dbErr) {
205
+ logger.warn(`[cloud-login] DB persistence failed (non-fatal): ${dbErr instanceof Error ? dbErr.message : dbErr}`);
206
+ }
207
+ if (state.cloudManager && !state.cloudManager.getClient()) await state.cloudManager.init();
208
+ sendJson(res, {
209
+ status: "authenticated",
210
+ keyPrefix: data.keyPrefix
211
+ });
212
+ } else sendJson(res, { status: data.status });
213
+ return true;
214
+ }
215
+ if (method === "GET" && pathname === "/api/cloud/agents") {
216
+ const client = state.cloudManager?.getClient();
217
+ if (!client) {
218
+ sendJsonError(res, "Not connected to Eliza Cloud", 401);
219
+ return true;
220
+ }
221
+ sendJson(res, {
222
+ ok: true,
223
+ agents: await client.listAgents()
224
+ });
225
+ return true;
226
+ }
227
+ if (method === "POST" && pathname === "/api/cloud/agents") {
228
+ const client = state.cloudManager?.getClient();
229
+ if (!client) {
230
+ sendJsonError(res, "Not connected to Eliza Cloud", 401);
231
+ return true;
232
+ }
233
+ const body = await readJsonBody(req, res);
234
+ if (!body) return true;
235
+ if (!body.agentName?.trim()) {
236
+ sendJsonError(res, "agentName is required");
237
+ return true;
238
+ }
239
+ sendJson(res, {
240
+ ok: true,
241
+ agent: await client.createAgent({
242
+ agentName: body.agentName,
243
+ agentConfig: body.agentConfig,
244
+ environmentVars: body.environmentVars
245
+ })
246
+ }, 201);
247
+ return true;
248
+ }
249
+ if (method === "POST" && pathname.startsWith("/api/cloud/agents/") && pathname.endsWith("/provision")) {
250
+ const agentId = extractAgentId(pathname);
251
+ if (!agentId || !state.cloudManager) {
252
+ sendJsonError(res, "Invalid agent ID or cloud not connected", 400);
253
+ return true;
254
+ }
255
+ sendJson(res, {
256
+ ok: true,
257
+ agentId,
258
+ agentName: (await state.cloudManager.connect(agentId)).agentName,
259
+ status: state.cloudManager.getStatus()
260
+ });
261
+ return true;
262
+ }
263
+ if (method === "POST" && pathname.startsWith("/api/cloud/agents/") && pathname.endsWith("/shutdown")) {
264
+ const agentId = extractAgentId(pathname);
265
+ if (!agentId || !state.cloudManager) {
266
+ sendJsonError(res, "Invalid agent ID or cloud not connected", 400);
267
+ return true;
268
+ }
269
+ const client = state.cloudManager.getClient();
270
+ if (!client) {
271
+ sendJsonError(res, "Not connected to Eliza Cloud", 401);
272
+ return true;
273
+ }
274
+ if (state.cloudManager.getActiveAgentId() === agentId) await state.cloudManager.disconnect();
275
+ await client.deleteAgent(agentId);
276
+ sendJson(res, {
277
+ ok: true,
278
+ agentId,
279
+ status: "stopped"
280
+ });
281
+ return true;
282
+ }
283
+ if (method === "POST" && pathname.startsWith("/api/cloud/agents/") && pathname.endsWith("/connect")) {
284
+ const agentId = extractAgentId(pathname);
285
+ if (!agentId || !state.cloudManager) {
286
+ sendJsonError(res, "Invalid agent ID or cloud not connected", 400);
287
+ return true;
288
+ }
289
+ if (state.cloudManager.getActiveAgentId()) await state.cloudManager.disconnect();
290
+ sendJson(res, {
291
+ ok: true,
292
+ agentId,
293
+ agentName: (await state.cloudManager.connect(agentId)).agentName,
294
+ status: state.cloudManager.getStatus()
295
+ });
296
+ return true;
297
+ }
298
+ if (method === "POST" && pathname === "/api/cloud/disconnect") {
299
+ if (state.cloudManager) await state.cloudManager.disconnect();
300
+ const cloud = state.config.cloud ?? {};
301
+ cloud.enabled = false;
302
+ delete cloud.apiKey;
303
+ state.config.cloud = cloud;
304
+ try {
305
+ saveMiladyConfig(state.config);
306
+ } catch (saveErr) {
307
+ logger.warn(`[cloud-login] Failed to save cloud disconnect state: ${saveErr instanceof Error ? saveErr.message : saveErr}`);
308
+ }
309
+ delete process.env.ELIZAOS_CLOUD_API_KEY;
310
+ delete process.env.ELIZAOS_CLOUD_ENABLED;
311
+ if (state.runtime) try {
312
+ if (!state.runtime.character.secrets) state.runtime.character.secrets = {};
313
+ const secrets = state.runtime.character.secrets;
314
+ delete secrets.ELIZAOS_CLOUD_API_KEY;
315
+ delete secrets.ELIZAOS_CLOUD_ENABLED;
316
+ await state.runtime.updateAgent(state.runtime.agentId, { secrets: { ...secrets } });
317
+ } catch (dbErr) {
318
+ logger.warn(`[cloud-login] Failed to clear cloud secrets from agent DB: ${dbErr instanceof Error ? dbErr.message : dbErr}`);
319
+ }
320
+ sendJson(res, {
321
+ ok: true,
322
+ status: "disconnected"
323
+ });
324
+ return true;
325
+ }
326
+ return false;
327
+ }
328
+
329
+ //#endregion
330
+ export { handleCloudRoute };
@@ -0,0 +1,155 @@
1
+ import { validateCloudBaseUrl } from "./cloud/validate-url.js";
2
+ import { logger } from "@elizaos/core";
3
+
4
+ //#region src/api/cloud-status-routes.ts
5
+ const DEFAULT_CLOUD_API_BASE_URL = "https://www.elizacloud.ai/api/v1";
6
+ const CLOUD_BILLING_URL = "https://www.elizacloud.ai/dashboard/settings?tab=billing";
7
+ function resolveCloudApiBaseUrl(rawBaseUrl) {
8
+ const base = (rawBaseUrl ?? DEFAULT_CLOUD_API_BASE_URL).trim().replace(/\/+$/, "");
9
+ if (base.endsWith("/api/v1")) return base;
10
+ return `${base}/api/v1`;
11
+ }
12
+ async function fetchCloudCreditsByApiKey(baseUrl, apiKey) {
13
+ const response = await fetch(`${baseUrl}/credits/balance`, {
14
+ headers: {
15
+ Accept: "application/json",
16
+ Authorization: `Bearer ${apiKey}`
17
+ },
18
+ redirect: "manual",
19
+ signal: AbortSignal.timeout(1e4)
20
+ });
21
+ if (response.status >= 300 && response.status < 400) throw new Error("Cloud credits request was redirected; redirects are not allowed");
22
+ const creditResponse = await response.json().catch(() => ({}));
23
+ if (!response.ok) {
24
+ const message = typeof creditResponse.error === "string" && creditResponse.error.trim() ? creditResponse.error : `HTTP ${response.status}`;
25
+ throw new Error(message);
26
+ }
27
+ const rawBalance = typeof creditResponse.balance === "number" ? creditResponse.balance : typeof creditResponse.data?.balance === "number" ? creditResponse.data.balance : void 0;
28
+ return typeof rawBalance === "number" ? rawBalance : null;
29
+ }
30
+ async function handleCloudStatusRoutes(ctx) {
31
+ const { res, method, pathname, config, runtime, json } = ctx;
32
+ if (method === "GET" && pathname === "/api/cloud/status") {
33
+ const cloudMode = config.cloud?.enabled;
34
+ const cloudEnabled = cloudMode === true;
35
+ const hasApiKey = Boolean(config.cloud?.apiKey?.trim());
36
+ const effectivelyEnabled = cloudEnabled || cloudMode !== false && hasApiKey;
37
+ const cloudAuth = runtime ? runtime.getService("CLOUD_AUTH") : null;
38
+ const authConnected = Boolean(cloudAuth?.isAuthenticated());
39
+ if (authConnected || hasApiKey) {
40
+ json(res, {
41
+ connected: true,
42
+ enabled: effectivelyEnabled,
43
+ hasApiKey,
44
+ userId: authConnected ? cloudAuth?.getUserId?.() : void 0,
45
+ organizationId: authConnected ? cloudAuth?.getOrganizationId?.() : void 0,
46
+ topUpUrl: CLOUD_BILLING_URL,
47
+ reason: authConnected ? void 0 : runtime ? "api_key_present_not_authenticated" : "api_key_present_runtime_not_started"
48
+ });
49
+ return true;
50
+ }
51
+ if (!runtime) {
52
+ json(res, {
53
+ connected: false,
54
+ enabled: effectivelyEnabled,
55
+ hasApiKey,
56
+ reason: "runtime_not_started"
57
+ });
58
+ return true;
59
+ }
60
+ json(res, {
61
+ connected: false,
62
+ enabled: effectivelyEnabled,
63
+ hasApiKey,
64
+ reason: "not_authenticated"
65
+ });
66
+ return true;
67
+ }
68
+ if (method === "GET" && pathname === "/api/cloud/credits") {
69
+ const cloudAuth = runtime ? runtime.getService("CLOUD_AUTH") : null;
70
+ const configApiKey = config.cloud?.apiKey?.trim();
71
+ if (!cloudAuth || !cloudAuth.isAuthenticated()) {
72
+ if (!configApiKey) {
73
+ json(res, {
74
+ balance: null,
75
+ connected: false
76
+ });
77
+ return true;
78
+ }
79
+ const resolvedBaseUrl = resolveCloudApiBaseUrl(config.cloud?.baseUrl);
80
+ const baseUrlRejection = await validateCloudBaseUrl(resolvedBaseUrl);
81
+ if (baseUrlRejection) {
82
+ json(res, {
83
+ balance: null,
84
+ connected: true,
85
+ error: baseUrlRejection
86
+ });
87
+ return true;
88
+ }
89
+ try {
90
+ const balance = await fetchCloudCreditsByApiKey(resolvedBaseUrl, configApiKey);
91
+ if (typeof balance !== "number") {
92
+ json(res, {
93
+ balance: null,
94
+ connected: true,
95
+ error: "unexpected response"
96
+ });
97
+ return true;
98
+ }
99
+ json(res, {
100
+ connected: true,
101
+ balance,
102
+ low: balance < 2,
103
+ critical: balance < .5,
104
+ topUpUrl: CLOUD_BILLING_URL
105
+ });
106
+ } catch (err) {
107
+ const msg = err instanceof Error ? err.message : "cloud API unreachable";
108
+ logger.debug(`[cloud/credits] Failed to fetch balance via API key: ${msg}`);
109
+ json(res, {
110
+ balance: null,
111
+ connected: true,
112
+ error: msg
113
+ });
114
+ }
115
+ return true;
116
+ }
117
+ let balance;
118
+ const client = cloudAuth.getClient();
119
+ try {
120
+ const creditResponse = await client.get("/credits/balance");
121
+ const rawBalance = typeof creditResponse?.balance === "number" ? creditResponse.balance : typeof (creditResponse?.data)?.balance === "number" ? creditResponse.data.balance : void 0;
122
+ if (typeof rawBalance !== "number") {
123
+ logger.debug(`[cloud/credits] Unexpected response shape: ${JSON.stringify(creditResponse)}`);
124
+ json(res, {
125
+ balance: null,
126
+ connected: true,
127
+ error: "unexpected response"
128
+ });
129
+ return true;
130
+ }
131
+ balance = rawBalance;
132
+ } catch (err) {
133
+ const msg = err instanceof Error ? err.message : "cloud API unreachable";
134
+ logger.debug(`[cloud/credits] Failed to fetch balance: ${msg}`);
135
+ json(res, {
136
+ balance: null,
137
+ connected: true,
138
+ error: msg
139
+ });
140
+ return true;
141
+ }
142
+ json(res, {
143
+ connected: true,
144
+ balance,
145
+ low: balance < 2,
146
+ critical: balance < .5,
147
+ topUpUrl: CLOUD_BILLING_URL
148
+ });
149
+ return true;
150
+ }
151
+ return false;
152
+ }
153
+
154
+ //#endregion
155
+ export { handleCloudStatusRoutes };
@@ -0,0 +1,111 @@
1
+ //#region src/api/compat-utils.ts
2
+ function asRecord(value) {
3
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
4
+ return value;
5
+ }
6
+ function readString(value) {
7
+ return typeof value === "string" ? value : "";
8
+ }
9
+ /**
10
+ * Extract a best-effort text string from OpenAI/Anthropic "content" fields.
11
+ * Supports:
12
+ * - string
13
+ * - array of parts: [{ type: "text", text: "..." }, ...]
14
+ * - objects with a `text` string field
15
+ */
16
+ function extractCompatTextContent(content) {
17
+ if (typeof content === "string") return content;
18
+ if (Array.isArray(content)) {
19
+ const chunks = [];
20
+ for (const item of content) {
21
+ const obj = asRecord(item);
22
+ if (!obj) continue;
23
+ const type = readString(obj.type);
24
+ if (type && type !== "text") continue;
25
+ const text = readString(obj.text);
26
+ if (text) chunks.push(text);
27
+ }
28
+ return chunks.join("");
29
+ }
30
+ const obj = asRecord(content);
31
+ if (obj) return readString(obj.text);
32
+ return "";
33
+ }
34
+ /**
35
+ * For OpenAI-compatible requests, we intentionally reduce "messages" to:
36
+ * - all system/developer messages (joined)
37
+ * - the last user message
38
+ *
39
+ * This keeps the server-side room memory coherent (so stateless clients that
40
+ * resend full history do not cause runaway duplication).
41
+ */
42
+ function extractOpenAiSystemAndLastUser(messages) {
43
+ if (!Array.isArray(messages)) return null;
44
+ let system = "";
45
+ let user = "";
46
+ for (const item of messages) {
47
+ const msg = asRecord(item);
48
+ if (!msg) continue;
49
+ const role = readString(msg.role);
50
+ const contentText = extractCompatTextContent(msg.content);
51
+ if (!contentText.trim()) continue;
52
+ if (role === "system" || role === "developer") {
53
+ system = system ? `${system}\n\n${contentText.trim()}` : contentText.trim();
54
+ continue;
55
+ }
56
+ if (role === "user") user = contentText.trim();
57
+ }
58
+ if (!user) return null;
59
+ return {
60
+ system,
61
+ user
62
+ };
63
+ }
64
+ function extractAnthropicSystemAndLastUser(args) {
65
+ if (!Array.isArray(args.messages)) return null;
66
+ const system = readString(args.system).trim();
67
+ let user = "";
68
+ for (const item of args.messages) {
69
+ const msg = asRecord(item);
70
+ if (!msg) continue;
71
+ if (readString(msg.role) !== "user") continue;
72
+ const contentText = extractCompatTextContent(msg.content);
73
+ if (!contentText.trim()) continue;
74
+ user = contentText.trim();
75
+ }
76
+ if (!user) return null;
77
+ return {
78
+ system,
79
+ user
80
+ };
81
+ }
82
+ function readNestedString(obj, key) {
83
+ const raw = obj[key];
84
+ if (typeof raw === "string" && raw.trim()) return raw.trim();
85
+ return null;
86
+ }
87
+ /**
88
+ * Resolve a stable room key for compatibility endpoints so manual testing can
89
+ * keep conversation memory when the client provides an identifier.
90
+ *
91
+ * We accept a few common fields without requiring any one client:
92
+ * - OpenAI: body.user (string)
93
+ * - OpenAI: body.metadata.conversation_id
94
+ * - Anthropic: body.metadata.user_id
95
+ * - Anthropic: body.metadata.conversation_id
96
+ */
97
+ function resolveCompatRoomKey(body, fallback = "default") {
98
+ const direct = readNestedString(body, "user");
99
+ if (direct) return direct;
100
+ const metadata = asRecord(body.metadata);
101
+ if (metadata) {
102
+ const conv = readNestedString(metadata, "conversation_id") ?? readNestedString(metadata, "conversationId");
103
+ if (conv) return conv;
104
+ const userId = readNestedString(metadata, "user_id");
105
+ if (userId) return userId;
106
+ }
107
+ return fallback;
108
+ }
109
+
110
+ //#endregion
111
+ export { extractAnthropicSystemAndLastUser, extractCompatTextContent, extractOpenAiSystemAndLastUser, resolveCompatRoomKey };
@@ -0,0 +1,69 @@
1
+ import { resolveConfigPath, resolveUserPath } from "./paths.js";
2
+ import { collectConfigEnvVars } from "./env-vars.js";
3
+ import { resolveConfigIncludes } from "./includes.js";
4
+ import path from "node:path";
5
+ import fs from "node:fs";
6
+ import JSON5 from "json5";
7
+
8
+ //#region src/config/config.ts
9
+ function loadMiladyConfig() {
10
+ const configPath = resolveConfigPath();
11
+ let raw;
12
+ try {
13
+ raw = fs.readFileSync(configPath, "utf-8");
14
+ } catch (err) {
15
+ if (err.code === "ENOENT") return { logging: { level: "error" } };
16
+ throw err;
17
+ }
18
+ const resolved = resolveConfigIncludes(JSON5.parse(raw), configPath);
19
+ const skillsJsonPath = resolveUserPath("~/.eliza/skills.json");
20
+ if (!fs.existsSync(skillsJsonPath)) try {
21
+ const skillsDir = path.dirname(skillsJsonPath);
22
+ if (!fs.existsSync(skillsDir)) fs.mkdirSync(skillsDir, { recursive: true });
23
+ fs.writeFileSync(skillsJsonPath, JSON.stringify({ extraDirs: [] }, null, 2), "utf-8");
24
+ } catch (err) {
25
+ console.warn(`[milady] Failed to auto-create ~/.eliza/skills.json: ${err instanceof Error ? err.message : String(err)}`);
26
+ }
27
+ if (fs.existsSync(skillsJsonPath)) try {
28
+ const skillsRaw = fs.readFileSync(skillsJsonPath, "utf-8");
29
+ const skillsConfig = JSON5.parse(skillsRaw);
30
+ if (skillsConfig.extraDirs && Array.isArray(skillsConfig.extraDirs) && skillsConfig.extraDirs.length > 0) {
31
+ if (!resolved.skills) resolved.skills = {};
32
+ if (!resolved.skills.load) resolved.skills.load = {};
33
+ if (!resolved.skills.load.extraDirs) resolved.skills.load.extraDirs = [];
34
+ const existing = new Set(resolved.skills.load.extraDirs);
35
+ for (const dir of skillsConfig.extraDirs) {
36
+ const loadedDir = resolveUserPath(dir);
37
+ if (!existing.has(loadedDir)) {
38
+ resolved.skills.load.extraDirs.push(loadedDir);
39
+ existing.add(loadedDir);
40
+ }
41
+ }
42
+ }
43
+ } catch (err) {
44
+ console.warn(`[milady] Failed to load ~/.eliza/skills.json: ${err instanceof Error ? err.message : String(err)}`);
45
+ }
46
+ if (!resolved.logging) resolved.logging = { level: "error" };
47
+ else if (!resolved.logging.level) resolved.logging.level = "error";
48
+ const envVars = collectConfigEnvVars(resolved);
49
+ for (const [key, value] of Object.entries(envVars)) if (process.env[key] === void 0) process.env[key] = value;
50
+ return resolved;
51
+ }
52
+ function saveMiladyConfig(config) {
53
+ const configPath = resolveConfigPath();
54
+ const dir = path.dirname(configPath);
55
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, {
56
+ recursive: true,
57
+ mode: 448
58
+ });
59
+ fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, {
60
+ encoding: "utf-8",
61
+ mode: 384
62
+ });
63
+ }
64
+ function configFileExists() {
65
+ return fs.existsSync(resolveConfigPath());
66
+ }
67
+
68
+ //#endregion
69
+ export { configFileExists, loadMiladyConfig, saveMiladyConfig };
@@ -0,0 +1,19 @@
1
+ //#region src/config/env-vars.ts
2
+ function collectConfigEnvVars(cfg) {
3
+ const envConfig = cfg?.env;
4
+ if (!envConfig) return {};
5
+ const entries = {};
6
+ if (envConfig.vars) for (const [key, value] of Object.entries(envConfig.vars)) {
7
+ if (!value) continue;
8
+ entries[key] = value;
9
+ }
10
+ for (const [key, value] of Object.entries(envConfig)) {
11
+ if (key === "shellEnv" || key === "vars") continue;
12
+ if (typeof value !== "string" || !value.trim()) continue;
13
+ entries[key] = value;
14
+ }
15
+ return entries;
16
+ }
17
+
18
+ //#endregion
19
+ export { collectConfigEnvVars };