aiox-core 5.0.7 → 5.0.8

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 (191) hide show
  1. package/.aiox-core/cli/commands/pro/buyer.js +379 -0
  2. package/.aiox-core/cli/commands/pro/index.js +191 -52
  3. package/.aiox-core/cli/commands/validate/index.js +2 -0
  4. package/.aiox-core/core/code-intel/helpers/dev-helper.js +1 -1
  5. package/.aiox-core/core/code-intel/helpers/devops-helper.js +0 -1
  6. package/.aiox-core/core/code-intel/helpers/planning-helper.js +1 -1
  7. package/.aiox-core/core/code-intel/helpers/qa-helper.js +2 -2
  8. package/.aiox-core/core/config/schemas/framework-config.schema.json +1 -0
  9. package/.aiox-core/core/config/template-overrides.js +1 -1
  10. package/.aiox-core/core/doctor/checks/ide-sync.js +81 -25
  11. package/.aiox-core/core/doctor/checks/rules-files.js +0 -1
  12. package/.aiox-core/core/doctor/checks/skills-count.js +83 -15
  13. package/.aiox-core/core/graph-dashboard/cli.js +1 -2
  14. package/.aiox-core/core/graph-dashboard/data-sources/code-intel-source.js +1 -1
  15. package/.aiox-core/core/ids/layer-classifier.js +1 -1
  16. package/.aiox-core/core/pro/pro-updater.js +578 -0
  17. package/.aiox-core/core/synapse/context/context-tracker.js +107 -9
  18. package/.aiox-core/core/synapse/layers/layer-processor.js +1 -1
  19. package/.aiox-core/core-config.yaml +15 -1
  20. package/.aiox-core/data/capability-detection.js +15 -15
  21. package/.aiox-core/data/entity-registry.yaml +18 -2
  22. package/.aiox-core/data/registry-update-log.jsonl +5 -0
  23. package/.aiox-core/data/tok3-token-comparison.js +0 -4
  24. package/.aiox-core/data/tool-search-validation.js +1 -1
  25. package/.aiox-core/development/agents/aiox-master.md +44 -6
  26. package/.aiox-core/development/agents/data-engineer.md +4 -4
  27. package/.aiox-core/development/agents/devops.md +52 -2
  28. package/.aiox-core/development/agents/po.md +1 -1
  29. package/.aiox-core/development/agents/qa.md +5 -11
  30. package/.aiox-core/development/agents/sm.md +3 -3
  31. package/.aiox-core/development/agents/ux-design-expert.md +1 -1
  32. package/.aiox-core/development/scripts/unified-activation-pipeline.js +29 -3
  33. package/.aiox-core/development/tasks/dev-develop-story.md +46 -7
  34. package/.aiox-core/development/tasks/devops-pro-access-grant.md +93 -0
  35. package/.aiox-core/development/tasks/devops-pro-activate.md +42 -0
  36. package/.aiox-core/development/tasks/devops-pro-check-access.md +34 -0
  37. package/.aiox-core/development/tasks/devops-pro-request-reset.md +34 -0
  38. package/.aiox-core/development/tasks/devops-pro-resend-verification.md +32 -0
  39. package/.aiox-core/development/tasks/devops-pro-reset-password.md +36 -0
  40. package/.aiox-core/development/tasks/devops-pro-validate-login.md +36 -0
  41. package/.aiox-core/development/tasks/devops-pro-verify-status.md +33 -0
  42. package/.aiox-core/development/tasks/qa-gate.md +54 -4
  43. package/.aiox-core/development/tasks/validate-next-story.md +39 -2
  44. package/.aiox-core/framework-config.yaml +1 -0
  45. package/.aiox-core/infrastructure/scripts/codex-skills-sync/README.md +69 -0
  46. package/.aiox-core/infrastructure/scripts/codex-skills-sync/bootstrap.js +727 -0
  47. package/.aiox-core/infrastructure/scripts/codex-skills-sync/index.js +10 -0
  48. package/.aiox-core/infrastructure/scripts/codex-skills-sync/validate.js +65 -4
  49. package/.aiox-core/infrastructure/scripts/generate-settings-json.js +29 -4
  50. package/.aiox-core/infrastructure/scripts/ide-sync/agent-parser.js +4 -0
  51. package/.aiox-core/infrastructure/scripts/ide-sync/index.js +67 -7
  52. package/.aiox-core/infrastructure/scripts/ide-sync/transformers/claude-code.js +145 -3
  53. package/.aiox-core/infrastructure/scripts/repair-agent-references.js +263 -0
  54. package/.aiox-core/infrastructure/scripts/validate-claude-integration.js +60 -8
  55. package/.aiox-core/infrastructure/scripts/validate-paths.js +13 -0
  56. package/.aiox-core/install-manifest.yaml +134 -82
  57. package/.aiox-core/utils/filters/index.js +2 -1
  58. package/.claude/commands/AIOX/agents/aiox-master.md +21 -0
  59. package/.claude/commands/AIOX/agents/analyst.md +21 -0
  60. package/.claude/commands/AIOX/agents/architect.md +21 -0
  61. package/.claude/commands/AIOX/agents/data-engineer.md +21 -0
  62. package/.claude/commands/AIOX/agents/dev.md +21 -0
  63. package/.claude/commands/AIOX/agents/devops.md +21 -0
  64. package/.claude/commands/AIOX/agents/pm.md +21 -0
  65. package/.claude/commands/AIOX/agents/po.md +21 -0
  66. package/.claude/commands/AIOX/agents/qa.md +21 -0
  67. package/.claude/commands/AIOX/agents/sm.md +21 -0
  68. package/.claude/commands/AIOX/agents/squad-creator.md +21 -0
  69. package/.claude/commands/AIOX/agents/ux-design-expert.md +21 -0
  70. package/.claude/commands/AIOX/scripts/agent-config-loader.js +624 -0
  71. package/.claude/commands/AIOX/scripts/generate-greeting.js +160 -0
  72. package/.claude/commands/AIOX/scripts/greeting-builder.js +866 -0
  73. package/.claude/commands/AIOX/scripts/session-context-loader.js +286 -0
  74. package/.claude/commands/AIOX/stories/story-6.1.4.md +1404 -0
  75. package/.claude/commands/cohort-squad/agents/cohort-manager.md +156 -0
  76. package/.claude/commands/design-system/agents/brad-frost.md +1097 -0
  77. package/.claude/commands/design-system/agents/dan-mall.md +857 -0
  78. package/.claude/commands/design-system/agents/dave-malouf.md +2272 -0
  79. package/.claude/commands/design-system/agents/design-chief.md +102 -0
  80. package/.claude/commands/design-system/agents/nano-banana-generator.md +162 -0
  81. package/.claude/commands/greet.md +101 -0
  82. package/.claude/commands/synapse/manager.md +75 -0
  83. package/.claude/commands/synapse/tasks/add-rule.md +94 -0
  84. package/.claude/commands/synapse/tasks/create-command.md +109 -0
  85. package/.claude/commands/synapse/tasks/create-domain.md +127 -0
  86. package/.claude/commands/synapse/tasks/diagnose-synapse.md +245 -0
  87. package/.claude/commands/synapse/tasks/edit-rule.md +109 -0
  88. package/.claude/commands/synapse/tasks/suggest-domain.md +116 -0
  89. package/.claude/commands/synapse/tasks/toggle-domain.md +83 -0
  90. package/.claude/commands/synapse/templates/domain-template +8 -0
  91. package/.claude/commands/synapse/templates/manifest-entry-template +4 -0
  92. package/.claude/commands/synapse/utils/manifest-parser-reference.md +134 -0
  93. package/.claude/hooks/precompact-session-digest.cjs +2 -2
  94. package/.claude/skills/AIOX/agents/aiox-master/SKILL.md +511 -0
  95. package/.claude/skills/AIOX/agents/analyst/SKILL.md +281 -0
  96. package/.claude/skills/AIOX/agents/architect/SKILL.md +482 -0
  97. package/.claude/skills/AIOX/agents/data-engineer/SKILL.md +503 -0
  98. package/.claude/skills/AIOX/agents/dev/SKILL.md +568 -0
  99. package/.claude/skills/AIOX/agents/devops/SKILL.md +597 -0
  100. package/.claude/skills/AIOX/agents/pm/SKILL.md +385 -0
  101. package/.claude/skills/AIOX/agents/po/SKILL.md +343 -0
  102. package/.claude/skills/AIOX/agents/qa/SKILL.md +451 -0
  103. package/.claude/skills/AIOX/agents/sm/SKILL.md +295 -0
  104. package/.claude/skills/AIOX/agents/squad-creator/SKILL.md +352 -0
  105. package/.claude/skills/AIOX/agents/ux-design-expert/SKILL.md +503 -0
  106. package/.claude/skills/architect-first/SKILL.md +275 -0
  107. package/.claude/skills/architect-first/assets/architecture-template.md +505 -0
  108. package/.claude/skills/architect-first/assets/config-template.yaml +351 -0
  109. package/.claude/skills/architect-first/references/architecture-checklist.md +216 -0
  110. package/.claude/skills/architect-first/references/pre-implementation-checklist.md +119 -0
  111. package/.claude/skills/architect-first/references/stop-rules-guide.md +291 -0
  112. package/.claude/skills/architect-first/references/testing-strategy-guide.md +477 -0
  113. package/.claude/skills/architect-first/scripts/architecture_validator.py +490 -0
  114. package/.claude/skills/architect-first/scripts/check_coupling.py +306 -0
  115. package/.claude/skills/architect-first/scripts/validate_risk_mitigation.py +382 -0
  116. package/.claude/skills/checklist-runner/SKILL.md +113 -0
  117. package/.claude/skills/clone-mind.md +329 -0
  118. package/.claude/skills/coderabbit-review/SKILL.md +106 -0
  119. package/.claude/skills/course-generation-workflow.md +76 -0
  120. package/.claude/skills/enhance-workflow.md +466 -0
  121. package/.claude/skills/mcp-builder/LICENSE.txt +202 -0
  122. package/.claude/skills/mcp-builder/SKILL.md +328 -0
  123. package/.claude/skills/mcp-builder/reference/evaluation.md +602 -0
  124. package/.claude/skills/mcp-builder/reference/mcp_best_practices.md +915 -0
  125. package/.claude/skills/mcp-builder/reference/node_mcp_server.md +916 -0
  126. package/.claude/skills/mcp-builder/reference/python_mcp_server.md +752 -0
  127. package/.claude/skills/mcp-builder/scripts/connections.py +151 -0
  128. package/.claude/skills/mcp-builder/scripts/evaluation.py +373 -0
  129. package/.claude/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  130. package/.claude/skills/mcp-builder/scripts/requirements.txt +2 -0
  131. package/.claude/skills/ralph.md +181 -0
  132. package/.claude/skills/skill-creator/LICENSE.txt +202 -0
  133. package/.claude/skills/skill-creator/SKILL.md +209 -0
  134. package/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
  135. package/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
  136. package/.claude/skills/skill-creator/scripts/quick_validate.py +65 -0
  137. package/.claude/skills/squad.md +301 -0
  138. package/.claude/skills/synapse/SKILL.md +132 -0
  139. package/.claude/skills/synapse/assets/README.md +50 -0
  140. package/.claude/skills/synapse/references/brackets.md +100 -0
  141. package/.claude/skills/synapse/references/commands.md +118 -0
  142. package/.claude/skills/synapse/references/domains.md +126 -0
  143. package/.claude/skills/synapse/references/layers.md +186 -0
  144. package/.claude/skills/synapse/references/manifest.md +142 -0
  145. package/.claude/skills/tech-search/SKILL.md +431 -0
  146. package/.claude/skills/tech-search/prompts/page-extract.md +133 -0
  147. package/README.en.md +2 -2
  148. package/README.md +8 -2
  149. package/bin/aiox.js +55 -4
  150. package/bin/utils/framework-guard.js +4 -2
  151. package/bin/utils/pro-detector.js +119 -28
  152. package/bin/utils/validate-publish.js +6 -6
  153. package/docs/aiox-agent-flows/devops-system.md +18 -0
  154. package/docs/aiox-workflows/README.md +1 -0
  155. package/docs/aiox-workflows/pro-access-grant-workflow.md +218 -0
  156. package/docs/guides/pro/access-grant-ops-playbook.md +370 -0
  157. package/docs/guides/pro/install-gate-setup.md +12 -6
  158. package/docs/guides/pro/squad-creator-handoff-pro-access-ops.md +134 -0
  159. package/docs/guides/supabase-ops-handoff.md +768 -0
  160. package/package.json +12 -1
  161. package/packages/aiox-pro-cli/bin/aiox-pro.js +33 -12
  162. package/packages/installer/src/config/configure-environment.js +118 -50
  163. package/packages/installer/src/installer/aiox-core-installer.js +124 -27
  164. package/packages/installer/src/installer/brownfield-upgrader.js +66 -9
  165. package/packages/installer/src/installer/dependency-installer.js +4 -0
  166. package/packages/installer/src/pro/pro-scaffolder.js +5 -5
  167. package/packages/installer/src/updater/index.js +151 -10
  168. package/packages/installer/src/wizard/ide-config-generator.js +73 -7
  169. package/packages/installer/src/wizard/index.js +119 -31
  170. package/packages/installer/src/wizard/pro-setup.js +118 -47
  171. package/packages/installer/src/wizard/validation/validators/dependency-validator.js +32 -25
  172. package/packages/installer/src/wizard/validation/validators/file-structure-validator.js +26 -0
  173. package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +84 -1
  174. package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +1 -1
  175. package/packages/installer/tests/unit/doctor/doctor-checks.test.js +85 -19
  176. package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +4 -4
  177. package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +5 -5
  178. package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +4 -4
  179. package/packages/installer/tests/unit/merger/yaml-merger.test.js +11 -11
  180. package/pro/README.md +12 -1
  181. package/pro/license/index.js +3 -11
  182. package/pro/license/license-api.js +25 -0
  183. package/pro/license/license-cache.js +135 -31
  184. package/pro/license/license-crypto.js +59 -3
  185. package/pro/package.json +5 -4
  186. package/pro/squads/README.md +16 -16
  187. package/pro/squads/index.js +1 -1
  188. package/scripts/e2e/installed-skills-smoke.js +264 -0
  189. package/scripts/package-synapse.js +3 -3
  190. package/scripts/validate-package-completeness.js +8 -11
  191. package/.aiox-core/lib/build.json +0 -1
