nukebase 2.2.0 → 2.2.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
@@ -33,7 +33,9 @@ function usage() {
33
33
 
34
34
  Usage: nukebase <command> (or: npx nukebase <command>)
35
35
 
36
- New here? Run: npx nukebase start
36
+ New here? Run: npx nukebase guide
37
+ what NukeBase can do and how to plan your app with Claude; creates nothing
38
+ Ready to set up? Run: npx nukebase start
37
39
  signs you in, creates a project and downloads it, ready to build
38
40
 
39
41
  Accounts
@@ -80,7 +82,7 @@ In a project folder (runs as the account that owns the project)
80
82
  domain <name> [--email e] connect in one go (DNS already set)
81
83
  domain remove disconnect the custom domain
82
84
  domain email send the app's emails from the custom domain
83
- (--root | --sub, --reply-to you@gmail.com, --wait)
85
+ (--root | --sub, --wait)
84
86
  domain email remove go back to sending from nukebase.com
85
87
  logs [--follow] recent log output; --follow streams
86
88
  download [--out f.zip] the project ZIP, exactly as the server has it
@@ -122,6 +124,8 @@ if (command === 'start') {
122
124
 
123
125
  if (args.has('version') && !command) {
124
126
  console.log(version);
127
+ } else if (command === 'guide') {
128
+ require('../lib/guide').guideCommand();
125
129
  } else if (!command || command === 'help' || (args.has('help') && !command)) {
126
130
  usage();
127
131
  } else if (ACCOUNT_COMMANDS.includes(command) || command === 'setup') {
package/lib/args.js CHANGED
@@ -6,7 +6,7 @@
6
6
  // `--name=value` works too.
7
7
 
8
8
  const VALUE_FLAGS = new Set([
9
- 'region', 'api', 'as', 'tier', 'streams', 'out', 'email', 'to', 'include', 'name', 'reply-to'
9
+ 'region', 'api', 'as', 'tier', 'streams', 'out', 'email', 'to', 'include', 'name', 'reply-to', 'from'
10
10
  ]);
11
11
 
12
12
  const SHORT = { y: 'yes', f: 'follow', h: 'help', v: 'version' };
package/lib/guide.js ADDED
@@ -0,0 +1,116 @@
1
+ // `nukebase guide`: the front door for someone who is not a coder.
2
+ //
3
+ // It creates nothing and signs nothing in. It prints a briefing written for
4
+ // the AI assistant the person is talking to (usually Claude Code), so the
5
+ // assistant knows what NukeBase can do and holds a conversation about the
6
+ // app before `nukebase start` creates anything. The person's own words on the
7
+ // landing page are just: "Run npx nukebase@latest guide, then help me build
8
+ // my app."
9
+ //
10
+ // No prices here on purpose: they change, and the dashboard shows them.
11
+
12
+ const GUIDE = `
13
+ NUKEBASE GUIDE
14
+ ==============
15
+
16
+ For the AI assistant reading this
17
+ ---------------------------------
18
+ The person you are helping may not be a coder. They want to build an app
19
+ with NukeBase. Nothing has been created yet, and nothing should be until they
20
+ agree to a plan.
21
+
22
+ 1. Have a short, friendly conversation to understand what they want
23
+ (questions below). Ask one or two at a time, in plain words. No jargon:
24
+ never ask them about databases, frameworks or APIs.
25
+ 2. Play the plan back in a few plain sentences, including the project name,
26
+ and ask: "Shall I set it up?"
27
+ 3. Only after they say yes, run the setup command (Commands, below), then
28
+ read the CLAUDE.md file in the new project folder before writing any
29
+ code. It explains how NukeBase apps are built.
30
+ 4. Save the agreed plan as PLAN.md in the project folder, so the next
31
+ session knows what was agreed.
32
+ 5. Build it, put it live, and show them the link. Then offer the extras
33
+ they would want: their own domain, emails from that domain.
34
+
35
+ What NukeBase is
36
+ ----------------
37
+ One place to build and run a web app: the website, its data, user sign-in,
38
+ email and hosting all come together, with nothing else to sign up for or
39
+ set up. Apps work on phones and computers and can be added to a phone's home
40
+ screen like an app from the store.
41
+
42
+ What it can do (features)
43
+ -------------------------
44
+ Website hosting Live at its own web address the moment it is set up.
45
+ Live data A built-in database. When one person changes
46
+ something, everyone else's screen updates instantly
47
+ (no refreshing): bookings, chats, sign-up lists.
48
+ User sign-in People sign in with a link sent to their email. No
49
+ passwords. One browser can stay signed in to several
50
+ accounts and switch between them.
51
+ Who can see what Rules for which users can read or change which data
52
+ (each user sees only their own orders, say).
53
+ File uploads Photos, documents and other files, stored with the app.
54
+ Emails Sign-in emails out of the box. With the person's own
55
+ domain: any email (reminders, receipts, notifications)
56
+ from addresses like support@theirdomain.com, and a
57
+ log of what was delivered or bounced.
58
+ Phone notifications Push notifications to phones and computers.
59
+ Works offline Pages open without signal; changes made offline are
60
+ sent when the connection comes back.
61
+ Own domain Their domain (theirbusiness.com) with SSL included.
62
+ Server code Logic that runs on the server: scheduled jobs,
63
+ actions triggered when data changes, calls to other
64
+ services (Stripe for payments, for example; payments
65
+ are not built in, but can be added).
66
+ Safety net Every deploy can be undone, and the data can be
67
+ backed up to their computer at any time.
68
+ Secrets API keys for other services, kept out of the code.
69
+ Sizes 128 MB, 256 MB, 512 MB or 1 GB. The default is fine to
70
+ start; it can be changed at any time from the
71
+ dashboard.
72
+
73
+ Good fits: booking and scheduling, member or client portals, sign-up lists
74
+ and waitlists, small shops and catalogs, dashboards and tools for a business,
75
+ chat and anything live, community and club sites, simple games.
76
+ Not the right tool: video processing, training AI models, or very large data
77
+ warehouses.
78
+
79
+ Questions to ask (adapt them; skip what is already clear)
80
+ ----------------------------------------------------------
81
+ - What do you want to build, and who is it for?
82
+ - What should people be able to do in it? (book a time, post, order, ...)
83
+ - Do people need to sign in? Is there an admin (you) with extra powers?
84
+ - Should it send emails, like confirmations or reminders?
85
+ - Phone notifications? Uploading photos or files?
86
+ - Do you have a domain name you want to use, or one in mind?
87
+ - What do you want your project to be named?
88
+ (lowercase letters, numbers and dashes, 3 to 31 characters, starting
89
+ with a letter, e.g. "bloom-bookings". It becomes part of the web address.)
90
+
91
+ Commands
92
+ --------
93
+ Setup (run in an empty folder; it signs them in first if needed: a browser
94
+ window opens to confirm, or a short code to type on any device):
95
+ npx nukebase@latest start <project-name> --yes
96
+
97
+ Then, inside the project folder:
98
+ npx nukebase push --yes put the latest version live
99
+ npx nukebase logs see what the app printed (errors too)
100
+ npx nukebase domain connect their own domain, step by step
101
+ npx nukebase domain email send the app's emails from that domain
102
+ npx nukebase rollback undo the last push (their data is kept)
103
+ npx nukebase backup copy the app's data to this computer
104
+ npx nukebase env push send secrets from .env to the server
105
+ npx nukebase pause / restart stop the app, or start it again
106
+ npx nukebase help every command
107
+
108
+ The dashboard at https://nukebase.com shows all their apps, usage and
109
+ billing, and does most of the above with buttons too.
110
+ `;
111
+
112
+ function guideCommand() {
113
+ console.log(GUIDE.trimEnd() + '\n');
114
+ }
115
+
116
+ module.exports = { guideCommand, GUIDE };
@@ -945,11 +945,12 @@ async function domainCommand() {
945
945
  }
946
946
  }
947
947
 
948
- // nukebase domain email [--root | --sub] [--reply-to you@gmail.com] [--wait]
948
+ // nukebase domain email [--root | --sub] [--wait]
949
949
  // nukebase domain email remove
950
950
  //
951
- // Send the app's emails (sign-in links today) from its custom domain:
952
- // login@theirapp.com, replies to --reply-to. The sending domain is either the
951
+ // Send the app's emails from its custom domain. The setup is only the
952
+ // domain and its DNS: the address, sender name and Reply-To are chosen per
953
+ // email in code (sendEmail's `from` / `replyTo`). The sending domain is either the
953
954
  // custom domain itself (--root) or mail.<domain> (--sub; safest when the
954
955
  // domain already has email). Prints the two DNS records to add, then waits
955
956
  // for the mail provider to verify them (Enter to check now, Ctrl+C to leave
@@ -970,18 +971,22 @@ async function emailDomainCommand(action) {
970
971
  const current = check.current;
971
972
  const wantRoot = args.has('root') ? true : args.has('sub') ? false : null;
972
973
  const replyTo = args.get('reply-to');
974
+ const fromArg = args.get('from');
973
975
  const changing = wantRoot !== null && (!current || current.root !== wantRoot);
974
976
 
975
977
  let rec = current;
976
- if (!current || changing || replyTo !== undefined) {
978
+ if (!current || changing || replyTo !== undefined || fromArg !== undefined) {
979
+ // --from / --reply-to are only carried by a region transfer, for setups
980
+ // made when those were part of it.
981
+ const from = fromArg;
977
982
  let root = wantRoot;
978
983
  if (root === null) root = current ? current.root : null;
979
984
  if (root === null) {
980
985
  const suggestRoot = check.suggested === 'root';
981
986
  console.log(`\n✉️ Send ${project}'s emails from ${domain}\n`);
982
- console.log(` 1) login@${domain}`);
987
+ console.log(` 1) anything@${domain}`);
983
988
  console.log(` ${check.hasEmail ? 'This domain already has email set up' + (check.spf ? ' (you will update its SPF record)' : '') + '.' : 'Recommended: this domain has no email set up yet.'}`);
984
- console.log(` 2) login@${domain}, sent via mail.${domain}`);
989
+ console.log(` 2) anything@${domain}, sent via mail.${domain}`);
985
990
  console.log(' Safest if you already use email on this domain: its records are not touched.\n');
986
991
  if (process.stdin.isTTY && !AUTO_YES) {
987
992
  const a = (await askLine(` Choose 1 or 2 [${suggestRoot ? 1 : 2}]: `)).trim();
@@ -990,11 +995,9 @@ async function emailDomainCommand(action) {
990
995
  root = suggestRoot;
991
996
  }
992
997
  }
993
- let reply = replyTo;
994
- if (reply === undefined && !current && process.stdin.isTTY && !AUTO_YES) {
995
- reply = (await askLine(' Where should replies go? (e.g. you@gmail.com, Enter to skip): ')).trim();
996
- }
998
+ const reply = replyTo;
997
999
  const body = { root };
1000
+ if (typeof from === 'string' && from) body.from = from;
998
1001
  if (typeof reply === 'string') body.replyTo = reply;
999
1002
  rec = (await projectPost('/email-domain', body, { label: 'email' })).emailDomain;
1000
1003
  }
@@ -1010,8 +1013,9 @@ async function emailDomainCommand(action) {
1010
1013
 
1011
1014
  function printEmailRecords(rec) {
1012
1015
  const ok = rec.status === 'verified';
1013
- console.log(`\n Sending domain: ${rec.name} ${ok ? `✓ verified — emails go out as login@${rec.fromDomain || rec.name}` : '… waiting for DNS'}`);
1014
- if (rec.replyTo) console.log(` Replies go to: ${rec.replyTo}`);
1016
+ const fromDomain = rec.fromDomain || (rec.root ? rec.name : rec.name.replace(/^mail\./, ''));
1017
+ console.log(`\n Sending domain: ${rec.name} ${ok ? '✓ verified' : '… waiting for DNS'}`);
1018
+ console.log(` Sends from: any address @${fromDomain} — your code picks it: sendEmail({ from: "Your Name <support>" })`);
1015
1019
  if (ok) { console.log(''); return; }
1016
1020
  console.log('\n At your registrar, add these DNS records:\n');
1017
1021
  for (const r of rec.records || []) {
@@ -1035,7 +1039,7 @@ async function waitForEmailDomain() {
1035
1039
  const marks = (rec.records || []).map(x => `${x.valid ? '✓' : '…'} ${x.kind.toUpperCase()}`).join(' ');
1036
1040
  process.stdout.write(`\r ${marks} `);
1037
1041
  if (rec.status === 'verified') {
1038
- console.log(`\n\n✓ Verified. ${project}'s emails now go out as login@${rec.fromDomain || rec.name}.`);
1042
+ console.log(`\n\n✓ Verified. ${project}'s emails now go out from @${rec.fromDomain || rec.name}.`);
1039
1043
  return;
1040
1044
  }
1041
1045
  await wait(20000);
@@ -1409,6 +1413,7 @@ async function regionTransfer() {
1409
1413
  const em = node.emailDomain;
1410
1414
  const emArgs = ['domain', 'email', em.root ? '--root' : '--sub', '--region', target, '--yes', ...asArgs];
1411
1415
  if (em.replyTo) emArgs.push('--reply-to', em.replyTo);
1416
+ if (em.fromLocal) emArgs.push('--from', em.fromLocal);
1412
1417
  const e = spawnSync(process.execPath, [BIN, ...emArgs], { stdio: 'inherit', cwd: root });
1413
1418
  if (e.status !== 0) console.error(` Email sending: finish later with: nukebase domain email --region ${target}${asArgs.length ? ' ' + asArgs.join(' ') : ''}`);
1414
1419
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nukebase",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "NukeBase CLI: create, deploy and manage NukeBase projects",
5
5
  "bin": {
6
6
  "nukebase": "bin/nukebase.js"