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,195 @@
1
+ import { CharacterSchema } from "../config/zod-schema.js";
2
+ import { ModelType, logger } from "@elizaos/core";
3
+
4
+ //#region src/api/character-routes.ts
5
+ function buildCharacterSummary(ctx) {
6
+ return [
7
+ ctx.name ? `Name: ${ctx.name}` : "",
8
+ ctx.system ? `System prompt: ${ctx.system}` : "",
9
+ ctx.bio ? `Bio: ${ctx.bio}` : "",
10
+ ctx.style?.all?.length ? `Style rules: ${ctx.style.all.join("; ")}` : ""
11
+ ].filter(Boolean).join("\n");
12
+ }
13
+ function buildGeneratePrompt(field, context, mode) {
14
+ const charSummary = buildCharacterSummary(context);
15
+ if (field === "bio") return `Given this character:\n${charSummary}\n\nWrite a concise, compelling bio for this character (3-4 short paragraphs, one per line). Just output the bio lines, nothing else. Match the character's voice and personality.`;
16
+ if (field === "style") return `Given this character:\n${charSummary}${mode === "append" && context.style?.all?.length ? `\nExisting style rules (add to these, don't repeat):\n${context.style.all.join("\n")}` : ""}\n\nGenerate 4-6 communication style rules for this character. Output a JSON object with keys "all", "chat", "post", each containing an array of short rule strings. Just output the JSON, nothing else.`;
17
+ if (field === "chatExamples") return `Given this character:\n${charSummary}\n\nGenerate 3 example chat conversations showing how this character responds. Output a JSON array where each element is an array of message objects like [{"user":"{{user1}}","content":{"text":"..."}},{"user":"{{agentName}}","content":{"text":"..."}}]. Just output the JSON array, nothing else.`;
18
+ return `Given this character:\n${charSummary}${mode === "append" && context.postExamples?.length ? `\nExisting posts (add new ones, don't repeat):\n${context.postExamples.join("\n")}` : ""}\n\nGenerate 3-5 example social media posts this character would write. Output a JSON array of strings. Just output the JSON array, nothing else.`;
19
+ }
20
+ const CHARACTER_SCHEMA_FIELDS = [
21
+ {
22
+ key: "name",
23
+ type: "string",
24
+ label: "Name",
25
+ description: "Agent display name",
26
+ maxLength: 100
27
+ },
28
+ {
29
+ key: "username",
30
+ type: "string",
31
+ label: "Username",
32
+ description: "Agent username for platforms",
33
+ maxLength: 50
34
+ },
35
+ {
36
+ key: "bio",
37
+ type: "string | string[]",
38
+ label: "Bio",
39
+ description: "Biography — single string or array of points"
40
+ },
41
+ {
42
+ key: "system",
43
+ type: "string",
44
+ label: "System Prompt",
45
+ description: "System prompt defining core behavior",
46
+ maxLength: 1e4
47
+ },
48
+ {
49
+ key: "adjectives",
50
+ type: "string[]",
51
+ label: "Adjectives",
52
+ description: "Personality adjectives (e.g. curious, witty)"
53
+ },
54
+ {
55
+ key: "topics",
56
+ type: "string[]",
57
+ label: "Topics",
58
+ description: "Topics the agent is knowledgeable about"
59
+ },
60
+ {
61
+ key: "style",
62
+ type: "object",
63
+ label: "Style",
64
+ description: "Communication style guides",
65
+ children: [
66
+ {
67
+ key: "all",
68
+ type: "string[]",
69
+ label: "All",
70
+ description: "Style guidelines for all responses"
71
+ },
72
+ {
73
+ key: "chat",
74
+ type: "string[]",
75
+ label: "Chat",
76
+ description: "Style guidelines for chat responses"
77
+ },
78
+ {
79
+ key: "post",
80
+ type: "string[]",
81
+ label: "Post",
82
+ description: "Style guidelines for social media posts"
83
+ }
84
+ ]
85
+ },
86
+ {
87
+ key: "messageExamples",
88
+ type: "array",
89
+ label: "Message Examples",
90
+ description: "Example conversations demonstrating the agent's voice"
91
+ },
92
+ {
93
+ key: "postExamples",
94
+ type: "string[]",
95
+ label: "Post Examples",
96
+ description: "Example social media posts"
97
+ }
98
+ ];
99
+ async function handleCharacterRoutes(ctx) {
100
+ const { req, res, method, pathname, state, readJsonBody, json, error, pickRandomNames } = ctx;
101
+ if (method === "GET" && pathname === "/api/character") {
102
+ const runtime = state.runtime;
103
+ const merged = {};
104
+ if (runtime) {
105
+ const character = runtime.character;
106
+ if (character.name) merged.name = character.name;
107
+ if (character.bio) merged.bio = character.bio;
108
+ if (character.system) merged.system = character.system;
109
+ if (character.adjectives) merged.adjectives = character.adjectives;
110
+ if (character.topics) merged.topics = character.topics;
111
+ if (character.style) merged.style = character.style;
112
+ if (character.postExamples) merged.postExamples = character.postExamples;
113
+ }
114
+ json(res, {
115
+ character: merged,
116
+ agentName: state.agentName
117
+ });
118
+ return true;
119
+ }
120
+ if (method === "PUT" && pathname === "/api/character") {
121
+ const body = await readJsonBody(req, res);
122
+ if (!body) return true;
123
+ const result = CharacterSchema.safeParse(body);
124
+ if (!result.success) {
125
+ json(res, {
126
+ ok: false,
127
+ validationErrors: result.error.issues.map((issue) => ({
128
+ path: issue.path.join("."),
129
+ message: issue.message
130
+ }))
131
+ }, 422);
132
+ return true;
133
+ }
134
+ if (state.runtime) {
135
+ const character = state.runtime.character;
136
+ if (body.name != null) character.name = String(body.name);
137
+ if (body.bio != null) character.bio = Array.isArray(body.bio) ? body.bio : [String(body.bio)];
138
+ if (body.system != null) character.system = String(body.system);
139
+ if (body.adjectives != null) character.adjectives = body.adjectives;
140
+ if (body.topics != null) character.topics = body.topics;
141
+ if (body.style != null) character.style = body.style;
142
+ if (body.postExamples != null) character.postExamples = body.postExamples;
143
+ }
144
+ if (body.name) state.agentName = String(body.name);
145
+ json(res, {
146
+ ok: true,
147
+ character: body,
148
+ agentName: state.agentName
149
+ });
150
+ return true;
151
+ }
152
+ if (method === "GET" && pathname === "/api/character/random-name") {
153
+ json(res, { name: pickRandomNames(1)[0] ?? "Reimu" });
154
+ return true;
155
+ }
156
+ if (method === "POST" && pathname === "/api/character/generate") {
157
+ const body = await readJsonBody(req, res);
158
+ if (!body) return true;
159
+ if (!body.field || !body.context) {
160
+ error(res, "field and context are required", 400);
161
+ return true;
162
+ }
163
+ const runtime = state.runtime;
164
+ if (!runtime) {
165
+ error(res, "Agent runtime not available. Start the agent first.", 503);
166
+ return true;
167
+ }
168
+ if (body.field !== "bio" && body.field !== "style" && body.field !== "chatExamples" && body.field !== "postExamples") {
169
+ error(res, `Unknown field: ${body.field}`, 400);
170
+ return true;
171
+ }
172
+ const prompt = buildGeneratePrompt(body.field, body.context, body.mode);
173
+ try {
174
+ const result = await runtime.useModel(ModelType.TEXT_SMALL, {
175
+ prompt,
176
+ temperature: .8,
177
+ maxTokens: 1500
178
+ });
179
+ json(res, { generated: String(result) });
180
+ } catch (err) {
181
+ const message = err instanceof Error ? err.message : "generation failed";
182
+ logger.error(`[character-generate] ${message}`);
183
+ error(res, message, 500);
184
+ }
185
+ return true;
186
+ }
187
+ if (method === "GET" && pathname === "/api/character/schema") {
188
+ json(res, { fields: CHARACTER_SCHEMA_FIELDS });
189
+ return true;
190
+ }
191
+ return false;
192
+ }
193
+
194
+ //#endregion
195
+ export { handleCharacterRoutes };
@@ -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 };