@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
159 lines • 6.23 kB
JavaScript
;
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.NgrokProvider = void 0;
const ngrok = __importStar(require("@ngrok/ngrok"));
const types_1 = require("./types");
const events_1 = require("events");
class NgrokTunnelInstance extends events_1.EventEmitter {
constructor(_url, listener) {
super();
this._url = _url;
this.listener = listener;
this.status = 'active';
this.createdAt = new Date();
this.provider = 'ngrok';
}
get url() {
return this._url;
}
async close() {
if (this.status === 'closing') {
return;
}
this.status = 'closing';
try {
await this.listener.close();
this.status = 'closing';
}
catch (error) {
this.status = 'error';
throw error;
}
}
async getHealth() {
if (this.status !== 'active') {
return {
healthy: false,
error: `Tunnel is ${this.status}`
};
}
return {
healthy: true,
latency: 0
};
}
}
class NgrokProvider {
constructor(_config) {
this._config = _config;
this.name = 'ngrok';
// Config is used in validateConfig and createTunnel
}
async isAvailable() {
try {
// Check if we can import ngrok (it's already installed)
const ngrokModule = await Promise.resolve().then(() => __importStar(require('@ngrok/ngrok')));
return !!ngrokModule;
}
catch {
return false;
}
}
async validateConfig() {
const available = await this.isAvailable();
if (!available) {
throw new types_1.TunnelProviderError(this.name, 'NGROK_NOT_FOUND', '@ngrok/ngrok package not found. Please install it: npm install @ngrok/ngrok');
}
// Try to read auth token from ngrok config file if not provided
if (!this._config?.authToken && !process.env.NGROK_AUTHTOKEN) {
try {
const os = await Promise.resolve().then(() => __importStar(require('os')));
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
const path = await Promise.resolve().then(() => __importStar(require('path')));
const configPath = path.join(os.homedir(), 'Library/Application Support/ngrok/ngrok.yml');
const configContent = await fs.readFile(configPath, 'utf8');
// Simple regex to extract authtoken
const authTokenMatch = configContent.match(/authtoken:\s*([^\s]+)/);
if (authTokenMatch && authTokenMatch[1]) {
process.env.NGROK_AUTHTOKEN = authTokenMatch[1];
}
}
catch (error) {
// Ignore error - ngrok might work without auth token
console.log('Note: Running ngrok without auth token. Some features may be limited.');
}
}
}
async createTunnel(port, _options) {
await this.validateConfig();
try {
// Configure ngrok with auth token if provided
if (this._config?.authToken) {
process.env.NGROK_AUTHTOKEN = this._config.authToken;
}
// Create tunnel using native ngrok library
const listener = await ngrok.connect({
addr: port,
authtoken: this._config?.authToken || process.env.NGROK_AUTHTOKEN,
region: this._config?.region,
// Set a custom metadata
metadata: JSON.stringify({
name: 'claude-code-spec-workflow',
readOnly: 'true'
})
});
// Get the public URL
const url = listener.url();
if (!url) {
throw new types_1.TunnelProviderError(this.name, 'NGROK_NO_URL', 'Failed to get tunnel URL from ngrok');
}
const instance = new NgrokTunnelInstance(url, listener);
return instance;
}
catch (error) {
if (error instanceof types_1.TunnelProviderError) {
throw error;
}
throw new types_1.TunnelProviderError(this.name, 'NGROK_START_ERROR', `Failed to start ngrok: ${error instanceof Error ? error.message : String(error)}`, 'Could not start ngrok tunnel. This might be a configuration or authentication issue.', [
'Check your ngrok auth token',
'Verify your internet connection',
'Check if you have reached your ngrok account limits'
]);
}
}
}
exports.NgrokProvider = NgrokProvider;
//# sourceMappingURL=ngrok-provider-native.js.map