Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

swytchcode

Package Overview
Dependencies
Maintainers
1
Versions
93
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

swytchcode - npm Package Compare versions

Comparing version
2.3.22
to
2.5.0
+445
-34
bin/swytchcode.js
#!/usr/bin/env node
'use strict';
const { spawnSync } = require('child_process');
const fs = require('fs');
const { spawnSync, execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const rl = require('readline');
const https = require('https');
const http = require('http');
// When stdin is piped (not a TTY), read it fully and re-pipe it explicitly to the
// child. Blindly inheriting a piped stdin fd can leave the Go binary with an
// already-EOF pipe, causing its HTTP context to cancel mid-response.
// ─── Colours ────────────────────────────────────────────────────────────────
const c = {
reset: '\x1b[0m',
bold: '\x1b[1m',
dim: '\x1b[2m',
green: '\x1b[32m',
yellow: '\x1b[33m',
cyan: '\x1b[36m',
white: '\x1b[37m',
gray: '\x1b[90m',
bgGreen: '\x1b[42m',
red: '\x1b[31m',
};
// ─── Helpers ────────────────────────────────────────────────────────────────
function write(msg) { process.stdout.write(msg); }
function writeln(msg = '') { process.stdout.write(msg + '\n'); }
function err(msg) { process.stderr.write(msg + '\n'); }
function box(lines, colour = c.cyan) {
const width = Math.max(...lines.map(l => stripAnsi(l).length)) + 4;
const bar = colour + '─'.repeat(width) + c.reset;
writeln(bar);
for (const line of lines) {
const pad = width - 2 - stripAnsi(line).length;
writeln(colour + ' ' + c.reset + line + ' '.repeat(pad) + colour + ' ' + c.reset);
}
writeln(bar);
}
function stripAnsi(str) {
return str.replace(/\x1b\[[0-9;]*m/g, '');
}
function ask(prompt) {
return new Promise(resolve => {
const iface = rl.createInterface({ input: process.stdin, output: process.stdout });
iface.question(prompt, answer => { iface.close(); resolve(answer.trim()); });
});
}
function menuArrow(prompt, options) {
// options: [{ label, value }]
return new Promise(async resolve => {
if (!process.stdin.isTTY) {
// TTY fallback to number input
writeln();
writeln(c.bold + prompt + c.reset);
writeln();
options.forEach((o, i) => {
writeln(` ${c.cyan}${i + 1}${c.reset} ${stripAnsi(o.label)}`);
});
writeln();
while (true) {
const raw = await ask(`${c.gray}Enter number [1-${options.length}]: ${c.reset}`);
const n = parseInt(raw, 10);
if (n >= 1 && n <= options.length) {
resolve(options[n - 1].value);
return;
}
writeln(`${c.red} Invalid choice. Please enter a number between 1 and ${options.length}.${c.reset}`);
}
}
// TTY interactive mode with arrow keys
let selectedIndex = 0;
// Hide cursor
write('\x1b[?25l');
function render() {
writeln();
writeln(c.bold + prompt + c.reset);
writeln();
options.forEach((o, i) => {
if (i === selectedIndex) {
writeln(` ${c.cyan}❯${c.reset} ${c.bold}${c.green}${stripAnsi(o.label)}${c.reset}`);
} else {
writeln(` ${c.dim}${stripAnsi(o.label)}${c.reset}`);
}
});
writeln();
}
function clean() {
const lines = options.length + 4;
rl.moveCursor(process.stdout, 0, -lines);
rl.clearScreenDown(process.stdout);
}
render();
const onKeypress = (char, key) => {
if (key) {
if (key.name === 'up') {
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
clean();
render();
} else if (key.name === 'down') {
selectedIndex = (selectedIndex + 1) % options.length;
clean();
render();
} else if (key.name === 'return' || key.name === 'enter') {
cleanup();
resolve(options[selectedIndex].value);
} else if (key.ctrl && key.name === 'c') {
cleanup();
process.exit(130);
}
}
};
rl.emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.on('keypress', onKeypress);
function cleanup() {
// Show cursor
write('\x1b[?25h');
process.stdin.removeListener('keypress', onKeypress);
try {
process.stdin.setRawMode(false);
} catch (_) {}
process.stdin.pause();
}
});
}
function spinner(msg) {
const frames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
let i = 0;
const iv = setInterval(() => {
process.stdout.write(`\r${c.cyan}${frames[i++ % frames.length]}${c.reset} ${msg}`);
}, 80);
return {
stop(doneMsg) {
clearInterval(iv);
process.stdout.write(`\r${c.green}✔${c.reset} ${doneMsg}\n`);
},
fail(failMsg) {
clearInterval(iv);
process.stdout.write(`\r${c.red}✖${c.reset} ${failMsg}\n`);
},
};
}
function apiPost(urlStr, body) {
return new Promise((resolve, reject) => {
const payload = JSON.stringify(body);
const parsed = new URL(urlStr);
const lib = parsed.protocol === 'https:' ? https : http;
const req = lib.request({
hostname: parsed.hostname,
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
path: parsed.pathname + parsed.search,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload),
'User-Agent': 'swytchcode-npx-onboarding/1.0',
},
}, res => {
let data = '';
res.on('data', d => { data += d; });
res.on('end', () => {
try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
catch { resolve({ status: res.statusCode, body: data }); }
});
});
req.on('error', reject);
req.setTimeout(15000, () => { req.destroy(new Error('Request timed out')); });
req.write(payload);
req.end();
});
}
// ─── Binary resolution (shared by normal pass-through AND onboarding) ────────
function spawnBin(binPath, args) {

@@ -15,7 +194,4 @@ if (process.stdin.isTTY) {

let stdinData;
try {
stdinData = fs.readFileSync(0); // fd 0 = stdin
} catch (_) {
stdinData = Buffer.alloc(0);
}
try { stdinData = fs.readFileSync(0); }
catch (_) { stdinData = Buffer.alloc(0); }
return spawnSync(binPath, args, {

@@ -27,3 +203,15 @@ stdio: ['pipe', 'inherit', 'inherit'],

// Explicit override: set SWYTCHCODE_BIN=/path/to/binary to bypass platform package resolution.
function exitCode(result) {
if (result.status !== null && result.status !== undefined) return result.status;
const SIGNALS = {
SIGHUP:1,SIGINT:2,SIGQUIT:3,SIGILL:4,SIGTRAP:5,SIGABRT:6,
SIGBUS:7,SIGFPE:8,SIGKILL:9,SIGUSR1:10,SIGSEGV:11,SIGUSR2:12,
SIGPIPE:13,SIGALRM:14,SIGTERM:15,
};
const sig = result.signal;
if (sig && SIGNALS[sig] !== undefined) return 128 + SIGNALS[sig];
return 1;
}
// Explicit override: SWYTCHCODE_BIN=/path/to/binary bypasses platform resolution.
const explicitBin = (process.env.SWYTCHCODE_BIN || '').trim();

@@ -33,6 +221,6 @@ if (explicitBin) {

if (result.error) {
process.stderr.write(`swytchcode: failed to launch SWYTCHCODE_BIN="${explicitBin}": ${result.error.message}\n`);
err(`swytchcode: failed to launch SWYTCHCODE_BIN="${explicitBin}": ${result.error.message}`);
process.exit(1);
}
process.exit(result.status ?? 1);
process.exit(exitCode(result));
}

@@ -50,11 +238,11 @@

const platform = process.platform;
const arch = process.arch;
const key = `${platform}-${arch}`;
const pkg = PLATFORM_PACKAGES[key];
const arch = process.arch;
const key = `${platform}-${arch}`;
const pkg = PLATFORM_PACKAGES[key];
if (!pkg) {
process.stderr.write(
err(
`swytchcode: unsupported platform "${key}".\n` +
`Supported: ${Object.keys(PLATFORM_PACKAGES).join(', ')}\n` +
`Install via curl instead: https://cli.swytchcode.com\n`
`Install via curl instead: https://cli.swytchcode.com`
);

@@ -65,4 +253,3 @@ process.exit(1);

const isWindows = platform === 'win32';
const binName = isWindows ? 'swytchcode.exe' : 'swytchcode';
const binName = isWindows ? 'swytchcode.exe' : 'swytchcode';
let binPath;

@@ -72,5 +259,5 @@ try {

} catch (_) {
process.stderr.write(
err(
`swytchcode: could not find the binary for "${key}".\n` +
`Try reinstalling: npm install -g swytchcode\n`
`Try reinstalling: npm install -g swytchcode`
);

@@ -80,4 +267,3 @@ process.exit(1);

// Detect npx: npm 7+ runs `npx` as `npm exec` (npm_command=exec),
// older npm puts the binary in a _npx temp dir, and even older npm names npx in npm_execpath.
// Detect npx
const isNpx =

@@ -89,16 +275,241 @@ (process.env.npm_command || '') === 'exec' ||

const result = spawnBin(binPath, process.argv.slice(2));
const userArgs = process.argv.slice(2);
if (result.error) {
process.stderr.write(`swytchcode: failed to launch binary: ${result.error.message}\n`);
process.exit(1);
// ─── Normal pass-through (not npx, or npx with args) ────────────────────────
if (!isNpx || userArgs.length > 0) {
const result = spawnBin(binPath, userArgs);
if (result.error) {
err(`swytchcode: failed to launch binary: ${result.error.message}`);
process.exit(1);
}
process.exit(exitCode(result));
}
if (isNpx && result.status === 0) {
process.stderr.write(
'\n\x1b[33m★ Speed up swytchcode — install it globally:\x1b[0m\n' +
' \x1b[1m\x1b[32mnpm install -g swytchcode\x1b[0m\n\n'
);
// ─── NPX ONBOARDING FLOW ─────────────────────────────────────────────────────
// Only reached when: isNpx === true AND no args passed (bare `npx swytchcode`)
const API_BASE = 'https://api-v2.swytchcode.com/v2/cli';
// Helper to perform HTTP GET (declared inside so it doesn't conflict)
function apiGet(urlStr) {
return new Promise((resolve, reject) => {
const parsed = new URL(urlStr);
const lib = parsed.protocol === 'https:' ? https : http;
const req = lib.request({
hostname: parsed.hostname,
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
path: parsed.pathname + parsed.search,
method: 'GET',
headers: { 'User-Agent': 'swytchcode-npx-onboarding/1.0' },
}, res => {
let data = '';
res.on('data', d => { data += d; });
res.on('end', () => {
try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
catch { resolve({ status: res.statusCode, body: data }); }
});
});
req.on('error', reject);
req.setTimeout(10000, () => { req.destroy(new Error('Request timed out')); });
req.end();
});
}
async function runOnboarding() {
let cliVersion = '0.0.0';
try {
const verResult = spawnSync(binPath, ['--version'], { encoding: 'utf8' });
const semverMatch = verResult.stdout && verResult.stdout.match(/[\d]+\.[\d]+\.[\d]+/);
if (semverMatch) {
cliVersion = semverMatch[0];
} else if (verResult.stdout && verResult.stdout.includes('dev')) {
cliVersion = 'dev';
} else {
const pkgJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8'));
cliVersion = pkgJson.version || '0.0.0';
}
} catch (_) {}
process.exit(result.status ?? 1);
// Clear screen for a clean experience
process.stdout.write('\x1Bc');
writeln();
const vPad = ' '.repeat(Math.max(0, 34 - cliVersion.length));
writeln(` ${c.yellow}==========================================${c.reset}`);
writeln(` ${c.yellow}|${c.reset} ${c.yellow}⚡ ${c.bold}Welcome to Swytchcode${c.reset} ${c.yellow}|${c.reset}`);
writeln(` ${c.yellow}|${c.reset} ${c.dim}v${cliVersion}${c.reset}${vPad}${c.yellow}|${c.reset}`);
writeln(` ${c.yellow}|${c.reset} ${c.dim}Execution authority layer${c.reset} ${c.yellow}|${c.reset}`);
writeln(` ${c.yellow}==========================================${c.reset}`);
writeln();
// ── Step 1: Demo ──────────────────────────────────────────────────────────
writeln(`${c.bold} Step 1 of 4 — See it in action${c.reset}`);
writeln(`${c.dim} Run a live API call right now, no setup required.${c.reset}`);
writeln();
// Fetch available demo tools
let demoTools = [];
try {
const res = await apiGet(`${API_BASE}/demo/tools`);
if (res.status === 200 && Array.isArray(res.body.tools)) {
demoTools = res.body.tools;
}
} catch (_) {
// silently fall through — skip demo if API is unreachable
}
if (demoTools.length === 0) {
writeln(`${c.yellow} Demo unavailable right now — skipping to setup.${c.reset}`);
writeln();
} else {
// Map tool IDs to friendly names
const friendlyName = {
'stripe.create_payment': 'Stripe — Create a payment ($20.00)',
};
const demoOptions = demoTools.map(t => ({
label: friendlyName[t] || t,
value: t,
}));
demoOptions.push({ label: 'Skip demo', value: '__skip__' });
const chosen = await menuArrow('Choose a demo to run:', demoOptions);
if (chosen !== '__skip__') {
writeln();
writeln(` ${c.gray}$ swytchcode demo stripe${c.reset}`);
spawnSync(binPath, ['demo', 'stripe'], { stdio: 'inherit' });
writeln();
writeln(` ${c.dim}That was a real API call. No credentials, no setup.${c.reset}`);
writeln(` ${c.dim}With Swytchcode, every API integration works exactly like this.${c.reset}`);
}
}
// ── Step 2: Install ───────────────────────────────────────────────────────
writeln();
writeln(' ' + '─'.repeat(45));
writeln();
writeln(`${c.bold} Step 2 of 4 — Install Swytchcode${c.reset}`);
writeln(`${c.dim} Install once, use everywhere. Adds the ${c.reset}${c.cyan}swytchcode${c.reset}${c.dim} command to your terminal.${c.reset}`);
writeln();
writeln(` ${c.dim}Installing into: ${process.cwd()}${c.reset}`);
writeln(` ${c.dim}$ npm install swytchcode${c.reset}`);
writeln();
const installChoice = await menuArrow('Install here?', [
{ label: 'Yes, install here', value: 'yes' },
{ label: 'Skip for now', value: 'no' },
]);
writeln();
if (installChoice === 'no') {
writeln(` ${c.dim}Skipped. Install manually with:${c.reset}`);
writeln(` ${c.cyan}npm install swytchcode${c.reset}`);
writeln(` ${c.dim}For global access:${c.reset} ${c.cyan}npm install -g swytchcode${c.reset}`);
} else {
const installSpin = spinner('Running npm install swytchcode ...');
try {
execSync('npm install swytchcode', { stdio: 'pipe' });
installSpin.stop('Installed successfully');
} catch (e) {
installSpin.fail('Installation failed.');
}
writeln();
writeln(` ${c.dim}To make it available globally, run:${c.reset}`);
writeln(` ${c.bold}${c.green}npm install -g swytchcode${c.reset}`);
}
// ── Step 3: Account sync ──────────────────────────────────────────────────
writeln();
writeln(' ' + '─'.repeat(45));
writeln();
writeln(`${c.bold} Step 3 of 4 — Connect your account${c.reset}`);
writeln(`${c.dim} See your usage analytics, execution history, and saved projects.${c.reset}`);
writeln();
const authChoice = await menuArrow('Do you already have an account?', [
{ label: 'Login to my account', value: 'login' },
{ label: 'Create a new account', value: 'register' },
{ label: 'Continue anonymously', value: 'anon' },
]);
writeln();
if (authChoice === 'login') {
writeln(` ${c.dim}Opening browser for login ...${c.reset}`);
writeln();
const result = spawnSync(binPath, ['login'], { stdio: 'inherit' });
if (result.error || result.status !== 0) {
writeln(` ${c.yellow}Login window closed or failed.${c.reset}`);
writeln(` ${c.dim}You can log in later with:${c.reset} ${c.cyan}swytchcode login${c.reset}`);
} else {
writeln(` ${c.green}✔ Logged in successfully.${c.reset}`);
}
} else if (authChoice === 'register') {
writeln(` ${c.dim}Opening browser to create your account ...${c.reset}`);
writeln();
// login command handles both login and registration via the browser flow
const result = spawnSync(binPath, ['login'], { stdio: 'inherit' });
if (result.error || result.status !== 0) {
writeln(` ${c.yellow}Browser window closed or failed.${c.reset}`);
writeln(` ${c.dim}Create your account at:${c.reset} ${c.cyan}https://swytchcode.com/signup${c.reset}`);
} else {
writeln(` ${c.green}✔ Account connected successfully.${c.reset}`);
}
} else {
writeln(` ${c.dim}No problem — you can connect your account any time with:${c.reset}`);
writeln(` ${c.cyan}swytchcode login${c.reset}`);
}
// ── Step 4: Next action ───────────────────────────────────────────────────
writeln();
writeln(' ' + '─'.repeat(45));
writeln();
writeln(`${c.bold} Step 4 of 4 — What would you like to do next?${c.reset}`);
writeln();
const nextAction = await menuArrow('Choose an action:', [
{ label: `${c.bold}Build a new integration${c.reset} ${c.dim}— run swytchcode init in your project${c.reset}`, value: 'init' },
{ label: `${c.bold}Explore examples${c.reset} ${c.dim}— scaffold a working example project${c.reset}`, value: 'examples' },
{ label: `${c.bold}Exit${c.reset}`, value: 'exit' },
]);
writeln();
if (nextAction === 'init') {
writeln(` ${c.dim}Starting project setup ...${c.reset}`);
writeln();
const result = spawnSync(binPath, ['init'], { stdio: 'inherit' });
if (result.error) {
writeln(` ${c.red}Could not launch swytchcode init: ${result.error.message}${c.reset}`);
}
} else if (nextAction === 'examples') {
writeln(` ${c.dim}Loading examples ...${c.reset}`);
writeln();
const result = spawnSync(binPath, ['examples'], { stdio: 'inherit' });
if (result.error) {
writeln(` ${c.red}Could not launch swytchcode examples: ${result.error.message}${c.reset}`);
}
} else {
writeln(` ${c.bold}You're all set.${c.reset}`);
writeln();
writeln(` Quick reference:`);
writeln(` ${c.cyan}swytchcode init${c.reset} ${c.dim}Set up a new project${c.reset}`);
writeln(` ${c.cyan}swytchcode examples${c.reset} ${c.dim}Browse example integrations${c.reset}`);
writeln(` ${c.cyan}swytchcode get <api>${c.reset} ${c.dim}Install an API integration${c.reset}`);
writeln(` ${c.cyan}swytchcode exec <id>${c.reset} ${c.dim}Execute an API method${c.reset}`);
writeln(` ${c.cyan}swytchcode --help${c.reset} ${c.dim}Full command reference${c.reset}`);
}
writeln();
writeln(` ${c.dim}Docs: https://cli.swytchcode.com${c.reset}`);
writeln();
}
runOnboarding().catch(e => {
// If onboarding crashes for any reason, fall back to normal binary pass-through
err(`\nswytchcode: onboarding error (${e.message}) — falling back to CLI.\n`);
const result = spawnBin(binPath, userArgs);
process.exit(exitCode(result));
});
+8
-8
{
"name": "swytchcode",
"version": "2.3.22",
"description": "Execution layer for API integrations",
"version": "2.5.0",
"description": "Execution authority layer for API integrations",
"keywords": [

@@ -26,9 +26,9 @@ "api",

"optionalDependencies": {
"swytchcode-cli-darwin-arm64": "2.3.22",
"swytchcode-cli-darwin-x64": "2.3.22",
"swytchcode-cli-linux-arm64": "2.3.22",
"swytchcode-cli-linux-x64": "2.3.22",
"swytchcode-cli-win32-arm64": "2.3.22",
"swytchcode-cli-win32-x64": "2.3.22"
"swytchcode-cli-darwin-arm64": "2.5.0",
"swytchcode-cli-darwin-x64": "2.5.0",
"swytchcode-cli-linux-arm64": "2.5.0",
"swytchcode-cli-linux-x64": "2.5.0",
"swytchcode-cli-win32-arm64": "2.5.0",
"swytchcode-cli-win32-x64": "2.5.0"
}
}