UNPKG

@pimzino/claude-code-spec-workflow

Version:

Automated workflows for Claude Code. Includes spec-driven development (Requirements → Design → Tasks → Implementation) with intelligent task execution, optional steering documents and streamlined bug fix workflow (Report → Analyze → Fix → Verify). We have

175 lines 7.26 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.CloudflareProvider = void 0; const types_1 = require("./types"); const events_1 = require("events"); const child_process_1 = require("child_process"); class CloudflareTunnelInstance extends events_1.EventEmitter { constructor(_url) { super(); this._url = _url; this.status = 'active'; this.createdAt = new Date(); this.provider = 'cloudflare'; } get url() { return this._url; } setProcess(process) { this.process = process; } async close() { if (this.status === 'closing') { return; } this.status = 'closing'; if (this.process) { const proc = this.process; return new Promise((resolve) => { const timeout = setTimeout(() => { proc.kill('SIGKILL'); resolve(); }, 5000); proc.once('exit', () => { clearTimeout(timeout); resolve(); }); proc.kill('SIGTERM'); }); } } async getHealth() { if (this.status !== 'active') { return { healthy: false, error: `Tunnel is ${this.status}` }; } return { healthy: true, latency: 0 }; } } class CloudflareProvider { constructor(_config) { this._config = _config; this.name = 'cloudflare'; } async isAvailable() { // For now, we'll use the cloudflared CLI approach since the npm package // requires setup and domain configuration // The cloudflared-tunnel package requires a one-time setup which is too complex // for a simple tunnel use case try { const { exec } = await Promise.resolve().then(() => __importStar(require('child_process'))); const { promisify } = await Promise.resolve().then(() => __importStar(require('util'))); const execPromise = promisify(exec); const { stdout } = await execPromise('which cloudflared'); return !!stdout.trim(); } catch { return false; } } async validateConfig() { const available = await this.isAvailable(); if (!available) { throw new types_1.TunnelProviderError(this.name, 'CLOUDFLARED_NOT_FOUND', 'cloudflared CLI not found. Please install it from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/'); } } async createTunnel(port, _options) { await this.validateConfig(); return new Promise((resolve, reject) => { // Use cloudflared CLI for simplicity // The npm packages require complex setup with domains const args = ['tunnel', '--url', `http://localhost:${port}`]; const cloudflared = (0, child_process_1.spawn)('cloudflared', args, { stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env } }); let urlFound = false; let errorOutput = ''; const handleOutput = (data) => { const output = data.toString(); // Look for the tunnel URL in the output const urlMatch = output.match(/https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/); if (urlMatch && !urlFound) { urlFound = true; const url = urlMatch[0]; // Create new instance with URL const tunnelInstance = new CloudflareTunnelInstance(url); tunnelInstance.setProcess(cloudflared); resolve(tunnelInstance); } // Capture any error messages if (output.toLowerCase().includes('error')) { errorOutput += output; } }; cloudflared.stdout.on('data', handleOutput); cloudflared.stderr.on('data', handleOutput); cloudflared.on('error', (error) => { reject(new types_1.TunnelProviderError(this.name, 'CLOUDFLARED_START_ERROR', `Failed to start cloudflared: ${error.message}`, 'Could not start Cloudflare tunnel.', [ 'Check that cloudflared is properly installed', 'Verify your internet connection', 'Try running: cloudflared tunnel --url http://localhost:' + port ])); }); cloudflared.on('exit', (code, signal) => { if (!urlFound) { reject(new types_1.TunnelProviderError(this.name, 'CLOUDFLARED_EXIT_EARLY', `cloudflared exited unexpectedly (code: ${code}, signal: ${signal}). ${errorOutput}`, 'Cloudflare tunnel exited before establishing connection.', [ 'Check that the port is not already in use', 'Verify cloudflared has proper permissions', 'Check your firewall settings' ])); } }); // Timeout if URL not found within 30 seconds setTimeout(() => { if (!urlFound) { cloudflared.kill(); reject(new types_1.TunnelProviderError(this.name, 'CLOUDFLARED_TIMEOUT', 'Timeout waiting for tunnel URL from cloudflared', 'Cloudflare tunnel took too long to start.', [ 'Check your internet connection', 'Try running cloudflared manually' ])); } }, 30000); }); } } exports.CloudflareProvider = CloudflareProvider; //# sourceMappingURL=cloudflare-provider-native.js.map