aios-core 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/.aios-core/.session/current-session.json +14 -0
  2. package/.aios-core/core/registry/registry-schema.json +166 -166
  3. package/.aios-core/core/registry/service-registry.json +6585 -6585
  4. package/.aios-core/data/entity-registry.yaml +208 -8
  5. package/.aios-core/data/registry-update-log.jsonl +165 -0
  6. package/.aios-core/development/scripts/approval-workflow.js +642 -642
  7. package/.aios-core/development/scripts/backup-manager.js +606 -606
  8. package/.aios-core/development/scripts/branch-manager.js +389 -389
  9. package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
  10. package/.aios-core/development/scripts/commit-message-generator.js +849 -849
  11. package/.aios-core/development/scripts/conflict-resolver.js +674 -674
  12. package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
  13. package/.aios-core/development/scripts/diff-generator.js +351 -351
  14. package/.aios-core/development/scripts/elicitation-engine.js +384 -384
  15. package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
  16. package/.aios-core/development/scripts/git-wrapper.js +461 -461
  17. package/.aios-core/development/scripts/manifest-preview.js +244 -244
  18. package/.aios-core/development/scripts/metrics-tracker.js +775 -775
  19. package/.aios-core/development/scripts/modification-validator.js +554 -554
  20. package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
  21. package/.aios-core/development/scripts/performance-analyzer.js +757 -757
  22. package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
  23. package/.aios-core/development/scripts/rollback-handler.js +530 -530
  24. package/.aios-core/development/scripts/security-checker.js +358 -358
  25. package/.aios-core/development/scripts/template-engine.js +239 -239
  26. package/.aios-core/development/scripts/template-validator.js +278 -278
  27. package/.aios-core/development/scripts/test-generator.js +843 -843
  28. package/.aios-core/development/scripts/transaction-manager.js +589 -589
  29. package/.aios-core/development/scripts/usage-tracker.js +673 -673
  30. package/.aios-core/development/scripts/validate-filenames.js +226 -226
  31. package/.aios-core/development/scripts/version-tracker.js +526 -526
  32. package/.aios-core/development/scripts/yaml-validator.js +396 -396
  33. package/.aios-core/development/tasks/validate-next-story.md +99 -2
  34. package/.aios-core/development/templates/service-template/README.md.hbs +158 -158
  35. package/.aios-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -237
  36. package/.aios-core/development/templates/service-template/client.ts.hbs +403 -403
  37. package/.aios-core/development/templates/service-template/errors.ts.hbs +182 -182
  38. package/.aios-core/development/templates/service-template/index.ts.hbs +120 -120
  39. package/.aios-core/development/templates/service-template/package.json.hbs +87 -87
  40. package/.aios-core/development/templates/service-template/types.ts.hbs +145 -145
  41. package/.aios-core/development/templates/squad-template/LICENSE +21 -21
  42. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +335 -0
  43. package/.aios-core/docs/component-creation-guide.md +458 -0
  44. package/.aios-core/docs/session-update-pattern.md +307 -0
  45. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +1963 -0
  46. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +1190 -0
  47. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +439 -0
  48. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +5398 -0
  49. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +523 -0
  50. package/.aios-core/docs/template-syntax.md +267 -0
  51. package/.aios-core/docs/troubleshooting-guide.md +625 -0
  52. package/.aios-core/infrastructure/templates/aios-sync.yaml.template +193 -193
  53. package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
  54. package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
  55. package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
  56. package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
  57. package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
  58. package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
  59. package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
  60. package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
  61. package/.aios-core/infrastructure/tests/utilities-audit-results.json +501 -0
  62. package/.aios-core/install-manifest.yaml +101 -101
  63. package/.aios-core/local-config.yaml.template +70 -70
  64. package/.aios-core/manifests/agents.csv +29 -0
  65. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  66. package/.aios-core/manifests/tasks.csv +198 -0
  67. package/.aios-core/manifests/workers.csv +204 -0
  68. package/.aios-core/monitor/hooks/lib/__init__.py +1 -1
  69. package/.aios-core/monitor/hooks/lib/enrich.py +58 -58
  70. package/.aios-core/monitor/hooks/lib/send_event.py +47 -47
  71. package/.aios-core/monitor/hooks/notification.py +29 -29
  72. package/.aios-core/monitor/hooks/post_tool_use.py +45 -45
  73. package/.aios-core/monitor/hooks/pre_compact.py +29 -29
  74. package/.aios-core/monitor/hooks/pre_tool_use.py +40 -40
  75. package/.aios-core/monitor/hooks/stop.py +29 -29
  76. package/.aios-core/monitor/hooks/subagent_stop.py +29 -29
  77. package/.aios-core/monitor/hooks/user_prompt_submit.py +38 -38
  78. package/.aios-core/product/templates/adr.hbs +125 -125
  79. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  80. package/.aios-core/product/templates/dbdr.hbs +241 -241
  81. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  82. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  83. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  84. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  85. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  86. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  87. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  88. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  89. package/.aios-core/product/templates/epic.hbs +212 -212
  90. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  91. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  92. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  93. package/.aios-core/product/templates/pmdr.hbs +186 -186
  94. package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
  95. package/.aios-core/product/templates/prd.hbs +201 -201
  96. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  97. package/.aios-core/product/templates/story.hbs +263 -263
  98. package/.aios-core/product/templates/task.hbs +170 -170
  99. package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
  100. package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
  101. package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
  102. package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
  103. package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
  104. package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
  105. package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
  106. package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
  107. package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
  108. package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
  109. package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
  110. package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
  111. package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
  112. package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
  113. package/.aios-core/product/templates/tmpl-view.sql +177 -177
  114. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  115. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  116. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  117. package/.aios-core/scripts/pm.sh +0 -0
  118. package/.claude/hooks/enforce-architecture-first.py +196 -196
  119. package/.claude/hooks/mind-clone-governance.py +192 -192
  120. package/.claude/hooks/read-protection.py +151 -151
  121. package/.claude/hooks/slug-validation.py +176 -176
  122. package/.claude/hooks/sql-governance.py +182 -182
  123. package/.claude/hooks/write-path-validation.py +194 -194
  124. package/.claude/rules/agent-authority.md +105 -0
  125. package/.claude/rules/coderabbit-integration.md +93 -0
  126. package/.claude/rules/ids-principles.md +112 -0
  127. package/.claude/rules/story-lifecycle.md +139 -0
  128. package/.claude/rules/workflow-execution.md +150 -0
  129. package/LICENSE +48 -48
  130. package/bin/aios-minimal.js +0 -0
  131. package/bin/aios.js +0 -0
  132. package/package.json +1 -1
  133. package/packages/aios-install/bin/aios-install.js +0 -0
  134. package/packages/aios-install/bin/edmcp.js +0 -0
  135. package/packages/aios-pro-cli/bin/aios-pro.js +0 -0
  136. package/packages/installer/src/wizard/pro-setup.js +433 -49
  137. package/scripts/check-markdown-links.py +352 -352
  138. package/scripts/code-intel-health-check.js +343 -0
  139. package/scripts/dashboard-parallel-dev.sh +0 -0
  140. package/scripts/dashboard-parallel-phase3.sh +0 -0
  141. package/scripts/dashboard-parallel-phase4.sh +0 -0
  142. package/scripts/glue/README.md +355 -0
  143. package/scripts/glue/compose-agent-prompt.cjs +362 -0
  144. package/scripts/install-monitor-hooks.sh +0 -0
  145. package/.aios-core/lib/build.json +0 -1
