mstro-app 0.1.57 → 0.2.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 (100) hide show
  1. package/bin/commands/login.js +27 -14
  2. package/bin/commands/logout.js +35 -1
  3. package/bin/commands/status.js +1 -1
  4. package/bin/mstro.js +5 -108
  5. package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
  6. package/dist/server/cli/headless/claude-invoker.js +432 -103
  7. package/dist/server/cli/headless/claude-invoker.js.map +1 -1
  8. package/dist/server/cli/headless/index.d.ts +2 -1
  9. package/dist/server/cli/headless/index.d.ts.map +1 -1
  10. package/dist/server/cli/headless/index.js +2 -0
  11. package/dist/server/cli/headless/index.js.map +1 -1
  12. package/dist/server/cli/headless/prompt-utils.d.ts +5 -8
  13. package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -1
  14. package/dist/server/cli/headless/prompt-utils.js +40 -5
  15. package/dist/server/cli/headless/prompt-utils.js.map +1 -1
  16. package/dist/server/cli/headless/runner.d.ts +1 -1
  17. package/dist/server/cli/headless/runner.d.ts.map +1 -1
  18. package/dist/server/cli/headless/runner.js +29 -7
  19. package/dist/server/cli/headless/runner.js.map +1 -1
  20. package/dist/server/cli/headless/stall-assessor.d.ts +77 -1
  21. package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
  22. package/dist/server/cli/headless/stall-assessor.js +336 -20
  23. package/dist/server/cli/headless/stall-assessor.js.map +1 -1
  24. package/dist/server/cli/headless/tool-watchdog.d.ts +67 -0
  25. package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -0
  26. package/dist/server/cli/headless/tool-watchdog.js +296 -0
  27. package/dist/server/cli/headless/tool-watchdog.js.map +1 -0
  28. package/dist/server/cli/headless/types.d.ts +80 -1
  29. package/dist/server/cli/headless/types.d.ts.map +1 -1
  30. package/dist/server/cli/improvisation-session-manager.d.ts +109 -2
  31. package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
  32. package/dist/server/cli/improvisation-session-manager.js +737 -132
  33. package/dist/server/cli/improvisation-session-manager.js.map +1 -1
  34. package/dist/server/index.js +5 -10
  35. package/dist/server/index.js.map +1 -1
  36. package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
  37. package/dist/server/mcp/bouncer-integration.js +18 -0
  38. package/dist/server/mcp/bouncer-integration.js.map +1 -1
  39. package/dist/server/mcp/security-audit.d.ts +2 -2
  40. package/dist/server/mcp/security-audit.d.ts.map +1 -1
  41. package/dist/server/mcp/security-audit.js +12 -8
  42. package/dist/server/mcp/security-audit.js.map +1 -1
  43. package/dist/server/mcp/security-patterns.d.ts.map +1 -1
  44. package/dist/server/mcp/security-patterns.js +9 -4
  45. package/dist/server/mcp/security-patterns.js.map +1 -1
  46. package/dist/server/routes/improvise.js +6 -6
  47. package/dist/server/routes/improvise.js.map +1 -1
  48. package/dist/server/services/analytics.d.ts +2 -0
  49. package/dist/server/services/analytics.d.ts.map +1 -1
  50. package/dist/server/services/analytics.js +13 -3
  51. package/dist/server/services/analytics.js.map +1 -1
  52. package/dist/server/services/platform.d.ts.map +1 -1
  53. package/dist/server/services/platform.js +4 -9
  54. package/dist/server/services/platform.js.map +1 -1
  55. package/dist/server/services/sandbox-utils.d.ts +6 -0
  56. package/dist/server/services/sandbox-utils.d.ts.map +1 -0
  57. package/dist/server/services/sandbox-utils.js +72 -0
  58. package/dist/server/services/sandbox-utils.js.map +1 -0
  59. package/dist/server/services/settings.d.ts +6 -0
  60. package/dist/server/services/settings.d.ts.map +1 -1
  61. package/dist/server/services/settings.js +21 -0
  62. package/dist/server/services/settings.js.map +1 -1
  63. package/dist/server/services/terminal/pty-manager.d.ts +3 -51
  64. package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
  65. package/dist/server/services/terminal/pty-manager.js +14 -100
  66. package/dist/server/services/terminal/pty-manager.js.map +1 -1
  67. package/dist/server/services/websocket/handler.d.ts +36 -15
  68. package/dist/server/services/websocket/handler.d.ts.map +1 -1
  69. package/dist/server/services/websocket/handler.js +452 -223
  70. package/dist/server/services/websocket/handler.js.map +1 -1
  71. package/dist/server/services/websocket/types.d.ts +6 -2
  72. package/dist/server/services/websocket/types.d.ts.map +1 -1
  73. package/hooks/bouncer.sh +11 -4
  74. package/package.json +4 -1
  75. package/server/cli/headless/claude-invoker.ts +602 -119
  76. package/server/cli/headless/index.ts +7 -1
  77. package/server/cli/headless/prompt-utils.ts +37 -5
  78. package/server/cli/headless/runner.ts +30 -8
  79. package/server/cli/headless/stall-assessor.ts +453 -22
  80. package/server/cli/headless/tool-watchdog.ts +390 -0
  81. package/server/cli/headless/types.ts +84 -1
  82. package/server/cli/improvisation-session-manager.ts +884 -143
  83. package/server/index.ts +5 -10
  84. package/server/mcp/bouncer-integration.ts +28 -0
  85. package/server/mcp/security-audit.ts +12 -8
  86. package/server/mcp/security-patterns.ts +8 -2
  87. package/server/routes/improvise.ts +6 -6
  88. package/server/services/analytics.ts +13 -3
  89. package/server/services/platform.test.ts +0 -10
  90. package/server/services/platform.ts +4 -10
  91. package/server/services/sandbox-utils.ts +78 -0
  92. package/server/services/settings.ts +25 -0
  93. package/server/services/terminal/pty-manager.ts +16 -127
  94. package/server/services/websocket/handler.ts +515 -251
  95. package/server/services/websocket/types.ts +10 -4
  96. package/dist/server/services/terminal/tmux-manager.d.ts +0 -82
  97. package/dist/server/services/terminal/tmux-manager.d.ts.map +0 -1
  98. package/dist/server/services/terminal/tmux-manager.js +0 -352
  99. package/dist/server/services/terminal/tmux-manager.js.map +0 -1
  100. package/server/services/terminal/tmux-manager.ts +0 -426
