@floatonnear/mcp 0.10.1 → 0.10.3
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/SKILL.md +144 -14
- package/dist/agent-key.d.ts +50 -0
- package/dist/agent-key.js +68 -2
- package/dist/create.d.ts +10 -0
- package/dist/create.js +23 -1
- package/dist/explain.d.ts +33 -0
- package/dist/explain.js +55 -0
- package/dist/keyring-path.d.ts +36 -0
- package/dist/keyring-path.js +117 -0
- package/dist/payloads.d.ts +10 -8
- package/dist/payloads.js +92 -23
- package/dist/roles.d.ts +29 -0
- package/dist/roles.js +49 -0
- package/dist/server.js +562 -85
- package/dist/token-meta.d.ts +16 -0
- package/dist/token-meta.js +18 -0
- package/package.json +1 -1
package/dist/payloads.d.ts
CHANGED
|
@@ -43,9 +43,11 @@ export interface OwnerPayload {
|
|
|
43
43
|
/** Non-empty only when the action is destructive or hard to undo. */
|
|
44
44
|
warnings: string[];
|
|
45
45
|
}
|
|
46
|
-
export declare function planAddNode(vault: string, parent: number | null, label: string, limits: HumanTokenLimit[], cooldownSec: number, recipients: string[] | null
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
export declare function planAddNode(vault: string, parent: number | null, label: string, limits: HumanTokenLimit[], cooldownSec: number, recipients: string[] | null,
|
|
47
|
+
/** The PARENT's human label, so the summary can say "under news". */
|
|
48
|
+
parentName?: string | null): OwnerPayload;
|
|
49
|
+
export declare function planSetNodeCaps(vault: string, nodeId: number, limits: HumanTokenLimit[], nodeName?: string | null): OwnerPayload;
|
|
50
|
+
export declare function planBindAgent(vault: string, nodeId: number, accountId: string, nodeName?: string | null): OwnerPayload;
|
|
49
51
|
/**
|
|
50
52
|
* A whole departmental agent in ONE owner signature: node, binding, gas key.
|
|
51
53
|
*
|
|
@@ -69,7 +71,7 @@ export declare function planBindAgent(vault: string, nodeId: number, accountId:
|
|
|
69
71
|
* and the vault carries the owner's full-access key.
|
|
70
72
|
*/
|
|
71
73
|
export declare function planAddRole(vault: string, label: string, limits: HumanTokenLimit[], cooldownSec: number, recipients: string[] | null, publicKey: string, fundingYocto: string): OwnerPayload;
|
|
72
|
-
export declare function planBindVirtualAgent(vault: string, nodeId: number, publicKey: string): OwnerPayload;
|
|
74
|
+
export declare function planBindVirtualAgent(vault: string, nodeId: number, publicKey: string, nodeName?: string | null): OwnerPayload;
|
|
73
75
|
/**
|
|
74
76
|
* Revoke a virtual agent's authority.
|
|
75
77
|
*
|
|
@@ -80,9 +82,9 @@ export declare function planBindVirtualAgent(vault: string, nodeId: number, publ
|
|
|
80
82
|
* half the binding's storage is intentionally unrecoverable.
|
|
81
83
|
*/
|
|
82
84
|
export declare function planUnbindVirtualAgent(vault: string, publicKey: string): OwnerPayload;
|
|
83
|
-
export declare function planSetRecipients(vault: string, nodeId: number, recipients: string[] | null): OwnerPayload;
|
|
84
|
-
export declare function planSetCooldown(vault: string, nodeId: number, cooldownSec: number): OwnerPayload;
|
|
85
|
-
export declare function planFreezeNode(vault: string, nodeId: number, frozen: boolean): OwnerPayload;
|
|
85
|
+
export declare function planSetRecipients(vault: string, nodeId: number, recipients: string[] | null, nodeName?: string | null): OwnerPayload;
|
|
86
|
+
export declare function planSetCooldown(vault: string, nodeId: number, cooldownSec: number, nodeName?: string | null): OwnerPayload;
|
|
87
|
+
export declare function planFreezeNode(vault: string, nodeId: number, frozen: boolean, nodeName?: string | null): OwnerPayload;
|
|
86
88
|
export declare function planSetPaused(vault: string, paused: boolean): OwnerPayload;
|
|
87
89
|
export declare function planSetStrictAllocation(vault: string, strict: boolean): OwnerPayload;
|
|
88
90
|
export declare function planPurgeSettled(vault: string, beforeDay: number, limit: number): OwnerPayload;
|
|
@@ -195,7 +197,7 @@ export declare function toCliCommand(p: OwnerPayload, network?: string): string;
|
|
|
195
197
|
* `toBaseUnits` throws rather than truncating excess precision — correct here:
|
|
196
198
|
* silently rounding 0.1234567 would set a threshold the owner did not choose.
|
|
197
199
|
*/
|
|
198
|
-
export declare function planSetApprovalThreshold(vault: string, nodeId: number, amount: string | null, decimals: number): OwnerPayload;
|
|
200
|
+
export declare function planSetApprovalThreshold(vault: string, nodeId: number, amount: string | null, decimals: number, nodeName?: string | null): OwnerPayload;
|
|
199
201
|
/**
|
|
200
202
|
* Execute a held payment.
|
|
201
203
|
*
|
package/dist/payloads.js
CHANGED
|
@@ -105,7 +105,9 @@ function assertCooldown(cooldownSec) {
|
|
|
105
105
|
throw new Error(`cooldown must be at most ${MAX_COOLDOWN_SEC}s (MAX_COOLDOWN_SEC)`);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
-
export function planAddNode(vault, parent, label, limits, cooldownSec, recipients
|
|
108
|
+
export function planAddNode(vault, parent, label, limits, cooldownSec, recipients,
|
|
109
|
+
/** The PARENT's human label, so the summary can say "under news". */
|
|
110
|
+
parentName) {
|
|
109
111
|
if (!label || label.length > MAX_LABEL_LEN) {
|
|
110
112
|
throw new Error(`label must be 1..=${MAX_LABEL_LEN} characters (MAX_LABEL_LEN)`);
|
|
111
113
|
}
|
|
@@ -123,35 +125,99 @@ export function planAddNode(vault, parent, label, limits, cooldownSec, recipient
|
|
|
123
125
|
},
|
|
124
126
|
gasTgas: DEFAULT_GAS_TGAS,
|
|
125
127
|
depositYocto: ONE_YOCTO,
|
|
126
|
-
summary:
|
|
128
|
+
summary:
|
|
129
|
+
// "the main budget" for a root, and the PARENT's name otherwise — a new
|
|
130
|
+
// child is described by where it hangs, which is the one thing the
|
|
131
|
+
// person choosing it cares about.
|
|
132
|
+
`Create a "${label}" budget ` +
|
|
133
|
+
`${parent === null ? "as the main budget" : `under ${budgetName(parent, parentName)}`}` +
|
|
127
134
|
` with ${describeLimits(limits)}` +
|
|
128
135
|
(cooldownSec ? `, ${cooldownSec}s cooldown between payments` : "") +
|
|
129
136
|
(recipients ? `, paying only: ${recipients.join(", ")}` : `, inheriting the payee allowlist`),
|
|
130
137
|
warnings: [],
|
|
131
138
|
};
|
|
132
139
|
}
|
|
133
|
-
|
|
140
|
+
/**
|
|
141
|
+
* How a budget is NAMED to a person.
|
|
142
|
+
*
|
|
143
|
+
* WHY THIS EXISTS. Every plan summary said "node 1", and `planVoice` copies
|
|
144
|
+
* the summary verbatim into the `say` a user reads — so the product spoke the
|
|
145
|
+
* tree's internal vocabulary: "Set node 1 caps to 1/day", "Freeze node 0".
|
|
146
|
+
* A node id is the contract's primary key. It is exactly right in `args`, in
|
|
147
|
+
* `technical`, and in the unsigned payload a wallet signs, and it is a
|
|
148
|
+
* stranger's filing reference to the person who called the thing "news".
|
|
149
|
+
*
|
|
150
|
+
* The label is NOT derivable here — it lives on chain, and this module is pure
|
|
151
|
+
* and synchronously testable, which is worth keeping. So the caller resolves
|
|
152
|
+
* it and passes it in; `server.ts` already fetches the tree to check the node
|
|
153
|
+
* exists, so this costs no extra call.
|
|
154
|
+
*
|
|
155
|
+
* FALLBACKS, in order of how much they can honestly claim:
|
|
156
|
+
* a label -> `the "news" budget`
|
|
157
|
+
* node 0, no label -> "the main budget" (root is the only id with a meaning)
|
|
158
|
+
* anything else -> "this budget" (true, and says nothing false)
|
|
159
|
+
*
|
|
160
|
+
* "this budget" is deliberately vague rather than wrong. The alternative was
|
|
161
|
+
* falling back to the id, which is the bug.
|
|
162
|
+
*/
|
|
163
|
+
/**
|
|
164
|
+
* Sentence-case a budget name for the start of a summary.
|
|
165
|
+
*
|
|
166
|
+
* `budgetName` returns a lowercase noun phrase because most of its uses sit
|
|
167
|
+
* mid-sentence ("Freeze the main budget"). A few lead the sentence, and
|
|
168
|
+
* "the main budget must wait 30s" reads like a fragment.
|
|
169
|
+
*/
|
|
170
|
+
function capitalise(s) {
|
|
171
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
172
|
+
}
|
|
173
|
+
function budgetName(nodeId, label) {
|
|
174
|
+
/**
|
|
175
|
+
* ROOT IS ALWAYS "the main budget", whatever its stored label.
|
|
176
|
+
*
|
|
177
|
+
* The contract hardcodes root's label to the literal string "root"
|
|
178
|
+
* (`lib.rs`: `label: "root".to_string()`), which is the tree's word, not a
|
|
179
|
+
* person's. `the "root" budget` would just be "node 0" wearing a hat.
|
|
180
|
+
*/
|
|
181
|
+
if (nodeId === 0)
|
|
182
|
+
return "the main budget";
|
|
183
|
+
const clean = typeof label === "string" ? label.trim() : "";
|
|
184
|
+
if (clean)
|
|
185
|
+
return `the "${clean}" budget`;
|
|
186
|
+
/**
|
|
187
|
+
* NO LABEL means the tree read FAILED — `Node.label` is a required `String`
|
|
188
|
+
* on chain (`types.rs`), so every real node has one. "this budget" is
|
|
189
|
+
* deliberately vague rather than wrong; the alternative was falling back to
|
|
190
|
+
* the id, which is the thing being removed.
|
|
191
|
+
*
|
|
192
|
+
* It does cost distinguishability in that degraded case: two different
|
|
193
|
+
* budgets read the same. The node id is still in `args.node_id`, which is
|
|
194
|
+
* what the WALLET displays and what the owner actually signs, so the
|
|
195
|
+
* signature is never ambiguous even when the sentence is. Pinned by a test.
|
|
196
|
+
*/
|
|
197
|
+
return "this budget";
|
|
198
|
+
}
|
|
199
|
+
export function planSetNodeCaps(vault, nodeId, limits, nodeName) {
|
|
134
200
|
return {
|
|
135
201
|
receiverId: vault,
|
|
136
202
|
methodName: "set_node_caps",
|
|
137
203
|
args: { node_id: nodeId, limits: limitsToArgs(limits) },
|
|
138
204
|
gasTgas: DEFAULT_GAS_TGAS,
|
|
139
205
|
depositYocto: ONE_YOCTO,
|
|
140
|
-
summary: `
|
|
206
|
+
summary: `Update ${budgetName(nodeId, nodeName)} to ${describeLimits(limits)}`,
|
|
141
207
|
warnings: [],
|
|
142
208
|
};
|
|
143
209
|
}
|
|
144
|
-
export function planBindAgent(vault, nodeId, accountId) {
|
|
210
|
+
export function planBindAgent(vault, nodeId, accountId, nodeName) {
|
|
145
211
|
return {
|
|
146
212
|
receiverId: vault,
|
|
147
213
|
methodName: "bind_agent",
|
|
148
214
|
args: { node_id: nodeId, account_id: accountId },
|
|
149
215
|
gasTgas: DEFAULT_GAS_TGAS,
|
|
150
216
|
depositYocto: ONE_YOCTO,
|
|
151
|
-
summary: `Let ${accountId} spend from
|
|
217
|
+
summary: `Let ${accountId} spend from ${budgetName(nodeId, nodeName)}`,
|
|
152
218
|
warnings: [
|
|
153
219
|
`${accountId} is an EXISTING account whose keys you do not control. ` +
|
|
154
|
-
`It can
|
|
220
|
+
`It can spend with the full authority of ${budgetName(nodeId, nodeName)}.`,
|
|
155
221
|
`Prefer a VIRTUAL agent (bind_virtual_agent): it binds one key you generate ` +
|
|
156
222
|
`rather than a whole account, costs ~168 bytes, and strands nothing.`,
|
|
157
223
|
],
|
|
@@ -218,7 +284,7 @@ export function planAddRole(vault, label, limits, cooldownSec, recipients, publi
|
|
|
218
284
|
],
|
|
219
285
|
};
|
|
220
286
|
}
|
|
221
|
-
export function planBindVirtualAgent(vault, nodeId, publicKey) {
|
|
287
|
+
export function planBindVirtualAgent(vault, nodeId, publicKey, nodeName) {
|
|
222
288
|
assertNodeId(nodeId);
|
|
223
289
|
assertPublicKey(publicKey);
|
|
224
290
|
return {
|
|
@@ -230,7 +296,7 @@ export function planBindVirtualAgent(vault, nodeId, publicKey) {
|
|
|
230
296
|
// `assert_owner()` -> `assert_one_yocto()`. Nothing is funded here, which
|
|
231
297
|
// is the whole point of moving off sub-accounts.
|
|
232
298
|
depositYocto: ONE_YOCTO,
|
|
233
|
-
summary: `Let
|
|
299
|
+
summary: `Let a new agent key spend from ${budgetName(nodeId, nodeName)}`,
|
|
234
300
|
warnings: [
|
|
235
301
|
`A node may hold more than one key — binding another does not replace this one.`,
|
|
236
302
|
`This grants spending authority only. The key still needs gas: install it as ` +
|
|
@@ -265,7 +331,7 @@ export function planUnbindVirtualAgent(vault, publicKey) {
|
|
|
265
331
|
],
|
|
266
332
|
};
|
|
267
333
|
}
|
|
268
|
-
export function planSetRecipients(vault, nodeId, recipients) {
|
|
334
|
+
export function planSetRecipients(vault, nodeId, recipients, nodeName) {
|
|
269
335
|
assertRecipients(recipients);
|
|
270
336
|
return {
|
|
271
337
|
receiverId: vault,
|
|
@@ -274,17 +340,18 @@ export function planSetRecipients(vault, nodeId, recipients) {
|
|
|
274
340
|
gasTgas: DEFAULT_GAS_TGAS,
|
|
275
341
|
depositYocto: ONE_YOCTO,
|
|
276
342
|
summary: recipients
|
|
277
|
-
?
|
|
278
|
-
:
|
|
343
|
+
? `${capitalise(budgetName(nodeId, nodeName))} may pay only: ${recipients.join(", ")}`
|
|
344
|
+
: `${capitalise(budgetName(nodeId, nodeName))} takes its list of allowed payees from the budget above it`,
|
|
279
345
|
warnings: recipients
|
|
280
346
|
? [
|
|
281
|
-
`This REPLACES the inherited
|
|
282
|
-
`it does not add to it. Payees it currently inherits will
|
|
347
|
+
`This REPLACES the inherited payee list for ${budgetName(nodeId, nodeName)} and ` +
|
|
348
|
+
`everything under it; it does not add to it. Payees it currently inherits will ` +
|
|
349
|
+
`stop being payable.`,
|
|
283
350
|
]
|
|
284
351
|
: [],
|
|
285
352
|
};
|
|
286
353
|
}
|
|
287
|
-
export function planSetCooldown(vault, nodeId, cooldownSec) {
|
|
354
|
+
export function planSetCooldown(vault, nodeId, cooldownSec, nodeName) {
|
|
288
355
|
assertCooldown(cooldownSec);
|
|
289
356
|
return {
|
|
290
357
|
receiverId: vault,
|
|
@@ -293,21 +360,23 @@ export function planSetCooldown(vault, nodeId, cooldownSec) {
|
|
|
293
360
|
gasTgas: DEFAULT_GAS_TGAS,
|
|
294
361
|
depositYocto: ONE_YOCTO,
|
|
295
362
|
summary: cooldownSec
|
|
296
|
-
?
|
|
297
|
-
: `Remove the
|
|
363
|
+
? `${capitalise(budgetName(nodeId, nodeName))} must wait ${cooldownSec}s between payments`
|
|
364
|
+
: `Remove the waiting time between payments on ${budgetName(nodeId, nodeName)}`,
|
|
298
365
|
warnings: [],
|
|
299
366
|
};
|
|
300
367
|
}
|
|
301
|
-
export function planFreezeNode(vault, nodeId, frozen) {
|
|
368
|
+
export function planFreezeNode(vault, nodeId, frozen, nodeName) {
|
|
302
369
|
return {
|
|
303
370
|
receiverId: vault,
|
|
304
371
|
methodName: "freeze_node",
|
|
305
372
|
args: { node_id: nodeId, frozen },
|
|
306
373
|
gasTgas: DEFAULT_GAS_TGAS,
|
|
307
374
|
depositYocto: ONE_YOCTO,
|
|
308
|
-
summary: frozen
|
|
375
|
+
summary: frozen
|
|
376
|
+
? `Freeze ${budgetName(nodeId, nodeName)} so nothing under it can pay until you unfreeze it`
|
|
377
|
+
: `Unfreeze ${budgetName(nodeId, nodeName)} so it can pay again`,
|
|
309
378
|
warnings: frozen
|
|
310
|
-
? [`This
|
|
379
|
+
? [`This stops payments from ${budgetName(nodeId, nodeName)} and every budget under it.`]
|
|
311
380
|
: [],
|
|
312
381
|
};
|
|
313
382
|
}
|
|
@@ -466,7 +535,7 @@ export function toCliCommand(p, network = "testnet") {
|
|
|
466
535
|
* `toBaseUnits` throws rather than truncating excess precision — correct here:
|
|
467
536
|
* silently rounding 0.1234567 would set a threshold the owner did not choose.
|
|
468
537
|
*/
|
|
469
|
-
export function planSetApprovalThreshold(vault, nodeId, amount, decimals) {
|
|
538
|
+
export function planSetApprovalThreshold(vault, nodeId, amount, decimals, nodeName) {
|
|
470
539
|
const base = amount === null ? null : toBaseUnits(amount, decimals).toString();
|
|
471
540
|
return {
|
|
472
541
|
receiverId: vault,
|
|
@@ -478,8 +547,8 @@ export function planSetApprovalThreshold(vault, nodeId, amount, decimals) {
|
|
|
478
547
|
// 1000000 (base units)", which is unreadable in precisely the moment the
|
|
479
548
|
// owner is checking the plan before signing it.
|
|
480
549
|
summary: amount
|
|
481
|
-
? `Payments above ${amount} from
|
|
482
|
-
: `
|
|
550
|
+
? `Payments above ${amount} from ${budgetName(nodeId, nodeName)} will need your approval`
|
|
551
|
+
: `Stop requiring your approval on ${budgetName(nodeId, nodeName)} — payments inside the limits run unattended`,
|
|
483
552
|
warnings: amount
|
|
484
553
|
? []
|
|
485
554
|
: ["After this, nothing at this node waits for a human: only the caps apply."],
|
package/dist/roles.d.ts
CHANGED
|
@@ -4,6 +4,26 @@ export interface RoleCredential {
|
|
|
4
4
|
role: string;
|
|
5
5
|
nodeId?: number;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Entries belonging to this vault that the loader REJECTS, and why.
|
|
9
|
+
*
|
|
10
|
+
* `loadable` drops anything without a string `secret`, and the reasoning is
|
|
11
|
+
* sound — the adapter applies the same predicate, so listing such an entry
|
|
12
|
+
* would advertise an agent that can never be selected. But DROPPING IT
|
|
13
|
+
* SILENTLY is the same failure this file already has a memory about: the user
|
|
14
|
+
* holds a key, the tool shows nothing, and there is no way to tell "no agent"
|
|
15
|
+
* from "an agent in a shape I refuse to read".
|
|
16
|
+
*
|
|
17
|
+
* Shapes do vary in the wild: `prove-refusal.mjs` reads
|
|
18
|
+
* `privateKey ?? secret ?? agentKey`, so a file written by another path can
|
|
19
|
+
* land here unreadable. Reported, not hidden.
|
|
20
|
+
*/
|
|
21
|
+
export declare function listUnloadable(cfg: {
|
|
22
|
+
agentKeysPath?: string;
|
|
23
|
+
vault?: string;
|
|
24
|
+
}): {
|
|
25
|
+
reason: string;
|
|
26
|
+
}[];
|
|
7
27
|
export declare class RoleSelectionError extends Error {
|
|
8
28
|
}
|
|
9
29
|
export declare function selectRole(cfg: {
|
|
@@ -43,4 +63,13 @@ export declare function listAgents(cfg: {
|
|
|
43
63
|
agentId: string;
|
|
44
64
|
role: string | null;
|
|
45
65
|
nodeId?: number;
|
|
66
|
+
/**
|
|
67
|
+
* THE PUBLIC half, so a caller can ask the CHAIN about this key.
|
|
68
|
+
*
|
|
69
|
+
* It was omitted, and that omission is what made `vault_roles` unable to
|
|
70
|
+
* tell a working agent from a dead one: with no public key there is nothing
|
|
71
|
+
* to look up, so the only thing it could report was what the local file
|
|
72
|
+
* claimed. No key material — the public key is public.
|
|
73
|
+
*/
|
|
74
|
+
publicKey?: string;
|
|
46
75
|
}[];
|
package/dist/roles.js
CHANGED
|
@@ -11,6 +11,54 @@
|
|
|
11
11
|
* as a credential is how an agent ends up "configured" and unreachable.
|
|
12
12
|
*/
|
|
13
13
|
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
/**
|
|
15
|
+
* Entries belonging to this vault that the loader REJECTS, and why.
|
|
16
|
+
*
|
|
17
|
+
* `loadable` drops anything without a string `secret`, and the reasoning is
|
|
18
|
+
* sound — the adapter applies the same predicate, so listing such an entry
|
|
19
|
+
* would advertise an agent that can never be selected. But DROPPING IT
|
|
20
|
+
* SILENTLY is the same failure this file already has a memory about: the user
|
|
21
|
+
* holds a key, the tool shows nothing, and there is no way to tell "no agent"
|
|
22
|
+
* from "an agent in a shape I refuse to read".
|
|
23
|
+
*
|
|
24
|
+
* Shapes do vary in the wild: `prove-refusal.mjs` reads
|
|
25
|
+
* `privateKey ?? secret ?? agentKey`, so a file written by another path can
|
|
26
|
+
* land here unreadable. Reported, not hidden.
|
|
27
|
+
*/
|
|
28
|
+
export function listUnloadable(cfg) {
|
|
29
|
+
const path = cfg.agentKeysPath ?? ".float/agent-keys.json";
|
|
30
|
+
if (!existsSync(path))
|
|
31
|
+
return [];
|
|
32
|
+
let parsed;
|
|
33
|
+
try {
|
|
34
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
return [{ reason: `${path} is not readable JSON (${e.message}). A UTF-8 BOM does this.` }];
|
|
38
|
+
}
|
|
39
|
+
if (!Array.isArray(parsed))
|
|
40
|
+
return [{ reason: `${path} is not a JSON array.` }];
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const e of parsed) {
|
|
43
|
+
if (!e || typeof e !== "object")
|
|
44
|
+
continue;
|
|
45
|
+
const id = (e.agentId ?? e.vault);
|
|
46
|
+
// Only this vault's entries; another vault's are not this tool's business.
|
|
47
|
+
if (cfg.vault && id !== cfg.vault)
|
|
48
|
+
continue;
|
|
49
|
+
if (typeof e.agentId === "string" && typeof e.secret === "string")
|
|
50
|
+
continue; // loadable
|
|
51
|
+
const has = ["secret", "privateKey", "agentKey"].filter((k) => typeof e[k] === "string");
|
|
52
|
+
out.push({
|
|
53
|
+
reason: typeof e.agentId !== "string"
|
|
54
|
+
? `an entry for this vault has no string 'agentId', so it cannot be matched to a vault.`
|
|
55
|
+
: `an entry has no 'secret' field${has.length ? ` (it uses '${has.join("', '")}' instead)` : ""}, ` +
|
|
56
|
+
`which is the only field the keyring loader reads. It will never be selectable. ` +
|
|
57
|
+
`Re-create the agent with vault_add_role.`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
14
62
|
/** Entries that `loadKeyring` would accept, nothing else. */
|
|
15
63
|
function loadable(path) {
|
|
16
64
|
if (!existsSync(path))
|
|
@@ -113,5 +161,6 @@ export function listAgents(cfg) {
|
|
|
113
161
|
agentId: e.agentId,
|
|
114
162
|
role: typeof e.role === "string" ? e.role : null,
|
|
115
163
|
nodeId: typeof e.nodeId === "number" ? e.nodeId : undefined,
|
|
164
|
+
publicKey: typeof e.publicKey === "string" ? e.publicKey : undefined,
|
|
116
165
|
}));
|
|
117
166
|
}
|