nexus-prime 7.9.15 → 7.9.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -21
- package/dist/agents/adapters/mcp/handlers/runtime.js +14 -0
- package/dist/agents/adapters/mcp/types.js +2 -0
- package/dist/cli/install-wizard.js +12 -4
- package/dist/cli.js +28 -12
- package/dist/dashboard/app/views/license.js +36 -0
- package/dist/engines/client-bootstrap.d.ts +1 -0
- package/dist/engines/client-bootstrap.js +16 -5
- package/dist/engines/mcp-entrypoint.d.ts +1 -1
- package/dist/engines/mcp-entrypoint.js +42 -4
- package/dist/licensing/enforcement.js +22 -5
- package/dist/licensing/index.d.ts +1 -1
- package/dist/licensing/license-manager.js +97 -4
- package/dist/licensing/license-sync.js +4 -3
- package/dist/licensing/types.d.ts +19 -3
- package/dist/licensing/upgrade-prompts.d.ts +2 -2
- package/dist/licensing/upgrade-prompts.js +39 -9
- package/dist/licensing/web-auth.d.ts +2 -2
- package/dist/licensing/web-auth.js +6 -6
- package/dist/postinstall-bootstrap.js +6 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
<p>
|
|
17
17
|
<img src="https://img.shields.io/badge/Protocol-MCP-4285F4?style=for-the-badge" alt="MCP Protocol">
|
|
18
18
|
<img src="https://img.shields.io/badge/license-Commercial-6f42c1?style=for-the-badge" alt="Commercial License">
|
|
19
|
-
<a href="https://nexus-prime.cfd/pricing"><img src="https://img.shields.io/badge/
|
|
20
|
-
<img src="https://img.shields.io/badge/
|
|
19
|
+
<a href="https://nexus-prime.cfd/pricing"><img src="https://img.shields.io/badge/3--day-grace%20then%20license-00ff88?style=for-the-badge" alt="3-day grace then license"></a>
|
|
20
|
+
<img src="https://img.shields.io/badge/licenses-reviewed%20within%2024h-ff69b4?style=for-the-badge" alt="Licenses reviewed within 24 hours">
|
|
21
21
|
<img src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-444?style=for-the-badge" alt="Cross-platform">
|
|
22
22
|
</p>
|
|
23
23
|
|
|
@@ -305,7 +305,7 @@ If any of that sounds like you — keep reading.
|
|
|
305
305
|
|
|
306
306
|
<div align="center">
|
|
307
307
|
|
|
308
|
-
**
|
|
308
|
+
**3-day no-license grace. Request a license before day 4. Manual approvals within 24 hours.**
|
|
309
309
|
|
|
310
310
|
</div>
|
|
311
311
|
|
|
@@ -317,16 +317,16 @@ If any of that sounds like you — keep reading.
|
|
|
317
317
|
<th>Caps</th>
|
|
318
318
|
</tr>
|
|
319
319
|
<tr>
|
|
320
|
-
<td><b>🎁
|
|
321
|
-
<td>Everyone —
|
|
322
|
-
<td>
|
|
323
|
-
<td>
|
|
320
|
+
<td><b>🎁 Local Grace</b></td>
|
|
321
|
+
<td>Everyone — install and verify locally</td>
|
|
322
|
+
<td>3 days without a license</td>
|
|
323
|
+
<td>Runtime available during grace; license required after day 3</td>
|
|
324
324
|
</tr>
|
|
325
325
|
<tr>
|
|
326
326
|
<td><b>💎 Independent Creators</b></td>
|
|
327
327
|
<td>Indie builders, solo engineers, students, OSS maintainers</td>
|
|
328
|
-
<td
|
|
329
|
-
<td>
|
|
328
|
+
<td>Request access</td>
|
|
329
|
+
<td>Manual creator licenses reviewed by the team</td>
|
|
330
330
|
</tr>
|
|
331
331
|
<tr>
|
|
332
332
|
<td><b>🚀 Pilot Program</b></td>
|
|
@@ -356,10 +356,10 @@ If any of that sounds like you — keep reading.
|
|
|
356
356
|
|
|
357
357
|
```bash
|
|
358
358
|
nexus-prime license status # check your plan
|
|
359
|
-
nexus-prime license activate <key> # activate
|
|
359
|
+
nexus-prime license activate <key> # activate the key shared by the team
|
|
360
360
|
```
|
|
361
361
|
|
|
362
|
-
→ **[nexus-prime.cfd/
|
|
362
|
+
→ **[nexus-prime.cfd/account](https://nexus-prime.cfd/account)** · **[Request by email](mailto:adarsh@nexus-prime.cfd?subject=Nexus%20Prime%20license%20request)**
|
|
363
363
|
|
|
364
364
|
---
|
|
365
365
|
|
|
@@ -380,19 +380,19 @@ No — it supercharges them. Keep the agents you already love. Nexus Prime adds
|
|
|
380
380
|
<details>
|
|
381
381
|
<summary><b>What happens after the 30-day trial ends?</b></summary>
|
|
382
382
|
<br>
|
|
383
|
-
|
|
383
|
+
The local trial window ends and paid-tier work requires an active license. Your local memory and data stay on your machine; activate a license or request renewal from the account page to continue.
|
|
384
384
|
</details>
|
|
385
385
|
|
|
386
386
|
<details>
|
|
387
387
|
<summary><b>Is it free for indie developers and creators?</b></summary>
|
|
388
388
|
<br>
|
|
389
|
-
|
|
389
|
+
Creator access is reviewed manually. If you build independently — solo founder, indie dev, content creator, student, OSS maintainer — request a creator license from the account page or email <a href="mailto:adarsh@nexus-prime.cfd?subject=Creator%20Program">adarsh@nexus-prime.cfd</a>.
|
|
390
390
|
</details>
|
|
391
391
|
|
|
392
392
|
<details>
|
|
393
393
|
<summary><b>My team wants to try this across the whole engineering org. What's the pilot?</b></summary>
|
|
394
394
|
<br>
|
|
395
|
-
We're taking <b>10 companies</b> into our pilot program —
|
|
395
|
+
We're taking <b>10 companies</b> into our pilot program — white-glove onboarding, direct access to the team building it, and team licenses issued after review. <a href="mailto:adarsh@nexus-prime.cfd?subject=Pilot%20Program">Reach out</a>.
|
|
396
396
|
</details>
|
|
397
397
|
|
|
398
398
|
<details>
|
|
@@ -416,7 +416,7 @@ Each engineer runs their own local instance — nothing is shared across machine
|
|
|
416
416
|
<details>
|
|
417
417
|
<summary><b>What if I hit my plan's cap?</b></summary>
|
|
418
418
|
<br>
|
|
419
|
-
|
|
419
|
+
The app shows an upgrade request path and keeps your local data intact. Features above your licensed tier require an approved upgrade.
|
|
420
420
|
</details>
|
|
421
421
|
|
|
422
422
|
<details>
|
|
@@ -433,7 +433,7 @@ Nexus Prime was designed privacy-first, because the code on your machine is your
|
|
|
433
433
|
|
|
434
434
|
- 🏠 **100% local.** All data lives on your disk, in your home directory.
|
|
435
435
|
- 🚫 **No cloud sync.** Your code, your memory, your logs — never uploaded.
|
|
436
|
-
- 🔐 **
|
|
436
|
+
- 🔐 **Short no-account grace.** You can run locally for 3 days without signing up; a license is required after that grace period.
|
|
437
437
|
- 📊 **Telemetry is opt-in.** Off by default. When on, only aggregate, anonymous event counts are sent — never source code, never memory contents.
|
|
438
438
|
- 🗑️ **Uninstall is clean.** Everything lives in one directory you can delete.
|
|
439
439
|
|
|
@@ -448,7 +448,8 @@ Nexus Prime was designed privacy-first, because the code on your machine is your
|
|
|
448
448
|
| 💬 [**Discord**](https://discord.gg/tByGZgk5gS) | Real-time help, show-and-tell, feature ideas |
|
|
449
449
|
| 🔴 [**Reddit — r/Nexus_Prime**](https://www.reddit.com/r/Nexus_Prime/) | Long-form posts, releases, community wins |
|
|
450
450
|
| 🐦 [**X / Twitter**](https://x.com/nexusprime_ai) | Launch announcements, tips, updates |
|
|
451
|
-
| 📧 [**
|
|
451
|
+
| 📧 [**adarsh@nexus-prime.cfd**](mailto:adarsh@nexus-prime.cfd) | License requests, upgrades, pilots, enterprise |
|
|
452
|
+
| 📧 [**hello@nexus-prime.cfd**](mailto:hello@nexus-prime.cfd) | General support, press, community |
|
|
452
453
|
| 🌐 [**nexus-prime.cfd**](https://nexus-prime.cfd) | Product site, demos, pricing, setup guides |
|
|
453
454
|
|
|
454
455
|
</div>
|
|
@@ -457,11 +458,11 @@ Nexus Prime was designed privacy-first, because the code on your machine is your
|
|
|
457
458
|
|
|
458
459
|
## License
|
|
459
460
|
|
|
460
|
-
Nexus Prime is a **commercial product** with
|
|
461
|
+
Nexus Prime is a **commercial product** with manual license issuance:
|
|
461
462
|
|
|
462
|
-
- ✅
|
|
463
|
-
- ✅
|
|
464
|
-
- ✅
|
|
463
|
+
- ✅ 3-day local no-license grace, for everyone
|
|
464
|
+
- ✅ Manual creator, pilot, team, and enterprise license review
|
|
465
|
+
- ✅ License and upgrade requests reviewed within 24 hours
|
|
465
466
|
- 💳 Paid plans for professional, team, and enterprise usage
|
|
466
467
|
|
|
467
468
|
Commercial use without a valid license is prohibited. See [nexus-prime.cfd/pricing](https://nexus-prime.cfd/pricing) for current plans and terms.
|
|
@@ -38,11 +38,23 @@ export async function handleRuntimeGroup(toolName, hctx, request, args, ctx) {
|
|
|
38
38
|
}
|
|
39
39
|
// summary (default)
|
|
40
40
|
const licStatus = getSharedLicenseManager().getStatus();
|
|
41
|
+
const trialLines = licStatus.trialPhase ? [
|
|
42
|
+
`- **Trial phase**: ${licStatus.trialPhase}`,
|
|
43
|
+
typeof licStatus.trialDaysRemaining === 'number'
|
|
44
|
+
? `- **Trial remaining**: ${licStatus.trialDaysRemaining} day${licStatus.trialDaysRemaining === 1 ? '' : 's'}`
|
|
45
|
+
: '',
|
|
46
|
+
licStatus.activationRequired
|
|
47
|
+
? `- **Agent motion**: License activation is required for paid-tier work; keep license/status tools available.`
|
|
48
|
+
: licStatus.trialPhase === 'activation'
|
|
49
|
+
? `- **Agent motion**: Trial remains active; ask the user to request or activate a license soon, then keep working.`
|
|
50
|
+
: `- **Agent motion**: No license is mandatory during the first 3 days; ask the user to request a license soon.`,
|
|
51
|
+
].filter((line) => Boolean(line)) : [];
|
|
41
52
|
const lines = [
|
|
42
53
|
`## License Status`,
|
|
43
54
|
`- **Plan**: ${pcu.tier.toUpperCase()}`,
|
|
44
55
|
`- **Status**: ${licStatus.valid ? 'Active' : licStatus.degradedReason ?? 'Inactive'}`,
|
|
45
56
|
licStatus.expiresAt ? `- **Expires**: ${new Date(licStatus.expiresAt).toISOString().split('T')[0]}` : '- **Expires**: Never',
|
|
57
|
+
...trialLines,
|
|
46
58
|
'',
|
|
47
59
|
`## Resource Usage`,
|
|
48
60
|
`- Memory: ${pcu.memory.current.toLocaleString()} / ${Number.isFinite(pcu.memory.limit) ? pcu.memory.limit.toLocaleString() : '∞'} (${pcu.memory.pct}%)`,
|
|
@@ -54,6 +66,8 @@ export async function handleRuntimeGroup(toolName, hctx, request, args, ctx) {
|
|
|
54
66
|
`## Upgrade`,
|
|
55
67
|
`- Dashboard: http://127.0.0.1:3377 → License tab`,
|
|
56
68
|
`- Web: https://nexus-prime.cfd/account`,
|
|
69
|
+
`- Email: adarsh@nexus-prime.cfd`,
|
|
70
|
+
`- SLA: team will share licenses within 24 hours`,
|
|
57
71
|
`- CLI: nexus-prime license sync`,
|
|
58
72
|
];
|
|
59
73
|
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
@@ -154,6 +154,8 @@ export class SessionTelemetry {
|
|
|
154
154
|
needsOptimizeTokens(currentToolName) {
|
|
155
155
|
if (currentToolName === 'nexus_optimize_tokens')
|
|
156
156
|
return false;
|
|
157
|
+
if (currentToolName === 'nexus_mindkit_check')
|
|
158
|
+
return false;
|
|
157
159
|
if (this.lifecyclePhase === 'pre-bootstrap')
|
|
158
160
|
return false;
|
|
159
161
|
if (this.tokenAutoApplied)
|
|
@@ -39,6 +39,13 @@ const SETUP_CLIENT_BY_IDE = {
|
|
|
39
39
|
cline: 'cline',
|
|
40
40
|
codex: 'codex',
|
|
41
41
|
};
|
|
42
|
+
function resolveSetupWorkspaceRoot(explicitWorkspaceRoot) {
|
|
43
|
+
return resolveWorkspaceContext({
|
|
44
|
+
workspaceRoot: explicitWorkspaceRoot,
|
|
45
|
+
cwd: process.cwd(),
|
|
46
|
+
env: process.env,
|
|
47
|
+
}).workspaceRoot;
|
|
48
|
+
}
|
|
42
49
|
function isSetupMarker(value) {
|
|
43
50
|
if (!value || typeof value !== 'object') {
|
|
44
51
|
return false;
|
|
@@ -89,7 +96,7 @@ export function writeSetupMarker(marker, markerPath = SETUP_MARKER_PATH) {
|
|
|
89
96
|
return nextMarker;
|
|
90
97
|
}
|
|
91
98
|
export async function configureIDE(ide, opts = {}) {
|
|
92
|
-
const workspaceRoot =
|
|
99
|
+
const workspaceRoot = resolveSetupWorkspaceRoot(opts.workspaceRoot);
|
|
93
100
|
const mappedClient = SETUP_CLIENT_BY_IDE[ide];
|
|
94
101
|
if (mappedClient) {
|
|
95
102
|
const definition = getSetupDefinition(mappedClient, {
|
|
@@ -285,7 +292,7 @@ export function runArchitectureUpgrade(opts = {}) {
|
|
|
285
292
|
}
|
|
286
293
|
/** Run the install wizard: detect IDEs and write MCP configs. */
|
|
287
294
|
export async function runInstallWizard(opts = {}) {
|
|
288
|
-
const workspaceRoot =
|
|
295
|
+
const workspaceRoot = resolveSetupWorkspaceRoot(opts.workspaceRoot);
|
|
289
296
|
const verbose = opts.verbose ?? true;
|
|
290
297
|
const dryRun = opts.dryRun ?? false;
|
|
291
298
|
const setupMarker = readSetupMarker();
|
|
@@ -414,6 +421,7 @@ export async function cliSetup(opts = []) {
|
|
|
414
421
|
const port = process.env.NEXUS_DASHBOARD_PORT ?? '3377';
|
|
415
422
|
const dashUrl = `http://localhost:${port}`;
|
|
416
423
|
const isNew = options.isNewUser ?? false;
|
|
424
|
+
const workspaceRoot = resolveSetupWorkspaceRoot();
|
|
417
425
|
const ANSI = { cyan: '\x1b[36m', green: '\x1b[32m', yellow: '\x1b[33m', dim: '\x1b[2m', reset: '\x1b[0m' };
|
|
418
426
|
const isTTY = process.stdout.isTTY === true && !process.env.NO_COLOR;
|
|
419
427
|
const c = (s, k) => isTTY ? `${ANSI[k]}${s}${ANSI.reset}` : s;
|
|
@@ -438,7 +446,7 @@ export async function cliSetup(opts = []) {
|
|
|
438
446
|
}
|
|
439
447
|
catch { /* non-fatal */ }
|
|
440
448
|
};
|
|
441
|
-
const result = await runInstallWizard({ dryRun, verbose: false });
|
|
449
|
+
const result = await runInstallWizard({ workspaceRoot, dryRun, verbose: false });
|
|
442
450
|
const allIDEs = [...result.configured, ...result.skipped, ...result.errors.map(e => e.ide)];
|
|
443
451
|
if (allIDEs.length > 0) {
|
|
444
452
|
for (const ide of allIDEs) {
|
|
@@ -499,7 +507,7 @@ export async function cliSetup(opts = []) {
|
|
|
499
507
|
let daemonReady = false;
|
|
500
508
|
if (!dryRun) {
|
|
501
509
|
try {
|
|
502
|
-
const workspace = resolveWorkspaceContext({ workspaceRoot: process.cwd() });
|
|
510
|
+
const workspace = resolveWorkspaceContext({ workspaceRoot, cwd: process.cwd(), env: process.env });
|
|
503
511
|
await withSpinner('Connecting to daemon', ensureDaemonReady(workspace, { timeoutMs: DEFAULT_DAEMON_READY_TIMEOUT_MS, entrypoint: process.argv[1] }));
|
|
504
512
|
daemonReady = true;
|
|
505
513
|
log(` ${c('✓', 'green')} Dashboard: ${c(dashUrl, 'cyan')}`);
|
package/dist/cli.js
CHANGED
|
@@ -454,11 +454,13 @@ function resolveClaudeDesktopConfigPath() {
|
|
|
454
454
|
return join(homedir(), '.claude', 'claude_desktop_config.json');
|
|
455
455
|
}
|
|
456
456
|
function getSetupDefinition(clientId) {
|
|
457
|
+
const workspaceRoot = getWorkspaceRoot();
|
|
457
458
|
const instructionFiles = buildInstructionFiles(clientId);
|
|
458
459
|
if (clientId === 'codex') {
|
|
459
460
|
return {
|
|
460
461
|
id: clientId,
|
|
461
462
|
label: 'Codex',
|
|
463
|
+
workspaceRoot,
|
|
462
464
|
configPath: resolveCodexConfigPath(),
|
|
463
465
|
instructionFiles,
|
|
464
466
|
};
|
|
@@ -467,6 +469,7 @@ function getSetupDefinition(clientId) {
|
|
|
467
469
|
return {
|
|
468
470
|
id: clientId,
|
|
469
471
|
label: 'Cursor',
|
|
472
|
+
workspaceRoot,
|
|
470
473
|
configPath: join(homedir(), '.cursor', 'mcp.json'),
|
|
471
474
|
instructionFiles,
|
|
472
475
|
};
|
|
@@ -475,7 +478,8 @@ function getSetupDefinition(clientId) {
|
|
|
475
478
|
return {
|
|
476
479
|
id: clientId,
|
|
477
480
|
label: 'Claude Code',
|
|
478
|
-
|
|
481
|
+
workspaceRoot,
|
|
482
|
+
configPath: join(workspaceRoot, '.mcp.json'),
|
|
479
483
|
instructionFiles,
|
|
480
484
|
};
|
|
481
485
|
}
|
|
@@ -483,6 +487,7 @@ function getSetupDefinition(clientId) {
|
|
|
483
487
|
return {
|
|
484
488
|
id: clientId,
|
|
485
489
|
label: 'Claude Desktop',
|
|
490
|
+
workspaceRoot,
|
|
486
491
|
configPath: resolveClaudeDesktopConfigPath(),
|
|
487
492
|
instructionFiles,
|
|
488
493
|
};
|
|
@@ -491,6 +496,7 @@ function getSetupDefinition(clientId) {
|
|
|
491
496
|
return {
|
|
492
497
|
id: clientId,
|
|
493
498
|
label: 'Opencode',
|
|
499
|
+
workspaceRoot,
|
|
494
500
|
configPath: join(homedir(), '.config', 'opencode', 'opencode.json'),
|
|
495
501
|
instructionFiles,
|
|
496
502
|
};
|
|
@@ -499,6 +505,7 @@ function getSetupDefinition(clientId) {
|
|
|
499
505
|
return {
|
|
500
506
|
id: clientId,
|
|
501
507
|
label: 'Windsurf',
|
|
508
|
+
workspaceRoot,
|
|
502
509
|
configPath: join(homedir(), '.windsurf', 'mcp.json'),
|
|
503
510
|
instructionFiles,
|
|
504
511
|
};
|
|
@@ -507,6 +514,7 @@ function getSetupDefinition(clientId) {
|
|
|
507
514
|
return {
|
|
508
515
|
id: clientId,
|
|
509
516
|
label: 'Antigravity / OpenClaw',
|
|
517
|
+
workspaceRoot,
|
|
510
518
|
configPath: join(homedir(), '.antigravity', 'mcp.json'),
|
|
511
519
|
instructionFiles,
|
|
512
520
|
};
|
|
@@ -515,6 +523,7 @@ function getSetupDefinition(clientId) {
|
|
|
515
523
|
return {
|
|
516
524
|
id: clientId,
|
|
517
525
|
label: 'Aider',
|
|
526
|
+
workspaceRoot,
|
|
518
527
|
configPath: join(homedir(), '.aider', 'mcp.json'),
|
|
519
528
|
instructionFiles,
|
|
520
529
|
};
|
|
@@ -523,6 +532,7 @@ function getSetupDefinition(clientId) {
|
|
|
523
532
|
return {
|
|
524
533
|
id: clientId,
|
|
525
534
|
label: 'Continue.dev',
|
|
535
|
+
workspaceRoot,
|
|
526
536
|
configPath: join(homedir(), '.continue', 'config.json'),
|
|
527
537
|
instructionFiles,
|
|
528
538
|
};
|
|
@@ -531,6 +541,7 @@ function getSetupDefinition(clientId) {
|
|
|
531
541
|
return {
|
|
532
542
|
id: clientId,
|
|
533
543
|
label: 'OpenClaw',
|
|
544
|
+
workspaceRoot,
|
|
534
545
|
configPath: join(homedir(), '.openclaw', 'openclaw.json'),
|
|
535
546
|
instructionFiles,
|
|
536
547
|
};
|
|
@@ -538,6 +549,7 @@ function getSetupDefinition(clientId) {
|
|
|
538
549
|
return {
|
|
539
550
|
id: clientId,
|
|
540
551
|
label: 'Cline',
|
|
552
|
+
workspaceRoot,
|
|
541
553
|
configPath: join(homedir(), '.vscode', 'cline-mcp.json'),
|
|
542
554
|
instructionFiles,
|
|
543
555
|
};
|
|
@@ -548,10 +560,10 @@ function installSetup(definition) {
|
|
|
548
560
|
writeCodexMcpConfig(definition.configPath);
|
|
549
561
|
}
|
|
550
562
|
else if (definition.id === 'opencode') {
|
|
551
|
-
writeOpencodeConfig(definition.configPath,
|
|
563
|
+
writeOpencodeConfig(definition.configPath, definition.workspaceRoot);
|
|
552
564
|
}
|
|
553
565
|
else {
|
|
554
|
-
writeStandardMcpConfig(definition.configPath,
|
|
566
|
+
writeStandardMcpConfig(definition.configPath, definition.workspaceRoot);
|
|
555
567
|
}
|
|
556
568
|
}
|
|
557
569
|
for (const file of definition.instructionFiles) {
|
|
@@ -585,12 +597,12 @@ function printSetupPreview(definition) {
|
|
|
585
597
|
console.log(JSON.stringify(definition.id === 'opencode'
|
|
586
598
|
? {
|
|
587
599
|
mcp: {
|
|
588
|
-
servers: [{ id: 'nexus-prime', ...buildStandardMcpServerConfig(
|
|
600
|
+
servers: [{ id: 'nexus-prime', ...buildStandardMcpServerConfig(definition.workspaceRoot) }]
|
|
589
601
|
}
|
|
590
602
|
}
|
|
591
603
|
: {
|
|
592
604
|
mcpServers: {
|
|
593
|
-
'nexus-prime': buildStandardMcpServerConfig(
|
|
605
|
+
'nexus-prime': buildStandardMcpServerConfig(definition.workspaceRoot)
|
|
594
606
|
}
|
|
595
607
|
}, null, 2));
|
|
596
608
|
}
|
|
@@ -612,10 +624,10 @@ function hasExpectedConfig(definition) {
|
|
|
612
624
|
return false;
|
|
613
625
|
if (definition.id === 'opencode') {
|
|
614
626
|
const server = parsed?.mcp?.['nexus-prime'];
|
|
615
|
-
return Boolean(server && isStableNexusMcpServerConfig(server, 'environment'));
|
|
627
|
+
return Boolean(server && isStableNexusMcpServerConfig(server, 'environment', definition.workspaceRoot));
|
|
616
628
|
}
|
|
617
629
|
const server = parsed?.mcpServers?.['nexus-prime'];
|
|
618
|
-
return Boolean(server && isStableNexusMcpServerConfig(server));
|
|
630
|
+
return Boolean(server && isStableNexusMcpServerConfig(server, 'env', definition.workspaceRoot));
|
|
619
631
|
}
|
|
620
632
|
catch {
|
|
621
633
|
return false;
|
|
@@ -1751,7 +1763,7 @@ program
|
|
|
1751
1763
|
.description('Manage your Nexus Prime license')
|
|
1752
1764
|
.addCommand(new Command('activate')
|
|
1753
1765
|
.description('Activate a license key')
|
|
1754
|
-
.argument('<key>', 'License key from
|
|
1766
|
+
.argument('<key>', 'License key from the Nexus account page or license team')
|
|
1755
1767
|
.action((key) => {
|
|
1756
1768
|
const mgr = getSharedLicenseManager();
|
|
1757
1769
|
const status = mgr.activate(key);
|
|
@@ -1764,7 +1776,8 @@ program
|
|
|
1764
1776
|
}
|
|
1765
1777
|
else {
|
|
1766
1778
|
console.error(`\u2717 License invalid: ${status.degradedReason ?? 'unknown'}`);
|
|
1767
|
-
console.error('
|
|
1779
|
+
console.error(' Request a valid key at https://nexus-prime.cfd/account');
|
|
1780
|
+
console.error(' Or email adarsh@nexus-prime.cfd — licenses are shared within 24 hours.');
|
|
1768
1781
|
process.exit(1);
|
|
1769
1782
|
}
|
|
1770
1783
|
}))
|
|
@@ -1792,7 +1805,8 @@ program
|
|
|
1792
1805
|
.action(() => {
|
|
1793
1806
|
getSharedLicenseManager().deactivate();
|
|
1794
1807
|
console.log('\u2713 License removed. Reverted to Free plan.');
|
|
1795
|
-
console.log('
|
|
1808
|
+
console.log(' Request a new key at https://nexus-prime.cfd/account');
|
|
1809
|
+
console.log(' Or email adarsh@nexus-prime.cfd — licenses are shared within 24 hours.');
|
|
1796
1810
|
}))
|
|
1797
1811
|
.addCommand(new Command('login')
|
|
1798
1812
|
.description('Login to nexus-prime.cfd for license sync')
|
|
@@ -1821,7 +1835,8 @@ program
|
|
|
1821
1835
|
}
|
|
1822
1836
|
catch {
|
|
1823
1837
|
console.log(' License sync skipped (no license found yet).');
|
|
1824
|
-
console.log('
|
|
1838
|
+
console.log(' Request a license at https://nexus-prime.cfd/account');
|
|
1839
|
+
console.log(' Or email adarsh@nexus-prime.cfd — licenses are shared within 24 hours.');
|
|
1825
1840
|
}
|
|
1826
1841
|
}
|
|
1827
1842
|
catch (err) {
|
|
@@ -1875,12 +1890,13 @@ program
|
|
|
1875
1890
|
try {
|
|
1876
1891
|
const result = await requestUpgrade(plan);
|
|
1877
1892
|
console.log(`\u2713 Upgrade request submitted (${result.status})`);
|
|
1878
|
-
console.log('
|
|
1893
|
+
console.log(' The Nexus Prime team will review it and share licenses within 24 hours.');
|
|
1879
1894
|
console.log(' Once approved, run: nexus-prime license sync');
|
|
1880
1895
|
}
|
|
1881
1896
|
catch (err) {
|
|
1882
1897
|
console.error(`\u2717 Request failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1883
1898
|
console.log(' Visit https://nexus-prime.cfd/account to request an upgrade.');
|
|
1899
|
+
console.log(' Or email adarsh@nexus-prime.cfd with your account email and target plan.');
|
|
1884
1900
|
process.exit(1);
|
|
1885
1901
|
}
|
|
1886
1902
|
}));
|
|
@@ -158,12 +158,41 @@ function _renewalNagBanner(status) {
|
|
|
158
158
|
</div>`;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
function _trialActivationBanner(status) {
|
|
162
|
+
if (!status?.trialPhase) return '';
|
|
163
|
+
const motion = status.activationMotion ?? {};
|
|
164
|
+
const required = status.activationRequired === true || status.trialPhase === 'activation' || status.trialPhase === 'expired';
|
|
165
|
+
const days = typeof status.trialDaysRemaining === 'number' ? status.trialDaysRemaining : null;
|
|
166
|
+
const color = status.trialPhase === 'expired' ? 'var(--bad)' : required ? 'var(--warn)' : 'var(--accent)';
|
|
167
|
+
const bg = status.trialPhase === 'expired'
|
|
168
|
+
? 'oklch(65% 0.22 25 / 8%)'
|
|
169
|
+
: required ? 'oklch(80% 0.16 80 / 8%)' : 'oklch(70% 0.16 250 / 8%)';
|
|
170
|
+
const title = motion.title ?? (required ? 'License required' : '3-day no-license grace');
|
|
171
|
+
const message = motion.message ?? (required
|
|
172
|
+
? 'The no-license grace period is over. Request a license from the account page or email adarsh@nexus-prime.cfd; the team will share licenses within 24 hours.'
|
|
173
|
+
: 'First 3 days run without a license. Request a license before day 4; the team will share licenses within 24 hours.');
|
|
174
|
+
const remaining = days == null ? '' : `<div style="font-size:11px;color:var(--text-muted);margin-top:4px">${days} day${days === 1 ? '' : 's'} remaining</div>`;
|
|
175
|
+
return `
|
|
176
|
+
<div style="background:${bg};border:1px solid ${color};border-radius:8px;padding:12px 16px;margin-bottom:16px;display:flex;align-items:center;gap:12px;flex-wrap:wrap">
|
|
177
|
+
<div style="flex:1;min-width:220px">
|
|
178
|
+
<div style="font:600 13px ui-sans-serif;color:${color};margin-bottom:3px">${esc(title)}</div>
|
|
179
|
+
<div style="font-size:12px;color:var(--text-muted);line-height:1.45">${esc(message)}</div>
|
|
180
|
+
${remaining}
|
|
181
|
+
</div>
|
|
182
|
+
<button class="btn btn-primary btn-sm" id="lic-trial-account-btn" style="flex-shrink:0;white-space:nowrap">Request license</button>
|
|
183
|
+
<button class="btn btn-sm" id="lic-trial-activate-btn" style="flex-shrink:0;white-space:nowrap">Activate key</button>
|
|
184
|
+
<button class="btn btn-sm" id="lic-trial-email-btn" style="flex-shrink:0;white-space:nowrap">Email team</button>
|
|
185
|
+
</div>`;
|
|
186
|
+
}
|
|
187
|
+
|
|
161
188
|
function _renderStep1(tier, status, pcu, formatted) {
|
|
162
189
|
const plan = PLANS.find(p => p.id === tier) ?? PLANS[0];
|
|
163
190
|
const valid = status.valid !== false;
|
|
164
191
|
|
|
165
192
|
return `
|
|
166
193
|
<div class="license-card card">
|
|
194
|
+
${_trialActivationBanner(status)}
|
|
195
|
+
|
|
167
196
|
<!-- Renewal nag (shown when expiry < 14 days) -->
|
|
168
197
|
${_renewalNagBanner(status)}
|
|
169
198
|
|
|
@@ -300,6 +329,13 @@ function _attachHandlers() {
|
|
|
300
329
|
$('lic-upgrade-btn')?.addEventListener('click', () => { _step = 2; render(); });
|
|
301
330
|
$('lic-sync-btn')?.addEventListener('click', _doSync);
|
|
302
331
|
$('lic-deactivate-btn')?.addEventListener('click', _doDeactivate);
|
|
332
|
+
$('lic-trial-activate-btn')?.addEventListener('click', () => { _step = 3; render(); });
|
|
333
|
+
$('lic-trial-account-btn')?.addEventListener('click', () => {
|
|
334
|
+
window.open('https://nexus-prime.cfd/account', '_blank', 'noopener');
|
|
335
|
+
});
|
|
336
|
+
$('lic-trial-email-btn')?.addEventListener('click', () => {
|
|
337
|
+
window.open('mailto:adarsh@nexus-prime.cfd?subject=Nexus%20Prime%20license%20request', '_blank', 'noopener');
|
|
338
|
+
});
|
|
303
339
|
$('lic-renew-nag-btn')?.addEventListener('click', () => {
|
|
304
340
|
window.open('https://nexus-prime.cfd/license', '_blank', 'noopener');
|
|
305
341
|
});
|
|
@@ -309,8 +309,8 @@ function codexTomlHasAutonomousProfile(doc) {
|
|
|
309
309
|
return false;
|
|
310
310
|
if (codexTomlExposesInternalGraphPeer(doc))
|
|
311
311
|
return false;
|
|
312
|
-
const commandOk = !/(?:^|\n)\s*command\s*=\s*"npx"\s*(?:\n|$)/.test(mcpSection);
|
|
313
|
-
const argsOk = /(?:^|\n)\s*args\s*=\s*\[[^\]]*"mcp"[^\]]*\]\s*(?:\n|$)/s.test(mcpSection);
|
|
312
|
+
const commandOk = !/(?:^|\n)\s*command\s*=\s*"(?:npx|nexus-prime)"\s*(?:\n|$)/.test(mcpSection);
|
|
313
|
+
const argsOk = /(?:^|\n)\s*args\s*=\s*\[[^\]]*["'][^"']*cli\.js["'][^\]]*"mcp"[^\]]*\]\s*(?:\n|$)/s.test(mcpSection);
|
|
314
314
|
const envOk = Boolean(envSection && /(?:^|\n)\s*NEXUS_MCP_TOOL_PROFILE\s*=\s*"autonomous"\s*(?:\n|$)/.test(envSection));
|
|
315
315
|
const noStartupRewrite = Boolean(envSection && /(?:^|\n)\s*NEXUS_MCP_AUTO_CONFIG\s*=\s*"0"\s*(?:\n|$)/.test(envSection));
|
|
316
316
|
const fullDnaOnDisconnect = Boolean(envSection && /(?:^|\n)\s*NEXUS_MCP_FULL_DNA_ON_DISCONNECT\s*=\s*"1"\s*(?:\n|$)/.test(envSection));
|
|
@@ -548,6 +548,7 @@ export function getSetupDefinition(clientId, options) {
|
|
|
548
548
|
return {
|
|
549
549
|
id: clientId,
|
|
550
550
|
label: 'Codex',
|
|
551
|
+
workspaceRoot,
|
|
551
552
|
configPath: resolveCodexConfigPath(),
|
|
552
553
|
instructionFiles,
|
|
553
554
|
};
|
|
@@ -557,6 +558,7 @@ export function getSetupDefinition(clientId, options) {
|
|
|
557
558
|
return {
|
|
558
559
|
id: clientId,
|
|
559
560
|
label: 'Cursor',
|
|
561
|
+
workspaceRoot,
|
|
560
562
|
configPath: join(workspacePath, '.cursor', 'mcp.json'),
|
|
561
563
|
instructionFiles,
|
|
562
564
|
};
|
|
@@ -565,6 +567,7 @@ export function getSetupDefinition(clientId, options) {
|
|
|
565
567
|
return {
|
|
566
568
|
id: clientId,
|
|
567
569
|
label: 'Claude Code',
|
|
570
|
+
workspaceRoot,
|
|
568
571
|
configPath: join(workspaceRoot, '.mcp.json'),
|
|
569
572
|
instructionFiles,
|
|
570
573
|
};
|
|
@@ -578,6 +581,7 @@ export function getSetupDefinition(clientId, options) {
|
|
|
578
581
|
return {
|
|
579
582
|
id: clientId,
|
|
580
583
|
label: 'Claude Desktop',
|
|
584
|
+
workspaceRoot,
|
|
581
585
|
configPath: claudeDesktopPath,
|
|
582
586
|
instructionFiles,
|
|
583
587
|
};
|
|
@@ -586,6 +590,7 @@ export function getSetupDefinition(clientId, options) {
|
|
|
586
590
|
return {
|
|
587
591
|
id: clientId,
|
|
588
592
|
label: 'Opencode',
|
|
593
|
+
workspaceRoot,
|
|
589
594
|
configPath: join(homedir(), '.config', 'opencode', 'opencode.json'),
|
|
590
595
|
instructionFiles,
|
|
591
596
|
};
|
|
@@ -594,6 +599,7 @@ export function getSetupDefinition(clientId, options) {
|
|
|
594
599
|
return {
|
|
595
600
|
id: clientId,
|
|
596
601
|
label: 'Windsurf',
|
|
602
|
+
workspaceRoot,
|
|
597
603
|
configPath: join(homedir(), '.windsurf', 'mcp.json'),
|
|
598
604
|
instructionFiles,
|
|
599
605
|
};
|
|
@@ -602,6 +608,7 @@ export function getSetupDefinition(clientId, options) {
|
|
|
602
608
|
return {
|
|
603
609
|
id: clientId,
|
|
604
610
|
label: 'Aider',
|
|
611
|
+
workspaceRoot,
|
|
605
612
|
configPath: join(homedir(), '.aider', 'mcp.json'),
|
|
606
613
|
instructionFiles,
|
|
607
614
|
};
|
|
@@ -610,6 +617,7 @@ export function getSetupDefinition(clientId, options) {
|
|
|
610
617
|
return {
|
|
611
618
|
id: clientId,
|
|
612
619
|
label: 'Continue.dev',
|
|
620
|
+
workspaceRoot,
|
|
613
621
|
configPath: join(homedir(), '.continue', 'config.json'),
|
|
614
622
|
instructionFiles,
|
|
615
623
|
};
|
|
@@ -618,6 +626,7 @@ export function getSetupDefinition(clientId, options) {
|
|
|
618
626
|
return {
|
|
619
627
|
id: clientId,
|
|
620
628
|
label: 'Cline',
|
|
629
|
+
workspaceRoot,
|
|
621
630
|
configPath: join(homedir(), '.vscode', 'cline-mcp.json'),
|
|
622
631
|
instructionFiles,
|
|
623
632
|
};
|
|
@@ -626,6 +635,7 @@ export function getSetupDefinition(clientId, options) {
|
|
|
626
635
|
return {
|
|
627
636
|
id: clientId,
|
|
628
637
|
label: 'OpenClaw',
|
|
638
|
+
workspaceRoot,
|
|
629
639
|
configPath: join(homedir(), '.openclaw', 'openclaw.json'),
|
|
630
640
|
instructionFiles,
|
|
631
641
|
};
|
|
@@ -633,6 +643,7 @@ export function getSetupDefinition(clientId, options) {
|
|
|
633
643
|
return {
|
|
634
644
|
id: clientId,
|
|
635
645
|
label: 'Antigravity',
|
|
646
|
+
workspaceRoot,
|
|
636
647
|
configPath: join(homedir(), '.antigravity', 'mcp.json'),
|
|
637
648
|
instructionFiles,
|
|
638
649
|
};
|
|
@@ -697,11 +708,11 @@ export function hasExpectedConfig(definition) {
|
|
|
697
708
|
if (definition.id === 'opencode') {
|
|
698
709
|
const server = parsed?.mcp?.['nexus-prime'];
|
|
699
710
|
return Boolean(server
|
|
700
|
-
&& isStableNexusMcpServerConfig(server, 'environment'));
|
|
711
|
+
&& isStableNexusMcpServerConfig(server, 'environment', definition.workspaceRoot));
|
|
701
712
|
}
|
|
702
713
|
const server = parsed?.mcpServers?.['nexus-prime'];
|
|
703
714
|
return Boolean(server
|
|
704
|
-
&& isStableNexusMcpServerConfig(server));
|
|
715
|
+
&& isStableNexusMcpServerConfig(server, 'env', definition.workspaceRoot));
|
|
705
716
|
}
|
|
706
717
|
catch {
|
|
707
718
|
return false;
|
|
@@ -986,7 +997,7 @@ export function ensureBootstrap(options) {
|
|
|
986
997
|
const stat = statSync(configPath);
|
|
987
998
|
const mtimeMs = stat.mtimeMs;
|
|
988
999
|
const now = Date.now();
|
|
989
|
-
if (now - mtimeMs < 60000) {
|
|
1000
|
+
if (now - mtimeMs < 60000 && hasExpectedConfig(definition)) {
|
|
990
1001
|
continue;
|
|
991
1002
|
}
|
|
992
1003
|
}
|
|
@@ -9,4 +9,4 @@ export declare function buildNexusMcpCommand(): {
|
|
|
9
9
|
};
|
|
10
10
|
export declare function buildNexusMcpEnv(workspaceRoot?: string): Record<string, string>;
|
|
11
11
|
export declare function buildNexusMcpServerConfig(workspaceRoot?: string): NexusMcpServerConfig;
|
|
12
|
-
export declare function isStableNexusMcpServerConfig(server: any, envKey?: 'env' | 'environment'): boolean;
|
|
12
|
+
export declare function isStableNexusMcpServerConfig(server: any, envKey?: 'env' | 'environment', expectedWorkspaceRoot?: string): boolean;
|
|
@@ -1,4 +1,22 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { dirname, isAbsolute, resolve } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
function resolveCliEntrypoint() {
|
|
5
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const candidates = [
|
|
7
|
+
resolve(currentDir, '..', 'cli.js'),
|
|
8
|
+
resolve(currentDir, '..', '..', 'dist', 'cli.js'),
|
|
9
|
+
];
|
|
10
|
+
return candidates.find((candidate) => existsSync(candidate)) ?? null;
|
|
11
|
+
}
|
|
1
12
|
export function buildNexusMcpCommand() {
|
|
13
|
+
const cliPath = resolveCliEntrypoint();
|
|
14
|
+
if (cliPath) {
|
|
15
|
+
return {
|
|
16
|
+
command: process.execPath,
|
|
17
|
+
args: [cliPath, 'mcp'],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
2
20
|
return {
|
|
3
21
|
command: 'nexus-prime',
|
|
4
22
|
args: ['mcp'],
|
|
@@ -26,16 +44,36 @@ export function buildNexusMcpServerConfig(workspaceRoot) {
|
|
|
26
44
|
env: buildNexusMcpEnv(workspaceRoot),
|
|
27
45
|
};
|
|
28
46
|
}
|
|
29
|
-
|
|
47
|
+
function sameResolvedPath(a, b) {
|
|
48
|
+
if (typeof a !== 'string' || !a.trim())
|
|
49
|
+
return false;
|
|
50
|
+
try {
|
|
51
|
+
return resolve(a) === resolve(b);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function isStableNexusMcpServerConfig(server, envKey = 'env', expectedWorkspaceRoot) {
|
|
30
58
|
const env = server?.[envKey];
|
|
59
|
+
const command = typeof server?.command === 'string' ? server.command : '';
|
|
31
60
|
const args = Array.isArray(server?.args) ? server.args.map((arg) => String(arg)) : [];
|
|
61
|
+
const cliArg = args.find((arg) => /(?:^|[/\\])cli\.js$/.test(arg));
|
|
62
|
+
const workspaceOk = expectedWorkspaceRoot
|
|
63
|
+
? sameResolvedPath(env?.NEXUS_WORKSPACE_ROOT, expectedWorkspaceRoot)
|
|
64
|
+
: true;
|
|
32
65
|
return Boolean(server
|
|
33
|
-
&&
|
|
34
|
-
&&
|
|
66
|
+
&& command
|
|
67
|
+
&& command !== 'npx'
|
|
68
|
+
&& command !== 'nexus-prime'
|
|
69
|
+
&& isAbsolute(command)
|
|
70
|
+
&& cliArg
|
|
71
|
+
&& isAbsolute(cliArg)
|
|
35
72
|
&& args.includes('mcp')
|
|
36
73
|
&& !args.includes('-y')
|
|
37
74
|
&& env?.NEXUS_MCP_TOOL_PROFILE === 'autonomous'
|
|
38
75
|
&& env?.NEXUS_MCP_AUTO_CONFIG === '0'
|
|
39
76
|
&& env?.NEXUS_DAEMON_AUTOSPAWN === '0'
|
|
40
|
-
&& env?.NEXUS_MCP_FULL_DNA_ON_DISCONNECT === '1'
|
|
77
|
+
&& env?.NEXUS_MCP_FULL_DNA_ON_DISCONNECT === '1'
|
|
78
|
+
&& workspaceOk);
|
|
41
79
|
}
|
|
@@ -21,6 +21,12 @@ function getMode() {
|
|
|
21
21
|
// Tools that create resources subject to quantity caps
|
|
22
22
|
const MEMORY_CREATION_TOOLS = new Set(['nexus_store_memory']);
|
|
23
23
|
const OPERATIVE_CREATION_TOOLS = new Set(['nexus_synapse_hire', 'nexus_synapse_mandate']);
|
|
24
|
+
const LICENSE_SAFE_TOOLS = new Set([
|
|
25
|
+
'nexus_license_usage',
|
|
26
|
+
'nexus_runtime_health',
|
|
27
|
+
'nexus_describe_tool',
|
|
28
|
+
'nexus_session_dna',
|
|
29
|
+
]);
|
|
24
30
|
/**
|
|
25
31
|
* LicenseEnforcementMiddleware — priority 5 (runs first in the pipeline).
|
|
26
32
|
*
|
|
@@ -35,8 +41,8 @@ export const LicenseEnforcementMiddleware = {
|
|
|
35
41
|
const status = lm.getStatus();
|
|
36
42
|
const mode = getMode();
|
|
37
43
|
const toolName = ctx.toolName;
|
|
38
|
-
//
|
|
39
|
-
if (toolName
|
|
44
|
+
// Keep activation/status paths available even when the trial gate is closed.
|
|
45
|
+
if (LICENSE_SAFE_TOOLS.has(toolName))
|
|
40
46
|
return;
|
|
41
47
|
// ── No license check ──────────────────────────────────────────────
|
|
42
48
|
if (status.degradedReason === 'not-activated') {
|
|
@@ -46,17 +52,28 @@ export const LicenseEnforcementMiddleware = {
|
|
|
46
52
|
// In all modes: let the tool execute (soft gate during ramp)
|
|
47
53
|
return;
|
|
48
54
|
}
|
|
55
|
+
if (status.degradedReason === 'activation-required') {
|
|
56
|
+
const msg = trialActiveMessage(status);
|
|
57
|
+
if (mode !== 'audit') {
|
|
58
|
+
ctx.meta.licenseUpgradeHint = msg;
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
49
62
|
if (status.degradedReason === 'trial-expired') {
|
|
63
|
+
const msg = trialExpiredMessage();
|
|
50
64
|
if (mode !== 'audit') {
|
|
51
|
-
ctx.meta.
|
|
65
|
+
ctx.meta.shortCircuitResult = {
|
|
66
|
+
content: [{ type: 'text', text: msg }],
|
|
67
|
+
};
|
|
68
|
+
ctx.meta.shortCircuitedBy = 'license-enforcement';
|
|
52
69
|
}
|
|
53
|
-
|
|
70
|
+
return;
|
|
54
71
|
}
|
|
55
72
|
if (status.trial && mode !== 'audit') {
|
|
56
73
|
// Soft footer so users know the runtime is being kept alive by the
|
|
57
74
|
// auto-issued local trial. Doesn't gate execution — paid tiers behave
|
|
58
75
|
// exactly like a real license while the trial is active.
|
|
59
|
-
ctx.meta.licenseUpgradeHint = trialActiveMessage(status
|
|
76
|
+
ctx.meta.licenseUpgradeHint = trialActiveMessage(status);
|
|
60
77
|
}
|
|
61
78
|
// ── Tool tier check ───────────────────────────────────────────────
|
|
62
79
|
if (!isToolAllowed(toolName, status.tier)) {
|
|
@@ -6,4 +6,4 @@ export { snapshotPCU, formatPCUStatus, type PCUSnapshot } from './pcu-meter.js';
|
|
|
6
6
|
export { capWarningMessage, capExceededMessage, toolGateMessage, noLicenseMessage, trialActiveMessage, trialExpiredMessage, } from './upgrade-prompts.js';
|
|
7
7
|
export { syncLicense, requestUpgrade } from './license-sync.js';
|
|
8
8
|
export { loginFromCLI, readAuthToken, readAuthInfo, isLoggedIn, logout } from './web-auth.js';
|
|
9
|
-
export type { PlanTier, PlanCaps, LicenseClaims, LicenseStatus, CapType, CapCheckResult, SkillProfile, DarwinScope, } from './types.js';
|
|
9
|
+
export type { PlanTier, TrialPhase, PlanCaps, LicenseClaims, LicenseStatus, CapType, CapCheckResult, SkillProfile, DarwinScope, } from './types.js';
|
|
@@ -28,6 +28,8 @@ async function emitLicenseEvent(eventType, payload) {
|
|
|
28
28
|
// The matching private key is stored securely on the Nexus Prime licensing backend.
|
|
29
29
|
const NEXUS_PUBLIC_KEY_B64 = 'MCowBQYDK2VwAyEAbrBiMBqzIyatM/Q/plA0Dn2Y/TAu2UVmWG8guGI0ElQ=';
|
|
30
30
|
const UPGRADE_URL = 'https://nexus-prime.cfd/pricing';
|
|
31
|
+
const ACCOUNT_URL = 'https://nexus-prime.cfd/account';
|
|
32
|
+
const LICENSE_TEAM_EMAIL = 'adarsh@nexus-prime.cfd';
|
|
31
33
|
// Warn at this fraction of the cap (e.g. 0.8 = 80%)
|
|
32
34
|
const WARN_THRESHOLD = 0.8;
|
|
33
35
|
// Trial configuration. Every install gets a 30-day full-tier trial the first
|
|
@@ -35,7 +37,9 @@ const WARN_THRESHOLD = 0.8;
|
|
|
35
37
|
// paid tools available — so users can actually evaluate the product. The
|
|
36
38
|
// trial marker (stateDir/trial.json) records issue + expiry, so subsequent
|
|
37
39
|
// loads stay deterministic offline. NEXUS_DISABLE_TRIAL=1 opts out (tests).
|
|
38
|
-
const
|
|
40
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
41
|
+
const TRIAL_DURATION_MS = 30 * DAY_MS;
|
|
42
|
+
const TRIAL_GRACE_MS = 3 * DAY_MS;
|
|
39
43
|
const TRIAL_TIER = 'enterprise';
|
|
40
44
|
const TRIAL_MARKER_FILENAME = 'trial.json';
|
|
41
45
|
function readTrialMarker(markerPath) {
|
|
@@ -69,6 +73,64 @@ function writeTrialMarker(markerPath, marker) {
|
|
|
69
73
|
// best-effort: trial still applies in memory even if we can't persist it.
|
|
70
74
|
}
|
|
71
75
|
}
|
|
76
|
+
function clampTrialMarker(marker, now) {
|
|
77
|
+
const issuedAt = Number.isFinite(marker.issuedAt) && marker.issuedAt <= now + DAY_MS
|
|
78
|
+
? marker.issuedAt
|
|
79
|
+
: now;
|
|
80
|
+
const maxExpiresAt = issuedAt + TRIAL_DURATION_MS;
|
|
81
|
+
const expiresAt = Number.isFinite(marker.expiresAt) && marker.expiresAt >= issuedAt
|
|
82
|
+
? Math.min(marker.expiresAt, maxExpiresAt)
|
|
83
|
+
: maxExpiresAt;
|
|
84
|
+
const normalized = {
|
|
85
|
+
issuedAt,
|
|
86
|
+
expiresAt,
|
|
87
|
+
tier: TRIAL_TIER,
|
|
88
|
+
orgId: marker.orgId ?? null,
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
marker: normalized,
|
|
92
|
+
changed: normalized.issuedAt !== marker.issuedAt
|
|
93
|
+
|| normalized.expiresAt !== marker.expiresAt
|
|
94
|
+
|| normalized.tier !== marker.tier
|
|
95
|
+
|| normalized.orgId !== marker.orgId,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function daysUntil(timestamp, now) {
|
|
99
|
+
return Math.max(0, Math.ceil((timestamp - now) / DAY_MS));
|
|
100
|
+
}
|
|
101
|
+
function buildActivationMotion(phase, daysRemaining) {
|
|
102
|
+
if (phase === 'expired') {
|
|
103
|
+
return {
|
|
104
|
+
phase: 'expired',
|
|
105
|
+
required: true,
|
|
106
|
+
title: 'Trial expired',
|
|
107
|
+
message: `The trial window is over. Request a license in the Nexus account page or email ${LICENSE_TEAM_EMAIL}. The team will share licenses within 24 hours.`,
|
|
108
|
+
action: 'Request license',
|
|
109
|
+
url: ACCOUNT_URL,
|
|
110
|
+
command: 'nexus-prime license activate <token>',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (phase === 'activation') {
|
|
114
|
+
return {
|
|
115
|
+
phase: 'activation',
|
|
116
|
+
required: false,
|
|
117
|
+
title: 'Activate license',
|
|
118
|
+
message: `The 3-day no-license grace period is over, but the 30-day trial remains active for ${daysRemaining} day${daysRemaining === 1 ? '' : 's'}. Keep running Nexus Prime and ask the user to request or activate a license soon. Request one in the Nexus account page or email ${LICENSE_TEAM_EMAIL}; the team will share licenses within 24 hours.`,
|
|
119
|
+
action: 'Request license',
|
|
120
|
+
url: ACCOUNT_URL,
|
|
121
|
+
command: 'nexus-prime license activate <token>',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
phase: 'grace',
|
|
126
|
+
required: false,
|
|
127
|
+
title: '3-day no-license grace',
|
|
128
|
+
message: `Nexus Prime is in the first 3-day no-license grace period. No license is mandatory during these first 3 days; ask the user to request a license soon so approvals can be shared within 24 hours by the Nexus Prime team.`,
|
|
129
|
+
action: 'Request license',
|
|
130
|
+
url: ACCOUNT_URL,
|
|
131
|
+
command: 'nexus-prime license activate <token>',
|
|
132
|
+
};
|
|
133
|
+
}
|
|
72
134
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
73
135
|
function base64urlDecode(s) {
|
|
74
136
|
return Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/'), 'base64');
|
|
@@ -101,7 +163,11 @@ function buildFreeStatus(degradedReason) {
|
|
|
101
163
|
degradedReason,
|
|
102
164
|
};
|
|
103
165
|
}
|
|
104
|
-
function buildTrialStatus(marker) {
|
|
166
|
+
function buildTrialStatus(marker, now = Date.now()) {
|
|
167
|
+
const graceEndsAt = marker.issuedAt + TRIAL_GRACE_MS;
|
|
168
|
+
const daysRemaining = daysUntil(marker.expiresAt, now);
|
|
169
|
+
const trialPhase = now >= graceEndsAt ? 'activation' : 'grace';
|
|
170
|
+
const trialDaysElapsed = Math.max(0, Math.floor((now - marker.issuedAt) / DAY_MS));
|
|
105
171
|
return {
|
|
106
172
|
valid: true,
|
|
107
173
|
tier: marker.tier,
|
|
@@ -109,6 +175,26 @@ function buildTrialStatus(marker) {
|
|
|
109
175
|
expiresAt: marker.expiresAt,
|
|
110
176
|
orgId: marker.orgId,
|
|
111
177
|
trial: true,
|
|
178
|
+
trialPhase,
|
|
179
|
+
trialIssuedAt: marker.issuedAt,
|
|
180
|
+
trialGraceEndsAt: graceEndsAt,
|
|
181
|
+
trialDaysElapsed,
|
|
182
|
+
trialDaysRemaining: daysRemaining,
|
|
183
|
+
activationRequired: false,
|
|
184
|
+
activationMotion: buildActivationMotion(trialPhase, daysRemaining),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function buildTrialExpiredStatus(marker, now = Date.now()) {
|
|
188
|
+
return {
|
|
189
|
+
...buildFreeStatus('trial-expired'),
|
|
190
|
+
trial: false,
|
|
191
|
+
trialPhase: 'expired',
|
|
192
|
+
trialIssuedAt: marker.issuedAt,
|
|
193
|
+
trialGraceEndsAt: marker.issuedAt + TRIAL_GRACE_MS,
|
|
194
|
+
trialDaysElapsed: Math.max(0, Math.floor((now - marker.issuedAt) / DAY_MS)),
|
|
195
|
+
trialDaysRemaining: 0,
|
|
196
|
+
activationRequired: true,
|
|
197
|
+
activationMotion: buildActivationMotion('expired', 0),
|
|
112
198
|
};
|
|
113
199
|
}
|
|
114
200
|
function validateClaims(claims) {
|
|
@@ -240,10 +326,17 @@ export class LicenseManager {
|
|
|
240
326
|
};
|
|
241
327
|
writeTrialMarker(this.trialPath, marker);
|
|
242
328
|
}
|
|
329
|
+
else {
|
|
330
|
+
const normalized = clampTrialMarker(marker, now);
|
|
331
|
+
marker = normalized.marker;
|
|
332
|
+
if (normalized.changed) {
|
|
333
|
+
writeTrialMarker(this.trialPath, marker);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
243
336
|
if (marker.expiresAt <= now) {
|
|
244
|
-
return
|
|
337
|
+
return buildTrialExpiredStatus(marker, now);
|
|
245
338
|
}
|
|
246
|
-
return buildTrialStatus(marker);
|
|
339
|
+
return buildTrialStatus(marker, now);
|
|
247
340
|
}
|
|
248
341
|
/** Returns the path of the trial marker (for tests + status surfaces). */
|
|
249
342
|
getTrialMarkerPath() {
|
|
@@ -22,7 +22,8 @@ export async function syncLicense() {
|
|
|
22
22
|
const authToken = readAuthToken();
|
|
23
23
|
if (!authToken) {
|
|
24
24
|
throw new Error('Not logged in. Run: nexus-prime license login\n' +
|
|
25
|
-
'Or
|
|
25
|
+
'Or request a license: https://nexus-prime.cfd/account\n' +
|
|
26
|
+
'Email fallback: adarsh@nexus-prime.cfd');
|
|
26
27
|
}
|
|
27
28
|
const res = await fetch(`${API_BASE}/api/license/current`, {
|
|
28
29
|
headers: { Authorization: `Bearer ${authToken}` },
|
|
@@ -37,7 +38,7 @@ export async function syncLicense() {
|
|
|
37
38
|
}
|
|
38
39
|
const data = await res.json();
|
|
39
40
|
if (!data.signed_key) {
|
|
40
|
-
throw new Error('No license key found.
|
|
41
|
+
throw new Error('No license key found. Request one at https://nexus-prime.cfd/account or email adarsh@nexus-prime.cfd. Licenses are reviewed within 24 hours.');
|
|
41
42
|
}
|
|
42
43
|
const lm = getSharedLicenseManager();
|
|
43
44
|
return lm.activate(data.signed_key);
|
|
@@ -49,7 +50,7 @@ export async function syncLicense() {
|
|
|
49
50
|
export async function requestUpgrade(requestedPlan) {
|
|
50
51
|
const authToken = readAuthToken();
|
|
51
52
|
if (!authToken) {
|
|
52
|
-
throw new Error('Not logged in. Run: nexus-prime license login');
|
|
53
|
+
throw new Error('Not logged in. Run: nexus-prime license login, or use https://nexus-prime.cfd/account / adarsh@nexus-prime.cfd.');
|
|
53
54
|
}
|
|
54
55
|
const res = await fetch(`${API_BASE}/api/license/request-upgrade`, {
|
|
55
56
|
method: 'POST',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type PlanTier = 'free' | 'pro' | 'team' | 'enterprise';
|
|
2
2
|
export type SkillProfile = 'foundation' | 'all-builtin' | 'all-custom' | 'all-registry';
|
|
3
3
|
export type DarwinScope = 'off' | 'solo' | 'team' | 'fleet';
|
|
4
|
+
export type TrialPhase = 'grace' | 'activation' | 'expired';
|
|
4
5
|
export interface PlanCaps {
|
|
5
6
|
maxProjects: number;
|
|
6
7
|
maxMemoryEntries: number;
|
|
@@ -27,9 +28,24 @@ export interface LicenseStatus {
|
|
|
27
28
|
orgId: string | null;
|
|
28
29
|
/** True when the active status is the auto-issued local trial. */
|
|
29
30
|
trial?: boolean;
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
/** Trial phase for PLG surfaces. First three days are quiet grace; the rest of the trial uses soft activation prompts. */
|
|
32
|
+
trialPhase?: TrialPhase;
|
|
33
|
+
trialIssuedAt?: number;
|
|
34
|
+
trialGraceEndsAt?: number;
|
|
35
|
+
trialDaysElapsed?: number;
|
|
36
|
+
trialDaysRemaining?: number;
|
|
37
|
+
activationRequired?: boolean;
|
|
38
|
+
activationMotion?: {
|
|
39
|
+
phase: TrialPhase;
|
|
40
|
+
required: boolean;
|
|
41
|
+
title: string;
|
|
42
|
+
message: string;
|
|
43
|
+
action: string;
|
|
44
|
+
url: string;
|
|
45
|
+
command: string;
|
|
46
|
+
};
|
|
47
|
+
/** Reason a license is degraded; absent when status is valid. */
|
|
48
|
+
degradedReason?: 'expired' | 'signature-invalid' | 'malformed' | 'not-activated' | 'activation-required' | 'trial-expired';
|
|
33
49
|
}
|
|
34
50
|
export type CapType = 'memory_entries' | 'projects' | 'operatives';
|
|
35
51
|
export interface CapCheckResult {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PlanTier, CapCheckResult } from './types.js';
|
|
1
|
+
import type { PlanTier, CapCheckResult, LicenseStatus } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Soft nudge at 80% of cap — single line appended to tool response.
|
|
4
4
|
*/
|
|
@@ -19,7 +19,7 @@ export declare function noLicenseMessage(): string;
|
|
|
19
19
|
* Trial is active. Soft hint that surfaces alongside paid tools so users
|
|
20
20
|
* know the auto-issued window is what's keeping their runtime alive.
|
|
21
21
|
*/
|
|
22
|
-
export declare function trialActiveMessage(
|
|
22
|
+
export declare function trialActiveMessage(statusOrExpiresAt: LicenseStatus | number | null): string;
|
|
23
23
|
/**
|
|
24
24
|
* Trial has expired — runtime drops to free caps. Tell the user clearly so
|
|
25
25
|
* the gap between trial-expired and a real license is obvious.
|
|
@@ -14,6 +14,7 @@ async function emitUpgradeNudge(currentTier, feature, message, ctaUrl) {
|
|
|
14
14
|
}
|
|
15
15
|
const UPGRADE_URL = 'https://nexus-prime.cfd/account';
|
|
16
16
|
const SIGNUP_URL = 'https://nexus-prime.cfd/signup';
|
|
17
|
+
const LICENSE_TEAM_EMAIL = 'adarsh@nexus-prime.cfd';
|
|
17
18
|
const CAP_LABELS = {
|
|
18
19
|
memory_entries: 'memory entries',
|
|
19
20
|
projects: 'projects',
|
|
@@ -55,21 +56,48 @@ export function toolGateMessage(toolName, requiredTier, currentTier) {
|
|
|
55
56
|
*/
|
|
56
57
|
export function noLicenseMessage() {
|
|
57
58
|
return [
|
|
58
|
-
`[Nexus Prime] No license activated.
|
|
59
|
+
`[Nexus Prime] No license activated. Request a license before paid-tier work runs:`,
|
|
59
60
|
` → ${SIGNUP_URL}`,
|
|
60
|
-
`
|
|
61
|
+
` Account: ${UPGRADE_URL}`,
|
|
62
|
+
` Email: ${LICENSE_TEAM_EMAIL}`,
|
|
63
|
+
` Licenses are reviewed and shared by the team within 24 hours.`,
|
|
64
|
+
` Then activate: nexus-prime license activate <your-token>`,
|
|
61
65
|
].join('\n');
|
|
62
66
|
}
|
|
63
67
|
/**
|
|
64
68
|
* Trial is active. Soft hint that surfaces alongside paid tools so users
|
|
65
69
|
* know the auto-issued window is what's keeping their runtime alive.
|
|
66
70
|
*/
|
|
67
|
-
export function trialActiveMessage(
|
|
68
|
-
const
|
|
71
|
+
export function trialActiveMessage(statusOrExpiresAt) {
|
|
72
|
+
const status = typeof statusOrExpiresAt === 'object' ? statusOrExpiresAt : null;
|
|
73
|
+
const expiresAt = status ? status.expiresAt : typeof statusOrExpiresAt === 'number' ? statusOrExpiresAt : null;
|
|
74
|
+
const daysLeft = status?.trialDaysRemaining
|
|
75
|
+
?? (expiresAt ? Math.max(0, Math.ceil((expiresAt - Date.now()) / (24 * 60 * 60 * 1000))) : 0);
|
|
76
|
+
if (status?.activationRequired) {
|
|
77
|
+
return [
|
|
78
|
+
`[Nexus Prime] License activation requested.`,
|
|
79
|
+
` Agent motion: ask the user to request or activate a Nexus Prime license now, but do not stop free/trial-safe work unless the trial has expired.`,
|
|
80
|
+
` Trial window: ${daysLeft} day${daysLeft === 1 ? '' : 's'} remaining.`,
|
|
81
|
+
` Request: ${UPGRADE_URL}`,
|
|
82
|
+
` Email: ${LICENSE_TEAM_EMAIL}`,
|
|
83
|
+
` The Nexus Prime team will share licenses within 24 hours.`,
|
|
84
|
+
` Activate: nexus-prime license activate <token>`,
|
|
85
|
+
].join('\n');
|
|
86
|
+
}
|
|
87
|
+
if (status?.trialPhase === 'activation') {
|
|
88
|
+
return [
|
|
89
|
+
`[Nexus Prime] Trial active (${daysLeft} day${daysLeft === 1 ? '' : 's'} remaining).`,
|
|
90
|
+
` Agent motion: the first 3 no-license days are over; ask the user to request or activate a license soon, then keep working.`,
|
|
91
|
+
` Request: ${UPGRADE_URL}`,
|
|
92
|
+
` Email: ${LICENSE_TEAM_EMAIL}`,
|
|
93
|
+
` Licenses are reviewed and shared within 24 hours.`,
|
|
94
|
+
].join('\n');
|
|
95
|
+
}
|
|
69
96
|
return [
|
|
70
|
-
`[Nexus Prime]
|
|
71
|
-
`
|
|
72
|
-
`
|
|
97
|
+
`[Nexus Prime] 3-day no-license grace active (${daysLeft} day${daysLeft === 1 ? '' : 's'} in the trial window).`,
|
|
98
|
+
` No license is mandatory during the first 3 days. Request one soon: ${UPGRADE_URL}`,
|
|
99
|
+
` Email fallback: ${LICENSE_TEAM_EMAIL}`,
|
|
100
|
+
` Licenses are reviewed and shared within 24 hours.`,
|
|
73
101
|
].join('\n');
|
|
74
102
|
}
|
|
75
103
|
/**
|
|
@@ -78,8 +106,10 @@ export function trialActiveMessage(expiresAt) {
|
|
|
78
106
|
*/
|
|
79
107
|
export function trialExpiredMessage() {
|
|
80
108
|
return [
|
|
81
|
-
`[Nexus Prime] Trial expired —
|
|
109
|
+
`[Nexus Prime] Trial expired — license required for paid-tier work.`,
|
|
110
|
+
` Request: ${UPGRADE_URL}`,
|
|
111
|
+
` Email: ${LICENSE_TEAM_EMAIL}`,
|
|
112
|
+
` Licenses are reviewed and shared within 24 hours.`,
|
|
82
113
|
` Activate: nexus-prime license activate <token>`,
|
|
83
|
-
` Pricing: ${UPGRADE_URL}`,
|
|
84
114
|
].join('\n');
|
|
85
115
|
}
|
|
@@ -7,8 +7,8 @@ interface AuthTokens {
|
|
|
7
7
|
/**
|
|
8
8
|
* Login to nexus-prime.cfd and store auth tokens locally. Errors are
|
|
9
9
|
* normalised so signup/signin failures don't surface raw HTTP codes —
|
|
10
|
-
* users get something actionable, plus a reminder that the local
|
|
11
|
-
*
|
|
10
|
+
* users get something actionable, plus a reminder that the local no-license
|
|
11
|
+
* grace period is intentionally short and licenses are reviewed manually.
|
|
12
12
|
*/
|
|
13
13
|
export declare function loginFromCLI(email: string, password: string): Promise<{
|
|
14
14
|
email: string;
|
|
@@ -12,8 +12,8 @@ const API_BASE = process.env.NEXUS_WEB_API_URL ?? 'https://nexus-prime.cfd';
|
|
|
12
12
|
/**
|
|
13
13
|
* Login to nexus-prime.cfd and store auth tokens locally. Errors are
|
|
14
14
|
* normalised so signup/signin failures don't surface raw HTTP codes —
|
|
15
|
-
* users get something actionable, plus a reminder that the local
|
|
16
|
-
*
|
|
15
|
+
* users get something actionable, plus a reminder that the local no-license
|
|
16
|
+
* grace period is intentionally short and licenses are reviewed manually.
|
|
17
17
|
*/
|
|
18
18
|
export async function loginFromCLI(email, password) {
|
|
19
19
|
let res;
|
|
@@ -28,8 +28,8 @@ export async function loginFromCLI(email, password) {
|
|
|
28
28
|
catch (err) {
|
|
29
29
|
const reason = err instanceof Error ? err.message : String(err);
|
|
30
30
|
throw new Error(`Could not reach ${API_BASE}: ${reason}. `
|
|
31
|
-
+ `
|
|
32
|
-
+ `
|
|
31
|
+
+ `If your 3-day no-license grace is over, request a license at ${API_BASE}/account `
|
|
32
|
+
+ `or email adarsh@nexus-prime.cfd. Licenses are shared within 24 hours.`);
|
|
33
33
|
}
|
|
34
34
|
if (!res.ok) {
|
|
35
35
|
const body = await res.json().catch(() => ({}));
|
|
@@ -41,10 +41,10 @@ export async function loginFromCLI(email, password) {
|
|
|
41
41
|
}
|
|
42
42
|
if (res.status === 404) {
|
|
43
43
|
throw new Error(`No account for ${email}. Sign up at ${API_BASE}/signup, then retry. `
|
|
44
|
-
+ `
|
|
44
|
+
+ `If you need manual help, email adarsh@nexus-prime.cfd. Licenses are shared within 24 hours.`);
|
|
45
45
|
}
|
|
46
46
|
if (res.status >= 500) {
|
|
47
|
-
throw new Error(`Server error (${res.status}) at ${API_BASE}.
|
|
47
|
+
throw new Error(`Server error (${res.status}) at ${API_BASE}. Try again shortly, or email adarsh@nexus-prime.cfd for a manual license review within 24 hours.`);
|
|
48
48
|
}
|
|
49
49
|
throw new Error(detail ?? `Login failed (HTTP ${res.status})`);
|
|
50
50
|
}
|
|
@@ -122,17 +122,19 @@ async function runWithRetry(maxRetries = 3, delayMs = 1000) {
|
|
|
122
122
|
registerInstall(installId || 'unknown', version);
|
|
123
123
|
}
|
|
124
124
|
catch { /* non-fatal — install tracking is best-effort */ }
|
|
125
|
-
// Show license
|
|
125
|
+
// Show license request prompt
|
|
126
126
|
if (shouldShowInstallBanner()) {
|
|
127
127
|
try {
|
|
128
128
|
const licenseKeyPath = path.join(os.homedir(), '.nexus-prime', 'license.key');
|
|
129
129
|
if (!fs.existsSync(licenseKeyPath)) {
|
|
130
130
|
console.log('');
|
|
131
131
|
console.log('\u2554' + '\u2550'.repeat(50) + '\u2557');
|
|
132
|
-
console.log('\u2551
|
|
133
|
-
console.log('\u2551
|
|
132
|
+
console.log('\u2551 3-day no-license grace is active. \u2551');
|
|
133
|
+
console.log('\u2551 Request a license before day 4: \u2551');
|
|
134
|
+
console.log('\u2551 \u2192 https://nexus-prime.cfd/account \u2551');
|
|
135
|
+
console.log('\u2551 \u2192 adarsh@nexus-prime.cfd \u2551');
|
|
134
136
|
console.log('\u2551 \u2551');
|
|
135
|
-
console.log('\u2551
|
|
137
|
+
console.log('\u2551 Team shares licenses within 24 hours. \u2551');
|
|
136
138
|
console.log('\u2551 nexus-prime license activate <your-token> \u2551');
|
|
137
139
|
console.log('\u255A' + '\u2550'.repeat(50) + '\u255D');
|
|
138
140
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-prime",
|
|
3
|
-
"version": "7.9.
|
|
3
|
+
"version": "7.9.17",
|
|
4
4
|
"description": "Local-first MCP control plane for coding agents with bootstrap-orchestrate execution, memory fabric, token budgeting, and worktree-backed swarms",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|