create-moncircle 1.0.2 → 1.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 (3) hide show
  1. package/README.md +6 -4
  2. package/index.js +347 -77
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -8,10 +8,12 @@ Interactive setup wizard for [@moncircle/sdk](https://npmjs.com/package/@moncirc
8
8
  npx create-moncircle
9
9
  ```
10
10
 
11
- This will:
12
- 1. Ask for your MongoDB URI, Monad wallet, and other config
13
- 2. Write a `.env` file in your current directory
14
- 3. Tell you how to start the server
11
+ ### What it does (v1.1.0 — Autopilot Release):
12
+ 1. **Config wizard** writes .env (MongoDB URI, Monad keys, etc.)
13
+ 2. **Contract deploy** deploys optimized MonLoyalty contract to Monad testnet
14
+ 3. **Full Scaffolding** Generates an advanced `loyalty-dashboard.html` and a pre-configured `server.js` backend
15
+ 4. **Autopilot Enabled** → Sets up DOM-based tracking for zero-conf rewarding
16
+ the server
15
17
 
16
18
  ## Reconfigure
17
19
 
package/index.js CHANGED
@@ -1,24 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * create-moncircle — Interactive setup wizard for @moncircle/sdk
3
+ * create-moncircle — Single command to set up, deploy & launch @moncircle/sdk
4
4
  *
5
5
  * Usage:
6
- * npx create-moncircle (standalone)
7
- * npm install @moncircle/sdk (auto-triggered via postinstall)
6
+ * npx create-moncircle
8
7
  *
9
- * Supports:
10
- * - NEW mode: creates .env from scratch
11
- * - EDIT mode: updates existing .env without destroying it
8
+ * What it does (in order):
9
+ * 1. Config wizard → writes .env (MongoDB URI, Monad keys, etc.)
10
+ * 2. Contract deploy deploys MonLoyalty.sol to Monad testnet → writes MONAD_CONTRACT_ADDRESS to .env
11
+ * 3. Starts the API → runs `npx mon-loyalty-api start`
12
12
  */
13
13
 
14
14
  const readline = require("readline")
15
15
  const fs = require("fs")
16
16
  const path = require("path")
17
- const { execSync } = require("child_process")
18
-
19
- const ENV_PATH = process.env.MON_LOYALTY_ENV_PATH || path.join(process.cwd(), ".env")
20
- const PKG_PATH = path.join(process.cwd(), "package.json")
17
+ const { execSync, spawnSync, spawn } = require("child_process")
21
18
 
19
+ // ── Colours ────────────────────────────────────────────────────────────────
22
20
  const CYAN = "\x1b[36m"
23
21
  const GREEN = "\x1b[32m"
24
22
  const YELLOW = "\x1b[33m"
@@ -27,16 +25,10 @@ const DIM = "\x1b[2m"
27
25
  const BOLD = "\x1b[1m"
28
26
  const RESET = "\x1b[0m"
29
27
 
30
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
31
-
32
- const ask = (question, defaultVal = "") =>
33
- new Promise((resolve) => {
34
- const hint = defaultVal ? ` ${DIM}(${defaultVal})${RESET}` : ""
35
- rl.question(` ${question}${hint} › `, (answer) => {
36
- resolve(answer.trim() || defaultVal)
37
- })
38
- })
28
+ // ── Paths ──────────────────────────────────────────────────────────────────
29
+ const ENV_PATH = process.env.MON_LOYALTY_ENV_PATH || path.join(process.cwd(), ".env")
39
30
 
31
+ // ── Config fields ──────────────────────────────────────────────────────────
40
32
  const fields = [
41
33
  {
42
34
  key: "MONGODB_URI",
@@ -57,7 +49,7 @@ const fields = [
57
49
  label: "JWT Secret",
58
50
  hint: "Long random string for signing tokens",
59
51
  required: false,
60
- default: "change_me_" + Math.random().toString(36).slice(2)
52
+ default: "moncircle_" + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2)
61
53
  },
62
54
  {
63
55
  key: "MONAD_RPC_URL",
@@ -69,19 +61,34 @@ const fields = [
69
61
  {
70
62
  key: "MONAD_PRIVATE_KEY",
71
63
  label: "Monad Private Key",
72
- hint: "Master wallet private key — KEEP SECRET",
64
+ hint: "Master wallet private key — KEEP SECRET. Funds this wallet = contract can pay users.",
73
65
  required: false,
74
66
  default: ""
75
67
  },
76
68
  {
77
- key: "MONAD_CONTRACT_ADDRESS",
78
- label: "MonLoyalty Contract Address",
79
- hint: "Deployed contract address (leave blank for now)",
69
+ key: "MON_PUBLIC_KEY",
70
+ label: "MonCircle Public Key",
71
+ hint: "Your platform's public key (e.g., pk_live_...) for SDK access",
72
+ required: false,
73
+ default: "pk_live_67df231b188e46d38a2baef03b217756"
74
+ },
75
+ {
76
+ key: "MON_API_KEY",
77
+ label: "MonCircle API Key",
78
+ hint: "Your platform's secret API key (e.g., sk_live_...) for backend verification",
80
79
  required: false,
81
80
  default: ""
82
81
  }
83
82
  ]
84
83
 
84
+ // ── Helpers ────────────────────────────────────────────────────────────────
85
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
86
+ const ask = (question, defaultVal = "") =>
87
+ new Promise((resolve) => {
88
+ const hint = defaultVal ? ` ${DIM}(${defaultVal})${RESET}` : ""
89
+ rl.question(` ${question}${hint} › `, (answer) => resolve(answer.trim() || defaultVal))
90
+ })
91
+
85
92
  function loadExistingEnv() {
86
93
  if (!fs.existsSync(ENV_PATH)) return {}
87
94
  const content = fs.readFileSync(ENV_PATH, "utf-8")
@@ -93,25 +100,15 @@ function loadExistingEnv() {
93
100
  return map
94
101
  }
95
102
 
96
- function mergeEnv(existing, updated) {
97
- const envContent = fs.existsSync(ENV_PATH) ? fs.readFileSync(ENV_PATH, "utf-8") : ""
98
- const lines = envContent.split("\n")
99
- const handled = new Set()
100
- const result = lines.map((line) => {
101
- const match = line.match(/^\s*([^#=\s][^=]*)=(.*)$/)
102
- if (match) {
103
- const key = match[1].trim()
104
- if (updated[key] !== undefined) {
105
- handled.add(key)
106
- return `${key}="${updated[key]}"`
107
- }
108
- }
109
- return line
110
- })
111
- for (const key of Object.keys(updated)) {
112
- if (!handled.has(key)) result.push(`${key}="${updated[key]}"`)
103
+ function mergeEnvKey(key, value) {
104
+ let content = fs.existsSync(ENV_PATH) ? fs.readFileSync(ENV_PATH, "utf-8") : ""
105
+ const regex = new RegExp(`^(${key}=.*)$`, "m")
106
+ if (regex.test(content)) {
107
+ content = content.replace(regex, `${key}="${value}"`)
108
+ } else {
109
+ content += `\n${key}="${value}"`
113
110
  }
114
- return result.join("\n")
111
+ fs.writeFileSync(ENV_PATH, content, "utf-8")
115
112
  }
116
113
 
117
114
  function writeFreshEnv(values) {
@@ -129,81 +126,354 @@ function writeFreshEnv(values) {
129
126
  "# ── Auth ─────────────────────────────────────",
130
127
  `JWT_SECRET="${values.JWT_SECRET}"`,
131
128
  "",
132
- "# ── Monad On-Chain (withdrawal/settlement) ────",
129
+ "# ── Monad On-Chain ────────────────────────────",
133
130
  `MONAD_RPC_URL="${values.MONAD_RPC_URL}"`,
134
131
  `MONAD_PRIVATE_KEY="${values.MONAD_PRIVATE_KEY}"`,
135
- `MONAD_CONTRACT_ADDRESS="${values.MONAD_CONTRACT_ADDRESS}"`,
132
+ `MONAD_CONTRACT_ADDRESS=""`,
133
+ `MON_PUBLIC_KEY="${values.MON_PUBLIC_KEY}"`,
134
+ `MON_API_KEY="${values.MON_API_KEY}"`,
136
135
  ""
137
136
  ]
138
137
  fs.writeFileSync(ENV_PATH, lines.join("\n"), "utf-8")
139
138
  }
140
139
 
141
- function sdkIsInstalled() {
142
- return fs.existsSync(path.join(process.cwd(), "node_modules", "@moncircle", "sdk"))
143
- }
144
-
145
- async function main() {
140
+ // ── Phase 1: Config wizard ─────────────────────────────────────────────────
141
+ async function runSetup() {
146
142
  const existing = loadExistingEnv()
147
- const isEditMode = Object.keys(existing).length > 0
143
+ const isEdit = Object.keys(existing).length > 0
148
144
 
149
145
  console.log(`\n${BOLD}${CYAN}╔══════════════════════════════════════════════════════╗`)
150
- console.log(`║ @moncircle/sdk — ${isEditMode ? "Edit Configuration " : "Setup Wizard "} ║`)
146
+ console.log(`║ @moncircle/sdk — ${isEdit ? "Edit Config " : "Setup Wizard "} ║`)
151
147
  console.log(`╚══════════════════════════════════════════════════════╝${RESET}\n`)
152
148
 
153
- if (isEditMode) {
154
- console.log(` ${GREEN}✓ Existing .env found.${RESET} ${DIM}Press Enter to keep current values.${RESET}\n`)
155
- } else {
156
- console.log(` ${DIM}Writing to: ${ENV_PATH}${RESET}\n`)
149
+ if (isEdit) {
150
+ console.log(` ${GREEN}✓ .env found.${RESET} ${DIM}Press Enter to keep values.${RESET}\n`)
157
151
  }
158
152
 
159
153
  const updated = { ...existing }
160
-
161
154
  for (const field of fields) {
162
155
  const currentVal = existing[field.key] ?? field.default
163
156
  const requiredTag = field.required ? ` ${RED}*required${RESET}` : ""
164
-
165
157
  console.log(` ${BOLD}${field.label}${RESET}${requiredTag}`)
166
- if (!isEditMode || !existing[field.key]) {
167
- console.log(` ${DIM}${field.hint}${RESET}`)
168
- }
169
-
158
+ if (!isEdit || !existing[field.key]) console.log(` ${DIM}${field.hint}${RESET}`)
170
159
  let val = ""
171
160
  while (true) {
172
161
  val = await ask(` →`, currentVal)
173
- if (field.required && !val) {
174
- console.log(` ${RED}✖ This field is required.${RESET}`)
175
- } else {
176
- break
177
- }
162
+ if (field.required && !val) console.log(` ${RED}✖ Required.${RESET}`)
163
+ else break
178
164
  }
179
165
  updated[field.key] = val
180
166
  console.log()
181
167
  }
182
168
 
183
- if (isEditMode) {
184
- fs.writeFileSync(ENV_PATH, mergeEnv(existing, updated), "utf-8")
185
- console.log(`${GREEN}${BOLD} ✅ .env updated successfully!${RESET}\n`)
169
+ if (isEdit) {
170
+ let content = fs.readFileSync(ENV_PATH, "utf-8")
171
+ for (const [k, v] of Object.entries(updated)) {
172
+ const regex = new RegExp(`^(${k}=.*)$`, "m")
173
+ if (regex.test(content)) content = content.replace(regex, `${k}="${v}"`)
174
+ else content += `\n${k}="${v}"`
175
+ }
176
+ fs.writeFileSync(ENV_PATH, content, "utf-8")
186
177
  } else {
187
178
  writeFreshEnv(updated)
188
- console.log(`${GREEN}${BOLD} ✅ .env created successfully!${RESET}\n`)
189
179
  }
190
180
 
191
- // Auto-install @moncircle/sdk if not in node_modules
192
- if (!sdkIsInstalled()) {
181
+ console.log(`${GREEN}${BOLD} ✅ .env saved!${RESET}\n`)
182
+
183
+ const useAutopilot = (await ask(` Enable Autopilot Mode? ${DIM}(DOM triggers + event tracking)${RESET}`, "Y")).toLowerCase() === "y"
184
+ updated.USE_AUTOPILOT = useAutopilot
185
+
186
+ return updated
187
+ }
188
+
189
+ // ── Phase 2: Deploy contract ───────────────────────────────────────────────
190
+ async function deployContract(values) {
191
+ const rpcUrl = values.MONAD_RPC_URL
192
+ const privateKey = values.MONAD_PRIVATE_KEY
193
+
194
+ if (!rpcUrl || !privateKey) {
195
+ console.log(` ${YELLOW}⚠ Skipping contract deploy — MONAD_RPC_URL or MONAD_PRIVATE_KEY not set.${RESET}`)
196
+ console.log(` ${DIM}Run: npx create-moncircle to add them later.${RESET}\n`)
197
+ return
198
+ }
199
+
200
+ console.log(` ${CYAN}${BOLD}Deploying MonLoyalty contract to Monad...${RESET}`)
201
+
202
+ // Find sdk's deploy script — works whether package is installed or run from source
203
+ const sdkRoot = (() => {
204
+ try { return path.dirname(require.resolve("@moncircle/sdk/package.json")) }
205
+ catch { return path.join(__dirname, "..") } // running from monorepo source
206
+ })()
207
+
208
+ const deployScript = path.join(sdkRoot, "scripts", "deploy-contract.js")
209
+ if (!fs.existsSync(deployScript)) {
210
+ console.log(` ${YELLOW}⚠ Deploy script not found. Build the SDK first: npm run build${RESET}\n`)
211
+ return
212
+ }
213
+
214
+ const result = spawnSync("node", [deployScript], {
215
+ stdio: ["inherit", "pipe", "inherit"],
216
+ env: { ...process.env, MONAD_RPC_URL: rpcUrl, MONAD_PRIVATE_KEY: privateKey }
217
+ })
218
+
219
+ if (result.status !== 0) {
220
+ console.log(` ${RED}✖ Contract deploy failed. Check your RPC URL and wallet balance.${RESET}\n`)
221
+ return
222
+ }
223
+
224
+ const output = result.stdout.toString().trim()
225
+ const match = output.match(/CONTRACT_ADDRESS=(0x[a-fA-F0-9]{40})/)
226
+ if (!match) {
227
+ console.log(` ${YELLOW}⚠ Could not parse contract address from deploy output.${RESET}`)
228
+ console.log(` ${DIM}${output}${RESET}\n`)
229
+ return
230
+ }
231
+
232
+ const contractAddress = match[1]
233
+ mergeEnvKey("MONAD_CONTRACT_ADDRESS", contractAddress)
234
+ console.log(` ${GREEN}✅ Contract deployed: ${CYAN}${contractAddress}${RESET}`)
235
+ console.log(` ${GREEN}✅ MONAD_CONTRACT_ADDRESS written to .env${RESET}\n`)
236
+ }
237
+
238
+ // ── Phase 3: Start the API ─────────────────────────────────────────────────
239
+ function startApi() {
240
+ // Check if @moncircle/sdk is installed
241
+ const sdkBin = (() => {
242
+ try {
243
+ const sdkRoot = path.dirname(require.resolve("@moncircle/sdk/package.json"))
244
+ const bin = path.join(sdkRoot, "dist", "bin.js")
245
+ return fs.existsSync(bin) ? bin : null
246
+ } catch { return null }
247
+ })()
248
+
249
+ if (!sdkBin) {
250
+ // Not installed — install it first
193
251
  console.log(` ${YELLOW}Installing @moncircle/sdk...${RESET}`)
194
252
  try {
195
253
  execSync("npm install @moncircle/sdk --save", { stdio: "inherit", cwd: process.cwd() })
196
- console.log(` ${GREEN}✓ @moncircle/sdk installed!${RESET}\n`)
197
254
  } catch {
198
- console.log(` ${YELLOW} Run ${CYAN}npm install @moncircle/sdk${RESET}${YELLOW} manually.${RESET}\n`)
255
+ console.log(` ${RED}✖ Could not install @moncircle/sdk. Run: npm install @moncircle/sdk${RESET}`)
256
+ return
199
257
  }
200
258
  }
201
259
 
202
- console.log(` Next steps:\n`)
203
- console.log(` ${CYAN}npx mon-loyalty-api start${RESET} — start the server`)
204
- console.log(` ${CYAN}npx create-moncircle${RESET} — reconfigure anytime\n`)
260
+ console.log(`\n${GREEN}${BOLD} 🚀 Starting MON Loyalty API...${RESET}\n`)
261
+
262
+ // Load .env before spawning
263
+
264
+ server.on("error", (err) => {
265
+ console.error(`${RED}Failed to start API:${RESET}`, err.message)
266
+ console.log(` Try manually: ${CYAN}npx mon-loyalty-api start${RESET}`)
267
+ })
268
+ }
269
+
270
+ // ── Phase 4: Create Scaffolding (Max Mode) ────────────────────────────────
271
+ function createScaffolding(values) {
272
+ const dashboardPath = path.join(process.cwd(), "loyalty-dashboard.html")
273
+ const serverPath = path.join(process.cwd(), "server.js")
274
+ const publicKey = values.MON_PUBLIC_KEY || "pk_live_67df231b188e46d38a2baef03b217756"
275
+
276
+ // 1. Generate Advanced Dashboard
277
+ const dashboardHtml = `<!DOCTYPE html>
278
+ <html lang="en">
279
+ <head>
280
+ <meta charset="UTF-8">
281
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
282
+ <title>MonCircle | Loyalty Dashboard</title>
283
+ <style>
284
+ :root { --bg: #0a0812; --card: #0f0d1a; --border: #1e1b2e; --primary: #6e54ff; --text: #e8e4f4; --dim: #6b6585; }
285
+ body { font-family: 'Geist', system-ui, sans-serif; background: var(--bg); color: var(--text); padding: 40px; }
286
+ .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 24px; max-width: 1200px; margin: 0 auto; }
287
+ .card { background: var(--card); border: 1px solid var(--border); border-radius: 16px; padding: 24px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); }
288
+ h1 { font-size: 28px; margin-bottom: 8px; letter-spacing: -0.02em; }
289
+ p { color: var(--dim); font-size: 14px; margin-bottom: 24px; }
290
+ .stat { margin-bottom: 20px; }
291
+ .stat-label { font-size: 11px; text-transform: uppercase; color: var(--dim); letter-spacing: 0.05em; margin-bottom: 4px; }
292
+ .stat-value { font-size: 24px; font-weight: 700; color: var(--primary); }
293
+ .btn { background: var(--primary); color: #fff; border: none; padding: 12px 24px; border-radius: 8px; font-weight: 600; cursor: pointer; display: block; width: 100%; margin-top: 10px; }
294
+ .feed { margin-top: 20px; font-size: 12px; color: var(--dim); border-top: 1px solid var(--border); padding-top: 12px; }
295
+ </style>
296
+ </head>
297
+ <body>
298
+ <div style="max-width: 1200px; margin: 0 auto 40px;">
299
+ <h1>Loyalty Dashboard</h1>
300
+ <p>Connected to <b>${publicKey}</b> • Monad Testnet</p>
301
+ </div>
302
+
303
+ <div class="grid">
304
+ <div class="card">
305
+ <div class="stat">
306
+ <div class="stat-label">Wallet Balance</div>
307
+ <div class="stat-value" id="dash-balance">0 MON</div>
308
+ </div>
309
+ <div class="stat">
310
+ <div class="stat-label">Recent Activity</div>
311
+ <div class="feed" id="dash-feed">Waiting for transactions...</div>
312
+ </div>
313
+ </div>
314
+
315
+ <div class="card">
316
+ <h3>Test Purchase</h3>
317
+ <p>Simulate a customer purchase to see Autopilot mode in action.</p>
318
+ <input type="number" id="purchase-amt" value="500" style="width:100%; background:#141121; border:1px solid var(--border); padding:10px; border-radius:8px; color:white; margin-bottom:12px;">
319
+ <button class="btn" onclick="simulatePurchase()">Place Order (Earn MON)</button>
320
+ </div>
321
+ </div>
322
+
323
+ <script>
324
+ function simulatePurchase() {
325
+ const amt = document.getElementById('purchase-amt').value;
326
+ const orderId = "ORD-" + Math.random().toString(36).slice(2, 9).toUpperCase();
327
+
328
+ // Emit Autopilot Event
329
+ window.dispatchEvent(new CustomEvent('mon-purchase-success', {
330
+ detail: { orderId, amount: amt }
331
+ }));
332
+
333
+ const feed = document.getElementById('dash-feed');
334
+ feed.innerHTML = "<div>Processed " + orderId + " • " + amt + " INR</div>" + feed.innerHTML;
335
+ }
336
+ </script>
337
+
338
+ <!-- MonCircle SDK Overlay -->
339
+ <script
340
+ src="https://moncircle.vercel.app/sdk/overlay.js"
341
+ data-key="${publicKey}"
342
+ data-user-id="demo_user_123"
343
+ data-autopilot="${values.USE_AUTOPILOT ? 'true' : 'false'}"
344
+ data-mon-amount="10">
345
+ </script>
346
+ </body>
347
+ </html>`
348
+
349
+ // 2. Generate Express Backend (ESM)
350
+ const serverJs = `import express from 'express';
351
+ import mongoose from 'mongoose';
352
+ import cors from 'cors';
353
+ import 'dotenv/config';
354
+
355
+ const app = express();
356
+ const PORT = process.env.PORT || 5000;
205
357
 
358
+ app.use(cors());
359
+ app.use(express.json());
360
+
361
+ // MongoDB Connection
362
+ mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/moncircle')
363
+ .then(() => console.log('✅ Loyalty DB Connected'))
364
+ .catch(err => console.error('❌ DB Connection Error:', err));
365
+
366
+ // Order Model
367
+ const OrderSchema = new mongoose.Schema({
368
+ orderId: { type: String, required: true, unique: true },
369
+ total: { type: Number, required: true },
370
+ customer: { walletAddress: String, email: String },
371
+ status: { type: String, default: 'completed' },
372
+ createdAt: { type: Date, default: Date.now }
373
+ });
374
+
375
+ const Order = mongoose.model('Order', OrderSchema);
376
+
377
+ // Checkout Endpoint (SDK will intercept this if proxy is set)
378
+ app.post('/api/checkout', async (req, res) => {
379
+ try {
380
+ const { orderId, total, customer } = req.body;
381
+ const newOrder = new Order({ orderId, total, customer });
382
+ await newOrder.save();
383
+
384
+ console.log(\`[Loyalty] Order \${orderId} saved locally.\`);
385
+
386
+ // Optional: Call MonCircle API for server-side verification
387
+ // fetch('https://moncircle.vercel.app/api/v1/orders', {
388
+ // headers: { 'x-api-key': process.env.MON_API_KEY }
389
+ // ...
390
+
391
+ res.json({ success: true, order: newOrder });
392
+ } catch (err) {
393
+ res.status(500).json({ error: err.message });
394
+ }
395
+ });
396
+
397
+ app.listen(PORT, () => console.log(\`🚀 Loyalty Backend running on http://localhost:\${PORT}\`));`
398
+
399
+ fs.writeFileSync(dashboardPath, dashboardHtml, "utf-8")
400
+ fs.writeFileSync(serverPath, serverJs, "utf-8")
401
+
402
+ return { dashboardPath, serverPath }
403
+ }
404
+
405
+ // ── Phase 4: Create demo page ──────────────────────────────────────────────
406
+ function createDemoPage(values) {
407
+ const demoPath = path.join(process.cwd(), "demo.html")
408
+ const publicKey = values.MON_PUBLIC_KEY || "pk_live_67df231b188e46d38a2baef03b217756"
409
+
410
+ const html = `<!DOCTYPE html>
411
+ <html lang="en">
412
+ <head>
413
+ <meta charset="UTF-8">
414
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
415
+ <title>MonCircle SDK Demo</title>
416
+ <style>
417
+ body { font-family: system-ui, sans-serif; background: #0a0812; color: #fff; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; }
418
+ .card { background: #0f0d1a; border: 1px solid #1e1b2e; padding: 32px; border-radius: 16px; text-align: center; max-width: 400px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); }
419
+ h1 { font-size: 24px; margin-bottom: 12px; }
420
+ p { color: #6b6585; font-size: 14px; margin-bottom: 24px; }
421
+ .btn { background: #6e54ff; color: #fff; border: none; padding: 12px 24px; border-radius: 8px; font-weight: 600; cursor: pointer; transition: transform 0.1s; }
422
+ .btn:active { transform: scale(0.98); }
423
+ </style>
424
+ </head>
425
+ <body>
426
+ <div class="card">
427
+ <h1>MonCircle Demo</h1>
428
+ <p>This page is connected to your platform via the MonCircle SDK. Click the widget in the corner to see your rewards!</p>
429
+ <button class="btn" onclick="simulateOrder()">Simulate Order (Earn MON)</button>
430
+ </div>
431
+
432
+ <script>
433
+ function simulateOrder() {
434
+ if (window.MonCircle) {
435
+ const orderId = "ORD-" + Math.random().toString(36).slice(2, 9).toUpperCase();
436
+ window.MonCircle.processOrder(orderId).then(() => {
437
+ console.log("Order simulated successfully!");
438
+ });
439
+ } else {
440
+ alert("MonCircle SDK not loaded yet.");
441
+ }
442
+ }
443
+ </script>
444
+
445
+ <!-- MonCircle SDK Overlay -->
446
+ <script
447
+ src="https://moncircle.vercel.app/sdk/overlay.js"
448
+ data-key="${publicKey}"
449
+ data-user-id="demo_user_123"
450
+ data-amount="1000"
451
+ data-mon-amount="10">
452
+ </script>
453
+ </body>
454
+ </html>`
455
+
456
+ fs.writeFileSync(demoPath, html, "utf-8")
457
+ return demoPath
458
+ }
459
+
460
+ // ── Main ───────────────────────────────────────────────────────────────────
461
+ async function main() {
462
+ const values = await runSetup()
206
463
  rl.close()
464
+
465
+ await deployContract(values)
466
+ const files = createScaffolding(values)
467
+
468
+ console.log(`${BOLD}${GREEN}╔══════════════════════════════════════════════════════╗`)
469
+ console.log(`║ 🎉 @moncircle/sdk — PREMIUM RELEASE! ║`)
470
+ console.log(`╚══════════════════════════════════════════════════════╝${RESET}\n`)
471
+ console.log(` ${GREEN}✅ Redesigned ${CYAN}Full-Screen UI${RESET} ${DIM}(Signature Purple Theme)${RESET}`)
472
+ console.log(` ${GREEN}✅ Created ${CYAN}loyalty-dashboard.html${RESET} ${DIM}(Advanced UI)${RESET}`)
473
+ console.log(` ${GREEN}✅ Created ${CYAN}server.js${RESET} ${DIM}(Simple Express Backend)${RESET}`)
474
+ console.log(` ${DIM}API docs: POST /v1/platforms → POST /v1/orders → POST /v1/withdraw${RESET}\n`)
475
+
476
+ startApi()
207
477
  }
208
478
 
209
479
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-moncircle",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "description": "Interactive setup wizard for @moncircle/sdk — run via npx create-moncircle",
5
5
  "bin": {
6
6
  "create-moncircle": "index.js"