nexus-prime 7.9.15 → 7.9.16

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 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/30--day-free%20trial-00ff88?style=for-the-badge" alt="30-day free trial"></a>
20
- <img src="https://img.shields.io/badge/independent%20creators-free%20forever-ff69b4?style=for-the-badge" alt="Free for IC creators">
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
- **Free for 30 days. No credit card. No feature gating.**
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>🎁 Free Trial</b></td>
321
- <td>Everyone — try it risk-free</td>
322
- <td>Free for 30 days</td>
323
- <td>All Pro features</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><b>Free forever</b></td>
329
- <td>Unlimited usage on personal projects</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 any paid tier
359
+ nexus-prime license activate <key> # activate the key shared by the team
360
360
  ```
361
361
 
362
- → **[nexus-prime.cfd/pricing](https://nexus-prime.cfd/pricing)** · **[Apply to the pilot program](mailto:hello@nexus-prime.cfd?subject=Pilot%20Program)**
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
- You're never locked out. You can keep using a free tier, pick a paid plan, or if you're an independent creator apply for free-forever access. Your memory, data, and projects stay yours either way.
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
- Yes. If you build independently — solo founder, indie dev, content creator, student, OSS maintainer — you qualify for unlimited free usage. Just <a href="mailto:hello@nexus-prime.cfd?subject=Creator%20Program">email us</a> and we'll sort it.
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 — full Team features free, white-glove onboarding, direct access to the team building it. First come, first served. <a href="mailto:hello@nexus-prime.cfd?subject=Pilot%20Program">Reach out</a>.
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
- Nothing stops working. You get a gentle in-app nudge and a link to upgrade. Your data is never held hostage.
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
- - 🔐 **No required account.** You can run the free trial without signing up for anything.
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
- | 📧 [**hello@nexus-prime.cfd**](mailto:hello@nexus-prime.cfd) | Pilot program, creator program, enterprise, press |
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 generous free tiers:
461
+ Nexus Prime is a **commercial product** with manual license issuance:
461
462
 
462
- - ✅ Free for 30 days, for everyone
463
- - ✅ Free forever for independent creators
464
- - ✅ Free pilot for the first 10 partner companies
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,21 @@ 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**: Stop paid-tier work and ask the user to request or activate a Nexus Prime license now.`
48
+ : `- **Agent motion**: 3-day no-license grace is active; ask the user to request a license before day 4.`,
49
+ ].filter((line) => Boolean(line)) : [];
41
50
  const lines = [
42
51
  `## License Status`,
43
52
  `- **Plan**: ${pcu.tier.toUpperCase()}`,
44
53
  `- **Status**: ${licStatus.valid ? 'Active' : licStatus.degradedReason ?? 'Inactive'}`,
45
54
  licStatus.expiresAt ? `- **Expires**: ${new Date(licStatus.expiresAt).toISOString().split('T')[0]}` : '- **Expires**: Never',
55
+ ...trialLines,
46
56
  '',
47
57
  `## Resource Usage`,
48
58
  `- Memory: ${pcu.memory.current.toLocaleString()} / ${Number.isFinite(pcu.memory.limit) ? pcu.memory.limit.toLocaleString() : '∞'} (${pcu.memory.pct}%)`,
@@ -54,6 +64,8 @@ export async function handleRuntimeGroup(toolName, hctx, request, args, ctx) {
54
64
  `## Upgrade`,
55
65
  `- Dashboard: http://127.0.0.1:3377 → License tab`,
56
66
  `- Web: https://nexus-prime.cfd/account`,
67
+ `- Email: adarsh@nexus-prime.cfd`,
68
+ `- SLA: team will share licenses within 24 hours`,
57
69
  `- CLI: nexus-prime license sync`,
58
70
  ];
59
71
  return { content: [{ type: 'text', text: lines.join('\n') }] };
package/dist/cli.js CHANGED
@@ -1751,7 +1751,7 @@ program
1751
1751
  .description('Manage your Nexus Prime license')
1752
1752
  .addCommand(new Command('activate')
1753
1753
  .description('Activate a license key')
1754
- .argument('<key>', 'License key from nexus-prime.cfd/signup or account page')
1754
+ .argument('<key>', 'License key from the Nexus account page or license team')
1755
1755
  .action((key) => {
1756
1756
  const mgr = getSharedLicenseManager();
1757
1757
  const status = mgr.activate(key);
@@ -1764,7 +1764,8 @@ program
1764
1764
  }
1765
1765
  else {
1766
1766
  console.error(`\u2717 License invalid: ${status.degradedReason ?? 'unknown'}`);
1767
- console.error(' Get a valid key at https://nexus-prime.cfd/signup');
1767
+ console.error(' Request a valid key at https://nexus-prime.cfd/account');
1768
+ console.error(' Or email adarsh@nexus-prime.cfd — licenses are shared within 24 hours.');
1768
1769
  process.exit(1);
1769
1770
  }
1770
1771
  }))
