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,163 @@
|
|
|
1
|
+
import { theme } from "../../terminal/theme.js";
|
|
2
|
+
import { formatDocsLink } from "../../terminal/links.js";
|
|
3
|
+
import { runCommandWithRuntime } from "../cli-utils.js";
|
|
4
|
+
|
|
5
|
+
//#region src/cli/program/register.tui.ts
|
|
6
|
+
const defaultRuntime = {
|
|
7
|
+
error: console.error,
|
|
8
|
+
exit: process.exit
|
|
9
|
+
};
|
|
10
|
+
function normalizeApiBaseUrl(value) {
|
|
11
|
+
return value.trim().replace(/\/+$/, "");
|
|
12
|
+
}
|
|
13
|
+
async function fetchJsonWithTimeout(url, timeoutMs = 1200, headers) {
|
|
14
|
+
const controller = new AbortController();
|
|
15
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
16
|
+
try {
|
|
17
|
+
const res = await fetch(url, {
|
|
18
|
+
method: "GET",
|
|
19
|
+
signal: controller.signal,
|
|
20
|
+
headers
|
|
21
|
+
});
|
|
22
|
+
let body = null;
|
|
23
|
+
try {
|
|
24
|
+
body = await res.json();
|
|
25
|
+
} catch {
|
|
26
|
+
body = null;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
ok: res.ok,
|
|
30
|
+
status: res.status,
|
|
31
|
+
body
|
|
32
|
+
};
|
|
33
|
+
} catch {
|
|
34
|
+
return {
|
|
35
|
+
ok: false,
|
|
36
|
+
status: 0,
|
|
37
|
+
body: null
|
|
38
|
+
};
|
|
39
|
+
} finally {
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function getMiladyApiProbeHeaders(includeAuth) {
|
|
44
|
+
if (!includeAuth) return void 0;
|
|
45
|
+
const token = process.env.MILADY_API_TOKEN?.trim();
|
|
46
|
+
if (!token) return void 0;
|
|
47
|
+
return { Authorization: `Bearer ${token}` };
|
|
48
|
+
}
|
|
49
|
+
async function probeMiladyApi(baseUrl, includeAuth = false) {
|
|
50
|
+
const headers = getMiladyApiProbeHeaders(includeAuth);
|
|
51
|
+
const statusRes = await fetchJsonWithTimeout(`${baseUrl}/api/status`, 1200, headers);
|
|
52
|
+
const reachable = statusRes.status !== 0 && statusRes.status !== 404;
|
|
53
|
+
if (!reachable) return {
|
|
54
|
+
baseUrl,
|
|
55
|
+
reachable: false,
|
|
56
|
+
runtimeState: null,
|
|
57
|
+
onboardingComplete: null,
|
|
58
|
+
pluginCount: null,
|
|
59
|
+
authDenied: false
|
|
60
|
+
};
|
|
61
|
+
const statusBody = statusRes.body && typeof statusRes.body === "object" ? statusRes.body : null;
|
|
62
|
+
const runtimeState = typeof statusBody?.state === "string" ? statusBody.state : null;
|
|
63
|
+
const conversationsRes = await fetchJsonWithTimeout(`${baseUrl}/api/conversations`, 1200, headers);
|
|
64
|
+
const onboardingRes = await fetchJsonWithTimeout(`${baseUrl}/api/onboarding/status`, 1200, headers);
|
|
65
|
+
const onboardingBody = onboardingRes.body && typeof onboardingRes.body === "object" ? onboardingRes.body : null;
|
|
66
|
+
const onboardingComplete = typeof onboardingBody?.complete === "boolean" ? onboardingBody.complete : null;
|
|
67
|
+
const pluginsRes = await fetchJsonWithTimeout(`${baseUrl}/api/plugins`, 1200, headers);
|
|
68
|
+
const pluginsBody = pluginsRes.body && typeof pluginsRes.body === "object" ? pluginsRes.body : null;
|
|
69
|
+
return {
|
|
70
|
+
baseUrl,
|
|
71
|
+
reachable,
|
|
72
|
+
runtimeState,
|
|
73
|
+
onboardingComplete,
|
|
74
|
+
pluginCount: Array.isArray(pluginsBody?.plugins) ? pluginsBody.plugins.length : null,
|
|
75
|
+
authDenied: [statusRes.status, conversationsRes.status].some((status) => status === 401 || status === 403)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function resolveTuiApiBaseUrl(cliValue) {
|
|
79
|
+
const explicit = cliValue?.trim();
|
|
80
|
+
if (explicit) return {
|
|
81
|
+
baseUrl: normalizeApiBaseUrl(explicit),
|
|
82
|
+
source: "cli",
|
|
83
|
+
reachableCandidateCount: 1
|
|
84
|
+
};
|
|
85
|
+
const envValue = process.env.MILADY_API_BASE_URL?.trim() || process.env.MILADY_API_BASE?.trim();
|
|
86
|
+
if (envValue) return {
|
|
87
|
+
baseUrl: normalizeApiBaseUrl(envValue),
|
|
88
|
+
source: "env",
|
|
89
|
+
reachableCandidateCount: 1
|
|
90
|
+
};
|
|
91
|
+
const candidates = [
|
|
92
|
+
process.env.MILADY_PORT?.trim() ? `http://127.0.0.1:${process.env.MILADY_PORT.trim()}` : null,
|
|
93
|
+
"http://127.0.0.1:31337",
|
|
94
|
+
"http://127.0.0.1:2138"
|
|
95
|
+
].filter((candidate) => Boolean(candidate));
|
|
96
|
+
const normalizedCandidates = [];
|
|
97
|
+
const seen = /* @__PURE__ */ new Set();
|
|
98
|
+
for (const candidate of candidates) {
|
|
99
|
+
const normalized = normalizeApiBaseUrl(candidate);
|
|
100
|
+
if (seen.has(normalized)) continue;
|
|
101
|
+
seen.add(normalized);
|
|
102
|
+
normalizedCandidates.push(normalized);
|
|
103
|
+
}
|
|
104
|
+
const probes = [];
|
|
105
|
+
for (const candidate of normalizedCandidates) probes.push(await probeMiladyApi(candidate, false));
|
|
106
|
+
const reachableCandidateCount = probes.filter((probe) => probe.reachable).length;
|
|
107
|
+
const ready = probes.find((probe) => probe.reachable && !probe.authDenied && probe.runtimeState === "running" && probe.onboardingComplete === true && (probe.pluginCount ?? 0) > 0);
|
|
108
|
+
if (ready) return {
|
|
109
|
+
baseUrl: ready.baseUrl,
|
|
110
|
+
source: "auto",
|
|
111
|
+
reachableCandidateCount
|
|
112
|
+
};
|
|
113
|
+
const onboarded = probes.find((probe) => probe.reachable && !probe.authDenied && probe.runtimeState === "running" && probe.onboardingComplete === true);
|
|
114
|
+
if (onboarded) return {
|
|
115
|
+
baseUrl: onboarded.baseUrl,
|
|
116
|
+
source: "auto",
|
|
117
|
+
reachableCandidateCount
|
|
118
|
+
};
|
|
119
|
+
const reachable = probes.find((probe) => probe.reachable);
|
|
120
|
+
if (reachable) return {
|
|
121
|
+
baseUrl: reachable.baseUrl,
|
|
122
|
+
source: "auto",
|
|
123
|
+
reachableCandidateCount
|
|
124
|
+
};
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
async function tuiAction(options) {
|
|
128
|
+
await runCommandWithRuntime(defaultRuntime, async () => {
|
|
129
|
+
const { launchTUI } = await import("../../tui/index.js");
|
|
130
|
+
if (options.localRuntime) {
|
|
131
|
+
const { bootElizaRuntime } = await import("../../runtime/eliza.js");
|
|
132
|
+
await launchTUI(await bootElizaRuntime({ requireConfig: true }), { modelOverride: options.model });
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const resolvedApi = await resolveTuiApiBaseUrl(options.apiBaseUrl);
|
|
136
|
+
if (!resolvedApi) throw new Error("No Milady API runtime detected. Start frontend/API first, pass --api-base-url, or use --local-runtime.");
|
|
137
|
+
const apiBaseUrl = resolvedApi.baseUrl;
|
|
138
|
+
const hasToken = Boolean(process.env.MILADY_API_TOKEN?.trim());
|
|
139
|
+
const includeAuthProbe = resolvedApi.source !== "auto" || resolvedApi.reachableCandidateCount === 1;
|
|
140
|
+
const suppressApiTokenForwarding = hasToken && resolvedApi.source === "auto" && resolvedApi.reachableCandidateCount > 1;
|
|
141
|
+
const probe = await probeMiladyApi(apiBaseUrl, includeAuthProbe);
|
|
142
|
+
if (!probe.reachable) throw new Error(`Could not reach Milady API runtime at ${apiBaseUrl}. Check port and network connectivity.`);
|
|
143
|
+
if (probe.authDenied) {
|
|
144
|
+
if (!hasToken) throw new Error(`Milady API runtime at ${apiBaseUrl} requires authentication. Set MILADY_API_TOKEN and retry.`);
|
|
145
|
+
if (suppressApiTokenForwarding) throw new Error(`Milady API runtime at ${apiBaseUrl} requires authentication, but multiple local API candidates were detected. For token safety, auto-discovery does not send MILADY_API_TOKEN to all ports. Re-run with --api-base-url ${apiBaseUrl} to opt in explicitly.`);
|
|
146
|
+
throw new Error(`Milady API runtime at ${apiBaseUrl} rejected MILADY_API_TOKEN (401/403). Verify token scope/value and retry.`);
|
|
147
|
+
}
|
|
148
|
+
if (probe.runtimeState !== "running") throw new Error(`Milady API runtime at ${apiBaseUrl} is not ready (state=${probe.runtimeState ?? "unknown"}). Wait for runtime startup to complete and resolve backend errors in frontend logs.`);
|
|
149
|
+
if (probe.onboardingComplete === false) throw new Error(`Milady API runtime at ${apiBaseUrl} is not onboarded yet (complete=false). Complete onboarding in the frontend for this runtime.`);
|
|
150
|
+
if (probe.pluginCount === 0) throw new Error(`Milady API runtime at ${apiBaseUrl} has no model/provider plugins loaded. Configure a provider in onboarding first.`);
|
|
151
|
+
await launchTUI(null, {
|
|
152
|
+
modelOverride: options.model,
|
|
153
|
+
apiBaseUrl,
|
|
154
|
+
apiToken: suppressApiTokenForwarding ? null : void 0
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function registerTuiCommand(program) {
|
|
159
|
+
program.command("tui", { isDefault: true }).description("Start Milady with the interactive TUI").option("-m, --model <model>", "Model to use (e.g. anthropic/claude-sonnet-4-20250514)").option("--api-base-url <url>", "API runtime base URL (default: env vars, then auto-detect 31337/2138)").option("--local-runtime", "Boot a standalone local runtime (advanced; API mode is default)").addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/tui", "docs.milady.ai/tui")}\n${theme.muted("Default mode:")} API runtime mode (shared with frontend).\n${theme.muted("API auth:")} Set MILADY_API_TOKEN when the API/websocket server requires auth.\n`).action(tuiAction);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
//#endregion
|
|
163
|
+
export { registerTuiCommand };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { CLI_VERSION } from "../version.js";
|
|
2
|
+
import { theme } from "../../terminal/theme.js";
|
|
3
|
+
|
|
4
|
+
//#region src/cli/program/register.update.ts
|
|
5
|
+
const ALL_CHANNELS = [
|
|
6
|
+
"stable",
|
|
7
|
+
"beta",
|
|
8
|
+
"nightly"
|
|
9
|
+
];
|
|
10
|
+
const CHANNEL_LABELS = {
|
|
11
|
+
stable: theme.success,
|
|
12
|
+
beta: theme.warn,
|
|
13
|
+
nightly: theme.accent
|
|
14
|
+
};
|
|
15
|
+
const CHANNEL_DESCRIPTIONS = {
|
|
16
|
+
stable: "Production-ready releases. Recommended for most users.",
|
|
17
|
+
beta: "Release candidates. May contain minor issues.",
|
|
18
|
+
nightly: "Latest development builds. May be unstable."
|
|
19
|
+
};
|
|
20
|
+
function channelLabel(ch) {
|
|
21
|
+
return CHANNEL_LABELS[ch](ch);
|
|
22
|
+
}
|
|
23
|
+
function parseChannelOrExit(raw) {
|
|
24
|
+
if (ALL_CHANNELS.includes(raw)) return raw;
|
|
25
|
+
console.error(theme.error(`Invalid channel "${raw}". Valid channels: ${ALL_CHANNELS.join(", ")}`));
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
async function updateAction(opts) {
|
|
29
|
+
const { loadMiladyConfig, saveMiladyConfig } = await import("../../config/config.js");
|
|
30
|
+
const { checkForUpdate, resolveChannel } = await import("../../services/update-checker.js");
|
|
31
|
+
const { detectInstallMethod, performUpdate } = await import("../../services/self-updater.js");
|
|
32
|
+
const config = loadMiladyConfig();
|
|
33
|
+
let newChannel;
|
|
34
|
+
if (opts.channel) {
|
|
35
|
+
newChannel = parseChannelOrExit(opts.channel);
|
|
36
|
+
const oldChannel = resolveChannel(config.update);
|
|
37
|
+
if (newChannel !== oldChannel) {
|
|
38
|
+
saveMiladyConfig({
|
|
39
|
+
...config,
|
|
40
|
+
update: {
|
|
41
|
+
...config.update,
|
|
42
|
+
channel: newChannel,
|
|
43
|
+
lastCheckAt: void 0,
|
|
44
|
+
lastCheckVersion: void 0
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
console.log(`\nRelease channel changed: ${channelLabel(oldChannel)} -> ${channelLabel(newChannel)}`);
|
|
48
|
+
console.log(theme.muted(` ${CHANNEL_DESCRIPTIONS[newChannel]}\n`));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const effectiveChannel = newChannel ?? resolveChannel(config.update);
|
|
52
|
+
console.log(`\n${theme.heading("Milady Update")} ${theme.muted(`(channel: ${effectiveChannel})`)}`);
|
|
53
|
+
console.log(theme.muted(`Current version: ${CLI_VERSION}\n`));
|
|
54
|
+
console.log("Checking for updates...\n");
|
|
55
|
+
const result = await checkForUpdate({ force: opts.force ?? !!newChannel });
|
|
56
|
+
if (result.error) {
|
|
57
|
+
console.error(theme.warn(` ${result.error}\n`));
|
|
58
|
+
if (!opts.check) process.exit(1);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (!result.updateAvailable) {
|
|
62
|
+
console.log(theme.success(` Already up to date! (${CLI_VERSION} is the latest on ${effectiveChannel})\n`));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
console.log(` ${theme.accent("Update available:")} ${CLI_VERSION} -> ${theme.success(result.latestVersion ?? "unknown")}`);
|
|
66
|
+
console.log(theme.muted(` Channel: ${effectiveChannel} | dist-tag: ${result.distTag}\n`));
|
|
67
|
+
if (opts.check) {
|
|
68
|
+
console.log(theme.muted(" Run `milady update` to install the update.\n"));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const method = detectInstallMethod();
|
|
72
|
+
if (method === "local-dev") {
|
|
73
|
+
console.log(theme.warn(" Local development install detected. Use `git pull` to update.\n"));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
console.log(theme.muted(` Install method: ${method}`));
|
|
77
|
+
console.log(" Installing update...\n");
|
|
78
|
+
const updateResult = await performUpdate(CLI_VERSION, effectiveChannel, method);
|
|
79
|
+
if (!updateResult.success) {
|
|
80
|
+
console.error(theme.error(`\n Update failed: ${updateResult.error}\n`));
|
|
81
|
+
console.log(theme.muted(` Command: ${updateResult.command}\n You can try running it manually.\n`));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
if (updateResult.newVersion) console.log(theme.success(`\n Updated successfully! ${CLI_VERSION} -> ${updateResult.newVersion}`));
|
|
85
|
+
else {
|
|
86
|
+
console.log(theme.success("\n Update command completed successfully."));
|
|
87
|
+
console.log(theme.warn(` Could not verify the new version. Expected: ${result.latestVersion ?? "unknown"}`));
|
|
88
|
+
}
|
|
89
|
+
console.log(theme.muted(" Restart milady for the new version to take effect.\n"));
|
|
90
|
+
}
|
|
91
|
+
async function statusAction() {
|
|
92
|
+
const { loadMiladyConfig } = await import("../../config/config.js");
|
|
93
|
+
const { resolveChannel, fetchAllChannelVersions } = await import("../../services/update-checker.js");
|
|
94
|
+
const { detectInstallMethod } = await import("../../services/self-updater.js");
|
|
95
|
+
console.log(`\n${theme.heading("Version Status")}\n`);
|
|
96
|
+
const config = loadMiladyConfig();
|
|
97
|
+
const channel = resolveChannel(config.update);
|
|
98
|
+
console.log(` Installed: ${theme.accent(CLI_VERSION)}`);
|
|
99
|
+
console.log(` Channel: ${channelLabel(channel)}`);
|
|
100
|
+
console.log(` Install: ${theme.muted(detectInstallMethod())}`);
|
|
101
|
+
console.log(`\n${theme.heading("Available Versions")}\n`);
|
|
102
|
+
console.log(" Fetching from npm registry...\n");
|
|
103
|
+
const versions = await fetchAllChannelVersions();
|
|
104
|
+
for (const ch of ALL_CHANNELS) {
|
|
105
|
+
const ver = versions[ch] ?? theme.muted("(not published)");
|
|
106
|
+
const marker = ch === channel ? theme.accent(" <-- current") : "";
|
|
107
|
+
console.log(` ${channelLabel(ch).padEnd(22)} ${ver}${marker}`);
|
|
108
|
+
}
|
|
109
|
+
if (config.update?.lastCheckAt) console.log(`\n ${theme.muted(`Last checked: ${new Date(config.update.lastCheckAt).toLocaleString()}`)}`);
|
|
110
|
+
console.log();
|
|
111
|
+
}
|
|
112
|
+
async function channelAction(channelArg) {
|
|
113
|
+
const { loadMiladyConfig, saveMiladyConfig } = await import("../../config/config.js");
|
|
114
|
+
const { resolveChannel } = await import("../../services/update-checker.js");
|
|
115
|
+
const config = loadMiladyConfig();
|
|
116
|
+
const current = resolveChannel(config.update);
|
|
117
|
+
if (!channelArg) {
|
|
118
|
+
console.log(`\n${theme.heading("Release Channel")}\n`);
|
|
119
|
+
console.log(` Current: ${channelLabel(current)}`);
|
|
120
|
+
console.log(theme.muted(` ${CHANNEL_DESCRIPTIONS[current]}\n`));
|
|
121
|
+
console.log(" Available channels:");
|
|
122
|
+
for (const ch of ALL_CHANNELS) {
|
|
123
|
+
const marker = ch === current ? theme.accent(" (active)") : "";
|
|
124
|
+
console.log(` ${channelLabel(ch)}${marker} ${theme.muted(CHANNEL_DESCRIPTIONS[ch])}`);
|
|
125
|
+
}
|
|
126
|
+
console.log(`\n ${theme.muted("Switch with: milady update channel <stable|beta|nightly>")}\n`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const newChannel = parseChannelOrExit(channelArg);
|
|
130
|
+
if (newChannel === current) {
|
|
131
|
+
console.log(`\n Already on ${channelLabel(current)} channel. No change needed.\n`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
saveMiladyConfig({
|
|
135
|
+
...config,
|
|
136
|
+
update: {
|
|
137
|
+
...config.update,
|
|
138
|
+
channel: newChannel,
|
|
139
|
+
lastCheckAt: void 0,
|
|
140
|
+
lastCheckVersion: void 0
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
console.log(`\n Channel changed: ${channelLabel(current)} -> ${channelLabel(newChannel)}`);
|
|
144
|
+
console.log(theme.muted(` ${CHANNEL_DESCRIPTIONS[newChannel]}`));
|
|
145
|
+
console.log(`\n ${theme.muted("Run `milady update` to fetch the latest version from this channel.")}\n`);
|
|
146
|
+
}
|
|
147
|
+
function registerUpdateCommand(program) {
|
|
148
|
+
const updateCmd = program.command("update").description("Check for and install updates").option("-c, --channel <channel>", "Switch release channel (stable, beta, nightly)").option("--check", "Check for updates without installing").option("--force", "Force update check (bypass interval cache)").action(updateAction);
|
|
149
|
+
updateCmd.command("status").description("Show current version and available updates across all channels").action(statusAction);
|
|
150
|
+
updateCmd.command("channel [channel]").description("View or change the release channel").action(channelAction);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//#endregion
|
|
154
|
+
export { registerUpdateCommand };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getPrimaryCommand, hasHelpOrVersion } from "./argv.js";
|
|
2
|
+
import { registerSubCliByName } from "./program/register.subclis.js";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
|
|
5
|
+
//#region src/cli/run-main.ts
|
|
6
|
+
async function loadDotEnv() {
|
|
7
|
+
try {
|
|
8
|
+
const { config } = await import("dotenv");
|
|
9
|
+
config({ quiet: true });
|
|
10
|
+
} catch (err) {
|
|
11
|
+
if (err.code !== "MODULE_NOT_FOUND" && err.code !== "ERR_MODULE_NOT_FOUND") throw err;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function formatUncaughtError(error) {
|
|
15
|
+
if (error instanceof Error) return error.stack ?? error.message;
|
|
16
|
+
return String(error);
|
|
17
|
+
}
|
|
18
|
+
async function runCli(argv = process.argv) {
|
|
19
|
+
await loadDotEnv();
|
|
20
|
+
if (!process.env.ZAI_API_KEY?.trim() && process.env.Z_AI_API_KEY?.trim()) process.env.ZAI_API_KEY = process.env.Z_AI_API_KEY;
|
|
21
|
+
const { buildProgram } = await import("./program.js");
|
|
22
|
+
const program = buildProgram();
|
|
23
|
+
process.on("unhandledRejection", (reason) => {
|
|
24
|
+
console.error("[milady] Unhandled rejection:", formatUncaughtError(reason));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
});
|
|
27
|
+
process.on("uncaughtException", (error) => {
|
|
28
|
+
console.error("[milady] Uncaught exception:", formatUncaughtError(error));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
});
|
|
31
|
+
const primary = getPrimaryCommand(argv);
|
|
32
|
+
if (primary && !hasHelpOrVersion(argv)) await registerSubCliByName(program, primary);
|
|
33
|
+
await program.parseAsync(argv);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
export { runCli };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { normalizeHostLike, normalizeIpForPolicy } from "../security/network-policy.js";
|
|
2
|
+
import net from "node:net";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import dns from "node:dns";
|
|
5
|
+
|
|
6
|
+
//#region src/cloud/validate-url.ts
|
|
7
|
+
/**
|
|
8
|
+
* Cloud base URL validation to prevent SSRF.
|
|
9
|
+
*
|
|
10
|
+
* Enforces HTTPS and blocks base URLs that resolve to internal/metadata ranges.
|
|
11
|
+
*/
|
|
12
|
+
const dnsLookupAll = promisify(dns.lookup);
|
|
13
|
+
const BLOCKED_IPV4_CIDRS = [
|
|
14
|
+
cidrV4("0.0.0.0", 8),
|
|
15
|
+
cidrV4("10.0.0.0", 8),
|
|
16
|
+
cidrV4("172.16.0.0", 12),
|
|
17
|
+
cidrV4("192.168.0.0", 16),
|
|
18
|
+
cidrV4("100.64.0.0", 10),
|
|
19
|
+
cidrV4("127.0.0.0", 8),
|
|
20
|
+
cidrV4("169.254.0.0", 16),
|
|
21
|
+
cidrV4("192.0.0.0", 24),
|
|
22
|
+
cidrV4("198.18.0.0", 15),
|
|
23
|
+
cidrV4("192.0.2.0", 24),
|
|
24
|
+
cidrV4("198.51.100.0", 24),
|
|
25
|
+
cidrV4("203.0.113.0", 24),
|
|
26
|
+
cidrV4("224.0.0.0", 4),
|
|
27
|
+
cidrV4("240.0.0.0", 4)
|
|
28
|
+
];
|
|
29
|
+
function cidrV4(base, prefix) {
|
|
30
|
+
const parsed = parseIpv4ToInt(base);
|
|
31
|
+
if (parsed === null) throw new Error(`Invalid CIDR base IPv4 address: ${base}`);
|
|
32
|
+
const shift = 32 - prefix;
|
|
33
|
+
const mask = shift === 32 ? 0 : 4294967295 << shift >>> 0;
|
|
34
|
+
return {
|
|
35
|
+
base: parsed & mask,
|
|
36
|
+
mask
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function parseIpv4ToInt(ip) {
|
|
40
|
+
const parts = ip.split(".");
|
|
41
|
+
if (parts.length !== 4) return null;
|
|
42
|
+
let value = 0;
|
|
43
|
+
for (const part of parts) {
|
|
44
|
+
if (!/^\d{1,3}$/.test(part)) return null;
|
|
45
|
+
const octet = Number.parseInt(part, 10);
|
|
46
|
+
if (!Number.isInteger(octet) || octet < 0 || octet > 255) return null;
|
|
47
|
+
value = value << 8 | octet;
|
|
48
|
+
}
|
|
49
|
+
return value >>> 0;
|
|
50
|
+
}
|
|
51
|
+
function isBlockedIpv4(ip) {
|
|
52
|
+
const asInt = parseIpv4ToInt(ip);
|
|
53
|
+
if (asInt === null) return true;
|
|
54
|
+
return BLOCKED_IPV4_CIDRS.some((cidr) => (asInt & cidr.mask) === cidr.base);
|
|
55
|
+
}
|
|
56
|
+
function isBlockedIpv6(ip) {
|
|
57
|
+
const normalized = ip.toLowerCase();
|
|
58
|
+
return normalized === "::" || normalized === "::1" || /^fe[89ab][0-9a-f]:/.test(normalized) || /^f[cd][0-9a-f]{2}:/i.test(normalized) || normalized.startsWith("ff");
|
|
59
|
+
}
|
|
60
|
+
function isBlockedIp(ip) {
|
|
61
|
+
const normalized = normalizeIpForPolicy(ip);
|
|
62
|
+
const family = net.isIP(normalized);
|
|
63
|
+
if (family === 4) return isBlockedIpv4(normalized);
|
|
64
|
+
if (family === 6) return isBlockedIpv6(normalized);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
async function validateCloudBaseUrl(rawUrl) {
|
|
68
|
+
let parsed;
|
|
69
|
+
try {
|
|
70
|
+
parsed = new URL(rawUrl);
|
|
71
|
+
} catch {
|
|
72
|
+
return `Invalid cloud base URL: "${rawUrl}"`;
|
|
73
|
+
}
|
|
74
|
+
if (parsed.protocol !== "https:") return `Cloud base URL must use HTTPS, got "${parsed.protocol}" in "${rawUrl}"`;
|
|
75
|
+
const hostname = normalizeHostLike(parsed.hostname);
|
|
76
|
+
if (!hostname) return `Invalid cloud base URL: "${rawUrl}"`;
|
|
77
|
+
if (hostname === "localhost" || hostname.endsWith(".localhost") || hostname.endsWith(".local")) return `Cloud base URL "${rawUrl}" points to a blocked local hostname.`;
|
|
78
|
+
if (isBlockedIp(hostname)) return `Cloud base URL "${rawUrl}" points to a blocked address.`;
|
|
79
|
+
try {
|
|
80
|
+
const results = await dnsLookupAll(hostname, { all: true });
|
|
81
|
+
const addresses = Array.isArray(results) ? results : [results];
|
|
82
|
+
for (const entry of addresses) {
|
|
83
|
+
const ip = typeof entry === "string" ? entry : entry.address;
|
|
84
|
+
if (isBlockedIp(ip)) return `Cloud base URL "${rawUrl}" resolves to ${ip}, which is a blocked internal/metadata address.`;
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
return `Cloud base URL "${rawUrl}" could not be resolved via DNS.`;
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
export { validateCloudBaseUrl };
|