@@ -2,10 +2,15 @@
2
2
  * Pro Installation Wizard with License Gate
3
3
  *
4
4
  * 3-step wizard: (1) License Gate, (2) Install/Scaffold, (3) Verify
5
- * Supports interactive mode, CI mode (AIOS_PRO_KEY env var), and lazy import.
5
+ * Supports interactive mode, CI mode (AIOS_PRO_KEY/AIOS_PRO_EMAIL env vars), and lazy import.
6
+ *
7
+ * License Gate supports two activation methods:
8
+ * - Email + Password authentication (recommended, PRO-11)
9
+ * - License key (legacy, PRO-6)
6
10
  *
7
11
  * @module wizard/pro-setup
8
12
  * @story INS-3.2 — Implement Pro Installation Wizard with License Gate
13
+ * @story PRO-11 — Email Authentication & Buyer-Based Pro Activation
9
14
  */
10
15
 
11
16
  'use strict';
@@ -35,6 +40,26 @@ const LICENSE_KEY_PATTERN = /^PRO-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4
35
40
  */
36
41
  const MAX_RETRIES = 3;
37
42
 
43
+ /**
44
+ * Email verification polling interval in milliseconds.
45
+ */
46
+ const VERIFY_POLL_INTERVAL_MS = 5000;
47
+
48
+ /**
49
+ * Email verification polling timeout in milliseconds (10 minutes).
50
+ */
51
+ const VERIFY_POLL_TIMEOUT_MS = 10 * 60 * 1000;
52
+
53
+ /**
54
+ * Minimum password length.
55
+ */
56
+ const MIN_PASSWORD_LENGTH = 8;
57
+
58
+ /**
59
+ * Email format regex.
60
+ */
61
+ const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
62
+
38
63
  /**
39
64
  * Detect CI environment.
40
65
  *
@@ -145,78 +170,425 @@ function loadProScaffolder() {
145
170
  }
146
171
 
147
172
  /**
148
- * Step 1: License Gate — validate license key.
173
+ * Step 1: License Gate — authenticate and validate license.
174
+ *
175
+ * Supports two activation methods:
176
+ * 1. Email + Password authentication (recommended, PRO-11)
177
+ * 2. License key (legacy, PRO-6)
149
178
  *
150
- * In CI mode, reads from AIOS_PRO_KEY env var.
151
- * In interactive mode, prompts with masked input.
179
+ * In CI mode, reads from AIOS_PRO_EMAIL + AIOS_PRO_PASSWORD or AIOS_PRO_KEY env vars.
180
+ * In interactive mode, prompts user to choose method.
152
181
  *
153
182
  * @param {Object} [options={}] - Options
154
183
  * @param {string} [options.key] - Pre-provided key (from CLI args or env)
184
+ * @param {string} [options.email] - Pre-provided email (from CLI args or env)
185
+ * @param {string} [options.password] - Pre-provided password (from CLI args or env)
155
186
  * @returns {Promise<Object>} Result with { success, key, activationResult }
156
187
  */
