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,246 @@
1
+ import { DISABLED_TRIGGER_INTERVAL_MS, buildTriggerConfig, buildTriggerMetadata, normalizeTriggerDraft } from "../triggers/scheduling.js";
2
+ import { TRIGGER_TASK_NAME, TRIGGER_TASK_TAGS, executeTriggerTask, getTriggerHealthSnapshot, getTriggerLimit, listTriggerTasks, readTriggerConfig, readTriggerRuns, taskToTriggerSummary, triggersFeatureEnabled } from "../triggers/runtime.js";
3
+ import { stringToUuid } from "@elizaos/core";
4
+ import crypto from "node:crypto";
5
+
6
+ //#region src/api/trigger-routes.ts
7
+ function trim(s) {
8
+ return s.trim().replace(/\s+/g, " ");
9
+ }
10
+ async function findTask(runtime, id) {
11
+ return (await listTriggerTasks(runtime)).find((t) => {
12
+ return readTriggerConfig(t)?.triggerId === id || t.id === id;
13
+ }) ?? null;
14
+ }
15
+ async function handleTriggerRoutes(ctx) {
16
+ const { method, pathname, req, res, runtime, readJsonBody, json, error } = ctx;
17
+ if (!pathname.startsWith("/api/triggers")) return false;
18
+ if (!runtime) {
19
+ error(res, "Agent is not running", 503);
20
+ return true;
21
+ }
22
+ if (!triggersFeatureEnabled(runtime) && pathname !== "/api/triggers/health") {
23
+ error(res, "Triggers are disabled by configuration", 503);
24
+ return true;
25
+ }
26
+ if (method === "GET" && pathname === "/api/triggers/health") {
27
+ json(res, await getTriggerHealthSnapshot(runtime));
28
+ return true;
29
+ }
30
+ if (method === "GET" && pathname === "/api/triggers") {
31
+ json(res, { triggers: (await listTriggerTasks(runtime)).map(taskToTriggerSummary).filter((s) => s !== null).sort((a, b) => a.displayName.localeCompare(b.displayName)) });
32
+ return true;
33
+ }
34
+ if (method === "POST" && pathname === "/api/triggers") {
35
+ const body = await readJsonBody(req, res);
36
+ if (!body) return true;
37
+ const creator = trim(body.createdBy ?? "") || "api";
38
+ const normalized = normalizeTriggerDraft({
39
+ input: {
40
+ ...body,
41
+ enabled: body.enabled ?? true,
42
+ createdBy: creator
43
+ },
44
+ fallback: {
45
+ displayName: trim(body.displayName ?? "") || "New Trigger",
46
+ instructions: trim(body.instructions ?? ""),
47
+ triggerType: body.triggerType ?? "interval",
48
+ wakeMode: body.wakeMode ?? "inject_now",
49
+ enabled: body.enabled ?? true,
50
+ createdBy: creator
51
+ }
52
+ });
53
+ if (!normalized.draft) {
54
+ error(res, normalized.error ?? "Invalid trigger request", 400);
55
+ return true;
56
+ }
57
+ const existingTasks = await listTriggerTasks(runtime);
58
+ const activeCount = existingTasks.filter((t) => {
59
+ const tr = readTriggerConfig(t);
60
+ return tr?.enabled && tr.createdBy === creator;
61
+ }).length;
62
+ const limit = getTriggerLimit(runtime);
63
+ if (activeCount >= limit) {
64
+ error(res, `Active trigger limit reached (${limit})`, 429);
65
+ return true;
66
+ }
67
+ const triggerId = stringToUuid(crypto.randomUUID());
68
+ const trigger = buildTriggerConfig({
69
+ draft: normalized.draft,
70
+ triggerId
71
+ });
72
+ if (existingTasks.find((t) => {
73
+ const et = readTriggerConfig(t);
74
+ return et?.enabled && et.dedupeKey && et.dedupeKey === trigger.dedupeKey;
75
+ })?.id) {
76
+ error(res, "Equivalent trigger already exists", 409);
77
+ return true;
78
+ }
79
+ const metadata = buildTriggerMetadata({
80
+ trigger,
81
+ nowMs: Date.now()
82
+ });
83
+ if (!metadata) {
84
+ error(res, "Unable to compute trigger schedule", 400);
85
+ return true;
86
+ }
87
+ const roomId = runtime.getService("AUTONOMY")?.getAutonomousRoomId?.();
88
+ const taskId = await runtime.createTask({
89
+ name: TRIGGER_TASK_NAME,
90
+ description: trigger.displayName,
91
+ roomId,
92
+ tags: [...TRIGGER_TASK_TAGS],
93
+ metadata
94
+ });
95
+ const created = await runtime.getTask(taskId);
96
+ const summary = created ? taskToTriggerSummary(created) : null;
97
+ if (!summary) {
98
+ error(res, "Trigger created but summary could not be generated", 500);
99
+ return true;
100
+ }
101
+ json(res, { trigger: summary }, 201);
102
+ return true;
103
+ }
104
+ const runsMatch = /^\/api\/triggers\/([^/]+)\/runs$/.exec(pathname);
105
+ if (method === "GET" && runsMatch) {
106
+ const task = await findTask(runtime, decodeURIComponent(runsMatch[1]));
107
+ if (!task) {
108
+ error(res, "Trigger not found", 404);
109
+ return true;
110
+ }
111
+ json(res, { runs: readTriggerRuns(task) });
112
+ return true;
113
+ }
114
+ const execMatch = /^\/api\/triggers\/([^/]+)\/execute$/.exec(pathname);
115
+ if (method === "POST" && execMatch) {
116
+ const task = await findTask(runtime, decodeURIComponent(execMatch[1]));
117
+ if (!task) {
118
+ error(res, "Trigger not found", 404);
119
+ return true;
120
+ }
121
+ const result = await executeTriggerTask(runtime, task, {
122
+ source: "manual",
123
+ force: true
124
+ });
125
+ const refreshed = task.id ? await runtime.getTask(task.id) : null;
126
+ json(res, {
127
+ ok: true,
128
+ result,
129
+ trigger: refreshed ? taskToTriggerSummary(refreshed) : null
130
+ });
131
+ return true;
132
+ }
133
+ const itemMatch = /^\/api\/triggers\/([^/]+)$/.exec(pathname);
134
+ if (!itemMatch) return false;
135
+ const triggerId = decodeURIComponent(itemMatch[1]);
136
+ if (method === "GET") {
137
+ const task = await findTask(runtime, triggerId);
138
+ if (!task) {
139
+ error(res, "Trigger not found", 404);
140
+ return true;
141
+ }
142
+ const summary = taskToTriggerSummary(task);
143
+ if (!summary) {
144
+ error(res, "Trigger metadata is invalid", 500);
145
+ return true;
146
+ }
147
+ json(res, { trigger: summary });
148
+ return true;
149
+ }
150
+ if (method === "DELETE") {
151
+ const task = await findTask(runtime, triggerId);
152
+ if (!task?.id) {
153
+ error(res, "Trigger not found", 404);
154
+ return true;
155
+ }
156
+ await runtime.deleteTask(task.id);
157
+ json(res, { ok: true });
158
+ return true;
159
+ }
160
+ if (method === "PUT") {
161
+ const task = await findTask(runtime, triggerId);
162
+ if (!task?.id) {
163
+ error(res, "Trigger not found", 404);
164
+ return true;
165
+ }
166
+ const current = readTriggerConfig(task);
167
+ if (!current) {
168
+ error(res, "Trigger metadata is invalid", 500);
169
+ return true;
170
+ }
171
+ const body = await readJsonBody(req, res);
172
+ if (!body) return true;
173
+ const normalized = normalizeTriggerDraft({
174
+ input: {
175
+ ...body,
176
+ createdBy: current.createdBy,
177
+ intervalMs: body.intervalMs ?? current.intervalMs,
178
+ scheduledAtIso: body.scheduledAtIso ?? current.scheduledAtIso,
179
+ cronExpression: body.cronExpression ?? current.cronExpression,
180
+ maxRuns: body.maxRuns ?? current.maxRuns
181
+ },
182
+ fallback: {
183
+ displayName: current.displayName,
184
+ instructions: current.instructions,
185
+ triggerType: current.triggerType,
186
+ wakeMode: current.wakeMode,
187
+ enabled: body.enabled ?? current.enabled,
188
+ createdBy: current.createdBy
189
+ }
190
+ });
191
+ if (!normalized.draft) {
192
+ error(res, normalized.error ?? "Invalid update", 400);
193
+ return true;
194
+ }
195
+ const nextTrigger = buildTriggerConfig({
196
+ draft: normalized.draft,
197
+ triggerId: current.triggerId,
198
+ previous: current
199
+ });
200
+ const existingMeta = task.metadata ?? {};
201
+ const existingRuns = readTriggerRuns(task);
202
+ let nextMeta;
203
+ if (!nextTrigger.enabled) nextMeta = {
204
+ ...existingMeta,
205
+ updatedAt: Date.now(),
206
+ updateInterval: DISABLED_TRIGGER_INTERVAL_MS,
207
+ trigger: {
208
+ ...nextTrigger,
209
+ nextRunAtMs: Date.now() + DISABLED_TRIGGER_INTERVAL_MS
210
+ },
211
+ triggerRuns: existingRuns
212
+ };
213
+ else {
214
+ const built = buildTriggerMetadata({
215
+ existingMetadata: existingMeta,
216
+ trigger: nextTrigger,
217
+ nowMs: Date.now()
218
+ });
219
+ if (!built) {
220
+ error(res, "Unable to compute trigger schedule", 400);
221
+ return true;
222
+ }
223
+ nextMeta = built;
224
+ }
225
+ await runtime.updateTask(task.id, {
226
+ description: nextTrigger.displayName,
227
+ metadata: nextMeta
228
+ });
229
+ const refreshed = await runtime.getTask(task.id);
230
+ if (!refreshed) {
231
+ error(res, "Trigger updated but no longer available", 500);
232
+ return true;
233
+ }
234
+ const summary = taskToTriggerSummary(refreshed);
235
+ if (!summary) {
236
+ error(res, "Trigger metadata is invalid", 500);
237
+ return true;
238
+ }
239
+ json(res, { trigger: summary });
240
+ return true;
241
+ }
242
+ return false;
243
+ }
244
+
245
+ //#endregion
246
+ export { handleTriggerRoutes };
@@ -0,0 +1,134 @@
1
+ import { resolveStateDir } from "../config/paths.js";
2
+ import path from "node:path";
3
+ import { logger } from "@elizaos/core";
4
+ import fs from "node:fs";
5
+
6
+ //#region src/api/twitter-verify.ts
7
+ /**
8
+ * Twitter/X verification for whitelist eligibility.
9
+ *
10
+ * Users post a verification message on X containing their agent name and
11
+ * wallet address. The app verifies the tweet exists using the FxTwitter API
12
+ * (free, no auth required). Verified addresses are stored locally and can
13
+ * be collected into a Merkle tree for on-chain whitelist proofs.
14
+ */
15
+ const WHITELIST_FILE = "whitelist.json";
16
+ function generateVerificationMessage(agentName, walletAddress) {
17
+ return `Verifying my Milady agent "${agentName}" | ${`${walletAddress.slice(0, 6)}...${walletAddress.slice(-4)}`} #MiladyAgent`;
18
+ }
19
+ function parseTweetUrl(url) {
20
+ const match = url.match(/(?:twitter\.com|x\.com)\/(\w+)\/status\/(\d+)/);
21
+ if (!match) return null;
22
+ return {
23
+ screenName: match[1],
24
+ tweetId: match[2]
25
+ };
26
+ }
27
+ async function verifyTweet(tweetUrl, walletAddress) {
28
+ const parsed = parseTweetUrl(tweetUrl);
29
+ if (!parsed) return {
30
+ verified: false,
31
+ error: "Invalid tweet URL. Use a twitter.com or x.com status URL.",
32
+ handle: null
33
+ };
34
+ const apiUrl = `https://api.fxtwitter.com/${parsed.screenName}/status/${parsed.tweetId}`;
35
+ let response;
36
+ try {
37
+ response = await fetch(apiUrl, {
38
+ headers: { "User-Agent": "MiladyVerifier/1.0" },
39
+ signal: AbortSignal.timeout(15e3)
40
+ });
41
+ } catch (err) {
42
+ logger.warn(`[twitter-verify] FxTwitter fetch failed: ${err}`);
43
+ return {
44
+ verified: false,
45
+ error: "Could not reach tweet verification service. Try again later.",
46
+ handle: null
47
+ };
48
+ }
49
+ if (!response.ok) {
50
+ if (response.status === 404) return {
51
+ verified: false,
52
+ error: "Tweet not found. Make sure the URL is correct and the tweet is public.",
53
+ handle: null
54
+ };
55
+ return {
56
+ verified: false,
57
+ error: `Tweet fetch failed (HTTP ${response.status})`,
58
+ handle: null
59
+ };
60
+ }
61
+ let data;
62
+ try {
63
+ data = await response.json();
64
+ } catch {
65
+ return {
66
+ verified: false,
67
+ error: "Invalid response from verification service",
68
+ handle: null
69
+ };
70
+ }
71
+ if (!data.tweet?.text) return {
72
+ verified: false,
73
+ error: "Could not read tweet content",
74
+ handle: null
75
+ };
76
+ const tweetText = data.tweet.text;
77
+ const handle = data.tweet.author?.screen_name ?? parsed.screenName;
78
+ const shortAddr = `${walletAddress.slice(0, 6)}...${walletAddress.slice(-4)}`;
79
+ const hasAddress = tweetText.includes(shortAddr) || tweetText.toLowerCase().includes(walletAddress.toLowerCase().slice(0, 10));
80
+ const hasHashtag = tweetText.includes("#MiladyAgent");
81
+ if (!hasAddress) return {
82
+ verified: false,
83
+ error: "Tweet does not contain your wallet address. Make sure you copied the full verification message.",
84
+ handle
85
+ };
86
+ if (!hasHashtag) return {
87
+ verified: false,
88
+ error: "Tweet is missing #MiladyAgent hashtag.",
89
+ handle
90
+ };
91
+ return {
92
+ verified: true,
93
+ error: null,
94
+ handle
95
+ };
96
+ }
97
+ function whitelistPath() {
98
+ return path.join(resolveStateDir(), WHITELIST_FILE);
99
+ }
100
+ function loadWhitelist() {
101
+ const filePath = whitelistPath();
102
+ if (!fs.existsSync(filePath)) return { verified: {} };
103
+ const raw = fs.readFileSync(filePath, "utf-8");
104
+ return JSON.parse(raw);
105
+ }
106
+ function saveWhitelist(data) {
107
+ const filePath = whitelistPath();
108
+ const dir = path.dirname(filePath);
109
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, {
110
+ recursive: true,
111
+ mode: 448
112
+ });
113
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), {
114
+ encoding: "utf-8",
115
+ mode: 384
116
+ });
117
+ }
118
+ function markAddressVerified(address, tweetUrl, handle) {
119
+ const wl = loadWhitelist();
120
+ wl.verified[address.toLowerCase()] = {
121
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
122
+ tweetUrl,
123
+ handle
124
+ };
125
+ saveWhitelist(wl);
126
+ logger.info(`[twitter-verify] Address ${address} verified via @${handle}`);
127
+ }
128
+ function isAddressWhitelisted(address) {
129
+ const wl = loadWhitelist();
130
+ return address.toLowerCase() in wl.verified;
131
+ }
132
+
133
+ //#endregion
134
+ export { generateVerificationMessage, isAddressWhitelisted, markAddressVerified, verifyTweet };
@@ -0,0 +1,108 @@
1
+ import { logger } from "@elizaos/core";
2
+ import { ethers } from "ethers";
3
+
4
+ //#region src/api/tx-service.ts
5
+ /**
6
+ * Ethereum transaction signing and contract interaction layer.
7
+ *
8
+ * Provides the missing transaction capability to Milady's wallet system,
9
+ * which currently only handles key generation and balance fetching.
10
+ * Used by the registry and drop services for on-chain operations.
11
+ */
12
+ /**
13
+ * Validate that a private key is a valid 32-byte hex string.
14
+ */
15
+ function isValidPrivateKey(key) {
16
+ const normalized = key.startsWith("0x") ? key.slice(2) : key;
17
+ if (normalized.length !== 64) return false;
18
+ return /^[0-9a-fA-F]+$/.test(normalized);
19
+ }
20
+ var TxService = class {
21
+ constructor(rpcUrl, privateKey) {
22
+ this.rpcUrl = rpcUrl;
23
+ this.provider = new ethers.JsonRpcProvider(rpcUrl);
24
+ if (!isValidPrivateKey(privateKey)) {
25
+ const preview = privateKey.length > 10 ? `${privateKey.slice(0, 6)}...${privateKey.slice(-4)}` : "(empty or too short)";
26
+ throw new Error(`Invalid EVM_PRIVATE_KEY: expected 64-character hex string, got ${preview}. Please set a valid private key in your environment or .env file.`);
27
+ }
28
+ const normalizedKey = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
29
+ this.wallet = new ethers.Wallet(normalizedKey, this.provider);
30
+ }
31
+ /**
32
+ * Get fresh nonce for the wallet address.
33
+ * Always fetches from blockchain using a fresh provider to avoid caching issues.
34
+ * This ensures we always get the correct nonce even after failed transactions.
35
+ */
36
+ async getFreshNonce() {
37
+ const freshProvider = new ethers.JsonRpcProvider(this.rpcUrl);
38
+ try {
39
+ return await freshProvider.getTransactionCount(this.wallet.address, "pending");
40
+ } finally {
41
+ freshProvider.destroy();
42
+ }
43
+ }
44
+ get address() {
45
+ return this.wallet.address;
46
+ }
47
+ async getBalance() {
48
+ return this.provider.getBalance(this.wallet.address);
49
+ }
50
+ async getBalanceFormatted() {
51
+ const balance = await this.getBalance();
52
+ return ethers.formatEther(balance);
53
+ }
54
+ async getChainId() {
55
+ const network = await this.provider.getNetwork();
56
+ return Number(network.chainId);
57
+ }
58
+ getContract(address, abi) {
59
+ return new ethers.Contract(address, abi, this.wallet);
60
+ }
61
+ getReadOnlyContract(address, abi) {
62
+ return new ethers.Contract(address, abi, this.provider);
63
+ }
64
+ async estimateGas(tx) {
65
+ return this.provider.estimateGas(tx);
66
+ }
67
+ async getFeeData() {
68
+ return this.provider.getFeeData();
69
+ }
70
+ /**
71
+ * Wait for a transaction to be mined and return the receipt.
72
+ * Throws if the transaction fails or times out.
73
+ */
74
+ async waitForTransaction(txHash, confirmations = 1, timeoutMs = 12e4) {
75
+ const receipt = await this.provider.waitForTransaction(txHash, confirmations, timeoutMs);
76
+ if (!receipt) throw new Error(`Transaction ${txHash} timed out after ${timeoutMs}ms`);
77
+ if (receipt.status === 0) throw new Error(`Transaction ${txHash} reverted`);
78
+ return receipt;
79
+ }
80
+ /**
81
+ * Estimate the gas cost in ETH for a contract call.
82
+ * Useful for showing users how much gas they'll need.
83
+ */
84
+ async estimateGasCostEth(tx) {
85
+ const gasLimit = await this.estimateGas(tx);
86
+ const feeData = await this.getFeeData();
87
+ const costWei = gasLimit * (feeData.gasPrice ?? feeData.maxFeePerGas ?? 0n);
88
+ return ethers.formatEther(costWei);
89
+ }
90
+ /**
91
+ * Check whether the wallet has enough balance for a given value + estimated gas.
92
+ */
93
+ async hasEnoughBalance(value, gasEstimate) {
94
+ const balance = await this.getBalance();
95
+ const feeData = await this.getFeeData();
96
+ return balance >= value + gasEstimate * (feeData.gasPrice ?? feeData.maxFeePerGas ?? 0n);
97
+ }
98
+ /**
99
+ * Log a summary of the tx service state for diagnostics.
100
+ */
101
+ async logStatus() {
102
+ const [balance, chainId] = await Promise.all([this.getBalanceFormatted(), this.getChainId()]);
103
+ logger.info(`[tx-service] address=${this.address} chain=${chainId} balance=${balance} ETH`);
104
+ }
105
+ };
106
+
107
+ //#endregion
108
+ export { TxService };