@@ -1792,7 +1793,8 @@ program
1792
1793
  .action(() => {
1793
1794
  getSharedLicenseManager().deactivate();
1794
1795
  console.log('\u2713 License removed. Reverted to Free plan.');
1795
- console.log(' Sign up again at https://nexus-prime.cfd/signup');
1796
+ console.log(' Request a new key at https://nexus-prime.cfd/account');
1797
+ console.log(' Or email adarsh@nexus-prime.cfd — licenses are shared within 24 hours.');
1796
1798
  }))
1797
1799
  .addCommand(new Command('login')
1798
1800
  .description('Login to nexus-prime.cfd for license sync')
@@ -1821,7 +1823,8 @@ program
1821
1823
  }
1822
1824
  catch {
1823
1825
  console.log(' License sync skipped (no license found yet).');
1824
- console.log(' Your free license is generated on signup at nexus-prime.cfd/signup');
1826
+ console.log(' Request a license at https://nexus-prime.cfd/account');
1827
+ console.log(' Or email adarsh@nexus-prime.cfd — licenses are shared within 24 hours.');
1825
1828
  }
1826
1829
  }
1827
1830
  catch (err) {
@@ -1875,12 +1878,13 @@ program
1875
1878
  try {
1876
1879
  const result = await requestUpgrade(plan);
1877
1880
  console.log(`\u2713 Upgrade request submitted (${result.status})`);
1878
- console.log(' We will review your request and notify you when approved.');
1881
+ console.log(' The Nexus Prime team will review it and share licenses within 24 hours.');
1879
1882
  console.log(' Once approved, run: nexus-prime license sync');
1880
1883
  }
1881
1884
  catch (err) {
1882
1885
  console.error(`\u2717 Request failed: ${err instanceof Error ? err.message : String(err)}`);
1883
1886
  console.log(' Visit https://nexus-prime.cfd/account to request an upgrade.');
1887
+ console.log(' Or email adarsh@nexus-prime.cfd with your account email and target plan.');
1884
1888
  process.exit(1);
1885
1889
  }
1886
1890
  }));
@@ -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
  });
@@ -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
- // Skip enforcement for license management tools
39
- if (toolName === 'nexus_license_usage')
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,31 @@ 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.shortCircuitResult = {
59
+ content: [{ type: 'text', text: msg }],
60
+ };
61
+ ctx.meta.shortCircuitedBy = 'license-enforcement';
62
+ }
63
+ return;
64
+ }
49
65
  if (status.degradedReason === 'trial-expired') {
66
+ const msg = trialExpiredMessage();
50
67
  if (mode !== 'audit') {
51
- ctx.meta.licenseUpgradeHint = trialExpiredMessage();
68
+ ctx.meta.shortCircuitResult = {
69
+ content: [{ type: 'text', text: msg }],
70
+ };
71
+ ctx.meta.shortCircuitedBy = 'license-enforcement';
52
72
  }
53
- // Fall through to tier check — caps revert to free tier on trial expiry.
73
+ return;
54
74
  }
55
75
  if (status.trial && mode !== 'audit') {
56
76
  // Soft footer so users know the runtime is being kept alive by the
57
77
  // auto-issued local trial. Doesn't gate execution — paid tiers behave
58
78
  // exactly like a real license while the trial is active.
59
- ctx.meta.licenseUpgradeHint = trialActiveMessage(status.expiresAt);
79
+ ctx.meta.licenseUpgradeHint = trialActiveMessage(status);
60
80
  }
61
81
  // ── Tool tier check ───────────────────────────────────────────────
