insta 0.0.43 → 0.0.44

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/README.md CHANGED
@@ -157,7 +157,7 @@ That host is a CloudFront cache, so after a change to the installer it can serve
157
157
  previous copy for up to about a day. This form is equivalent and always current:
158
158
 
159
159
  ```bash
160
- curl -fsSL https://raw.githubusercontent.com/InsForge/insta-cli/main/install.sh | sh -s -- --agents --staging -y
160
+ curl -fsSL https://raw.githubusercontent.com/InsForge/insta-cli/main/install.sh | sh -s -- --agents --staging
161
161
  ```
162
162
 
163
163
  If the environment cannot be applied — an installed CLI older than 0.0.23 has no `insta
@@ -6,7 +6,7 @@
6
6
  // Stack skills (tigris/better-auth) intentionally stay per-project: their presence in a
7
7
  // project doubles as its stack manifest — that install happens on `project create|link`.
8
8
  import { spawn } from 'node:child_process';
9
- import { existsSync, readFileSync, statSync } from 'node:fs';
9
+ import { closeSync, createReadStream, existsSync, openSync, readFileSync, statSync } from 'node:fs';
10
10
  import { dirname, join } from 'node:path';
11
11
  import os from 'node:os';
12
12
  import { createInterface } from 'node:readline';
@@ -357,8 +357,55 @@ export function shouldOfferLogin(yes, loggedIn, stdinTty, stdoutTty) {
357
357
  // One Enter continues into the browser login; only an explicit n/no declines. Matches the
358
358
  // curl-installer feel: the single command carries you as far as automation can go, and the one
359
359
  // genuinely human step (authorizing in the browser) starts itself instead of being homework.
360
+ // Where to read the answer from. Under `curl … | sh` stdin is the SCRIPT pipe, not the human —
361
+ // but the controlling terminal can still answer, via /dev/tty (the standard installer trick;
362
+ // Homebrew prompts the same way). Never on Windows (no /dev/tty, and the curl path doesn't exist
363
+ // there), and never without one (agents, CI, cron — openSync fails, so they can't be prompted).
364
+ /** Wrap an already-open fd as a prompt input with SINGLE-OWNER cleanup: the stream owns the fd
365
+ * (autoClose), close() only destroys the stream, and post-close stream errors are swallowed.
366
+ * ReadStream.destroy() closes the fd asynchronously on Node 20, so a second closeSync here
367
+ * would race it into an unhandled EBADF right after the user answers. */
368
+ export function makePromptSource(fd) {
369
+ const stream = createReadStream('', { fd, autoClose: true });
370
+ stream.on('error', () => { });
371
+ return { input: stream, close: () => { try {
372
+ stream.destroy();
373
+ }
374
+ catch { /* already destroyed */ } } };
375
+ }
376
+ const openPromptInput = () => {
377
+ if (process.stdin.isTTY)
378
+ return { input: process.stdin, close: () => { } };
379
+ if (process.platform === 'win32')
380
+ return null;
381
+ try {
382
+ return makePromptSource(openSync('/dev/tty', 'r'));
383
+ }
384
+ catch {
385
+ return null;
386
+ }
387
+ };
388
+ /** Whether a human can answer a prompt at all: an interactive stdin, or a reachable /dev/tty
389
+ * (the curl|sh case). The stdout-TTY requirement lives in shouldOfferLogin — an agent piping
390
+ * our output must never be prompted even though its process may have a controlling terminal. */
391
+ export function canPromptViaTty() {
392
+ if (process.stdin.isTTY)
393
+ return true;
394
+ if (process.platform === 'win32')
395
+ return false;
396
+ try {
397
+ closeSync(openSync('/dev/tty', 'r'));
398
+ return true;
399
+ }
400
+ catch {
401
+ return false;
402
+ }
403
+ }
360
404
  const defaultAsk = async (question) => {
361
- const rl = createInterface({ input: process.stdin, output: process.stdout });
405
+ const src = openPromptInput();
406
+ if (!src)
407
+ return false; // gate said yes but the terminal vanished — decline, never hang
408
+ const rl = createInterface({ input: src.input, output: process.stdout });
362
409
  // EOF (Ctrl-D) closes the interface without ever answering the question — resolve that as a
363
410
  // decline instead of hanging forever after the checkmarks.
364
411
  const answer = await new Promise((resolve) => {
@@ -366,12 +413,13 @@ const defaultAsk = async (question) => {
366
413
  rl.question(question, resolve);
367
414
  });
368
415
  rl.close();
416
+ src.close();
369
417
  return !/^n/i.test(answer.trim());
370
418
  };
371
419
  export async function setupAgent(opts, run = defaultRunner, mint, installConfigs = installAgentConfigs, ensure = (r) => ensureCliInstalled(r), readStored = readPersistedGlobal, switchEnv = (n) => envUse(n), loginFlow = {
372
420
  ask: defaultAsk,
373
421
  login: () => loginOauth('github', {}),
374
- stdinTty: !!process.stdin.isTTY,
422
+ stdinTty: canPromptViaTty(),
375
423
  stdoutTty: !!process.stdout.isTTY,
376
424
  }) {
377
425
  if (!opts.yes && !process.stdout.isTTY) {
@@ -445,7 +493,7 @@ export async function setupAgent(opts, run = defaultRunner, mint, installConfigs
445
493
  // (project create/link, deploys, login via the device flow), so the human just asks for the
446
494
  // thing they actually want.
447
495
  info(loggedIn
448
- ? 'next: open your coding agent inside your app and ask it to "deploy this app on InstaCloud"'
449
- : 'next: open your coding agent inside your app and ask it to "deploy this app on InstaCloud" — it will walk you through `insta login`');
496
+ ? 'next: open your coding agent inside your app and start building — ask it to "deploy this app on InstaCloud" when you\'re ready'
497
+ : 'next: open your coding agent inside your app and start building — ask it to "deploy this app on InstaCloud" when you\'re ready (it will walk you through `insta login`)');
450
498
  }
451
499
  //# sourceMappingURL=setup.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "insta",
3
- "version": "0.0.43",
3
+ "version": "0.0.44",
4
4
  "type": "module",
5
5
  "description": "InstaCloud CLI — a thin client of the platform control-plane API.",
6
6
  "keywords": [