@guilhermefsousa/open-spec-kit 0.0.3 → 0.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guilhermefsousa/open-spec-kit",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "CLI para spec-driven development com suporte a Claude Code e GitHub Copilot",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,7 +4,7 @@ import { join } from 'path';
4
4
  import { execSync } from 'child_process';
5
5
  import yaml from 'yaml';
6
6
  import { detectMcpRunner, getInstallInstructions } from '../utils/mcp-detect.js';
7
- import { confluenceRequest, figmaRequest } from '../utils/http.js';
7
+ import { confluenceRequest, figmaRequest, isSslError } from '../utils/http.js';
8
8
 
9
9
  export async function doctorCommand() {
10
10
  console.log(chalk.bold('\n open-spec-kit doctor\n'));
@@ -319,6 +319,21 @@ export async function doctorCommand() {
319
319
  console.log(chalk.dim(' Renove em: Figma > Account Settings > Personal Access Tokens'));
320
320
  console.log(chalk.dim(' Atualize FIGMA_API_KEY no .env'));
321
321
  fail++;
322
+ } else if (isSslError(err)) {
323
+ try {
324
+ const result = await figmaRequest(figmaKey, 10000, true);
325
+ console.log(chalk.green(` ✓ Token Figma válido — SSL corporativo ignorado (usuário: ${result.data.handle || result.data.email || 'OK'})`));
326
+ pass++;
327
+ } catch (retryErr) {
328
+ if (retryErr.status === 401 || retryErr.status === 403) {
329
+ console.log(chalk.red(` ✗ Token Figma inválido ou expirado (HTTP ${retryErr.status})`));
330
+ console.log(chalk.dim(' Renove em: Figma > Account Settings > Personal Access Tokens'));
331
+ console.log(chalk.dim(' Atualize FIGMA_API_KEY no .env'));
332
+ } else {
333
+ console.log(chalk.yellow(` ⚠ Não foi possível verificar token Figma: ${retryErr.message}`));
334
+ }
335
+ fail++;
336
+ }
322
337
  } else {
323
338
  console.log(chalk.yellow(` ⚠ Não foi possível verificar token Figma: ${err.message}`));
324
339
  fail++;
@@ -6,7 +6,7 @@ import { join, dirname } from 'path';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { execSync } from 'child_process';
8
8
  import { detectMcpRunner, detectNpxRunner, tryInstallMcpAtlassian, getInstallInstructions } from '../utils/mcp-detect.js';
9
- import { confluenceRequest, figmaRequest } from '../utils/http.js';
9
+ import { confluenceRequest, figmaRequest, isSslError } from '../utils/http.js';
10
10
 
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
  const TEMPLATES_DIR = join(__dirname, '..', '..', 'templates');
@@ -47,26 +47,6 @@ const KNOWN_TECH_KEYWORDS = [
47
47
  'SQS', 'SNS', 'DynamoDB', 'Cassandra', 'Nginx', 'Apache',
48
48
  ];
49
49
 
50
- // ──────────────────────────────────────────────────────
51
- // SSL error detection (corporate proxy workaround)
52
- // ──────────────────────────────────────────────────────
53
- const SSL_ERROR_CODES = new Set([
54
- 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
55
- 'SELF_SIGNED_CERT_IN_CHAIN',
56
- 'DEPTH_ZERO_SELF_SIGNED_CERT',
57
- 'CERT_HAS_EXPIRED',
58
- 'ERR_TLS_CERT_ALTNAME_INVALID',
59
- 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
60
- ]);
61
-
62
- function isSslError(err) {
63
- return err.status === 0 && (
64
- SSL_ERROR_CODES.has(err.code) ||
65
- SSL_ERROR_CODES.has(err.message) ||
66
- (err.message && SSL_ERROR_CODES.has(err.message.split(':').pop()?.trim()))
67
- );
68
- }
69
-
70
50
  function stripHtml(html) {
71
51
  if (!html) return '';
72
52
  return html
@@ -785,6 +765,25 @@ export async function initCommand() {
785
765
  phase3.hasFigma = false;
786
766
  phase3.figmaFileUrl = '';
787
767
  phase3.figmaToken = '';
768
+ } else if (isSslError(err)) {
769
+ figmaSpinner.text = 'Certificado SSL corporativo detectado — tentando sem verificação...';
770
+ try {
771
+ const result = await figmaRequest(phase3.figmaToken.trim(), 15000, true);
772
+ figmaSpinner.succeed(`Token Figma validado (SSL corporativo ignorado) — usuário: ${result.data.handle || result.data.email || 'OK'}`);
773
+ figmaTokenValidated = true;
774
+ } catch (retryErr) {
775
+ if (retryErr.status === 401 || retryErr.status === 403) {
776
+ figmaSpinner.fail(`Token Figma inválido (HTTP ${retryErr.status}). Verifique o Personal Access Token.`);
777
+ console.log(chalk.dim(' Gere em: Figma > Account Settings > Personal Access Tokens'));
778
+ console.log(chalk.yellow(' Continuando sem Figma...\n'));
779
+ phase3.hasFigma = false;
780
+ phase3.figmaFileUrl = '';
781
+ phase3.figmaToken = '';
782
+ } else {
783
+ figmaSpinner.warn(`Não foi possível validar o token Figma: ${retryErr.message}`);
784
+ console.log(chalk.yellow(' Token será salvo, mas verifique com "open-spec-kit doctor".\n'));
785
+ }
786
+ }
788
787
  } else {
789
788
  figmaSpinner.warn(`Não foi possível validar o token Figma: ${err.message}`);
790
789
  console.log(chalk.yellow(' Token será salvo, mas verifique com "open-spec-kit doctor".\n'));
package/src/utils/http.js CHANGED
@@ -49,6 +49,23 @@ export function confluenceRequest(baseUrl, path, user, token, timeoutMs = 15000,
49
49
  return httpGetJson(url, { 'Authorization': `Basic ${auth}` }, { timeoutMs, allowInsecure });
50
50
  }
51
51
 
52
- export function figmaRequest(token, timeoutMs = 15000) {
53
- return httpGetJson('https://api.figma.com/v1/me', { 'X-Figma-Token': token }, { timeoutMs });
52
+ export function figmaRequest(token, timeoutMs = 15000, allowInsecure = false) {
53
+ return httpGetJson('https://api.figma.com/v1/me', { 'X-Figma-Token': token }, { timeoutMs, allowInsecure });
54
+ }
55
+
56
+ const SSL_ERROR_CODES = new Set([
57
+ 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',
58
+ 'SELF_SIGNED_CERT_IN_CHAIN',
59
+ 'DEPTH_ZERO_SELF_SIGNED_CERT',
60
+ 'CERT_HAS_EXPIRED',
61
+ 'ERR_TLS_CERT_ALTNAME_INVALID',
62
+ 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
63
+ ]);
64
+
65
+ export function isSslError(err) {
66
+ return err.status === 0 && (
67
+ SSL_ERROR_CODES.has(err.code) ||
68
+ SSL_ERROR_CODES.has(err.message) ||
69
+ (err.message && SSL_ERROR_CODES.has(err.message.split(':').pop()?.trim()))
70
+ );
54
71
  }