swytchcode 2.5.2 → 2.6.1
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.
- package/bin/swytchcode.js +125 -58
- 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
|
-
|
|
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
|
|
|
@@ -186,17 +205,29 @@ function apiPost(urlStr, body) {
|
|
|
186
205
|
}
|
|
187
206
|
|
|
188
207
|
// ─── Binary resolution (shared by normal pass-through AND onboarding) ────────
|
|
189
|
-
function spawnBin(binPath, args) {
|
|
208
|
+
function spawnBin(binPath, args, extraEnv = {}) {
|
|
209
|
+
const env = { ...process.env, ...extraEnv };
|
|
210
|
+
let result;
|
|
190
211
|
if (process.stdin.isTTY) {
|
|
191
|
-
|
|
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
|
+
});
|
|
192
222
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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;
|
|
200
231
|
}
|
|
201
232
|
|
|
202
233
|
function exitCode(result) {
|
|
@@ -266,22 +297,47 @@ const isNpx =
|
|
|
266
297
|
(process.env.npm_execpath || '').includes('npx');
|
|
267
298
|
|
|
268
299
|
const userArgs = process.argv.slice(2);
|
|
269
|
-
|
|
300
|
+
// Capture original arg BEFORE mutation
|
|
301
|
+
const originalArg = process.argv[2] || '';
|
|
302
|
+
const isCanonicalDemoArg = /^[\w]+\.[\w.]+$/.test(originalArg) && !originalArg.startsWith('-');
|
|
270
303
|
// Legacy support: npx swytchcode <canonical_id> → npx swytchcode exec <canonical_id>
|
|
271
304
|
if (userArgs.length > 0 && /^[\w]+\.[\w.]+$/.test(userArgs[0]) && !userArgs[0].startsWith('-')) {
|
|
272
305
|
userArgs.unshift('exec');
|
|
273
306
|
}
|
|
307
|
+
async function runOnboardingDirectDemo(canonicalId) {
|
|
308
|
+
writeln();
|
|
309
|
+
writeln(` ${c.yellow}⚡ Running ${c.bold}${canonicalId}${c.reset}${c.yellow} live...${c.reset}`);
|
|
310
|
+
writeln();
|
|
311
|
+
const demoResult = spawnBin(binPath, ['exec', canonicalId], { SWYTCHCODE_ONBOARDING: '1' });
|
|
312
|
+
writeln();
|
|
313
|
+
if (demoResult.status === 0) {
|
|
314
|
+
writeln(` ${c.dim}That was a real API call through Swytchcode.${c.reset}`);
|
|
315
|
+
writeln();
|
|
316
|
+
} else {
|
|
317
|
+
writeln(` ${c.yellow}⚠ ${c.bold}${canonicalId}${c.reset}${c.yellow} isn't a valid demo tool.${c.reset}`);
|
|
318
|
+
writeln(` ${c.dim}Let's run a working demo instead.${c.reset}`);
|
|
319
|
+
writeln();
|
|
320
|
+
await runDemoSelection();
|
|
321
|
+
}
|
|
322
|
+
await runOnboardingSteps2to4();
|
|
323
|
+
}
|
|
274
324
|
|
|
275
|
-
// ─── Normal pass-through (not npx, or npx with args)
|
|
276
|
-
if (!isNpx || userArgs.length > 0) {
|
|
325
|
+
// ─── Normal pass-through (not npx, or npx with non-demo args) ───────────────
|
|
326
|
+
if (!isNpx || (userArgs.length > 0 && !isCanonicalDemoArg)) {
|
|
277
327
|
const result = spawnBin(binPath, userArgs);
|
|
278
328
|
if (result.error) {
|
|
279
329
|
err(`swytchcode: failed to launch binary: ${result.error.message}`);
|
|
280
|
-
process.exit(1);
|
|
281
330
|
}
|
|
282
331
|
process.exit(exitCode(result));
|
|
283
332
|
}
|
|
284
|
-
|
|
333
|
+
// ─── NPX with canonical ID: run demo directly then show onboarding ───────────
|
|
334
|
+
if (isNpx && isCanonicalDemoArg) {
|
|
335
|
+
runOnboardingDirectDemo(originalArg).catch(e => {
|
|
336
|
+
err(`\nswytchcode: onboarding error (${e.message}) — falling back to CLI.\n`);
|
|
337
|
+
const result = spawnBin(binPath, userArgs);
|
|
338
|
+
process.exit(exitCode(result));
|
|
339
|
+
});
|
|
340
|
+
} else {
|
|
285
341
|
// ─── NPX ONBOARDING FLOW ─────────────────────────────────────────────────────
|
|
286
342
|
// Only reached when: isNpx === true AND no args passed (bare `npx swytchcode`)
|
|
287
343
|
|
|
@@ -311,6 +367,46 @@ function apiGet(urlStr) {
|
|
|
311
367
|
req.end();
|
|
312
368
|
});
|
|
313
369
|
}
|
|
370
|
+
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async function runDemoSelection() {
|
|
374
|
+
let demoTools = [];
|
|
375
|
+
try {
|
|
376
|
+
const res = await apiGet(`${API_BASE}/demo/tools`);
|
|
377
|
+
if (res.status === 200 && Array.isArray(res.body.tools)) {
|
|
378
|
+
demoTools = res.body.tools;
|
|
379
|
+
}
|
|
380
|
+
} catch (_) {}
|
|
381
|
+
|
|
382
|
+
if (demoTools.length === 0) {
|
|
383
|
+
writeln(`${c.yellow} Demo unavailable right now — skipping to setup.${c.reset}`);
|
|
384
|
+
writeln();
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const friendlyName = {
|
|
389
|
+
'stripe.create_payment': 'Stripe — Create a payment ($20.00)',
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const demoOptions = demoTools.map(t => ({
|
|
393
|
+
label: friendlyName[t] || t,
|
|
394
|
+
value: t,
|
|
395
|
+
}));
|
|
396
|
+
demoOptions.push({ label: 'Skip demo', value: '__skip__' });
|
|
397
|
+
|
|
398
|
+
const chosen = await menuArrow('Choose a demo to run:', demoOptions);
|
|
399
|
+
|
|
400
|
+
if (chosen !== '__skip__') {
|
|
401
|
+
writeln();
|
|
402
|
+
writeln(` ${c.gray}$ swytchcode demo stripe${c.reset}`);
|
|
403
|
+
spawnBin(binPath, ['demo', 'stripe'], { SWYTCHCODE_ONBOARDING: '1' });
|
|
404
|
+
writeln();
|
|
405
|
+
writeln(` ${c.dim}That was a real API call. No credentials, no setup.${c.reset}`);
|
|
406
|
+
writeln(` ${c.dim}With Swytchcode, every API integration works exactly like this.${c.reset}`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
314
410
|
async function runOnboarding() {
|
|
315
411
|
let cliVersion = '0.0.0';
|
|
316
412
|
try {
|
|
@@ -342,45 +438,11 @@ async function runOnboarding() {
|
|
|
342
438
|
writeln(`${c.bold} Step 1 of 4 — See it in action${c.reset}`);
|
|
343
439
|
writeln(`${c.dim} Run a live API call right now, no setup required.${c.reset}`);
|
|
344
440
|
writeln();
|
|
441
|
+
await runDemoSelection();
|
|
442
|
+
await runOnboardingSteps2to4();
|
|
443
|
+
}
|
|
345
444
|
|
|
346
|
-
|
|
347
|
-
let demoTools = [];
|
|
348
|
-
try {
|
|
349
|
-
const res = await apiGet(`${API_BASE}/demo/tools`);
|
|
350
|
-
if (res.status === 200 && Array.isArray(res.body.tools)) {
|
|
351
|
-
demoTools = res.body.tools;
|
|
352
|
-
}
|
|
353
|
-
} catch (_) {
|
|
354
|
-
// silently fall through — skip demo if API is unreachable
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (demoTools.length === 0) {
|
|
358
|
-
writeln(`${c.yellow} Demo unavailable right now — skipping to setup.${c.reset}`);
|
|
359
|
-
writeln();
|
|
360
|
-
} else {
|
|
361
|
-
// Map tool IDs to friendly names
|
|
362
|
-
const friendlyName = {
|
|
363
|
-
'stripe.create_payment': 'Stripe — Create a payment ($20.00)',
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
const demoOptions = demoTools.map(t => ({
|
|
367
|
-
label: friendlyName[t] || t,
|
|
368
|
-
value: t,
|
|
369
|
-
}));
|
|
370
|
-
demoOptions.push({ label: 'Skip demo', value: '__skip__' });
|
|
371
|
-
|
|
372
|
-
const chosen = await menuArrow('Choose a demo to run:', demoOptions);
|
|
373
|
-
|
|
374
|
-
if (chosen !== '__skip__') {
|
|
375
|
-
writeln();
|
|
376
|
-
writeln(` ${c.gray}$ swytchcode demo stripe${c.reset}`);
|
|
377
|
-
spawnSync(binPath, ['demo', 'stripe'], { stdio: 'inherit' });
|
|
378
|
-
writeln();
|
|
379
|
-
writeln(` ${c.dim}That was a real API call. No credentials, no setup.${c.reset}`);
|
|
380
|
-
writeln(` ${c.dim}With Swytchcode, every API integration works exactly like this.${c.reset}`);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
445
|
+
async function runOnboardingSteps2to4() {
|
|
384
446
|
// ── Step 2: Install ───────────────────────────────────────────────────────
|
|
385
447
|
writeln();
|
|
386
448
|
writeln(' ' + '─'.repeat(45));
|
|
@@ -409,6 +471,12 @@ async function runOnboarding() {
|
|
|
409
471
|
execSync('npm install swytchcode', { stdio: 'pipe' });
|
|
410
472
|
installSpin.stop('Installed successfully');
|
|
411
473
|
} catch (e) {
|
|
474
|
+
if (e.signal === 'SIGINT') {
|
|
475
|
+
installSpin.stop('Cancelled');
|
|
476
|
+
try { process.stdin.setRawMode(false); } catch (_) {}
|
|
477
|
+
process.stdout.write('\x1b[?25h');
|
|
478
|
+
process.exit(130);
|
|
479
|
+
}
|
|
412
480
|
installSpin.fail('Installation failed.');
|
|
413
481
|
}
|
|
414
482
|
|
|
@@ -436,7 +504,7 @@ async function runOnboarding() {
|
|
|
436
504
|
if (authChoice === 'login') {
|
|
437
505
|
writeln(` ${c.dim}Opening browser for login ...${c.reset}`);
|
|
438
506
|
writeln();
|
|
439
|
-
const result =
|
|
507
|
+
const result = spawnBin(binPath, ['login']);
|
|
440
508
|
if (result.error || result.status !== 0) {
|
|
441
509
|
writeln(` ${c.yellow}Login window closed or failed.${c.reset}`);
|
|
442
510
|
writeln(` ${c.dim}You can log in later with:${c.reset} ${c.cyan}swytchcode login${c.reset}`);
|
|
@@ -447,7 +515,7 @@ async function runOnboarding() {
|
|
|
447
515
|
writeln(` ${c.dim}Opening browser to create your account ...${c.reset}`);
|
|
448
516
|
writeln();
|
|
449
517
|
// login command handles both login and registration via the browser flow
|
|
450
|
-
const result =
|
|
518
|
+
const result = spawnBin(binPath, ['login']);
|
|
451
519
|
if (result.error || result.status !== 0) {
|
|
452
520
|
writeln(` ${c.yellow}Browser window closed or failed.${c.reset}`);
|
|
453
521
|
writeln(` ${c.dim}Create your account at:${c.reset} ${c.cyan}https://swytchcode.com/signup${c.reset}`);
|
|
@@ -477,14 +545,14 @@ async function runOnboarding() {
|
|
|
477
545
|
if (nextAction === 'init') {
|
|
478
546
|
writeln(` ${c.dim}Starting project setup ...${c.reset}`);
|
|
479
547
|
writeln();
|
|
480
|
-
const result =
|
|
548
|
+
const result = spawnBin(binPath, ['init']);
|
|
481
549
|
if (result.error) {
|
|
482
550
|
writeln(` ${c.red}Could not launch swytchcode init: ${result.error.message}${c.reset}`);
|
|
483
551
|
}
|
|
484
552
|
} else if (nextAction === 'examples') {
|
|
485
553
|
writeln(` ${c.dim}Loading examples ...${c.reset}`);
|
|
486
554
|
writeln();
|
|
487
|
-
const result =
|
|
555
|
+
const result = spawnBin(binPath, ['examples']);
|
|
488
556
|
if (result.error) {
|
|
489
557
|
writeln(` ${c.red}Could not launch swytchcode examples: ${result.error.message}${c.reset}`);
|
|
490
558
|
}
|
|
@@ -505,7 +573,6 @@ async function runOnboarding() {
|
|
|
505
573
|
}
|
|
506
574
|
|
|
507
575
|
runOnboarding().catch(e => {
|
|
508
|
-
// If onboarding crashes for any reason, fall back to normal binary pass-through
|
|
509
576
|
err(`\nswytchcode: onboarding error (${e.message}) — falling back to CLI.\n`);
|
|
510
577
|
const result = spawnBin(binPath, userArgs);
|
|
511
578
|
process.exit(exitCode(result));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swytchcode",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
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.
|
|
27
|
-
"swytchcode-cli-darwin-x64": "2.
|
|
28
|
-
"swytchcode-cli-linux-arm64": "2.
|
|
29
|
-
"swytchcode-cli-linux-x64": "2.
|
|
30
|
-
"swytchcode-cli-win32-arm64": "2.
|
|
31
|
-
"swytchcode-cli-win32-x64": "2.
|
|
26
|
+
"swytchcode-cli-darwin-arm64": "2.6.1",
|
|
27
|
+
"swytchcode-cli-darwin-x64": "2.6.1",
|
|
28
|
+
"swytchcode-cli-linux-arm64": "2.6.1",
|
|
29
|
+
"swytchcode-cli-linux-x64": "2.6.1",
|
|
30
|
+
"swytchcode-cli-win32-arm64": "2.6.1",
|
|
31
|
+
"swytchcode-cli-win32-x64": "2.6.1"
|
|
32
32
|
}
|
|
33
33
|
}
|