@robbiesrobotics/alice-agents 1.3.0 → 1.3.1

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/lib/doctor.mjs CHANGED
@@ -157,6 +157,39 @@ export async function runDoctor() {
157
157
  );
158
158
  allOk = allOk && modelOk;
159
159
 
160
+ // 6. License check
161
+ const { checkProLicense } = await import('./license.mjs');
162
+ const manifest = (() => {
163
+ try {
164
+ const mPath = join(OPENCLAW_DIR, '.alice-manifest.json');
165
+ if (!existsSync(mPath)) return null;
166
+ return JSON.parse(readFileSync(mPath, 'utf8'));
167
+ } catch {
168
+ return null;
169
+ }
170
+ })();
171
+
172
+ const licenseResult = await checkProLicense();
173
+ if (manifest?.tier === 'pro' || licenseResult.licensed) {
174
+ if (licenseResult.licensed) {
175
+ const maskedKey = licenseResult.key.slice(0, 13) + '****';
176
+ check(
177
+ `Pro license: ${maskedKey} (stored at ~/.alice/license)`,
178
+ true
179
+ );
180
+ } else {
181
+ check(
182
+ 'Pro license: not found (running Starter tier)',
183
+ false,
184
+ 'Purchase a Pro license at: https://getalice.av3.ai/pricing'
185
+ );
186
+ allOk = false;
187
+ }
188
+ } else {
189
+ // Starter tier — just note no license needed
190
+ check('License: Starter tier (no license required)', true);
191
+ }
192
+
160
193
  // Summary
161
194
  console.log();
162
195
  if (allOk) {
package/lib/installer.mjs CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  promptModelPreset,
12
12
  promptCustomModel,
13
13
  promptTier,
14
+ promptLicenseKey,
14
15
  confirm,
15
16
  choose,
16
17
  input,
@@ -311,22 +312,58 @@ export async function runInstall(options = {}) {
311
312
  }
312
313
 
313
314
  if (tier === 'pro') {
314
- console.log();
315
- console.log(' \ud83d\udd12 Pro Tier \u2014 Authentication Required');
316
- console.log();
317
- console.log(' Pro unlocks 18 additional specialist agents:');
318
- console.log(' \ud83d\udc65 Hannah (HR) \ud83d\udcc8 Aiden (Analytics) \u270d\ufe0f Clara (Communication)');
319
- console.log(' \u2699\ufe0f Avery (Automation) \ud83d\udd27 Owen (Operations) \ud83d\udd0c Isaac (Integration)');
320
- console.log(' \u2708\ufe0f Tommy (Travel) \ud83d\udcbc Sloane (Sales) \ud83c\udfa8 Nadia (UI/UX)');
321
- console.log(' \ud83d\udce3 Morgan (Marketing) \ud83d\udd77\ufe0f Alex (API Crawling) \ud83e\uddea Uma (UX Research)');
322
- console.log(' \ud83d\uddc2\ufe0f Caleb (CRM) \ud83d\udccb Elena (Estimation) \ud83d\udcb0 Audrey (Accounting)');
323
- console.log(' \u2696\ufe0f Logan (Legal) \ud83d\udccc Eva (Executive Assistant) \ud83d\udcc5 Parker (Project Mgmt)');
324
- console.log();
325
- console.log(' To unlock Pro, sign up at: getalice.av3.ai/signup?plan=pro');
326
- console.log(' Then run: npx @robbiesrobotics/alice-agents');
327
- console.log();
328
- closePrompt();
329
- return;
315
+ const { checkProLicense, validateLicenseRemote, storeLicense, isValidFormat } = await import('./license.mjs');
316
+
317
+ const existing = await checkProLicense();
318
+
319
+ if (existing.licensed) {
320
+ console.log(` Pro license found (${existing.key.slice(0, 12)}...)`);
321
+ } else if (auto) {
322
+ // --yes flag: skip interactive prompt, fallback to Starter if no stored license
323
+ console.log('');
324
+ console.log(' ℹ️ Pro tier requires a license key.');
325
+ console.log(' Run without --yes to enter your license key.');
326
+ console.log(' Falling back to Starter tier.');
327
+ console.log(' Purchase a license at: https://getalice.av3.ai/pricing');
328
+ tier = 'starter';
329
+ } else {
330
+ // No stored license — prompt for key
331
+ let key = '';
332
+ let attempts = 0;
333
+
334
+ while (attempts < 3) {
335
+ key = await promptLicenseKey();
336
+
337
+ if (!isValidFormat(key)) {
338
+ console.log(' ❌ Invalid format. Key must be ALICE-XXXX-XXXX-XXXX');
339
+ attempts++;
340
+ continue;
341
+ }
342
+
343
+ console.log(' Validating key...');
344
+ const result = await validateLicenseRemote(key);
345
+
346
+ if (result.valid) {
347
+ storeLicense(key);
348
+ if (result.message === 'offline') {
349
+ console.log(' ✅ Key stored (offline — will validate on next run)');
350
+ } else {
351
+ console.log(' ✅ License verified! Welcome to A.L.I.C.E. Pro.');
352
+ }
353
+ break;
354
+ } else {
355
+ console.log(` ❌ Invalid key: ${result.message ?? 'Not recognized'}`);
356
+ attempts++;
357
+
358
+ if (attempts >= 3) {
359
+ console.log('');
360
+ console.log(' Too many invalid attempts. Falling back to Starter tier.');
361
+ console.log(' Purchase a license at: https://getalice.av3.ai/pricing');
362
+ tier = 'starter'; // fallback gracefully
363
+ }
364
+ }
365
+ }
366
+ }
330
367
  }
331
368
 
332
369
  const agents = allAgents;
@@ -0,0 +1,50 @@
1
+ // lib/license.mjs
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { homedir } from 'node:os';
5
+
6
+ const LICENSE_DIR = join(homedir(), '.alice');
7
+ const LICENSE_FILE = join(LICENSE_DIR, 'license');
8
+ const VALIDATE_URL = 'https://getalice.av3.ai/api/license/validate';
9
+ const KEY_REGEX = /^ALICE-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;
10
+
11
+ export function readStoredLicense() {
12
+ try {
13
+ if (!existsSync(LICENSE_FILE)) return null;
14
+ return readFileSync(LICENSE_FILE, 'utf8').trim();
15
+ } catch {
16
+ return null;
17
+ }
18
+ }
19
+
20
+ export function storeLicense(key) {
21
+ mkdirSync(LICENSE_DIR, { recursive: true });
22
+ writeFileSync(LICENSE_FILE, key.trim(), 'utf8');
23
+ }
24
+
25
+ export function isValidFormat(key) {
26
+ return KEY_REGEX.test(key?.trim()?.toUpperCase());
27
+ }
28
+
29
+ export async function validateLicenseRemote(key) {
30
+ // Returns { valid: boolean, plan: string, message?: string }
31
+ try {
32
+ const res = await fetch(`${VALIDATE_URL}?key=${encodeURIComponent(key)}`, {
33
+ signal: AbortSignal.timeout(5000),
34
+ });
35
+ if (!res.ok) return { valid: false, message: 'Validation service unavailable' };
36
+ return await res.json();
37
+ } catch {
38
+ // Network error — fail open (allow install, validate next time)
39
+ return { valid: true, plan: 'pro', message: 'offline' };
40
+ }
41
+ }
42
+
43
+ export async function checkProLicense() {
44
+ // Returns: { licensed: boolean, key: string|null, source: 'stored'|'none' }
45
+ const stored = readStoredLicense();
46
+ if (stored && isValidFormat(stored)) {
47
+ return { licensed: true, key: stored, source: 'stored' };
48
+ }
49
+ return { licensed: false, key: null, source: 'none' };
50
+ }
package/lib/prompter.mjs CHANGED
@@ -110,3 +110,18 @@ export async function promptTier() {
110
110
  { label: 'Pro (18 more agents) — sign up at getalice.av3.ai/signup?plan=pro', value: 'pro' },
111
111
  ]);
