@nativescript/cloudflare-cli 0.1.0-beta.3 → 0.1.0-beta.5

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 (3) hide show
  1. package/README.md +2 -1
  2. package/dist/bin.js +53 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -3,9 +3,10 @@
3
3
  ```sh
4
4
  npx @nativescript/cloudflare-cli live # a phone app: opens the IDE and prints a QR code for NativeScript Preview
5
5
  npx @nativescript/cloudflare-cli live --mac # a Mac app: opens the IDE and runs the app on this Mac, streamed into the IDE
6
+ npx @nativescript/cloudflare-cli live --electron # an Electron app with native views, run the same way
6
7
  ```
7
8
 
8
- `live` creates a project on preview.nativescript.org (`--server` or `NS_EDGE_SERVER` for another edge), saves it to `.ns-edge.json` and opens its IDE. Run it again in the same folder to reopen that project, or add `--new` for another. Without a create token the project is free: a few hosted agent turns a day, no backend, deleted after 30 days unused. `--no-open` skips the browser; with `--mac`, `--allow-eval` lets the agent run code on this Mac.
9
+ `live` creates a project on preview.nativescript.org (`--server` or `NS_EDGE_SERVER` for another edge), saves it to `.ns-edge.json` and opens its IDE. Run it again in the same folder to reopen that project (no platform flag needed), or add `--new` for another. While `live` runs a Mac app it restarts the app if it crashes, unless it crashes three times in a minute. Without a create token the project is free: a few hosted agent turns a day, no backend, deleted after 30 days unused. `--no-open` skips the browser; with `--mac` or `--electron`, `--allow-eval` lets the agent run code on this Mac.
9
10
 
10
11
  ## Mirror a local project
11
12
 
package/dist/bin.js CHANGED
@@ -9,9 +9,15 @@ import { applyChanges, collect, createProject, projectUrl, report, SYNCED } from
9
9
  const CONFIG = '.ns-edge.json';
10
10
  const DEBOUNCE_MS = 120;
11
11
  const LIVE_SERVER = process.env.NS_EDGE_SERVER ?? 'https://preview.nativescript.org';
12
- /** The desktop host `live --mac` runs; bump it with each host release this CLI is tested against. */
13
- const DESKTOP_HOST = '@nativescript/cloudflare-desktop@0.1.0-alpha.2';
12
+ /** The hosts `live --mac` and `live --electron` run; bump them with each host release this CLI is tested against. */
13
+ const DESKTOP_HOSTS = {
14
+ macos: '@nativescript/cloudflare-desktop@0.1.0-alpha.4',
15
+ electron: '@nativescript/cloudflare-electron@0.1.0-alpha.1',
16
+ };
14
17
  const DESKTOP_TEMPLATES = new Set(['macos', 'electron']);
18
+ const MAX_CRASHES = 3;
19
+ const CRASH_WINDOW_MS = 60_000;
20
+ const RESTART_DELAY_MS = 1000;
15
21
  const [cmd = 'help', ...args] = process.argv.slice(2);
