create-metamynd-agent 0.9.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/README.md +24 -4
- package/index.mjs +153 -14
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -236,6 +236,8 @@ METAMYND_PASSWORD='…' npx create-metamynd-agent --yes …
|
|
|
236
236
|
| `--merchants <a,b>` | — | any |
|
|
237
237
|
| `--byok` | — | generate the keypair locally, provision + prove control |
|
|
238
238
|
| `--public-key <hex>` | — | BYOK with a key you already hold (you prove control yourself) |
|
|
239
|
+
| `--daemon-socket <p>` | — | `--byok` via an already-running agentsafe-signer daemon instead of locally (needs `--daemon-admin-socket` too) |
|
|
240
|
+
| `--daemon-admin-socket <p>` | — | that daemon's admin socket, for `generate-key` |
|
|
239
241
|
| `--out <dir>` | — | `./<agent-slug>` |
|
|
240
242
|
| `--yes`, `-y` | — | non-interactive |
|
|
241
243
|
|
|
@@ -284,10 +286,28 @@ npx create-metamynd-agent --byok --email you@example.com --name "Support Bot"
|
|
|
284
286
|
```
|
|
285
287
|
|
|
286
288
|
Generates an Ed25519 keypair **on your machine**, provisions the agent with only the public key, then
|
|
287
|
-
proves control (signs the one-time challenge → `verify-key`). MetaMynd never sees the private key.
|
|
288
|
-
generated private key is written into `agent.metamynd.json` (gitignored)
|
|
289
|
-
|
|
290
|
-
prints the challenge
|
|
289
|
+
proves control (signs the one-time challenge → `verify-key`). MetaMynd never sees the private key. By
|
|
290
|
+
default the generated private key is written into `agent.metamynd.json` (gitignored) — the same
|
|
291
|
+
process this CLI runs in holds it, at least briefly. Pass `--public-key <hex>` instead to register a
|
|
292
|
+
key you already hold elsewhere — then you complete `verify-key` yourself (the CLI prints the challenge
|
|
293
|
+
+ endpoint).
|
|
294
|
+
|
|
295
|
+
### Keeping the key out of this process entirely (`--daemon-socket`)
|
|
296
|
+
|
|
297
|
+
If you already have an [agentsafe-signer](../agentsafe-signer/README.md) daemon running for this
|
|
298
|
+
agent (`agentsafe-signer start --admin`, per its own install guide), point `--byok` at it instead:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
npx create-metamynd-agent --byok --daemon-socket ./.agentsafe-signer/signer.sock \
|
|
302
|
+
--daemon-admin-socket ./.agentsafe-signer/signer-admin.sock \
|
|
303
|
+
--email you@example.com --name "Support Bot"
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The daemon generates the key and signs the `verify-key` challenge itself — the private key never
|
|
307
|
+
enters this CLI's process at all, not even briefly. `agent.metamynd.json` gets `keyProvider: 'daemon'`
|
|
308
|
+
+ `daemonSocketPath` instead of a plaintext key (see `@metamynd/agentsafe-guard`'s `key-providers.mjs`
|
|
309
|
+
for how the scaffolded guard resolves that). Requires both flags together, and only applies when no
|
|
310
|
+
`--public-key` is given (an external key has nothing for the daemon to generate).
|
|
291
311
|
|
|
292
312
|
## Delegated issuance (`--request` / `--claim`)
|
|
293
313
|
|
package/index.mjs
CHANGED
|
@@ -18,6 +18,7 @@ import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } from
|
|
|
18
18
|
import { join, resolve } from 'node:path';
|
|
19
19
|
import readline from 'node:readline';
|
|
20
20
|
import crypto from 'node:crypto';
|
|
21
|
+
import net from 'node:net';
|
|
21
22
|
|
|
22
23
|
const GUARD_PKG = '@metamynd/agentsafe-guard';
|
|
23
24
|
// Must track the guard's MINOR line, not just its major. On a 0.x package `^0.4.0` means
|
|
@@ -32,7 +33,21 @@ const GUARD_PKG = '@metamynd/agentsafe-guard';
|
|
|
32
33
|
// 0.8.0 adds an optional `currency` scope to the amount-over/cumulative-over atoms
|
|
33
34
|
// (harnessDefaultSop, below, now sets it) — a guard below this version can't evaluate that
|
|
34
35
|
// field, so a scaffolded currency-scoped cap would silently never fire on a currency mismatch.
|
|
35
|
-
|
|
36
|
+
// 0.9.0 makes buildSignedRequest() async (the keyProvider seam, docs/design/
|
|
37
|
+
// agent-key-custody-local-signer-daemon-plan.md) — this scaffold's own bookFlightViaGateway/
|
|
38
|
+
// callGateway templates now `await` it, so a guard below this version would hand back a
|
|
39
|
+
// Promise object where a signed request is expected instead of failing loudly.
|
|
40
|
+
// 0.10.0 adds `resource` as a genuinely signed field (mirrors the mandate's own
|
|
41
|
+
// ResourceService.scopeConstraint()) — no scaffolded template passes it yet, but the floor
|
|
42
|
+
// must still cover the real current version regardless, per this repo's standing
|
|
43
|
+
// internal-pin invariant.
|
|
44
|
+
// 0.11.0 adds `signLocalDecision` support to `createDaemonKeyProvider` — this scaffold's
|
|
45
|
+
// `--byok --daemon-socket` path can now get local-decision audit reporting too, but no
|
|
46
|
+
// template code changes yet; the floor must still cover the real current version.
|
|
47
|
+
// 0.12.0 adds passphrase-encrypted managed key delivery (createGuardFromConfig's
|
|
48
|
+
// `{ passphrase }`) — no scaffolded template passes one yet, but the floor must still cover
|
|
49
|
+
// the real current version regardless, per this repo's standing internal-pin invariant.
|
|
50
|
+
const GUARD_VERSION = '^0.12.0';
|
|
36
51
|
// The default hosted scaffold's SECOND process — the tool gateway (see scaffoldProject).
|
|
37
52
|
const MCP_GUARD_PKG = '@metamynd/agentsafe-mcp-guard';
|
|
38
53
|
// 0.2.0 adds requireAuthorization (closes replay + cumulative spend) — this scaffold sets that
|
|
@@ -40,7 +55,14 @@ const MCP_GUARD_PKG = '@metamynd/agentsafe-mcp-guard';
|
|
|
40
55
|
// 0.3.0 adds the same amount-unknown atom as the guard, above — same reasoning, same miss.
|
|
41
56
|
// 0.4.0 adds the same amount-over/cumulative-over `currency` scope as the guard, above —
|
|
42
57
|
// same reasoning, same miss.
|
|
43
|
-
|
|
58
|
+
// 0.5.0 adds the keyProvider seam alongside the guard's own 0.9.0 (same design doc) — this
|
|
59
|
+
// scaffold's createMcpGuard() calls never use a handshake here, so no template code changes,
|
|
60
|
+
// but the floor must still cover the real current version regardless, per this repo's
|
|
61
|
+
// standing internal-pin invariant.
|
|
62
|
+
// 0.6.0 brings buildAuthMessage's `resource` field and buildLocalDecisionMessage into this
|
|
63
|
+
// package's own bundled policy-core.mjs (alongside the guard's own 0.10.0) — no scaffolded
|
|
64
|
+
// template code changes, but the floor must still cover the real current version.
|
|
65
|
+
const MCP_GUARD_VERSION = '^0.6.0';
|
|
44
66
|
const GATEWAY_PKG = '@metamynd/agentsafe-http-gateway';
|
|
45
67
|
// 0.2.0 fixes a confused-deputy gap (payload not bound to the signed request) — the CLI must
|
|
46
68
|
// never scaffold a range that could resolve below it.
|
|
@@ -118,6 +140,13 @@ ${c.b('Options')}
|
|
|
118
140
|
--byok Bring-your-own-key: generate the keypair locally, provision + prove control
|
|
119
141
|
(MetaMynd never sees the private key). Overridden by --public-key.
|
|
120
142
|
--public-key <hex> BYOK with a key you already hold (SPKI/raw hex); you prove control yourself
|
|
143
|
+
--daemon-socket <p> --byok only: use an already-running agentsafe-signer daemon (started
|
|
144
|
+
separately, e.g. \`agentsafe-signer start --admin\`) to generate the key and
|
|
145
|
+
prove control instead — the private key never enters this CLI's process at
|
|
146
|
+
all, and agent.metamynd.json gets keyProvider:'daemon' instead of a
|
|
147
|
+
plaintext key. Requires --daemon-admin-socket too. See README#byok.
|
|
148
|
+
--daemon-admin-socket <p> The same daemon's admin socket (for generate-key) — required with
|
|
149
|
+
--daemon-socket.
|
|
121
150
|
--out <dir> Output project directory (default ./<agent-slug>)
|
|
122
151
|
--no-gateway Hosted flow only: skip the separate tool-gateway process (see
|
|
123
152
|
README#separate-tool-gateway-default) and scaffold the old
|
|
@@ -310,6 +339,72 @@ function signChallengeHex(privateKeyHex, challenge) {
|
|
|
310
339
|
return crypto.sign(null, Buffer.from(challenge, 'utf8'), key).toString('hex');
|
|
311
340
|
}
|
|
312
341
|
|
|
342
|
+
// ---------- BYOK via an already-running agentsafe-signer daemon (--daemon-socket) ----------
|
|
343
|
+
// Opt-in alternative to generateAgentKeypair() above: instead of generating the keypair in THIS
|
|
344
|
+
// process and writing it in plaintext into agent.metamynd.json, ask an already-running
|
|
345
|
+
// agentsafe-signer daemon (docs/design/agent-key-custody-local-signer-daemon-plan.md — started
|
|
346
|
+
// separately, e.g. `agentsafe-signer start --admin`) to generate the key and sign the
|
|
347
|
+
// proof-of-possession challenge. The private key never enters this process at all, and the
|
|
348
|
+
// scaffolded config gets `keyProvider: 'daemon'` instead of a plaintext `agentKey` — see
|
|
349
|
+
// agentsafe-guard/key-providers.mjs's resolveKeyProvider(), which reads that field exactly.
|
|
350
|
+
//
|
|
351
|
+
// Vendored rather than depending on @metamynd/agentsafe-signer or @metamynd/agentsafe-guard for
|
|
352
|
+
// it — this CLI is intentionally zero-dependency, and this is the SAME small, self-contained
|
|
353
|
+
// reimplementation of the daemon's local JSON-over-socket protocol that agentsafe-guard/
|
|
354
|
+
// key-providers.mjs and agentsafe-mcp-guard/key-providers.mjs already each carry their own copy
|
|
355
|
+
// of, rather than a fourth package depending on a signer package built for a persistent service,
|
|
356
|
+
// not a one-shot scaffolding command.
|
|
357
|
+
function toPlatformSocketPath(logicalPath) {
|
|
358
|
+
if (process.platform !== 'win32') return logicalPath;
|
|
359
|
+
const name = crypto.createHash('sha256').update(resolve(logicalPath)).digest('hex').slice(0, 32);
|
|
360
|
+
return `\\\\.\\pipe\\agentsafe-signer-${name}`;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function daemonRequest(socketPath, op, params, { connectTimeoutMs = 5000 } = {}) {
|
|
364
|
+
return new Promise((resolve_, reject) => {
|
|
365
|
+
const deadline = Date.now() + connectTimeoutMs;
|
|
366
|
+
let settled = false;
|
|
367
|
+
const overallTimer = setTimeout(() => {
|
|
368
|
+
settled = true;
|
|
369
|
+
reject(Object.assign(new Error(`agentsafe-signer daemon unreachable at ${socketPath}: timed out after ${connectTimeoutMs}ms`), { code: 'DAEMON_UNREACHABLE' }));
|
|
370
|
+
}, connectTimeoutMs);
|
|
371
|
+
function attempt() {
|
|
372
|
+
if (settled) return;
|
|
373
|
+
const sock = net.connect(toPlatformSocketPath(socketPath));
|
|
374
|
+
const requestId = crypto.randomUUID();
|
|
375
|
+
let buf = '';
|
|
376
|
+
const cleanup = () => sock.destroy();
|
|
377
|
+
sock.once('error', (err) => {
|
|
378
|
+
cleanup();
|
|
379
|
+
if (settled) return;
|
|
380
|
+
if (err.code === 'ENOENT' && Date.now() < deadline) { setTimeout(attempt, 20); return; }
|
|
381
|
+
settled = true;
|
|
382
|
+
clearTimeout(overallTimer);
|
|
383
|
+
reject(Object.assign(new Error(`agentsafe-signer daemon unreachable at ${socketPath}: ${err.message}`), { code: 'DAEMON_UNREACHABLE' }));
|
|
384
|
+
});
|
|
385
|
+
sock.once('connect', () => {
|
|
386
|
+
if (settled) return;
|
|
387
|
+
sock.write(JSON.stringify({ protocolVersion: 1, requestId, op, params }) + '\n');
|
|
388
|
+
});
|
|
389
|
+
sock.on('data', (chunk) => {
|
|
390
|
+
if (settled) return;
|
|
391
|
+
buf += chunk.toString('utf8');
|
|
392
|
+
const idx = buf.indexOf('\n');
|
|
393
|
+
if (idx === -1) return;
|
|
394
|
+
let res;
|
|
395
|
+
try { res = JSON.parse(buf.slice(0, idx)); }
|
|
396
|
+
catch (err) { cleanup(); settled = true; clearTimeout(overallTimer); reject(err); return; }
|
|
397
|
+
cleanup();
|
|
398
|
+
settled = true;
|
|
399
|
+
clearTimeout(overallTimer);
|
|
400
|
+
if (res.ok) resolve_(res.result);
|
|
401
|
+
else reject(Object.assign(new Error(res.error?.message || res.error?.code || 'daemon rejected request'), { code: res.error?.code }));
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
attempt();
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
313
408
|
// ---------- API ----------
|
|
314
409
|
async function apiPost(base, path, body, token) {
|
|
315
410
|
let res;
|
|
@@ -522,7 +617,7 @@ const GATEWAY = process.env.GATEWAY_URL || 'http://localhost:${gatewayPort}';
|
|
|
522
617
|
// --- the gateway atomically claim single-use execution, closing replay + cumulative spend, not
|
|
523
618
|
// --- just re-checking policy. See ./gateway/README.md.
|
|
524
619
|
async function bookFlightViaGateway(args, decision) {
|
|
525
|
-
const signed = guard.buildSignedRequest({
|
|
620
|
+
const signed = await guard.buildSignedRequest({
|
|
526
621
|
action: '${scope}',
|
|
527
622
|
amount: args.amount,
|
|
528
623
|
currency: 'USD',
|
|
@@ -698,7 +793,13 @@ function examplePackageJson(slug) {
|
|
|
698
793
|
) + '\n';
|
|
699
794
|
}
|
|
700
795
|
|
|
701
|
-
function exampleReadme(slug, scope, withGateway, gatewayPort) {
|
|
796
|
+
function exampleReadme(slug, scope, withGateway, gatewayPort, daemonKey = false) {
|
|
797
|
+
const configFileLine = daemonKey
|
|
798
|
+
? `- \`agent.metamynd.json\` — your portable guard config (identity, mandate scope \`${scope}\`, issuer keys).
|
|
799
|
+
**Holds no secret key.** Signing goes through your already-running agentsafe-signer daemon
|
|
800
|
+
(\`daemonSocketPath\`) instead — see \`docs/integration/INSTALL-AGENTSAFE-SIGNER.md\`.`
|
|
801
|
+
: `- \`agent.metamynd.json\` — your portable guard config (identity, mandate scope \`${scope}\`, issuer keys).
|
|
802
|
+
**Contains the agent's secret key — never commit it.** It is already in \`.gitignore\`.`;
|
|
702
803
|
const gatewaySection = withGateway
|
|
703
804
|
? `## Run
|
|
704
805
|
|
|
@@ -721,8 +822,7 @@ an ESCALATE (high risk). The BLOCK and ESCALATE never reach the gateway at all
|
|
|
721
822
|
|
|
722
823
|
## Files
|
|
723
824
|
|
|
724
|
-
|
|
725
|
-
**Contains the agent's secret key — never commit it.** It is already in \`.gitignore\`.
|
|
825
|
+
${configFileLine}
|
|
726
826
|
- \`index.mjs\` — signs each request and calls \`./gateway\` for it; \`guard.guardTool()\` here is a
|
|
727
827
|
fast local pre-check, not the enforcement boundary.
|
|
728
828
|
- \`gateway/\` — a **separate process**. It holds the real tool and independently re-verifies every
|
|
@@ -743,8 +843,7 @@ You should see an ALLOW, a BLOCK (over the per-transaction cap), and an ESCALATE
|
|
|
743
843
|
|
|
744
844
|
## Files
|
|
745
845
|
|
|
746
|
-
|
|
747
|
-
**Contains the agent's secret key — never commit it.** It is already in \`.gitignore\`.
|
|
846
|
+
${configFileLine}
|
|
748
847
|
- \`index.mjs\` — wraps a tool with \`guard.guardTool(...)\`; the tool only runs when the gate allows.
|
|
749
848
|
|
|
750
849
|
## What this is not
|
|
@@ -1055,7 +1154,7 @@ function scaffoldProject({ outDir, config, slug, scope, perTxnMax, sandbox, with
|
|
|
1055
1154
|
writeFileSafe(outDir, 'index.mjs', withGateway ? exampleIndex(scope, perTxnMax, gatewayPort) : exampleIndexNoGateway(scope, perTxnMax), force);
|
|
1056
1155
|
writeFileSafe(outDir, 'package.json', examplePackageJson(slug), force);
|
|
1057
1156
|
writeFileSafe(outDir, '.gitignore', gitignore(), force);
|
|
1058
|
-
writeFileSafe(outDir, 'README.md', exampleReadme(slug, scope, withGateway, gatewayPort), force);
|
|
1157
|
+
writeFileSafe(outDir, 'README.md', exampleReadme(slug, scope, withGateway, gatewayPort, config.keyProvider === 'daemon'), force);
|
|
1059
1158
|
|
|
1060
1159
|
if (withGateway) {
|
|
1061
1160
|
const apiBase = config.apiBase ?? config.api ?? DEFAULT_API;
|
|
@@ -1072,6 +1171,8 @@ function scaffoldProject({ outDir, config, slug, scope, perTxnMax, sandbox, with
|
|
|
1072
1171
|
console.log(`\n${c.green(c.b(' ✓ Done.'))} Your governed agent is ready.\n`);
|
|
1073
1172
|
if (sandbox) {
|
|
1074
1173
|
console.log(` ${c.dim('Shared sandbox agent — for trying MetaMynd only. Provision your own (drop --sandbox) for anything real.')}\n`);
|
|
1174
|
+
} else if (config.keyProvider === 'daemon') {
|
|
1175
|
+
console.log(` ${c.dim('agent.metamynd.json holds no secret key — signing goes through your agentsafe-signer daemon at')} ${c.b(config.daemonSocketPath)}${c.dim('.')}\n`);
|
|
1075
1176
|
} else if (config.agentKey) {
|
|
1076
1177
|
console.log(` ${c.yellow('⚠ agent.metamynd.json holds the agent secret key')} — it is gitignored; never commit it.\n`);
|
|
1077
1178
|
}
|
|
@@ -1730,7 +1831,7 @@ const GATEWAY = process.env.HARNESS_GATEWAY_URL || 'http://localhost:${gatewayPo
|
|
|
1730
1831
|
// issuer): it builds and signs the same canonical message a real gate would verify, entirely
|
|
1731
1832
|
// offline, using this agent's own did:key — the gateway verifies that signature for itself.
|
|
1732
1833
|
async function callGateway(path, action, args) {
|
|
1733
|
-
const signed = guard.buildSignedRequest({ action, amount: args.amount, currency: 'USD', merchant: args.merchant, context: { tool: '${scope}', riskLevel: args.riskLevel ?? 'low' } });
|
|
1834
|
+
const signed = await guard.buildSignedRequest({ action, amount: args.amount, currency: 'USD', merchant: args.merchant, context: { tool: '${scope}', riskLevel: args.riskLevel ?? 'low' } });
|
|
1734
1835
|
const res = await fetch(GATEWAY + path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ signed, args }) });
|
|
1735
1836
|
const body = await res.json().catch(() => null);
|
|
1736
1837
|
if (!res.ok) {
|
|
@@ -2224,11 +2325,35 @@ async function main() {
|
|
|
2224
2325
|
);
|
|
2225
2326
|
const merchants = String(merchantsRaw).split(',').map((s) => s.trim()).filter(Boolean);
|
|
2226
2327
|
|
|
2227
|
-
// BYOK: --byok generates a keypair on THIS machine (MetaMynd never sees the private key)
|
|
2228
|
-
//
|
|
2328
|
+
// BYOK: --byok generates a keypair on THIS machine (MetaMynd never sees the private key) —
|
|
2329
|
+
// either locally in this process (default) or, opt-in, via an already-running agentsafe-signer
|
|
2330
|
+
// daemon (--daemon-socket + --daemon-admin-socket, see their own help text) so the private key
|
|
2331
|
+
// never enters this process at all. An explicit --public-key means the caller holds the key
|
|
2332
|
+
// elsewhere and will prove it themselves — daemon flags are meaningless with it.
|
|
2229
2333
|
let publicKey = typeof args['public-key'] === 'string' ? args['public-key'] : undefined;
|
|
2334
|
+
const daemonSocket = typeof args['daemon-socket'] === 'string' ? args['daemon-socket'] : undefined;
|
|
2335
|
+
const daemonAdminSocket = typeof args['daemon-admin-socket'] === 'string' ? args['daemon-admin-socket'] : undefined;
|
|
2336
|
+
if (Boolean(daemonSocket) !== Boolean(daemonAdminSocket)) {
|
|
2337
|
+
rl?.close();
|
|
2338
|
+
fail('--daemon-socket and --daemon-admin-socket must be used together.');
|
|
2339
|
+
}
|
|
2340
|
+
if (daemonSocket && !args.byok) {
|
|
2341
|
+
rl?.close();
|
|
2342
|
+
fail('--daemon-socket requires --byok.');
|
|
2343
|
+
}
|
|
2344
|
+
if (daemonSocket && publicKey) {
|
|
2345
|
+
rl?.close();
|
|
2346
|
+
fail('--daemon-socket generates its own key — pass --byok alone, not --public-key.');
|
|
2347
|
+
}
|
|
2230
2348
|
let generatedKey = null;
|
|
2231
|
-
|
|
2349
|
+
let daemonPublicKeyHex = null;
|
|
2350
|
+
if (args.byok && !publicKey && daemonSocket) {
|
|
2351
|
+
console.log(c.dim(' → asking the agentsafe-signer daemon to generate a key …'));
|
|
2352
|
+
const { publicKeyHex } = await daemonRequest(daemonAdminSocket, 'generate-key', { allowRekey: false });
|
|
2353
|
+
daemonPublicKeyHex = publicKeyHex;
|
|
2354
|
+
publicKey = publicKeyHex;
|
|
2355
|
+
console.log(` ${c.green('✓')} generated an Ed25519 keypair via the signer daemon ${c.dim('(the private key never left it)')}`);
|
|
2356
|
+
} else if (args.byok && !publicKey) {
|
|
2232
2357
|
generatedKey = generateAgentKeypair();
|
|
2233
2358
|
publicKey = generatedKey.publicKeyHex;
|
|
2234
2359
|
console.log(` ${c.green('✓')} generated an Ed25519 keypair locally ${c.dim('(private key stays on this machine)')}`);
|
|
@@ -2253,7 +2378,21 @@ async function main() {
|
|
|
2253
2378
|
if (config.standards?.length) console.log(` ${c.green('✓')} enforced Standards: ${config.standards.join(', ')}`);
|
|
2254
2379
|
|
|
2255
2380
|
// 3b. BYOK: prove control of the key (verify-key), else the gate blocks with AGENT_KEY_UNVERIFIED.
|
|
2256
|
-
if (
|
|
2381
|
+
if (daemonPublicKeyHex) {
|
|
2382
|
+
// The daemon holds the private key — it never entered this process. Point the scaffolded
|
|
2383
|
+
// guard at the daemon instead of embedding a plaintext key (agentsafe-guard/key-providers.mjs's
|
|
2384
|
+
// resolveKeyProvider() reads these two fields and never looks for `agentKey` when present).
|
|
2385
|
+
config.keyProvider = 'daemon';
|
|
2386
|
+
config.daemonSocketPath = daemonSocket;
|
|
2387
|
+
if (config.challenge) {
|
|
2388
|
+
console.log(c.dim(' → proving key control via the daemon (verify-key) …'));
|
|
2389
|
+
const { signature } = await daemonRequest(daemonSocket, 'sign-key-control-challenge', { challenge: config.challenge });
|
|
2390
|
+
await apiPost(base, `/agent-identity/${encodeURIComponent(config.identityId)}/verify-key`, { signature }, token);
|
|
2391
|
+
config.keyVerified = true;
|
|
2392
|
+
delete config.challenge; // one-time; consumed
|
|
2393
|
+
console.log(` ${c.green('✓')} key verified — MetaMynd never saw your private key, and neither did this CLI`);
|
|
2394
|
+
}
|
|
2395
|
+
} else if (generatedKey) {
|
|
2257
2396
|
// We hold the private key — inject it into the config so the scaffolded guard can sign, and
|
|
2258
2397
|
// prove possession by signing the issued challenge.
|
|
2259
2398
|
config.agentKey = generatedKey.privateKeyHex;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-metamynd-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "Scaffold a MetaMynd/AgentSafe-governed AI agent in one command — logs in, provisions the agent (identity + mandate + SOP + Standards) in a single call, writes agent.metamynd.json plus a runnable agent + separate tool-gateway process that closes direct-call, confused-deputy, replay, and cumulative-spend bypasses. --harness scaffolds a free, local, zero-network governance harness instead.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"create-metamynd-agent": "index.mjs"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "node harness-gateway.smoke.mjs"
|
|
10
|
+
"test": "node harness-gateway.smoke.mjs && node byok-daemon.smoke.mjs"
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"index.mjs",
|