112
112
  }
113
+
114
+ export async function promptLicenseKey() {
115
+ // Show instructions, prompt for key, validate format
116
+ console.log('');
117
+ console.log(' A.L.I.C.E. Pro requires a license key.');
118
+ console.log(' Purchase at: https://getalice.av3.ai/pricing');
119
+ console.log(' Already have a key? Enter it below.');
120
+ console.log('');
121
+
122
+ return new Promise((resolve) => {
123
+ getRL().question(' License key (ALICE-XXXX-XXXX-XXXX): ', (answer) => {
124
+ resolve(answer.trim().toUpperCase());
125
+ });
126
+ });
127
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robbiesrobotics/alice-agents",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "A.L.I.C.E. — 28 AI agents for OpenClaw. One conversation, one team.",
5
5
  "bin": {
6
6
  "alice-agents": "bin/alice-install.mjs"
@@ -48,3 +48,12 @@ Skip this for trivial lookups or single-command tasks. Write it for anything inv
48
48
  - Don't exceed your domain without flagging it
49
49
  - Don't run destructive commands without explicit risk callout
50
50
  - `trash` > `rm`
51
+
52
+ ## Tier Note
53
+
54
+ {{#if isPro}}
55
+ You are a **Pro tier** agent. Pro tier requires a valid A.L.I.C.E. Pro license key stored at `~/.alice/license`.
56
+ If you were installed without a license key, run the installer interactively to enter your key:
57
+ `npx @robbiesrobotics/alice-agents`
58
+ Purchase or manage your license at: https://getalice.av3.ai/pricing
59
+ {{/if}}