mstro-app 0.1.58 → 0.3.0

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 (161) hide show
  1. package/PRIVACY.md +126 -0
  2. package/README.md +24 -23
  3. package/bin/commands/login.js +85 -42
  4. package/bin/commands/logout.js +35 -1
  5. package/bin/commands/status.js +1 -1
  6. package/bin/mstro.js +231 -131
  7. package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
  8. package/dist/server/cli/headless/claude-invoker.js +550 -115
  9. package/dist/server/cli/headless/claude-invoker.js.map +1 -1
  10. package/dist/server/cli/headless/index.d.ts +2 -1
  11. package/dist/server/cli/headless/index.d.ts.map +1 -1
  12. package/dist/server/cli/headless/index.js +2 -0
  13. package/dist/server/cli/headless/index.js.map +1 -1
  14. package/dist/server/cli/headless/prompt-utils.d.ts +5 -8
  15. package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -1
  16. package/dist/server/cli/headless/prompt-utils.js +40 -5
  17. package/dist/server/cli/headless/prompt-utils.js.map +1 -1
  18. package/dist/server/cli/headless/runner.d.ts +1 -1
  19. package/dist/server/cli/headless/runner.d.ts.map +1 -1
  20. package/dist/server/cli/headless/runner.js +52 -7
  21. package/dist/server/cli/headless/runner.js.map +1 -1
  22. package/dist/server/cli/headless/stall-assessor.d.ts +79 -1
  23. package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
  24. package/dist/server/cli/headless/stall-assessor.js +355 -20
  25. package/dist/server/cli/headless/stall-assessor.js.map +1 -1
  26. package/dist/server/cli/headless/tool-watchdog.d.ts +70 -0
  27. package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -0
  28. package/dist/server/cli/headless/tool-watchdog.js +302 -0
  29. package/dist/server/cli/headless/tool-watchdog.js.map +1 -0
  30. package/dist/server/cli/headless/types.d.ts +98 -1
  31. package/dist/server/cli/headless/types.d.ts.map +1 -1
  32. package/dist/server/cli/improvisation-session-manager.d.ts +136 -2
  33. package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
  34. package/dist/server/cli/improvisation-session-manager.js +929 -132
  35. package/dist/server/cli/improvisation-session-manager.js.map +1 -1
  36. package/dist/server/index.js +5 -13
  37. package/dist/server/index.js.map +1 -1
  38. package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
  39. package/dist/server/mcp/bouncer-integration.js +18 -0
  40. package/dist/server/mcp/bouncer-integration.js.map +1 -1
  41. package/dist/server/mcp/security-audit.d.ts +2 -2
  42. package/dist/server/mcp/security-audit.d.ts.map +1 -1
  43. package/dist/server/mcp/security-audit.js +12 -8
  44. package/dist/server/mcp/security-audit.js.map +1 -1
  45. package/dist/server/mcp/security-patterns.d.ts.map +1 -1
  46. package/dist/server/mcp/security-patterns.js +9 -4
  47. package/dist/server/mcp/security-patterns.js.map +1 -1
  48. package/dist/server/routes/improvise.js +6 -6
  49. package/dist/server/routes/improvise.js.map +1 -1
  50. package/dist/server/services/analytics.d.ts +2 -0
  51. package/dist/server/services/analytics.d.ts.map +1 -1
  52. package/dist/server/services/analytics.js +26 -4
  53. package/dist/server/services/analytics.js.map +1 -1
  54. package/dist/server/services/platform.d.ts.map +1 -1
  55. package/dist/server/services/platform.js +17 -10
  56. package/dist/server/services/platform.js.map +1 -1
  57. package/dist/server/services/sandbox-utils.d.ts +6 -0
  58. package/dist/server/services/sandbox-utils.d.ts.map +1 -0
  59. package/dist/server/services/sandbox-utils.js +72 -0
  60. package/dist/server/services/sandbox-utils.js.map +1 -0
  61. package/dist/server/services/settings.d.ts +6 -0
  62. package/dist/server/services/settings.d.ts.map +1 -1
  63. package/dist/server/services/settings.js +21 -0
  64. package/dist/server/services/settings.js.map +1 -1
  65. package/dist/server/services/terminal/pty-manager.d.ts +5 -51
  66. package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
  67. package/dist/server/services/terminal/pty-manager.js +63 -102
  68. package/dist/server/services/terminal/pty-manager.js.map +1 -1
  69. package/dist/server/services/websocket/file-explorer-handlers.d.ts +5 -0
  70. package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -0
  71. package/dist/server/services/websocket/file-explorer-handlers.js +518 -0
  72. package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -0
  73. package/dist/server/services/websocket/git-handlers.d.ts +36 -0
  74. package/dist/server/services/websocket/git-handlers.d.ts.map +1 -0
  75. package/dist/server/services/websocket/git-handlers.js +797 -0
  76. package/dist/server/services/websocket/git-handlers.js.map +1 -0
  77. package/dist/server/services/websocket/git-pr-handlers.d.ts +4 -0
  78. package/dist/server/services/websocket/git-pr-handlers.d.ts.map +1 -0
  79. package/dist/server/services/websocket/git-pr-handlers.js +299 -0
  80. package/dist/server/services/websocket/git-pr-handlers.js.map +1 -0
  81. package/dist/server/services/websocket/git-worktree-handlers.d.ts +4 -0
  82. package/dist/server/services/websocket/git-worktree-handlers.d.ts.map +1 -0
  83. package/dist/server/services/websocket/git-worktree-handlers.js +353 -0
  84. package/dist/server/services/websocket/git-worktree-handlers.js.map +1 -0
  85. package/dist/server/services/websocket/handler-context.d.ts +32 -0
  86. package/dist/server/services/websocket/handler-context.d.ts.map +1 -0
  87. package/dist/server/services/websocket/handler-context.js +4 -0
  88. package/dist/server/services/websocket/handler-context.js.map +1 -0
  89. package/dist/server/services/websocket/handler.d.ts +27 -338
  90. package/dist/server/services/websocket/handler.d.ts.map +1 -1
  91. package/dist/server/services/websocket/handler.js +74 -2106
  92. package/dist/server/services/websocket/handler.js.map +1 -1
  93. package/dist/server/services/websocket/index.d.ts +1 -1
  94. package/dist/server/services/websocket/index.d.ts.map +1 -1
  95. package/dist/server/services/websocket/index.js.map +1 -1
  96. package/dist/server/services/websocket/session-handlers.d.ts +10 -0
  97. package/dist/server/services/websocket/session-handlers.d.ts.map +1 -0
  98. package/dist/server/services/websocket/session-handlers.js +507 -0
  99. package/dist/server/services/websocket/session-handlers.js.map +1 -0
  100. package/dist/server/services/websocket/settings-handlers.d.ts +6 -0
  101. package/dist/server/services/websocket/settings-handlers.d.ts.map +1 -0
  102. package/dist/server/services/websocket/settings-handlers.js +125 -0
  103. package/dist/server/services/websocket/settings-handlers.js.map +1 -0
  104. package/dist/server/services/websocket/tab-handlers.d.ts +10 -0
  105. package/dist/server/services/websocket/tab-handlers.d.ts.map +1 -0
  106. package/dist/server/services/websocket/tab-handlers.js +131 -0
  107. package/dist/server/services/websocket/tab-handlers.js.map +1 -0
  108. package/dist/server/services/websocket/terminal-handlers.d.ts +9 -0
  109. package/dist/server/services/websocket/terminal-handlers.d.ts.map +1 -0
  110. package/dist/server/services/websocket/terminal-handlers.js +220 -0
  111. package/dist/server/services/websocket/terminal-handlers.js.map +1 -0
  112. package/dist/server/services/websocket/types.d.ts +67 -2
  113. package/dist/server/services/websocket/types.d.ts.map +1 -1
  114. package/hooks/bouncer.sh +11 -4
  115. package/package.json +7 -2
  116. package/server/README.md +176 -159
  117. package/server/cli/headless/claude-invoker.ts +740 -133
  118. package/server/cli/headless/index.ts +7 -1
  119. package/server/cli/headless/output-utils.test.ts +225 -0
  120. package/server/cli/headless/prompt-utils.ts +37 -5
  121. package/server/cli/headless/runner.ts +55 -8
  122. package/server/cli/headless/stall-assessor.test.ts +165 -0
  123. package/server/cli/headless/stall-assessor.ts +478 -22
  124. package/server/cli/headless/tool-watchdog.test.ts +429 -0
  125. package/server/cli/headless/tool-watchdog.ts +398 -0
  126. package/server/cli/headless/types.ts +93 -1
  127. package/server/cli/improvisation-session-manager.ts +1133 -145
  128. package/server/index.ts +5 -14
  129. package/server/mcp/README.md +59 -67
  130. package/server/mcp/bouncer-integration.test.ts +161 -0
  131. package/server/mcp/bouncer-integration.ts +28 -0
  132. package/server/mcp/security-audit.ts +12 -8
  133. package/server/mcp/security-patterns.test.ts +258 -0
  134. package/server/mcp/security-patterns.ts +8 -2
  135. package/server/routes/improvise.ts +6 -6
  136. package/server/services/analytics.ts +26 -4
  137. package/server/services/platform.test.ts +0 -10
  138. package/server/services/platform.ts +16 -11
  139. package/server/services/sandbox-utils.ts +78 -0
  140. package/server/services/settings.ts +25 -0
  141. package/server/services/terminal/pty-manager.ts +68 -129
  142. package/server/services/websocket/autocomplete.test.ts +194 -0
  143. package/server/services/websocket/file-explorer-handlers.ts +587 -0
  144. package/server/services/websocket/git-handlers.ts +924 -0
  145. package/server/services/websocket/git-pr-handlers.ts +363 -0
  146. package/server/services/websocket/git-worktree-handlers.ts +403 -0
  147. package/server/services/websocket/handler-context.ts +44 -0
  148. package/server/services/websocket/handler.test.ts +1 -1
  149. package/server/services/websocket/handler.ts +90 -2421
  150. package/server/services/websocket/index.ts +1 -1
  151. package/server/services/websocket/session-handlers.ts +574 -0
  152. package/server/services/websocket/settings-handlers.ts +150 -0
  153. package/server/services/websocket/tab-handlers.ts +150 -0
  154. package/server/services/websocket/terminal-handlers.ts +277 -0
  155. package/server/services/websocket/types.ts +145 -4
  156. package/bin/release.sh +0 -110
  157. package/dist/server/services/terminal/tmux-manager.d.ts +0 -82
  158. package/dist/server/services/terminal/tmux-manager.d.ts.map +0 -1
  159. package/dist/server/services/terminal/tmux-manager.js +0 -352
  160. package/dist/server/services/terminal/tmux-manager.js.map +0 -1
  161. package/server/services/terminal/tmux-manager.ts +0 -426
