swytchcode
Advanced tools
+445
-34
| #!/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" | ||
| } | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
21810
266.12%454
446.99%4
300%