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,297 @@
|
|
|
1
|
+
import { createEngine, detectBestEngine } from "./sandbox-engine.js";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { mkdirSync } from "node:fs";
|
|
5
|
+
|
|
6
|
+
//#region src/services/sandbox-manager.ts
|
|
7
|
+
/** Sandbox container lifecycle: create, exec, health check, teardown. */
|
|
8
|
+
var SandboxManager = class {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.state = "uninitialized";
|
|
11
|
+
this.containerId = null;
|
|
12
|
+
this.browserContainerId = null;
|
|
13
|
+
this.eventLog = [];
|
|
14
|
+
this.config = {
|
|
15
|
+
image: "milady-sandbox:bookworm-slim",
|
|
16
|
+
containerPrefix: "milady-sandbox",
|
|
17
|
+
workdir: "/workspace",
|
|
18
|
+
network: "none",
|
|
19
|
+
user: "1000:1000",
|
|
20
|
+
capDrop: ["ALL"],
|
|
21
|
+
memory: "512m",
|
|
22
|
+
cpus: 1,
|
|
23
|
+
pidsLimit: 256,
|
|
24
|
+
...config
|
|
25
|
+
};
|
|
26
|
+
this.engine = config.engineType ? createEngine(config.engineType) : detectBestEngine();
|
|
27
|
+
}
|
|
28
|
+
getState() {
|
|
29
|
+
return this.state;
|
|
30
|
+
}
|
|
31
|
+
getMode() {
|
|
32
|
+
return this.config.mode;
|
|
33
|
+
}
|
|
34
|
+
isReady() {
|
|
35
|
+
return this.state === "ready";
|
|
36
|
+
}
|
|
37
|
+
getMainContainerConfig() {
|
|
38
|
+
const image = this.config.image ?? "milady-sandbox:bookworm-slim";
|
|
39
|
+
const containerPrefix = this.config.containerPrefix ?? "milady-sandbox";
|
|
40
|
+
const workdir = this.config.workdir ?? "/workspace";
|
|
41
|
+
const network = this.config.network ?? "none";
|
|
42
|
+
const user = this.config.user ?? "1000:1000";
|
|
43
|
+
const wsRoot = this.config.workspaceRoot ?? join(process.env.HOME ?? process.env.USERPROFILE ?? os.tmpdir(), ".milady", "sandbox-workspace");
|
|
44
|
+
mkdirSync(wsRoot, { recursive: true });
|
|
45
|
+
return {
|
|
46
|
+
image,
|
|
47
|
+
containerPrefix,
|
|
48
|
+
workdir,
|
|
49
|
+
network,
|
|
50
|
+
user,
|
|
51
|
+
wsRoot
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async createMainContainer() {
|
|
55
|
+
const config = this.getMainContainerConfig();
|
|
56
|
+
return this.engine.runContainer({
|
|
57
|
+
image: config.image,
|
|
58
|
+
name: `${config.containerPrefix}-${Date.now()}`,
|
|
59
|
+
detach: true,
|
|
60
|
+
mounts: [{
|
|
61
|
+
host: config.wsRoot,
|
|
62
|
+
container: config.workdir,
|
|
63
|
+
readonly: false
|
|
64
|
+
}],
|
|
65
|
+
env: this.config.env ?? {},
|
|
66
|
+
network: config.network,
|
|
67
|
+
user: config.user,
|
|
68
|
+
capDrop: this.config.capDrop ?? [],
|
|
69
|
+
memory: this.config.memory,
|
|
70
|
+
cpus: this.config.cpus,
|
|
71
|
+
pidsLimit: this.config.pidsLimit,
|
|
72
|
+
readOnlyRoot: this.config.readOnlyRoot,
|
|
73
|
+
dns: this.config.dns
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async cleanupContainer(containerId) {
|
|
77
|
+
if (!containerId) return;
|
|
78
|
+
await this.engine.stopContainer(containerId);
|
|
79
|
+
await this.engine.removeContainer(containerId);
|
|
80
|
+
}
|
|
81
|
+
setState(newState) {
|
|
82
|
+
const oldState = this.state;
|
|
83
|
+
this.state = newState;
|
|
84
|
+
this.emitEvent({
|
|
85
|
+
timestamp: Date.now(),
|
|
86
|
+
type: "state_change",
|
|
87
|
+
detail: `${oldState} → ${newState}`
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async start() {
|
|
91
|
+
if (this.config.mode === "off") {
|
|
92
|
+
this.setState("stopped");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (this.config.mode === "light") {
|
|
96
|
+
this.setState("ready");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
this.setState("initializing");
|
|
100
|
+
try {
|
|
101
|
+
const config = this.getMainContainerConfig();
|
|
102
|
+
if (!this.engine.isAvailable()) throw new Error(`Container engine "${this.engine.engineType}" is not available. Install Docker or Apple Container.`);
|
|
103
|
+
if (!this.engine.imageExists(config.image)) try {
|
|
104
|
+
await this.engine.pullImage(config.image);
|
|
105
|
+
} catch {
|
|
106
|
+
throw new Error(`Sandbox image "${config.image}" not found. Build with: scripts/sandbox-setup.sh`);
|
|
107
|
+
}
|
|
108
|
+
const orphans = this.engine.listContainers(config.containerPrefix);
|
|
109
|
+
for (const id of orphans) {
|
|
110
|
+
await this.engine.stopContainer(id);
|
|
111
|
+
await this.engine.removeContainer(id);
|
|
112
|
+
}
|
|
113
|
+
this.containerId = await this.createMainContainer();
|
|
114
|
+
this.emitEvent({
|
|
115
|
+
timestamp: Date.now(),
|
|
116
|
+
type: "container_start",
|
|
117
|
+
detail: `Container started: ${this.containerId}`
|
|
118
|
+
});
|
|
119
|
+
if (this.config.browser?.enabled && this.config.browser?.autoStart) try {
|
|
120
|
+
this.browserContainerId = await this.createBrowserContainer();
|
|
121
|
+
} catch (err) {
|
|
122
|
+
this.emitEvent({
|
|
123
|
+
timestamp: Date.now(),
|
|
124
|
+
type: "error",
|
|
125
|
+
detail: `Browser container start failed: ${err instanceof Error ? err.message : String(err)}`
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (await this.healthCheck()) this.setState("ready");
|
|
129
|
+
else this.setState("degraded");
|
|
130
|
+
} catch (err) {
|
|
131
|
+
this.emitEvent({
|
|
132
|
+
timestamp: Date.now(),
|
|
133
|
+
type: "error",
|
|
134
|
+
detail: `Sandbox start failed: ${err instanceof Error ? err.message : String(err)}`
|
|
135
|
+
});
|
|
136
|
+
this.setState("degraded");
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async recover() {
|
|
141
|
+
if (this.state !== "degraded") return;
|
|
142
|
+
this.setState("recovering");
|
|
143
|
+
this.emitEvent({
|
|
144
|
+
timestamp: Date.now(),
|
|
145
|
+
type: "state_change",
|
|
146
|
+
detail: "Attempting recovery from degraded state"
|
|
147
|
+
});
|
|
148
|
+
try {
|
|
149
|
+
const config = this.getMainContainerConfig();
|
|
150
|
+
await this.cleanupContainer(this.containerId);
|
|
151
|
+
await this.cleanupContainer(this.browserContainerId);
|
|
152
|
+
this.containerId = null;
|
|
153
|
+
this.browserContainerId = null;
|
|
154
|
+
const orphans = this.engine.listContainers(config.containerPrefix);
|
|
155
|
+
for (const id of orphans) {
|
|
156
|
+
await this.engine.stopContainer(id);
|
|
157
|
+
await this.engine.removeContainer(id);
|
|
158
|
+
}
|
|
159
|
+
this.containerId = await this.createMainContainer();
|
|
160
|
+
if (await this.healthCheck()) this.setState("ready");
|
|
161
|
+
else this.setState("degraded");
|
|
162
|
+
} catch (err) {
|
|
163
|
+
this.emitEvent({
|
|
164
|
+
timestamp: Date.now(),
|
|
165
|
+
type: "error",
|
|
166
|
+
detail: `Recovery failed: ${err instanceof Error ? err.message : String(err)}`
|
|
167
|
+
});
|
|
168
|
+
this.setState("degraded");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async stop() {
|
|
172
|
+
this.setState("stopping");
|
|
173
|
+
try {
|
|
174
|
+
await this.cleanupContainer(this.browserContainerId);
|
|
175
|
+
await this.cleanupContainer(this.containerId);
|
|
176
|
+
this.browserContainerId = null;
|
|
177
|
+
this.containerId = null;
|
|
178
|
+
} catch (err) {
|
|
179
|
+
this.emitEvent({
|
|
180
|
+
timestamp: Date.now(),
|
|
181
|
+
type: "error",
|
|
182
|
+
detail: `Sandbox stop error: ${err instanceof Error ? err.message : String(err)}`
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
this.setState("stopped");
|
|
186
|
+
}
|
|
187
|
+
async exec(options) {
|
|
188
|
+
const start = Date.now();
|
|
189
|
+
if (this.config.mode === "off" || this.config.mode === "light") return {
|
|
190
|
+
exitCode: 1,
|
|
191
|
+
stdout: "",
|
|
192
|
+
stderr: "Sandbox exec not available in current mode",
|
|
193
|
+
durationMs: Date.now() - start,
|
|
194
|
+
executedInSandbox: false
|
|
195
|
+
};
|
|
196
|
+
if (!this.containerId || this.state !== "ready") {
|
|
197
|
+
this.emitEvent({
|
|
198
|
+
timestamp: Date.now(),
|
|
199
|
+
type: "exec_denied",
|
|
200
|
+
detail: `Sandbox not ready (state=${this.state})`
|
|
201
|
+
});
|
|
202
|
+
return {
|
|
203
|
+
exitCode: 1,
|
|
204
|
+
stdout: "",
|
|
205
|
+
stderr: `Sandbox not ready (state=${this.state})`,
|
|
206
|
+
durationMs: Date.now() - start,
|
|
207
|
+
executedInSandbox: false
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
this.emitEvent({
|
|
211
|
+
timestamp: Date.now(),
|
|
212
|
+
type: "exec",
|
|
213
|
+
detail: options.command.substring(0, 200),
|
|
214
|
+
metadata: { workdir: options.workdir ?? this.config.workdir ?? "/workspace" }
|
|
215
|
+
});
|
|
216
|
+
try {
|
|
217
|
+
return {
|
|
218
|
+
...await this.engine.execInContainer({
|
|
219
|
+
containerId: this.containerId,
|
|
220
|
+
command: options.command,
|
|
221
|
+
workdir: options.workdir,
|
|
222
|
+
env: options.env,
|
|
223
|
+
timeoutMs: options.timeoutMs,
|
|
224
|
+
stdin: options.stdin
|
|
225
|
+
}),
|
|
226
|
+
executedInSandbox: true
|
|
227
|
+
};
|
|
228
|
+
} catch (err) {
|
|
229
|
+
return {
|
|
230
|
+
exitCode: 1,
|
|
231
|
+
stdout: "",
|
|
232
|
+
stderr: `Exec error: ${err instanceof Error ? err.message : String(err)}`,
|
|
233
|
+
durationMs: Date.now() - start,
|
|
234
|
+
executedInSandbox: false
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
getBrowserCdpEndpoint() {
|
|
239
|
+
if (!this.browserContainerId) return null;
|
|
240
|
+
return `http://localhost:${this.config.browser?.cdpPort ?? 9222}`;
|
|
241
|
+
}
|
|
242
|
+
getBrowserWsEndpoint() {
|
|
243
|
+
if (!this.browserContainerId) return null;
|
|
244
|
+
return `ws://localhost:${this.config.browser?.cdpPort ?? 9222}`;
|
|
245
|
+
}
|
|
246
|
+
async createBrowserContainer() {
|
|
247
|
+
const name = `${this.config.containerPrefix}-browser-${Date.now()}`;
|
|
248
|
+
const cdpPort = this.config.browser?.cdpPort ?? 9222;
|
|
249
|
+
const vncPort = this.config.browser?.vncPort ?? 5900;
|
|
250
|
+
const image = this.config.browser?.image ?? "milady-sandbox-browser:bookworm-slim";
|
|
251
|
+
return this.engine.runContainer({
|
|
252
|
+
image,
|
|
253
|
+
name,
|
|
254
|
+
detach: true,
|
|
255
|
+
mounts: [],
|
|
256
|
+
env: {},
|
|
257
|
+
network: "bridge",
|
|
258
|
+
user: "1000:1000",
|
|
259
|
+
capDrop: [],
|
|
260
|
+
ports: [{
|
|
261
|
+
host: cdpPort,
|
|
262
|
+
container: 9222
|
|
263
|
+
}, {
|
|
264
|
+
host: vncPort,
|
|
265
|
+
container: 5900
|
|
266
|
+
}]
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
async healthCheck() {
|
|
270
|
+
if (!this.containerId) return false;
|
|
271
|
+
const healthy = await this.engine.healthCheck(this.containerId);
|
|
272
|
+
this.emitEvent({
|
|
273
|
+
timestamp: Date.now(),
|
|
274
|
+
type: "health_check",
|
|
275
|
+
detail: healthy ? "healthy" : "unhealthy"
|
|
276
|
+
});
|
|
277
|
+
return healthy;
|
|
278
|
+
}
|
|
279
|
+
emitEvent(event) {
|
|
280
|
+
this.eventLog.push(event);
|
|
281
|
+
if (this.eventLog.length > 1e3) this.eventLog = this.eventLog.slice(-500);
|
|
282
|
+
}
|
|
283
|
+
getEventLog() {
|
|
284
|
+
return [...this.eventLog];
|
|
285
|
+
}
|
|
286
|
+
getStatus() {
|
|
287
|
+
return {
|
|
288
|
+
state: this.state,
|
|
289
|
+
mode: this.config.mode,
|
|
290
|
+
containerId: this.containerId,
|
|
291
|
+
browserContainerId: this.browserContainerId
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
//#endregion
|
|
297
|
+
export { SandboxManager };
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { CHANNEL_DIST_TAGS } from "./update-checker.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { execSync, spawn } from "node:child_process";
|
|
6
|
+
|
|
7
|
+
//#region src/services/self-updater.ts
|
|
8
|
+
/**
|
|
9
|
+
* Detects the installation method and runs the appropriate upgrade command.
|
|
10
|
+
* Falls back to npm if detection is ambiguous.
|
|
11
|
+
*/
|
|
12
|
+
const NPM_PACKAGE_NAME = "miladyai";
|
|
13
|
+
function whichSync(binary) {
|
|
14
|
+
try {
|
|
15
|
+
return execSync(`which ${binary}`, {
|
|
16
|
+
stdio: [
|
|
17
|
+
"ignore",
|
|
18
|
+
"pipe",
|
|
19
|
+
"ignore"
|
|
20
|
+
],
|
|
21
|
+
timeout: 5e3
|
|
22
|
+
}).toString().trim();
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function isLocalDev() {
|
|
28
|
+
try {
|
|
29
|
+
const rootPkg = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../package.json");
|
|
30
|
+
return JSON.parse(fs.readFileSync(rootPkg, "utf-8")).devDependencies !== void 0;
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function detectInstallMethod() {
|
|
36
|
+
const miladyBin = whichSync("milady");
|
|
37
|
+
if (!miladyBin) return isLocalDev() ? "local-dev" : "unknown";
|
|
38
|
+
let resolved;
|
|
39
|
+
try {
|
|
40
|
+
resolved = fs.realpathSync(miladyBin);
|
|
41
|
+
} catch {
|
|
42
|
+
resolved = miladyBin;
|
|
43
|
+
}
|
|
44
|
+
if (resolved.includes("/Cellar/") || resolved.includes("/homebrew/")) return "homebrew";
|
|
45
|
+
if (resolved.includes("/snap/")) return "snap";
|
|
46
|
+
if (resolved.includes("/flatpak/") || resolved.includes("ai.milady.Milady")) return "flatpak";
|
|
47
|
+
if (resolved.startsWith("/usr/") && !resolved.includes("node_modules")) return "apt";
|
|
48
|
+
if (resolved.includes("/.bun/")) return "bun-global";
|
|
49
|
+
if (resolved.includes("node_modules")) return "npm-global";
|
|
50
|
+
return "unknown";
|
|
51
|
+
}
|
|
52
|
+
function buildUpdateCommand(method, channel) {
|
|
53
|
+
const spec = `${NPM_PACKAGE_NAME}@${CHANNEL_DIST_TAGS[channel]}`;
|
|
54
|
+
switch (method) {
|
|
55
|
+
case "npm-global": return {
|
|
56
|
+
command: "npm",
|
|
57
|
+
args: [
|
|
58
|
+
"install",
|
|
59
|
+
"-g",
|
|
60
|
+
spec
|
|
61
|
+
]
|
|
62
|
+
};
|
|
63
|
+
case "bun-global": return {
|
|
64
|
+
command: "bun",
|
|
65
|
+
args: [
|
|
66
|
+
"install",
|
|
67
|
+
"-g",
|
|
68
|
+
spec
|
|
69
|
+
]
|
|
70
|
+
};
|
|
71
|
+
case "homebrew": return {
|
|
72
|
+
command: "brew",
|
|
73
|
+
args: ["upgrade", "milady"]
|
|
74
|
+
};
|
|
75
|
+
case "snap": return {
|
|
76
|
+
command: "sudo",
|
|
77
|
+
args: [
|
|
78
|
+
"snap",
|
|
79
|
+
"refresh",
|
|
80
|
+
"milady",
|
|
81
|
+
`--channel=${channel === "nightly" ? "edge" : channel === "beta" ? "beta" : "stable"}`
|
|
82
|
+
]
|
|
83
|
+
};
|
|
84
|
+
case "apt": return {
|
|
85
|
+
command: "sh",
|
|
86
|
+
args: ["-c", "sudo apt-get update && sudo apt-get install --only-upgrade -y milady"]
|
|
87
|
+
};
|
|
88
|
+
case "flatpak": return {
|
|
89
|
+
command: "flatpak",
|
|
90
|
+
args: ["update", "ai.milady.Milady"]
|
|
91
|
+
};
|
|
92
|
+
case "local-dev": return null;
|
|
93
|
+
case "unknown": return {
|
|
94
|
+
command: "npm",
|
|
95
|
+
args: [
|
|
96
|
+
"install",
|
|
97
|
+
"-g",
|
|
98
|
+
spec
|
|
99
|
+
]
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function runCommand(command, args) {
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
const child = spawn(command, args, { stdio: [
|
|
106
|
+
"inherit",
|
|
107
|
+
"inherit",
|
|
108
|
+
"pipe"
|
|
109
|
+
] });
|
|
110
|
+
let stderr = "";
|
|
111
|
+
child.stderr?.on("data", (chunk) => {
|
|
112
|
+
stderr += chunk.toString();
|
|
113
|
+
process.stderr.write(chunk);
|
|
114
|
+
});
|
|
115
|
+
child.on("error", (err) => {
|
|
116
|
+
resolve({
|
|
117
|
+
exitCode: 1,
|
|
118
|
+
stderr: err.message
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
child.on("close", (code) => {
|
|
122
|
+
resolve({
|
|
123
|
+
exitCode: code ?? 1,
|
|
124
|
+
stderr
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function readPostUpdateVersion() {
|
|
130
|
+
try {
|
|
131
|
+
return execSync("milady --version", {
|
|
132
|
+
stdio: [
|
|
133
|
+
"ignore",
|
|
134
|
+
"pipe",
|
|
135
|
+
"ignore"
|
|
136
|
+
],
|
|
137
|
+
timeout: 1e4
|
|
138
|
+
}).toString().trim().match(/(\d+\.\d+\.\d+(?:-[\w.]+)?)/)?.[1] ?? null;
|
|
139
|
+
} catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async function performUpdate(currentVersion, channel, method) {
|
|
144
|
+
method ??= detectInstallMethod();
|
|
145
|
+
const cmdInfo = buildUpdateCommand(method, channel);
|
|
146
|
+
if (!cmdInfo) return {
|
|
147
|
+
success: false,
|
|
148
|
+
method,
|
|
149
|
+
command: "",
|
|
150
|
+
previousVersion: currentVersion,
|
|
151
|
+
newVersion: null,
|
|
152
|
+
error: "Cannot auto-update a local development install. Use git pull instead."
|
|
153
|
+
};
|
|
154
|
+
const commandString = `${cmdInfo.command} ${cmdInfo.args.join(" ")}`;
|
|
155
|
+
const { exitCode, stderr } = await runCommand(cmdInfo.command, cmdInfo.args);
|
|
156
|
+
if (exitCode !== 0) return {
|
|
157
|
+
success: false,
|
|
158
|
+
method,
|
|
159
|
+
command: commandString,
|
|
160
|
+
previousVersion: currentVersion,
|
|
161
|
+
newVersion: null,
|
|
162
|
+
error: stderr || `Update command exited with code ${exitCode}.`
|
|
163
|
+
};
|
|
164
|
+
return {
|
|
165
|
+
success: true,
|
|
166
|
+
method,
|
|
167
|
+
command: commandString,
|
|
168
|
+
previousVersion: currentVersion,
|
|
169
|
+
newVersion: readPostUpdateVersion(),
|
|
170
|
+
error: null
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
//#endregion
|
|
175
|
+
export { detectInstallMethod, performUpdate };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { logger } from "@elizaos/core";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
|
|
6
|
+
//#region src/services/skill-catalog-client.ts
|
|
7
|
+
/**
|
|
8
|
+
* Skill Catalog Client for Milady.
|
|
9
|
+
*
|
|
10
|
+
* Provides a cached skill catalog (memory → file) sourced from the
|
|
11
|
+
* local skills/.cache/catalog.json. Supports search and browse.
|
|
12
|
+
*
|
|
13
|
+
* @module services/skill-catalog-client
|
|
14
|
+
*/
|
|
15
|
+
let memoryCache = null;
|
|
16
|
+
const MEMORY_TTL_MS = 6e5;
|
|
17
|
+
/**
|
|
18
|
+
* Find the catalog.json file. Checks:
|
|
19
|
+
* 1. MILADY_SKILLS_CATALOG env override
|
|
20
|
+
* 2. skills/.cache/catalog.json relative to package root
|
|
21
|
+
* 3. ~/.milady/skills/catalog.json
|
|
22
|
+
*/
|
|
23
|
+
function findCatalogPaths() {
|
|
24
|
+
const paths = [];
|
|
25
|
+
const envPath = process.env.MILADY_SKILLS_CATALOG?.trim();
|
|
26
|
+
if (envPath) return [envPath];
|
|
27
|
+
let dir = import.meta.dirname ?? path.dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
for (let i = 0; i < 5; i++) {
|
|
29
|
+
paths.push(path.join(dir, "skills", ".cache", "catalog.json"));
|
|
30
|
+
const parent = path.dirname(dir);
|
|
31
|
+
if (parent === dir) break;
|
|
32
|
+
dir = parent;
|
|
33
|
+
}
|
|
34
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
35
|
+
if (home) paths.push(path.join(home, ".milady", "skills", "catalog.json"));
|
|
36
|
+
return paths;
|
|
37
|
+
}
|
|
38
|
+
async function readCatalogFile() {
|
|
39
|
+
for (const catalogPath of findCatalogPaths()) try {
|
|
40
|
+
const raw = await fs.readFile(catalogPath, "utf-8");
|
|
41
|
+
const parsed = JSON.parse(raw);
|
|
42
|
+
if (Array.isArray(parsed.data) && parsed.data.length > 0) {
|
|
43
|
+
logger.debug(`[skill-catalog] Loaded ${parsed.data.length} skills from ${catalogPath}`);
|
|
44
|
+
return parsed.data;
|
|
45
|
+
}
|
|
46
|
+
} catch {}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
/** Get all skills from the catalog. Resolution: memory → file. */
|
|
50
|
+
async function getCatalogSkills() {
|
|
51
|
+
if (memoryCache && Date.now() - memoryCache.loadedAt < MEMORY_TTL_MS) return memoryCache.skills;
|
|
52
|
+
const skills = await readCatalogFile();
|
|
53
|
+
if (!skills) {
|
|
54
|
+
logger.warn("[skill-catalog] No catalog file found");
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
memoryCache = {
|
|
58
|
+
skills,
|
|
59
|
+
loadedAt: Date.now()
|
|
60
|
+
};
|
|
61
|
+
return skills;
|
|
62
|
+
}
|
|
63
|
+
/** Force-refresh from file. */
|
|
64
|
+
async function refreshCatalog() {
|
|
65
|
+
memoryCache = null;
|
|
66
|
+
return getCatalogSkills();
|
|
67
|
+
}
|
|
68
|
+
/** Get a single skill by slug. */
|
|
69
|
+
async function getCatalogSkill(slug) {
|
|
70
|
+
return (await getCatalogSkills()).find((s) => s.slug === slug) ?? null;
|
|
71
|
+
}
|
|
72
|
+
/** Search skills by query (local fuzzy match on name/summary/slug). */
|
|
73
|
+
async function searchCatalogSkills(query, limit = 30) {
|
|
74
|
+
const skills = await getCatalogSkills();
|
|
75
|
+
const lq = query.toLowerCase();
|
|
76
|
+
const terms = lq.split(/\s+/).filter((t) => t.length > 1);
|
|
77
|
+
const scored = [];
|
|
78
|
+
for (const skill of skills) {
|
|
79
|
+
const slug = skill.slug.toLowerCase();
|
|
80
|
+
const name = (skill.displayName ?? "").toLowerCase();
|
|
81
|
+
const summary = (skill.summary ?? "").toLowerCase();
|
|
82
|
+
let score = 0;
|
|
83
|
+
if (slug === lq || name === lq) score += 100;
|
|
84
|
+
else if (slug.includes(lq)) score += 50;
|
|
85
|
+
else if (name.includes(lq)) score += 45;
|
|
86
|
+
if (summary.includes(lq)) score += 30;
|
|
87
|
+
for (const tag of Object.keys(skill.tags)) if (tag.toLowerCase().includes(lq)) score += 20;
|
|
88
|
+
for (const term of terms) {
|
|
89
|
+
if (slug.includes(term)) score += 15;
|
|
90
|
+
if (name.includes(term)) score += 12;
|
|
91
|
+
if (summary.includes(term)) score += 8;
|
|
92
|
+
}
|
|
93
|
+
if (score > 0) {
|
|
94
|
+
if (skill.stats.downloads > 50) score += 3;
|
|
95
|
+
if (skill.stats.downloads > 200) score += 3;
|
|
96
|
+
if (skill.stats.stars > 0) score += 2;
|
|
97
|
+
if (skill.stats.installsCurrent > 0) score += 2;
|
|
98
|
+
scored.push({
|
|
99
|
+
s: skill,
|
|
100
|
+
score
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
scored.sort((a, b) => b.score - a.score || b.s.stats.downloads - a.s.stats.downloads);
|
|
105
|
+
const max = scored[0]?.score || 1;
|
|
106
|
+
return scored.slice(0, limit).map(({ s, score }) => ({
|
|
107
|
+
slug: s.slug,
|
|
108
|
+
displayName: s.displayName,
|
|
109
|
+
summary: s.summary,
|
|
110
|
+
score: score / max,
|
|
111
|
+
latestVersion: s.latestVersion?.version ?? null,
|
|
112
|
+
downloads: s.stats.downloads,
|
|
113
|
+
stars: s.stats.stars,
|
|
114
|
+
installs: s.stats.installsAllTime
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
//#endregion
|
|
119
|
+
export { getCatalogSkill, getCatalogSkills, refreshCatalog, searchCatalogSkills };
|