package/bin/mstro.js CHANGED
@@ -8,20 +8,19 @@
8
8
  * Main entry point for the Mstro AI assistant.
9
9
  *
10
10
  * Usage:
11
- * mstro # Start Mstro (auto-finds available port)
12
- * mstro login # Authenticate this device
11
+ * mstro # Start Mstro (logs in automatically if needed)
12
+ * mstro login # Re-authenticate this device
13
13
  * mstro logout # Sign out
14
14
  * mstro whoami # Show current user
15
15
  * mstro status # Show connection status
16
- * mstro setup-terminal # Enable web terminal
17
16
  * mstro -p 4105 # Start on specific port (overrides auto port)
18
17
  * mstro configure-hooks # Configure Claude Code security hooks
19
18
  * mstro --help # Show help
20
19
  */
21
20
 
22
- import { execSync, spawn } from 'node:child_process';
21
+ import { spawn } from 'node:child_process';
23
22
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
24
- import { homedir, platform as osPlatform } from 'node:os';
23
+ import { homedir } from 'node:os';
25
24
  import { dirname, join, resolve } from 'node:path';
26
25
  import { createInterface } from 'node:readline';
27
26
  import { fileURLToPath } from 'node:url';
@@ -36,10 +35,27 @@ const CLIENT_ROOT = resolve(__dirname, '..');
36
35
  const pkg = JSON.parse(readFileSync(join(CLIENT_ROOT, 'package.json'), 'utf-8'));
