@4mica/cli 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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/index.js +628 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +68 -0
  6. package/snapshot.json +100 -0
  7. package/templates/agent-buyer-node/.env.example +16 -0
  8. package/templates/agent-buyer-node/README.md +26 -0
  9. package/templates/agent-buyer-node/_gitignore +6 -0
  10. package/templates/agent-buyer-node/package.json +19 -0
  11. package/templates/agent-buyer-node/src/brain.ts +133 -0
  12. package/templates/agent-buyer-node/src/critic.ts +195 -0
  13. package/templates/agent-buyer-node/src/pay.ts +54 -0
  14. package/templates/agent-buyer-node/tsconfig.json +21 -0
  15. package/templates/agent-seller-express/.env.example +16 -0
  16. package/templates/agent-seller-express/README.md +26 -0
  17. package/templates/agent-seller-express/_gitignore +6 -0
  18. package/templates/agent-seller-express/package.json +22 -0
  19. package/templates/agent-seller-express/src/brain.ts +121 -0
  20. package/templates/agent-seller-express/src/comedian.ts +153 -0
  21. package/templates/agent-seller-express/tsconfig.json +21 -0
  22. package/templates/buyer-express/.env.example +12 -0
  23. package/templates/buyer-express/README.md +26 -0
  24. package/templates/buyer-express/_gitignore +6 -0
  25. package/templates/buyer-express/package.json +18 -0
  26. package/templates/buyer-express/src/buyer.ts +82 -0
  27. package/templates/buyer-express/tsconfig.json +21 -0
  28. package/templates/buyer-hono/.env.example +12 -0
  29. package/templates/buyer-hono/README.md +26 -0
  30. package/templates/buyer-hono/_gitignore +6 -0
  31. package/templates/buyer-hono/package.json +18 -0
  32. package/templates/buyer-hono/src/buyer.ts +82 -0
  33. package/templates/buyer-hono/tsconfig.json +21 -0
  34. package/templates/buyer-next/.env.example +12 -0
  35. package/templates/buyer-next/README.md +26 -0
  36. package/templates/buyer-next/_gitignore +6 -0
  37. package/templates/buyer-next/package.json +18 -0
  38. package/templates/buyer-next/src/buyer.ts +82 -0
  39. package/templates/buyer-next/tsconfig.json +21 -0
  40. package/templates/seller-express/.env.example +12 -0
  41. package/templates/seller-express/README.md +26 -0
  42. package/templates/seller-express/_gitignore +6 -0
  43. package/templates/seller-express/package.json +22 -0
  44. package/templates/seller-express/src/server.ts +64 -0
  45. package/templates/seller-express/tsconfig.json +21 -0
  46. package/templates/seller-hono/.env.example +12 -0
  47. package/templates/seller-hono/README.md +26 -0
  48. package/templates/seller-hono/_gitignore +6 -0
  49. package/templates/seller-hono/package.json +22 -0
  50. package/templates/seller-hono/src/server.ts +69 -0
  51. package/templates/seller-hono/tsconfig.json +21 -0
  52. package/templates/seller-next/.env.example +12 -0
  53. package/templates/seller-next/README.md +26 -0
  54. package/templates/seller-next/_gitignore +6 -0
  55. package/templates/seller-next/app/api/protected/route.ts +27 -0
  56. package/templates/seller-next/app/api/session/route.ts +8 -0
  57. package/templates/seller-next/app/globals.css +556 -0
  58. package/templates/seller-next/app/icon.svg +5 -0
  59. package/templates/seller-next/app/layout.tsx +22 -0
  60. package/templates/seller-next/app/page.tsx +189 -0
  61. package/templates/seller-next/next.config.mjs +3 -0
  62. package/templates/seller-next/package.json +25 -0
  63. package/templates/seller-next/public/logo-light.svg +19 -0
  64. package/templates/seller-next/public/logo.svg +19 -0
  65. package/templates/seller-next/scripts/serve.mjs +46 -0
  66. package/templates/seller-next/tsconfig.json +30 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 4mica Network
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @4mica/cli
2
+
3
+ The command-line tool for the [4Mica](https://4mica.io) x402 payment network.
4
+ Scaffold a trading agent or payment app, install it, run it, and manage your
5
+ agents from a local dashboard.
6
+
7
+ ## Quick start
8
+
9
+ ```bash
10
+ npx @4mica/cli
11
+ # or
12
+ npm create @4mica
13
+ ```
14
+
15
+ Answer a few prompts and you'll have a running project in demo mode — no
16
+ credentials required.
17
+
18
+ ## Commands
19
+
20
+ ```bash
21
+ 4mica init [dir] # scaffold a new agent or app (default command)
22
+ 4mica dashboard # open the dashboard to manage agents & transactions
23
+ 4mica dev # run the current project's dev server
24
+ ```
25
+
26
+ ### `4mica init`
27
+
28
+ Interactive by default. Every prompt has a matching flag for non-interactive /
29
+ CI use:
30
+
31
+ ```bash
32
+ 4mica init my-app \
33
+ --type seller \ # agent | seller | buyer
34
+ --framework hono \ # express | hono | next (seller/buyer)
35
+ --role seller \ # seller | buyer (agent)
36
+ --pm pnpm \ # pnpm | npm | yarn | bun
37
+ --no-run \ # scaffold without starting the dev server
38
+ --yes # accept defaults, no prompts
39
+ ```
40
+
41
+ ## Templates
42
+
43
+ | Type | Frameworks | Based on |
44
+ | --- | --- | --- |
45
+ | Trading agent | seller (Express) · buyer (Node) | seller/buyer agents |
46
+ | Paywalled API (seller) | Express · Hono · Next | x402 paywall |
47
+ | Buyer client | Express · Hono · Next targets | x402 handshake |
48
+
49
+ Every template runs in **demo mode** with a mock verifier. To go live, copy
50
+ `.env.example` → `.env`, fill in your `4MICA_*` credentials, and swap the mock
51
+ verifier for `createClient()` from `@4mica/sdk-node`.
52
+
53
+ ## Dashboard
54
+
55
+ `4mica dashboard` launches a local dashboard (Stripe test-mode style) to add and
56
+ remove agents that can trade, view and publish an agent's profile (accuracy,
57
+ uptime, pricing, policy, verification), inspect transactions, and manage the
58
+ trade allow-list. It runs on mock data today; a hosted version with accounts and
59
+ a sandbox→live switch is coming.
package/dist/index.js ADDED
@@ -0,0 +1,628 @@
1
+ #!/usr/bin/env node
2
+ import yargs from 'yargs';
3
+ import { hideBin } from 'yargs/helpers';
4
+ import { readFile, access, readdir, mkdir, writeFile } from 'fs/promises';
5
+ import { join, resolve, dirname, relative, isAbsolute } from 'path';
6
+ import { execa } from 'execa';
7
+ import { fileURLToPath } from 'url';
8
+ import { findUp } from 'find-up';
9
+ import ora from 'ora';
10
+ import pc from 'picocolors';
11
+ import { intro, outro, confirm, cancel, select, text, note, isCancel } from '@clack/prompts';
12
+ import terminalLink from 'terminal-link';
13
+
14
+ function bundledTemplatesDir() {
15
+ const here = dirname(fileURLToPath(import.meta.url));
16
+ return join(here, "..", "templates");
17
+ }
18
+ async function resolveRunContext(cwd = process.cwd()) {
19
+ const workspaceFile = await findUp("pnpm-workspace.yaml", { cwd });
20
+ if (workspaceFile) {
21
+ const root = dirname(workspaceFile);
22
+ try {
23
+ const pkg = JSON.parse(
24
+ await readFile(join(root, "package.json"), "utf8")
25
+ );
26
+ if (pkg.name === "4mica") {
27
+ return {
28
+ inMonorepo: true,
29
+ monorepoRoot: root,
30
+ templatesDir: bundledTemplatesDir()
31
+ };
32
+ }
33
+ } catch {
34
+ }
35
+ }
36
+ return { inMonorepo: false, templatesDir: bundledTemplatesDir() };
37
+ }
38
+ var LOCKFILES = {
39
+ "pnpm-lock.yaml": "pnpm",
40
+ "package-lock.json": "npm",
41
+ "yarn.lock": "yarn",
42
+ "bun.lockb": "bun",
43
+ "bun.lock": "bun"
44
+ };
45
+ function fromUserAgent() {
46
+ const ua = process.env.npm_config_user_agent;
47
+ if (!ua) return void 0;
48
+ const name = ua.split("/")[0];
49
+ if (name === "pnpm" || name === "npm" || name === "yarn" || name === "bun") {
50
+ return name;
51
+ }
52
+ return void 0;
53
+ }
54
+ async function fromLockfile(cwd) {
55
+ const found = await findUp(Object.keys(LOCKFILES), { cwd });
56
+ if (!found) return void 0;
57
+ const file = found.slice(found.lastIndexOf("/") + 1);
58
+ return LOCKFILES[file];
59
+ }
60
+ async function detectPackageManager(cwd, override) {
61
+ return override ?? fromUserAgent() ?? await fromLockfile(cwd) ?? "pnpm";
62
+ }
63
+ function installArgs(_pm) {
64
+ return ["install"];
65
+ }
66
+ function runArgs(pm, script) {
67
+ return pm === "yarn" ? [script] : ["run", script];
68
+ }
69
+ async function install(pm, cwd) {
70
+ await execa(pm, installArgs(), { cwd, stdio: "pipe" });
71
+ }
72
+ async function runScript(pm, script, cwd) {
73
+ await execa(pm, runArgs(pm, script), { cwd, stdio: "inherit" });
74
+ }
75
+ async function gitInit(cwd) {
76
+ try {
77
+ await execa("git", ["init", "-q"], { cwd });
78
+ await execa("git", ["add", "-A"], { cwd });
79
+ } catch {
80
+ }
81
+ }
82
+ async function openBrowser(url) {
83
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
84
+ try {
85
+ await execa(cmd, [url], { stdio: "ignore", detached: true });
86
+ } catch {
87
+ }
88
+ }
89
+ var brand = (s) => pc.cyan(pc.bold(s));
90
+ var dim = (s) => pc.dim(s);
91
+ var ok = (s) => pc.green(s);
92
+ var warn = (s) => pc.yellow(s);
93
+ var err = (s) => pc.red(s);
94
+ function spinner(text2) {
95
+ return ora({ text: text2, color: "cyan" }).start();
96
+ }
97
+ async function step(text2, fn, doneText) {
98
+ const s = spinner(text2);
99
+ try {
100
+ const result = await fn();
101
+ s.succeed(ok(doneText ?? text2));
102
+ return result;
103
+ } catch (error) {
104
+ s.fail(err(`${text2} \u2014 ${error.message}`));
105
+ throw error;
106
+ }
107
+ }
108
+
109
+ // src/commands/dashboard.ts
110
+ async function exists(path) {
111
+ try {
112
+ await access(path);
113
+ return true;
114
+ } catch {
115
+ return false;
116
+ }
117
+ }
118
+ async function launchDashboard(opts = {}) {
119
+ const context = await resolveRunContext();
120
+ if (!context.inMonorepo || !context.monorepoRoot) {
121
+ process.stdout.write(
122
+ `
123
+ ${warn("Dashboard")} runs from the 4Mica monorepo in this release.
124
+ Clone ${pc.cyan("github.com/4mica-Network/homepage")} and run ${pc.bold("pnpm --filter @4mica/dashboard dev")},
125
+ or use the hosted dashboard (coming soon). ${dim("More: 4mica.io/docs")}
126
+
127
+ `
128
+ );
129
+ return;
130
+ }
131
+ const dashboardDir = join(context.monorepoRoot, "apps", "dashboard");
132
+ if (!await exists(dashboardDir)) {
133
+ process.stdout.write(
134
+ `
135
+ ${warn("apps/dashboard not found in this checkout.")}
136
+
137
+ `
138
+ );
139
+ return;
140
+ }
141
+ const pm = await detectPackageManager(context.monorepoRoot);
142
+ const args = pm === "yarn" ? ["workspace", "@4mica/dashboard", "dev"] : ["--filter", "@4mica/dashboard", "dev"];
143
+ process.stdout.write(`
144
+ ${ok("Starting the 4Mica dashboard\u2026")}
145
+
146
+ `);
147
+ if (opts.background) {
148
+ const child2 = execa(pm, args, {
149
+ cwd: context.monorepoRoot,
150
+ stdio: "ignore",
151
+ detached: true
152
+ });
153
+ child2.unref();
154
+ if (opts.open) {
155
+ setTimeout(() => void openBrowser("http://localhost:4173"), 2500);
156
+ }
157
+ return;
158
+ }
159
+ const child = execa(pm, args, {
160
+ cwd: context.monorepoRoot,
161
+ stdio: "inherit"
162
+ });
163
+ if (opts.open) {
164
+ setTimeout(() => void openBrowser("http://localhost:4173"), 2500);
165
+ }
166
+ await child;
167
+ }
168
+
169
+ // src/commands/dev.ts
170
+ async function devCommand(script = "dev") {
171
+ const cwd = process.cwd();
172
+ const pm = await detectPackageManager(cwd);
173
+ await runScript(pm, script, cwd);
174
+ }
175
+ var cache = null;
176
+ async function loadSnapshot() {
177
+ if (cache) return cache;
178
+ const here = dirname(fileURLToPath(import.meta.url));
179
+ const path = join(here, "..", "snapshot.json");
180
+ try {
181
+ cache = JSON.parse(await readFile(path, "utf8"));
182
+ } catch {
183
+ cache = { catalog: {}, catalogs: {}, fourMica: {} };
184
+ }
185
+ return cache;
186
+ }
187
+ function resolveCatalog(snap, spec, name) {
188
+ const named = spec.slice("catalog:".length).trim();
189
+ const table = named ? snap.catalogs[named] ?? {} : snap.catalog;
190
+ return table[name] ?? "latest";
191
+ }
192
+ function resolveWorkspace(snap, name) {
193
+ const version = snap.fourMica[name];
194
+ return version ? `^${version}` : "latest";
195
+ }
196
+ function rewriteBlock(block, snap, keepWorkspace) {
197
+ if (!block) return;
198
+ for (const [name, spec] of Object.entries(block)) {
199
+ if (spec.startsWith("workspace:")) {
200
+ block[name] = keepWorkspace ? spec : resolveWorkspace(snap, name);
201
+ } else if (spec.startsWith("catalog:")) {
202
+ block[name] = resolveCatalog(snap, spec, name);
203
+ }
204
+ }
205
+ }
206
+ async function rewritePackageJson(pkg, opts) {
207
+ const snap = await loadSnapshot();
208
+ rewriteBlock(
209
+ pkg.dependencies,
210
+ snap,
211
+ opts.keepWorkspace
212
+ );
213
+ rewriteBlock(
214
+ pkg.devDependencies,
215
+ snap,
216
+ opts.keepWorkspace
217
+ );
218
+ if (!opts.keepWorkspace) {
219
+ pkg.private = void 0;
220
+ }
221
+ return pkg;
222
+ }
223
+
224
+ // src/core/substitute.ts
225
+ function substitute(body, tokens) {
226
+ const table = tokens;
227
+ return body.replace(/__([A-Z_]+)__/g, (match, key) => {
228
+ const value = table[key];
229
+ return value === void 0 ? match : value;
230
+ });
231
+ }
232
+ function toPackageName(input) {
233
+ const cleaned = input.trim().toLowerCase().replace(/[^a-z0-9-~._]+/g, "-").replace(/^[-_.]+|[-_.]+$/g, "");
234
+ return cleaned || "my-4mica-app";
235
+ }
236
+
237
+ // src/core/copy-engine.ts
238
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", ".next", ".turbo", ".git"]);
239
+ var TEXT_EXT = /* @__PURE__ */ new Set([
240
+ ".ts",
241
+ ".tsx",
242
+ ".js",
243
+ ".jsx",
244
+ ".mjs",
245
+ ".cjs",
246
+ ".json",
247
+ ".md",
248
+ ".css",
249
+ ".html",
250
+ ".svg",
251
+ ".txt",
252
+ ".yaml",
253
+ ".yml",
254
+ ""
255
+ ]);
256
+ function targetName(name) {
257
+ if (name === "_gitignore") return ".gitignore";
258
+ if (name === "_npmrc") return ".npmrc";
259
+ return name;
260
+ }
261
+ function extOf(name) {
262
+ const dot = name.lastIndexOf(".");
263
+ return dot === -1 ? "" : name.slice(dot);
264
+ }
265
+ async function copyTemplate(opts) {
266
+ await walk(opts.templateDir, opts);
267
+ }
268
+ async function walk(dir, opts) {
269
+ const entries = await readdir(dir, { withFileTypes: true });
270
+ for (const entry of entries) {
271
+ if (entry.isDirectory()) {
272
+ if (SKIP_DIRS.has(entry.name)) continue;
273
+ await walk(join(dir, entry.name), opts);
274
+ continue;
275
+ }
276
+ await copyFile(join(dir, entry.name), opts);
277
+ }
278
+ }
279
+ async function copyFile(srcPath, opts) {
280
+ const rel = relative(opts.templateDir, srcPath);
281
+ const parts = rel.split("/");
282
+ parts[parts.length - 1] = targetName(parts[parts.length - 1]);
283
+ const destPath = join(opts.targetDir, ...parts);
284
+ await mkdir(join(destPath, ".."), { recursive: true });
285
+ const base = parts[parts.length - 1];
286
+ if (base === "package.json") {
287
+ await writePackageJson(srcPath, destPath, opts);
288
+ return;
289
+ }
290
+ if (TEXT_EXT.has(extOf(base))) {
291
+ const body = await readFile(srcPath, "utf8");
292
+ await writeFile(destPath, substitute(body, opts.tokens));
293
+ return;
294
+ }
295
+ const raw = await readFile(srcPath);
296
+ await writeFile(destPath, raw);
297
+ }
298
+ async function writePackageJson(srcPath, destPath, opts) {
299
+ const body = substitute(await readFile(srcPath, "utf8"), opts.tokens);
300
+ const pkg = JSON.parse(body);
301
+ const rewritten = await rewritePackageJson(pkg, {
302
+ keepWorkspace: opts.keepWorkspace
303
+ });
304
+ await writeFile(destPath, `${JSON.stringify(rewritten, null, 2)}
305
+ `);
306
+ }
307
+
308
+ // src/core/template-registry.ts
309
+ function resolveTemplate(opts) {
310
+ if (opts.type === "agent") {
311
+ return opts.agentRole === "seller" ? {
312
+ dir: "agent-seller-express",
313
+ port: 4100,
314
+ tmpfile: "4mica-agent.url",
315
+ runScript: "dev",
316
+ description: "Trading seller agent: sells information, paywalls the premium result with dynamic per-request pricing"
317
+ } : {
318
+ dir: "agent-buyer-node",
319
+ port: 4100,
320
+ tmpfile: "4mica-agent.url",
321
+ runScript: "start",
322
+ description: "Trading buyer agent: pursues a goal under a budget, pays sellers over x402, and adapts its strategy"
323
+ };
324
+ }
325
+ const ports = {
326
+ express: 3e3,
327
+ hono: 3001,
328
+ next: 3002
329
+ };
330
+ const tmpfile = `4mica-${opts.framework}.url`;
331
+ if (opts.type === "seller") {
332
+ return {
333
+ dir: `seller-${opts.framework}`,
334
+ port: ports[opts.framework],
335
+ tmpfile,
336
+ runScript: "dev",
337
+ description: `Seller (recipient): gate a ${opts.framework} route behind a 4Mica x402 paywall`
338
+ };
339
+ }
340
+ return {
341
+ dir: `buyer-${opts.framework}`,
342
+ port: ports[opts.framework],
343
+ tmpfile,
344
+ runScript: "start",
345
+ description: `Buyer (payer): perform the x402 handshake against a ${opts.framework} seller`
346
+ };
347
+ }
348
+ function bail() {
349
+ cancel("Scaffolding cancelled.");
350
+ process.exit(0);
351
+ }
352
+ function unwrap(value) {
353
+ if (isCancel(value)) bail();
354
+ return value;
355
+ }
356
+ async function runInitFlow(flags, defaultPm) {
357
+ if (flags.type === void 0) {
358
+ const fromScratch = unwrap(
359
+ await confirm({
360
+ message: "Generate a new 4Mica project from scratch?",
361
+ initialValue: true
362
+ })
363
+ );
364
+ if (!fromScratch) {
365
+ cancel(
366
+ "No problem \u2014 add 4Mica to an existing app with `npm i @4mica/sdk @4mica/sdk-node`, then wrap a route with a paywall. Docs: https://4mica.io/docs"
367
+ );
368
+ process.exit(0);
369
+ }
370
+ }
371
+ const type = flags.type ?? unwrap(
372
+ await select({
373
+ message: "What are you building?",
374
+ options: [
375
+ {
376
+ value: "agent",
377
+ label: "Trading agent",
378
+ hint: "buys/sells over x402"
379
+ },
380
+ { value: "seller", label: "Paywalled API (seller)" },
381
+ { value: "buyer", label: "Buyer client" }
382
+ ]
383
+ })
384
+ );
385
+ let agentRole = flags.agentRole ?? "seller";
386
+ let framework = flags.framework ?? "express";
387
+ if (type === "agent") {
388
+ if (flags.agentRole === void 0) {
389
+ agentRole = unwrap(
390
+ await select({
391
+ message: "Which side does your agent play?",
392
+ options: [
393
+ {
394
+ value: "seller",
395
+ label: "Seller agent",
396
+ hint: "earns by selling info"
397
+ },
398
+ {
399
+ value: "buyer",
400
+ label: "Buyer agent",
401
+ hint: "spends to reach a goal"
402
+ }
403
+ ]
404
+ })
405
+ );
406
+ }
407
+ } else if (flags.framework === void 0) {
408
+ framework = unwrap(
409
+ await select({
410
+ message: "Which framework?",
411
+ options: [
412
+ { value: "express", label: "Express" },
413
+ { value: "hono", label: "Hono" },
414
+ { value: "next", label: "Next.js" }
415
+ ]
416
+ })
417
+ );
418
+ }
419
+ const defaultName = type === "agent" ? `my-${agentRole}-agent` : `my-${type}-${framework}`;
420
+ const name = flags.name ?? toPackageName(
421
+ unwrap(
422
+ await text({
423
+ message: "Project name?",
424
+ placeholder: defaultName,
425
+ defaultValue: defaultName
426
+ })
427
+ )
428
+ );
429
+ const packageManager = flags.packageManager ?? unwrap(
430
+ await select({
431
+ message: "Install with which package manager?",
432
+ initialValue: defaultPm,
433
+ options: [
434
+ { value: "pnpm", label: "pnpm" },
435
+ { value: "npm", label: "npm" },
436
+ { value: "yarn", label: "yarn" },
437
+ { value: "bun", label: "bun" }
438
+ ]
439
+ })
440
+ );
441
+ const openDashboard = flags.openDashboard ?? unwrap(
442
+ await confirm({
443
+ message: "Open the 4Mica dashboard to manage agents & transactions?",
444
+ initialValue: true
445
+ })
446
+ );
447
+ return {
448
+ dir: flags.dir ?? name,
449
+ name,
450
+ type,
451
+ framework,
452
+ agentRole,
453
+ packageManager,
454
+ install: flags.install ?? true,
455
+ run: flags.run ?? true,
456
+ openDashboard
457
+ };
458
+ }
459
+
460
+ // src/commands/init.ts
461
+ async function exists2(path) {
462
+ try {
463
+ await access(path);
464
+ return true;
465
+ } catch {
466
+ return false;
467
+ }
468
+ }
469
+ function isInside(child, parent) {
470
+ const rel = relative(parent, child);
471
+ return !rel.startsWith("..") && !isAbsolute(rel);
472
+ }
473
+ async function initCommand(flags) {
474
+ intro(brand("4mica"));
475
+ const context = await resolveRunContext();
476
+ const defaultPm = await detectPackageManager(
477
+ process.cwd(),
478
+ flags.packageManager
479
+ );
480
+ const opts = await runInitFlow(flags, defaultPm);
481
+ const template = resolveTemplate(opts);
482
+ const templateDir = join(context.templatesDir, template.dir);
483
+ const targetDir = resolve(process.cwd(), opts.dir);
484
+ if (!await exists2(templateDir)) {
485
+ throw new Error(`Template not found: ${template.dir}`);
486
+ }
487
+ if (await exists2(targetDir)) {
488
+ throw new Error(
489
+ `Directory already exists: ${opts.dir} \u2014 choose another name.`
490
+ );
491
+ }
492
+ const keepWorkspace = context.inMonorepo && context.monorepoRoot !== void 0 && isInside(targetDir, context.monorepoRoot);
493
+ await step(
494
+ `Scaffolding ${pc.bold(opts.name)}`,
495
+ () => copyTemplate({
496
+ templateDir,
497
+ targetDir,
498
+ tokens: {
499
+ PROJECT_NAME: opts.name,
500
+ DESCRIPTION: template.description,
501
+ PORT: String(template.port),
502
+ TMPFILE: template.tmpfile
503
+ },
504
+ keepWorkspace
505
+ })
506
+ );
507
+ await gitInit(targetDir);
508
+ if (opts.install) {
509
+ await step(
510
+ `Installing dependencies with ${opts.packageManager}`,
511
+ () => install(opts.packageManager, targetDir),
512
+ "Dependencies installed"
513
+ );
514
+ }
515
+ printNextSteps(opts, template.runScript);
516
+ outro(ok("Done \u2014 happy building!"));
517
+ if (opts.openDashboard) {
518
+ await launchDashboard({ open: true, background: true });
519
+ }
520
+ if (opts.install && opts.run) {
521
+ await runScript(opts.packageManager, template.runScript, targetDir);
522
+ }
523
+ }
524
+ function printNextSteps(opts, script) {
525
+ const docs = terminalLink("4mica.io/docs", "https://4mica.io/docs");
526
+ const lines = [`cd ${opts.dir}`];
527
+ if (!opts.install) lines.push(`${opts.packageManager} install`);
528
+ lines.push(`${opts.packageManager} run ${script}`);
529
+ lines.push("");
530
+ lines.push(`${dim("Demo mode needs no config. Going live?")} ${docs}`);
531
+ lines.push(`${dim("Env template:")} ${warn(join(opts.dir, ".env.example"))}`);
532
+ note(lines.join("\n"), "Next steps");
533
+ }
534
+
535
+ // src/cli.ts
536
+ var BUILD_TYPES = ["agent", "seller", "buyer"];
537
+ var FRAMEWORKS = ["express", "hono", "next"];
538
+ var AGENT_ROLES = ["seller", "buyer"];
539
+ var PMS = ["pnpm", "npm", "yarn", "bun"];
540
+ function flagsFromArgv(argv) {
541
+ const flags = {};
542
+ if (argv.dir) flags.dir = String(argv.dir);
543
+ if (argv.name) flags.name = String(argv.name);
544
+ if (argv.type) flags.type = argv.type;
545
+ if (argv.framework) flags.framework = argv.framework;
546
+ if (argv.role) flags.agentRole = argv.role;
547
+ if (argv.pm) flags.packageManager = argv.pm;
548
+ if (argv.install === false) flags.install = false;
549
+ if (argv.run === false) flags.run = false;
550
+ if (argv.dashboard !== void 0)
551
+ flags.openDashboard = Boolean(argv.dashboard);
552
+ if (argv.yes) {
553
+ flags.type ??= "agent";
554
+ if (flags.type === "agent") flags.agentRole ??= "seller";
555
+ else flags.framework ??= "express";
556
+ flags.packageManager ??= "pnpm";
557
+ flags.install ??= true;
558
+ flags.run ??= false;
559
+ flags.openDashboard ??= false;
560
+ flags.name ??= flags.type === "agent" ? `my-${flags.agentRole}-agent` : `my-${flags.type}-${flags.framework}`;
561
+ }
562
+ return flags;
563
+ }
564
+ async function run() {
565
+ const cli = yargs(hideBin(process.argv)).scriptName("4mica").usage("$0 <command> [options]").command(
566
+ ["init [dir]", "$0 [dir]"],
567
+ "Scaffold a new 4Mica agent or payment app",
568
+ (y) => y.positional("dir", { type: "string", describe: "Target directory" }).option("type", { choices: BUILD_TYPES, describe: "What to build" }).option("framework", {
569
+ choices: FRAMEWORKS,
570
+ describe: "Framework (seller/buyer)"
571
+ }).option("role", {
572
+ choices: AGENT_ROLES,
573
+ describe: "Trading role (agent)"
574
+ }).option("name", { type: "string", describe: "Project name" }).option("pm", { choices: PMS, describe: "Package manager" }).option("install", {
575
+ type: "boolean",
576
+ default: true,
577
+ describe: "Install dependencies"
578
+ }).option("run", {
579
+ type: "boolean",
580
+ default: true,
581
+ describe: "Start the dev server after scaffolding"
582
+ }).option("dashboard", {
583
+ type: "boolean",
584
+ describe: "Open the dashboard after scaffolding"
585
+ }).option("yes", {
586
+ alias: "y",
587
+ type: "boolean",
588
+ describe: "Accept defaults; no prompts (CI)"
589
+ }),
590
+ async (argv) => {
591
+ await initCommand(flagsFromArgv(argv));
592
+ }
593
+ ).command(
594
+ "dashboard",
595
+ "Launch the 4Mica dashboard (manage agents, transactions, whitelist)",
596
+ (y) => y.option("open", {
597
+ type: "boolean",
598
+ default: true,
599
+ describe: "Open a browser"
600
+ }),
601
+ async (argv) => {
602
+ await launchDashboard({ open: Boolean(argv.open) });
603
+ }
604
+ ).command(
605
+ "dev",
606
+ "Run the current project's dev server",
607
+ (y) => y,
608
+ async () => {
609
+ await devCommand();
610
+ }
611
+ ).demandCommand(0).strict().help().alias("help", "h").version().alias("version", "v").fail((msg, error) => {
612
+ process.stderr.write(err(`
613
+ ${msg || error?.message || "Error"}
614
+ `));
615
+ process.exit(1);
616
+ });
617
+ await cli.parse();
618
+ }
619
+
620
+ // src/index.ts
621
+ run().catch((error) => {
622
+ process.stderr.write(err(`
623
+ ${error.message}
624
+ `));
625
+ process.exitCode = 1;
626
+ });
627
+ //# sourceMappingURL=index.js.map
628
+ //# sourceMappingURL=index.js.map