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.
- package/dist/_virtual/_rolldown/runtime.js +7 -0
- package/dist/actions/emote.js +64 -0
- package/dist/actions/restart.js +81 -0
- package/dist/actions/send-message.js +152 -0
- package/dist/agent-admin-routes.js +82 -0
- package/dist/agent-lifecycle-routes.js +79 -0
- package/dist/agent-transfer-routes.js +102 -0
- package/dist/api/agent-admin-routes.js +82 -0
- package/dist/api/agent-lifecycle-routes.js +79 -0
- package/dist/api/agent-transfer-routes.js +102 -0
- package/dist/api/apps-hyperscape-routes.js +58 -0
- package/dist/api/apps-routes.js +114 -0
- package/dist/api/auth-routes.js +56 -0
- package/dist/api/autonomy-routes.js +44 -0
- package/dist/api/bug-report-routes.js +111 -0
- package/dist/api/character-routes.js +195 -0
- package/dist/api/cloud-routes.js +330 -0
- package/dist/api/cloud-status-routes.js +155 -0
- package/dist/api/compat-utils.js +111 -0
- package/dist/api/database.js +735 -0
- package/dist/api/diagnostics-routes.js +205 -0
- package/dist/api/drop-service.js +134 -0
- package/dist/api/early-logs.js +86 -0
- package/dist/api/http-helpers.js +131 -0
- package/dist/api/knowledge-routes.js +534 -0
- package/dist/api/memory-bounds.js +71 -0
- package/dist/api/models-routes.js +28 -0
- package/dist/api/og-tracker.js +36 -0
- package/dist/api/permissions-routes.js +109 -0
- package/dist/api/plugin-validation.js +198 -0
- package/dist/api/provider-switch-config.js +41 -0
- package/dist/api/registry-routes.js +86 -0
- package/dist/api/registry-service.js +164 -0
- package/dist/api/sandbox-routes.js +1112 -0
- package/dist/api/server.js +7949 -0
- package/dist/api/subscription-routes.js +172 -0
- package/dist/api/terminal-run-limits.js +24 -0
- package/dist/api/training-routes.js +158 -0
- package/dist/api/trajectory-routes.js +300 -0
- package/dist/api/trigger-routes.js +246 -0
- package/dist/api/twitter-verify.js +134 -0
- package/dist/api/tx-service.js +108 -0
- package/dist/api/wallet-routes.js +266 -0
- package/dist/api/wallet.js +568 -0
- package/dist/api/whatsapp-routes.js +182 -0
- package/dist/api/zip-utils.js +109 -0
- package/dist/apps-hyperscape-routes.js +58 -0
- package/dist/apps-routes.js +114 -0
- package/dist/ascii.js +20 -0
- package/dist/auth/anthropic.js +44 -0
- package/dist/auth/apply-stealth.js +41 -0
- package/dist/auth/claude-code-stealth.js +78 -0
- package/dist/auth/credentials.js +156 -0
- package/dist/auth/index.js +5 -0
- package/dist/auth/openai-codex.js +66 -0
- package/dist/auth/types.js +9 -0
- package/dist/auth-routes.js +56 -0
- package/dist/autonomy-routes.js +44 -0
- package/dist/bug-report-routes.js +111 -0
- package/dist/build-info.json +6 -0
- package/dist/character-routes.js +195 -0
- package/dist/cli/argv.js +63 -0
- package/dist/cli/banner.js +34 -0
- package/dist/cli/cli-name.js +21 -0
- package/dist/cli/cli-utils.js +16 -0
- package/dist/cli/git-commit.js +78 -0
- package/dist/cli/parse-duration.js +15 -0
- package/dist/cli/plugins-cli.js +590 -0
- package/dist/cli/profile-utils.js +9 -0
- package/dist/cli/profile.js +95 -0
- package/dist/cli/program/build-program.js +17 -0
- package/dist/cli/program/command-registry.js +23 -0
- package/dist/cli/program/help.js +47 -0
- package/dist/cli/program/preaction.js +33 -0
- package/dist/cli/program/register.config.js +106 -0
- package/dist/cli/program/register.configure.js +20 -0
- package/dist/cli/program/register.dashboard.js +124 -0
- package/dist/cli/program/register.models.js +23 -0
- package/dist/cli/program/register.setup.js +36 -0
- package/dist/cli/program/register.start.js +22 -0
- package/dist/cli/program/register.subclis.js +70 -0
- package/dist/cli/program/register.tui.js +163 -0
- package/dist/cli/program/register.update.js +154 -0
- package/dist/cli/program.js +3 -0
- package/dist/cli/run-main.js +37 -0
- package/dist/cli/version.js +7 -0
- package/dist/cloud/validate-url.js +93 -0
- package/dist/cloud-routes.js +330 -0
- package/dist/cloud-status-routes.js +155 -0
- package/dist/compat-utils.js +111 -0
- package/dist/config/config.js +69 -0
- package/dist/config/env-vars.js +19 -0
- package/dist/config/includes.js +121 -0
- package/dist/config/object-utils.js +7 -0
- package/dist/config/paths.js +38 -0
- package/dist/config/plugin-auto-enable.js +231 -0
- package/dist/config/schema.js +864 -0
- package/dist/config/telegram-custom-commands.js +76 -0
- package/dist/config/zod-schema.agent-runtime.js +519 -0
- package/dist/config/zod-schema.core.js +538 -0
- package/dist/config/zod-schema.hooks.js +103 -0
- package/dist/config/zod-schema.js +488 -0
- package/dist/config/zod-schema.providers-core.js +785 -0
- package/dist/config/zod-schema.session.js +73 -0
- package/dist/core-plugins.js +37 -0
- package/dist/custom-actions.js +250 -0
- package/dist/database.js +735 -0
- package/dist/diagnostics/integration-observability.js +57 -0
- package/dist/diagnostics-routes.js +205 -0
- package/dist/drop-service.js +134 -0
- package/dist/early-logs.js +24 -0
- package/dist/eliza.js +2061 -0
- package/dist/emotes/catalog.js +271 -0
- package/dist/entry.js +40 -0
- package/dist/hooks/discovery.js +167 -0
- package/dist/hooks/eligibility.js +64 -0
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/loader.js +147 -0
- package/dist/hooks/registry.js +55 -0
- package/dist/http-helpers.js +131 -0
- package/dist/index.js +49 -0
- package/dist/knowledge-routes.js +534 -0
- package/dist/memory-bounds.js +71 -0
- package/dist/milady-plugin.js +90 -0
- package/dist/models-routes.js +28 -0
- package/dist/onboarding-names.js +78 -0
- package/dist/onboarding-presets.js +922 -0
- package/dist/package.json +1 -0
- package/dist/permissions-routes.js +109 -0
- package/dist/plugin-validation.js +107 -0
- package/dist/plugins/whatsapp/actions.js +91 -0
- package/dist/plugins/whatsapp/index.js +16 -0
- package/dist/plugins/whatsapp/service.js +270 -0
- package/dist/provider-switch-config.js +41 -0
- package/dist/providers/admin-trust.js +46 -0
- package/dist/providers/autonomous-state.js +101 -0
- package/dist/providers/session-bridge.js +86 -0
- package/dist/providers/session-utils.js +36 -0
- package/dist/providers/simple-mode.js +50 -0
- package/dist/providers/ui-catalog.js +15 -0
- package/dist/providers/workspace-provider.js +93 -0
- package/dist/providers/workspace.js +348 -0
- package/dist/registry-routes.js +86 -0
- package/dist/registry-service.js +164 -0
- package/dist/restart.js +40 -0
- package/dist/runtime/core-plugins.js +37 -0
- package/dist/runtime/custom-actions.js +250 -0
- package/dist/runtime/eliza.js +2061 -0
- package/dist/runtime/embedding-manager-support.js +185 -0
- package/dist/runtime/embedding-manager.js +193 -0
- package/dist/runtime/embedding-presets.js +54 -0
- package/dist/runtime/embedding-state.js +8 -0
- package/dist/runtime/milady-plugin.js +90 -0
- package/dist/runtime/onboarding-names.js +78 -0
- package/dist/runtime/restart.js +40 -0
- package/dist/runtime/version.js +7 -0
- package/dist/sandbox-routes.js +1112 -0
- package/dist/security/audit-log.js +149 -0
- package/dist/security/network-policy.js +70 -0
- package/dist/server.js +7949 -0
- package/dist/services/agent-export.js +559 -0
- package/dist/services/app-manager.js +389 -0
- package/dist/services/browser-capture.js +86 -0
- package/dist/services/fallback-training-service.js +128 -0
- package/dist/services/mcp-marketplace.js +134 -0
- package/dist/services/plugin-installer.js +396 -0
- package/dist/services/plugin-manager-types.js +15 -0
- package/dist/services/registry-client-app-meta.js +144 -0
- package/dist/services/registry-client-endpoints.js +166 -0
- package/dist/services/registry-client-local.js +271 -0
- package/dist/services/registry-client-network.js +93 -0
- package/dist/services/registry-client-queries.js +70 -0
- package/dist/services/registry-client.js +157 -0
- package/dist/services/sandbox-engine.js +511 -0
- package/dist/services/sandbox-manager.js +297 -0
- package/dist/services/self-updater.js +175 -0
- package/dist/services/skill-catalog-client.js +119 -0
- package/dist/services/skill-marketplace.js +521 -0
- package/dist/services/stream-manager.js +236 -0
- package/dist/services/update-checker.js +121 -0
- package/dist/services/update-notifier.js +29 -0
- package/dist/services/version-compat.js +78 -0
- package/dist/services/whatsapp-pairing.js +196 -0
- package/dist/shared/ui-catalog-prompt.js +728 -0
- package/dist/subscription-routes.js +172 -0
- package/dist/terminal/links.js +19 -0
- package/dist/terminal/palette.js +14 -0
- package/dist/terminal/theme.js +25 -0
- package/dist/terminal-run-limits.js +24 -0
- package/dist/training-routes.js +158 -0
- package/dist/trajectory-routes.js +300 -0
- package/dist/trigger-routes.js +246 -0
- package/dist/triggers/action.js +218 -0
- package/dist/triggers/runtime.js +281 -0
- package/dist/triggers/scheduling.js +295 -0
- package/dist/triggers/types.js +5 -0
- package/dist/tui/components/assistant-message.js +76 -0
- package/dist/tui/components/chat-editor.js +34 -0
- package/dist/tui/components/embeddings-overlay.js +46 -0
- package/dist/tui/components/footer.js +60 -0
- package/dist/tui/components/index.js +15 -0
- package/dist/tui/components/modal-frame.js +45 -0
- package/dist/tui/components/modal-style.js +15 -0
- package/dist/tui/components/model-selector.js +70 -0
- package/dist/tui/components/pinned-chat-layout.js +46 -0
- package/dist/tui/components/plugins-endpoints-tab.js +196 -0
- package/dist/tui/components/plugins-installed-tab-view.js +69 -0
- package/dist/tui/components/plugins-installed-tab.js +319 -0
- package/dist/tui/components/plugins-overlay-catalog.js +81 -0
- package/dist/tui/components/plugins-overlay-data-api.js +21 -0
- package/dist/tui/components/plugins-overlay-data-shared.js +20 -0
- package/dist/tui/components/plugins-overlay-data.js +323 -0
- package/dist/tui/components/plugins-overlay.js +117 -0
- package/dist/tui/components/plugins-store-tab.js +148 -0
- package/dist/tui/components/settings-overlay.js +61 -0
- package/dist/tui/components/status-bar.js +64 -0
- package/dist/tui/components/tool-execution.js +68 -0
- package/dist/tui/components/user-message.js +22 -0
- package/dist/tui/eliza-tui-bridge.js +606 -0
- package/dist/tui/index.js +370 -0
- package/dist/tui/modal-presets.js +33 -0
- package/dist/tui/model-spec.js +46 -0
- package/dist/tui/sse-parser.js +78 -0
- package/dist/tui/theme.js +110 -0
- package/dist/tui/titlebar-spinner.js +62 -0
- package/dist/tui/tui-app.js +311 -0
- package/dist/tui/ws-client.js +215 -0
- package/dist/twitter-verify.js +134 -0
- package/dist/tx-service.js +108 -0
- package/dist/utils/exec-safety.js +17 -0
- package/dist/utils/globals.js +20 -0
- package/dist/utils/milady-root.js +61 -0
- package/dist/utils/number-parsing.js +37 -0
- package/dist/version-resolver.js +37 -0
- package/dist/version.js +7 -0
- package/dist/wallet-routes.js +266 -0
- package/dist/wallet.js +568 -0
- package/dist/whatsapp-routes.js +182 -0
- package/dist/zip-utils.js +109 -0
- package/milady.mjs +14 -0
- 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 };
|