nukebase 2.0.0 → 2.0.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/nukebase.js CHANGED
@@ -20,6 +20,9 @@ function usage() {
20
20
 
21
21
  Usage: nukebase <command> (or: npx nukebase <command>)
22
22
 
23
+ New here? Run: npx nukebase start
24
+ signs you in, creates a project and downloads it, ready to build
25
+
23
26
  Accounts
24
27
  login sign in by approving in your browser
25
28
  login --email you@x.com sign in as a specific account (adds it; others stay)
@@ -33,6 +36,8 @@ Accounts
33
36
  whoami who owns this project, and are you signed in
34
37
 
35
38
  New projects
39
+ start [name] [size] outside a project: sign in if needed, then create
40
+ (inside a project, start = restart)
36
41
  create <name> [size] [email]
37
42
  make a project and download it, e.g.
38
43
  nukebase create demoapp123 256mb you@example.com
@@ -79,15 +84,19 @@ dashboard under Signed-in devices).
79
84
  Sign-ins are kept in ~/.nukebase/credentials.json, never in a project.`);
80
85
  }
81
86
 
82
- const command = args.command;
87
+ let command = args.command;
88
+
89
+ // `start` means two things: inside a project it restarts the app (as it always
90
+ // has); anywhere else it is the new-user setup (sign in, create, download).
91
+ if (command === 'start' && !require('../lib/project').findProject(process.cwd())) command = 'setup';
83
92
 
84
93
  if (args.has('version') && !command) {
85
94
  console.log(version);
86
95
  } else if (!command || command === 'help' || (args.has('help') && !command)) {
87
96
  usage();
88
- } else if (ACCOUNT_COMMANDS.includes(command)) {
97
+ } else if (ACCOUNT_COMMANDS.includes(command) || command === 'setup') {
89
98
  Promise.resolve()
90
- .then(() => require('../lib/account-commands')[command](args))
99
+ .then(() => require('../lib/account-commands')[command === 'setup' ? 'start' : command](args))
91
100
  .then(() => process.exit(0), err => { console.error('❌', err.message); process.exit(1); });
92
101
  } else if (PROJECT_COMMANDS.includes(command)) {
93
102
  require('../lib/project-commands');
@@ -295,6 +295,10 @@ async function createCommand(args) {
295
295
  let msg = '';
296
296
  try { msg = (await res.json()).error; } catch {}
297
297
  if (res.status === 401) fail(`The sign-in for ${acct.label} is no longer valid. Run: nukebase login --email ${acct.label}`);
298
+ // No plan, or no credit: both are fixed on the dashboard.
299
+ if (res.status === 403 && /plan|funds|credit|wallet|balance/i.test(msg)) {
300
+ fail(`${msg}\n Choose a plan or add credit at ${api}/`);
301
+ }
298
302
  fail(`Create failed (${res.status}): ${msg || res.statusText}`);
299
303
  }
300
304
 
@@ -319,6 +323,44 @@ async function createCommand(args) {
319
323
  console.log(' nukebase push deploy your changes');
320
324
  }
321
325
 
326
+ // ---- start ----------------------------------------------------------------
327
+ // The one command a new user runs after signing up: sign in if needed, then
328
+ // create a project and download it. `nukebase start [name] [size] [email]`.
329
+ // (Inside a project folder `start` means restart; bin/nukebase.js routes it.)
330
+
331
+ async function startCommand(args) {
332
+ const autoYes = args.has('yes');
333
+ const { parse } = require('./args');
334
+ const passFlags = [];
335
+ for (const f of ['api', 'region']) if (args.get(f)) passFlags.push(`--${f}`, args.get(f));
336
+ if (autoYes) passFlags.push('--yes');
337
+ if (args.has('no-install')) passFlags.push('--no-install');
338
+
339
+ console.log('\n Welcome to NukeBase.');
340
+ const emailArg = args.positional.find(a => a.includes('@'));
341
+ if (!creds.list().length || (emailArg && !creds.find(emailArg))) {
342
+ console.log('\n Step 1 of 2: sign in. Click Allow in the browser window that opens.');
343
+ await loginCommand(parse(['login', ...(emailArg ? ['--email', emailArg] : []), ...passFlags]));
344
+ }
345
+
346
+ console.log(`\n ${creds.list().length ? 'Step 2 of 2: ' : ''}create your project`);
347
+ let name = args.positional.find(a => !a.includes('@') && PROJECT_NAME_RE.test(a));
348
+ if (!name) {
349
+ if (autoYes || !process.stdin.isTTY) fail('Give the project a name, e.g.: nukebase start myapp');
350
+ for (;;) {
351
+ name = (await makeAskLine(false)('\n Project name (lowercase, e.g. myapp): ')).trim().toLowerCase();
352
+ if (!name) fail('No name given.');
353
+ if (PROJECT_NAME_RE.test(name)) break;
354
+ console.log(' 3-31 characters: lowercase letters, numbers, - and _, starting with a letter.');
355
+ }
356
+ }
357
+ const rest = args.positional.filter(a => a !== name);
358
+ await createCommand(parse(['create', name, ...rest, ...passFlags]));
359
+
360
+ console.log('\n Building with Claude Code? Open the folder and describe your app.');
361
+ console.log(' Its CLAUDE.md explains NukeBase, and Claude can deploy with: npx nukebase push --yes');
362
+ }
363
+
322
364
  // ---- link -----------------------------------------------------------------
323
365
 
324
366
  async function linkCommand(args) {
@@ -364,6 +406,7 @@ module.exports = {
364
406
  use: useCommand,
365
407
  whoami: whoamiCommand,
366
408
  create: createCommand,
409
+ start: startCommand,
367
410
  link: linkCommand,
368
411
  resolveTier, sizeToMb
369
412
  };
package/lib/login.js CHANGED
@@ -98,7 +98,13 @@ async function browser(api, { email } = {}) {
98
98
  console.log(' Waiting for approval… (Ctrl+C to cancel)');
99
99
  openBrowser(url.toString());
100
100
 
101
- const q = await result;
101
+ // The usual reason for a long wait: no account yet, so no sign-in email
102
+ // ever arrives (the server doesn't say which addresses have accounts).
103
+ const hint = setTimeout(() => {
104
+ console.log(`\n Still waiting. No account yet? Sign up at ${api}/start.html first, then run this again.`);
105
+ }, 90 * 1000);
106
+ hint.unref();
107
+ const q = await result.finally(() => clearTimeout(hint));
102
108
  if (q.error) throw new Error('Sign-in was cancelled in the browser.');
103
109
  if (!q.code) throw new Error('The browser did not return a sign-in code.');
104
110
  const r = await postJson(`${api}/cli/token`, { code: q.code, verifier });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nukebase",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "NukeBase CLI: create, deploy and manage NukeBase projects",
5
5
  "bin": {
6
6
  "nukebase": "bin/nukebase.js"