37
36
 
38
37
  // Check for updates (runs async in background, notifies on next run)
38
+ // update-notifier initializes lastUpdateCheck to Date.now(), which means the
39
+ // first check won't happen until 24h after install. We detect first-run by
40
+ // checking if the configstore has never stored an update result, and if so
41
+ // reset the timestamp to force an immediate background check.
39
42
  const notifier = updateNotifier({
40
43
  pkg,
41
44
  updateCheckInterval: 1000 * 60 * 60 * 24 // Check daily
42
45
  });
46
+ try {
47
+ if (notifier.config && !notifier.config.has('update') && !notifier.update) {
48
+ const lastCheck = notifier.config.get('lastUpdateCheck');
49
+ // If lastUpdateCheck was just set (within the last 30s), this is a fresh
50
+ // configstore — reset it to 0 so the library spawns a check immediately.
51
+ if (lastCheck && (Date.now() - lastCheck) < 30_000) {
52
+ notifier.config.set('lastUpdateCheck', 0);
53
+ notifier.check();
54
+ }
55
+ }
56
+ } catch {
57
+ // Non-critical — don't let update check logic crash the CLI
58
+ }
43
59
 
44
60
  // Capture the user's original working directory before any cwd changes
45
61
  const USER_CWD = process.cwd();