62
82
  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 TRIAL_DURATION_MS = 30 * 24 * 60 * 60 * 1000;
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: true,
117
+ title: 'License required',
118
+ message: `The 3-day no-license grace period is over. Nexus Prime requires an activated license now; ${daysRemaining} day${daysRemaining === 1 ? '' : 's'} remain in the trial window after activation. 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. Request a license before day 4; approvals are 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,24 @@ 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));
171
+ if (trialPhase === 'activation') {
172
+ return {
173
+ ...buildFreeStatus('activation-required'),
174
+ trial: true,
175
+ trialPhase,
176
+ trialIssuedAt: marker.issuedAt,
177
+ trialGraceEndsAt: graceEndsAt,
178
+ trialDaysElapsed,
179
+ trialDaysRemaining: daysRemaining,
180
+ activationRequired: true,
181
+ activationMotion: buildActivationMotion(trialPhase, daysRemaining),
182
+ };
183
+ }
105
184
  return {
106
185
  valid: true,
107
186
  tier: marker.tier,
@@ -109,6 +188,26 @@ function buildTrialStatus(marker) {
109
188
  expiresAt: marker.expiresAt,
110
189
  orgId: marker.orgId,
111
190
  trial: true,
191
+ trialPhase,
192
+ trialIssuedAt: marker.issuedAt,
193
+ trialGraceEndsAt: graceEndsAt,
194
+ trialDaysElapsed,
195
+ trialDaysRemaining: daysRemaining,
196
+ activationRequired: false,
197
+ activationMotion: buildActivationMotion(trialPhase, daysRemaining),
198
+ };
199
+ }
200
+ function buildTrialExpiredStatus(marker, now = Date.now()) {
201
+ return {
202
+ ...buildFreeStatus('trial-expired'),
203
+ trial: false,
204
+ trialPhase: 'expired',
205
+ trialIssuedAt: marker.issuedAt,
206
+ trialGraceEndsAt: marker.issuedAt + TRIAL_GRACE_MS,
207
+ trialDaysElapsed: Math.max(0, Math.floor((now - marker.issuedAt) / DAY_MS)),
208
+ trialDaysRemaining: 0,
209
+ activationRequired: true,
210
+ activationMotion: buildActivationMotion('expired', 0),
112
211
  };
113
212
  }
114
213
  function validateClaims(claims) {
@@ -240,10 +339,17 @@ export class LicenseManager {
240
339
  };
241
340
  writeTrialMarker(this.trialPath, marker);
242
341
  }
342
+ else {
343
+ const normalized = clampTrialMarker(marker, now);
344
+ marker = normalized.marker;
345
+ if (normalized.changed) {
346
+ writeTrialMarker(this.trialPath, marker);
347
+ }
348
+ }
243
349
  if (marker.expiresAt <= now) {
244
- return buildFreeStatus('trial-expired');
350
+ return buildTrialExpiredStatus(marker, now);
245
351
  }
246
- return buildTrialStatus(marker);
352
+ return buildTrialStatus(marker, now);
247
353
  }
248
354
  /** Returns the path of the trial marker (for tests + status surfaces). */
249
355
  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 activate manually: nexus-prime license activate <token>');
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. Sign up at https://nexus-prime.cfd/signup');
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
- /** Reason a license is degraded; absent when status is valid. Trials report
31
- * `trial-expired` after the auto-issued window has closed. */
32
- degradedReason?: 'expired' | 'signature-invalid' | 'malformed' | 'not-activated' | 'trial-expired';
31
+ /** Trial phase for PLG surfaces. First three days are grace, then activation prompts become direct. */
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(expiresAt: number | null): string;
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,39 @@ export function toolGateMessage(toolName, requiredTier, currentTier) {
55
56
  */
56
57
  export function noLicenseMessage() {
57
58
  return [
58
- `[Nexus Prime] No license activated. Sign up for your free license:`,
59
+ `[Nexus Prime] No license activated. Request a license before paid-tier work runs:`,
59
60
  ` → ${SIGNUP_URL}`,
60
- ` Then: nexus-prime license activate <your-token>`,
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(expiresAt) {
68
- const daysLeft = expiresAt ? Math.max(0, Math.ceil((expiresAt - Date.now()) / (24 * 60 * 60 * 1000))) : 0;
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 required — the 3-day no-license grace period is over.`,
79
+ ` Agent motion: ask the user to request or activate a Nexus Prime license now before continuing.`,
80
+ ` Trial window after activation: ${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
+ }
69
87
  return [
70
- `[Nexus Prime] Trial active (${daysLeft} day${daysLeft === 1 ? '' : 's'} remaining).`,
71
- ` Activate a license: nexus-prime license activate <token>`,
72
- ` Sign up: ${SIGNUP_URL}`,
88
+ `[Nexus Prime] 3-day no-license grace active (${daysLeft} day${daysLeft === 1 ? '' : 's'} in the trial window).`,
89
+ ` Request a license before day 4: ${UPGRADE_URL}`,
90
+ ` Email fallback: ${LICENSE_TEAM_EMAIL}`,
91
+ ` Licenses are reviewed and shared within 24 hours.`,
73
92
  ].join('\n');
74
93
  }
75
94
  /**
@@ -78,8 +97,10 @@ export function trialActiveMessage(expiresAt) {
78
97
  */
79
98
  export function trialExpiredMessage() {
80
99
  return [
81
- `[Nexus Prime] Trial expired — running on free caps.`,
100
+ `[Nexus Prime] Trial expired — license required for paid-tier work.`,
101
+ ` Request: ${UPGRADE_URL}`,
102
+ ` Email: ${LICENSE_TEAM_EMAIL}`,
103
+ ` Licenses are reviewed and shared within 24 hours.`,
82
104
  ` Activate: nexus-prime license activate <token>`,
83
- ` Pricing: ${UPGRADE_URL}`,
84
105
  ].join('\n');
85
106
  }
@@ -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 30-day
11
- * trial keeps the runtime working while they sort out account state.
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 30-day
16
- * trial keeps the runtime working while they sort out account state.
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
- + `Your 30-day local trial remains active run \`nexus-prime status\` to verify, `
32
- + `then retry login when the network is back.`);
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
- + `(Your 30-day local trial keeps the runtime active in the meantime.)`);
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}. Your 30-day local trial keeps the runtime active try again shortly.`);
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 signup prompt
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 Sign up for your free license: \u2551');
133
- console.log('\u2551 \u2192 https://nexus-prime.cfd/signup \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 Then activate: \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.15",
3
+ "version": "7.9.16",
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",