157
188
  async function stepLicenseGate(options = {}) {
158
- showStep(1, 3, 'License Validation');
189
+ showStep(1, 3, 'License Activation');
159
190
 
160
191
  const isCI = isCIEnvironment();
161
- let key = options.key || null;
162
192
 
163
- // CI mode: read from env var
164
- if (!key && isCI) {
165
- key = process.env.AIOS_PRO_KEY || null;
193
+ // CI mode: check env vars
194
+ if (isCI) {
195
+ return stepLicenseGateCI(options);
196
+ }
166
197
 
167
- if (!key) {
168
- return {
169
- success: false,
170
- error: 'CI mode: AIOS_PRO_KEY environment variable not set.',
171
- };
172
- }
198
+ // Pre-provided key (from CLI args)
199
+ if (options.key) {
200
+ return stepLicenseGateWithKey(options.key);
201
+ }
202
+
203
+ // Pre-provided email credentials (from CLI args)
204
+ if (options.email && options.password) {
205
+ return authenticateWithEmail(options.email, options.password);
173
206
  }
174
207
 
175
- // Interactive mode: prompt for key
176
- if (!key && !isCI) {
177
- const inquirer = require('inquirer');
208
+ // Interactive mode: prompt for method
209
+ const inquirer = require('inquirer');
178
210
 
179
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
180
- const { licenseKey } = await inquirer.prompt([
211
+ const { method } = await inquirer.prompt([
212
+ {
213
+ type: 'list',
214
+ name: 'method',
215
+ message: colors.primary('How would you like to activate Pro?'),
216
+ choices: [
181
217
  {
182
- type: 'password',
183
- name: 'licenseKey',
184
- message: colors.primary('Enter your Pro license key:'),
185
- mask: '*',
186
- validate: (input) => {
187
- if (!input || !input.trim()) {
188
- return 'License key is required';
189
- }
190
- if (!validateKeyFormat(input)) {
191
- return 'Invalid format. Expected: PRO-XXXX-XXXX-XXXX-XXXX';
192
- }
193
- return true;
194
- },
218
+ name: 'Login with email and password (Recommended)',
219
+ value: 'email',
195
220
  },
196
- ]);
221
+ {
222
+ name: 'Enter license key',
223
+ value: 'key',
224
+ },
225
+ ],
226
+ },
227
+ ]);
228
+
229
+ if (method === 'email') {
230
+ return stepLicenseGateWithEmail();
231
+ }
232
+
233
+ return stepLicenseGateWithKeyInteractive();
234
+ }
235
+
236
+ /**
237
+ * CI mode license gate — reads from env vars.
238
+ *
239
+ * Priority: AIOS_PRO_EMAIL + AIOS_PRO_PASSWORD > AIOS_PRO_KEY
240
+ *
241
+ * @param {Object} options - Options with possible pre-provided credentials
242
+ * @returns {Promise<Object>} Result with { success, key, activationResult }
243
+ */
244
+ async function stepLicenseGateCI(options) {
245
+ const email = options.email || process.env.AIOS_PRO_EMAIL;
246
+ const password = options.password || process.env.AIOS_PRO_PASSWORD;
247
+ const key = options.key || process.env.AIOS_PRO_KEY;
248
+
249
+ // Prefer email auth over key
250
+ if (email && password) {
251
+ return authenticateWithEmail(email, password);
252
+ }
253
+
254
+ if (key) {
255
+ return stepLicenseGateWithKey(key);
256
+ }
257
+
258
+ return {
259
+ success: false,
260
+ error: 'CI mode: Set AIOS_PRO_EMAIL + AIOS_PRO_PASSWORD or AIOS_PRO_KEY environment variables.',
261
+ };
262
+ }
263
+
264
+ /**
265
+ * Interactive email/password license gate flow.
266
+ *
267
+ * Prompts for email, then checks if account exists to determine signup vs login.
268
+ *
269
+ * @returns {Promise<Object>} Result with { success, key, activationResult }
270
+ */
271
+ async function stepLicenseGateWithEmail() {
272
+ const inquirer = require('inquirer');
273
+
274
+ const { email } = await inquirer.prompt([
275
+ {
276
+ type: 'input',
277
+ name: 'email',
278
+ message: colors.primary('Email:'),
279
+ validate: (input) => {
280
+ if (!input || !input.trim()) {
281
+ return 'Email is required';
282
+ }
283
+ if (!EMAIL_PATTERN.test(input.trim())) {
284
+ return 'Please enter a valid email address';
285
+ }
286
+ return true;
287
+ },
288
+ },
289
+ ]);
290
+
291
+ const { password } = await inquirer.prompt([
292
+ {
293
+ type: 'password',
294
+ name: 'password',
295
+ message: colors.primary('Password:'),
296
+ mask: '*',
297
+ validate: (input) => {
298
+ if (!input || input.length < MIN_PASSWORD_LENGTH) {
299
+ return `Password must be at least ${MIN_PASSWORD_LENGTH} characters`;
300
+ }
301
+ return true;
302
+ },
303
+ },
304
+ ]);
305
+
306
+ return authenticateWithEmail(email.trim(), password);
307
+ }
308
+
309
+ /**
310
+ * Authenticate with email and password.
311
+ *
312
+ * Tries login first. If user doesn't exist, offers to create account.
313
+ * Handles email verification polling for new signups.
314
+ *
315
+ * @param {string} email - User email
316
+ * @param {string} password - User password
317
+ * @returns {Promise<Object>} Result with { success, key, activationResult }
318
+ */
319
+ async function authenticateWithEmail(email, password) {
320
+ const loader = module.exports._testing ? module.exports._testing.loadLicenseApi : loadLicenseApi;
321
+ const licenseModule = loader();
322
+
323
+ if (!licenseModule) {
324
+ return {
325
+ success: false,
326
+ error: 'Pro license module not available. Ensure @aios-fullstack/pro is installed.',
327
+ };
328
+ }
329
+
330
+ const { LicenseApiClient } = licenseModule;
331
+ const client = new LicenseApiClient();
332
+
333
+ // Check connectivity
334
+ const online = await client.isOnline();
335
+ if (!online) {
336
+ return {
337
+ success: false,
338
+ error: 'License server is unreachable. Check your internet connection and try again.',
339
+ };
340
+ }
341
+
342
+ // Try login first
343
+ const spinner = createSpinner('Authenticating...');
344
+ spinner.start();
345
+
346
+ let sessionToken;
347
+ let emailVerified;
348
+
349
+ try {
350
+ const loginResult = await client.login(email, password);
351
+ sessionToken = loginResult.sessionToken;
352
+ emailVerified = loginResult.emailVerified;
353
+ spinner.succeed('Authenticated successfully.');
354
+ } catch (loginError) {
355
+ // If invalid credentials, try signup for new users
356
+ if (loginError.code === 'INVALID_CREDENTIALS') {
357
+ spinner.info('No account found. Creating a new account...');
358
+
359
+ try {
360
+ await client.signup(email, password);
361
+ showSuccess('Account created. Verification email sent!');
362
+ emailVerified = false;
363
+
364
+ // Login after signup to get session token
365
+ const loginAfterSignup = await client.login(email, password);
366
+ sessionToken = loginAfterSignup.sessionToken;
367
+ } catch (signupError) {
368
+ if (signupError.code === 'EMAIL_ALREADY_REGISTERED') {
369
+ showError('An account exists with this email but the password is incorrect.');
370
+ showInfo('Forgot your password? Visit https://pro.synkra.ai/reset-password or contact support@synkra.ai');
371
+ return { success: false, error: signupError.message };
372
+ }
373
+ return { success: false, error: signupError.message };
374
+ }
375
+ } else if (loginError.code === 'AUTH_RATE_LIMITED') {
376
+ spinner.fail(loginError.message);
377
+ return { success: false, error: loginError.message };
378
+ } else {
379
+ spinner.fail(`Authentication failed: ${loginError.message}`);
380
+ return { success: false, error: loginError.message };
381
+ }
382
+ }
383
+
384
+ // Wait for email verification if needed
385
+ if (!emailVerified) {
386
+ const verifyResult = await waitForEmailVerification(client, sessionToken);
387
+ if (!verifyResult.success) {
388
+ return verifyResult;
389
+ }
390
+ }
391
+
392
+ // Activate Pro
393
+ return activateProByAuth(client, sessionToken);
394
+ }
197
395
 
198
- key = licenseKey.trim().toUpperCase();
396
+ /**
397
+ * Wait for email verification with polling.
398
+ *
399
+ * Polls the server every 5 seconds for up to 10 minutes.
400
+ * User can press R to resend verification email.
401
+ *
402
+ * @param {object} client - LicenseApiClient instance
403
+ * @param {string} sessionToken - Session token
404
+ * @returns {Promise<Object>} Result with { success }
405
+ */
406
+ async function waitForEmailVerification(client, sessionToken) {
407
+ console.log('');
408
+ showInfo('Waiting for email verification...');
409
+ showInfo('Open your email and click the verification link.');
410
+ console.log(colors.dim(' (Checking every 5 seconds... timeout in 10 minutes)'));
199
411
 
200
- // Validate with API
201
- const result = await validateKeyWithApi(key);
412
+ if (!isCIEnvironment()) {
413
+ console.log(colors.dim(' [Press R to resend verification email]'));
414
+ }
415
+
416
+ const startTime = Date.now();
417
+ let resendHint = false;
418
+
419
+ // Set up keyboard listener for resend (non-CI only)
420
+ let keyListener;
421
+ if (!isCIEnvironment() && process.stdin.setRawMode) {
422
+ process.stdin.setRawMode(true);
423
+ process.stdin.resume();
424
+ keyListener = (key) => {
425
+ if (key.toString().toLowerCase() === 'r') {
426
+ resendHint = true;
427
+ }
428
+ // Ctrl+C
429
+ if (key.toString() === '\u0003') {
430
+ cleanupKeyListener();
431
+ process.exit(0);
432
+ }
433
+ };
434
+ process.stdin.on('data', keyListener);
435
+ }
436
+
437
+ function cleanupKeyListener() {
438
+ if (keyListener) {
439
+ process.stdin.removeListener('data', keyListener);
440
+ if (process.stdin.setRawMode) {
441
+ process.stdin.setRawMode(false);
442
+ }
443
+ process.stdin.pause();
444
+ }
445
+ }
202
446
 
203
- if (result.success) {
204
- showSuccess(`License validated: ${maskLicenseKey(key)}`);
205
- return { success: true, key, activationResult: result.data };
447
+ try {
448
+ while (Date.now() - startTime < VERIFY_POLL_TIMEOUT_MS) {
449
+ // Handle resend request
450
+ if (resendHint) {
451
+ resendHint = false;
452
+ try {
453
+ await client.resendVerification(sessionToken);
454
+ showInfo('Verification email resent.');
455
+ } catch (error) {
456
+ showWarning(`Could not resend: ${error.message}`);
457
+ }
206
458
  }
207
459
 
208
- // Show error and retry
209
- const remaining = MAX_RETRIES - attempt;
210
- if (remaining > 0) {
211
- showError(`${result.error} (${remaining} attempt${remaining > 1 ? 's' : ''} remaining)`);
212
- } else {
213
- showError(`${result.error} no attempts remaining.`);
214
- return { success: false, error: result.error };
460
+ // Poll verification status
461
+ try {
462
+ const status = await client.checkEmailVerified(sessionToken);
463
+ if (status.verified) {
464
+ showSuccess('Email verified!');
465
+ return { success: true };
466
+ }
467
+ } catch {
468
+ // Polling failure is non-fatal, continue
215
469
  }
470
+
471
+ // Wait before next poll
472
+ await new Promise((resolve) => setTimeout(resolve, VERIFY_POLL_INTERVAL_MS));
473
+ }
474
+
475
+ // Timeout
476
+ showError('Email verification timed out after 10 minutes.');
477
+ showInfo('Run the installer again to retry verification.');
478
+ return { success: false, error: 'Email verification timed out.' };
479
+ } finally {
480
+ cleanupKeyListener();
481
+ }
482
+ }
483
+
484
+ /**
485
+ * Activate Pro using an authenticated session.
486
+ *
487
+ * @param {object} client - LicenseApiClient instance
488
+ * @param {string} sessionToken - Authenticated session token
489
+ * @returns {Promise<Object>} Result with { success, key, activationResult }
490
+ */
491
+ async function activateProByAuth(client, sessionToken) {
492
+ const spinner = createSpinner('Validating Pro subscription...');
493
+ spinner.start();
494
+
495
+ try {
496
+ // Generate machine fingerprint
497
+ const os = require('os');
498
+ const crypto = require('crypto');
499
+ const machineId = crypto
500
+ .createHash('sha256')
501
+ .update(`${os.hostname()}-${os.platform()}-${os.arch()}`)
502
+ .digest('hex')
503
+ .substring(0, 32);
504
+
505
+ // Read aios-core version
506
+ let aiosCoreVersion = 'unknown';
507
+ try {
508
+ const path = require('path');
509
+ const fs = require('fs');
510
+ const pkgPath = path.join(__dirname, '..', '..', '..', '..', 'package.json');
511
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
512
+ aiosCoreVersion = pkg.version || 'unknown';
513
+ } catch {
514
+ // Keep 'unknown'
515
+ }
516
+
517
+ const activationResult = await client.activateByAuth(sessionToken, machineId, aiosCoreVersion);
518
+
519
+ spinner.succeed(`Pro subscription confirmed! License: ${maskLicenseKey(activationResult.key)}`);
520
+ return { success: true, key: activationResult.key, activationResult };
521
+ } catch (error) {
522
+ if (error.code === 'NOT_A_BUYER') {
523
+ spinner.fail('No active Pro subscription found for this email.');
524
+ showInfo('Purchase Pro at https://pro.synkra.ai');
525
+ return { success: false, error: error.message };
526
+ }
527
+ if (error.code === 'SEAT_LIMIT_EXCEEDED') {
528
+ spinner.fail(error.message);
529
+ showInfo('Deactivate another device or upgrade your license.');
530
+ return { success: false, error: error.message };
531
+ }
532
+
533
+ spinner.fail(`Activation failed: ${error.message}`);
534
+ return { success: false, error: error.message };
535
+ }
536
+ }
537
+
538
+ /**
539
+ * Interactive license key gate (legacy flow).
540
+ *
541
+ * @returns {Promise<Object>} Result with { success, key, activationResult }
542
+ */
543
+ async function stepLicenseGateWithKeyInteractive() {
544
+ const inquirer = require('inquirer');
545
+
546
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
547
+ const { licenseKey } = await inquirer.prompt([
548
+ {
549
+ type: 'password',
550
+ name: 'licenseKey',
551
+ message: colors.primary('Enter your Pro license key:'),
552
+ mask: '*',
553
+ validate: (input) => {
554
+ if (!input || !input.trim()) {
555
+ return 'License key is required';
556
+ }
557
+ if (!validateKeyFormat(input)) {
558
+ return 'Invalid format. Expected: PRO-XXXX-XXXX-XXXX-XXXX';
559
+ }
560
+ return true;
561
+ },
562
+ },
563
+ ]);
564
+
565
+ const key = licenseKey.trim().toUpperCase();
566
+ const result = await validateKeyWithApi(key);
567
+
568
+ if (result.success) {
569
+ showSuccess(`License validated: ${maskLicenseKey(key)}`);
570
+ return { success: true, key, activationResult: result.data };
571
+ }
572
+
573
+ const remaining = MAX_RETRIES - attempt;
574
+ if (remaining > 0) {
575
+ showError(`${result.error} (${remaining} attempt${remaining > 1 ? 's' : ''} remaining)`);
576
+ } else {
577
+ showError(`${result.error} — no attempts remaining.`);
578
+ return { success: false, error: result.error };
216
579
  }
217
580
  }
218
581
 
219
- // Validate key format
582
+ return { success: false, error: 'Maximum attempts reached.' };
583
+ }
584
+
585
+ /**
586
+ * Validate with pre-provided license key (CI or CLI arg).
587
+ *
588
+ * @param {string} key - License key
589
+ * @returns {Promise<Object>} Result with { success, key, activationResult }
590
+ */
591
+ async function stepLicenseGateWithKey(key) {
220
592
  if (!validateKeyFormat(key)) {
221
593
  return {
222
594
  success: false,
@@ -224,7 +596,6 @@ async function stepLicenseGate(options = {}) {
224
596
  };
225
597
  }
226
598
 
227
- // Validate with API
228
599
  const spinner = createSpinner(`Validating license ${maskLicenseKey(key)}...`);
229
600
  spinner.start();
230
601
 
@@ -489,6 +860,8 @@ async function runProWizard(options = {}) {
489
860
  // Step 1: License Gate
490
861
  const licenseResult = await stepLicenseGate({
491
862
  key: options.key || process.env.AIOS_PRO_KEY,
863
+ email: options.email || process.env.AIOS_PRO_EMAIL,
864
+ password: options.password || process.env.AIOS_PRO_PASSWORD,
492
865
  });
493
866
 
494
867
  if (!licenseResult.success) {
@@ -538,10 +911,21 @@ module.exports = {
538
911
  // Internal helpers exported for testing
539
912
  _testing: {
540
913
  validateKeyWithApi,
914
+ authenticateWithEmail,
915
+ waitForEmailVerification,
916
+ activateProByAuth,
917
+ stepLicenseGateCI,
918
+ stepLicenseGateWithKey,
919
+ stepLicenseGateWithKeyInteractive,
920
+ stepLicenseGateWithEmail,
541
921
  loadLicenseApi,
542
922
  loadFeatureGate,
543
923
  loadProScaffolder,
544
924
  MAX_RETRIES,
545
925
  LICENSE_KEY_PATTERN,
926
+ EMAIL_PATTERN,
927
+ MIN_PASSWORD_LENGTH,
928
+ VERIFY_POLL_INTERVAL_MS,
929
+ VERIFY_POLL_TIMEOUT_MS,
546
930
  },
547
931
  };