@@ -47,10 +63,10 @@ const USER_CWD = process.cwd();
47
63
  // First-run detection paths
48
64
  const MSTRO_CONFIG_DIR = join(homedir(), '.mstro');
49
65
  const MSTRO_FIRST_RUN_FLAG = join(MSTRO_CONFIG_DIR, '.configured');
50
- const MSTRO_TERMINAL_CHECKED_FLAG = join(MSTRO_CONFIG_DIR, '.terminal-checked');
51
66
  const CLAUDE_SETTINGS_FILE = join(homedir(), '.claude', 'settings.json');
52
67
  const CLAUDE_HOOKS_DIR = join(homedir(), '.claude', 'hooks');
53
68
  const BOUNCER_HOOK_FILE = join(CLAUDE_HOOKS_DIR, 'bouncer.sh');
69
+ const PTY_SETUP_DISMISSED_FLAG = join(MSTRO_CONFIG_DIR, '.pty-setup-dismissed');
54
70
 
55
71
  /**
56
72
  * Mark Mstro as configured by writing the first-run flag file
@@ -195,19 +211,163 @@ async function promptBouncerSetup() {
195
211
  }
196
212
  }
197
213
 
214
+ /**
215
+ * Check if node-pty native module is loadable
216
+ */
217
+ async function isNodePtyAvailable() {
218
+ try {
219
+ await import('node-pty');
220
+ return true;
221
+ } catch {
222
+ return false;
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Check if user has dismissed the pty setup prompt
228
+ */
229
+ function hasUserDismissedPtySetup() {
230
+ return existsSync(PTY_SETUP_DISMISSED_FLAG);
231
+ }
232
+
233
+ /**
234
+ * Mark pty setup prompt as dismissed
235
+ */
236
+ function markPtySetupDismissed() {
237
+ if (!existsSync(MSTRO_CONFIG_DIR)) {
238
+ mkdirSync(MSTRO_CONFIG_DIR, { recursive: true, mode: 0o700 });
239
+ }
240
+ writeFileSync(PTY_SETUP_DISMISSED_FLAG, new Date().toISOString());
241
+ }
242
+
243
+ /**
244
+ * Show a one-line warning that node-pty is not available
245
+ */
246
+ function showPtyWarning() {
247
+ log(' Terminal support not available. Run: mstro setup-terminal', colors.dim);
248
+ }
249
+
250
+ /**
251
+ * Get platform-specific build tool instructions
252
+ */
253
+ function getPtyBuildInstructions() {
254
+ const os = process.platform;
255
+ if (os === 'darwin') {
256
+ return ' Install Xcode Command Line Tools: xcode-select --install';
257
+ }
258
+ if (os === 'win32') {
259
+ return ' Install Windows Build Tools: npm install -g windows-build-tools';
260
+ }
261
+ return ' Debian/Ubuntu: sudo apt install build-essential python3\n' +
262
+ ' Fedora/RHEL: sudo dnf install gcc-c++ make python3\n' +
263
+ ' Arch: sudo pacman -S base-devel python';
264
+ }
265
+
266
+ /**
267
+ * Attempt to rebuild/install node-pty from CLIENT_ROOT
268
+ * Returns true if npm command succeeded, false otherwise
269
+ */
270
+ function attemptPtyRebuild() {
271
+ return new Promise((resolve) => {
272
+ const nodePtyDir = join(CLIENT_ROOT, 'node_modules', 'node-pty');
273
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
274
+ const command = existsSync(nodePtyDir) ? 'rebuild' : 'install';
275
+ const args = command === 'rebuild'
276
+ ? ['rebuild', 'node-pty']
277
+ : ['install', 'node-pty', '--no-save'];
278
+
279
+ log(`\n ${command === 'rebuild' ? 'Rebuilding' : 'Installing'} node-pty...`, colors.dim);
280
+
281
+ const child = spawn(npmCmd, args, {
282
+ cwd: CLIENT_ROOT,
283
+ stdio: 'inherit',
284
+ });
285
+
286
+ child.on('error', (err) => {
287
+ log(` Error: ${err.message}`, colors.red);
288
+ resolve(false);
289
+ });
290
+
291
+ child.on('exit', (code) => {
292
+ resolve(code === 0);
293
+ });
294
+ });
295
+ }
296
+
297
+ /**
298
+ * Prompt user to set up node-pty for terminal support
299
+ * Returns: 'configure' | 'skip' | 'never'
300
+ */
301
+ async function promptPtySetup() {
302
+ log('\n Terminal Support\n', colors.bold + colors.cyan);
303
+ log(' Mstro includes a web terminal that lets you open a shell', colors.dim);
304
+ log(' directly in your browser. This requires compiling a native module (node-pty).\n', colors.dim);
305
+
306
+ const isInteractive = process.stdin.isTTY;
307
+
308
+ if (!isInteractive) {
309
+ log(' Non-interactive mode: skipping terminal setup.', colors.yellow);
310
+ log(' Run "mstro setup-terminal" to enable terminal support.\n', colors.dim);
311
+ return 'skip';
312
+ }
313
+
314
+ log(' Set up terminal support now?', colors.bold);
315
+ log(' [Y] Yes, compile now (requires build tools)', colors.dim);
316
+ log(' [n] Not now (ask again next time)', colors.dim);
317
+ log(' [d] Don\'t show this again\n', colors.dim);
318
+
319
+ const answer = await prompt(' Your choice [Y/n/d]: ');
320
+ const choice = answer.toLowerCase();
321
+
322
+ if (choice === '' || choice === 'y' || choice === 'yes') {
323
+ return 'configure';
324
+ }
325
+ if (choice === 'd' || choice === 'dont' || choice === "don't") {
326
+ log('\n Got it! You can set up later with: mstro setup-terminal\n', colors.dim);
327
+ markPtySetupDismissed();
328
+ return 'never';
329
+ }
330
+ log('\n Skipping for now. Will ask again next time.', colors.yellow);
331
+ log(' You can also set up with: mstro setup-terminal\n', colors.dim);
332
+ return 'skip';
333
+ }
334
+
335
+ /**
336
+ * Run the pty rebuild and show results
337
+ */
338
+ async function runPtySetup() {
339
+ const success = await attemptPtyRebuild();
340
+
341
+ if (success) {
342
+ const available = await isNodePtyAvailable();
343
+ if (available) {
344
+ log('\n Terminal support enabled successfully!\n', colors.bold + colors.green);
345
+ return true;
346
+ }
347
+ log('\n node-pty installed but failed to load.', colors.red);
348
+ }
349
+
350
+ log('\n Could not compile node-pty automatically.\n', colors.yellow);
351
+ log(' You may need to install build tools first:\n', colors.bold);
352
+ log(getPtyBuildInstructions(), colors.dim);
353
+ log('');
354
+ log(' After installing build tools, run: mstro setup-terminal\n', colors.dim);
355
+ return false;
356
+ }
357
+
198
358
  function showHelp() {
199
- log('\n Mstro - No-code AI Workspace\n', colors.bold + colors.cyan);
200
- log(' Run Claude Code workflows from your laptop, cloud VM, or any machine.\n', colors.dim);
359
+ log('\n Mstro - Run Claude Code from any browser\n', colors.bold + colors.cyan);
360
+ log(' Streams live Claude Code sessions from your machine to mstro.app.\n', colors.dim);
201
361
  log(' Usage:', colors.bold);
202
- log(' mstro Start Mstro (auto-finds available port)', colors.dim);
203
- log(' mstro login Authenticate this device with mstro.app', colors.dim);
362
+ log(' mstro Start Mstro (logs in automatically if needed)', colors.dim);
363
+ log(' mstro login Re-authenticate this device with mstro.app', colors.dim);
204
364
  log(' mstro logout Sign out of mstro.app', colors.dim);
205
365
  log(' mstro whoami Show current user and device info', colors.dim);
206
366
  log(' mstro status Show connection and auth status', colors.dim);
207
367
  log(' mstro telemetry [on|off] Enable/disable anonymous telemetry', colors.dim);
208
- log(' mstro setup-terminal Enable web terminal (compiles native module)', colors.dim);
209
368
  log(' mstro -p 4105 Start on specific port (overrides auto port)', colors.dim);
210
369
  log(' mstro configure-hooks Configure Claude Code security hooks', colors.dim);
370
+ log(' mstro setup-terminal Enable web terminal (compiles native module)', colors.dim);
211
371
  log(' mstro --version Show version number', colors.dim);
212
372
  log(' mstro --help Show this help message', colors.dim);
213
373
  log('');
@@ -217,8 +377,8 @@ function showHelp() {
217
377
  log(' --verbose, -v Enable verbose output', colors.dim);
218
378
  log('');
219
379
  log(' Authentication:', colors.bold);
220
- log(' Run "mstro login" to connect this device to your mstro.app account.', colors.dim);
221
- log(' Once logged in, orchestras sync automatically with your web dashboard.', colors.dim);
380
+ log(' Running "mstro" will prompt you to log in automatically if needed.', colors.dim);
381
+ log(' Once logged in, machines sync automatically with your web dashboard.', colors.dim);
222
382
  log('');
223
383
  log(' Security:', colors.bold);
224
384
  log(' Mstro includes a Security Bouncer that automatically manages', colors.dim);
@@ -229,7 +389,7 @@ function showHelp() {
229
389
 
230
390
  function runNpmScript(script, args = [], envOverrides = {}) {
231
391
  const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
232
- const child = spawn(npmCmd, ['run', script, ...args], {
392
+ const child = spawn(npmCmd, ['run', '--silent', script, ...args], {
233
393
  cwd: CLIENT_ROOT,
234
394
  stdio: 'inherit',
235
395
  env: { ...process.env, MSTRO_WORKING_DIR: USER_CWD, ...envOverrides },
@@ -291,7 +451,6 @@ function runConfigureHooks(andThenStart = false) {
291
451
  markConfigured();
292
452
  if (andThenStart) {
293
453
  // After configuring, start the server
294
- log('\nStarting Mstro client...', colors.bold + colors.cyan);
295
454
  const requestedPort = parsePort(process.argv.slice(2));
296
455
  const envOverrides = requestedPort ? { PORT: String(requestedPort) } : {};
297
456
  runNpmScript('start', [], envOverrides);
@@ -325,14 +484,18 @@ function parsePort(args) {
325
484
  * Show update notification if available
326
485
  */
327
486
  function showUpdateNotification() {
328
- if (notifier.update && semverGt(notifier.update.latest, notifier.update.current)) {
329
- const { current, latest, type } = notifier.update;
330
- const updateCmd = 'npm i -g mstro@latest';
331
-
332
- log('');
333
- log(` ${colors.yellow}Update available:${colors.reset} ${colors.dim}${current}${colors.reset} → ${colors.green}${latest}${colors.reset} ${colors.dim}(${type})${colors.reset}`);
334
- log(` Run: ${colors.cyan}${updateCmd}${colors.reset}`);
335
- log('');
487
+ try {
488
+ if (notifier.update && semverGt(notifier.update.latest, notifier.update.current)) {
489
+ const { current, latest, type } = notifier.update;
490
+ const updateCmd = `npm i -g ${pkg.name}`;
491
+
492
+ log('');
493
+ log(` ${colors.yellow}Update available:${colors.reset} ${colors.dim}${current}${colors.reset} → ${colors.green}${latest}${colors.reset} ${colors.dim}(${type})${colors.reset}`);
494
+ log(` Run: ${colors.cyan}${updateCmd}${colors.reset}`);
495
+ log('');
496
+ }
497
+ } catch {
498
+ // Don't let a corrupted cache or invalid semver crash the CLI
336
499
  }
337
500
  }
338
501
 
@@ -353,136 +516,63 @@ function isLoggedIn() {
353
516
  }
354
517
 
355
518
  /**
356
- * Show login required message
519
+ * Auto-login if not authenticated. Exits on failure.
357
520
  */
358
- function showLoginRequired() {
359
- log('\n Authentication required', colors.bold + colors.yellow);
360
- log('');
361
- log(' You must be logged in to use mstro.', colors.dim);
362
- log(' Run "mstro login" to authenticate this device.', colors.dim);
363
- log('');
364
- }
365
-
366
- /**
367
- * Check if node-pty is loadable (native module compiled correctly)
368
- */
369
- async function isNodePtyAvailable() {
521
+ async function ensureLoggedIn() {
522
+ if (isLoggedIn()) return;
523
+ log('\n Not logged in — starting authentication...\n', colors.bold + colors.cyan);
370
524
  try {
371
- const pty = await import('node-pty');
372
- // Verify the native module actually works, not just that it imports
373
- const test = pty.spawn('/bin/echo', ['test'], { name: 'xterm', cols: 80, rows: 24 });
374
- test.kill();
375
- return true;
376
- } catch {
377
- return false;
525
+ const { login } = await import('./commands/login.js');
526
+ await login(args, { inline: true });
527
+ } catch (err) {
528
+ log(`\n Login failed: ${err.message}`, colors.red);
529
+ log(' Run "mstro login" to try again.\n', colors.dim);
530
+ process.exit(1);
378
531
  }
379
532
  }
380
533
 
381
534
  /**
382
- * Try to rebuild node-pty silently. Returns true on success.
535
+ * Prompt for bouncer setup if not configured
536
+ * Returns true if runConfigureHooks was called (async exit path)
383
537
  */
384
- function tryRebuildNodePty() {
385
- try {
386
- execSync('npm rebuild node-pty', { cwd: CLIENT_ROOT, stdio: 'pipe' });
387
- return true;
388
- } catch {
538
+ async function ensureBouncerSetup() {
539
+ if (isBouncerConfigured()) return false;
540
+ if (hasUserDismissedSetup()) {
541
+ showBouncerWarning();
389
542
  return false;
390
543
  }
391
- }
392
-
393
- /**
394
- * Get platform-specific build tool install instructions
395
- */
396
- function getBuildToolInstructions() {
397
- const os = osPlatform();
398
- if (os === 'darwin') {
399
- return ' xcode-select --install';
400
- } else if (os === 'win32') {
401
- return ' npm install -g windows-build-tools';
402
- } else {
403
- return ' sudo apt install build-essential python3 # Debian/Ubuntu\n sudo dnf install gcc-c++ make python3 # Fedora/RHEL';
544
+ const choice = await promptBouncerSetup();
545
+ if (choice === 'configure') {
546
+ runConfigureHooks(true);
547
+ return true;
404
548
  }
549
+ return false;
405
550
  }
406
551
 
407
552
  /**
408
- * First-run terminal setup check (runs after bouncer setup).
409
- * Tries to rebuild node-pty automatically. If that fails, shows instructions.
553
+ * Prompt for node-pty setup if not available
410
554
  */
411
- async function checkTerminalSetup() {
412
- if (await isNodePtyAvailable()) {
413
- return; // Already working
414
- }
415
-
416
- log('\n Web Terminal', colors.bold + colors.cyan);
417
- log(' mstro includes a browser-based terminal (optional).\n', colors.dim);
418
- log(' Attempting to compile native module...', colors.dim);
419
-
420
- if (tryRebuildNodePty()) {
421
- log(' Terminal support enabled!\n', colors.green);
555
+ async function ensurePtySetup() {
556
+ const ptyAvailable = await isNodePtyAvailable();
557
+ if (ptyAvailable) return;
558
+ if (hasUserDismissedPtySetup()) {
559
+ showPtyWarning();
422
560
  return;
423
561
  }
424
-
425
- log(' Could not compile terminal module.\n', colors.yellow);
426
- log(' To enable the web terminal later:', colors.dim);
427
- log(' 1. Install build tools:', colors.dim);
428
- log(getBuildToolInstructions(), colors.dim);
429
- log(' 2. Run:', colors.dim);
430
- log(' mstro setup-terminal\n', colors.dim);
431
- }
432
-
433
- /**
434
- * Explicit setup-terminal command
435
- */
436
- async function setupTerminal() {
437
- log('\n Setting up terminal support...\n', colors.bold + colors.cyan);
438
-
439
- if (await isNodePtyAvailable()) {
440
- log(' Terminal support is already enabled.\n', colors.green);
441
- return;
562
+ const choice = await promptPtySetup();
563
+ if (choice === 'configure') {
564
+ await runPtySetup();
442
565
  }
443
-
444
- log(' Rebuilding node-pty native module...', colors.dim);
445
-
446
- if (tryRebuildNodePty()) {
447
- log('\n Terminal support enabled! Restart mstro to use it.\n', colors.green + colors.bold);
448
- return;
449
- }
450
-
451
- log('\n Failed to build node-pty.\n', colors.red);
452
- log(' Install build tools first:', colors.dim);
453
- log(getBuildToolInstructions(), colors.dim);
454
- log('\n Then re-run: mstro setup-terminal\n', colors.dim);
455
- process.exit(1);
456
566
  }
457
567
 
458
568
  async function startServer(envOverrides) {
459
- if (!isLoggedIn()) {
460
- showLoginRequired();
461
- process.exit(1);
462
- }
569
+ await ensureLoggedIn();
463
570
 
464
- if (!isBouncerConfigured()) {
465
- if (hasUserDismissedSetup()) {
466
- showBouncerWarning();
467
- } else {
468
- const choice = await promptBouncerSetup();
469
- if (choice === 'configure') {
470
- runConfigureHooks(true);
471
- return;
472
- }
473
- }
474
- }
571
+ if (await ensureBouncerSetup()) return;
475
572
 
476
- if (!existsSync(MSTRO_TERMINAL_CHECKED_FLAG)) {
477
- await checkTerminalSetup();
478
- if (!existsSync(MSTRO_CONFIG_DIR)) {
479
- mkdirSync(MSTRO_CONFIG_DIR, { recursive: true, mode: 0o700 });
480
- }
481
- writeFileSync(MSTRO_TERMINAL_CHECKED_FLAG, new Date().toISOString());
482
- }
573
+ await ensurePtySetup();
483
574
 
484
575
  showUpdateNotification();
485
- log('\nStarting Mstro client...', colors.bold + colors.cyan);
486
576
  runNpmScript('start', [], envOverrides);
487
577
  }
488
578
 
@@ -504,7 +594,7 @@ async function main() {
504
594
  }],
505
595
  ['logout', async () => {
506
596
  const { logout } = await import('./commands/logout.js');
507
- await logout();
597
+ await logout(args.slice(args.indexOf('logout') + 1));
508
598
  }],
509
599
  ['whoami', async () => {
510
600
  const { whoami } = await import('./commands/whoami.js');
@@ -518,8 +608,17 @@ async function main() {
518
608
  const { telemetry } = await import('./commands/config.js');
519
609
  await telemetry(args.slice(args.indexOf('telemetry') + 1));
520
610
  }],
521
- ['setup-terminal', () => setupTerminal()],
522
611
  ['configure-hooks', () => runConfigureHooks(false)],
612
+ ['setup-terminal', async () => {
613
+ log('\n Mstro Terminal Setup\n', colors.bold + colors.cyan);
614
+ const alreadyAvailable = await isNodePtyAvailable();
615
+ if (alreadyAvailable) {
616
+ log(' node-pty is already available. Terminal support is enabled!\n', colors.green);
617
+ return;
618
+ }
619
+ const success = await runPtySetup();
620
+ process.exit(success ? 0 : 1);
621
+ }],
523
622
  ]);
524
623
 
525
624
  // Flag-based commands
@@ -544,6 +643,7 @@ async function main() {
544
643
  const handler = subcommand ? commands.get(subcommand) : undefined;
545
644
  if (handler) {
546
645
  await handler();
646
+ showUpdateNotification();
547
647
  return;
548
648
  }
549
649
 
@@ -1 +1 @@
1
- {"version":3,"file":"claude-invoker.d.ts","sourceRoot":"","sources":["../../../../server/cli/headless/claude-invoker.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAK9D,OAAO,KAAK,EACV,eAAe,EACf,sBAAsB,EAEvB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,sBAAsB,CAAC;IAC/B,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC7C;AA6WD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,eAAe,CAAC,CAgN1B"}
1
+ {"version":3,"file":"claude-invoker.d.ts","sourceRoot":"","sources":["../../../../server/cli/headless/claude-invoker.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAO9D,OAAO,KAAK,EACV,eAAe,EACf,sBAAsB,EAGvB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,sBAAsB,CAAC;IAC/B,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC7C;AAmgCD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,eAAe,CAAC,CA0H1B"}