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,109 @@
|
|
|
1
|
+
//#region src/api/permissions-routes.ts
|
|
2
|
+
async function handlePermissionRoutes(ctx) {
|
|
3
|
+
const { req, res, method, pathname, state, readJsonBody, json, error, saveConfig, scheduleRuntimeRestart } = ctx;
|
|
4
|
+
if (!pathname.startsWith("/api/permissions")) return false;
|
|
5
|
+
if (method === "GET" && pathname === "/api/permissions") {
|
|
6
|
+
json(res, {
|
|
7
|
+
permissions: state.permissionStates ?? {},
|
|
8
|
+
platform: process.platform,
|
|
9
|
+
shellEnabled: state.shellEnabled ?? true
|
|
10
|
+
});
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
if (method === "GET" && pathname === "/api/permissions/shell") {
|
|
14
|
+
const enabled = state.shellEnabled ?? true;
|
|
15
|
+
if (!state.permissionStates) state.permissionStates = {};
|
|
16
|
+
const shellState = state.permissionStates.shell;
|
|
17
|
+
const permission = {
|
|
18
|
+
id: "shell",
|
|
19
|
+
status: enabled ? "granted" : "denied",
|
|
20
|
+
lastChecked: shellState?.lastChecked ?? Date.now(),
|
|
21
|
+
canRequest: false
|
|
22
|
+
};
|
|
23
|
+
state.permissionStates.shell = permission;
|
|
24
|
+
json(res, {
|
|
25
|
+
enabled,
|
|
26
|
+
...permission,
|
|
27
|
+
permission
|
|
28
|
+
});
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
if (method === "GET" && pathname.startsWith("/api/permissions/")) {
|
|
32
|
+
const permId = pathname.slice(17);
|
|
33
|
+
if (!permId || permId.includes("/")) {
|
|
34
|
+
error(res, "Invalid permission ID", 400);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
const permState = (state.permissionStates ?? {})[permId];
|
|
38
|
+
if (!permState) {
|
|
39
|
+
json(res, {
|
|
40
|
+
id: permId,
|
|
41
|
+
status: "not-applicable",
|
|
42
|
+
lastChecked: Date.now(),
|
|
43
|
+
canRequest: false
|
|
44
|
+
});
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
json(res, permState);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
if (method === "POST" && pathname === "/api/permissions/refresh") {
|
|
51
|
+
json(res, {
|
|
52
|
+
message: "Permission refresh requested",
|
|
53
|
+
action: "ipc:permissions:refresh"
|
|
54
|
+
});
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (method === "POST" && pathname.match(/^\/api\/permissions\/[^/]+\/request$/)) {
|
|
58
|
+
const permId = pathname.split("/")[3];
|
|
59
|
+
json(res, {
|
|
60
|
+
message: `Permission request for ${permId}`,
|
|
61
|
+
action: `ipc:permissions:request:${permId}`
|
|
62
|
+
});
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (method === "POST" && pathname.match(/^\/api\/permissions\/[^/]+\/open-settings$/)) {
|
|
66
|
+
const permId = pathname.split("/")[3];
|
|
67
|
+
json(res, {
|
|
68
|
+
message: `Opening settings for ${permId}`,
|
|
69
|
+
action: `ipc:permissions:openSettings:${permId}`
|
|
70
|
+
});
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (method === "PUT" && pathname === "/api/permissions/shell") {
|
|
74
|
+
const body = await readJsonBody(req, res);
|
|
75
|
+
if (!body) return true;
|
|
76
|
+
const enabled = body.enabled === true;
|
|
77
|
+
state.shellEnabled = enabled;
|
|
78
|
+
if (!state.permissionStates) state.permissionStates = {};
|
|
79
|
+
state.permissionStates.shell = {
|
|
80
|
+
id: "shell",
|
|
81
|
+
status: enabled ? "granted" : "denied",
|
|
82
|
+
lastChecked: Date.now(),
|
|
83
|
+
canRequest: false
|
|
84
|
+
};
|
|
85
|
+
if (!state.config.features) state.config.features = {};
|
|
86
|
+
state.config.features.shellEnabled = enabled;
|
|
87
|
+
saveConfig(state.config);
|
|
88
|
+
if (state.runtime) scheduleRuntimeRestart(`Shell access ${enabled ? "enabled" : "disabled"}`);
|
|
89
|
+
json(res, {
|
|
90
|
+
shellEnabled: enabled,
|
|
91
|
+
permission: state.permissionStates.shell
|
|
92
|
+
});
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
if (method === "PUT" && pathname === "/api/permissions/state") {
|
|
96
|
+
const body = await readJsonBody(req, res);
|
|
97
|
+
if (!body) return true;
|
|
98
|
+
if (body.permissions && typeof body.permissions === "object") state.permissionStates = body.permissions;
|
|
99
|
+
json(res, {
|
|
100
|
+
updated: true,
|
|
101
|
+
permissions: state.permissionStates
|
|
102
|
+
});
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//#endregion
|
|
109
|
+
export { handlePermissionRoutes };
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
//#region src/api/plugin-validation.ts
|
|
2
|
+
const KEY_PREFIX_HINTS = {
|
|
3
|
+
ANTHROPIC_API_KEY: {
|
|
4
|
+
prefix: "sk-ant-",
|
|
5
|
+
label: "Anthropic"
|
|
6
|
+
},
|
|
7
|
+
OPENAI_API_KEY: {
|
|
8
|
+
prefix: "sk-",
|
|
9
|
+
label: "OpenAI"
|
|
10
|
+
},
|
|
11
|
+
GROQ_API_KEY: {
|
|
12
|
+
prefix: "gsk_",
|
|
13
|
+
label: "Groq"
|
|
14
|
+
},
|
|
15
|
+
XAI_API_KEY: {
|
|
16
|
+
prefix: "xai-",
|
|
17
|
+
label: "xAI"
|
|
18
|
+
},
|
|
19
|
+
OPENROUTER_API_KEY: {
|
|
20
|
+
prefix: "sk-or-",
|
|
21
|
+
label: "OpenRouter"
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Validate a plugin's configuration.
|
|
26
|
+
*
|
|
27
|
+
* Checks all required parameters (from the plugin's package.json metadata)
|
|
28
|
+
* and applies format-specific warnings for known API key patterns.
|
|
29
|
+
*
|
|
30
|
+
* @param pluginId - The plugin identifier (e.g. "anthropic", "discord")
|
|
31
|
+
* @param category - Plugin category
|
|
32
|
+
* @param envKey - Primary environment variable key (legacy, used as fallback)
|
|
33
|
+
* @param configKeys - All known config key names for this plugin
|
|
34
|
+
* @param providedConfig - Config values being set (for PUT validation)
|
|
35
|
+
* @param paramDefs - Full parameter definitions with required/sensitive metadata
|
|
36
|
+
*/
|
|
37
|
+
function validatePluginConfig(_pluginId, _category, envKey, configKeys, providedConfig, paramDefs) {
|
|
38
|
+
const errors = [];
|
|
39
|
+
const warnings = [];
|
|
40
|
+
const allowedConfigKeys = new Set(configKeys);
|
|
41
|
+
const canonicalKeyByNormalized = new Map(configKeys.map((key) => [key.trim().toUpperCase(), key]));
|
|
42
|
+
if (providedConfig) for (const key of Object.keys(providedConfig)) {
|
|
43
|
+
if (allowedConfigKeys.has(key)) continue;
|
|
44
|
+
const canonical = canonicalKeyByNormalized.get(key.trim().toUpperCase());
|
|
45
|
+
if (canonical) {
|
|
46
|
+
errors.push({
|
|
47
|
+
field: key,
|
|
48
|
+
message: `${key} does not match declared config key casing; use ${canonical}`
|
|
49
|
+
});
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
errors.push({
|
|
53
|
+
field: key,
|
|
54
|
+
message: `${key} is not a declared config key for this plugin`
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (paramDefs && paramDefs.length > 0) for (const param of paramDefs) {
|
|
58
|
+
if (!param.required) continue;
|
|
59
|
+
const value = providedConfig?.[param.key] ?? process.env[param.key];
|
|
60
|
+
if (!value || !value.trim()) {
|
|
61
|
+
if (param.default) warnings.push({
|
|
62
|
+
field: param.key,
|
|
63
|
+
message: `${param.key} is not set (will use default: ${param.default})`
|
|
64
|
+
});
|
|
65
|
+
else errors.push({
|
|
66
|
+
field: param.key,
|
|
67
|
+
message: `${param.key} is required but not set`
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const hint = KEY_PREFIX_HINTS[param.key];
|
|
72
|
+
if (hint && !value.startsWith(hint.prefix)) warnings.push({
|
|
73
|
+
field: param.key,
|
|
74
|
+
message: `${hint.label} key should start with "${hint.prefix}" — the current value may be invalid`
|
|
75
|
+
});
|
|
76
|
+
if (param.sensitive && value.trim().length < 10) warnings.push({
|
|
77
|
+
field: param.key,
|
|
78
|
+
message: `${param.key} looks too short (${value.trim().length} chars)`
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
else if (envKey) {
|
|
82
|
+
const currentValue = providedConfig?.[envKey] ?? process.env[envKey];
|
|
83
|
+
if (!currentValue || !currentValue.trim()) errors.push({
|
|
84
|
+
field: envKey,
|
|
85
|
+
message: `${envKey} is required but not set`
|
|
86
|
+
});
|
|
87
|
+
else {
|
|
88
|
+
const hint = KEY_PREFIX_HINTS[envKey];
|
|
89
|
+
if (hint && !currentValue.startsWith(hint.prefix)) warnings.push({
|
|
90
|
+
field: envKey,
|
|
91
|
+
message: `${hint.label} key should start with "${hint.prefix}" — the current value may be invalid`
|
|
92
|
+
});
|
|
93
|
+
if (currentValue.trim().length < 10) warnings.push({
|
|
94
|
+
field: envKey,
|
|
95
|
+
message: `${envKey} looks too short (${currentValue.trim().length} chars)`
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
valid: errors.length === 0,
|
|
101
|
+
errors,
|
|
102
|
+
warnings
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Validate a runtime context object for null, undefined, empty, and
|
|
107
|
+
* non-serializable fields.
|
|
108
|
+
*
|
|
109
|
+
* Used after provider + plugin resolution to detect and surface invalid
|
|
110
|
+
* or malformed context early — before it reaches the agent runtime.
|
|
111
|
+
*
|
|
112
|
+
* @param context - The context object to validate.
|
|
113
|
+
* @param maxDepth - Maximum nesting depth to inspect (default: 5).
|
|
114
|
+
*/
|
|
115
|
+
function validateRuntimeContext(context, maxDepth = 5) {
|
|
116
|
+
const nullFields = [];
|
|
117
|
+
const undefinedFields = [];
|
|
118
|
+
const emptyFields = [];
|
|
119
|
+
const nonSerializableFields = [];
|
|
120
|
+
function walk(obj, prefix, depth) {
|
|
121
|
+
if (depth > maxDepth) return;
|
|
122
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
123
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
124
|
+
if (value === null) {
|
|
125
|
+
nullFields.push(path);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (value === void 0) {
|
|
129
|
+
undefinedFields.push(path);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (typeof value === "string" && value.trim() === "") {
|
|
133
|
+
emptyFields.push(path);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (typeof value === "function") {
|
|
137
|
+
nonSerializableFields.push(path);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (typeof value === "symbol") {
|
|
141
|
+
nonSerializableFields.push(path);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (typeof value === "bigint") {
|
|
145
|
+
nonSerializableFields.push(path);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (typeof value === "object" && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof RegExp)) walk(value, path, depth + 1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
walk(context, "", 0);
|
|
152
|
+
let serializable = true;
|
|
153
|
+
if (nonSerializableFields.length > 0) serializable = false;
|
|
154
|
+
else try {
|
|
155
|
+
JSON.stringify(context);
|
|
156
|
+
} catch {
|
|
157
|
+
serializable = false;
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
valid: nullFields.length === 0 && undefinedFields.length === 0 && emptyFields.length === 0,
|
|
161
|
+
serializable,
|
|
162
|
+
nullFields,
|
|
163
|
+
undefinedFields,
|
|
164
|
+
emptyFields,
|
|
165
|
+
nonSerializableFields
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Log the full resolved plugin/provider context for debugging.
|
|
170
|
+
*
|
|
171
|
+
* Prints a structured summary of all loaded plugins, providers,
|
|
172
|
+
* and any validation issues detected in the context.
|
|
173
|
+
*
|
|
174
|
+
* @param plugins - Array of resolved plugin names.
|
|
175
|
+
* @param providers - Array of resolved provider names.
|
|
176
|
+
* @param context - The runtime context object to inspect.
|
|
177
|
+
* @param log - Logger function (defaults to console.debug).
|
|
178
|
+
*/
|
|
179
|
+
function debugLogResolvedContext(plugins, providers, context, log = console.debug) {
|
|
180
|
+
log("[milady:debug] ══════ Resolved Plugin/Provider Context ══════");
|
|
181
|
+
log(`[milady:debug] Plugins loaded (${plugins.length}):`);
|
|
182
|
+
for (const name of plugins) log(`[milady:debug] • ${name}`);
|
|
183
|
+
log(`[milady:debug] Providers loaded (${providers.length}):`);
|
|
184
|
+
for (const name of providers) log(`[milady:debug] • ${name}`);
|
|
185
|
+
const validation = validateRuntimeContext(context);
|
|
186
|
+
if (validation.valid && validation.serializable) log("[milady:debug] Context validation: ✓ PASS (all fields valid, serializable)");
|
|
187
|
+
else {
|
|
188
|
+
log("[milady:debug] Context validation: ✗ ISSUES DETECTED");
|
|
189
|
+
if (validation.nullFields.length > 0) log(`[milady:debug] null fields: ${validation.nullFields.join(", ")}`);
|
|
190
|
+
if (validation.undefinedFields.length > 0) log(`[milady:debug] undefined fields: ${validation.undefinedFields.join(", ")}`);
|
|
191
|
+
if (validation.emptyFields.length > 0) log(`[milady:debug] empty fields: ${validation.emptyFields.join(", ")}`);
|
|
192
|
+
if (validation.nonSerializableFields.length > 0) log(`[milady:debug] non-serializable fields: ${validation.nonSerializableFields.join(", ")}`);
|
|
193
|
+
}
|
|
194
|
+
log("[milady:debug] ══════════════════════════════════════════════");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
//#endregion
|
|
198
|
+
export { debugLogResolvedContext, validatePluginConfig, validateRuntimeContext };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { SUBSCRIPTION_PROVIDER_MAP } from "../auth/types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/api/provider-switch-config.ts
|
|
4
|
+
/**
|
|
5
|
+
* Apply subscription provider configuration to the config object.
|
|
6
|
+
*
|
|
7
|
+
* Sets `agents.defaults.subscriptionProvider` and `agents.defaults.model.primary`
|
|
8
|
+
* so the runtime auto-detects the correct provider on restart.
|
|
9
|
+
*
|
|
10
|
+
* Mutates `config` in place.
|
|
11
|
+
*/
|
|
12
|
+
function applySubscriptionProviderConfig(config, provider) {
|
|
13
|
+
config.agents ??= {};
|
|
14
|
+
config.agents.defaults ??= {};
|
|
15
|
+
const defaults = config.agents.defaults;
|
|
16
|
+
const subscriptionKey = provider === "openai-subscription" ? "openai-codex" : provider;
|
|
17
|
+
const modelProvider = SUBSCRIPTION_PROVIDER_MAP[subscriptionKey];
|
|
18
|
+
if (modelProvider) {
|
|
19
|
+
defaults.subscriptionProvider = subscriptionKey;
|
|
20
|
+
defaults.model = {
|
|
21
|
+
...defaults.model,
|
|
22
|
+
primary: modelProvider
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Clear subscription provider configuration from the config object.
|
|
28
|
+
*
|
|
29
|
+
* Removes `agents.defaults.subscriptionProvider` so the runtime
|
|
30
|
+
* doesn't try to auto-detect a subscription provider on restart.
|
|
31
|
+
*
|
|
32
|
+
* Mutates `config` in place.
|
|
33
|
+
*/
|
|
34
|
+
function clearSubscriptionProviderConfig(config) {
|
|
35
|
+
config.agents ??= {};
|
|
36
|
+
config.agents.defaults ??= {};
|
|
37
|
+
delete config.agents.defaults.subscriptionProvider;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
export { applySubscriptionProviderConfig, clearSubscriptionProviderConfig };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { parseClampedInteger } from "../utils/number-parsing.js";
|
|
2
|
+
|
|
3
|
+
//#region src/api/registry-routes.ts
|
|
4
|
+
async function handleRegistryRoutes(ctx) {
|
|
5
|
+
const { res, method, pathname, url, json, error, getPluginManager, getLoadedPluginNames, getBundledPluginIds } = ctx;
|
|
6
|
+
if (method === "GET" && pathname === "/api/registry/plugins") {
|
|
7
|
+
try {
|
|
8
|
+
const pluginManager = getPluginManager();
|
|
9
|
+
const registry = await pluginManager.refreshRegistry();
|
|
10
|
+
const installed = await pluginManager.listInstalledPlugins();
|
|
11
|
+
const installedNames = new Set(installed.map((plugin) => plugin.name));
|
|
12
|
+
const loadedNames = new Set(getLoadedPluginNames());
|
|
13
|
+
const bundledIds = getBundledPluginIds();
|
|
14
|
+
const plugins = Array.from(registry.values()).map((plugin) => {
|
|
15
|
+
const shortId = plugin.name.replace(/^@[^/]+\/plugin-/, "").replace(/^@[^/]+\//, "").replace(/^plugin-/, "");
|
|
16
|
+
return {
|
|
17
|
+
...plugin,
|
|
18
|
+
installed: installedNames.has(plugin.name),
|
|
19
|
+
installedVersion: installed.find((entry) => entry.name === plugin.name)?.version ?? null,
|
|
20
|
+
loaded: loadedNames.has(plugin.name) || loadedNames.has(plugin.name.replace("@elizaos/", "")),
|
|
21
|
+
bundled: bundledIds.has(shortId)
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
json(res, {
|
|
25
|
+
count: plugins.length,
|
|
26
|
+
plugins
|
|
27
|
+
});
|
|
28
|
+
} catch (err) {
|
|
29
|
+
error(res, `Failed to fetch registry: ${err instanceof Error ? err.message : String(err)}`, 502);
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
if (method === "GET" && pathname.startsWith("/api/registry/plugins/") && pathname.length > 22) {
|
|
34
|
+
const name = decodeURIComponent(pathname.slice(22));
|
|
35
|
+
try {
|
|
36
|
+
const info = await getPluginManager().getRegistryPlugin(name);
|
|
37
|
+
if (!info) {
|
|
38
|
+
error(res, `Plugin "${name}" not found in registry`, 404);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
json(res, { plugin: info });
|
|
42
|
+
} catch (err) {
|
|
43
|
+
error(res, `Failed to look up plugin: ${err instanceof Error ? err.message : String(err)}`, 502);
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (method === "GET" && pathname === "/api/registry/search") {
|
|
48
|
+
const query = url.searchParams.get("q") || "";
|
|
49
|
+
if (!query.trim()) {
|
|
50
|
+
error(res, "Query parameter 'q' is required", 400);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const limitParam = url.searchParams.get("limit");
|
|
55
|
+
const limit = limitParam ? parseClampedInteger(limitParam, {
|
|
56
|
+
min: 1,
|
|
57
|
+
max: 50,
|
|
58
|
+
fallback: 15
|
|
59
|
+
}) : 15;
|
|
60
|
+
const results = await getPluginManager().searchRegistry(query, limit);
|
|
61
|
+
json(res, {
|
|
62
|
+
query,
|
|
63
|
+
count: results.length,
|
|
64
|
+
results
|
|
65
|
+
});
|
|
66
|
+
} catch (err) {
|
|
67
|
+
error(res, `Search failed: ${err instanceof Error ? err.message : String(err)}`, 502);
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (method === "POST" && pathname === "/api/registry/refresh") {
|
|
72
|
+
try {
|
|
73
|
+
json(res, {
|
|
74
|
+
ok: true,
|
|
75
|
+
count: (await getPluginManager().refreshRegistry()).size
|
|
76
|
+
});
|
|
77
|
+
} catch (err) {
|
|
78
|
+
error(res, `Refresh failed: ${err instanceof Error ? err.message : String(err)}`, 502);
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
export { handleRegistryRoutes };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { logger } from "@elizaos/core";
|
|
2
|
+
import { ethers } from "ethers";
|
|
3
|
+
|
|
4
|
+
//#region src/api/registry-service.ts
|
|
5
|
+
/**
|
|
6
|
+
* ERC-8004 Agent Identity Registry service.
|
|
7
|
+
*
|
|
8
|
+
* Handles all interactions with the MiladyAgentRegistry contract:
|
|
9
|
+
* - Registration (self and delegated)
|
|
10
|
+
* - Profile updates
|
|
11
|
+
* - Metadata (tokenURI) management
|
|
12
|
+
* - Status queries
|
|
13
|
+
*/
|
|
14
|
+
const REGISTRY_ABI = [
|
|
15
|
+
"function registerAgent(string,string,bytes32,string) external returns (uint256)",
|
|
16
|
+
"function registerAgentFor(address,string,string,bytes32,string) external returns (uint256)",
|
|
17
|
+
"function updateAgent(string,bytes32) external",
|
|
18
|
+
"function updateAgentProfile(string,string,bytes32,string) external",
|
|
19
|
+
"function updateTokenURI(uint256,string) external",
|
|
20
|
+
"function deactivateAgent() external",
|
|
21
|
+
"function reactivateAgent() external",
|
|
22
|
+
"function getAgentInfo(uint256) view returns (string,string,bytes32,bool)",
|
|
23
|
+
"function addressToTokenId(address) view returns (uint256)",
|
|
24
|
+
"function isRegistered(address) view returns (bool)",
|
|
25
|
+
"function getTokenId(address) view returns (uint256)",
|
|
26
|
+
"function totalAgents() view returns (uint256)",
|
|
27
|
+
"function isEndpointTaken(string) view returns (bool)",
|
|
28
|
+
"function balanceOf(address) view returns (uint256)",
|
|
29
|
+
"function ownerOf(uint256) view returns (address)",
|
|
30
|
+
"function tokenURI(uint256) view returns (string)",
|
|
31
|
+
"event AgentRegistered(uint256 indexed tokenId, address indexed owner, string name, string endpoint)",
|
|
32
|
+
"event AgentUpdated(uint256 indexed tokenId, string endpoint, bytes32 capabilitiesHash)"
|
|
33
|
+
];
|
|
34
|
+
const DEFAULT_CAPABILITIES_HASH = ethers.id("milady-agent");
|
|
35
|
+
var RegistryService = class {
|
|
36
|
+
constructor(txService, registryAddress) {
|
|
37
|
+
this.txService = txService;
|
|
38
|
+
this.registryAddress = registryAddress;
|
|
39
|
+
this.contract = txService.getContract(registryAddress, REGISTRY_ABI);
|
|
40
|
+
}
|
|
41
|
+
get address() {
|
|
42
|
+
return this.txService.address;
|
|
43
|
+
}
|
|
44
|
+
get contractAddress() {
|
|
45
|
+
return this.registryAddress;
|
|
46
|
+
}
|
|
47
|
+
async getChainId() {
|
|
48
|
+
return this.txService.getChainId();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get the full registration status for the current wallet.
|
|
52
|
+
*/
|
|
53
|
+
async getStatus() {
|
|
54
|
+
const addr = this.txService.address;
|
|
55
|
+
const [registered, totalAgentsBN] = await Promise.all([this.contract.isRegistered(addr), this.contract.totalAgents()]);
|
|
56
|
+
if (!registered) return {
|
|
57
|
+
registered: false,
|
|
58
|
+
tokenId: 0,
|
|
59
|
+
agentName: "",
|
|
60
|
+
agentEndpoint: "",
|
|
61
|
+
capabilitiesHash: "",
|
|
62
|
+
isActive: false,
|
|
63
|
+
tokenURI: "",
|
|
64
|
+
walletAddress: addr,
|
|
65
|
+
totalAgents: Number(totalAgentsBN)
|
|
66
|
+
};
|
|
67
|
+
const tokenId = Number(await this.contract.getTokenId(addr));
|
|
68
|
+
const [[name, endpoint, capHash, isActive], uri] = await Promise.all([this.contract.getAgentInfo(tokenId), this.contract.tokenURI(tokenId)]);
|
|
69
|
+
return {
|
|
70
|
+
registered: true,
|
|
71
|
+
tokenId,
|
|
72
|
+
agentName: name,
|
|
73
|
+
agentEndpoint: endpoint,
|
|
74
|
+
capabilitiesHash: capHash,
|
|
75
|
+
isActive,
|
|
76
|
+
tokenURI: uri,
|
|
77
|
+
walletAddress: addr,
|
|
78
|
+
totalAgents: Number(totalAgentsBN)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Register the current wallet as an agent.
|
|
83
|
+
* The NFT is minted to the wallet address stored in EVM_PRIVATE_KEY.
|
|
84
|
+
*/
|
|
85
|
+
async register(params) {
|
|
86
|
+
const capHash = params.capabilitiesHash || DEFAULT_CAPABILITIES_HASH;
|
|
87
|
+
logger.info(`[registry] Registering agent "${params.name}" from ${this.txService.address}`);
|
|
88
|
+
const nonce = await this.txService.getFreshNonce();
|
|
89
|
+
const tx = await this.contract.registerAgent(params.name, params.endpoint, capHash, params.tokenURI, { nonce });
|
|
90
|
+
logger.info(`[registry] Registration tx submitted: ${tx.hash}`);
|
|
91
|
+
const receipt = await tx.wait();
|
|
92
|
+
const iface = new ethers.Interface(REGISTRY_ABI);
|
|
93
|
+
let tokenId = 0;
|
|
94
|
+
for (const log of receipt.logs) {
|
|
95
|
+
const parsed = iface.parseLog({
|
|
96
|
+
topics: log.topics,
|
|
97
|
+
data: log.data
|
|
98
|
+
});
|
|
99
|
+
if (parsed && parsed.name === "AgentRegistered") {
|
|
100
|
+
tokenId = Number(parsed.args[0]);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (tokenId === 0) tokenId = Number(await this.contract.getTokenId(this.txService.address));
|
|
105
|
+
logger.info(`[registry] Agent registered: tokenId=${tokenId} txHash=${receipt.hash}`);
|
|
106
|
+
return {
|
|
107
|
+
tokenId,
|
|
108
|
+
txHash: receipt.hash
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Update the tokenURI (metadata pointer) for the current wallet's agent.
|
|
113
|
+
* Called when the character is edited.
|
|
114
|
+
*/
|
|
115
|
+
async updateTokenURI(newURI) {
|
|
116
|
+
const tokenId = Number(await this.contract.getTokenId(this.txService.address));
|
|
117
|
+
if (tokenId === 0) throw new Error("Agent not registered, cannot update token URI");
|
|
118
|
+
logger.info(`[registry] Updating tokenURI for token ${tokenId}: ${newURI}`);
|
|
119
|
+
const nonce = await this.txService.getFreshNonce();
|
|
120
|
+
const receipt = await (await this.contract.updateTokenURI(tokenId, newURI, { nonce })).wait();
|
|
121
|
+
logger.info(`[registry] TokenURI updated: txHash=${receipt.hash}`);
|
|
122
|
+
return receipt.hash;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Update the agent's endpoint and capabilities hash.
|
|
126
|
+
*/
|
|
127
|
+
async updateAgent(endpoint, capabilitiesHash) {
|
|
128
|
+
const capHash = capabilitiesHash || DEFAULT_CAPABILITIES_HASH;
|
|
129
|
+
logger.info(`[registry] Updating agent profile: endpoint=${endpoint}`);
|
|
130
|
+
const nonce = await this.txService.getFreshNonce();
|
|
131
|
+
const receipt = await (await this.contract.updateAgent(endpoint, capHash, { nonce })).wait();
|
|
132
|
+
logger.info(`[registry] Agent updated: txHash=${receipt.hash}`);
|
|
133
|
+
return receipt.hash;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if a specific address is registered.
|
|
137
|
+
*/
|
|
138
|
+
/**
|
|
139
|
+
* Sync the full agent profile on-chain: name, endpoint, capabilities, and tokenURI.
|
|
140
|
+
* Called when the character is edited and user wants to push changes to chain.
|
|
141
|
+
*/
|
|
142
|
+
async syncProfile(params) {
|
|
143
|
+
const capHash = params.capabilitiesHash || DEFAULT_CAPABILITIES_HASH;
|
|
144
|
+
logger.info(`[registry] Syncing profile: name="${params.name}" endpoint="${params.endpoint}"`);
|
|
145
|
+
const nonce = await this.txService.getFreshNonce();
|
|
146
|
+
const tx = await this.contract.updateAgentProfile(params.name, params.endpoint, capHash, params.tokenURI, { nonce });
|
|
147
|
+
logger.info(`[registry] Sync tx submitted: ${tx.hash}`);
|
|
148
|
+
const receipt = await tx.wait();
|
|
149
|
+
logger.info(`[registry] Profile synced: txHash=${receipt.hash}`);
|
|
150
|
+
return receipt.hash;
|
|
151
|
+
}
|
|
152
|
+
async isRegistered(address) {
|
|
153
|
+
return this.contract.isRegistered(address);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Build the default capabilities hash used for Milady agents.
|
|
157
|
+
*/
|
|
158
|
+
static defaultCapabilitiesHash() {
|
|
159
|
+
return DEFAULT_CAPABILITIES_HASH;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
//#endregion
|
|
164
|
+
export { RegistryService };
|