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,311 @@
1
+ import { tuiTheme } from "./theme.js";
2
+ import { ChatEditor } from "./components/chat-editor.js";
3
+ import { EmbeddingsOverlayComponent } from "./components/embeddings-overlay.js";
4
+ import { FooterComponent } from "./components/footer.js";
5
+ import { ModelSelectorComponent } from "./components/model-selector.js";
6
+ import { PinnedChatLayout } from "./components/pinned-chat-layout.js";
7
+ import { PluginsOverlayComponent } from "./components/plugins-overlay.js";
8
+ import { SettingsOverlayComponent } from "./components/settings-overlay.js";
9
+ import { StatusBar } from "./components/status-bar.js";
10
+ import "./components/index.js";
11
+ import { MODAL_PRESETS } from "./modal-presets.js";
12
+ import { TitlebarSpinner } from "./titlebar-spinner.js";
13
+ import path from "node:path";
14
+ import { getModels, getProviders } from "@mariozechner/pi-ai";
15
+ import { CombinedAutocompleteProvider, Container, ProcessTerminal, Spacer, TUI, Text } from "@mariozechner/pi-tui";
16
+
17
+ //#region src/tui/tui-app.ts
18
+ var MiladyTUI = class {
19
+ constructor(options) {
20
+ this.options = options;
21
+ this.terminal = new ProcessTerminal();
22
+ this.chatContainer = new Container();
23
+ this.ephemeralStatusContainer = new Container();
24
+ this.statusBar = new StatusBar();
25
+ this.footer = new FooterComponent();
26
+ this.modelOverlay = null;
27
+ this.settingsOverlay = null;
28
+ this.embeddingsOverlay = null;
29
+ this.pluginsOverlay = null;
30
+ this.toolOutputExpanded = false;
31
+ this.showThinking = process.env.MILADY_TUI_SHOW_THINKING === "1";
32
+ this.titlebarSpinner = new TitlebarSpinner({ setTitle: (title) => this.terminal.setTitle(title) });
33
+ }
34
+ setOnSubmit(handler) {
35
+ this.onSubmit = handler;
36
+ }
37
+ setOnCtrlC(handler) {
38
+ this.onCtrlC = handler;
39
+ }
40
+ setOnToggleToolExpand(handler) {
41
+ this.onToggleToolExpand = handler;
42
+ }
43
+ setOnToggleThinking(handler) {
44
+ this.onToggleThinking = handler;
45
+ }
46
+ getShowThinking() {
47
+ return this.showThinking;
48
+ }
49
+ setModelSelectorHandlers(handlers) {
50
+ this.modelSelectorHandlers = handlers;
51
+ }
52
+ setEmbeddingHandlers(handlers) {
53
+ this.embeddingHandlers = handlers;
54
+ }
55
+ getToolOutputExpanded() {
56
+ return this.toolOutputExpanded;
57
+ }
58
+ async start() {
59
+ this.ui = new TUI(this.terminal);
60
+ this.chatContainer = new Container();
61
+ this.ephemeralStatusContainer = new Container();
62
+ const agentName = this.options.runtime.character?.name ?? "milady";
63
+ this.statusBar.update({ agentName });
64
+ this.titlebarSpinner.setBaseTitle(this.getBaseTitle());
65
+ const logo = tuiTheme.bold(tuiTheme.accent("Milady")) + tuiTheme.dim(` — ${agentName}`);
66
+ this.chatContainer.addChild(new Spacer(1));
67
+ this.chatContainer.addChild(new Text(logo, 1, 0));
68
+ this.chatContainer.addChild(new Spacer(1));
69
+ this.editor = new ChatEditor(this.ui, tuiTheme.editor, { paddingX: 1 });
70
+ this.editor.onSubmit = (text) => {
71
+ const trimmed = text.trim();
72
+ if (!trimmed) return;
73
+ this.editor.addToHistory(trimmed);
74
+ this.editor.setText("");
75
+ this.onSubmit?.(trimmed);
76
+ };
77
+ this.editor.onCtrlC = () => {
78
+ this.onCtrlC?.();
79
+ };
80
+ this.editor.onCtrlE = () => {
81
+ this.toolOutputExpanded = !this.toolOutputExpanded;
82
+ this.onToggleToolExpand?.(this.toolOutputExpanded);
83
+ this.ui.requestRender();
84
+ };
85
+ this.editor.onCtrlP = () => {
86
+ this.showModelSelector();
87
+ };
88
+ this.editor.onCtrlG = () => {
89
+ this.showPlugins();
90
+ };
91
+ const getModelCompletions = (argumentPrefix) => {
92
+ const prefix = argumentPrefix.trim().toLowerCase();
93
+ const items = [];
94
+ for (const provider of getProviders()) for (const model of getModels(provider)) {
95
+ const spec = `${model.provider}/${model.id}`;
96
+ if (!prefix || spec.toLowerCase().startsWith(prefix)) items.push({
97
+ value: spec,
98
+ label: spec,
99
+ description: model.api
100
+ });
101
+ if (items.length >= 80) return items;
102
+ }
103
+ return items;
104
+ };
105
+ this.editor.setAutocompleteProvider(new CombinedAutocompleteProvider([
106
+ {
107
+ name: "model",
108
+ description: "Switch model (open selector or /model provider/id)",
109
+ getArgumentCompletions: (argumentPrefix) => getModelCompletions(argumentPrefix)
110
+ },
111
+ {
112
+ name: "models",
113
+ description: "Alias for /model",
114
+ getArgumentCompletions: (argumentPrefix) => getModelCompletions(argumentPrefix)
115
+ },
116
+ {
117
+ name: "embeddings",
118
+ description: "Open/switch embedding model (/embeddings [fallback|standard|performance])"
119
+ },
120
+ {
121
+ name: "clear",
122
+ description: "Clear chat"
123
+ },
124
+ {
125
+ name: "settings",
126
+ description: "Open settings panel"
127
+ },
128
+ {
129
+ name: "plugins",
130
+ description: "Open plugin manager"
131
+ },
132
+ {
133
+ name: "help",
134
+ description: "Show help"
135
+ },
136
+ {
137
+ name: "exit",
138
+ description: "Quit"
139
+ },
140
+ {
141
+ name: "quit",
142
+ description: "Alias for /exit"
143
+ }
144
+ ]));
145
+ this.ui.addChild(new PinnedChatLayout({
146
+ chat: this.chatContainer,
147
+ ephemeralStatus: this.ephemeralStatusContainer,
148
+ statusBar: this.statusBar,
149
+ editor: this.editor,
150
+ footer: this.footer,
151
+ getTerminalRows: () => this.terminal.rows,
152
+ spacerLines: 1
153
+ }));
154
+ this.ui.onDebug = () => {
155
+ const info = [
156
+ `chat children: ${this.chatContainer.children.length}`,
157
+ `overlays: model=${!!this.modelOverlay} embeddings=${!!this.embeddingsOverlay}`,
158
+ `tool expand: ${this.toolOutputExpanded}`,
159
+ `terminal: ${this.terminal.columns}×${this.terminal.rows}`
160
+ ].join(" | ");
161
+ this.addToChatContainer(new Text(tuiTheme.dim(`[debug] ${info}`), 1, 0));
162
+ };
163
+ this.ui.setFocus(this.editor);
164
+ this.ui.start();
165
+ }
166
+ async stop() {
167
+ this.titlebarSpinner.dispose();
168
+ this.modelOverlay?.hide();
169
+ this.modelOverlay = null;
170
+ this.settingsOverlay?.hide();
171
+ this.settingsOverlay = null;
172
+ this.embeddingsOverlay?.hide();
173
+ this.embeddingsOverlay = null;
174
+ this.pluginsOverlay?.hide();
175
+ this.pluginsOverlay = null;
176
+ this.ui.stop();
177
+ }
178
+ addToChatContainer(component) {
179
+ this.chatContainer.addChild(component);
180
+ this.ui.requestRender();
181
+ }
182
+ setEphemeralStatus(component) {
183
+ this.ephemeralStatusContainer.clear();
184
+ this.ephemeralStatusContainer.addChild(component);
185
+ this.ui.requestRender();
186
+ }
187
+ clearEphemeralStatus() {
188
+ this.ephemeralStatusContainer.clear();
189
+ this.ui.requestRender();
190
+ }
191
+ requestRender() {
192
+ this.ui.requestRender();
193
+ }
194
+ getTUI() {
195
+ return this.ui;
196
+ }
197
+ getStatusBar() {
198
+ return this.statusBar;
199
+ }
200
+ openModelSelector() {
201
+ this.showModelSelector();
202
+ }
203
+ openSettings() {
204
+ this.showSettings();
205
+ }
206
+ openEmbeddings() {
207
+ this.showEmbeddings();
208
+ }
209
+ openPlugins() {
210
+ this.showPlugins();
211
+ }
212
+ setBusy(busy) {
213
+ if (busy) this.titlebarSpinner.start();
214
+ else this.titlebarSpinner.stop();
215
+ }
216
+ clearChat() {
217
+ this.chatContainer.clear();
218
+ this.ui.requestRender();
219
+ }
220
+ getBaseTitle() {
221
+ const cwd = path.basename(process.cwd());
222
+ return `${this.options.runtime.character?.name ?? "milady"} - ${cwd}`;
223
+ }
224
+ showSettings() {
225
+ if (this.settingsOverlay) return;
226
+ const settings = new SettingsOverlayComponent({
227
+ showThinking: this.showThinking,
228
+ toolExpand: this.toolOutputExpanded,
229
+ onToggleThinking: (enabled) => {
230
+ this.showThinking = enabled;
231
+ this.onToggleThinking?.(enabled);
232
+ },
233
+ onToggleToolExpand: (expanded) => {
234
+ this.toolOutputExpanded = expanded;
235
+ this.onToggleToolExpand?.(expanded);
236
+ },
237
+ onClose: () => {
238
+ this.settingsOverlay?.hide();
239
+ this.settingsOverlay = null;
240
+ this.ui.setFocus(this.editor);
241
+ this.ui.requestRender();
242
+ }
243
+ });
244
+ this.settingsOverlay = this.ui.showOverlay(settings, MODAL_PRESETS.compact);
245
+ this.ui.requestRender();
246
+ }
247
+ showEmbeddings() {
248
+ if (!this.embeddingHandlers) return;
249
+ if (this.embeddingsOverlay) return;
250
+ const embeddings = new EmbeddingsOverlayComponent({
251
+ options: this.embeddingHandlers.getOptions(),
252
+ onSelectTier: (tier) => {
253
+ this.embeddingsOverlay?.hide();
254
+ this.embeddingsOverlay = null;
255
+ this.ui.setFocus(this.editor);
256
+ this.ui.requestRender();
257
+ this.embeddingHandlers?.onSelectTier(tier);
258
+ },
259
+ onCancel: () => {
260
+ this.embeddingsOverlay?.hide();
261
+ this.embeddingsOverlay = null;
262
+ this.ui.setFocus(this.editor);
263
+ this.ui.requestRender();
264
+ }
265
+ });
266
+ this.embeddingsOverlay = this.ui.showOverlay(embeddings, MODAL_PRESETS.standard);
267
+ this.ui.requestRender();
268
+ }
269
+ showPlugins() {
270
+ if (this.pluginsOverlay) return;
271
+ const plugins = new PluginsOverlayComponent({
272
+ runtime: this.options.runtime,
273
+ apiBaseUrl: this.options.apiBaseUrl,
274
+ onClose: () => {
275
+ this.pluginsOverlay?.hide();
276
+ this.pluginsOverlay = null;
277
+ this.ui.setFocus(this.editor);
278
+ this.ui.requestRender();
279
+ },
280
+ requestRender: () => this.ui.requestRender()
281
+ });
282
+ this.pluginsOverlay = this.ui.showOverlay(plugins, MODAL_PRESETS.wide);
283
+ this.ui.requestRender();
284
+ }
285
+ showModelSelector() {
286
+ if (!this.modelSelectorHandlers) return;
287
+ if (this.modelOverlay) return;
288
+ const selector = new ModelSelectorComponent({
289
+ currentModel: this.modelSelectorHandlers.getCurrentModel(),
290
+ hasCredentials: this.modelSelectorHandlers.hasCredentials,
291
+ onSelect: (model) => {
292
+ this.modelSelectorHandlers?.onSelectModel(model);
293
+ this.modelOverlay?.hide();
294
+ this.modelOverlay = null;
295
+ this.ui.setFocus(this.editor);
296
+ this.ui.requestRender();
297
+ },
298
+ onCancel: () => {
299
+ this.modelOverlay?.hide();
300
+ this.modelOverlay = null;
301
+ this.ui.setFocus(this.editor);
302
+ this.ui.requestRender();
303
+ }
304
+ });
305
+ this.modelOverlay = this.ui.showOverlay(selector, MODAL_PRESETS.standard);
306
+ this.ui.requestRender();
307
+ }
308
+ };
309
+
310
+ //#endregion
311
+ export { MiladyTUI };
@@ -0,0 +1,215 @@
1
+ import process from "node:process";
2
+ import { WebSocket } from "ws";
3
+
4
+ //#region src/tui/ws-client.ts
5
+ const WS_OPEN = 1;
6
+ const WS_CONNECTING = 0;
7
+ const WS_CLOSED = 3;
8
+ function defaultSocketFactory(url, options) {
9
+ return new WebSocket(url, { headers: options.headers });
10
+ }
11
+ function normalizeError(error) {
12
+ if (error instanceof Error) return error;
13
+ return new Error(String(error));
14
+ }
15
+ function decodeWsMessage(data) {
16
+ if (typeof data === "string") return data;
17
+ if (data instanceof ArrayBuffer) return Buffer.from(data).toString("utf8");
18
+ if (ArrayBuffer.isView(data)) return Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString("utf8");
19
+ if (Array.isArray(data)) {
20
+ const buffers = data.map((part) => {
21
+ if (typeof part === "string") return Buffer.from(part, "utf8");
22
+ if (part instanceof ArrayBuffer) return Buffer.from(part);
23
+ if (ArrayBuffer.isView(part)) return Buffer.from(part.buffer, part.byteOffset, part.byteLength);
24
+ return Buffer.from(String(part), "utf8");
25
+ });
26
+ return Buffer.concat(buffers).toString("utf8");
27
+ }
28
+ return String(data);
29
+ }
30
+ var ApiModeWsClient = class {
31
+ constructor(options) {
32
+ this.options = options;
33
+ this.ws = null;
34
+ this.wsSendQueue = [];
35
+ this.reconnectTimer = null;
36
+ this.closed = false;
37
+ this.latestActiveConversationId = null;
38
+ this.lastSentActiveConversationId = null;
39
+ this.queueLimit = Math.max(1, options.queueLimit ?? 32);
40
+ this.backoffMs = options.reconnectInitialDelayMs ?? 500;
41
+ this.reconnectMaxDelayMs = options.reconnectMaxDelayMs ?? 1e4;
42
+ this.wsUrl = this.buildWsUrl(options.apiBaseUrl);
43
+ this.socketFactory = options.socketFactory ?? defaultSocketFactory;
44
+ this.setTimeoutFn = options.setTimeoutFn ?? setTimeout;
45
+ this.clearTimeoutFn = options.clearTimeoutFn ?? clearTimeout;
46
+ }
47
+ connect() {
48
+ if (this.closed) return;
49
+ if (this.ws && (this.ws.readyState === WS_OPEN || this.ws.readyState === WS_CONNECTING)) return;
50
+ const token = this.getAuthToken();
51
+ const headers = {};
52
+ if (token) headers.Authorization = `Bearer ${token}`;
53
+ let socket;
54
+ try {
55
+ socket = this.socketFactory(this.wsUrl, { headers });
56
+ this.ws = socket;
57
+ } catch (error) {
58
+ const rootCause = normalizeError(error);
59
+ if (token) this.handleError(/* @__PURE__ */ new Error(`Failed to connect websocket with Authorization header. When MILADY_API_TOKEN is set, TUI websocket auth requires header-capable websocket support. Root cause: ${rootCause.message}`));
60
+ else this.handleError(rootCause);
61
+ this.scheduleReconnect();
62
+ return;
63
+ }
64
+ socket.on("open", () => {
65
+ if (this.closed || this.ws !== socket) return;
66
+ this.backoffMs = this.options.reconnectInitialDelayMs ?? 500;
67
+ this.flushSendQueue();
68
+ this.syncActiveConversation();
69
+ });
70
+ socket.on("message", (data) => {
71
+ if (this.closed || this.ws !== socket) return;
72
+ this.handleIncomingMessage(data);
73
+ });
74
+ socket.on("close", () => {
75
+ if (this.ws !== socket) return;
76
+ this.ws = null;
77
+ this.lastSentActiveConversationId = null;
78
+ if (this.closed) return;
79
+ this.scheduleReconnect();
80
+ });
81
+ socket.on("error", (error) => {
82
+ if (this.closed || this.ws !== socket) return;
83
+ this.handleError(error);
84
+ });
85
+ }
86
+ close() {
87
+ this.closed = true;
88
+ if (this.reconnectTimer) {
89
+ this.clearTimeoutFn(this.reconnectTimer);
90
+ this.reconnectTimer = null;
91
+ }
92
+ const socket = this.ws;
93
+ this.ws = null;
94
+ socket?.close();
95
+ }
96
+ sendMessage(data) {
97
+ const payload = JSON.stringify(data);
98
+ if (this.ws?.readyState === WS_OPEN) try {
99
+ this.ws.send(payload);
100
+ this.noteSentActiveConversation(data);
101
+ return;
102
+ } catch {}
103
+ this.queuePayload(payload, data);
104
+ if (!this.ws || this.ws.readyState === WS_CLOSED) this.connect();
105
+ }
106
+ setActiveConversationId(conversationId) {
107
+ const normalized = conversationId?.trim() || null;
108
+ this.latestActiveConversationId = normalized;
109
+ if (!normalized) {
110
+ this.wsSendQueue = this.wsSendQueue.filter((queued) => {
111
+ try {
112
+ return JSON.parse(queued).type !== "active-conversation";
113
+ } catch {
114
+ return true;
115
+ }
116
+ });
117
+ this.lastSentActiveConversationId = null;
118
+ return;
119
+ }
120
+ this.sendMessage({
121
+ type: "active-conversation",
122
+ conversationId: normalized
123
+ });
124
+ }
125
+ buildWsUrl(apiBaseUrl) {
126
+ const base = new URL(apiBaseUrl);
127
+ const wsUrl = new URL(base.toString());
128
+ wsUrl.pathname = `${wsUrl.pathname.replace(/\/+$/, "")}/ws`.replace(/\/+/g, "/");
129
+ wsUrl.protocol = base.protocol === "https:" ? "wss:" : "ws:";
130
+ wsUrl.search = "";
131
+ wsUrl.hash = "";
132
+ return wsUrl.toString();
133
+ }
134
+ getAuthToken() {
135
+ if (this.options.getAuthToken) {
136
+ const explicit = this.options.getAuthToken();
137
+ if (typeof explicit !== "string") return null;
138
+ return explicit.trim() || null;
139
+ }
140
+ return process.env.MILADY_API_TOKEN?.trim() || null;
141
+ }
142
+ flushSendQueue() {
143
+ if (!this.ws || this.ws.readyState !== WS_OPEN || this.wsSendQueue.length < 1) return;
144
+ const pending = this.wsSendQueue;
145
+ this.wsSendQueue = [];
146
+ for (let i = 0; i < pending.length; i++) {
147
+ if (!this.ws || this.ws.readyState !== WS_OPEN) {
148
+ this.wsSendQueue = pending.slice(i).concat(this.wsSendQueue);
149
+ break;
150
+ }
151
+ const payload = pending[i];
152
+ try {
153
+ this.ws.send(payload);
154
+ this.noteSentActiveConversationFromPayload(payload);
155
+ } catch {
156
+ this.wsSendQueue = pending.slice(i).concat(this.wsSendQueue);
157
+ break;
158
+ }
159
+ }
160
+ }
161
+ syncActiveConversation() {
162
+ if (!this.latestActiveConversationId) return;
163
+ if (this.lastSentActiveConversationId === this.latestActiveConversationId) return;
164
+ this.sendMessage({
165
+ type: "active-conversation",
166
+ conversationId: this.latestActiveConversationId
167
+ });
168
+ }
169
+ queuePayload(payload, data) {
170
+ if (data.type === "active-conversation") this.wsSendQueue = this.wsSendQueue.filter((queued) => {
171
+ try {
172
+ return JSON.parse(queued).type !== "active-conversation";
173
+ } catch {
174
+ return true;
175
+ }
176
+ });
177
+ if (this.wsSendQueue.length >= this.queueLimit) this.wsSendQueue.shift();
178
+ this.wsSendQueue.push(payload);
179
+ }
180
+ scheduleReconnect() {
181
+ if (this.closed || this.reconnectTimer) return;
182
+ this.reconnectTimer = this.setTimeoutFn(() => {
183
+ this.reconnectTimer = null;
184
+ this.connect();
185
+ }, this.backoffMs);
186
+ this.backoffMs = Math.min(this.backoffMs * 1.5, this.reconnectMaxDelayMs);
187
+ }
188
+ handleIncomingMessage(rawData) {
189
+ const encoded = decodeWsMessage(rawData);
190
+ let parsed;
191
+ try {
192
+ parsed = JSON.parse(encoded);
193
+ } catch {
194
+ return;
195
+ }
196
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return;
197
+ this.options.onMessage(parsed);
198
+ }
199
+ handleError(error) {
200
+ this.options.onError?.(normalizeError(error));
201
+ }
202
+ noteSentActiveConversation(data) {
203
+ if (data.type !== "active-conversation") return;
204
+ this.lastSentActiveConversationId = (typeof data.conversationId === "string" ? data.conversationId.trim() : "") || null;
205
+ }
206
+ noteSentActiveConversationFromPayload(payload) {
207
+ try {
208
+ const parsed = JSON.parse(payload);
209
+ this.noteSentActiveConversation(parsed);
210
+ } catch {}
211
+ }
212
+ };
213
+
214
+ //#endregion
215
+ export { ApiModeWsClient };
@@ -0,0 +1,134 @@
1
+ import { resolveStateDir } from "./config/paths.js";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { logger } from "@elizaos/core";
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 };