create-metamynd-agent 0.1.0 → 0.2.0
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/index.mjs +47 -15
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
// npx create-metamynd-agent
|
|
13
13
|
// npx create-metamynd-agent --api http://localhost:9926/api/v1 --email you@x.com \
|
|
14
14
|
// --name "Support Bot" --scope flight-purchase --per-txn-max 500 --out ./support-bot --yes
|
|
15
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
15
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } from 'node:fs';
|
|
16
16
|
import { join, resolve } from 'node:path';
|
|
17
17
|
import readline from 'node:readline';
|
|
18
18
|
import crypto from 'node:crypto';
|
|
19
19
|
|
|
20
20
|
const GUARD_PKG = '@metamynd/agentsafe-guard';
|
|
21
|
-
const GUARD_VERSION = '^0.
|
|
21
|
+
const GUARD_VERSION = '^0.3.0';
|
|
22
22
|
const DEFAULT_API = 'https://metamynd.ai/api/v1';
|
|
23
23
|
|
|
24
24
|
// ---------- tiny ANSI ----------
|
|
@@ -39,6 +39,8 @@ function parseArgs(argv) {
|
|
|
39
39
|
if (a === '-h' || a === '--help') { out.help = true; continue; }
|
|
40
40
|
if (a === '-v' || a === '--version') { out.version = true; continue; }
|
|
41
41
|
if (a === '-y' || a === '--yes' || a === '--non-interactive') { out.yes = true; continue; }
|
|
42
|
+
// Explicit, so `--force ./dir` cannot swallow the path as this flag's value.
|
|
43
|
+
if (a === '-f' || a === '--force') { out.force = true; continue; }
|
|
42
44
|
if (a.startsWith('--')) {
|
|
43
45
|
const eq = a.indexOf('=');
|
|
44
46
|
if (eq !== -1) { out[a.slice(2, eq)] = a.slice(eq + 1); continue; }
|
|
@@ -62,6 +64,7 @@ ${c.b('Options')}
|
|
|
62
64
|
--request Delegated: request an agent for an owner's org (--owner <email>, +--byok)
|
|
63
65
|
--claim [--watch] Delegated: claim the config once the owner approves (reads metamynd-request.json)
|
|
64
66
|
--owner <email> Target owner's email (with --request)
|
|
67
|
+
--force, -f Scaffold into a non-empty directory, overwriting existing files
|
|
65
68
|
--api <url> API base (default ${DEFAULT_API})
|
|
66
69
|
--email <email> Owner login email
|
|
67
70
|
--password <pw> Owner password (prefer the interactive prompt or METAMYND_PASSWORD)
|
|
@@ -272,22 +275,48 @@ function gitignore() {
|
|
|
272
275
|
return `node_modules/\nagent.metamynd.json\n.env\n`;
|
|
273
276
|
}
|
|
274
277
|
|
|
275
|
-
function writeFileSafe(dir, name, content) {
|
|
278
|
+
function writeFileSafe(dir, name, content, force = false) {
|
|
276
279
|
const p = join(dir, name);
|
|
277
|
-
|
|
280
|
+
const exists = existsSync(p);
|
|
281
|
+
if (exists && !force) { console.log(` ${c.yellow('skip')} ${name} ${c.dim('(exists)')}`); return; }
|
|
278
282
|
writeFileSync(p, content);
|
|
279
|
-
console.log(` ${c.green('create')} ${name}`);
|
|
283
|
+
console.log(` ${exists ? c.yellow('overwrite') : c.green('create')} ${name}`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Refuse to scaffold into a non-empty directory unless --force.
|
|
288
|
+
*
|
|
289
|
+
* Silently skipping an existing agent.metamynd.json is worse than it sounds:
|
|
290
|
+
* provisioning has already minted a NEW agent server-side, so the scaffold prints
|
|
291
|
+
* success while leaving the OLD config in place. Every later gate call then runs as
|
|
292
|
+
* the previous identity, against whatever apiBase that file happens to carry — which
|
|
293
|
+
* is exactly how a stale http:// base survived a re-scaffold and 404'd every call.
|
|
294
|
+
*/
|
|
295
|
+
function assertScaffoldTarget(outDir, force) {
|
|
296
|
+
if (force || !existsSync(outDir)) return;
|
|
297
|
+
const entries = readdirSync(outDir);
|
|
298
|
+
if (entries.length === 0) return;
|
|
299
|
+
const rel = outDir.replace(resolve('.'), '.').replace(/\\/g, '/');
|
|
300
|
+
fail(
|
|
301
|
+
`${rel} is not empty (${entries.length} item${entries.length === 1 ? '' : 's'}).\n\n` +
|
|
302
|
+
` Scaffolding here would KEEP the existing files — including any agent.metamynd.json —\n` +
|
|
303
|
+
` so this project would keep running as the identity in that file, against the apiBase\n` +
|
|
304
|
+
` in that file, and the newly provisioned agent would go unused.\n\n` +
|
|
305
|
+
` Scaffold somewhere new: --out ./another-dir\n` +
|
|
306
|
+
` or overwrite this one on purpose: --force`,
|
|
307
|
+
);
|
|
280
308
|
}
|
|
281
309
|
|
|
282
310
|
/** Write the scaffolded project + print next steps. Shared by the provision and sandbox paths. */
|
|
283
|
-
function scaffoldProject({ outDir, config, slug, scope, perTxnMax, sandbox }) {
|
|
311
|
+
function scaffoldProject({ outDir, config, slug, scope, perTxnMax, sandbox, force = false }) {
|
|
312
|
+
assertScaffoldTarget(outDir, force);
|
|
284
313
|
console.log(`\n ${c.b('Scaffolding')} ${c.dim(outDir)}`);
|
|
285
314
|
if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
|
|
286
|
-
writeFileSafe(outDir, 'agent.metamynd.json', JSON.stringify(config, null, 2) + '\n');
|
|
287
|
-
writeFileSafe(outDir, 'index.mjs', exampleIndex(scope, perTxnMax));
|
|
288
|
-
writeFileSafe(outDir, 'package.json', examplePackageJson(slug));
|
|
289
|
-
writeFileSafe(outDir, '.gitignore', gitignore());
|
|
290
|
-
writeFileSafe(outDir, 'README.md', exampleReadme(slug, scope));
|
|
315
|
+
writeFileSafe(outDir, 'agent.metamynd.json', JSON.stringify(config, null, 2) + '\n', force);
|
|
316
|
+
writeFileSafe(outDir, 'index.mjs', exampleIndex(scope, perTxnMax), force);
|
|
317
|
+
writeFileSafe(outDir, 'package.json', examplePackageJson(slug), force);
|
|
318
|
+
writeFileSafe(outDir, '.gitignore', gitignore(), force);
|
|
319
|
+
writeFileSafe(outDir, 'README.md', exampleReadme(slug, scope), force);
|
|
291
320
|
|
|
292
321
|
const rel = outDir.replace(resolve('.'), '.').replace(/\\/g, '/');
|
|
293
322
|
console.log(`\n${c.green(c.b(' ✓ Done.'))} Your governed agent is ready.\n`);
|
|
@@ -307,6 +336,10 @@ function scaffoldProject({ outDir, config, slug, scope, perTxnMax, sandbox }) {
|
|
|
307
336
|
async function runSandbox(args) {
|
|
308
337
|
const apiRaw = (typeof args.api === 'string' ? args.api : undefined) ?? process.env.METAMYND_API ?? DEFAULT_API;
|
|
309
338
|
const base = String(apiRaw).replace(/\/+$/, '');
|
|
339
|
+
// Check the target BEFORE provisioning: refusing afterwards would mint an agent
|
|
340
|
+
// server-side and then throw it away.
|
|
341
|
+
const outDir = resolve(String(args.out || './metamynd-sandbox'));
|
|
342
|
+
assertScaffoldTarget(outDir, !!args.force);
|
|
310
343
|
console.log(c.dim(` → requesting a sandbox agent from ${base} …`));
|
|
311
344
|
const provisioned = await apiPost(base, '/onboarding/sandbox', {}, null);
|
|
312
345
|
const config = provisioned?.data;
|
|
@@ -314,8 +347,7 @@ async function runSandbox(args) {
|
|
|
314
347
|
console.log(` ${c.green('✓')} sandbox agent ${c.b(config.agentDid)} ${c.dim('(shared test identity)')}`);
|
|
315
348
|
const scope = config.mandate?.scope || 'flight-purchase';
|
|
316
349
|
const perTxnMax = Number(config.perTxnMax) || 500;
|
|
317
|
-
|
|
318
|
-
scaffoldProject({ outDir, config, slug: 'metamynd-sandbox', scope, perTxnMax, sandbox: true });
|
|
350
|
+
scaffoldProject({ outDir, config, slug: 'metamynd-sandbox', scope, perTxnMax, sandbox: true, force: !!args.force });
|
|
319
351
|
}
|
|
320
352
|
|
|
321
353
|
// ---------- delegated issuance (#6) ----------
|
|
@@ -429,7 +461,7 @@ async function runClaim(args) {
|
|
|
429
461
|
|
|
430
462
|
const slug = slugify(state.name || 'metamynd-agent');
|
|
431
463
|
const outDir = resolve(String(args.out || `./${slug}`));
|
|
432
|
-
scaffoldProject({ outDir, config, slug, scope: state.scope || config.mandate?.scope || 'flight-purchase', perTxnMax: Number(state.perTxnMax) || 500, sandbox: false });
|
|
464
|
+
scaffoldProject({ outDir, config, slug, scope: state.scope || config.mandate?.scope || 'flight-purchase', perTxnMax: Number(state.perTxnMax) || 500, sandbox: false, force: !!args.force });
|
|
433
465
|
}
|
|
434
466
|
|
|
435
467
|
// ---------- main ----------
|
|
@@ -536,7 +568,7 @@ async function main() {
|
|
|
536
568
|
}
|
|
537
569
|
|
|
538
570
|
// 4. Scaffold + next steps
|
|
539
|
-
scaffoldProject({ outDir, config, slug, scope, perTxnMax, sandbox: false });
|
|
571
|
+
scaffoldProject({ outDir, config, slug, scope, perTxnMax, sandbox: false, force: !!args.force });
|
|
540
572
|
}
|
|
541
573
|
|
|
542
574
|
main().catch((e) => fail(e?.stack || e?.message || String(e)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-metamynd-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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, and drops a runnable example.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|