swytchcode 2.6.0 → 2.6.2

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.
Files changed (2) hide show
  1. package/bin/swytchcode.js +90 -43
  2. package/package.json +7 -7
package/bin/swytchcode.js CHANGED
@@ -1,6 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
+ process.on('SIGINT', () => {
5
+ try { process.stdin.setRawMode(false); } catch (_) {} // prevent stuck raw mode on Windows CMD
6
+ process.stdout.write('\x1b[?25h'); // restore cursor
7
+ process.exit(130);
8
+ });
9
+
4
10
  const { spawnSync, execSync } = require('child_process');
5
11
  const fs = require('fs');
6
12
  const path = require('path');
@@ -45,7 +51,20 @@ function stripAnsi(str) {
45
51
  function ask(prompt) {
46
52
  return new Promise(resolve => {
47
53
  const iface = rl.createInterface({ input: process.stdin, output: process.stdout });
48
- iface.question(prompt, answer => { iface.close(); resolve(answer.trim()); });
54
+
55
+ function onClose() {
56
+ try { process.stdin.setRawMode(false); } catch (_) {}
57
+ process.stdout.write('\x1b[?25h');
58
+ process.exit(130);
59
+ }
60
+
61
+ iface.on('close', onClose);
62
+
63
+ iface.question(prompt, answer => {
64
+ iface.removeListener('close', onClose); // only removes this specific listener
65
+ iface.close();
66
+ resolve(answer.trim());
67
+ });
49
68
  });
50
69
  }
51
70
 
@@ -188,17 +207,27 @@ function apiPost(urlStr, body) {
188
207
  // ─── Binary resolution (shared by normal pass-through AND onboarding) ────────
189
208
  function spawnBin(binPath, args, extraEnv = {}) {
190
209
  const env = { ...process.env, ...extraEnv };
210
+ let result;
191
211
  if (process.stdin.isTTY) {
192
- return spawnSync(binPath, args, { stdio: 'inherit', env });
212
+ result = spawnSync(binPath, args, { stdio: 'inherit', env });
213
+ } else {
214
+ let stdinData;
215
+ try { stdinData = fs.readFileSync(0); }
216
+ catch (_) { stdinData = Buffer.alloc(0); }
217
+ result = spawnSync(binPath, args, {
218
+ stdio: ['pipe', 'inherit', 'inherit'],
219
+ input: stdinData,
220
+ env,
221
+ });
193
222
  }
194
- let stdinData;
195
- try { stdinData = fs.readFileSync(0); }
196
- catch (_) { stdinData = Buffer.alloc(0); }
197
- return spawnSync(binPath, args, {
198
- stdio: ['pipe', 'inherit', 'inherit'],
199
- input: stdinData,
200
- env,
201
- });
223
+
224
+ if (result.signal === 'SIGINT') {
225
+ try { process.stdin.setRawMode(false); } catch (_) {}
226
+ process.stdout.write('\x1b[?25h');
227
+ process.exit(130);
228
+ }
229
+
230
+ return result;
202
231
  }
203
232
 
204
233
  function exitCode(result) {
@@ -271,6 +300,8 @@ const userArgs = process.argv.slice(2);
271
300
  // Capture original arg BEFORE mutation
272
301
  const originalArg = process.argv[2] || '';
273
302
  const isCanonicalDemoArg = /^[\w]+\.[\w.]+$/.test(originalArg) && !originalArg.startsWith('-');
303
+ // A single bare word under npx is a potential (invalid) demo lookup — don't pass through to cobra
304
+ const isNpxBareWordArg = isNpx && userArgs.length === 1 && /^[\w]+$/.test(originalArg) && !originalArg.startsWith('-');
274
305
  // Legacy support: npx swytchcode <canonical_id> → npx swytchcode exec <canonical_id>
275
306
  if (userArgs.length > 0 && /^[\w]+\.[\w.]+$/.test(userArgs[0]) && !userArgs[0].startsWith('-')) {
276
307
  userArgs.unshift('exec');
@@ -293,28 +324,9 @@ async function runOnboardingDirectDemo(canonicalId) {
293
324
  await runOnboardingSteps2to4();
294
325
  }
295
326
 
296
- // ─── Normal pass-through (not npx, or npx with non-demo args) ───────────────
297
- if (!isNpx || (userArgs.length > 0 && !isCanonicalDemoArg)) {
298
- const result = spawnBin(binPath, userArgs);
299
- if (result.error) {
300
- err(`swytchcode: failed to launch binary: ${result.error.message}`);
301
- }
302
- process.exit(exitCode(result));
303
- }
304
- // ─── NPX with canonical ID: run demo directly then show onboarding ───────────
305
- if (isNpx && isCanonicalDemoArg) {
306
- runOnboardingDirectDemo(originalArg).catch(e => {
307
- err(`\nswytchcode: onboarding error (${e.message}) — falling back to CLI.\n`);
308
- const result = spawnBin(binPath, userArgs);
309
- process.exit(exitCode(result));
310
- });
311
- } else {
312
- // ─── NPX ONBOARDING FLOW ─────────────────────────────────────────────────────
313
- // Only reached when: isNpx === true AND no args passed (bare `npx swytchcode`)
314
-
327
+ // ─── HTTP helpers (used by both canonical-demo and bare-npx onboarding paths) ─
315
328
  const API_BASE = 'https://api-v2.swytchcode.com/v2/cli';
316
329
 
317
- // Helper to perform HTTP GET (declared inside so it doesn't conflict)
318
330
  function apiGet(urlStr) {
319
331
  return new Promise((resolve, reject) => {
320
332
  const parsed = new URL(urlStr);
@@ -339,6 +351,43 @@ function apiGet(urlStr) {
339
351
  });
340
352
  }
341
353
 
354
+ // ─── Normal pass-through (not npx, or npx with non-demo args) ───────────────
355
+ if (!isNpx || (userArgs.length > 0 && !isCanonicalDemoArg && !isNpxBareWordArg)) {
356
+ const result = spawnBin(binPath, userArgs);
357
+ if (result.error) {
358
+ err(`swytchcode: failed to launch binary: ${result.error.message}`);
359
+ }
360
+ process.exit(exitCode(result));
361
+ }
362
+
363
+ // ─── NPX routing: three mutually exclusive paths ─────────────────────────────
364
+ if (isNpx && isCanonicalDemoArg) {
365
+ // Dotted canonical ID (e.g. stripe.create_payment) — run demo then onboard
366
+ runOnboardingDirectDemo(originalArg).catch(e => {
367
+ err(`\nswytchcode: onboarding error (${e.message}) — falling back to CLI.\n`);
368
+ const result = spawnBin(binPath, userArgs);
369
+ process.exit(exitCode(result));
370
+ });
371
+ } else if (isNpxBareWordArg) {
372
+ // Single bare word (e.g. "abcd") — not a valid canonical ID; show message then onboard
373
+ writeln();
374
+ writeln(` ${c.yellow}⚠ ${c.bold}${originalArg}${c.reset}${c.yellow} isn't a valid demo tool — canonical IDs look like ${c.bold}provider.method${c.reset}`);
375
+ writeln(` ${c.dim}Let's run a working demo instead.${c.reset}`);
376
+ writeln();
377
+ Promise.resolve()
378
+ .then(() => runDemoSelection())
379
+ .then(() => runOnboardingSteps2to4())
380
+ .catch(e => {
381
+ err(`\nswytchcode: onboarding error (${e.message}) — falling back to CLI.\n`);
382
+ process.exit(1);
383
+ });
384
+ } else {
385
+ // Bare npx swytchcode (no args) — full onboarding with welcome banner
386
+ runOnboarding().catch(e => {
387
+ err(`\nswytchcode: onboarding error (${e.message}) — falling back to CLI.\n`);
388
+ const result = spawnBin(binPath, userArgs);
389
+ process.exit(exitCode(result));
390
+ });
342
391
  }
343
392
 
344
393
  async function runDemoSelection() {
@@ -371,10 +420,7 @@ async function runDemoSelection() {
371
420
  if (chosen !== '__skip__') {
372
421
  writeln();
373
422
  writeln(` ${c.gray}$ swytchcode demo stripe${c.reset}`);
374
- spawnSync(binPath, ['demo', 'stripe'], {
375
- stdio: 'inherit',
376
- env: { ...process.env, SWYTCHCODE_ONBOARDING: '1' }
377
- });
423
+ spawnBin(binPath, ['demo', 'stripe'], { SWYTCHCODE_ONBOARDING: '1' });
378
424
  writeln();
379
425
  writeln(` ${c.dim}That was a real API call. No credentials, no setup.${c.reset}`);
380
426
  writeln(` ${c.dim}With Swytchcode, every API integration works exactly like this.${c.reset}`);
@@ -445,6 +491,12 @@ async function runOnboardingSteps2to4() {
445
491
  execSync('npm install swytchcode', { stdio: 'pipe' });
446
492
  installSpin.stop('Installed successfully');
447
493
  } catch (e) {
494
+ if (e.signal === 'SIGINT') {
495
+ installSpin.stop('Cancelled');
496
+ try { process.stdin.setRawMode(false); } catch (_) {}
497
+ process.stdout.write('\x1b[?25h');
498
+ process.exit(130);
499
+ }
448
500
  installSpin.fail('Installation failed.');
449
501
  }
450
502
 
@@ -472,7 +524,7 @@ async function runOnboardingSteps2to4() {
472
524
  if (authChoice === 'login') {
473
525
  writeln(` ${c.dim}Opening browser for login ...${c.reset}`);
474
526
  writeln();
475
- const result = spawnSync(binPath, ['login'], { stdio: 'inherit' });
527
+ const result = spawnBin(binPath, ['login']);
476
528
  if (result.error || result.status !== 0) {
477
529
  writeln(` ${c.yellow}Login window closed or failed.${c.reset}`);
478
530
  writeln(` ${c.dim}You can log in later with:${c.reset} ${c.cyan}swytchcode login${c.reset}`);
@@ -483,7 +535,7 @@ async function runOnboardingSteps2to4() {
483
535
  writeln(` ${c.dim}Opening browser to create your account ...${c.reset}`);
484
536
  writeln();
485
537
  // login command handles both login and registration via the browser flow
486
- const result = spawnSync(binPath, ['login'], { stdio: 'inherit' });
538
+ const result = spawnBin(binPath, ['login']);
487
539
  if (result.error || result.status !== 0) {
488
540
  writeln(` ${c.yellow}Browser window closed or failed.${c.reset}`);
489
541
  writeln(` ${c.dim}Create your account at:${c.reset} ${c.cyan}https://swytchcode.com/signup${c.reset}`);
@@ -513,14 +565,14 @@ async function runOnboardingSteps2to4() {
513
565
  if (nextAction === 'init') {
514
566
  writeln(` ${c.dim}Starting project setup ...${c.reset}`);
515
567
  writeln();
516
- const result = spawnSync(binPath, ['init'], { stdio: 'inherit' });
568
+ const result = spawnBin(binPath, ['init']);
517
569
  if (result.error) {
518
570
  writeln(` ${c.red}Could not launch swytchcode init: ${result.error.message}${c.reset}`);
519
571
  }
520
572
  } else if (nextAction === 'examples') {
521
573
  writeln(` ${c.dim}Loading examples ...${c.reset}`);
522
574
  writeln();
523
- const result = spawnSync(binPath, ['examples'], { stdio: 'inherit' });
575
+ const result = spawnBin(binPath, ['examples']);
524
576
  if (result.error) {
525
577
  writeln(` ${c.red}Could not launch swytchcode examples: ${result.error.message}${c.reset}`);
526
578
  }
@@ -540,8 +592,3 @@ async function runOnboardingSteps2to4() {
540
592
  writeln();
541
593
  }
542
594
 
543
- runOnboarding().catch(e => {
544
- err(`\nswytchcode: onboarding error (${e.message}) — falling back to CLI.\n`);
545
- const result = spawnBin(binPath, userArgs);
546
- process.exit(exitCode(result));
547
- });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swytchcode",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
4
4
  "description": "Execution authority layer for API integrations",
5
5
  "keywords": [
6
6
  "api",
@@ -23,11 +23,11 @@
23
23
  "README.md"
24
24
  ],
25
25
  "optionalDependencies": {
26
- "swytchcode-cli-darwin-arm64": "2.6.0",
27
- "swytchcode-cli-darwin-x64": "2.6.0",
28
- "swytchcode-cli-linux-arm64": "2.6.0",
29
- "swytchcode-cli-linux-x64": "2.6.0",
30
- "swytchcode-cli-win32-arm64": "2.6.0",
31
- "swytchcode-cli-win32-x64": "2.6.0"
26
+ "swytchcode-cli-darwin-arm64": "2.6.2",
27
+ "swytchcode-cli-darwin-x64": "2.6.2",
28
+ "swytchcode-cli-linux-arm64": "2.6.2",
29
+ "swytchcode-cli-linux-x64": "2.6.2",
30
+ "swytchcode-cli-win32-arm64": "2.6.2",
31
+ "swytchcode-cli-win32-x64": "2.6.2"
32
32
  }
33
33
  }