@elizaos/autonomous 2.0.0-alpha.10
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/LICENSE +21 -0
- package/package.json +270 -0
- package/src/actions/emote.ts +101 -0
- package/src/actions/restart.ts +101 -0
- package/src/actions/send-message.ts +168 -0
- package/src/actions/stream-control.ts +439 -0
- package/src/actions/switch-stream-source.ts +126 -0
- package/src/actions/terminal.ts +186 -0
- package/src/api/agent-admin-routes.ts +178 -0
- package/src/api/agent-lifecycle-routes.ts +129 -0
- package/src/api/agent-model.ts +143 -0
- package/src/api/agent-transfer-routes.ts +211 -0
- package/src/api/apps-routes.ts +210 -0
- package/src/api/auth-routes.ts +90 -0
- package/src/api/bsc-trade.ts +736 -0
- package/src/api/bug-report-routes.ts +161 -0
- package/src/api/character-routes.ts +421 -0
- package/src/api/cloud-billing-routes.ts +598 -0
- package/src/api/cloud-compat-routes.ts +192 -0
- package/src/api/cloud-routes.ts +529 -0
- package/src/api/cloud-status-routes.ts +234 -0
- package/src/api/compat-utils.ts +154 -0
- package/src/api/connector-health.ts +135 -0
- package/src/api/coordinator-wiring.ts +179 -0
- package/src/api/credit-detection.ts +47 -0
- package/src/api/database.ts +1357 -0
- package/src/api/diagnostics-routes.ts +389 -0
- package/src/api/drop-service.ts +205 -0
- package/src/api/early-logs.ts +111 -0
- package/src/api/http-helpers.ts +252 -0
- package/src/api/index.ts +85 -0
- package/src/api/knowledge-routes.ts +1189 -0
- package/src/api/knowledge-service-loader.ts +92 -0
- package/src/api/memory-bounds.ts +121 -0
- package/src/api/memory-routes.ts +349 -0
- package/src/api/merkle-tree.ts +239 -0
- package/src/api/models-routes.ts +72 -0
- package/src/api/nfa-routes.ts +169 -0
- package/src/api/nft-verify.ts +188 -0
- package/src/api/og-tracker.ts +72 -0
- package/src/api/parse-action-block.ts +145 -0
- package/src/api/permissions-routes.ts +222 -0
- package/src/api/plugin-validation.ts +355 -0
- package/src/api/provider-switch-config.ts +455 -0
- package/src/api/registry-routes.ts +165 -0
- package/src/api/registry-service.ts +292 -0
- package/src/api/route-helpers.ts +21 -0
- package/src/api/sandbox-routes.ts +1480 -0
- package/src/api/server.ts +17674 -0
- package/src/api/signal-routes.ts +265 -0
- package/src/api/stream-persistence.ts +297 -0
- package/src/api/stream-route-state.ts +48 -0
- package/src/api/stream-routes.ts +1046 -0
- package/src/api/stream-voice-routes.ts +208 -0
- package/src/api/streaming-text.ts +129 -0
- package/src/api/streaming-types.ts +23 -0
- package/src/api/subscription-routes.ts +283 -0
- package/src/api/terminal-run-limits.ts +31 -0
- package/src/api/training-backend-check.ts +40 -0
- package/src/api/training-routes.ts +314 -0
- package/src/api/training-service-like.ts +46 -0
- package/src/api/trajectory-routes.ts +714 -0
- package/src/api/trigger-routes.ts +438 -0
- package/src/api/twitter-verify.ts +226 -0
- package/src/api/tx-service.ts +193 -0
- package/src/api/wallet-dex-prices.ts +206 -0
- package/src/api/wallet-evm-balance.ts +989 -0
- package/src/api/wallet-routes.ts +505 -0
- package/src/api/wallet-rpc.ts +523 -0
- package/src/api/wallet-trading-profile.ts +694 -0
- package/src/api/wallet.ts +745 -0
- package/src/api/whatsapp-routes.ts +282 -0
- package/src/api/zip-utils.ts +130 -0
- package/src/auth/anthropic.ts +63 -0
- package/src/auth/apply-stealth.ts +38 -0
- package/src/auth/claude-code-stealth.ts +141 -0
- package/src/auth/credentials.ts +226 -0
- package/src/auth/index.ts +18 -0
- package/src/auth/openai-codex.ts +94 -0
- package/src/auth/types.ts +24 -0
- package/src/awareness/registry.ts +220 -0
- package/src/bin.ts +10 -0
- package/src/cli/index.ts +36 -0
- package/src/cli/parse-duration.ts +43 -0
- package/src/cloud/auth.test.ts +370 -0
- package/src/cloud/auth.ts +176 -0
- package/src/cloud/backup.test.ts +150 -0
- package/src/cloud/backup.ts +50 -0
- package/src/cloud/base-url.ts +45 -0
- package/src/cloud/bridge-client.test.ts +481 -0
- package/src/cloud/bridge-client.ts +307 -0
- package/src/cloud/cloud-manager.test.ts +223 -0
- package/src/cloud/cloud-manager.ts +151 -0
- package/src/cloud/cloud-proxy.test.ts +122 -0
- package/src/cloud/cloud-proxy.ts +52 -0
- package/src/cloud/index.ts +23 -0
- package/src/cloud/reconnect.test.ts +178 -0
- package/src/cloud/reconnect.ts +108 -0
- package/src/cloud/validate-url.test.ts +147 -0
- package/src/cloud/validate-url.ts +176 -0
- package/src/config/character-schema.ts +44 -0
- package/src/config/config.ts +149 -0
- package/src/config/env-vars.ts +86 -0
- package/src/config/includes.ts +196 -0
- package/src/config/index.ts +15 -0
- package/src/config/object-utils.ts +10 -0
- package/src/config/paths.ts +92 -0
- package/src/config/plugin-auto-enable.ts +520 -0
- package/src/config/schema.ts +1342 -0
- package/src/config/telegram-custom-commands.ts +99 -0
- package/src/config/types.agent-defaults.ts +342 -0
- package/src/config/types.agents.ts +112 -0
- package/src/config/types.gateway.ts +243 -0
- package/src/config/types.hooks.ts +124 -0
- package/src/config/types.messages.ts +201 -0
- package/src/config/types.milady.ts +791 -0
- package/src/config/types.tools.ts +416 -0
- package/src/config/types.ts +7 -0
- package/src/config/zod-schema.agent-runtime.ts +777 -0
- package/src/config/zod-schema.core.ts +778 -0
- package/src/config/zod-schema.hooks.ts +139 -0
- package/src/config/zod-schema.providers-core.ts +1126 -0
- package/src/config/zod-schema.session.ts +98 -0
- package/src/config/zod-schema.ts +865 -0
- package/src/contracts/apps.ts +46 -0
- package/src/contracts/awareness.ts +56 -0
- package/src/contracts/config.ts +172 -0
- package/src/contracts/drop.ts +21 -0
- package/src/contracts/index.ts +8 -0
- package/src/contracts/onboarding.ts +592 -0
- package/src/contracts/permissions.ts +52 -0
- package/src/contracts/verification.ts +9 -0
- package/src/contracts/wallet.ts +503 -0
- package/src/diagnostics/integration-observability.ts +132 -0
- package/src/emotes/catalog.ts +655 -0
- package/src/external-modules.d.ts +7 -0
- package/src/hooks/discovery.test.ts +357 -0
- package/src/hooks/discovery.ts +231 -0
- package/src/hooks/eligibility.ts +146 -0
- package/src/hooks/hooks.test.ts +320 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/loader.test.ts +418 -0
- package/src/hooks/loader.ts +256 -0
- package/src/hooks/registry.test.ts +168 -0
- package/src/hooks/registry.ts +74 -0
- package/src/hooks/types.ts +121 -0
- package/src/index.ts +19 -0
- package/src/onboarding-presets.ts +828 -0
- package/src/plugins/custom-rtmp/index.ts +40 -0
- package/src/providers/admin-trust.ts +76 -0
- package/src/providers/session-bridge.ts +143 -0
- package/src/providers/session-utils.ts +42 -0
- package/src/providers/simple-mode.ts +113 -0
- package/src/providers/ui-catalog.ts +135 -0
- package/src/providers/workspace-provider.ts +213 -0
- package/src/providers/workspace.ts +497 -0
- package/src/runtime/agent-event-service.ts +57 -0
- package/src/runtime/cloud-onboarding.test.ts +489 -0
- package/src/runtime/cloud-onboarding.ts +408 -0
- package/src/runtime/core-plugins.ts +53 -0
- package/src/runtime/custom-actions.ts +605 -0
- package/src/runtime/eliza.ts +4941 -0
- package/src/runtime/embedding-presets.ts +73 -0
- package/src/runtime/index.ts +8 -0
- package/src/runtime/milady-plugin.ts +180 -0
- package/src/runtime/onboarding-names.ts +76 -0
- package/src/runtime/release-plugin-policy.ts +119 -0
- package/src/runtime/restart.ts +59 -0
- package/src/runtime/trajectory-persistence.ts +2584 -0
- package/src/runtime/version.ts +6 -0
- package/src/security/audit-log.ts +222 -0
- package/src/security/network-policy.ts +91 -0
- package/src/server/index.ts +6 -0
- package/src/services/agent-export.ts +976 -0
- package/src/services/app-manager.ts +755 -0
- package/src/services/browser-capture.ts +215 -0
- package/src/services/coding-agent-context.ts +355 -0
- package/src/services/fallback-training-service.ts +196 -0
- package/src/services/index.ts +17 -0
- package/src/services/mcp-marketplace.ts +327 -0
- package/src/services/plugin-manager-types.ts +185 -0
- package/src/services/privy-wallets.ts +352 -0
- package/src/services/registry-client-app-meta.ts +201 -0
- package/src/services/registry-client-endpoints.ts +253 -0
- package/src/services/registry-client-local.ts +485 -0
- package/src/services/registry-client-network.ts +173 -0
- package/src/services/registry-client-queries.ts +176 -0
- package/src/services/registry-client-types.ts +104 -0
- package/src/services/registry-client.ts +366 -0
- package/src/services/remote-signing-service.ts +261 -0
- package/src/services/sandbox-engine.ts +753 -0
- package/src/services/sandbox-manager.ts +503 -0
- package/src/services/self-updater.ts +213 -0
- package/src/services/signal-pairing.ts +189 -0
- package/src/services/signing-policy.ts +230 -0
- package/src/services/skill-catalog-client.ts +195 -0
- package/src/services/skill-marketplace.ts +909 -0
- package/src/services/stream-manager.ts +707 -0
- package/src/services/tts-stream-bridge.ts +465 -0
- package/src/services/update-checker.ts +163 -0
- package/src/services/version-compat.ts +367 -0
- package/src/services/whatsapp-pairing.ts +279 -0
- package/src/shared/ui-catalog-prompt.ts +1158 -0
- package/src/test-support/process-helpers.ts +35 -0
- package/src/test-support/route-test-helpers.ts +113 -0
- package/src/test-support/test-helpers.ts +304 -0
- package/src/testing/index.ts +3 -0
- package/src/triggers/action.ts +342 -0
- package/src/triggers/runtime.ts +432 -0
- package/src/triggers/scheduling.ts +472 -0
- package/src/triggers/types.ts +133 -0
- package/src/types/app-hyperscape-routes-shim.d.ts +29 -0
- package/src/types/external-modules.d.ts +7 -0
- package/src/utils/exec-safety.ts +23 -0
- package/src/utils/number-parsing.ts +112 -0
- package/src/utils/spoken-text.ts +65 -0
- package/src/version-resolver.ts +60 -0
- package/test/api/agent-admin-routes.test.ts +160 -0
- package/test/api/agent-lifecycle-routes.test.ts +164 -0
- package/test/api/agent-transfer-routes.test.ts +136 -0
- package/test/api/apps-routes.test.ts +140 -0
- package/test/api/auth-routes.test.ts +160 -0
- package/test/api/bug-report-routes.test.ts +88 -0
- package/test/api/knowledge-routes.test.ts +73 -0
- package/test/api/lifecycle.test.ts +342 -0
- package/test/api/memory-routes.test.ts +74 -0
- package/test/api/models-routes.test.ts +112 -0
- package/test/api/nfa-routes.test.ts +78 -0
- package/test/api/permissions-routes.test.ts +185 -0
- package/test/api/registry-routes.test.ts +157 -0
- package/test/api/signal-routes.test.ts +113 -0
- package/test/api/subscription-routes.test.ts +90 -0
- package/test/api/trigger-routes.test.ts +87 -0
- package/test/api/wallet-routes.observability.test.ts +191 -0
- package/test/api/wallet-routes.test.ts +502 -0
- package/test/diagnostics/integration-observability.test.ts +135 -0
- package/test/security/audit-log.test.ts +229 -0
- package/test/security/network-policy.test.ts +143 -0
- package/test/services/version-compat.test.ts +127 -0
- package/tsconfig.build.json +21 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin ↔ Core version compatibility validation.
|
|
3
|
+
*
|
|
4
|
+
* Detects version skew between @elizaos/core and plugins that depend on
|
|
5
|
+
* specific core exports. This catches the class of bug where plugins on npm
|
|
6
|
+
* advance past the core version, importing symbols that don't exist yet in
|
|
7
|
+
* the installed core — causing silent import failures that take down every
|
|
8
|
+
* model provider.
|
|
9
|
+
*
|
|
10
|
+
* @see https://github.com/milady-ai/milady/issues/10
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Types
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
/** Result of a single plugin compatibility check. */
|
|
18
|
+
export interface PluginCompatResult {
|
|
19
|
+
/** Plugin package name. */
|
|
20
|
+
plugin: string;
|
|
21
|
+
/** Whether the plugin is compatible with the installed core. */
|
|
22
|
+
compatible: boolean;
|
|
23
|
+
/** The installed plugin version, or null if unresolvable. */
|
|
24
|
+
pluginVersion: string | null;
|
|
25
|
+
/** The installed core version. */
|
|
26
|
+
coreVersion: string;
|
|
27
|
+
/** List of symbols the plugin needs that are missing from core. */
|
|
28
|
+
missingExports: string[];
|
|
29
|
+
/** Human-readable explanation when incompatible. */
|
|
30
|
+
message: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Aggregate result of validating all critical plugins. */
|
|
34
|
+
export interface VersionCompatReport {
|
|
35
|
+
/** Whether all checked plugins are compatible. */
|
|
36
|
+
compatible: boolean;
|
|
37
|
+
/** Per-plugin results. */
|
|
38
|
+
results: PluginCompatResult[];
|
|
39
|
+
/** Plugins that failed the check. */
|
|
40
|
+
failures: PluginCompatResult[];
|
|
41
|
+
/** Advisory message (e.g. "pin to alpha.3" or "upgrade core"). */
|
|
42
|
+
advisory: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Known critical exports per version
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Exports introduced in specific core versions.
|
|
51
|
+
*
|
|
52
|
+
* When a plugin imports a symbol from @elizaos/core, we can check whether
|
|
53
|
+
* the installed core version actually contains that export. This map records
|
|
54
|
+
* which version first introduced each critical export.
|
|
55
|
+
*
|
|
56
|
+
* Key: export name. Value: minimum core version that includes it.
|
|
57
|
+
*/
|
|
58
|
+
const CORE_EXPORT_INTRODUCED_IN: Readonly<Record<string, string>> = {
|
|
59
|
+
MAX_EMBEDDING_TOKENS: "2.0.0-alpha.4",
|
|
60
|
+
MAX_EMBEDDING_CHARS: "2.0.0-alpha.4",
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Map of plugin → list of core exports the plugin requires.
|
|
65
|
+
*
|
|
66
|
+
* These are the imports that caused issue #10. If more cross-version
|
|
67
|
+
* import issues are discovered, add them here.
|
|
68
|
+
*/
|
|
69
|
+
const PLUGIN_REQUIRED_CORE_EXPORTS: Readonly<
|
|
70
|
+
Record<string, readonly string[]>
|
|
71
|
+
> = {
|
|
72
|
+
"@elizaos/plugin-openrouter": ["MAX_EMBEDDING_TOKENS"],
|
|
73
|
+
"@elizaos/plugin-openai": ["MAX_EMBEDDING_TOKENS"],
|
|
74
|
+
"@elizaos/plugin-ollama": ["MAX_EMBEDDING_TOKENS"],
|
|
75
|
+
"@elizaos/plugin-google-genai": ["MAX_EMBEDDING_TOKENS"],
|
|
76
|
+
"@elizaos/plugin-knowledge": ["MAX_EMBEDDING_TOKENS"],
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Plugins that provide AI model capabilities. If ALL of these fail to load
|
|
81
|
+
* the agent is completely non-functional — no responses can be generated.
|
|
82
|
+
*/
|
|
83
|
+
export const AI_PROVIDER_PLUGINS: readonly string[] = [
|
|
84
|
+
"@elizaos/plugin-anthropic",
|
|
85
|
+
"@elizaos/plugin-openai",
|
|
86
|
+
"@elizaos/plugin-openrouter",
|
|
87
|
+
"@elizaos/plugin-ollama",
|
|
88
|
+
"@elizaos/plugin-google-genai",
|
|
89
|
+
"@elizaos/plugin-groq",
|
|
90
|
+
"@elizaos/plugin-xai",
|
|
91
|
+
"@homunculuslabs/plugin-zai",
|
|
92
|
+
"@elizaos/plugin-pi-ai",
|
|
93
|
+
"@elizaos/plugin-elizacloud",
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Semver comparison (simplified for alpha tags)
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Parse a semver string (including pre-release tags) into a comparable tuple.
|
|
102
|
+
* Returns null for unparseable versions.
|
|
103
|
+
*
|
|
104
|
+
* Examples:
|
|
105
|
+
* "2.0.0-alpha.3" → [2, 0, 0, 3]
|
|
106
|
+
* "2.0.0-alpha.4" → [2, 0, 0, 4]
|
|
107
|
+
* "2.0.0-nightly.20260208" → [2, 0, 0, 20260208]
|
|
108
|
+
* "2.0.0" → [2, 0, 0, Infinity] (release beats any pre-release)
|
|
109
|
+
*
|
|
110
|
+
* Note: comparisons are only meaningful within the same pre-release tag type
|
|
111
|
+
* (alpha vs alpha, nightly vs nightly). Cross-tag comparisons (alpha.7 vs beta.1)
|
|
112
|
+
* compare only the numeric suffix, which may not reflect the intended ordering.
|
|
113
|
+
* The update checker always compares within the same channel, so this is safe.
|
|
114
|
+
*/
|
|
115
|
+
export function parseSemver(
|
|
116
|
+
version: string,
|
|
117
|
+
): [number, number, number, number] | null {
|
|
118
|
+
const match = version.match(
|
|
119
|
+
/^(\d+)\.(\d+)\.(\d+)(?:-(?:alpha|beta|rc|nightly)\.(\d+))?$/,
|
|
120
|
+
);
|
|
121
|
+
if (!match) return null;
|
|
122
|
+
|
|
123
|
+
const major = Number(match[1]);
|
|
124
|
+
const minor = Number(match[2]);
|
|
125
|
+
const patch = Number(match[3]);
|
|
126
|
+
// A release without a pre-release tag sorts after any pre-release.
|
|
127
|
+
const pre =
|
|
128
|
+
match[4] !== undefined ? Number(match[4]) : Number.POSITIVE_INFINITY;
|
|
129
|
+
|
|
130
|
+
return [major, minor, patch, pre];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Compare two semver strings. Returns:
|
|
135
|
+
* -1 if a < b
|
|
136
|
+
* 0 if a === b
|
|
137
|
+
* 1 if a > b
|
|
138
|
+
* null if either version is unparseable.
|
|
139
|
+
*/
|
|
140
|
+
export function compareSemver(a: string, b: string): -1 | 0 | 1 | null {
|
|
141
|
+
const pa = parseSemver(a);
|
|
142
|
+
const pb = parseSemver(b);
|
|
143
|
+
if (!pa || !pb) return null;
|
|
144
|
+
|
|
145
|
+
for (let i = 0; i < 4; i++) {
|
|
146
|
+
if (pa[i] < pb[i]) return -1;
|
|
147
|
+
if (pa[i] > pb[i]) return 1;
|
|
148
|
+
}
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Check if `installed` version satisfies `>= required`.
|
|
154
|
+
*/
|
|
155
|
+
export function versionSatisfies(installed: string, required: string): boolean {
|
|
156
|
+
const cmp = compareSemver(installed, required);
|
|
157
|
+
return cmp !== null && cmp >= 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Core export probing
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Check whether a specific named export exists in `@elizaos/core`.
|
|
166
|
+
*
|
|
167
|
+
* This does a live import check — it tests the *actual* installed module,
|
|
168
|
+
* not a version number lookup.
|
|
169
|
+
*/
|
|
170
|
+
export async function coreExportExists(exportName: string): Promise<boolean> {
|
|
171
|
+
try {
|
|
172
|
+
const core = (await import("@elizaos/core")) as Record<string, unknown>;
|
|
173
|
+
return exportName in core && core[exportName] !== undefined;
|
|
174
|
+
} catch (err) {
|
|
175
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
176
|
+
if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Read the installed version of a package from its package.json.
|
|
185
|
+
* Returns null if the package is not installed or the version is unreadable.
|
|
186
|
+
*/
|
|
187
|
+
export async function getInstalledVersion(
|
|
188
|
+
packageName: string,
|
|
189
|
+
): Promise<string | null> {
|
|
190
|
+
try {
|
|
191
|
+
// Dynamic import of package.json is not universally supported, so we
|
|
192
|
+
// use createRequire as a robust fallback for reading metadata.
|
|
193
|
+
const { createRequire } = await import("node:module");
|
|
194
|
+
const require = createRequire(import.meta.url);
|
|
195
|
+
const pkgPath = require.resolve(`${packageName}/package.json`);
|
|
196
|
+
const { readFileSync } = await import("node:fs");
|
|
197
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as {
|
|
198
|
+
version: string;
|
|
199
|
+
};
|
|
200
|
+
return pkg.version ?? null;
|
|
201
|
+
} catch (err) {
|
|
202
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
203
|
+
if (
|
|
204
|
+
code === "MODULE_NOT_FOUND" ||
|
|
205
|
+
code === "ERR_MODULE_NOT_FOUND" ||
|
|
206
|
+
code === "ENOENT"
|
|
207
|
+
) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
throw err;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
// Validation
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Validate a single plugin's compatibility with the installed core.
|
|
220
|
+
*/
|
|
221
|
+
export async function validatePluginCompat(
|
|
222
|
+
pluginName: string,
|
|
223
|
+
coreVersion: string,
|
|
224
|
+
): Promise<PluginCompatResult> {
|
|
225
|
+
const requiredExports = PLUGIN_REQUIRED_CORE_EXPORTS[pluginName];
|
|
226
|
+
|
|
227
|
+
// If we don't track this plugin's requirements, assume compatible.
|
|
228
|
+
if (!requiredExports || requiredExports.length === 0) {
|
|
229
|
+
return {
|
|
230
|
+
plugin: pluginName,
|
|
231
|
+
compatible: true,
|
|
232
|
+
pluginVersion: await getInstalledVersion(pluginName),
|
|
233
|
+
coreVersion,
|
|
234
|
+
missingExports: [],
|
|
235
|
+
message: "",
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const missingExports: string[] = [];
|
|
240
|
+
|
|
241
|
+
for (const exportName of requiredExports) {
|
|
242
|
+
const minVersion = CORE_EXPORT_INTRODUCED_IN[exportName];
|
|
243
|
+
|
|
244
|
+
if (minVersion) {
|
|
245
|
+
// Fast path: compare versions without importing.
|
|
246
|
+
if (!versionSatisfies(coreVersion, minVersion)) {
|
|
247
|
+
missingExports.push(exportName);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Slow path: probe the actual module to be sure.
|
|
253
|
+
const exists = await coreExportExists(exportName);
|
|
254
|
+
if (!exists) {
|
|
255
|
+
missingExports.push(exportName);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const pluginVersion = await getInstalledVersion(pluginName);
|
|
260
|
+
const compatible = missingExports.length === 0;
|
|
261
|
+
|
|
262
|
+
let message = "";
|
|
263
|
+
if (!compatible) {
|
|
264
|
+
message =
|
|
265
|
+
`${pluginName}${pluginVersion ? `@${pluginVersion}` : ""} requires ` +
|
|
266
|
+
`${missingExports.join(", ")} from @elizaos/core, but the installed ` +
|
|
267
|
+
`core@${coreVersion} does not export ${missingExports.length === 1 ? "it" : "them"}. ` +
|
|
268
|
+
`Pin this plugin to a version compatible with core@${coreVersion}, or upgrade core.`;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
plugin: pluginName,
|
|
273
|
+
compatible,
|
|
274
|
+
pluginVersion,
|
|
275
|
+
coreVersion,
|
|
276
|
+
missingExports,
|
|
277
|
+
message,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Validate all known critical plugins against the installed core.
|
|
283
|
+
*
|
|
284
|
+
* Returns a report with per-plugin results and an overall advisory.
|
|
285
|
+
*/
|
|
286
|
+
export async function validateVersionCompat(): Promise<VersionCompatReport> {
|
|
287
|
+
const coreVersion = (await getInstalledVersion("@elizaos/core")) ?? "unknown";
|
|
288
|
+
const pluginNames = Object.keys(PLUGIN_REQUIRED_CORE_EXPORTS);
|
|
289
|
+
|
|
290
|
+
const results = await Promise.all(
|
|
291
|
+
pluginNames.map((name) => validatePluginCompat(name, coreVersion)),
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
const failures = results.filter((r) => !r.compatible);
|
|
295
|
+
const compatible = failures.length === 0;
|
|
296
|
+
|
|
297
|
+
let advisory = "";
|
|
298
|
+
if (!compatible) {
|
|
299
|
+
const affectedNames = failures.map((f) => f.plugin).join(", ");
|
|
300
|
+
const allMissing = [...new Set(failures.flatMap((f) => f.missingExports))];
|
|
301
|
+
advisory =
|
|
302
|
+
`Version skew detected: @elizaos/core@${coreVersion} is missing ` +
|
|
303
|
+
`${allMissing.join(", ")} required by ${affectedNames}. ` +
|
|
304
|
+
`Fix: pin affected plugins to versions compatible with core@${coreVersion}, ` +
|
|
305
|
+
`or upgrade @elizaos/core to a version that exports these symbols.`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return { compatible, results, failures, advisory };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* After plugin resolution, check whether at least one AI provider plugin
|
|
313
|
+
* loaded successfully. If none loaded, return a diagnostic message explaining
|
|
314
|
+
* whether this is a version-skew issue or a configuration issue.
|
|
315
|
+
*
|
|
316
|
+
* @param loadedPluginNames - Names of plugins that loaded successfully.
|
|
317
|
+
* @param failedPlugins - Names + error strings of plugins that failed to load.
|
|
318
|
+
*/
|
|
319
|
+
export function diagnoseNoAIProvider(
|
|
320
|
+
loadedPluginNames: string[],
|
|
321
|
+
failedPlugins: Array<{ name: string; error: string }>,
|
|
322
|
+
): string | null {
|
|
323
|
+
const loadedProviders = loadedPluginNames.filter((n) =>
|
|
324
|
+
AI_PROVIDER_PLUGINS.includes(n),
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
// At least one AI provider loaded — no issue.
|
|
328
|
+
if (loadedProviders.length > 0) return null;
|
|
329
|
+
|
|
330
|
+
// Check if any AI provider plugins were attempted but failed.
|
|
331
|
+
const failedProviders = failedPlugins.filter((f) =>
|
|
332
|
+
AI_PROVIDER_PLUGINS.includes(f.name),
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
if (failedProviders.length === 0) {
|
|
336
|
+
return (
|
|
337
|
+
"No AI provider plugin was loaded. Set an API key environment variable " +
|
|
338
|
+
"(e.g. ANTHROPIC_API_KEY, OPENAI_API_KEY, OPENROUTER_API_KEY) or log in " +
|
|
339
|
+
"to Eliza Cloud (ELIZAOS_CLOUD_API_KEY) to enable at least one model provider."
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Check for the specific version-skew signature.
|
|
344
|
+
const versionSkewPlugins = failedProviders.filter(
|
|
345
|
+
(f) =>
|
|
346
|
+
f.error.includes("not found in module") ||
|
|
347
|
+
f.error.includes("Export named") ||
|
|
348
|
+
f.error.includes("does not provide an export named"),
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
if (versionSkewPlugins.length > 0) {
|
|
352
|
+
const names = versionSkewPlugins.map((f) => f.name).join(", ");
|
|
353
|
+
return (
|
|
354
|
+
`Version skew detected: ${names} failed to import required symbols from ` +
|
|
355
|
+
`@elizaos/core. This usually means the plugin version is ahead of the ` +
|
|
356
|
+
`installed core version. Pin the affected plugins to a version compatible ` +
|
|
357
|
+
`with your installed @elizaos/core, or upgrade core. ` +
|
|
358
|
+
`See: https://github.com/milady-ai/milady/issues/10`
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Generic failure.
|
|
363
|
+
const details = failedProviders
|
|
364
|
+
.map((f) => ` ${f.name}: ${f.error}`)
|
|
365
|
+
.join("\n");
|
|
366
|
+
return `All AI provider plugins failed to load:\n${details}`;
|
|
367
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WhatsApp pairing service — manages Baileys sessions for QR code authentication.
|
|
3
|
+
*
|
|
4
|
+
* This service is separate from `@elizaos/plugin-whatsapp` because the plugin
|
|
5
|
+
* initializes during runtime startup (too late for interactive QR flow).
|
|
6
|
+
* Once pairing succeeds, the auth state is persisted to disk so the plugin
|
|
7
|
+
* can reconnect automatically on subsequent startups.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
|
|
13
|
+
const LOG_PREFIX = "[whatsapp-pairing]";
|
|
14
|
+
|
|
15
|
+
/** Validate accountId to prevent path traversal. Only allows alphanumeric, dash, underscore. */
|
|
16
|
+
export function sanitizeAccountId(raw: string): string {
|
|
17
|
+
const cleaned = raw.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
18
|
+
if (!cleaned || cleaned !== raw) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Invalid accountId: must only contain alphanumeric characters, dashes, and underscores`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return cleaned;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type WhatsAppPairingStatus =
|
|
27
|
+
| "idle"
|
|
28
|
+
| "initializing"
|
|
29
|
+
| "waiting_for_qr"
|
|
30
|
+
| "connected"
|
|
31
|
+
| "disconnected"
|
|
32
|
+
| "timeout"
|
|
33
|
+
| "error";
|
|
34
|
+
|
|
35
|
+
export interface WhatsAppPairingEvent {
|
|
36
|
+
type: "whatsapp-qr" | "whatsapp-status";
|
|
37
|
+
accountId: string;
|
|
38
|
+
qrDataUrl?: string;
|
|
39
|
+
expiresInMs?: number;
|
|
40
|
+
status?: WhatsAppPairingStatus;
|
|
41
|
+
phoneNumber?: string;
|
|
42
|
+
error?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface WhatsAppPairingOptions {
|
|
46
|
+
authDir: string;
|
|
47
|
+
accountId: string;
|
|
48
|
+
onEvent: (event: WhatsAppPairingEvent) => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class WhatsAppPairingSession {
|
|
52
|
+
private socket: ReturnType<
|
|
53
|
+
typeof import("@whiskeysockets/baileys").default
|
|
54
|
+
> | null = null;
|
|
55
|
+
private status: WhatsAppPairingStatus = "idle";
|
|
56
|
+
private options: WhatsAppPairingOptions;
|
|
57
|
+
private qrAttempts = 0;
|
|
58
|
+
private readonly MAX_QR_ATTEMPTS = 5;
|
|
59
|
+
private restartTimer: ReturnType<typeof setTimeout> | null = null;
|
|
60
|
+
|
|
61
|
+
constructor(options: WhatsAppPairingOptions) {
|
|
62
|
+
this.options = options;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async start(): Promise<void> {
|
|
66
|
+
this.setStatus("initializing");
|
|
67
|
+
|
|
68
|
+
const baileys = await import("@whiskeysockets/baileys");
|
|
69
|
+
const makeWASocket = baileys.default;
|
|
70
|
+
const { useMultiFileAuthState, fetchLatestBaileysVersion, DisconnectReason } =
|
|
71
|
+
baileys;
|
|
72
|
+
const QRCode = (await import("qrcode")).default;
|
|
73
|
+
const { Boom } = await import("@hapi/boom");
|
|
74
|
+
|
|
75
|
+
fs.mkdirSync(this.options.authDir, { recursive: true });
|
|
76
|
+
|
|
77
|
+
const { state, saveCreds } = await useMultiFileAuthState(
|
|
78
|
+
this.options.authDir,
|
|
79
|
+
);
|
|
80
|
+
const { version } = await fetchLatestBaileysVersion();
|
|
81
|
+
|
|
82
|
+
const pino = (await import("pino")).default;
|
|
83
|
+
const baileysLogger = pino({ level: "silent" });
|
|
84
|
+
|
|
85
|
+
this.socket = makeWASocket({
|
|
86
|
+
version,
|
|
87
|
+
auth: state,
|
|
88
|
+
logger: baileysLogger,
|
|
89
|
+
printQRInTerminal: false,
|
|
90
|
+
browser: ["Milady AI", "Desktop", "1.0.0"],
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
this.socket.ev.on("creds.update", saveCreds);
|
|
94
|
+
|
|
95
|
+
this.socket.ev.on("connection.update", async (update) => {
|
|
96
|
+
const { connection, lastDisconnect, qr } = update;
|
|
97
|
+
|
|
98
|
+
if (qr) {
|
|
99
|
+
this.qrAttempts++;
|
|
100
|
+
console.info(
|
|
101
|
+
`${LOG_PREFIX} QR code received (attempt ${this.qrAttempts}/${this.MAX_QR_ATTEMPTS})`,
|
|
102
|
+
);
|
|
103
|
+
if (this.qrAttempts > this.MAX_QR_ATTEMPTS) {
|
|
104
|
+
this.setStatus("timeout");
|
|
105
|
+
this.stop();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const qrDataUrl = await QRCode.toDataURL(qr, {
|
|
111
|
+
width: 256,
|
|
112
|
+
margin: 2,
|
|
113
|
+
color: { dark: "#000000", light: "#ffffff" },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
this.setStatus("waiting_for_qr");
|
|
117
|
+
this.options.onEvent({
|
|
118
|
+
type: "whatsapp-qr",
|
|
119
|
+
accountId: this.options.accountId,
|
|
120
|
+
qrDataUrl,
|
|
121
|
+
expiresInMs: 20_000,
|
|
122
|
+
});
|
|
123
|
+
} catch {
|
|
124
|
+
// QR generation failure — non-fatal, next QR attempt will retry.
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (connection === "close") {
|
|
129
|
+
const statusCode = (lastDisconnect?.error as InstanceType<typeof Boom>)
|
|
130
|
+
?.output?.statusCode;
|
|
131
|
+
console.info(
|
|
132
|
+
`${LOG_PREFIX} Connection closed, statusCode=${statusCode}, status=${this.status}`,
|
|
133
|
+
);
|
|
134
|
+
if (statusCode === DisconnectReason.loggedOut) {
|
|
135
|
+
this.setStatus("disconnected");
|
|
136
|
+
} else if (
|
|
137
|
+
statusCode === DisconnectReason.restartRequired ||
|
|
138
|
+
statusCode === DisconnectReason.timedOut ||
|
|
139
|
+
statusCode === DisconnectReason.connectionClosed ||
|
|
140
|
+
statusCode === DisconnectReason.connectionReplaced
|
|
141
|
+
) {
|
|
142
|
+
console.info(
|
|
143
|
+
`${LOG_PREFIX} Restarting pairing after transient close...`,
|
|
144
|
+
);
|
|
145
|
+
this.socket = null;
|
|
146
|
+
this.qrAttempts = 0;
|
|
147
|
+
this.restartTimer = setTimeout(() => {
|
|
148
|
+
this.restartTimer = null;
|
|
149
|
+
this.start().catch((err) => {
|
|
150
|
+
console.error(`${LOG_PREFIX} Restart failed:`, err);
|
|
151
|
+
this.setStatus("error");
|
|
152
|
+
this.options.onEvent({
|
|
153
|
+
type: "whatsapp-status",
|
|
154
|
+
accountId: this.options.accountId,
|
|
155
|
+
status: "error",
|
|
156
|
+
error: err instanceof Error ? err.message : String(err),
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}, 3000);
|
|
160
|
+
}
|
|
161
|
+
} else if (connection === "open") {
|
|
162
|
+
const phoneNumber = this.socket?.user?.id?.split(":")[0] ?? "";
|
|
163
|
+
this.setStatus("connected");
|
|
164
|
+
this.options.onEvent({
|
|
165
|
+
type: "whatsapp-status",
|
|
166
|
+
accountId: this.options.accountId,
|
|
167
|
+
status: "connected",
|
|
168
|
+
phoneNumber,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
stop(): void {
|
|
175
|
+
if (this.restartTimer) {
|
|
176
|
+
clearTimeout(this.restartTimer);
|
|
177
|
+
this.restartTimer = null;
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
this.socket?.end(undefined);
|
|
181
|
+
} catch {
|
|
182
|
+
// Ignore cleanup errors.
|
|
183
|
+
}
|
|
184
|
+
this.socket = null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
getStatus(): WhatsAppPairingStatus {
|
|
188
|
+
return this.status;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private setStatus(status: WhatsAppPairingStatus): void {
|
|
192
|
+
this.status = status;
|
|
193
|
+
this.options.onEvent({
|
|
194
|
+
type: "whatsapp-status",
|
|
195
|
+
accountId: this.options.accountId,
|
|
196
|
+
status,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function whatsappAuthExists(
|
|
202
|
+
workspaceDir: string,
|
|
203
|
+
accountId = "default",
|
|
204
|
+
): boolean {
|
|
205
|
+
const credsPath = path.join(
|
|
206
|
+
workspaceDir,
|
|
207
|
+
"whatsapp-auth",
|
|
208
|
+
accountId,
|
|
209
|
+
"creds.json",
|
|
210
|
+
);
|
|
211
|
+
return fs.existsSync(credsPath);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export async function whatsappLogout(
|
|
215
|
+
workspaceDir: string,
|
|
216
|
+
accountId = "default",
|
|
217
|
+
): Promise<void> {
|
|
218
|
+
const authDir = path.join(workspaceDir, "whatsapp-auth", accountId);
|
|
219
|
+
const credsPath = path.join(authDir, "creds.json");
|
|
220
|
+
|
|
221
|
+
if (fs.existsSync(credsPath)) {
|
|
222
|
+
try {
|
|
223
|
+
const baileys = await import("@whiskeysockets/baileys");
|
|
224
|
+
const makeWASocket = baileys.default;
|
|
225
|
+
const { useMultiFileAuthState, fetchLatestBaileysVersion } = baileys;
|
|
226
|
+
const pino = (await import("pino")).default;
|
|
227
|
+
const logger = pino({ level: "silent" });
|
|
228
|
+
|
|
229
|
+
const { state } = await useMultiFileAuthState(authDir);
|
|
230
|
+
const { version } = await fetchLatestBaileysVersion();
|
|
231
|
+
|
|
232
|
+
const sock = makeWASocket({
|
|
233
|
+
version,
|
|
234
|
+
auth: state,
|
|
235
|
+
logger,
|
|
236
|
+
printQRInTerminal: false,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
await new Promise<void>((resolve) => {
|
|
240
|
+
let settled = false;
|
|
241
|
+
const finish = () => {
|
|
242
|
+
if (settled) return;
|
|
243
|
+
settled = true;
|
|
244
|
+
clearTimeout(timeout);
|
|
245
|
+
try {
|
|
246
|
+
sock.ev.removeAllListeners("connection.update");
|
|
247
|
+
} catch {
|
|
248
|
+
/* */
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
sock.end(undefined);
|
|
252
|
+
} catch {
|
|
253
|
+
/* */
|
|
254
|
+
}
|
|
255
|
+
resolve();
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const timeout = setTimeout(finish, 10_000);
|
|
259
|
+
|
|
260
|
+
sock.ev.on("connection.update", async (update) => {
|
|
261
|
+
if (update.connection === "open") {
|
|
262
|
+
try {
|
|
263
|
+
await sock.logout();
|
|
264
|
+
} catch {
|
|
265
|
+
// May fail if already logged out remotely.
|
|
266
|
+
}
|
|
267
|
+
finish();
|
|
268
|
+
} else if (update.connection === "close") {
|
|
269
|
+
finish();
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
} catch {
|
|
274
|
+
// If Baileys can't connect, just delete files anyway.
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
fs.rmSync(authDir, { recursive: true, force: true });
|
|
279
|
+
}
|