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.
- package/.aiox-core/cli/commands/pro/buyer.js +379 -0
- package/.aiox-core/cli/commands/pro/index.js +191 -52
- package/.aiox-core/cli/commands/validate/index.js +2 -0
- package/.aiox-core/core/code-intel/helpers/dev-helper.js +1 -1
- package/.aiox-core/core/code-intel/helpers/devops-helper.js +0 -1
- package/.aiox-core/core/code-intel/helpers/planning-helper.js +1 -1
- package/.aiox-core/core/code-intel/helpers/qa-helper.js +2 -2
- package/.aiox-core/core/config/schemas/framework-config.schema.json +1 -0
- package/.aiox-core/core/config/template-overrides.js +1 -1
- package/.aiox-core/core/doctor/checks/ide-sync.js +81 -25
- package/.aiox-core/core/doctor/checks/rules-files.js +0 -1
- package/.aiox-core/core/doctor/checks/skills-count.js +83 -15
- package/.aiox-core/core/graph-dashboard/cli.js +1 -2
- package/.aiox-core/core/graph-dashboard/data-sources/code-intel-source.js +1 -1
- package/.aiox-core/core/ids/layer-classifier.js +1 -1
- package/.aiox-core/core/pro/pro-updater.js +578 -0
- package/.aiox-core/core/synapse/context/context-tracker.js +107 -9
- package/.aiox-core/core/synapse/layers/layer-processor.js +1 -1
- package/.aiox-core/core-config.yaml +15 -1
- package/.aiox-core/data/capability-detection.js +15 -15
- package/.aiox-core/data/entity-registry.yaml +18 -2
- package/.aiox-core/data/registry-update-log.jsonl +5 -0
- package/.aiox-core/data/tok3-token-comparison.js +0 -4
- package/.aiox-core/data/tool-search-validation.js +1 -1
- package/.aiox-core/development/agents/aiox-master.md +44 -6
- package/.aiox-core/development/agents/data-engineer.md +4 -4
- package/.aiox-core/development/agents/devops.md +52 -2
- package/.aiox-core/development/agents/po.md +1 -1
- package/.aiox-core/development/agents/qa.md +5 -11
- package/.aiox-core/development/agents/sm.md +3 -3
- package/.aiox-core/development/agents/ux-design-expert.md +1 -1
- package/.aiox-core/development/scripts/unified-activation-pipeline.js +29 -3
- package/.aiox-core/development/tasks/dev-develop-story.md +46 -7
- package/.aiox-core/development/tasks/devops-pro-access-grant.md +93 -0
- package/.aiox-core/development/tasks/devops-pro-activate.md +42 -0
- package/.aiox-core/development/tasks/devops-pro-check-access.md +34 -0
- package/.aiox-core/development/tasks/devops-pro-request-reset.md +34 -0
- package/.aiox-core/development/tasks/devops-pro-resend-verification.md +32 -0
- package/.aiox-core/development/tasks/devops-pro-reset-password.md +36 -0
- package/.aiox-core/development/tasks/devops-pro-validate-login.md +36 -0
- package/.aiox-core/development/tasks/devops-pro-verify-status.md +33 -0
- package/.aiox-core/development/tasks/qa-gate.md +54 -4
- package/.aiox-core/development/tasks/validate-next-story.md +39 -2
- package/.aiox-core/framework-config.yaml +1 -0
- package/.aiox-core/infrastructure/scripts/codex-skills-sync/README.md +69 -0
- package/.aiox-core/infrastructure/scripts/codex-skills-sync/bootstrap.js +727 -0
- package/.aiox-core/infrastructure/scripts/codex-skills-sync/index.js +10 -0
- package/.aiox-core/infrastructure/scripts/codex-skills-sync/validate.js +65 -4
- package/.aiox-core/infrastructure/scripts/generate-settings-json.js +29 -4
- package/.aiox-core/infrastructure/scripts/ide-sync/agent-parser.js +4 -0
- package/.aiox-core/infrastructure/scripts/ide-sync/index.js +67 -7
- package/.aiox-core/infrastructure/scripts/ide-sync/transformers/claude-code.js +145 -3
- package/.aiox-core/infrastructure/scripts/repair-agent-references.js +263 -0
- package/.aiox-core/infrastructure/scripts/validate-claude-integration.js +60 -8
- package/.aiox-core/infrastructure/scripts/validate-paths.js +13 -0
- package/.aiox-core/install-manifest.yaml +134 -82
- package/.aiox-core/utils/filters/index.js +2 -1
- package/.claude/commands/AIOX/agents/aiox-master.md +21 -0
- package/.claude/commands/AIOX/agents/analyst.md +21 -0
- package/.claude/commands/AIOX/agents/architect.md +21 -0
- package/.claude/commands/AIOX/agents/data-engineer.md +21 -0
- package/.claude/commands/AIOX/agents/dev.md +21 -0
- package/.claude/commands/AIOX/agents/devops.md +21 -0
- package/.claude/commands/AIOX/agents/pm.md +21 -0
- package/.claude/commands/AIOX/agents/po.md +21 -0
- package/.claude/commands/AIOX/agents/qa.md +21 -0
- package/.claude/commands/AIOX/agents/sm.md +21 -0
- package/.claude/commands/AIOX/agents/squad-creator.md +21 -0
- package/.claude/commands/AIOX/agents/ux-design-expert.md +21 -0
- package/.claude/commands/AIOX/scripts/agent-config-loader.js +624 -0
- package/.claude/commands/AIOX/scripts/generate-greeting.js +160 -0
- package/.claude/commands/AIOX/scripts/greeting-builder.js +866 -0
- package/.claude/commands/AIOX/scripts/session-context-loader.js +286 -0
- package/.claude/commands/AIOX/stories/story-6.1.4.md +1404 -0
- package/.claude/commands/cohort-squad/agents/cohort-manager.md +156 -0
- package/.claude/commands/design-system/agents/brad-frost.md +1097 -0
- package/.claude/commands/design-system/agents/dan-mall.md +857 -0
- package/.claude/commands/design-system/agents/dave-malouf.md +2272 -0
- package/.claude/commands/design-system/agents/design-chief.md +102 -0
- package/.claude/commands/design-system/agents/nano-banana-generator.md +162 -0
- package/.claude/commands/greet.md +101 -0
- package/.claude/commands/synapse/manager.md +75 -0
- package/.claude/commands/synapse/tasks/add-rule.md +94 -0
- package/.claude/commands/synapse/tasks/create-command.md +109 -0
- package/.claude/commands/synapse/tasks/create-domain.md +127 -0
- package/.claude/commands/synapse/tasks/diagnose-synapse.md +245 -0
- package/.claude/commands/synapse/tasks/edit-rule.md +109 -0
- package/.claude/commands/synapse/tasks/suggest-domain.md +116 -0
- package/.claude/commands/synapse/tasks/toggle-domain.md +83 -0
- package/.claude/commands/synapse/templates/domain-template +8 -0
- package/.claude/commands/synapse/templates/manifest-entry-template +4 -0
- package/.claude/commands/synapse/utils/manifest-parser-reference.md +134 -0
- package/.claude/hooks/precompact-session-digest.cjs +2 -2
- package/.claude/skills/AIOX/agents/aiox-master/SKILL.md +511 -0
- package/.claude/skills/AIOX/agents/analyst/SKILL.md +281 -0
- package/.claude/skills/AIOX/agents/architect/SKILL.md +482 -0
- package/.claude/skills/AIOX/agents/data-engineer/SKILL.md +503 -0
- package/.claude/skills/AIOX/agents/dev/SKILL.md +568 -0
- package/.claude/skills/AIOX/agents/devops/SKILL.md +597 -0
- package/.claude/skills/AIOX/agents/pm/SKILL.md +385 -0
- package/.claude/skills/AIOX/agents/po/SKILL.md +343 -0
- package/.claude/skills/AIOX/agents/qa/SKILL.md +451 -0
- package/.claude/skills/AIOX/agents/sm/SKILL.md +295 -0
- package/.claude/skills/AIOX/agents/squad-creator/SKILL.md +352 -0
- package/.claude/skills/AIOX/agents/ux-design-expert/SKILL.md +503 -0
- package/.claude/skills/architect-first/SKILL.md +275 -0
- package/.claude/skills/architect-first/assets/architecture-template.md +505 -0
- package/.claude/skills/architect-first/assets/config-template.yaml +351 -0
- package/.claude/skills/architect-first/references/architecture-checklist.md +216 -0
- package/.claude/skills/architect-first/references/pre-implementation-checklist.md +119 -0
- package/.claude/skills/architect-first/references/stop-rules-guide.md +291 -0
- package/.claude/skills/architect-first/references/testing-strategy-guide.md +477 -0
- package/.claude/skills/architect-first/scripts/architecture_validator.py +490 -0
- package/.claude/skills/architect-first/scripts/check_coupling.py +306 -0
- package/.claude/skills/architect-first/scripts/validate_risk_mitigation.py +382 -0
- package/.claude/skills/checklist-runner/SKILL.md +113 -0
- package/.claude/skills/clone-mind.md +329 -0
- package/.claude/skills/coderabbit-review/SKILL.md +106 -0
- package/.claude/skills/course-generation-workflow.md +76 -0
- package/.claude/skills/enhance-workflow.md +466 -0
- package/.claude/skills/mcp-builder/LICENSE.txt +202 -0
- package/.claude/skills/mcp-builder/SKILL.md +328 -0
- package/.claude/skills/mcp-builder/reference/evaluation.md +602 -0
- package/.claude/skills/mcp-builder/reference/mcp_best_practices.md +915 -0
- package/.claude/skills/mcp-builder/reference/node_mcp_server.md +916 -0
- package/.claude/skills/mcp-builder/reference/python_mcp_server.md +752 -0
- package/.claude/skills/mcp-builder/scripts/connections.py +151 -0
- package/.claude/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/.claude/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/.claude/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/.claude/skills/ralph.md +181 -0
- package/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/.claude/skills/skill-creator/SKILL.md +209 -0
- package/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.claude/skills/skill-creator/scripts/quick_validate.py +65 -0
- package/.claude/skills/squad.md +301 -0
- package/.claude/skills/synapse/SKILL.md +132 -0
- package/.claude/skills/synapse/assets/README.md +50 -0
- package/.claude/skills/synapse/references/brackets.md +100 -0
- package/.claude/skills/synapse/references/commands.md +118 -0
- package/.claude/skills/synapse/references/domains.md +126 -0
- package/.claude/skills/synapse/references/layers.md +186 -0
- package/.claude/skills/synapse/references/manifest.md +142 -0
- package/.claude/skills/tech-search/SKILL.md +431 -0
- package/.claude/skills/tech-search/prompts/page-extract.md +133 -0
- package/README.en.md +2 -2
- package/README.md +8 -2
- package/bin/aiox.js +55 -4
- package/bin/utils/framework-guard.js +4 -2
- package/bin/utils/pro-detector.js +119 -28
- package/bin/utils/validate-publish.js +6 -6
- package/docs/aiox-agent-flows/devops-system.md +18 -0
- package/docs/aiox-workflows/README.md +1 -0
- package/docs/aiox-workflows/pro-access-grant-workflow.md +218 -0
- package/docs/guides/pro/access-grant-ops-playbook.md +370 -0
- package/docs/guides/pro/install-gate-setup.md +12 -6
- package/docs/guides/pro/squad-creator-handoff-pro-access-ops.md +134 -0
- package/docs/guides/supabase-ops-handoff.md +768 -0
- package/package.json +12 -1
- package/packages/aiox-pro-cli/bin/aiox-pro.js +33 -12
- package/packages/installer/src/config/configure-environment.js +118 -50
- package/packages/installer/src/installer/aiox-core-installer.js +124 -27
- package/packages/installer/src/installer/brownfield-upgrader.js +66 -9
- package/packages/installer/src/installer/dependency-installer.js +4 -0
- package/packages/installer/src/pro/pro-scaffolder.js +5 -5
- package/packages/installer/src/updater/index.js +151 -10
- package/packages/installer/src/wizard/ide-config-generator.js +73 -7
- package/packages/installer/src/wizard/index.js +119 -31
- package/packages/installer/src/wizard/pro-setup.js +118 -47
- package/packages/installer/src/wizard/validation/validators/dependency-validator.js +32 -25
- package/packages/installer/src/wizard/validation/validators/file-structure-validator.js +26 -0
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +84 -1
- package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +1 -1
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +85 -19
- package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +4 -4
- package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +5 -5
- package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +4 -4
- package/packages/installer/tests/unit/merger/yaml-merger.test.js +11 -11
- package/pro/README.md +12 -1
- package/pro/license/index.js +3 -11
- package/pro/license/license-api.js +25 -0
- package/pro/license/license-cache.js +135 -31
- package/pro/license/license-crypto.js +59 -3
- package/pro/package.json +5 -4
- package/pro/squads/README.md +16 -16
- package/pro/squads/index.js +1 -1
- package/scripts/e2e/installed-skills-smoke.js +264 -0
- package/scripts/package-synapse.js +3 -3
- package/scripts/validate-package-completeness.js +8 -11
- 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
|
+
};
|