@@ -143,7 +143,7 @@ function openBrowser(url) {
143
143
  /**
144
144
  * Request device code from platform
145
145
  */
146
- async function requestDeviceCode(clientId, platformUrl) {
146
+ async function requestDeviceCode(clientId, platformUrl, force = false) {
147
147
  const machineHostname = hostname();
148
148
  const osType = type().toLowerCase();
149
149
  const cpuArch = arch();
@@ -158,6 +158,7 @@ async function requestDeviceCode(clientId, platformUrl) {
158
158
  osType,
159
159
  cpuArch,
160
160
  nodeVersion,
161
+ ...(force ? { force: true } : {}),
161
162
  }),
162
163
  });
163
164
 
@@ -243,10 +244,8 @@ export async function login(args = []) {
243
244
  const devMode = args.includes('--dev');
244
245
  const platformUrl = devMode ? DEV_PLATFORM_URL : PROD_PLATFORM_URL;
245
246
 
246
- log('\n Mstro Login\n', colors.bold + colors.cyan);
247
-
248
247
  if (devMode) {
249
- log(` [DEV MODE] Using ${platformUrl}\n`, colors.yellow);
248
+ log(`[DEV MODE] Using ${platformUrl}\n`, colors.yellow);
250
249
  }
251
250
 
252
251
  // Check if already logged in
@@ -259,18 +258,35 @@ export async function login(args = []) {
259
258
 
260
259
  const clientId = getClientId();
261
260
 
262
- log(' Requesting authorization...', colors.dim);
261
+ // If force re-auth and we have existing credentials, deregister the old device first
262
+ if (forceReauth) {
263
+ const existingCreds = getCredentials();
264
+ if (existingCreds?.token) {
265
+ try {
266
+ await fetch(`${platformUrl}/api/auth/device/deregister`, {
267
+ method: 'POST',
268
+ headers: {
269
+ 'Content-Type': 'application/json',
270
+ 'Authorization': `Bearer ${existingCreds.token}`,
271
+ },
272
+ });
273
+ } catch {
274
+ // Deregister failed (e.g. network issue), force flag on request will handle it
275
+ }
276
+ }
277
+ }
278
+
279
+ log('Requesting authorization...', colors.dim);
263
280
 
264
281
  try {
265
282
  // Step 1: Request device code
266
- const { deviceCode, userCode, verificationUrlComplete, interval } = await requestDeviceCode(clientId, platformUrl);
283
+ const { deviceCode, userCode, verificationUrlComplete, interval } = await requestDeviceCode(clientId, platformUrl, forceReauth);
267
284
 
268
285
  // Step 2: Show code and open browser
286
+ log(`Your authorization code: ${userCode}`, colors.bold);
269
287
  log('');
270
- log(` Your authorization code: ${userCode}`, colors.bold);
271
- log('');
272
- log(' Opening browser to complete login...', colors.dim);
273
- log(` If browser doesn't open, visit: ${verificationUrlComplete}`, colors.dim);
288
+ log('Opening browser to complete login...', colors.dim);
289
+ log(` If browser doesn't open, visit: ${verificationUrlComplete}`, colors.dim);
274
290
  log('');
275
291
 
276
292
  openBrowser(verificationUrlComplete);
@@ -297,10 +313,7 @@ export async function login(args = []) {
297
313
  log('');
298
314
  log(` Logged in as ${result.user.email}`, colors.bold + colors.green);
299
315
  log('');
300
- log(' This device is now connected to your mstro.app account.', colors.dim);
301
- log(' Any "mstro" commands will sync with your web dashboard.', colors.dim);
302
- log('');
303
- log(' Run "mstro" to start an orchestra.', colors.cyan);
316
+ log(' Run "mstro" to start a machine.', colors.cyan);
304
317
  log('');
305
318
  } catch (err) {
306
319
  log('');
@@ -8,6 +8,9 @@ import { existsSync, readFileSync, unlinkSync } from 'node:fs';
8
8
  import { homedir } from 'node:os';
9
9
  import { join } from 'node:path';
10
10
 
11
+ const PROD_PLATFORM_URL = 'https://api.mstro.app';
12
+ const DEV_PLATFORM_URL = 'http://localhost:4102';
13
+
11
14
  const colors = {
12
15
  reset: '\x1b[0m',
13
16
  bold: '\x1b[1m',
@@ -39,10 +42,36 @@ function getCredentials() {
39
42
  }
40
43
  }
41
44
 
45
+ /**
46
+ * Deregister device from the platform server
47
+ */
48
+ async function deregisterDevice(token, platformUrl) {
49
+ try {
50
+ const response = await fetch(`${platformUrl}/api/auth/device/deregister`, {
51
+ method: 'POST',
52
+ headers: {
53
+ 'Content-Type': 'application/json',
54
+ 'Authorization': `Bearer ${token}`,
55
+ },
56
+ });
57
+
58
+ if (!response.ok) {
59
+ const data = await response.json().catch(() => ({}));
60
+ log(` Warning: Could not deregister device from server: ${data.error || response.statusText}`, colors.yellow);
61
+ }
62
+ } catch {
63
+ // Network error - proceed with local logout anyway
64
+ log(' Warning: Could not reach server to deregister device. Local credentials will still be removed.', colors.yellow);
65
+ }
66
+ }
67
+
42
68
  /**
43
69
  * Main logout command
44
70
  */
45
- export async function logout() {
71
+ export async function logout(args = []) {
72
+ const devMode = Array.isArray(args) && args.includes('--dev');
73
+ const platformUrl = devMode ? DEV_PLATFORM_URL : PROD_PLATFORM_URL;
74
+
46
75
  log('\n Mstro Logout\n', colors.bold + colors.cyan);
47
76
 
48
77
  const creds = getCredentials();
@@ -56,6 +85,11 @@ export async function logout() {
56
85
  const email = creds.email;
57
86
 
58
87
  try {
88
+ // Deregister device from server so it can be re-registered later
89
+ if (creds.token) {
90
+ await deregisterDevice(creds.token, platformUrl);
91
+ }
92
+
59
93
  // Delete credentials file
60
94
  if (existsSync(CREDENTIALS_FILE)) {
61
95
  unlinkSync(CREDENTIALS_FILE);
@@ -187,7 +187,7 @@ export async function status() {
187
187
 
188
188
  // Quick commands
189
189
  log(' Commands', colors.bold);
190
- log(' mstro Start an orchestra', colors.dim);
190
+ log(' mstro Start a machine', colors.dim);
191
191
  log(' mstro login Sign in to your account', colors.dim);
192
192
  log(' mstro logout Sign out', colors.dim);
193
193
  log(' mstro whoami Show account details', colors.dim);
package/bin/mstro.js CHANGED
@@ -13,15 +13,14 @@
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';
@@ -47,7 +46,6 @@ const USER_CWD = process.cwd();
47
46
  // First-run detection paths
48
47
  const MSTRO_CONFIG_DIR = join(homedir(), '.mstro');
49
48
  const MSTRO_FIRST_RUN_FLAG = join(MSTRO_CONFIG_DIR, '.configured');
50
- const MSTRO_TERMINAL_CHECKED_FLAG = join(MSTRO_CONFIG_DIR, '.terminal-checked');
51
49
  const CLAUDE_SETTINGS_FILE = join(homedir(), '.claude', 'settings.json');
52
50
  const CLAUDE_HOOKS_DIR = join(homedir(), '.claude', 'hooks');
53
51
  const BOUNCER_HOOK_FILE = join(CLAUDE_HOOKS_DIR, 'bouncer.sh');
@@ -205,7 +203,6 @@ function showHelp() {
205
203
  log(' mstro whoami Show current user and device info', colors.dim);
206
204
  log(' mstro status Show connection and auth status', colors.dim);
207
205
  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
206
  log(' mstro -p 4105 Start on specific port (overrides auto port)', colors.dim);
210
207
  log(' mstro configure-hooks Configure Claude Code security hooks', colors.dim);
211
208
  log(' mstro --version Show version number', colors.dim);
@@ -218,7 +215,7 @@ function showHelp() {
218
215
  log('');
219
216
  log(' Authentication:', colors.bold);
220
217
  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);
218
+ log(' Once logged in, machines sync automatically with your web dashboard.', colors.dim);
222
219
  log('');
223
220
  log(' Security:', colors.bold);
224
221
  log(' Mstro includes a Security Bouncer that automatically manages', colors.dim);
@@ -229,7 +226,7 @@ function showHelp() {
229
226
 
230
227
  function runNpmScript(script, args = [], envOverrides = {}) {
231
228
  const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
232
- const child = spawn(npmCmd, ['run', script, ...args], {
229
+ const child = spawn(npmCmd, ['run', '--silent', script, ...args], {
233
230
  cwd: CLIENT_ROOT,
234
231
  stdio: 'inherit',
235
232
  env: { ...process.env, MSTRO_WORKING_DIR: USER_CWD, ...envOverrides },
@@ -291,7 +288,6 @@ function runConfigureHooks(andThenStart = false) {
291
288
  markConfigured();
292
289
  if (andThenStart) {
293
290
  // After configuring, start the server
294
- log('\nStarting Mstro client...', colors.bold + colors.cyan);
295
291
  const requestedPort = parsePort(process.argv.slice(2));
296
292
  const envOverrides = requestedPort ? { PORT: String(requestedPort) } : {};
297
293
  runNpmScript('start', [], envOverrides);
@@ -366,95 +362,6 @@ function showLoginRequired() {
366
362
  /**
367
363
  * Check if node-pty is loadable (native module compiled correctly)
368
364
  */
369
- async function isNodePtyAvailable() {
370
- 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;
378
- }
379
- }
380
-
381
- /**
382
- * Try to rebuild node-pty silently. Returns true on success.
383
- */
384
- function tryRebuildNodePty() {
385
- try {
386
- execSync('npm rebuild node-pty', { cwd: CLIENT_ROOT, stdio: 'pipe' });
387
- return true;
388
- } catch {
389
- return false;
390
- }
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';
404
- }
405
- }
406
-
407
- /**
408
- * First-run terminal setup check (runs after bouncer setup).
409
- * Tries to rebuild node-pty automatically. If that fails, shows instructions.
410
- */
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);
422
- return;
423
- }
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;
442
- }
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
- }
457
-
458
365
  async function startServer(envOverrides) {
459
366
  if (!isLoggedIn()) {
460
367
  showLoginRequired();
@@ -473,16 +380,7 @@ async function startServer(envOverrides) {
473
380
  }
474
381
  }
475
382
 
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
- }
483
-
484
383
  showUpdateNotification();
485
- log('\nStarting Mstro client...', colors.bold + colors.cyan);
486
384
  runNpmScript('start', [], envOverrides);
487
385
  }
488
386
 
@@ -504,7 +402,7 @@ async function main() {
504
402
  }],
505
403
  ['logout', async () => {
506
404
  const { logout } = await import('./commands/logout.js');
507
- await logout();
405
+ await logout(args.slice(args.indexOf('logout') + 1));
508
406
  }],
509
407
  ['whoami', async () => {
510
408
  const { whoami } = await import('./commands/whoami.js');
@@ -518,7 +416,6 @@ async function main() {
518
416
  const { telemetry } = await import('./commands/config.js');
519
417
  await telemetry(args.slice(args.indexOf('telemetry') + 1));
520
418
  }],
521
- ['setup-terminal', () => setupTerminal()],
522
419
  ['configure-hooks', () => runConfigureHooks(false)],
523
420
  ]);
524
421
 
@@ -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;AAm5BD;;;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,CA0I1B"}