moltengine-cli 0.1.4 → 0.1.6
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/package.json +1 -1
- package/src/moltengine.js +539 -141
package/package.json
CHANGED
package/src/moltengine.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { request } from "undici";
|
|
4
|
+
import { exec } from "child_process";
|
|
5
|
+
import { createInterface } from "readline";
|
|
4
6
|
|
|
5
7
|
// Configuration
|
|
6
|
-
const API_URL = process.env.MOLTENGINE_API || "
|
|
8
|
+
const API_URL = process.env.MOLTENGINE_API || "https://api.moltengine.com";
|
|
7
9
|
const TENANT_KEY = process.env.MOLT_TENANT_KEY || "";
|
|
8
10
|
const WEB_URL = process.env.MOLTENGINE_WEB || "https://moltengine.com";
|
|
9
11
|
|
|
@@ -60,9 +62,11 @@ ${colors.primary} __ __ _ _ _
|
|
|
60
62
|
`;
|
|
61
63
|
|
|
62
64
|
const ASCII_LOGO_COMPACT = `
|
|
63
|
-
${colors.primary}
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
${colors.primary} __ __ _ _ _
|
|
66
|
+
| \\/ |___| | |_ ___ _ _ _(_)_ _ ___
|
|
67
|
+
| |\\/| / _ \\ | __/ -_) ' \\/ _\` | ' \\/ -_)
|
|
68
|
+
|_| |_\\___/_|\\__\\___|_||_\\__, |_||_\\___|
|
|
69
|
+
|___/${colors.reset}
|
|
66
70
|
`;
|
|
67
71
|
|
|
68
72
|
// ============================================================================
|
|
@@ -93,6 +97,48 @@ function error(message) {
|
|
|
93
97
|
process.exit(1);
|
|
94
98
|
}
|
|
95
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Check if tenant needs to complete payment and prompt if so
|
|
102
|
+
* Returns true if payment is needed (and command should stop)
|
|
103
|
+
*/
|
|
104
|
+
function checkPaymentRequired(tenant) {
|
|
105
|
+
const unpaidStatuses = ['pending', 'incomplete', 'incomplete_expired', 'past_due', 'unpaid', 'trialing'];
|
|
106
|
+
const billingStatus = tenant.billingStatus || tenant.billing_status || tenant.status;
|
|
107
|
+
|
|
108
|
+
// Check if tenant needs to pay
|
|
109
|
+
if (unpaidStatuses.includes(billingStatus?.toLowerCase())) {
|
|
110
|
+
console.log(ASCII_LOGO_COMPACT);
|
|
111
|
+
console.log();
|
|
112
|
+
print(`${colors.warning}⚡ Subscription Required${colors.reset}`, colors.bold);
|
|
113
|
+
console.log();
|
|
114
|
+
|
|
115
|
+
if (billingStatus === 'past_due') {
|
|
116
|
+
console.log(` Your subscription payment is past due.`);
|
|
117
|
+
console.log(` Please update your payment method to continue.`);
|
|
118
|
+
} else {
|
|
119
|
+
console.log(` Complete your subscription to start using Moltengine.`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log();
|
|
123
|
+
print("Plans", colors.bold);
|
|
124
|
+
print("─".repeat(40), colors.muted);
|
|
125
|
+
console.log(` ${colors.accent}Starter${colors.reset} ${PLAN_PRICES.starter} — 1 agent, all tools, approvals`);
|
|
126
|
+
console.log(` ${colors.accent}Team${colors.reset} ${PLAN_PRICES.team} — 5 agents, approvals`);
|
|
127
|
+
console.log(` ${colors.accent}Business${colors.reset} ${PLAN_PRICES.business} — 25 agents, SIEM, RBAC`);
|
|
128
|
+
console.log(` ${colors.accent}Enterprise${colors.reset} ${PLAN_PRICES.enterprise} — SSO, VPC, compliance`);
|
|
129
|
+
console.log();
|
|
130
|
+
|
|
131
|
+
const checkoutUrl = `${WEB_URL}/checkout`;
|
|
132
|
+
print(`${colors.info}→ Subscribe now: ${colors.accent}${checkoutUrl}${colors.reset}`, colors.bold);
|
|
133
|
+
console.log();
|
|
134
|
+
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
96
142
|
/**
|
|
97
143
|
* Print success message
|
|
98
144
|
*/
|
|
@@ -111,12 +157,42 @@ function info(message) {
|
|
|
111
157
|
// API CLIENT
|
|
112
158
|
// ============================================================================
|
|
113
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Show signup/login prompt when no tenant key is set
|
|
162
|
+
*/
|
|
163
|
+
function showAuthRequired() {
|
|
164
|
+
console.log(ASCII_LOGO_COMPACT);
|
|
165
|
+
console.log();
|
|
166
|
+
print(`${colors.warning}🔑 Authentication Required${colors.reset}`, colors.bold);
|
|
167
|
+
console.log();
|
|
168
|
+
console.log(` You need a Moltengine account to use the CLI.`);
|
|
169
|
+
console.log();
|
|
170
|
+
print("Get Started", colors.bold);
|
|
171
|
+
print("─".repeat(40), colors.muted);
|
|
172
|
+
console.log(` 1. Sign up or log in at ${colors.accent}${WEB_URL}/login${colors.reset}`);
|
|
173
|
+
console.log(` 2. Subscribe to a plan at ${colors.accent}${WEB_URL}/checkout${colors.reset}`);
|
|
174
|
+
console.log(` 3. Copy your tenant key from the dashboard`);
|
|
175
|
+
console.log(` 4. Set it in your environment:`);
|
|
176
|
+
console.log();
|
|
177
|
+
console.log(` ${colors.muted}export MOLT_TENANT_KEY="your-key-here"${colors.reset}`);
|
|
178
|
+
console.log();
|
|
179
|
+
print("Plans", colors.bold);
|
|
180
|
+
print("─".repeat(40), colors.muted);
|
|
181
|
+
console.log(` ${colors.accent}Starter${colors.reset} ${PLAN_PRICES.starter} — 1 agent, all tools, approvals`);
|
|
182
|
+
console.log(` ${colors.accent}Team${colors.reset} ${PLAN_PRICES.team} — 5 agents, approvals`);
|
|
183
|
+
console.log(` ${colors.accent}Business${colors.reset} ${PLAN_PRICES.business} — 25 agents, SIEM, RBAC`);
|
|
184
|
+
console.log();
|
|
185
|
+
print(`${colors.info}→ Start free: ${colors.accent}${WEB_URL}/login${colors.reset}`, colors.bold);
|
|
186
|
+
console.log();
|
|
187
|
+
process.exit(0);
|
|
188
|
+
}
|
|
189
|
+
|
|
114
190
|
/**
|
|
115
191
|
* Make an authenticated GET request
|
|
116
192
|
*/
|
|
117
193
|
async function apiGet(path) {
|
|
118
194
|
if (!TENANT_KEY) {
|
|
119
|
-
|
|
195
|
+
showAuthRequired();
|
|
120
196
|
}
|
|
121
197
|
|
|
122
198
|
const url = `${API_URL}${path}`;
|
|
@@ -151,7 +227,7 @@ async function apiGet(path) {
|
|
|
151
227
|
*/
|
|
152
228
|
async function apiPost(path, body = {}) {
|
|
153
229
|
if (!TENANT_KEY) {
|
|
154
|
-
|
|
230
|
+
showAuthRequired();
|
|
155
231
|
}
|
|
156
232
|
|
|
157
233
|
const url = `${API_URL}${path}`;
|
|
@@ -266,10 +342,316 @@ function padLeft(str, len) {
|
|
|
266
342
|
return str.padStart(len);
|
|
267
343
|
}
|
|
268
344
|
|
|
345
|
+
// ============================================================================
|
|
346
|
+
// BROWSER HELPER
|
|
347
|
+
// ============================================================================
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Open a URL in the default browser
|
|
351
|
+
*/
|
|
352
|
+
function openBrowser(url) {
|
|
353
|
+
const platform = process.platform;
|
|
354
|
+
let command;
|
|
355
|
+
|
|
356
|
+
if (platform === "darwin") {
|
|
357
|
+
command = `open "${url}"`;
|
|
358
|
+
} else if (platform === "win32") {
|
|
359
|
+
command = `start "" "${url}"`;
|
|
360
|
+
} else {
|
|
361
|
+
// Linux and others
|
|
362
|
+
command = `xdg-open "${url}" 2>/dev/null || sensible-browser "${url}" 2>/dev/null || x-www-browser "${url}" 2>/dev/null || gnome-open "${url}" 2>/dev/null`;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return new Promise((resolve) => {
|
|
366
|
+
exec(command, (err) => {
|
|
367
|
+
resolve(!err);
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Prompt user for input
|
|
374
|
+
*/
|
|
375
|
+
function prompt(question) {
|
|
376
|
+
const rl = createInterface({
|
|
377
|
+
input: process.stdin,
|
|
378
|
+
output: process.stdout
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
return new Promise((resolve) => {
|
|
382
|
+
rl.question(question, (answer) => {
|
|
383
|
+
rl.close();
|
|
384
|
+
resolve(answer.trim());
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
269
389
|
// ============================================================================
|
|
270
390
|
// COMMANDS
|
|
271
391
|
// ============================================================================
|
|
272
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Login command - authenticate and get API key
|
|
395
|
+
*/
|
|
396
|
+
async function cmdLogin() {
|
|
397
|
+
console.log(ASCII_LOGO_COMPACT);
|
|
398
|
+
|
|
399
|
+
// Check if already authenticated
|
|
400
|
+
if (TENANT_KEY) {
|
|
401
|
+
try {
|
|
402
|
+
const tenant = await apiGet("/tenant/me");
|
|
403
|
+
const billingStatus = tenant.billingStatus || tenant.billing_status || tenant.status;
|
|
404
|
+
|
|
405
|
+
if (billingStatus === "active") {
|
|
406
|
+
success("You're already logged in!");
|
|
407
|
+
console.log();
|
|
408
|
+
console.log(` ${colors.muted}Tenant:${colors.reset} ${tenant.name}`);
|
|
409
|
+
console.log(` ${colors.muted}Email:${colors.reset} ${tenant.email}`);
|
|
410
|
+
console.log(` ${colors.muted}Plan:${colors.reset} ${formatPlan(tenant.plan || "starter")}`);
|
|
411
|
+
console.log();
|
|
412
|
+
console.log(` ${colors.info}Run ${colors.accent}moltctl status${colors.reset} to see your dashboard.`);
|
|
413
|
+
console.log();
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Has key but not paid - redirect to checkout
|
|
418
|
+
console.log();
|
|
419
|
+
print(`${colors.warning}⚡ Subscription Required${colors.reset}`, colors.bold);
|
|
420
|
+
console.log();
|
|
421
|
+
console.log(` Your account exists but needs an active subscription.`);
|
|
422
|
+
console.log();
|
|
423
|
+
|
|
424
|
+
const checkoutUrl = `${WEB_URL}/checkout`;
|
|
425
|
+
print(`Opening checkout in your browser...`, colors.info);
|
|
426
|
+
console.log();
|
|
427
|
+
|
|
428
|
+
const opened = await openBrowser(checkoutUrl);
|
|
429
|
+
if (!opened) {
|
|
430
|
+
console.log(` ${colors.muted}Could not open browser. Please visit:${colors.reset}`);
|
|
431
|
+
console.log(` ${colors.accent}${checkoutUrl}${colors.reset}`);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
console.log();
|
|
435
|
+
console.log(` ${colors.muted}After subscribing, run ${colors.accent}moltctl status${colors.reset}${colors.muted} to verify.${colors.reset}`);
|
|
436
|
+
console.log();
|
|
437
|
+
return;
|
|
438
|
+
} catch {
|
|
439
|
+
// Key is invalid, proceed with login flow
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// No key or invalid key - start login flow
|
|
444
|
+
console.log();
|
|
445
|
+
print("Welcome to Moltengine!", colors.bold);
|
|
446
|
+
console.log();
|
|
447
|
+
console.log(` Let's get you set up with a secure Moltbot runtime.`);
|
|
448
|
+
console.log();
|
|
449
|
+
|
|
450
|
+
print("Choose an option:", colors.bold);
|
|
451
|
+
print("─".repeat(40), colors.muted);
|
|
452
|
+
console.log(` ${colors.accent}[1]${colors.reset} Sign up / Subscribe (new users)`);
|
|
453
|
+
console.log(` ${colors.accent}[2]${colors.reset} Log in (existing users)`);
|
|
454
|
+
console.log(` ${colors.accent}[3]${colors.reset} Enter API key manually`);
|
|
455
|
+
console.log();
|
|
456
|
+
|
|
457
|
+
const choice = await prompt(` ${colors.muted}Enter choice (1/2/3):${colors.reset} `);
|
|
458
|
+
console.log();
|
|
459
|
+
|
|
460
|
+
if (choice === "1") {
|
|
461
|
+
// New user - go to checkout
|
|
462
|
+
const checkoutUrl = `${WEB_URL}/checkout`;
|
|
463
|
+
print("Opening checkout in your browser...", colors.info);
|
|
464
|
+
|
|
465
|
+
const opened = await openBrowser(checkoutUrl);
|
|
466
|
+
if (!opened) {
|
|
467
|
+
console.log();
|
|
468
|
+
console.log(` ${colors.muted}Could not open browser. Please visit:${colors.reset}`);
|
|
469
|
+
console.log(` ${colors.accent}${checkoutUrl}${colors.reset}`);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
console.log();
|
|
473
|
+
print("After checkout:", colors.bold);
|
|
474
|
+
print("─".repeat(40), colors.muted);
|
|
475
|
+
console.log(` 1. Complete payment via Stripe`);
|
|
476
|
+
console.log(` 2. Copy your API key from the success page`);
|
|
477
|
+
console.log(` 3. Run: ${colors.accent}moltctl login${colors.reset} and choose option 3`);
|
|
478
|
+
console.log(` Or set: ${colors.accent}export MOLT_TENANT_KEY="your-key"${colors.reset}`);
|
|
479
|
+
console.log();
|
|
480
|
+
|
|
481
|
+
} else if (choice === "2") {
|
|
482
|
+
// Existing user - go to login
|
|
483
|
+
const loginUrl = `${WEB_URL}/login`;
|
|
484
|
+
print("Opening login page in your browser...", colors.info);
|
|
485
|
+
|
|
486
|
+
const opened = await openBrowser(loginUrl);
|
|
487
|
+
if (!opened) {
|
|
488
|
+
console.log();
|
|
489
|
+
console.log(` ${colors.muted}Could not open browser. Please visit:${colors.reset}`);
|
|
490
|
+
console.log(` ${colors.accent}${loginUrl}${colors.reset}`);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
console.log();
|
|
494
|
+
print("After login:", colors.bold);
|
|
495
|
+
print("─".repeat(40), colors.muted);
|
|
496
|
+
console.log(` 1. Check your email for the magic link`);
|
|
497
|
+
console.log(` 2. Go to Credentials in your dashboard`);
|
|
498
|
+
console.log(` 3. Copy your API key`);
|
|
499
|
+
console.log(` 4. Run: ${colors.accent}moltctl login${colors.reset} and choose option 3`);
|
|
500
|
+
console.log(` Or set: ${colors.accent}export MOLT_TENANT_KEY="your-key"${colors.reset}`);
|
|
501
|
+
console.log();
|
|
502
|
+
|
|
503
|
+
} else if (choice === "3") {
|
|
504
|
+
// Manual key entry
|
|
505
|
+
const key = await prompt(` ${colors.muted}Enter your API key:${colors.reset} `);
|
|
506
|
+
|
|
507
|
+
if (!key) {
|
|
508
|
+
print("No key entered.", colors.warning);
|
|
509
|
+
console.log();
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Validate the key
|
|
514
|
+
print("Validating key...", colors.info);
|
|
515
|
+
|
|
516
|
+
try {
|
|
517
|
+
// Temporarily set the key to validate
|
|
518
|
+
const response = await request(`${API_URL}/tenant/me`, {
|
|
519
|
+
method: "GET",
|
|
520
|
+
headers: {
|
|
521
|
+
"x-tenant-key": key,
|
|
522
|
+
"Accept": "application/json"
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const text = await response.body.text();
|
|
527
|
+
|
|
528
|
+
if (response.statusCode >= 400) {
|
|
529
|
+
print("Invalid API key. Please check and try again.", colors.error);
|
|
530
|
+
console.log();
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const tenant = JSON.parse(text);
|
|
535
|
+
const billingStatus = tenant.billingStatus || tenant.billing_status || tenant.status;
|
|
536
|
+
|
|
537
|
+
console.log();
|
|
538
|
+
success("API key validated!");
|
|
539
|
+
console.log();
|
|
540
|
+
console.log(` ${colors.muted}Tenant:${colors.reset} ${tenant.name}`);
|
|
541
|
+
console.log(` ${colors.muted}Email:${colors.reset} ${tenant.email}`);
|
|
542
|
+
console.log(` ${colors.muted}Plan:${colors.reset} ${formatPlan(tenant.plan || "starter")}`);
|
|
543
|
+
console.log(` ${colors.muted}Status:${colors.reset} ${formatStatus(billingStatus)}`);
|
|
544
|
+
console.log();
|
|
545
|
+
|
|
546
|
+
// Check if payment needed
|
|
547
|
+
const unpaidStatuses = ['pending', 'incomplete', 'incomplete_expired', 'unpaid'];
|
|
548
|
+
if (unpaidStatuses.includes(billingStatus?.toLowerCase())) {
|
|
549
|
+
print(`${colors.warning}⚠ Subscription required${colors.reset}`, colors.bold);
|
|
550
|
+
console.log();
|
|
551
|
+
console.log(` Complete your subscription at: ${colors.accent}${WEB_URL}/checkout${colors.reset}`);
|
|
552
|
+
console.log();
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Show how to save the key
|
|
556
|
+
print("Save your key:", colors.bold);
|
|
557
|
+
print("─".repeat(40), colors.muted);
|
|
558
|
+
console.log(` Add to your shell config (~/.bashrc or ~/.zshrc):`);
|
|
559
|
+
console.log();
|
|
560
|
+
console.log(` ${colors.accent}export MOLT_TENANT_KEY="${key}"${colors.reset}`);
|
|
561
|
+
console.log();
|
|
562
|
+
console.log(` Then run: ${colors.accent}source ~/.bashrc${colors.reset}`);
|
|
563
|
+
console.log();
|
|
564
|
+
|
|
565
|
+
} catch (err) {
|
|
566
|
+
if (err.code === "ECONNREFUSED") {
|
|
567
|
+
print(`Cannot connect to API at ${API_URL}`, colors.error);
|
|
568
|
+
} else {
|
|
569
|
+
print(`Validation failed: ${err.message}`, colors.error);
|
|
570
|
+
}
|
|
571
|
+
console.log();
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
} else {
|
|
575
|
+
print("Invalid choice. Please run 'moltctl login' again.", colors.warning);
|
|
576
|
+
console.log();
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Checkout command - go directly to checkout/subscribe
|
|
582
|
+
*/
|
|
583
|
+
async function cmdCheckout(plan) {
|
|
584
|
+
console.log(ASCII_LOGO_COMPACT);
|
|
585
|
+
console.log();
|
|
586
|
+
|
|
587
|
+
// Check if already subscribed
|
|
588
|
+
if (TENANT_KEY) {
|
|
589
|
+
try {
|
|
590
|
+
const tenant = await apiGet("/tenant/me");
|
|
591
|
+
const billingStatus = tenant.billingStatus || tenant.billing_status || tenant.status;
|
|
592
|
+
|
|
593
|
+
if (billingStatus === "active") {
|
|
594
|
+
success("You already have an active subscription!");
|
|
595
|
+
console.log();
|
|
596
|
+
console.log(` ${colors.muted}Plan:${colors.reset} ${formatPlan(tenant.plan || "starter")}`);
|
|
597
|
+
console.log();
|
|
598
|
+
console.log(` ${colors.info}To upgrade, run: ${colors.accent}moltctl upgrade${colors.reset}`);
|
|
599
|
+
console.log(` ${colors.info}To manage billing: ${colors.accent}${WEB_URL}/dashboard/subscription${colors.reset}`);
|
|
600
|
+
console.log();
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
} catch {
|
|
604
|
+
// Key invalid, proceed to checkout
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
print("Plans", colors.bold);
|
|
609
|
+
print("─".repeat(40), colors.muted);
|
|
610
|
+
console.log(` ${colors.accent}starter${colors.reset} ${PLAN_PRICES.starter} — 1 agent, all tools, approvals`);
|
|
611
|
+
console.log(` ${colors.accent}team${colors.reset} ${PLAN_PRICES.team} — 5 agents, approvals`);
|
|
612
|
+
console.log(` ${colors.accent}business${colors.reset} ${PLAN_PRICES.business} — 25 agents, SIEM, RBAC`);
|
|
613
|
+
console.log(` ${colors.accent}enterprise${colors.reset} ${PLAN_PRICES.enterprise} — SSO, VPC, compliance`);
|
|
614
|
+
console.log();
|
|
615
|
+
|
|
616
|
+
// Build checkout URL
|
|
617
|
+
let checkoutUrl = `${WEB_URL}/checkout`;
|
|
618
|
+
if (plan && ['starter', 'team', 'business', 'enterprise'].includes(plan.toLowerCase())) {
|
|
619
|
+
if (plan.toLowerCase() === 'enterprise') {
|
|
620
|
+
print("Enterprise Plan", colors.bold);
|
|
621
|
+
print("─".repeat(40), colors.muted);
|
|
622
|
+
console.log(` Contact sales for custom pricing and features.`);
|
|
623
|
+
console.log();
|
|
624
|
+
console.log(` ${colors.info}→ Email: ${colors.accent}sales@mg.moltengine.com${colors.reset}`);
|
|
625
|
+
console.log();
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
checkoutUrl += `?plan=${plan.toLowerCase()}`;
|
|
629
|
+
print(`Opening checkout for ${plan} plan...`, colors.info);
|
|
630
|
+
} else if (plan) {
|
|
631
|
+
print(`Unknown plan: ${plan}. Opening general checkout...`, colors.warning);
|
|
632
|
+
} else {
|
|
633
|
+
print("Opening checkout in your browser...", colors.info);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const opened = await openBrowser(checkoutUrl);
|
|
637
|
+
|
|
638
|
+
if (!opened) {
|
|
639
|
+
console.log();
|
|
640
|
+
console.log(` ${colors.muted}Could not open browser. Please visit:${colors.reset}`);
|
|
641
|
+
console.log(` ${colors.accent}${checkoutUrl}${colors.reset}`);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
console.log();
|
|
645
|
+
print("Next steps:", colors.bold);
|
|
646
|
+
print("─".repeat(40), colors.muted);
|
|
647
|
+
console.log(` 1. Complete checkout via Stripe`);
|
|
648
|
+
console.log(` 2. Copy your API key from the success page`);
|
|
649
|
+
console.log(` 3. Set your environment variable:`);
|
|
650
|
+
console.log(` ${colors.accent}export MOLT_TENANT_KEY="your-key"${colors.reset}`);
|
|
651
|
+
console.log(` 4. Run ${colors.accent}moltctl status${colors.reset} to verify`);
|
|
652
|
+
console.log();
|
|
653
|
+
}
|
|
654
|
+
|
|
273
655
|
/**
|
|
274
656
|
* Display help with branding
|
|
275
657
|
*/
|
|
@@ -282,6 +664,11 @@ function showHelp() {
|
|
|
282
664
|
console.log(` ${colors.primary}moltctl${colors.reset} <command> [options]`);
|
|
283
665
|
console.log();
|
|
284
666
|
|
|
667
|
+
print("GETTING STARTED", colors.bold);
|
|
668
|
+
console.log(` ${colors.accent}login${colors.reset} Authenticate and get your API key`);
|
|
669
|
+
console.log(` ${colors.accent}checkout${colors.reset} Subscribe to a plan (opens browser)`);
|
|
670
|
+
console.log();
|
|
671
|
+
|
|
285
672
|
print("COMMANDS", colors.bold);
|
|
286
673
|
console.log(` ${colors.accent}status${colors.reset} Show status, usage, and pending approvals`);
|
|
287
674
|
console.log(` ${colors.accent}whoami${colors.reset} Show current tenant and plan information`);
|
|
@@ -289,11 +676,19 @@ function showHelp() {
|
|
|
289
676
|
console.log(` ${colors.accent}approvals${colors.reset} List and manage pending approvals`);
|
|
290
677
|
console.log(` ${colors.accent}deployments${colors.reset} List recent deployments`);
|
|
291
678
|
console.log(` ${colors.accent}upgrade${colors.reset} Upgrade to a higher plan`);
|
|
292
|
-
console.log(` ${colors.accent}audit${colors.reset} Search and export audit logs
|
|
293
|
-
console.log(` ${colors.accent}policies${colors.reset} Manage security policies
|
|
679
|
+
console.log(` ${colors.accent}audit${colors.reset} Search and export audit logs`);
|
|
680
|
+
console.log(` ${colors.accent}policies${colors.reset} Manage security policies`);
|
|
294
681
|
console.log(` ${colors.accent}help${colors.reset} Show this help message`);
|
|
295
682
|
console.log();
|
|
296
683
|
|
|
684
|
+
print("QUICK START", colors.bold);
|
|
685
|
+
console.log(` ${colors.muted}# New user? Start here:${colors.reset}`);
|
|
686
|
+
console.log(` ${colors.primary}moltctl${colors.reset} login`);
|
|
687
|
+
console.log();
|
|
688
|
+
console.log(` ${colors.muted}# Or go directly to checkout:${colors.reset}`);
|
|
689
|
+
console.log(` ${colors.primary}moltctl${colors.reset} checkout starter`);
|
|
690
|
+
console.log();
|
|
691
|
+
|
|
297
692
|
print("APPROVALS", colors.bold);
|
|
298
693
|
console.log(` ${colors.primary}moltctl${colors.reset} approvals list List pending approvals`);
|
|
299
694
|
console.log(` ${colors.primary}moltctl${colors.reset} approvals approve <id> Approve a pending action`);
|
|
@@ -302,19 +697,7 @@ function showHelp() {
|
|
|
302
697
|
|
|
303
698
|
print("ENVIRONMENT", colors.bold);
|
|
304
699
|
console.log(` ${colors.muted}MOLTENGINE_API${colors.reset} API URL (default: http://localhost:8080)`);
|
|
305
|
-
console.log(` ${colors.muted}MOLT_TENANT_KEY${colors.reset} Your tenant API key (
|
|
306
|
-
console.log();
|
|
307
|
-
|
|
308
|
-
print("EXAMPLES", colors.bold);
|
|
309
|
-
console.log(` ${colors.muted}# Set up environment${colors.reset}`);
|
|
310
|
-
console.log(` export MOLTENGINE_API="https://api.moltengine.com"`);
|
|
311
|
-
console.log(` export MOLT_TENANT_KEY="your-tenant-key-here"`);
|
|
312
|
-
console.log();
|
|
313
|
-
console.log(` ${colors.muted}# Check status and pending approvals${colors.reset}`);
|
|
314
|
-
console.log(` ${colors.primary}moltctl${colors.reset} status`);
|
|
315
|
-
console.log();
|
|
316
|
-
console.log(` ${colors.muted}# Approve a pending action${colors.reset}`);
|
|
317
|
-
console.log(` ${colors.primary}moltctl${colors.reset} approvals approve 1`);
|
|
700
|
+
console.log(` ${colors.muted}MOLT_TENANT_KEY${colors.reset} Your tenant API key (from checkout)`);
|
|
318
701
|
console.log();
|
|
319
702
|
}
|
|
320
703
|
|
|
@@ -324,6 +707,11 @@ function showHelp() {
|
|
|
324
707
|
async function cmdWhoami() {
|
|
325
708
|
const data = await apiGet("/tenant/me");
|
|
326
709
|
|
|
710
|
+
// Check if payment is required
|
|
711
|
+
if (checkPaymentRequired(data)) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
|
|
327
715
|
printHeader("Tenant Information");
|
|
328
716
|
console.log(` ${colors.muted}ID:${colors.reset} ${data.tenantId}`);
|
|
329
717
|
console.log(` ${colors.muted}Name:${colors.reset} ${data.name}`);
|
|
@@ -362,9 +750,16 @@ async function cmdWhoami() {
|
|
|
362
750
|
* Show tenant status with usage and approvals/blocked actions
|
|
363
751
|
*/
|
|
364
752
|
async function cmdStatus() {
|
|
365
|
-
// Fetch tenant info
|
|
366
|
-
const
|
|
367
|
-
|
|
753
|
+
// Fetch tenant info first to check payment status
|
|
754
|
+
const tenant = await apiGet("/tenant/me");
|
|
755
|
+
|
|
756
|
+
// Check if payment is required
|
|
757
|
+
if (checkPaymentRequired(tenant)) {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Fetch usage and limits in parallel
|
|
762
|
+
const [usage, limits] = await Promise.all([
|
|
368
763
|
apiGet("/tenant/usage").catch(() => ({ runs: 0, events: 0, agents: 0 })),
|
|
369
764
|
apiGet("/tenant/limits").catch(() => ({ runs: 100, events: 1000, agents: 1 }))
|
|
370
765
|
]);
|
|
@@ -387,16 +782,14 @@ async function cmdStatus() {
|
|
|
387
782
|
}
|
|
388
783
|
|
|
389
784
|
const plan = tenant.plan || "starter";
|
|
390
|
-
|
|
785
|
+
// All plans have approvals
|
|
391
786
|
|
|
392
787
|
// Print logo
|
|
393
788
|
console.log(ASCII_LOGO_COMPACT);
|
|
394
789
|
|
|
395
790
|
// Tenant Status section
|
|
396
791
|
printHeader("Tenant Status");
|
|
397
|
-
const planDisplay = plan
|
|
398
|
-
? `Starter (${PLAN_PRICES.starter}) — ${colors.warning}Safe Mode${colors.reset}`
|
|
399
|
-
: `${plan.charAt(0).toUpperCase() + plan.slice(1)} (${PLAN_PRICES[plan] || ""})`;
|
|
792
|
+
const planDisplay = `${plan.charAt(0).toUpperCase() + plan.slice(1)} (${PLAN_PRICES[plan] || ""})`;
|
|
400
793
|
console.log(` ${colors.muted}Plan:${colors.reset} ${planDisplay}`);
|
|
401
794
|
|
|
402
795
|
const statusIcon = tenant.status === "active" ? "●" : "○";
|
|
@@ -424,67 +817,38 @@ async function cmdStatus() {
|
|
|
424
817
|
const runsPct = limits.runs > 0 ? (usage.runs / limits.runs) * 100 : 0;
|
|
425
818
|
const eventsPct = limits.events > 0 ? (usage.events / limits.events) * 100 : 0;
|
|
426
819
|
|
|
427
|
-
// Approvals section (
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
print(`Approvals ${colors.warning}⚠ ${pendingCount} pending${colors.reset}`, colors.bold);
|
|
435
|
-
} else {
|
|
436
|
-
print(`Approvals ${colors.success}✓ none pending${colors.reset}`, colors.bold);
|
|
437
|
-
}
|
|
438
|
-
print("─".repeat(40), colors.muted);
|
|
439
|
-
|
|
440
|
-
if (pendingCount > 0) {
|
|
441
|
-
// Show up to 5 pending approvals
|
|
442
|
-
const toShow = pendingApprovals.slice(0, 5);
|
|
443
|
-
for (let i = 0; i < toShow.length; i++) {
|
|
444
|
-
const a = toShow[i];
|
|
445
|
-
const actionType = (a.action_type || "ACTION").toUpperCase().padEnd(5);
|
|
446
|
-
const target = truncate(a.action_path || a.description || "unknown", 28);
|
|
447
|
-
const time = formatRelativeTime(a.created_at);
|
|
448
|
-
console.log(` ${colors.muted}[${i + 1}]${colors.reset} ${actionType} ${target.padEnd(28)} ${colors.muted}${time}${colors.reset}`);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
if (pendingCount > 5) {
|
|
452
|
-
console.log(` ${colors.muted}... and ${pendingCount - 5} more${colors.reset}`);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
console.log();
|
|
456
|
-
console.log(` ${colors.info}→ moltctl approvals approve <id>${colors.reset}`);
|
|
457
|
-
console.log(` ${colors.info}→ moltctl approvals list --all${colors.reset}`);
|
|
458
|
-
} else {
|
|
459
|
-
console.log(` ${colors.muted}No actions waiting for approval${colors.reset}`);
|
|
460
|
-
}
|
|
820
|
+
// Approvals section (available on all plans)
|
|
821
|
+
const pendingApprovals = approvals.filter(a => a.status === "pending");
|
|
822
|
+
const pendingCount = pendingApprovals.length;
|
|
823
|
+
|
|
824
|
+
console.log();
|
|
825
|
+
if (pendingCount > 0) {
|
|
826
|
+
print(`Approvals ${colors.warning}⚠ ${pendingCount} pending${colors.reset}`, colors.bold);
|
|
461
827
|
} else {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
828
|
+
print(`Approvals ${colors.success}✓ none pending${colors.reset}`, colors.bold);
|
|
829
|
+
}
|
|
830
|
+
print("─".repeat(40), colors.muted);
|
|
831
|
+
|
|
832
|
+
if (pendingCount > 0) {
|
|
833
|
+
// Show up to 5 pending approvals
|
|
834
|
+
const toShow = pendingApprovals.slice(0, 5);
|
|
835
|
+
for (let i = 0; i < toShow.length; i++) {
|
|
836
|
+
const a = toShow[i];
|
|
837
|
+
const actionType = (a.action_type || "ACTION").toUpperCase().padEnd(5);
|
|
838
|
+
const target = truncate(a.action_path || a.description || "unknown", 28);
|
|
839
|
+
const time = formatRelativeTime(a.created_at);
|
|
840
|
+
console.log(` ${colors.muted}[${i + 1}]${colors.reset} ${actionType} ${target.padEnd(28)} ${colors.muted}${time}${colors.reset}`);
|
|
471
841
|
}
|
|
472
|
-
print("─".repeat(40), colors.muted);
|
|
473
842
|
|
|
474
|
-
if (
|
|
475
|
-
|
|
476
|
-
const toShow = blockedActions.slice(0, 3);
|
|
477
|
-
for (const b of toShow) {
|
|
478
|
-
const actionType = (b.action_type || "ACTION").toUpperCase().padEnd(5);
|
|
479
|
-
const target = truncate(b.action_path || b.description || "unknown", 28);
|
|
480
|
-
const time = formatRelativeTime(b.created_at);
|
|
481
|
-
console.log(` ${actionType} ${target.padEnd(28)} ${colors.muted}${time}${colors.reset}`);
|
|
482
|
-
}
|
|
483
|
-
console.log();
|
|
843
|
+
if (pendingCount > 5) {
|
|
844
|
+
console.log(` ${colors.muted}... and ${pendingCount - 5} more${colors.reset}`);
|
|
484
845
|
}
|
|
485
846
|
|
|
486
|
-
console.log(
|
|
487
|
-
console.log(` ${colors.info}→
|
|
847
|
+
console.log();
|
|
848
|
+
console.log(` ${colors.info}→ moltctl approvals approve <id>${colors.reset}`);
|
|
849
|
+
console.log(` ${colors.info}→ moltctl approvals list --all${colors.reset}`);
|
|
850
|
+
} else {
|
|
851
|
+
console.log(` ${colors.muted}No high-risk actions waiting for approval${colors.reset}`);
|
|
488
852
|
}
|
|
489
853
|
|
|
490
854
|
// Show warning if approaching limits
|
|
@@ -514,6 +878,12 @@ function truncate(str, len) {
|
|
|
514
878
|
* List recent deployments
|
|
515
879
|
*/
|
|
516
880
|
async function cmdDeployments() {
|
|
881
|
+
// First check payment status
|
|
882
|
+
const tenant = await apiGet("/tenant/me");
|
|
883
|
+
if (checkPaymentRequired(tenant)) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
|
|
517
887
|
const data = await apiGet("/tenant/deployments");
|
|
518
888
|
|
|
519
889
|
printHeader("Recent Deployments");
|
|
@@ -540,8 +910,15 @@ async function cmdDeployments() {
|
|
|
540
910
|
* Show detailed usage vs plan limits
|
|
541
911
|
*/
|
|
542
912
|
async function cmdUsage() {
|
|
543
|
-
|
|
544
|
-
|
|
913
|
+
// Fetch tenant info first to check payment status
|
|
914
|
+
const tenant = await apiGet("/tenant/me");
|
|
915
|
+
|
|
916
|
+
// Check if payment is required
|
|
917
|
+
if (checkPaymentRequired(tenant)) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
const [usage, limits] = await Promise.all([
|
|
545
922
|
apiGet("/tenant/usage").catch(() => ({ runs: 0, events: 0, agents: 0, approvals: 0 })),
|
|
546
923
|
apiGet("/tenant/limits").catch(() => ({ runs: 100, events: 1000, agents: 1, approvals: 0, retention_days: 7 }))
|
|
547
924
|
]);
|
|
@@ -587,12 +964,41 @@ async function cmdUsage() {
|
|
|
587
964
|
*/
|
|
588
965
|
async function cmdUpgrade(targetPlan) {
|
|
589
966
|
const tenant = await apiGet("/tenant/me");
|
|
967
|
+
|
|
968
|
+
// Check if payment is required - but allow showing upgrade options for unpaid users
|
|
969
|
+
const billingStatus = tenant.billingStatus || tenant.billing_status || tenant.status;
|
|
970
|
+
const unpaidStatuses = ['pending', 'incomplete', 'incomplete_expired', 'unpaid'];
|
|
971
|
+
|
|
972
|
+
if (unpaidStatuses.includes(billingStatus?.toLowerCase())) {
|
|
973
|
+
// User hasn't subscribed yet - redirect to checkout
|
|
974
|
+
console.log(ASCII_LOGO_COMPACT);
|
|
975
|
+
console.log();
|
|
976
|
+
print(`${colors.warning}⚡ Subscription Required${colors.reset}`, colors.bold);
|
|
977
|
+
console.log();
|
|
978
|
+
console.log(` You need an active subscription to use Moltengine.`);
|
|
979
|
+
console.log();
|
|
980
|
+
print("Choose a Plan", colors.bold);
|
|
981
|
+
print("─".repeat(40), colors.muted);
|
|
982
|
+
console.log(` ${colors.accent}Starter${colors.reset} ${PLAN_PRICES.starter} — 1 agent, all tools, approvals`);
|
|
983
|
+
console.log(` ${colors.accent}Team${colors.reset} ${PLAN_PRICES.team} — 5 agents, approvals`);
|
|
984
|
+
console.log(` ${colors.accent}Business${colors.reset} ${PLAN_PRICES.business} — 25 agents, SIEM, RBAC`);
|
|
985
|
+
console.log(` ${colors.accent}Enterprise${colors.reset} ${PLAN_PRICES.enterprise} — SSO, VPC, compliance`);
|
|
986
|
+
console.log();
|
|
987
|
+
|
|
988
|
+
const checkoutUrl = targetPlan
|
|
989
|
+
? `${WEB_URL}/checkout?plan=${targetPlan}`
|
|
990
|
+
: `${WEB_URL}/checkout`;
|
|
991
|
+
print(`${colors.info}→ Subscribe now: ${colors.accent}${checkoutUrl}${colors.reset}`, colors.bold);
|
|
992
|
+
console.log();
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
|
|
590
996
|
const currentPlan = tenant.plan || "starter";
|
|
591
997
|
|
|
592
998
|
console.log(ASCII_LOGO_COMPACT);
|
|
593
999
|
|
|
594
1000
|
const plans = [
|
|
595
|
-
{ id: "starter", price: "$49/mo", agents: 1, runs: "100/day", highlight: "
|
|
1001
|
+
{ id: "starter", price: "$49/mo", agents: 1, runs: "100/day", highlight: "All Tools" },
|
|
596
1002
|
{ id: "team", price: "$299/mo", agents: 5, runs: "1,000/day", highlight: "Human-in-the-loop approvals" },
|
|
597
1003
|
{ id: "business", price: "$999/mo", agents: 25, runs: "10,000/day", highlight: "SIEM export, RBAC" },
|
|
598
1004
|
{ id: "enterprise", price: "Custom", agents: 100, runs: "100,000/day", highlight: "SSO, VPC, compliance" }
|
|
@@ -663,31 +1069,18 @@ async function cmdUpgrade(targetPlan) {
|
|
|
663
1069
|
|
|
664
1070
|
/**
|
|
665
1071
|
* Approvals command - list, approve, or deny pending approvals
|
|
1072
|
+
* Available on all plans (required for high-risk actions)
|
|
666
1073
|
*/
|
|
667
1074
|
async function cmdApprovals(subcommand, id) {
|
|
668
1075
|
const tenant = await apiGet("/tenant/me");
|
|
669
|
-
const plan = tenant.plan || "starter";
|
|
670
1076
|
|
|
671
|
-
// Check if
|
|
672
|
-
if (
|
|
673
|
-
console.log(ASCII_LOGO_COMPACT);
|
|
674
|
-
console.log();
|
|
675
|
-
print(`${colors.error}⛔ Approvals require Team plan or higher${colors.reset}`, colors.bold);
|
|
676
|
-
console.log();
|
|
677
|
-
console.log(` Your current plan (Starter) runs in Safe Mode where`);
|
|
678
|
-
console.log(` risky actions are automatically blocked.`);
|
|
679
|
-
console.log();
|
|
680
|
-
console.log(` ${colors.info}Team plan includes:${colors.reset}`);
|
|
681
|
-
console.log(` • Human-in-the-loop approvals`);
|
|
682
|
-
console.log(` • Audit log search & export`);
|
|
683
|
-
console.log(` • Policy editor`);
|
|
684
|
-
console.log(` • Email alerts`);
|
|
685
|
-
console.log();
|
|
686
|
-
console.log(` ${colors.info}→ Upgrade: ${colors.accent}moltctl upgrade team${colors.reset}`);
|
|
687
|
-
console.log();
|
|
1077
|
+
// Check if payment is required
|
|
1078
|
+
if (checkPaymentRequired(tenant)) {
|
|
688
1079
|
return;
|
|
689
1080
|
}
|
|
690
1081
|
|
|
1082
|
+
// Approvals available on all plans
|
|
1083
|
+
|
|
691
1084
|
// Handle subcommands
|
|
692
1085
|
switch (subcommand) {
|
|
693
1086
|
case "list":
|
|
@@ -779,30 +1172,19 @@ async function denyAction(id) {
|
|
|
779
1172
|
}
|
|
780
1173
|
|
|
781
1174
|
/**
|
|
782
|
-
* Audit command - search and export audit logs
|
|
1175
|
+
* Audit command - search and export audit logs
|
|
1176
|
+
* Available on all plans
|
|
783
1177
|
*/
|
|
784
1178
|
async function cmdAudit(subcommand, ...args) {
|
|
785
1179
|
const tenant = await apiGet("/tenant/me");
|
|
786
|
-
const plan = tenant.plan || "starter";
|
|
787
1180
|
|
|
788
|
-
if
|
|
789
|
-
|
|
790
|
-
console.log();
|
|
791
|
-
print(`${colors.error}⛔ Audit search requires Team plan or higher${colors.reset}`, colors.bold);
|
|
792
|
-
console.log();
|
|
793
|
-
console.log(` Your current plan (Starter) stores audit logs but`);
|
|
794
|
-
console.log(` search and export require an upgrade.`);
|
|
795
|
-
console.log();
|
|
796
|
-
console.log(` ${colors.info}Team plan includes:${colors.reset}`);
|
|
797
|
-
console.log(` • Audit log search`);
|
|
798
|
-
console.log(` • CSV/JSON export`);
|
|
799
|
-
console.log(` • 90-day retention (vs 7 days)`);
|
|
800
|
-
console.log();
|
|
801
|
-
console.log(` ${colors.info}→ Upgrade: ${colors.accent}moltctl upgrade team${colors.reset}`);
|
|
802
|
-
console.log();
|
|
1181
|
+
// Check if payment is required
|
|
1182
|
+
if (checkPaymentRequired(tenant)) {
|
|
803
1183
|
return;
|
|
804
1184
|
}
|
|
805
1185
|
|
|
1186
|
+
// Audit available on all plans
|
|
1187
|
+
|
|
806
1188
|
// Handle subcommands
|
|
807
1189
|
switch (subcommand) {
|
|
808
1190
|
case "search":
|
|
@@ -939,30 +1321,19 @@ async function auditStats() {
|
|
|
939
1321
|
}
|
|
940
1322
|
|
|
941
1323
|
/**
|
|
942
|
-
* Policies command - manage security policies
|
|
1324
|
+
* Policies command - manage security policies
|
|
1325
|
+
* Available on all plans
|
|
943
1326
|
*/
|
|
944
1327
|
async function cmdPolicies(subcommand, ...args) {
|
|
945
1328
|
const tenant = await apiGet("/tenant/me");
|
|
946
|
-
const plan = tenant.plan || "starter";
|
|
947
1329
|
|
|
948
|
-
if
|
|
949
|
-
|
|
950
|
-
console.log();
|
|
951
|
-
print(`${colors.error}⛔ Policy editor requires Team plan or higher${colors.reset}`, colors.bold);
|
|
952
|
-
console.log();
|
|
953
|
-
console.log(` Your current plan (Starter) uses default security`);
|
|
954
|
-
console.log(` policies. Custom policies require an upgrade.`);
|
|
955
|
-
console.log();
|
|
956
|
-
console.log(` ${colors.info}Team plan includes:${colors.reset}`);
|
|
957
|
-
console.log(` • Tool allowlist/blocklist`);
|
|
958
|
-
console.log(` • Network domain policies`);
|
|
959
|
-
console.log(` • Filesystem access rules`);
|
|
960
|
-
console.log();
|
|
961
|
-
console.log(` ${colors.info}→ Upgrade: ${colors.accent}moltctl upgrade team${colors.reset}`);
|
|
962
|
-
console.log();
|
|
1330
|
+
// Check if payment is required
|
|
1331
|
+
if (checkPaymentRequired(tenant)) {
|
|
963
1332
|
return;
|
|
964
1333
|
}
|
|
965
1334
|
|
|
1335
|
+
// Policy editor available on all plans
|
|
1336
|
+
|
|
966
1337
|
// Handle subcommands
|
|
967
1338
|
switch (subcommand) {
|
|
968
1339
|
case "list":
|
|
@@ -1008,9 +1379,9 @@ async function policiesList() {
|
|
|
1008
1379
|
console.log(` ${colors.muted}Using secure defaults${colors.reset}`);
|
|
1009
1380
|
console.log();
|
|
1010
1381
|
console.log(` ${colors.info}Default policy:${colors.reset}`);
|
|
1011
|
-
console.log(` •
|
|
1382
|
+
console.log(` • All tools available (high-risk actions require approval)`);
|
|
1012
1383
|
console.log(` • Network requests allowed (except blocked domains)`);
|
|
1013
|
-
console.log(` •
|
|
1384
|
+
console.log(` • Sandboxing enabled by default (user-configurable)`);
|
|
1014
1385
|
console.log();
|
|
1015
1386
|
return;
|
|
1016
1387
|
}
|
|
@@ -1219,6 +1590,12 @@ async function policiesBlockDomain(domain) {
|
|
|
1219
1590
|
*/
|
|
1220
1591
|
async function cmdExport(subcommand, ...args) {
|
|
1221
1592
|
const tenant = await apiGet("/tenant/me");
|
|
1593
|
+
|
|
1594
|
+
// Check if payment is required
|
|
1595
|
+
if (checkPaymentRequired(tenant)) {
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1222
1599
|
const plan = tenant.plan || "starter";
|
|
1223
1600
|
|
|
1224
1601
|
if (plan === "starter" || plan === "team") {
|
|
@@ -1410,6 +1787,12 @@ async function exportHistory(configId) {
|
|
|
1410
1787
|
*/
|
|
1411
1788
|
async function cmdUsers(subcommand, ...args) {
|
|
1412
1789
|
const tenant = await apiGet("/tenant/me");
|
|
1790
|
+
|
|
1791
|
+
// Check if payment is required
|
|
1792
|
+
if (checkPaymentRequired(tenant)) {
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1413
1796
|
const plan = tenant.plan || "starter";
|
|
1414
1797
|
|
|
1415
1798
|
if (plan === "starter" || plan === "team") {
|
|
@@ -1551,6 +1934,12 @@ async function usersRemove(userId) {
|
|
|
1551
1934
|
*/
|
|
1552
1935
|
async function cmdEnv(subcommand, ...args) {
|
|
1553
1936
|
const tenant = await apiGet("/tenant/me");
|
|
1937
|
+
|
|
1938
|
+
// Check if payment is required
|
|
1939
|
+
if (checkPaymentRequired(tenant)) {
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1554
1943
|
const plan = tenant.plan || "starter";
|
|
1555
1944
|
|
|
1556
1945
|
if (plan === "starter" || plan === "team") {
|
|
@@ -1702,6 +2091,15 @@ async function main() {
|
|
|
1702
2091
|
console.log();
|
|
1703
2092
|
|
|
1704
2093
|
switch (command) {
|
|
2094
|
+
case "login":
|
|
2095
|
+
await cmdLogin();
|
|
2096
|
+
break;
|
|
2097
|
+
|
|
2098
|
+
case "checkout":
|
|
2099
|
+
case "subscribe":
|
|
2100
|
+
await cmdCheckout(args[0]);
|
|
2101
|
+
break;
|
|
2102
|
+
|
|
1705
2103
|
case "whoami":
|
|
1706
2104
|
await cmdWhoami();
|
|
1707
2105
|
break;
|