16
22
  /**
17
23
  * Editors and tools write temp, swap and backup files next to the real one
@@ -72,20 +78,46 @@ function openInBrowser(url) {
72
78
  const [command, ...rest] = process.platform === 'darwin' ? ['open', url] : process.platform === 'win32' ? ['cmd', '/c', 'start', '""', url.replace(/&/g, '^&')] : ['xdg-open', url];
73
79
  spawn(command, rest, { stdio: 'ignore', detached: true }).on('error', () => { }).unref();
74
80
  }
75
- /** Runs the Mac host for a desktop project until it exits; the host prints what joining allows. */
76
- function runDesktopHost(ref) {
81
+ /**
82
+ * Runs the Mac host for a desktop project until it quits or Ctrl+C. A host that
83
+ * crashes is started again, since nothing in the browser can reach this Mac to
84
+ * do it, unless it keeps crashing. The host says what joining allows.
85
+ */
86
+ async function runDesktopHost(ref) {
77
87
  if (process.platform !== 'darwin' || process.arch !== 'arm64') {
78
88
  throw new Error('Mac projects run on an Apple Silicon Mac (macOS 14.4 or later). Run the same command there.');
79
89
  }
80
90
  const device = `${projectUrl(ref)}#${ref.deviceKey}`;
81
91
  const extra = has('allow-eval') ? ['--allow-eval'] : [];
82
- const child = spawn('npx', ['-y', DESKTOP_HOST, device, '--yes', ...extra], { stdio: 'inherit' });
83
- for (const signal of ['SIGINT', 'SIGTERM'])
84
- process.on(signal, () => child.kill(signal));
85
- return new Promise((resolve, reject) => {
86
- child.on('error', reject);
87
- child.on('exit', (code) => (code ? reject(new Error(`The Mac host exited with code ${code}.`)) : resolve()));
88
- });
92
+ const host = DESKTOP_HOSTS[ref.template ?? 'macos'] ?? DESKTOP_HOSTS.macos;
93
+ let child;
94
+ let stopping = false;
95
+ for (const signal of ['SIGINT', 'SIGTERM']) {
96
+ process.on(signal, () => {
97
+ stopping = true;
98
+ child?.kill(signal);
99
+ });
100
+ }
101
+ const crashes = [];
102
+ for (;;) {
103
+ child = spawn('npx', ['-y', host, device, '--yes', ...extra], { stdio: 'inherit' });
104
+ const exit = await new Promise((resolve, reject) => {
105
+ child.on('error', reject);
106
+ child.on('exit', (code, signal) => resolve({ code, signal }));
107
+ });
108
+ if (stopping || exit.code === 0)
109
+ return;
110
+ const now = Date.now();
111
+ crashes.push(now);
112
+ while (crashes[0] < now - CRASH_WINDOW_MS)
113
+ crashes.shift();
114
+ const why = exit.signal ?? `code ${exit.code}`;
115
+ if (crashes.length > MAX_CRASHES) {
116
+ throw new Error(`The Mac host crashed ${crashes.length} times in a minute (last: ${why}), so it wasn't restarted. Fix the error above, then run this command again.`);
117
+ }
118
+ console.log(`\nThe Mac host stopped (${why}). Starting it again…\n`);
119
+ await new Promise((r) => setTimeout(r, RESTART_DELAY_MS));
120
+ }
89
121
  }
90
122
  const commands = {
91
123
  async live() {
@@ -93,19 +125,23 @@ const commands = {
93
125
  if (has(later))
94
126
  throw new Error(`--${later} isn't available yet. Run \`live\` for a phone app or \`live --mac\` for a Mac app.`);
95
127
  }
96
- const template = flag('template') ?? (has('mac') ? 'macos' : 'core');
97
- const desktop = DESKTOP_TEMPLATES.has(template);
98
128
  let ref = has('new') ? null : await savedProject();
129
+ // Without a platform flag, this folder's project runs as whatever it was made as.
130
+ const template = flag('template') ?? (has('electron') ? 'electron' : has('mac') ? 'macos' : (ref?.template ?? 'core'));
131
+ const desktop = DESKTOP_TEMPLATES.has(template);
99
132
  if (ref && DESKTOP_TEMPLATES.has(ref.template ?? 'core') !== desktop) {
100
133
  throw new Error(`This folder's project (${CONFIG}) is a ${desktop ? 'phone' : 'Mac'} project. Add --new to replace it, or run this in another folder.`);
101
134
  }
135
+ if (ref && desktop && ref.template !== template) {
136
+ throw new Error(`This folder's project (${CONFIG}) is a ${ref.template} project. Add --new to replace it, or run this in another folder.`);
137
+ }
102
138
  if (ref) {
103
139
  console.log(`Opening this folder's project ${ref.id} (${CONFIG})`);
104
140
  }
105
141
  else {
106
142
  ref = await createProject(flag('server') ?? LIVE_SERVER, flag('token') ?? process.env.NS_EDGE_CREATE_TOKEN, template);
107
143
  await writeFile(join(root, CONFIG), JSON.stringify(ref, null, 2) + '\n');
108
- console.log(`Created a ${desktop ? 'Mac' : 'phone'} project, saved to ${CONFIG} (it holds the project's keys: keep it private and out of git).`);
144
+ console.log(`Created ${template === 'electron' ? 'an Electron' : desktop ? 'a Mac' : 'a phone'} project, saved to ${CONFIG} (it holds the project's keys: keep it private and out of git).`);
109
145
  if (ref.free) {
110
146
  console.log(`It's a free project: ${ref.free.agentTurnsPerDay} hosted agent turn${ref.free.agentTurnsPerDay === 1 ? '' : 's'} a day, deleted after ${ref.free.idleDays} days unused. ` +
111
147
  'For an unlimited agent, add your own API key in the agent panel or connect Claude Code from Connect agent.');
@@ -116,7 +152,7 @@ const commands = {
116
152
  if (!has('no-open'))
117
153
  openInBrowser(ide);
118
154
  if (desktop) {
119
- console.log('Starting the app on this Mac. Its window streams to the IDE\'s Device pane. Press Ctrl+C to stop.\n');
155
+ console.log(`Starting the app on this Mac${template === 'electron' ? ' (the first run downloads Electron)' : ''}. Its window streams to the IDE's Device pane. Press Ctrl+C to stop.\n`);
120
156
  return runDesktopHost(ref);
121
157
  }
122
158
  const device = `${projectUrl(ref)}#${ref.deviceKey}`;
@@ -196,9 +232,9 @@ const commands = {
196
232
  async help() {
197
233
  console.log(`ns-edge <command>
198
234
 
199
- live [--mac] [--new] [--no-open] [--allow-eval]
235
+ live [--mac|--electron] [--new] [--no-open] [--allow-eval]
200
236
  create a project (or reopen this folder's), open its IDE, and print a QR code
201
- for your phone; --mac runs it on this Mac instead
237
+ for your phone; --mac or --electron runs it on this Mac instead
202
238
  create [--server <url>] [--token <t>] [--template core|vue|react|solid|svelte|angular]
203
239
  create an edge project and save .ns-edge.json
204
240
  push upload app/ and server/ in one transaction
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nativescript/cloudflare-cli",
3
- "version": "0.1.0-beta.3",
3
+ "version": "0.1.0-beta.5",
4
4
  "description": "Mirror a NativeScript project into an ns-edge project from a terminal or a Vite dev server",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",