@@ -0,0 +1,379 @@
1
+ /**
2
+ * Pro Buyer Subcommand Module
3
+ *
4
+ * CLI commands for AIOX Pro buyer validation and management.
5
+ * Consumes aiox-license-server via pro/license/license-api.js.
6
+ *
7
+ * Subcommands (Wave 1 — this file):
8
+ * aiox pro buyer validate --email <E> [--json]
9
+ * aiox pro buyer validate-batch --file <F> [--concurrency N] [--json]
10
+ *
11
+ * Subcommands (Wave 2 — depends on POST /api/v1/admin/buyers/register in
12
+ * aiox-license-server; not yet implemented):
13
+ * aiox pro buyer register --email <E> --name <N> [--cpf <C>] [--yes] [--json]
14
+ *
15
+ * @module cli/commands/pro/buyer
16
+ * @story 123.8 — Cohort Buyer CLI Migration
17
+ * @see docs/architecture/design-cohort-buyer-cli-migration.md
18
+ */
19
+
20
+ 'use strict';
21
+
22
+ const { Command } = require('commander');
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+
26
+ // Dynamic license path resolution — duplicates pattern from pro/index.js so
27
+ // buyer.js can be loaded independently. Kept intentionally self-contained.
28
+ //
29
+ // Resolution order (matches pro/index.js and pro-setup.js):
30
+ // 1. Bundled pro/ (framework-dev / npx context with submodule)
31
+ // 2. npm canonical scope (@aiox-squads/pro)
32
+ // 3. npm legacy scopes (@aiox-fullstack/pro, @aios-fullstack/pro)
33
+ // 4. cwd node_modules under either scope
34
+ const PRO_PACKAGE_CANONICAL = '@aiox-squads/pro';
35
+ const PRO_PACKAGE_FALLBACK = '@aiox-fullstack/pro';
36
+ const PRO_PACKAGE_LEGACY = '@aios-fullstack/pro';
37
+ const PRO_PACKAGES = [PRO_PACKAGE_CANONICAL, PRO_PACKAGE_FALLBACK, PRO_PACKAGE_LEGACY];
38
+
39
+ function resolveLicensePath() {
40
+ const relativePath = path.resolve(__dirname, '..', '..', '..', '..', 'pro', 'license');
41
+ if (fs.existsSync(relativePath)) {
42
+ return relativePath;
43
+ }
44
+
45
+ // Try canonical first, then fallback scope via require.resolve
46
+ for (const pkgName of PRO_PACKAGES) {
47
+ try {
48
+ const proPkg = require.resolve(`${pkgName}/package.json`);
49
+ const proDir = path.dirname(proPkg);
50
+ const npmPath = path.join(proDir, 'license');
51
+ if (fs.existsSync(npmPath)) {
52
+ return npmPath;
53
+ }
54
+ } catch {
55
+ // package not installed under this scope — try next
56
+ }
57
+ }
58
+
59
+ // cwd fallback (when require.resolve doesn't see the package, e.g., npx context)
60
+ for (const pkgName of PRO_PACKAGES) {
61
+ const [scope, pkg] = pkgName.split('/');
62
+ const cwdPath = path.join(process.cwd(), 'node_modules', scope, pkg, 'license');
63
+ if (fs.existsSync(cwdPath)) {
64
+ return cwdPath;
65
+ }
66
+ }
67
+
68
+ return relativePath;
69
+ }
70
+
71
+ const licensePath = resolveLicensePath();
72
+
73
+ function loadClient() {
74
+ try {
75
+ const { licenseApi } = require(path.join(licensePath, 'license-api'));
76
+ return licenseApi;
77
+ } catch (error) {
78
+ console.error('Erro: módulo AIOX Pro license não disponível.');
79
+ console.error(`Instale: npm install ${PRO_PACKAGE_CANONICAL}`);
80
+ console.error(`(ou fallback: npm install ${PRO_PACKAGE_FALLBACK} / ${PRO_PACKAGE_LEGACY})`);
81
+ console.error(`Detalhe: ${error.message}`);
82
+ process.exit(2);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Email format check — minimal, server-side validates definitively.
88
+ */
89
+ function isValidEmail(email) {
90
+ if (typeof email !== 'string' || email.length === 0 || email.length > 254) {
91
+ return false;
92
+ }
93
+ // Simple RFC 5322-lite: no whitespace, one @, dot in domain
94
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
95
+ }
96
+
97
+ /**
98
+ * Classify error into exit code and human-friendly message.
99
+ * Exit codes per story 123.8 ACs:
100
+ * 0 success, 1 validation failed (isBuyer: false),
101
+ * 2 transport/server, 3 auth (reserved for Wave 2 register).
102
+ */
103
+ function classifyError(err) {
104
+ const code = err && err.code ? err.code : null;
105
+
106
+ // Network / transport / server
107
+ if (code === 'NETWORK_ERROR' || code === 'SERVER_ERROR') {
108
+ return {
109
+ exitCode: 2,
110
+ message: 'Falha de rede/servidor. Tente novamente em instantes.',
111
+ hint: 'Para mais detalhes: AIOX_DEBUG=true aiox pro buyer validate ...',
112
+ };
113
+ }
114
+ if (code === 'AUTH_RATE_LIMITED' || code === 'RATE_LIMITED') {
115
+ const retry = err.details && err.details.retryAfter ? ` (retry em ${err.details.retryAfter}s)` : '';
116
+ return {
117
+ exitCode: 2,
118
+ message: `Rate limit atingido${retry}.`,
119
+ hint: 'Aguarde antes de tentar novamente.',
120
+ };
121
+ }
122
+
123
+ // Default: unknown / bad request
124
+ return {
125
+ exitCode: 2,
126
+ message: err && err.message ? err.message : 'Erro desconhecido.',
127
+ hint: null,
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Emit result to stdout.
133
+ * @param {object} payload - Shape { email, isBuyer, hasAccount }
134
+ * @param {boolean} asJson - If true, emit JSON only (no decorative text)
135
+ */
136
+ function emitValidateResult(payload, asJson) {
137
+ if (asJson) {
138
+ process.stdout.write(`${JSON.stringify(payload)}\n`);
139
+ return;
140
+ }
141
+ const statusIcon = payload.isBuyer ? '✅' : '❌';
142
+ const buyerLabel = payload.isBuyer ? 'Sim' : 'Não';
143
+ const accountLabel = payload.hasAccount ? 'Sim' : 'Não';
144
+ process.stdout.write(
145
+ `\n${statusIcon} ${payload.email}\n` +
146
+ ` Buyer: ${buyerLabel}\n` +
147
+ ` Account: ${accountLabel}\n\n`,
148
+ );
149
+ }
150
+
151
+ // ---------------------------------------------------------------------------
152
+ // aiox pro buyer validate
153
+ // ---------------------------------------------------------------------------
154
+
155
+ async function validateAction(options) {
156
+ const email = options && options.email;
157
+ const asJson = Boolean(options && options.json);
158
+
159
+ if (!isValidEmail(email)) {
160
+ if (asJson) {
161
+ process.stdout.write(`${JSON.stringify({ error: 'INVALID_EMAIL', email })}\n`);
162
+ } else {
163
+ process.stderr.write('Erro: email inválido.\n');
164
+ }
165
+ process.exit(2);
166
+ }
167
+
168
+ const client = loadClient();
169
+
170
+ try {
171
+ const result = await client.validateBuyer(email);
172
+ emitValidateResult(result, asJson);
173
+ process.exit(result.isBuyer ? 0 : 1);
174
+ } catch (err) {
175
+ const classified = classifyError(err);
176
+ if (asJson) {
177
+ process.stdout.write(`${JSON.stringify({
178
+ error: err && err.code ? err.code : 'UNKNOWN',
179
+ message: classified.message,
180
+ email,
181
+ })}\n`);
182
+ } else {
183
+ process.stderr.write(`\nFalha: ${classified.message}\n`);
184
+ if (classified.hint) {
185
+ process.stderr.write(`${classified.hint}\n`);
186
+ }
187
+ }
188
+ process.exit(classified.exitCode);
189
+ }
190
+ }
191
+
192
+ // ---------------------------------------------------------------------------
193
+ // aiox pro buyer validate-batch
194
+ // ---------------------------------------------------------------------------
195
+
196
+ /**
197
+ * Run `worker(item)` over `items` with bounded parallelism.
198
+ * Keeps order of results matching input.
199
+ */
200
+ async function mapWithConcurrency(items, concurrency, worker) {
201
+ const results = new Array(items.length);
202
+ let cursor = 0;
203
+
204
+ async function next() {
205
+ while (true) {
206
+ const idx = cursor++;
207
+ if (idx >= items.length) return;
208
+ results[idx] = await worker(items[idx], idx);
209
+ }
210
+ }
211
+
212
+ const limit = Math.max(1, Math.min(concurrency, items.length));
213
+ const workers = [];
214
+ for (let i = 0; i < limit; i += 1) {
215
+ workers.push(next());
216
+ }
217
+ await Promise.all(workers);
218
+ return results;
219
+ }
220
+
221
+ function parseEmailsFile(filePath) {
222
+ const raw = fs.readFileSync(filePath, 'utf8');
223
+ return raw
224
+ .split('\n')
225
+ .map((line) => line.trim())
226
+ .filter((line) => line.length > 0 && !line.startsWith('#'));
227
+ }
228
+
229
+ async function validateBatchAction(options) {
230
+ const filePath = options && options.file;
231
+ const asJson = Boolean(options && options.json);
232
+ const concurrencyRaw = options && options.concurrency ? Number(options.concurrency) : 5;
233
+ const concurrency = Math.max(1, Math.min(10, Number.isFinite(concurrencyRaw) ? concurrencyRaw : 5));
234
+
235
+ if (!filePath || !fs.existsSync(filePath)) {
236
+ const msg = filePath ? `Arquivo não encontrado: ${filePath}` : 'Erro: --file é obrigatório.';
237
+ if (asJson) {
238
+ process.stdout.write(`${JSON.stringify({ error: 'FILE_NOT_FOUND', file: filePath })}\n`);
239
+ } else {
240
+ process.stderr.write(`${msg}\n`);
241
+ }
242
+ process.exit(2);
243
+ }
244
+
245
+ let emails;
246
+ try {
247
+ emails = parseEmailsFile(filePath);
248
+ } catch (err) {
249
+ if (asJson) {
250
+ process.stdout.write(`${JSON.stringify({ error: 'FILE_READ_ERROR', message: err.message })}\n`);
251
+ } else {
252
+ process.stderr.write(`Falha ao ler arquivo: ${err.message}\n`);
253
+ }
254
+ process.exit(2);
255
+ }
256
+
257
+ if (emails.length === 0) {
258
+ if (asJson) {
259
+ process.stdout.write('[]\n');
260
+ } else {
261
+ process.stdout.write('Nenhum email no arquivo.\n');
262
+ }
263
+ process.exit(0);
264
+ }
265
+
266
+ const client = loadClient();
267
+
268
+ const results = await mapWithConcurrency(emails, concurrency, async (email) => {
269
+ if (!isValidEmail(email)) {
270
+ return { email, isBuyer: false, hasAccount: false, error: 'INVALID_EMAIL' };
271
+ }
272
+ try {
273
+ const r = await client.validateBuyer(email);
274
+ return { email: r.email, isBuyer: r.isBuyer, hasAccount: r.hasAccount };
275
+ } catch (err) {
276
+ return {
277
+ email,
278
+ isBuyer: false,
279
+ hasAccount: false,
280
+ error: err && err.code ? err.code : 'UNKNOWN',
281
+ };
282
+ }
283
+ });
284
+
285
+ if (asJson) {
286
+ process.stdout.write(`${JSON.stringify(results)}\n`);
287
+ } else {
288
+ for (const r of results) {
289
+ if (r.error) {
290
+ process.stdout.write(`⚠️ ${r.email} [${r.error}]\n`);
291
+ } else {
292
+ const icon = r.isBuyer ? '✅' : '❌';
293
+ process.stdout.write(`${icon} ${r.email} buyer=${r.isBuyer} account=${r.hasAccount}\n`);
294
+ }
295
+ }
296
+ const successes = results.filter((r) => !r.error && r.isBuyer).length;
297
+ process.stdout.write(`\n${successes}/${results.length} buyers.\n`);
298
+ }
299
+
300
+ const anyFailure = results.some((r) => r.error || !r.isBuyer);
301
+ process.exit(anyFailure ? 1 : 0);
302
+ }
303
+
304
+ // ---------------------------------------------------------------------------
305
+ // aiox pro buyer register — Wave 2 stub (hidden until endpoint exists)
306
+ // ---------------------------------------------------------------------------
307
+
308
+ async function registerAction(options = {}) {
309
+ if (options.json) {
310
+ process.stdout.write(
311
+ JSON.stringify({
312
+ status: 'pending_wave_2',
313
+ message: '`register` pendente (Wave 2 da Story 123.8).',
314
+ reason: 'Endpoint POST /api/v1/admin/buyers/register em aiox-license-server ainda não foi implementado.',
315
+ story: 'docs/stories/epic-123/STORY-123.8-cohort-buyer-cli-migration.md',
316
+ }) + '\n',
317
+ );
318
+ } else {
319
+ process.stderr.write(
320
+ '\nOperação `register` pendente (Wave 2 da Story 123.8).\n' +
321
+ 'Depende do endpoint POST /api/v1/admin/buyers/register no repo aiox-license-server,\n' +
322
+ 'que ainda não foi implementado.\n\n' +
323
+ 'Acompanhe em docs/stories/epic-123/STORY-123.8-cohort-buyer-cli-migration.md\n',
324
+ );
325
+ }
326
+ process.exit(2);
327
+ }
328
+
329
+ // ---------------------------------------------------------------------------
330
+ // Command builder
331
+ // ---------------------------------------------------------------------------
332
+
333
+ /**
334
+ * Create the `aiox pro buyer` subcommand group.
335
+ * @returns {Command}
336
+ */
337
+ function createBuyerCommand() {
338
+ const cmd = new Command('buyer')
339
+ .description('Validar e gerenciar buyers AIOX Pro (Cohort admin)');
340
+
341
+ cmd
342
+ .command('validate')
343
+ .description('Verificar se um email é comprador AIOX Pro')
344
+ .requiredOption('-e, --email <email>', 'Email do buyer a validar')
345
+ .option('--json', 'Emitir saída JSON estável (sem decoração)')
346
+ .action(validateAction);
347
+
348
+ cmd
349
+ .command('validate-batch')
350
+ .description('Validar múltiplos emails de um arquivo (bounded concurrency)')
351
+ .requiredOption('-f, --file <path>', 'Arquivo com um email por linha')
352
+ .option('-c, --concurrency <n>', 'Requisições paralelas (default 5, máx 10)', '5')
353
+ .option('--json', 'Emitir saída JSON (array de resultados)')
354
+ .action(validateBatchAction);
355
+
356
+ // Wave 2 stub — kept so the CLI surface is stable once endpoint lands.
357
+ cmd
358
+ .command('register')
359
+ .description('Cadastrar novo buyer (pendente Wave 2 — endpoint cross-repo)')
360
+ .option('-e, --email <email>', 'Email do buyer')
361
+ .option('-n, --name <name>', 'Nome do buyer')
362
+ .option('--cpf <cpf>', 'CPF (opcional)')
363
+ .option('-y, --yes', 'Pular confirmação')
364
+ .option('--json', 'Emitir saída JSON')
365
+ .action(registerAction);
366
+
367
+ return cmd;
368
+ }
369
+
370
+ module.exports = {
371
+ createBuyerCommand,
372
+ // Exports internos para testes:
373
+ _internal: {
374
+ isValidEmail,
375
+ classifyError,
376
+ parseEmailsFile,
377
+ mapWithConcurrency,
378
+ },
379
+ };