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,590 @@
|
|
|
1
|
+
import { parseClampedInteger } from "../utils/number-parsing.js";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
|
|
4
|
+
//#region src/cli/plugins-cli.ts
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a user-provided plugin name to its fully-qualified form.
|
|
7
|
+
* Accepts `@scope/plugin-x`, `plugin-x`, or shorthand `x` (→ `@elizaos/plugin-x`).
|
|
8
|
+
*/
|
|
9
|
+
function normalizePluginName(name) {
|
|
10
|
+
if (name.startsWith("@") || name.startsWith("plugin-")) return name;
|
|
11
|
+
return `@elizaos/plugin-${name}`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parse plugin name and optional version from user input.
|
|
15
|
+
* Examples:
|
|
16
|
+
* - "twitter" → { name: "@elizaos/plugin-twitter", version: undefined }
|
|
17
|
+
* - "twitter@1.2.3" → { name: "@elizaos/plugin-twitter", version: "1.2.3" }
|
|
18
|
+
* - "@custom/plugin-x@2.0.0" → { name: "@custom/plugin-x", version: "2.0.0" }
|
|
19
|
+
*/
|
|
20
|
+
function parsePluginSpec(input) {
|
|
21
|
+
const trimmed = input.trim();
|
|
22
|
+
let namePart = trimmed;
|
|
23
|
+
let versionPart;
|
|
24
|
+
if (trimmed.startsWith("@")) {
|
|
25
|
+
const secondAt = trimmed.indexOf("@", 1);
|
|
26
|
+
if (secondAt !== -1) {
|
|
27
|
+
namePart = trimmed.slice(0, secondAt);
|
|
28
|
+
versionPart = trimmed.slice(secondAt + 1);
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
const atIndex = trimmed.indexOf("@");
|
|
32
|
+
if (atIndex !== -1) {
|
|
33
|
+
namePart = trimmed.slice(0, atIndex);
|
|
34
|
+
versionPart = trimmed.slice(atIndex + 1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const version = versionPart?.trim() || void 0;
|
|
38
|
+
return {
|
|
39
|
+
name: normalizePluginName(namePart),
|
|
40
|
+
version
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Display plugin configuration parameters in a formatted table.
|
|
45
|
+
*/
|
|
46
|
+
function displayPluginConfig(plugin, currentEnv) {
|
|
47
|
+
const params = plugin.parameters ?? [];
|
|
48
|
+
if (params.length === 0) {
|
|
49
|
+
console.log(chalk.dim(" No configurable parameters."));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
for (const param of params) {
|
|
53
|
+
const hint = plugin.configUiHints?.[param.key] ?? {};
|
|
54
|
+
const label = hint.label ?? param.key;
|
|
55
|
+
const value = currentEnv[param.key];
|
|
56
|
+
const isSet = value != null && value !== "";
|
|
57
|
+
const isSensitive = param.sensitive || hint.sensitive;
|
|
58
|
+
const displayValue = !isSet ? chalk.dim("(not set)") : isSensitive ? chalk.dim("●●●●●●●●") : chalk.white(value);
|
|
59
|
+
const required = param.required ? chalk.red(" *") : "";
|
|
60
|
+
const help = hint.help ?? param.description ? chalk.dim(` — ${hint.help ?? param.description}`) : "";
|
|
61
|
+
console.log(` ${chalk.cyan(label.padEnd(30))} ${displayValue}${required}${help}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function getPluginManager() {
|
|
65
|
+
const { PluginManagerService } = await import("@elizaos/plugin-plugin-manager");
|
|
66
|
+
return new PluginManagerService({
|
|
67
|
+
plugins: [],
|
|
68
|
+
actions: [],
|
|
69
|
+
providers: [],
|
|
70
|
+
evaluators: [],
|
|
71
|
+
services: [],
|
|
72
|
+
getService: () => null,
|
|
73
|
+
registerService: () => {},
|
|
74
|
+
registerAction: () => {},
|
|
75
|
+
registerProvider: () => {},
|
|
76
|
+
registerEvaluator: () => {},
|
|
77
|
+
registerEvent: () => {}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function registerPluginsCli(program) {
|
|
81
|
+
const pluginsCommand = program.command("plugins").description("Browse, search, install, and manage ElizaOS plugins from the registry");
|
|
82
|
+
pluginsCommand.command("list").description("List all plugins from the registry (next branch)").option("-q, --query <query>", "Filter plugins by name or keyword").option("-l, --limit <number>", "Max results to show", "30").action(async (opts) => {
|
|
83
|
+
const pluginManager = await getPluginManager();
|
|
84
|
+
const limit = parseClampedInteger(opts.limit, {
|
|
85
|
+
min: 1,
|
|
86
|
+
max: 500,
|
|
87
|
+
fallback: 30
|
|
88
|
+
});
|
|
89
|
+
const installed = await pluginManager.listInstalledPlugins();
|
|
90
|
+
const installedNames = new Set(installed.map((p) => p.name));
|
|
91
|
+
if (opts.query) {
|
|
92
|
+
const results = await pluginManager.searchRegistry(opts.query, limit);
|
|
93
|
+
if (results.length === 0) {
|
|
94
|
+
console.log(`\nNo plugins found matching "${opts.query}"\n`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
console.log(`\n${chalk.bold(`Found ${results.length} plugins matching "${opts.query}":`)}\n`);
|
|
98
|
+
for (const r of results) {
|
|
99
|
+
const versionBadges = [];
|
|
100
|
+
if (r.supports.v0) versionBadges.push("v0");
|
|
101
|
+
if (r.supports.v1) versionBadges.push("v1");
|
|
102
|
+
if (r.supports.v2) versionBadges.push("v2");
|
|
103
|
+
const badge = installedNames.has(r.name) ? chalk.green(" ✓ installed") : "";
|
|
104
|
+
console.log(` ${chalk.cyan(r.name)} ${r.latestVersion ? chalk.dim(`v${r.latestVersion}`) : ""}${badge}`);
|
|
105
|
+
if (r.description) console.log(` ${r.description}`);
|
|
106
|
+
if (r.tags.length > 0) console.log(` ${chalk.dim(`tags: ${r.tags.slice(0, 5).join(", ")}`)}`);
|
|
107
|
+
if (versionBadges.length > 0) console.log(` ${chalk.dim(`supports: ${versionBadges.join(", ")}`)}`);
|
|
108
|
+
console.log();
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
const registry = await pluginManager.refreshRegistry();
|
|
112
|
+
const all = Array.from(registry.values());
|
|
113
|
+
const installedCount = all.filter((p) => installedNames.has(p.name)).length;
|
|
114
|
+
console.log(`\n${chalk.bold(`${all.length} plugins available in registry`)}${installedCount > 0 ? chalk.green(` (${installedCount} installed)`) : ""}${chalk.bold(":")}\n`);
|
|
115
|
+
const sorted = all.sort((a, b) => a.name.localeCompare(b.name)).slice(0, limit);
|
|
116
|
+
for (const plugin of sorted) {
|
|
117
|
+
const desc = plugin.description ? ` — ${plugin.description}` : "";
|
|
118
|
+
const badge = installedNames.has(plugin.name) ? chalk.green(" ✓") : "";
|
|
119
|
+
console.log(` ${chalk.cyan(plugin.name)}${badge}${chalk.dim(desc)}`);
|
|
120
|
+
}
|
|
121
|
+
if (all.length > limit) console.log(chalk.dim(`\n ... and ${all.length - limit} more (use --limit to show more)`));
|
|
122
|
+
console.log();
|
|
123
|
+
}
|
|
124
|
+
console.log(chalk.dim("Install a plugin: milady plugins install <name>"));
|
|
125
|
+
console.log(chalk.dim("Search: milady plugins list -q <keyword>"));
|
|
126
|
+
console.log();
|
|
127
|
+
});
|
|
128
|
+
pluginsCommand.command("search <query>").description("Search the plugin registry by keyword").option("-l, --limit <number>", "Max results", "15").action(async (query, opts) => {
|
|
129
|
+
const pluginManager = await getPluginManager();
|
|
130
|
+
const limit = parseClampedInteger(opts.limit, {
|
|
131
|
+
min: 1,
|
|
132
|
+
max: 50,
|
|
133
|
+
fallback: 15
|
|
134
|
+
});
|
|
135
|
+
const results = await pluginManager.searchRegistry(query, limit);
|
|
136
|
+
if (results.length === 0) {
|
|
137
|
+
console.log(`\nNo plugins found matching "${query}"\n`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
console.log(`\n${chalk.bold(`${results.length} results for "${query}":`)}\n`);
|
|
141
|
+
for (const r of results) {
|
|
142
|
+
const match = (r.score * 100).toFixed(0);
|
|
143
|
+
console.log(` ${chalk.cyan(r.name)} ${chalk.dim(`(${match}% match)`)}`);
|
|
144
|
+
if (r.description) console.log(` ${r.description}`);
|
|
145
|
+
if (r.stars > 0) console.log(` ${chalk.dim(`stars: ${r.stars}`)}`);
|
|
146
|
+
console.log();
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
pluginsCommand.command("info <name>").description("Show detailed information about a plugin").action(async (name) => {
|
|
150
|
+
const pluginManager = await getPluginManager();
|
|
151
|
+
const normalizedName = normalizePluginName(name);
|
|
152
|
+
const info = await pluginManager.getRegistryPlugin(normalizedName);
|
|
153
|
+
if (!info) {
|
|
154
|
+
console.log(`\n${chalk.red("Not found:")} ${normalizedName}`);
|
|
155
|
+
console.log(chalk.dim("Run 'milady plugins search <keyword>' to find plugins.\n"));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
console.log();
|
|
159
|
+
console.log(chalk.bold(info.name));
|
|
160
|
+
console.log(chalk.dim("─".repeat(info.name.length)));
|
|
161
|
+
if (info.description) console.log(`\n ${info.description}`);
|
|
162
|
+
console.log(`\n ${chalk.dim("Repository:")} https://github.com/${info.gitRepo}`);
|
|
163
|
+
if (info.homepage) console.log(` ${chalk.dim("Homepage:")} ${info.homepage}`);
|
|
164
|
+
console.log(` ${chalk.dim("Language:")} ${info.language}`);
|
|
165
|
+
console.log(` ${chalk.dim("Stars:")} ${info.stars}`);
|
|
166
|
+
if (info.topics.length > 0) console.log(` ${chalk.dim("Topics:")} ${info.topics.join(", ")}`);
|
|
167
|
+
const versions = [];
|
|
168
|
+
if (info.npm.v0Version) versions.push(`v0: ${info.npm.v0Version}`);
|
|
169
|
+
if (info.npm.v1Version) versions.push(`v1: ${info.npm.v1Version}`);
|
|
170
|
+
if (info.npm.v2Version) versions.push(`v2: ${info.npm.v2Version}`);
|
|
171
|
+
if (versions.length > 0) console.log(` ${chalk.dim("npm:")} ${versions.join(" | ")}`);
|
|
172
|
+
const supported = [];
|
|
173
|
+
if (info.supports.v0) supported.push("v0");
|
|
174
|
+
if (info.supports.v1) supported.push("v1");
|
|
175
|
+
if (info.supports.v2) supported.push("v2");
|
|
176
|
+
if (supported.length > 0) console.log(` ${chalk.dim("Supports:")} ${supported.join(", ")}`);
|
|
177
|
+
console.log(`\n Install: ${chalk.cyan(`milady plugins install ${info.name}`)}\n`);
|
|
178
|
+
});
|
|
179
|
+
pluginsCommand.command("install <name>").description("Install a plugin from the registry. Optionally pin to a specific version or dist-tag (e.g., twitter@1.2.3, twitter@next)").option("--no-restart", "Install without restarting the agent").action(async (name, opts) => {
|
|
180
|
+
const { name: normalizedName, version } = parsePluginSpec(name);
|
|
181
|
+
const displayName = version ? `${normalizedName}@${version}` : normalizedName;
|
|
182
|
+
console.log(`\nInstalling ${chalk.cyan(displayName)}...\n`);
|
|
183
|
+
const progressHandler = (progress) => {
|
|
184
|
+
console.log(` [${progress.phase}] ${progress.message}`);
|
|
185
|
+
};
|
|
186
|
+
const { installPlugin } = await import("../services/plugin-installer.js");
|
|
187
|
+
const result = await installPlugin(normalizedName, progressHandler, version);
|
|
188
|
+
if (result.success) {
|
|
189
|
+
console.log(`\n${chalk.green("Success!")} ${result.pluginName}@${result.version} installed.`);
|
|
190
|
+
if (result.requiresRestart && !opts.restart) console.log(chalk.yellow("\nRestart your agent to load the new plugin."));
|
|
191
|
+
else if (result.requiresRestart) {
|
|
192
|
+
console.log(chalk.dim("Agent is restarting to load the new plugin..."));
|
|
193
|
+
const { requestRestart } = await import("../runtime/restart.js");
|
|
194
|
+
await Promise.resolve(requestRestart(`Plugin ${result.pluginName} installed`));
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
console.log(`\n${chalk.red("Failed:")} ${result.error}`);
|
|
198
|
+
process.exitCode = 1;
|
|
199
|
+
}
|
|
200
|
+
console.log();
|
|
201
|
+
});
|
|
202
|
+
pluginsCommand.command("uninstall <name>").description("Uninstall a user-installed plugin").option("--no-restart", "Uninstall without restarting the agent").action(async (name, opts) => {
|
|
203
|
+
const pluginManager = await getPluginManager();
|
|
204
|
+
console.log(`\nUninstalling ${chalk.cyan(name)}...\n`);
|
|
205
|
+
const result = await pluginManager.uninstallPlugin(name);
|
|
206
|
+
if (result.success) {
|
|
207
|
+
console.log(`${chalk.green("Success!")} ${result.pluginName} uninstalled.`);
|
|
208
|
+
if (result.requiresRestart && !opts.restart) console.log(chalk.yellow("\nRestart your agent to apply changes."));
|
|
209
|
+
} else {
|
|
210
|
+
console.log(`\n${chalk.red("Failed:")} ${result.error}`);
|
|
211
|
+
process.exitCode = 1;
|
|
212
|
+
}
|
|
213
|
+
console.log();
|
|
214
|
+
});
|
|
215
|
+
pluginsCommand.command("installed").description("List plugins installed from the registry").action(async () => {
|
|
216
|
+
const plugins = await (await getPluginManager()).listInstalledPlugins();
|
|
217
|
+
if (plugins.length === 0) {
|
|
218
|
+
console.log("\nNo plugins installed from the registry.\n");
|
|
219
|
+
console.log(chalk.dim("Install one: milady plugins install <name>\n"));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
console.log(`\n${chalk.bold(`${plugins.length} user-installed plugins:`)}\n`);
|
|
223
|
+
for (const p of plugins) {
|
|
224
|
+
console.log(` ${chalk.cyan(p.name)} ${chalk.dim(`v${p.version}`)}`);
|
|
225
|
+
console.log();
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
pluginsCommand.command("refresh").description("Force-refresh the plugin registry cache").action(async () => {
|
|
229
|
+
const pluginManager = await getPluginManager();
|
|
230
|
+
console.log("\nRefreshing registry cache...");
|
|
231
|
+
const registry = await pluginManager.refreshRegistry();
|
|
232
|
+
console.log(`${chalk.green("Done!")} ${registry.size} plugins loaded.\n`);
|
|
233
|
+
});
|
|
234
|
+
pluginsCommand.command("test").description("Validate custom drop-in plugins in ~/.milady/plugins/custom/").action(async () => {
|
|
235
|
+
const nodePath = await import("node:path");
|
|
236
|
+
const { pathToFileURL } = await import("node:url");
|
|
237
|
+
const fsPromises = await import("node:fs/promises");
|
|
238
|
+
const { resolveStateDir, resolveUserPath } = await import("../config/paths.js");
|
|
239
|
+
const { loadMiladyConfig } = await import("../config/config.js");
|
|
240
|
+
const { CUSTOM_PLUGINS_DIRNAME, scanDropInPlugins, resolvePackageEntry } = await import("../runtime/eliza.js");
|
|
241
|
+
const customDir = nodePath.join(resolveStateDir(), CUSTOM_PLUGINS_DIRNAME);
|
|
242
|
+
const scanDirs = [customDir];
|
|
243
|
+
let config = null;
|
|
244
|
+
try {
|
|
245
|
+
config = loadMiladyConfig();
|
|
246
|
+
} catch (err) {
|
|
247
|
+
console.log(chalk.dim(` (Could not read milady.json: ${err instanceof Error ? err.message : String(err)} — scanning default directory only)\n`));
|
|
248
|
+
}
|
|
249
|
+
for (const p of config?.plugins?.load?.paths ?? []) scanDirs.push(resolveUserPath(p));
|
|
250
|
+
console.log(`\n${chalk.bold("Custom plugins directory:")} ${chalk.dim(customDir)}\n`);
|
|
251
|
+
const candidates = [];
|
|
252
|
+
for (const dir of scanDirs) for (const [name, record] of Object.entries(await scanDropInPlugins(dir))) candidates.push({
|
|
253
|
+
name,
|
|
254
|
+
installPath: record.installPath ?? "",
|
|
255
|
+
version: record.version ?? ""
|
|
256
|
+
});
|
|
257
|
+
if (candidates.length === 0) {
|
|
258
|
+
console.log(" No custom plugins found.\n");
|
|
259
|
+
console.log(chalk.dim(` Drop a plugin directory into ${customDir} and run this command again.\n`));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
console.log(`${chalk.bold(`Found ${candidates.length} custom plugin(s):`)}\n`);
|
|
263
|
+
let validCount = 0;
|
|
264
|
+
let failedCount = 0;
|
|
265
|
+
const fail = (msg) => {
|
|
266
|
+
console.log(` ${chalk.red("✗")} ${msg}`);
|
|
267
|
+
failedCount++;
|
|
268
|
+
console.log();
|
|
269
|
+
};
|
|
270
|
+
for (const candidate of candidates) {
|
|
271
|
+
const ver = candidate.version !== "0.0.0" ? chalk.dim(` v${candidate.version}`) : "";
|
|
272
|
+
console.log(` ${chalk.cyan(candidate.name)}${ver}`);
|
|
273
|
+
console.log(` ${chalk.dim("Path:")} ${candidate.installPath}`);
|
|
274
|
+
let entryPoint;
|
|
275
|
+
try {
|
|
276
|
+
entryPoint = await resolvePackageEntry(candidate.installPath);
|
|
277
|
+
} catch (err) {
|
|
278
|
+
fail(`Entry point failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
console.log(` ${chalk.dim("Entry:")} ${nodePath.relative(candidate.installPath, entryPoint)}`);
|
|
282
|
+
try {
|
|
283
|
+
await fsPromises.access(entryPoint);
|
|
284
|
+
} catch {
|
|
285
|
+
fail(`File not found: ${entryPoint}`);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
let mod;
|
|
289
|
+
try {
|
|
290
|
+
mod = await import(pathToFileURL(entryPoint).href);
|
|
291
|
+
} catch (err) {
|
|
292
|
+
fail(`Import failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const plugin = findPluginExport(mod);
|
|
296
|
+
if (plugin) {
|
|
297
|
+
console.log(` ${chalk.green("✓ Valid plugin")} — ${plugin.name}: ${chalk.dim(plugin.description)}`);
|
|
298
|
+
validCount++;
|
|
299
|
+
} else {
|
|
300
|
+
fail("No valid Plugin export — needs { name: string, description: string }");
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
console.log();
|
|
304
|
+
}
|
|
305
|
+
const parts = [];
|
|
306
|
+
if (validCount > 0) parts.push(chalk.green(`${validCount} valid`));
|
|
307
|
+
if (failedCount > 0) parts.push(chalk.red(`${failedCount} failed`));
|
|
308
|
+
console.log(` ${chalk.bold("Summary:")} ${parts.join(", ")} out of ${candidates.length}\n`);
|
|
309
|
+
});
|
|
310
|
+
pluginsCommand.command("add-path <path>").description("Register an additional plugin search directory in config").action(async (rawPath) => {
|
|
311
|
+
await import("node:path");
|
|
312
|
+
const nodeFs = await import("node:fs");
|
|
313
|
+
const { resolveUserPath } = await import("../config/paths.js");
|
|
314
|
+
const { loadMiladyConfig, saveMiladyConfig } = await import("../config/config.js");
|
|
315
|
+
const resolved = resolveUserPath(rawPath);
|
|
316
|
+
if (!nodeFs.existsSync(resolved) || !nodeFs.statSync(resolved).isDirectory()) {
|
|
317
|
+
console.log(`\n${chalk.red("Error:")} ${resolved} is not a directory.\n`);
|
|
318
|
+
process.exitCode = 1;
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
let config;
|
|
322
|
+
try {
|
|
323
|
+
config = loadMiladyConfig();
|
|
324
|
+
} catch {
|
|
325
|
+
config = {};
|
|
326
|
+
}
|
|
327
|
+
if (!config.plugins) config.plugins = {};
|
|
328
|
+
if (!config.plugins.load) config.plugins.load = {};
|
|
329
|
+
if (!config.plugins.load.paths) config.plugins.load.paths = [];
|
|
330
|
+
if (config.plugins.load.paths.map(resolveUserPath).includes(resolved)) {
|
|
331
|
+
console.log(`\n${chalk.yellow("Already registered:")} ${rawPath}\n`);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
config.plugins.load.paths.push(rawPath);
|
|
335
|
+
saveMiladyConfig(config);
|
|
336
|
+
console.log(`\n${chalk.green("Added:")} ${rawPath} → ${resolved}`);
|
|
337
|
+
console.log(chalk.dim("Restart your agent to load plugins from this path.\n"));
|
|
338
|
+
});
|
|
339
|
+
pluginsCommand.command("paths").description("List all plugin search directories and their contents").action(async () => {
|
|
340
|
+
const nodePath = await import("node:path");
|
|
341
|
+
const { resolveStateDir, resolveUserPath } = await import("../config/paths.js");
|
|
342
|
+
const { loadMiladyConfig } = await import("../config/config.js");
|
|
343
|
+
const { CUSTOM_PLUGINS_DIRNAME, scanDropInPlugins } = await import("../runtime/eliza.js");
|
|
344
|
+
let config = null;
|
|
345
|
+
try {
|
|
346
|
+
config = loadMiladyConfig();
|
|
347
|
+
} catch {}
|
|
348
|
+
const customDir = nodePath.join(resolveStateDir(), CUSTOM_PLUGINS_DIRNAME);
|
|
349
|
+
const dirs = [{
|
|
350
|
+
label: customDir,
|
|
351
|
+
path: customDir,
|
|
352
|
+
origin: "custom"
|
|
353
|
+
}];
|
|
354
|
+
for (const p of config?.plugins?.load?.paths ?? []) dirs.push({
|
|
355
|
+
label: p,
|
|
356
|
+
path: resolveUserPath(p),
|
|
357
|
+
origin: "config"
|
|
358
|
+
});
|
|
359
|
+
console.log(`\n${chalk.bold("Plugin search directories:")}\n`);
|
|
360
|
+
for (const dir of dirs) {
|
|
361
|
+
const records = await scanDropInPlugins(dir.path);
|
|
362
|
+
const count = Object.keys(records).length;
|
|
363
|
+
const badge = chalk.dim(`[${dir.origin}]`);
|
|
364
|
+
const countStr = count > 0 ? chalk.green(`${count} plugin${count !== 1 ? "s" : ""}`) : chalk.dim("empty");
|
|
365
|
+
console.log(` ${badge} ${dir.label} (${countStr})`);
|
|
366
|
+
for (const [name, record] of Object.entries(records)) {
|
|
367
|
+
const ver = record.version !== "0.0.0" ? ` v${record.version}` : "";
|
|
368
|
+
console.log(` ${chalk.cyan(name)}${chalk.dim(ver)}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
console.log();
|
|
372
|
+
});
|
|
373
|
+
pluginsCommand.command("config <name>").description("Show or edit plugin configuration").option("-e, --edit", "Interactive edit mode").action(async (name, opts) => {
|
|
374
|
+
const nodeFs = await import("node:fs");
|
|
375
|
+
const pluginsPath = (await import("node:path")).resolve(process.cwd(), "plugins.json");
|
|
376
|
+
let catalog;
|
|
377
|
+
try {
|
|
378
|
+
catalog = JSON.parse(nodeFs.readFileSync(pluginsPath, "utf8"));
|
|
379
|
+
} catch (err) {
|
|
380
|
+
console.log(`\n${chalk.red("Error:")} Could not read plugins.json: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
381
|
+
process.exitCode = 1;
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const plugin = (catalog.plugins ?? []).find((p) => p.id === name || p.npmName === name || typeof p.name === "string" && p.name.toLowerCase().includes(name.toLowerCase()));
|
|
385
|
+
if (!plugin) {
|
|
386
|
+
console.log(`\n${chalk.red("Not found:")} ${name}`);
|
|
387
|
+
console.log(chalk.dim("Run 'milady plugins list' to see available plugins.\n"));
|
|
388
|
+
process.exitCode = 1;
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const pluginId = String(plugin.id ?? "");
|
|
392
|
+
const pluginName = String(plugin.name ?? pluginId);
|
|
393
|
+
const params = plugin.pluginParameters;
|
|
394
|
+
const configUiHints = plugin.configUiHints;
|
|
395
|
+
if (!opts.edit) {
|
|
396
|
+
console.log(`\n${chalk.bold(pluginName)} ${chalk.dim(`(${pluginId})`)}`);
|
|
397
|
+
console.log(chalk.dim("─".repeat(pluginName.length + pluginId.length + 3)));
|
|
398
|
+
displayPluginConfig({
|
|
399
|
+
id: pluginId,
|
|
400
|
+
name: pluginName,
|
|
401
|
+
parameters: params ? Object.entries(params).map(([key, param]) => ({
|
|
402
|
+
key,
|
|
403
|
+
description: param.description,
|
|
404
|
+
required: param.required,
|
|
405
|
+
sensitive: param.sensitive
|
|
406
|
+
})) : [],
|
|
407
|
+
configUiHints
|
|
408
|
+
}, process.env);
|
|
409
|
+
console.log();
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const clack = await import("@clack/prompts");
|
|
413
|
+
console.log(`\n${chalk.bold("Configure")} ${chalk.cyan(pluginName)}\n`);
|
|
414
|
+
const newValues = {};
|
|
415
|
+
if (!params || Object.keys(params).length === 0) {
|
|
416
|
+
console.log(chalk.dim(" No configurable parameters.\n"));
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
for (const [key, param] of Object.entries(params)) {
|
|
420
|
+
const hint = configUiHints?.[key] ?? {};
|
|
421
|
+
const label = hint.label ?? key;
|
|
422
|
+
const currentValue = process.env[key];
|
|
423
|
+
const isSensitive = param.sensitive || hint.sensitive;
|
|
424
|
+
const help = hint.help ?? param.description ?? "";
|
|
425
|
+
const displayCurrent = currentValue ? isSensitive ? chalk.dim("●●●●●●●●") : chalk.dim(`(current: ${currentValue})`) : chalk.dim("(not set)");
|
|
426
|
+
let promptValue;
|
|
427
|
+
if (param.type === "boolean") promptValue = await clack.confirm({
|
|
428
|
+
message: `${label} ${displayCurrent}`,
|
|
429
|
+
initialValue: currentValue === "true"
|
|
430
|
+
});
|
|
431
|
+
else if (isSensitive) promptValue = await clack.password({
|
|
432
|
+
message: `${label} ${displayCurrent}`,
|
|
433
|
+
validate: (v) => param.required && !v ? "This field is required" : void 0
|
|
434
|
+
});
|
|
435
|
+
else promptValue = await clack.text({
|
|
436
|
+
message: `${label} ${displayCurrent}`,
|
|
437
|
+
placeholder: help || void 0,
|
|
438
|
+
validate: (v) => param.required && !v ? "This field is required" : void 0
|
|
439
|
+
});
|
|
440
|
+
if (clack.isCancel(promptValue)) {
|
|
441
|
+
clack.cancel("Configuration cancelled.");
|
|
442
|
+
process.exit(0);
|
|
443
|
+
}
|
|
444
|
+
if (typeof promptValue === "boolean") newValues[key] = String(promptValue);
|
|
445
|
+
else if (typeof promptValue === "string" && promptValue !== "") newValues[key] = promptValue;
|
|
446
|
+
}
|
|
447
|
+
const { loadMiladyConfig, saveMiladyConfig } = await import("../config/config.js");
|
|
448
|
+
let config;
|
|
449
|
+
try {
|
|
450
|
+
config = loadMiladyConfig();
|
|
451
|
+
} catch {
|
|
452
|
+
config = {};
|
|
453
|
+
}
|
|
454
|
+
const configAny = config;
|
|
455
|
+
if (!configAny.plugins || typeof configAny.plugins !== "object") configAny.plugins = {};
|
|
456
|
+
const pluginsObj = configAny.plugins;
|
|
457
|
+
if (!pluginsObj.entries || typeof pluginsObj.entries !== "object") pluginsObj.entries = {};
|
|
458
|
+
const entries = pluginsObj.entries;
|
|
459
|
+
if (!entries[pluginId]) entries[pluginId] = {
|
|
460
|
+
enabled: true,
|
|
461
|
+
config: {}
|
|
462
|
+
};
|
|
463
|
+
if (!entries[pluginId].config || typeof entries[pluginId].config !== "object") entries[pluginId].config = {};
|
|
464
|
+
const pluginConfig = entries[pluginId].config;
|
|
465
|
+
for (const [key, value] of Object.entries(newValues)) {
|
|
466
|
+
process.env[key] = value;
|
|
467
|
+
pluginConfig[key] = value;
|
|
468
|
+
}
|
|
469
|
+
saveMiladyConfig(config);
|
|
470
|
+
console.log(`\n${chalk.green("Success!")} Configuration saved for ${pluginName}.`);
|
|
471
|
+
console.log(chalk.dim("Restart your agent to apply changes.\n"));
|
|
472
|
+
});
|
|
473
|
+
pluginsCommand.command("open [name-or-path]").description("Open a plugin directory (or the custom plugins folder) in your editor").action(async (nameOrPath) => {
|
|
474
|
+
const nodePath = await import("node:path");
|
|
475
|
+
const nodeFs = await import("node:fs");
|
|
476
|
+
const { spawnSync } = await import("node:child_process");
|
|
477
|
+
const { resolveStateDir, resolveUserPath } = await import("../config/paths.js");
|
|
478
|
+
const { CUSTOM_PLUGINS_DIRNAME, scanDropInPlugins } = await import("../runtime/eliza.js");
|
|
479
|
+
const customDir = nodePath.join(resolveStateDir(), CUSTOM_PLUGINS_DIRNAME);
|
|
480
|
+
let targetDir;
|
|
481
|
+
if (!nameOrPath) targetDir = customDir;
|
|
482
|
+
else if (nodeFs.existsSync(resolveUserPath(nameOrPath)) && nodeFs.statSync(resolveUserPath(nameOrPath)).isDirectory()) targetDir = resolveUserPath(nameOrPath);
|
|
483
|
+
else {
|
|
484
|
+
const records = await scanDropInPlugins(customDir);
|
|
485
|
+
const match = records[nameOrPath];
|
|
486
|
+
if (match?.installPath) targetDir = match.installPath;
|
|
487
|
+
else {
|
|
488
|
+
console.log(`\n${chalk.red("Not found:")} "${nameOrPath}" is not a path or known custom plugin.`);
|
|
489
|
+
console.log(chalk.dim(`Custom plugins: ${Object.keys(records).join(", ") || "(none)"}\n`));
|
|
490
|
+
process.exitCode = 1;
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
function splitCommand(command) {
|
|
495
|
+
const trimmed = command.trim();
|
|
496
|
+
if (!trimmed) return {
|
|
497
|
+
cmd: "code",
|
|
498
|
+
args: []
|
|
499
|
+
};
|
|
500
|
+
const tokens = [];
|
|
501
|
+
let current = "";
|
|
502
|
+
let quote = null;
|
|
503
|
+
let escaped = false;
|
|
504
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
505
|
+
const char = trimmed[i];
|
|
506
|
+
if (escaped) {
|
|
507
|
+
current += char;
|
|
508
|
+
escaped = false;
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
if (char === "\\") {
|
|
512
|
+
if (quote === "'") {
|
|
513
|
+
current += char;
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
const next = trimmed[i + 1];
|
|
517
|
+
if (next === "\"" || next === "'" || next === "\\" || next && /\s/.test(next)) {
|
|
518
|
+
escaped = true;
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
current += char;
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
if (quote) {
|
|
525
|
+
if (char === quote) {
|
|
526
|
+
quote = null;
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
current += char;
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
if (char === "\"" || char === "'") {
|
|
533
|
+
quote = char;
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
if (/\s/.test(char)) {
|
|
537
|
+
if (current) {
|
|
538
|
+
tokens.push(current);
|
|
539
|
+
current = "";
|
|
540
|
+
}
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
current += char;
|
|
544
|
+
}
|
|
545
|
+
if (current) tokens.push(current);
|
|
546
|
+
const [cmd, ...args] = tokens.length > 0 ? tokens : ["code"];
|
|
547
|
+
return {
|
|
548
|
+
cmd,
|
|
549
|
+
args
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
const { cmd: editorCmd, args: editorArgs } = splitCommand(process.env.EDITOR || "code");
|
|
553
|
+
console.log(`\nOpening ${chalk.cyan(targetDir)} with ${editorCmd}...\n`);
|
|
554
|
+
try {
|
|
555
|
+
const result = spawnSync(editorCmd, [...editorArgs, targetDir], { stdio: "inherit" });
|
|
556
|
+
if (result.error) throw result.error;
|
|
557
|
+
} catch {}
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
/** Find the first export that looks like a Plugin ({ name, description }). */
|
|
561
|
+
function findPluginExport(mod) {
|
|
562
|
+
const isPluginBasic = (v) => v !== null && typeof v === "object" && typeof v.name === "string" && typeof v.description === "string";
|
|
563
|
+
const hasPluginCapabilities = (v) => {
|
|
564
|
+
if (v === null || typeof v !== "object") return false;
|
|
565
|
+
const obj = v;
|
|
566
|
+
return Array.isArray(obj.services) || Array.isArray(obj.providers) || Array.isArray(obj.actions) || Array.isArray(obj.routes) || Array.isArray(obj.events) || typeof obj.init === "function";
|
|
567
|
+
};
|
|
568
|
+
const isPluginStrict = (v) => isPluginBasic(v) && hasPluginCapabilities(v);
|
|
569
|
+
if (isPluginStrict(mod.default)) return mod.default;
|
|
570
|
+
if (isPluginStrict(mod.plugin)) return mod.plugin;
|
|
571
|
+
if (isPluginStrict(mod)) return mod;
|
|
572
|
+
const keys = Object.keys(mod).filter((key) => key !== "default" && key !== "plugin");
|
|
573
|
+
const preferred = keys.filter((key) => /plugin$/i.test(key) || /^plugin/i.test(key));
|
|
574
|
+
const fallback = keys.filter((key) => !preferred.includes(key));
|
|
575
|
+
for (const key of [...preferred, ...fallback]) {
|
|
576
|
+
const value = mod[key];
|
|
577
|
+
if (isPluginStrict(value)) return value;
|
|
578
|
+
}
|
|
579
|
+
for (const key of preferred) {
|
|
580
|
+
const value = mod[key];
|
|
581
|
+
if (isPluginBasic(value)) return value;
|
|
582
|
+
}
|
|
583
|
+
if (isPluginBasic(mod.default)) return mod.default;
|
|
584
|
+
if (isPluginBasic(mod.plugin)) return mod.plugin;
|
|
585
|
+
if (isPluginBasic(mod)) return mod;
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
//#endregion
|
|
590
|
+
export { registerPluginsCli };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { isValidProfileName } from "./profile-utils.js";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
//#region src/cli/profile.ts
|
|
6
|
+
function takeValue(raw, next) {
|
|
7
|
+
if (raw.includes("=")) {
|
|
8
|
+
const [, value] = raw.split("=", 2);
|
|
9
|
+
return {
|
|
10
|
+
value: (value ?? "").trim() || null,
|
|
11
|
+
consumedNext: false
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
value: (next ?? "").trim() || null,
|
|
16
|
+
consumedNext: Boolean(next)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function parseCliProfileArgs(argv) {
|
|
20
|
+
if (argv.length < 2) return {
|
|
21
|
+
ok: true,
|
|
22
|
+
profile: null,
|
|
23
|
+
argv
|
|
24
|
+
};
|
|
25
|
+
const out = argv.slice(0, 2);
|
|
26
|
+
let profile = null;
|
|
27
|
+
let sawDev = false;
|
|
28
|
+
let sawCommand = false;
|
|
29
|
+
const args = argv.slice(2);
|
|
30
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
31
|
+
const arg = args[i];
|
|
32
|
+
if (arg === void 0) continue;
|
|
33
|
+
if (sawCommand) {
|
|
34
|
+
out.push(arg);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (arg === "--dev") {
|
|
38
|
+
if (profile && profile !== "dev") return {
|
|
39
|
+
ok: false,
|
|
40
|
+
error: "Cannot combine --dev with --profile"
|
|
41
|
+
};
|
|
42
|
+
sawDev = true;
|
|
43
|
+
profile = "dev";
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (arg === "--profile" || arg.startsWith("--profile=")) {
|
|
47
|
+
if (sawDev) return {
|
|
48
|
+
ok: false,
|
|
49
|
+
error: "Cannot combine --dev with --profile"
|
|
50
|
+
};
|
|
51
|
+
const next = args[i + 1];
|
|
52
|
+
const { value, consumedNext } = takeValue(arg, next);
|
|
53
|
+
if (consumedNext) i += 1;
|
|
54
|
+
if (!value) return {
|
|
55
|
+
ok: false,
|
|
56
|
+
error: "--profile requires a value"
|
|
57
|
+
};
|
|
58
|
+
if (!isValidProfileName(value)) return {
|
|
59
|
+
ok: false,
|
|
60
|
+
error: "Invalid --profile (use letters, numbers, \"_\", \"-\" only)"
|
|
61
|
+
};
|
|
62
|
+
profile = value;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (!arg.startsWith("-")) {
|
|
66
|
+
sawCommand = true;
|
|
67
|
+
out.push(arg);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
out.push(arg);
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
ok: true,
|
|
74
|
+
profile,
|
|
75
|
+
argv: out
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function resolveProfileStateDir(profile, homedir) {
|
|
79
|
+
const suffix = profile.toLowerCase() === "default" ? "" : `-${profile}`;
|
|
80
|
+
return path.join(homedir(), `.milady${suffix}`);
|
|
81
|
+
}
|
|
82
|
+
function applyCliProfileEnv(params) {
|
|
83
|
+
const env = params.env ?? process.env;
|
|
84
|
+
const homedir = params.homedir ?? os.homedir;
|
|
85
|
+
const profile = params.profile.trim();
|
|
86
|
+
if (!profile) return;
|
|
87
|
+
env.MILADY_PROFILE = profile;
|
|
88
|
+
const stateDir = env.MILADY_STATE_DIR?.trim() || resolveProfileStateDir(profile, homedir);
|
|
89
|
+
if (!env.MILADY_STATE_DIR?.trim()) env.MILADY_STATE_DIR = stateDir;
|
|
90
|
+
if (!env.MILADY_CONFIG_PATH?.trim()) env.MILADY_CONFIG_PATH = path.join(stateDir, "milady.json");
|
|
91
|
+
if (profile === "dev" && !env.MILADY_GATEWAY_PORT?.trim()) env.MILADY_GATEWAY_PORT = "19001";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
export { applyCliProfileEnv, parseCliProfileArgs };
|