@elizaos/ui 2.0.0-alpha.528 → 2.0.0-alpha.529
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/package.json +1 -1
- package/packages/agent/src/actions/browser-autofill-login.d.ts +36 -0
- package/packages/agent/src/actions/browser-autofill-login.d.ts.map +1 -0
- package/packages/agent/src/actions/browser-autofill-login.js +347 -0
- package/packages/agent/src/api/experience-routes.d.ts.map +1 -1
- package/packages/agent/src/api/experience-routes.js +40 -2
- package/packages/agent/src/api/static-file-server.d.ts +2 -2
- package/packages/agent/src/api/static-file-server.d.ts.map +1 -1
- package/packages/agent/src/api/static-file-server.js +4 -2
- package/packages/agent/src/runtime/agent-wallets.d.ts +103 -0
- package/packages/agent/src/runtime/agent-wallets.d.ts.map +1 -0
- package/packages/agent/src/runtime/agent-wallets.js +244 -0
- package/packages/agent/src/runtime/eliza-plugin.d.ts.map +1 -1
- package/packages/agent/src/runtime/eliza-plugin.js +2 -0
- package/packages/agent/src/runtime/eliza.d.ts.map +1 -1
- package/packages/agent/src/runtime/eliza.js +67 -1
- package/packages/agent/src/runtime/operations/index.d.ts +1 -1
- package/packages/agent/src/runtime/operations/index.d.ts.map +1 -1
- package/packages/agent/src/runtime/operations/index.js +1 -1
- package/packages/agent/src/runtime/operations/vault-bridge.d.ts +22 -1
- package/packages/agent/src/runtime/operations/vault-bridge.d.ts.map +1 -1
- package/packages/agent/src/runtime/operations/vault-bridge.js +53 -0
- package/packages/agent/src/runtime/plugin-lifecycle.js +1 -1
- package/packages/agent/src/runtime/vault-profile-resolver.d.ts +37 -0
- package/packages/agent/src/runtime/vault-profile-resolver.d.ts.map +1 -0
- package/packages/agent/src/runtime/vault-profile-resolver.js +75 -0
- package/packages/app-core/src/api/client-agent.d.ts +1 -0
- package/packages/app-core/src/api/client-agent.d.ts.map +1 -1
- package/packages/app-core/src/api/client-base.d.ts.map +1 -1
- package/packages/app-core/src/api/client-base.js +11 -2
- package/packages/app-core/src/api/client-vault.d.ts +53 -0
- package/packages/app-core/src/api/client-vault.d.ts.map +1 -0
- package/packages/app-core/src/api/client-vault.js +48 -0
- package/packages/app-core/src/api/client.d.ts +1 -0
- package/packages/app-core/src/api/client.d.ts.map +1 -1
- package/packages/app-core/src/api/client.js +1 -0
- package/packages/app-core/src/registry/index.d.ts +7 -0
- package/packages/app-core/src/registry/index.d.ts.map +1 -0
- package/packages/app-core/src/registry/index.js +43 -0
- package/packages/app-core/src/registry/legacy-adapter.d.ts +32 -0
- package/packages/app-core/src/registry/legacy-adapter.d.ts.map +1 -0
- package/packages/app-core/src/registry/legacy-adapter.js +101 -0
- package/packages/app-core/src/registry/loader.d.ts +27 -0
- package/packages/app-core/src/registry/loader.d.ts.map +1 -0
- package/packages/app-core/src/registry/loader.js +111 -0
- package/packages/app-core/src/registry/schema.d.ts +986 -0
- package/packages/app-core/src/registry/schema.d.ts.map +1 -0
- package/packages/app-core/src/registry/schema.js +250 -0
- package/packages/app-core/src/services/vault-bootstrap.d.ts +31 -0
- package/packages/app-core/src/services/vault-bootstrap.d.ts.map +1 -0
- package/packages/app-core/src/services/vault-bootstrap.js +245 -0
- package/packages/app-core/src/services/vault-mirror.d.ts +44 -0
- package/packages/app-core/src/services/vault-mirror.d.ts.map +1 -0
- package/packages/app-core/src/services/vault-mirror.js +80 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../../../app-core/src/registry/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// Registry SoT for apps, plugins, and connectors.
|
|
2
|
+
//
|
|
3
|
+
// Replaces the fragmented surface of:
|
|
4
|
+
// - plugins.json (97 entries, 5 categories)
|
|
5
|
+
// - PluginInfo (api/client-types-config.ts)
|
|
6
|
+
// - ConfigUiHint (types/index.ts)
|
|
7
|
+
// - RegistryAppInfo (shared/contracts/apps.ts)
|
|
8
|
+
// - VISIBLE_CONNECTOR_IDS / DEFAULT_ICONS / FEATURE_SUBGROUP / SUBGROUP_DISPLAY_ORDER
|
|
9
|
+
// (components/pages/plugin-list-utils.ts)
|
|
10
|
+
// - paramsToSchema() heuristics (PORT/TIMEOUT/MODEL guessing)
|
|
11
|
+
//
|
|
12
|
+
// Static registry only. Runtime overlay (enabled, configured, isActive,
|
|
13
|
+
// validationErrors) lives in RegistryRuntimeOverlay and is merged at API read
|
|
14
|
+
// time — never in the registry files themselves.
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Config field schema — replaces PluginParamDef + ConfigUiHint.
|
|
18
|
+
// One field, one place. UI hints are co-located with type info.
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
const configFieldType = z.enum([
|
|
21
|
+
"string",
|
|
22
|
+
"secret",
|
|
23
|
+
"boolean",
|
|
24
|
+
"number",
|
|
25
|
+
"select",
|
|
26
|
+
"multiselect",
|
|
27
|
+
"json",
|
|
28
|
+
"textarea",
|
|
29
|
+
"url",
|
|
30
|
+
"file-path",
|
|
31
|
+
]);
|
|
32
|
+
const configFieldOption = z.object({
|
|
33
|
+
value: z.string(),
|
|
34
|
+
label: z.string(),
|
|
35
|
+
description: z.string().optional(),
|
|
36
|
+
icon: z.string().optional(),
|
|
37
|
+
disabled: z.boolean().optional(),
|
|
38
|
+
});
|
|
39
|
+
const visibilityCondition = z.object({
|
|
40
|
+
key: z.string(),
|
|
41
|
+
equals: z.unknown().optional(),
|
|
42
|
+
in: z.array(z.unknown()).optional(),
|
|
43
|
+
notEquals: z.unknown().optional(),
|
|
44
|
+
});
|
|
45
|
+
export const configFieldSchema = z.object({
|
|
46
|
+
type: configFieldType,
|
|
47
|
+
required: z.boolean(),
|
|
48
|
+
sensitive: z.boolean().optional(),
|
|
49
|
+
default: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),
|
|
50
|
+
label: z.string().optional(),
|
|
51
|
+
help: z.string().optional(),
|
|
52
|
+
placeholder: z.string().optional(),
|
|
53
|
+
group: z.string().optional(),
|
|
54
|
+
order: z.number().int().optional(),
|
|
55
|
+
width: z.enum(["full", "half", "third"]).optional(),
|
|
56
|
+
advanced: z.boolean().optional(),
|
|
57
|
+
hidden: z.boolean().optional(),
|
|
58
|
+
readonly: z.boolean().optional(),
|
|
59
|
+
icon: z.string().optional(),
|
|
60
|
+
options: z.array(configFieldOption).optional(),
|
|
61
|
+
pattern: z.string().optional(),
|
|
62
|
+
patternError: z.string().optional(),
|
|
63
|
+
min: z.number().optional(),
|
|
64
|
+
max: z.number().optional(),
|
|
65
|
+
step: z.number().optional(),
|
|
66
|
+
unit: z.string().optional(),
|
|
67
|
+
visible: visibilityCondition.optional(),
|
|
68
|
+
});
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Render hints — replaces VISIBLE_CONNECTOR_IDS / DEFAULT_ICONS /
|
|
71
|
+
// FEATURE_SUBGROUP / SUBGROUP_DISPLAY_ORDER.
|
|
72
|
+
//
|
|
73
|
+
// Surface mapping is implicit:
|
|
74
|
+
// kind: "connector" → ConnectorsView (primary)
|
|
75
|
+
// kind: "app" → AppsView (primary)
|
|
76
|
+
// kind: "plugin" → PluginsView (primary)
|
|
77
|
+
// Every entry shows in its primary surface unless `visible: false`.
|
|
78
|
+
//
|
|
79
|
+
// Use `pinTo` to ALSO surface an item somewhere it wouldn't appear by default
|
|
80
|
+
// (e.g. promoting an app into the chat quick-launcher). Opt-in only — keeps
|
|
81
|
+
// the common case zero-config.
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
const renderActionSchema = z.enum([
|
|
84
|
+
"enable",
|
|
85
|
+
"configure",
|
|
86
|
+
"launch",
|
|
87
|
+
"attach",
|
|
88
|
+
"detach",
|
|
89
|
+
"stop",
|
|
90
|
+
"uninstall",
|
|
91
|
+
"install",
|
|
92
|
+
"setup-guide",
|
|
93
|
+
]);
|
|
94
|
+
const secondarySurfaceSchema = z.enum([
|
|
95
|
+
"chat-apps-section",
|
|
96
|
+
"companion-shell",
|
|
97
|
+
"settings-integrations",
|
|
98
|
+
]);
|
|
99
|
+
export const renderSchema = z.object({
|
|
100
|
+
visible: z.boolean().default(true),
|
|
101
|
+
pinTo: z.array(secondarySurfaceSchema).default([]),
|
|
102
|
+
style: z.enum(["card", "setup-panel", "hero-card"]).default("card"),
|
|
103
|
+
icon: z.string().optional(),
|
|
104
|
+
heroImage: z.string().optional(),
|
|
105
|
+
group: z.string(),
|
|
106
|
+
groupOrder: z.number().int().optional(),
|
|
107
|
+
actions: z.array(renderActionSchema).default([]),
|
|
108
|
+
});
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// External resources (already in plugins.json today).
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
export const resourcesSchema = z.object({
|
|
113
|
+
homepage: z.string().url().optional(),
|
|
114
|
+
repository: z.string().url().optional(),
|
|
115
|
+
setupGuideUrl: z.string().url().optional(),
|
|
116
|
+
});
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// App-only: launch + viewer + session (mirrors RegistryAppInfo).
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
const appViewerSchema = z.object({
|
|
121
|
+
url: z.string(),
|
|
122
|
+
embedParams: z.record(z.string(), z.string()).optional(),
|
|
123
|
+
postMessageAuth: z.boolean().optional(),
|
|
124
|
+
sandbox: z.string().optional(),
|
|
125
|
+
});
|
|
126
|
+
const appSessionSchema = z.object({
|
|
127
|
+
mode: z.enum(["viewer", "spectate-and-steer", "external"]),
|
|
128
|
+
features: z
|
|
129
|
+
.array(z.enum(["commands", "telemetry", "pause", "resume", "suggestions"]))
|
|
130
|
+
.optional(),
|
|
131
|
+
});
|
|
132
|
+
const appSupportsSchema = z.object({
|
|
133
|
+
v0: z.boolean(),
|
|
134
|
+
v1: z.boolean(),
|
|
135
|
+
v2: z.boolean(),
|
|
136
|
+
});
|
|
137
|
+
const appNpmSchema = z.object({
|
|
138
|
+
package: z.string(),
|
|
139
|
+
v0Version: z.string().nullable(),
|
|
140
|
+
v1Version: z.string().nullable(),
|
|
141
|
+
v2Version: z.string().nullable(),
|
|
142
|
+
});
|
|
143
|
+
const appRoutePluginSchema = z.object({
|
|
144
|
+
specifier: z.string().min(1),
|
|
145
|
+
exportName: z.string().min(1).optional(),
|
|
146
|
+
});
|
|
147
|
+
export const appLaunchSchema = z.object({
|
|
148
|
+
type: z.enum(["internal-tab", "overlay", "server-launch"]),
|
|
149
|
+
target: z.string().optional(),
|
|
150
|
+
url: z.string().nullable().optional(),
|
|
151
|
+
viewer: appViewerSchema.optional(),
|
|
152
|
+
session: appSessionSchema.optional(),
|
|
153
|
+
supports: appSupportsSchema.optional(),
|
|
154
|
+
npm: appNpmSchema.optional(),
|
|
155
|
+
capabilities: z.array(z.string()).default([]),
|
|
156
|
+
uiExtension: z.object({ detailPanelId: z.string() }).optional(),
|
|
157
|
+
curatedSlug: z.string().optional(),
|
|
158
|
+
routePlugin: appRoutePluginSchema.optional(),
|
|
159
|
+
});
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Common fields shared by every entry.
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
const commonFields = {
|
|
164
|
+
id: z
|
|
165
|
+
.string()
|
|
166
|
+
.min(1)
|
|
167
|
+
.regex(/^[a-z0-9][a-z0-9-]*$/, "id must be kebab-case ascii"),
|
|
168
|
+
name: z.string().min(1),
|
|
169
|
+
description: z.string().optional(),
|
|
170
|
+
npmName: z.string().optional(),
|
|
171
|
+
version: z.string().optional(),
|
|
172
|
+
releaseStream: z.enum(["latest", "alpha"]).optional(),
|
|
173
|
+
source: z.enum(["bundled", "store"]).default("bundled"),
|
|
174
|
+
tags: z.array(z.string()).default([]),
|
|
175
|
+
config: z.record(z.string(), configFieldSchema).default({}),
|
|
176
|
+
render: renderSchema,
|
|
177
|
+
resources: resourcesSchema.default({}),
|
|
178
|
+
dependsOn: z.array(z.string()).default([]),
|
|
179
|
+
};
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Discriminated union — three kinds, each with their own constraints.
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
const pluginSubtype = z.enum([
|
|
184
|
+
"ai-provider",
|
|
185
|
+
"feature",
|
|
186
|
+
"database",
|
|
187
|
+
"voice",
|
|
188
|
+
"knowledge",
|
|
189
|
+
"blockchain",
|
|
190
|
+
"media",
|
|
191
|
+
"agents",
|
|
192
|
+
"automation",
|
|
193
|
+
"storage",
|
|
194
|
+
"gaming",
|
|
195
|
+
"devtools",
|
|
196
|
+
"other",
|
|
197
|
+
]);
|
|
198
|
+
const connectorSubtype = z.enum([
|
|
199
|
+
"messaging",
|
|
200
|
+
"social",
|
|
201
|
+
"streaming",
|
|
202
|
+
"email",
|
|
203
|
+
"calendar",
|
|
204
|
+
"other",
|
|
205
|
+
]);
|
|
206
|
+
export const pluginEntrySchema = z.object({
|
|
207
|
+
...commonFields,
|
|
208
|
+
kind: z.literal("plugin"),
|
|
209
|
+
subtype: pluginSubtype,
|
|
210
|
+
});
|
|
211
|
+
export const connectorEntrySchema = z.object({
|
|
212
|
+
...commonFields,
|
|
213
|
+
kind: z.literal("connector"),
|
|
214
|
+
subtype: connectorSubtype,
|
|
215
|
+
auth: z
|
|
216
|
+
.object({
|
|
217
|
+
kind: z.enum(["token", "oauth", "credentials", "none"]),
|
|
218
|
+
credentialKeys: z.array(z.string()).default([]),
|
|
219
|
+
})
|
|
220
|
+
.optional(),
|
|
221
|
+
});
|
|
222
|
+
export const appEntrySchema = z.object({
|
|
223
|
+
...commonFields,
|
|
224
|
+
kind: z.literal("app"),
|
|
225
|
+
subtype: z.enum(["game", "tool", "shell", "marketplace", "trading", "other"]),
|
|
226
|
+
launch: appLaunchSchema,
|
|
227
|
+
});
|
|
228
|
+
export const registryEntrySchema = z.discriminatedUnion("kind", [
|
|
229
|
+
pluginEntrySchema,
|
|
230
|
+
connectorEntrySchema,
|
|
231
|
+
appEntrySchema,
|
|
232
|
+
]);
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
// Runtime overlay — never in registry files. Merged at API read time.
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
export const registryRuntimeOverlaySchema = z.object({
|
|
237
|
+
id: z.string(),
|
|
238
|
+
enabled: z.boolean(),
|
|
239
|
+
configured: z.boolean(),
|
|
240
|
+
isActive: z.boolean(),
|
|
241
|
+
loadError: z.string().optional(),
|
|
242
|
+
validationErrors: z
|
|
243
|
+
.array(z.object({ field: z.string(), message: z.string() }))
|
|
244
|
+
.default([]),
|
|
245
|
+
validationWarnings: z
|
|
246
|
+
.array(z.object({ field: z.string(), message: z.string() }))
|
|
247
|
+
.default([]),
|
|
248
|
+
installedVersion: z.string().optional(),
|
|
249
|
+
latestVersion: z.string().nullable().optional(),
|
|
250
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boot-time secret hydration: walk known plaintext sources, push sensitive
|
|
3
|
+
* values to the shared vault, and rewrite the on-disk plaintext to
|
|
4
|
+
* `vault://<KEY>` sentinels.
|
|
5
|
+
*
|
|
6
|
+
* Sources (in order):
|
|
7
|
+
* 1. milady.json `env[KEY]` and `env.vars[KEY]`
|
|
8
|
+
* 2. `<stateDir>/config.env`
|
|
9
|
+
* 3. milady.json `plugins.entries[<id>].config[KEY]`
|
|
10
|
+
* 4. `process.env[KEY]` for keys flagged sensitive in any registered plugin
|
|
11
|
+
* (does not mutate process.env — only mirrors to the vault).
|
|
12
|
+
*
|
|
13
|
+
* Idempotent: a hydration marker at `<stateDir>/.vault-hydrated.json`
|
|
14
|
+
* keeps re-runs cheap once a clean migration has completed. Per-key
|
|
15
|
+
* failures are isolated; if every write fails the function throws.
|
|
16
|
+
*/
|
|
17
|
+
import type { Vault } from "@elizaos/vault";
|
|
18
|
+
export interface VaultBootstrapResult {
|
|
19
|
+
migrated: number;
|
|
20
|
+
alreadyHydrated: number;
|
|
21
|
+
failed: string[];
|
|
22
|
+
}
|
|
23
|
+
interface VaultBootstrapOptions {
|
|
24
|
+
configPath?: string;
|
|
25
|
+
stateDir?: string;
|
|
26
|
+
/** Test seam — defaults to `sharedVault()`. */
|
|
27
|
+
vault?: Vault;
|
|
28
|
+
}
|
|
29
|
+
export declare function runVaultBootstrap(opts?: VaultBootstrapOptions): Promise<VaultBootstrapResult>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=vault-bootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault-bootstrap.d.ts","sourceRoot":"","sources":["../../../../../../app-core/src/services/vault-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAgBH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAe5C,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,UAAU,qBAAqB;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AA+MD,wBAAsB,iBAAiB,CACrC,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAgE/B"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boot-time secret hydration: walk known plaintext sources, push sensitive
|
|
3
|
+
* values to the shared vault, and rewrite the on-disk plaintext to
|
|
4
|
+
* `vault://<KEY>` sentinels.
|
|
5
|
+
*
|
|
6
|
+
* Sources (in order):
|
|
7
|
+
* 1. milady.json `env[KEY]` and `env.vars[KEY]`
|
|
8
|
+
* 2. `<stateDir>/config.env`
|
|
9
|
+
* 3. milady.json `plugins.entries[<id>].config[KEY]`
|
|
10
|
+
* 4. `process.env[KEY]` for keys flagged sensitive in any registered plugin
|
|
11
|
+
* (does not mutate process.env — only mirrors to the vault).
|
|
12
|
+
*
|
|
13
|
+
* Idempotent: a hydration marker at `<stateDir>/.vault-hydrated.json`
|
|
14
|
+
* keeps re-runs cheap once a clean migration has completed. Per-key
|
|
15
|
+
* failures are isolated; if every write fails the function throws.
|
|
16
|
+
*/
|
|
17
|
+
import fs from "node:fs/promises";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { persistConfigEnv, readConfigEnv } from "@elizaos/agent/api/config-env";
|
|
20
|
+
import { loadElizaConfig, saveElizaConfig, } from "@elizaos/agent/config/config";
|
|
21
|
+
import { resolveStateDir } from "@elizaos/agent/config/paths";
|
|
22
|
+
import { formatVaultRef, isVaultRef, } from "@elizaos/agent/runtime/operations/vault-bridge";
|
|
23
|
+
import { logger } from "@elizaos/core";
|
|
24
|
+
import { loadRegistry } from "../registry";
|
|
25
|
+
import { sharedVault } from "./vault-mirror";
|
|
26
|
+
const MARKER_VERSION = 1;
|
|
27
|
+
const MARKER_FILENAME = ".vault-hydrated.json";
|
|
28
|
+
const ENV_VAR_KEY = /^[A-Z][A-Z0-9_]*$/;
|
|
29
|
+
function inferSensitiveByHeuristic(key) {
|
|
30
|
+
return /(?:_API_KEY|_SECRET|_TOKEN|_PASSWORD|_PRIVATE_KEY|_SIGNING_|ENCRYPTION_)/i.test(key);
|
|
31
|
+
}
|
|
32
|
+
/** Build the set of plugin-config keys flagged sensitive in the registry. */
|
|
33
|
+
function sensitiveKeysFromRegistry() {
|
|
34
|
+
const keys = new Set();
|
|
35
|
+
const registry = loadRegistry();
|
|
36
|
+
for (const entry of registry.all) {
|
|
37
|
+
for (const [fieldKey, field] of Object.entries(entry.config)) {
|
|
38
|
+
if (field.sensitive === true)
|
|
39
|
+
keys.add(fieldKey);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return keys;
|
|
43
|
+
}
|
|
44
|
+
async function readMarker(stateDir) {
|
|
45
|
+
const filePath = path.join(stateDir, MARKER_FILENAME);
|
|
46
|
+
try {
|
|
47
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
48
|
+
const parsed = JSON.parse(raw);
|
|
49
|
+
if (typeof parsed.version === "number" &&
|
|
50
|
+
typeof parsed.completedAt === "string" &&
|
|
51
|
+
Array.isArray(parsed.migratedKeys) &&
|
|
52
|
+
Array.isArray(parsed.skippedKeys)) {
|
|
53
|
+
return parsed;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
if (err.code === "ENOENT")
|
|
59
|
+
return null;
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function writeMarker(stateDir, marker) {
|
|
64
|
+
await fs.mkdir(stateDir, { recursive: true });
|
|
65
|
+
const filePath = path.join(stateDir, MARKER_FILENAME);
|
|
66
|
+
const tmpPath = `${filePath}.tmp`;
|
|
67
|
+
await fs.writeFile(tmpPath, JSON.stringify(marker, null, 2), {
|
|
68
|
+
encoding: "utf8",
|
|
69
|
+
mode: 0o600,
|
|
70
|
+
});
|
|
71
|
+
await fs.rename(tmpPath, filePath);
|
|
72
|
+
}
|
|
73
|
+
function isPlainRecord(value) {
|
|
74
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Walk milady.json env / env.vars / plugins.entries[*].config in place,
|
|
78
|
+
* pushing sensitive plaintext values to the vault and replacing them with
|
|
79
|
+
* sentinels. Returns the keys we attempted to migrate plus the failures.
|
|
80
|
+
*/
|
|
81
|
+
async function migrateMiladyJson(config, vault, sensitiveKeys) {
|
|
82
|
+
const migrated = [];
|
|
83
|
+
const skipped = [];
|
|
84
|
+
const failed = [];
|
|
85
|
+
let mutated = false;
|
|
86
|
+
async function tryMigrate(container, key) {
|
|
87
|
+
const value = container[key];
|
|
88
|
+
if (typeof value !== "string" || value.length === 0)
|
|
89
|
+
return;
|
|
90
|
+
if (isVaultRef(value)) {
|
|
91
|
+
skipped.push(key);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const isSensitive = sensitiveKeys.has(key) || inferSensitiveByHeuristic(key);
|
|
95
|
+
if (!isSensitive)
|
|
96
|
+
return;
|
|
97
|
+
try {
|
|
98
|
+
await vault.set(key, value, { sensitive: true });
|
|
99
|
+
container[key] = formatVaultRef(key);
|
|
100
|
+
migrated.push(key);
|
|
101
|
+
mutated = true;
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
failed.push(key);
|
|
105
|
+
logger.error({ err, key }, "[vault-bootstrap] failed to migrate milady.json secret");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const env = config.env;
|
|
109
|
+
if (isPlainRecord(env)) {
|
|
110
|
+
for (const key of Object.keys(env)) {
|
|
111
|
+
if (!ENV_VAR_KEY.test(key))
|
|
112
|
+
continue;
|
|
113
|
+
await tryMigrate(env, key);
|
|
114
|
+
}
|
|
115
|
+
const vars = env.vars;
|
|
116
|
+
if (isPlainRecord(vars)) {
|
|
117
|
+
for (const key of Object.keys(vars)) {
|
|
118
|
+
if (!ENV_VAR_KEY.test(key))
|
|
119
|
+
continue;
|
|
120
|
+
await tryMigrate(vars, key);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const plugins = config.plugins;
|
|
125
|
+
if (isPlainRecord(plugins)) {
|
|
126
|
+
const entries = plugins.entries;
|
|
127
|
+
if (isPlainRecord(entries)) {
|
|
128
|
+
for (const entryValue of Object.values(entries)) {
|
|
129
|
+
if (!isPlainRecord(entryValue))
|
|
130
|
+
continue;
|
|
131
|
+
const entryConfig = entryValue.config;
|
|
132
|
+
if (!isPlainRecord(entryConfig))
|
|
133
|
+
continue;
|
|
134
|
+
for (const fieldKey of Object.keys(entryConfig)) {
|
|
135
|
+
await tryMigrate(entryConfig, fieldKey);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { migrated, skipped, failed, mutated };
|
|
141
|
+
}
|
|
142
|
+
async function migrateConfigEnvFile(stateDir, vault, sensitiveKeys) {
|
|
143
|
+
const migrated = [];
|
|
144
|
+
const skipped = [];
|
|
145
|
+
const failed = [];
|
|
146
|
+
const entries = await readConfigEnv(stateDir);
|
|
147
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
148
|
+
if (typeof value !== "string" || value.length === 0)
|
|
149
|
+
continue;
|
|
150
|
+
if (isVaultRef(value)) {
|
|
151
|
+
skipped.push(key);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const isSensitive = sensitiveKeys.has(key) || inferSensitiveByHeuristic(key);
|
|
155
|
+
if (!isSensitive)
|
|
156
|
+
continue;
|
|
157
|
+
try {
|
|
158
|
+
await vault.set(key, value, { sensitive: true });
|
|
159
|
+
await persistConfigEnv(key, formatVaultRef(key), { stateDir });
|
|
160
|
+
migrated.push(key);
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
failed.push(key);
|
|
164
|
+
logger.error({ err, key }, "[vault-bootstrap] failed to migrate config.env secret");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return { migrated, skipped, failed };
|
|
168
|
+
}
|
|
169
|
+
async function mirrorProcessEnvSensitive(vault, sensitiveKeys, seenKeys) {
|
|
170
|
+
const migrated = [];
|
|
171
|
+
const failed = [];
|
|
172
|
+
for (const [key, rawValue] of Object.entries(process.env)) {
|
|
173
|
+
if (!ENV_VAR_KEY.test(key))
|
|
174
|
+
continue;
|
|
175
|
+
if (seenKeys.has(key))
|
|
176
|
+
continue;
|
|
177
|
+
if (typeof rawValue !== "string" || rawValue.length === 0)
|
|
178
|
+
continue;
|
|
179
|
+
if (isVaultRef(rawValue))
|
|
180
|
+
continue;
|
|
181
|
+
const isSensitive = sensitiveKeys.has(key) || inferSensitiveByHeuristic(key);
|
|
182
|
+
if (!isSensitive)
|
|
183
|
+
continue;
|
|
184
|
+
if (await vault.has(key))
|
|
185
|
+
continue;
|
|
186
|
+
try {
|
|
187
|
+
await vault.set(key, rawValue, { sensitive: true });
|
|
188
|
+
migrated.push(key);
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
failed.push(key);
|
|
192
|
+
logger.error({ err, key }, "[vault-bootstrap] failed to mirror process.env secret");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return { migrated, failed };
|
|
196
|
+
}
|
|
197
|
+
export async function runVaultBootstrap(opts = {}) {
|
|
198
|
+
const stateDir = opts.stateDir ?? resolveStateDir();
|
|
199
|
+
const vault = opts.vault ?? sharedVault();
|
|
200
|
+
const marker = await readMarker(stateDir);
|
|
201
|
+
const alreadyHydrated = marker && marker.version === MARKER_VERSION
|
|
202
|
+
? marker.migratedKeys.length + marker.skippedKeys.length
|
|
203
|
+
: 0;
|
|
204
|
+
const sensitiveKeys = sensitiveKeysFromRegistry();
|
|
205
|
+
const config = loadElizaConfig();
|
|
206
|
+
const json = await migrateMiladyJson(config, vault, sensitiveKeys);
|
|
207
|
+
if (json.mutated) {
|
|
208
|
+
saveElizaConfig(config);
|
|
209
|
+
}
|
|
210
|
+
const env = await migrateConfigEnvFile(stateDir, vault, sensitiveKeys);
|
|
211
|
+
// Skip keys we already attempted (success or fail) so process.env
|
|
212
|
+
// mirroring doesn't double-count keys that just failed against the json
|
|
213
|
+
// file or the config.env file.
|
|
214
|
+
const seen = new Set([
|
|
215
|
+
...json.migrated,
|
|
216
|
+
...json.failed,
|
|
217
|
+
...env.migrated,
|
|
218
|
+
...env.failed,
|
|
219
|
+
]);
|
|
220
|
+
const proc = await mirrorProcessEnvSensitive(vault, sensitiveKeys, seen);
|
|
221
|
+
const migratedKeys = [...json.migrated, ...env.migrated, ...proc.migrated];
|
|
222
|
+
const skippedKeys = [...json.skipped, ...env.skipped];
|
|
223
|
+
const failedKeys = [...json.failed, ...env.failed, ...proc.failed];
|
|
224
|
+
const attempted = migratedKeys.length + failedKeys.length;
|
|
225
|
+
if (attempted > 0 && migratedKeys.length === 0) {
|
|
226
|
+
throw new Error(`[vault-bootstrap] all ${failedKeys.length} secret writes failed; vault unreachable`);
|
|
227
|
+
}
|
|
228
|
+
await writeMarker(stateDir, {
|
|
229
|
+
version: MARKER_VERSION,
|
|
230
|
+
completedAt: new Date().toISOString(),
|
|
231
|
+
migratedKeys,
|
|
232
|
+
skippedKeys,
|
|
233
|
+
});
|
|
234
|
+
if (migratedKeys.length > 0 || failedKeys.length > 0) {
|
|
235
|
+
logger.info(`[vault-bootstrap] migrated=${migratedKeys.length} skipped=${skippedKeys.length} failed=${failedKeys.length}`);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
logger.debug(`[vault-bootstrap] no plaintext secrets found (already-hydrated keys=${alreadyHydrated})`);
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
migrated: migratedKeys.length,
|
|
242
|
+
alreadyHydrated,
|
|
243
|
+
failed: failedKeys,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write-through mirror to @elizaos/vault for plugin sensitive fields.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from plugins-compat-routes.ts so unit tests can exercise the
|
|
5
|
+
* mirror logic without dragging in the entire @elizaos/agent runtime.
|
|
6
|
+
*
|
|
7
|
+
* Concurrency: the vault PUT path is hit concurrently when the UI saves
|
|
8
|
+
* multiple plugin configs in parallel. `VaultImpl.mutate()` has its own
|
|
9
|
+
* process and filesystem locks; the process-level manager cache keeps the
|
|
10
|
+
* plugin-save path and `/api/secrets/manager/*` routes sharing one facade.
|
|
11
|
+
*/
|
|
12
|
+
import { type SecretsManager, type Vault } from "@elizaos/vault";
|
|
13
|
+
export declare function sharedSecretsManager(): SecretsManager;
|
|
14
|
+
export declare function sharedVault(): Vault;
|
|
15
|
+
/**
|
|
16
|
+
* Test-only: drop the cached vault so the next `sharedVault()` call
|
|
17
|
+
* re-initializes from the (possibly newly configured) environment.
|
|
18
|
+
* Also lets tests inject a test vault built via `createTestVault`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function _resetSharedVaultForTesting(next?: Vault | null): void;
|
|
21
|
+
/**
|
|
22
|
+
* Write-through mirror to @elizaos/vault. Iterates the plugin's
|
|
23
|
+
* declared parameters, finds sensitive ones, and writes whatever
|
|
24
|
+
* value the user just submitted into the vault as a sensitive entry.
|
|
25
|
+
*
|
|
26
|
+
* Returns the list of keys that failed to write. The PUT handler
|
|
27
|
+
* surfaces them under `vaultMirrorFailures` in the response so the UI
|
|
28
|
+
* can warn the user that their secret was saved to legacy config but
|
|
29
|
+
* not mirrored to the vault. Per-key try/catch keeps one failed key
|
|
30
|
+
* from aborting the rest of the loop.
|
|
31
|
+
*
|
|
32
|
+
* Vault key shape: the env-var name itself (e.g.
|
|
33
|
+
* `OPENROUTER_API_KEY`). Stable, matches what the legacy code uses,
|
|
34
|
+
* and lets the read-side hydration round-trip cleanly.
|
|
35
|
+
*/
|
|
36
|
+
export declare function mirrorPluginSensitiveToVault(plugin: {
|
|
37
|
+
parameters: Array<{
|
|
38
|
+
key: string;
|
|
39
|
+
sensitive: boolean;
|
|
40
|
+
}>;
|
|
41
|
+
}, body: unknown): Promise<{
|
|
42
|
+
failures: string[];
|
|
43
|
+
}>;
|
|
44
|
+
//# sourceMappingURL=vault-mirror.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault-mirror.d.ts","sourceRoot":"","sources":["../../../../../../app-core/src/services/vault-mirror.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAiB,KAAK,cAAc,EAAE,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAIhF,wBAAgB,oBAAoB,IAAI,cAAc,CAGrD;AAED,wBAAgB,WAAW,IAAI,KAAK,CAEnC;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,GAAE,KAAK,GAAG,IAAW,GAAG,IAAI,CAE3E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE;IAAE,UAAU,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CAAE,EAClE,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAgCjC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write-through mirror to @elizaos/vault for plugin sensitive fields.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from plugins-compat-routes.ts so unit tests can exercise the
|
|
5
|
+
* mirror logic without dragging in the entire @elizaos/agent runtime.
|
|
6
|
+
*
|
|
7
|
+
* Concurrency: the vault PUT path is hit concurrently when the UI saves
|
|
8
|
+
* multiple plugin configs in parallel. `VaultImpl.mutate()` has its own
|
|
9
|
+
* process and filesystem locks; the process-level manager cache keeps the
|
|
10
|
+
* plugin-save path and `/api/secrets/manager/*` routes sharing one facade.
|
|
11
|
+
*/
|
|
12
|
+
import { logger } from "@elizaos/core";
|
|
13
|
+
import { asRecord } from "@elizaos/shared";
|
|
14
|
+
import { createManager } from "@elizaos/vault";
|
|
15
|
+
let cachedManager = null;
|
|
16
|
+
export function sharedSecretsManager() {
|
|
17
|
+
if (!cachedManager)
|
|
18
|
+
cachedManager = createManager();
|
|
19
|
+
return cachedManager;
|
|
20
|
+
}
|
|
21
|
+
export function sharedVault() {
|
|
22
|
+
return sharedSecretsManager().vault;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Test-only: drop the cached vault so the next `sharedVault()` call
|
|
26
|
+
* re-initializes from the (possibly newly configured) environment.
|
|
27
|
+
* Also lets tests inject a test vault built via `createTestVault`.
|
|
28
|
+
*/
|
|
29
|
+
export function _resetSharedVaultForTesting(next = null) {
|
|
30
|
+
cachedManager = next ? createManager({ vault: next }) : null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Write-through mirror to @elizaos/vault. Iterates the plugin's
|
|
34
|
+
* declared parameters, finds sensitive ones, and writes whatever
|
|
35
|
+
* value the user just submitted into the vault as a sensitive entry.
|
|
36
|
+
*
|
|
37
|
+
* Returns the list of keys that failed to write. The PUT handler
|
|
38
|
+
* surfaces them under `vaultMirrorFailures` in the response so the UI
|
|
39
|
+
* can warn the user that their secret was saved to legacy config but
|
|
40
|
+
* not mirrored to the vault. Per-key try/catch keeps one failed key
|
|
41
|
+
* from aborting the rest of the loop.
|
|
42
|
+
*
|
|
43
|
+
* Vault key shape: the env-var name itself (e.g.
|
|
44
|
+
* `OPENROUTER_API_KEY`). Stable, matches what the legacy code uses,
|
|
45
|
+
* and lets the read-side hydration round-trip cleanly.
|
|
46
|
+
*/
|
|
47
|
+
export async function mirrorPluginSensitiveToVault(plugin, body) {
|
|
48
|
+
const failures = [];
|
|
49
|
+
const config = asRecord(body)?.config;
|
|
50
|
+
const configRecord = asRecord(config);
|
|
51
|
+
if (!configRecord)
|
|
52
|
+
return { failures };
|
|
53
|
+
const sensitiveKeys = plugin.parameters
|
|
54
|
+
.filter((p) => p.sensitive)
|
|
55
|
+
.map((p) => p.key);
|
|
56
|
+
if (sensitiveKeys.length === 0)
|
|
57
|
+
return { failures };
|
|
58
|
+
const manager = sharedSecretsManager();
|
|
59
|
+
for (const key of sensitiveKeys) {
|
|
60
|
+
const value = configRecord[key];
|
|
61
|
+
if (typeof value !== "string")
|
|
62
|
+
continue;
|
|
63
|
+
try {
|
|
64
|
+
if (value.length === 0) {
|
|
65
|
+
await manager.remove(key);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
await manager.set(key, value, {
|
|
69
|
+
sensitive: true,
|
|
70
|
+
caller: "plugins-compat",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
failures.push(key);
|
|
76
|
+
logger.warn(`[plugins-compat] vault mirror for ${key} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { failures };
|
|
80
|
+
}
|