mstro-app 0.2.0 → 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.
- package/PRIVACY.md +126 -0
- package/README.md +24 -23
- package/bin/commands/login.js +79 -49
- package/bin/mstro.js +240 -37
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker.js +133 -27
- package/dist/server/cli/headless/claude-invoker.js.map +1 -1
- package/dist/server/cli/headless/runner.d.ts.map +1 -1
- package/dist/server/cli/headless/runner.js +23 -0
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/stall-assessor.d.ts +3 -1
- package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
- package/dist/server/cli/headless/stall-assessor.js +20 -1
- package/dist/server/cli/headless/stall-assessor.js.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.d.ts +4 -1
- package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.js +30 -24
- package/dist/server/cli/headless/tool-watchdog.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts +19 -1
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +28 -1
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +221 -29
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/index.js +0 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/services/analytics.d.ts.map +1 -1
- package/dist/server/services/analytics.js +13 -1
- package/dist/server/services/analytics.js.map +1 -1
- package/dist/server/services/platform.d.ts.map +1 -1
- package/dist/server/services/platform.js +13 -1
- package/dist/server/services/platform.js.map +1 -1
- package/dist/server/services/terminal/pty-manager.d.ts +2 -0
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-manager.js +50 -3
- package/dist/server/services/terminal/pty-manager.js.map +1 -1
- package/dist/server/services/websocket/file-explorer-handlers.d.ts +5 -0
- package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/file-explorer-handlers.js +518 -0
- package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -0
- package/dist/server/services/websocket/git-handlers.d.ts +36 -0
- package/dist/server/services/websocket/git-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/git-handlers.js +797 -0
- package/dist/server/services/websocket/git-handlers.js.map +1 -0
- package/dist/server/services/websocket/git-pr-handlers.d.ts +4 -0
- package/dist/server/services/websocket/git-pr-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/git-pr-handlers.js +299 -0
- package/dist/server/services/websocket/git-pr-handlers.js.map +1 -0
- package/dist/server/services/websocket/git-worktree-handlers.d.ts +4 -0
- package/dist/server/services/websocket/git-worktree-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/git-worktree-handlers.js +353 -0
- package/dist/server/services/websocket/git-worktree-handlers.js.map +1 -0
- package/dist/server/services/websocket/handler-context.d.ts +32 -0
- package/dist/server/services/websocket/handler-context.d.ts.map +1 -0
- package/dist/server/services/websocket/handler-context.js +4 -0
- package/dist/server/services/websocket/handler-context.js.map +1 -0
- package/dist/server/services/websocket/handler.d.ts +27 -359
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +67 -2328
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/index.d.ts +1 -1
- package/dist/server/services/websocket/index.d.ts.map +1 -1
- package/dist/server/services/websocket/index.js.map +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts +10 -0
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/session-handlers.js +507 -0
- package/dist/server/services/websocket/session-handlers.js.map +1 -0
- package/dist/server/services/websocket/settings-handlers.d.ts +6 -0
- package/dist/server/services/websocket/settings-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/settings-handlers.js +125 -0
- package/dist/server/services/websocket/settings-handlers.js.map +1 -0
- package/dist/server/services/websocket/tab-handlers.d.ts +10 -0
- package/dist/server/services/websocket/tab-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/tab-handlers.js +131 -0
- package/dist/server/services/websocket/tab-handlers.js.map +1 -0
- package/dist/server/services/websocket/terminal-handlers.d.ts +9 -0
- package/dist/server/services/websocket/terminal-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/terminal-handlers.js +220 -0
- package/dist/server/services/websocket/terminal-handlers.js.map +1 -0
- package/dist/server/services/websocket/types.d.ts +63 -2
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/package.json +4 -2
- package/server/README.md +176 -159
- package/server/cli/headless/claude-invoker.ts +155 -31
- package/server/cli/headless/output-utils.test.ts +225 -0
- package/server/cli/headless/runner.ts +25 -0
- package/server/cli/headless/stall-assessor.test.ts +165 -0
- package/server/cli/headless/stall-assessor.ts +25 -0
- package/server/cli/headless/tool-watchdog.test.ts +429 -0
- package/server/cli/headless/tool-watchdog.ts +33 -25
- package/server/cli/headless/types.ts +10 -1
- package/server/cli/improvisation-session-manager.ts +277 -30
- package/server/index.ts +0 -4
- package/server/mcp/README.md +59 -67
- package/server/mcp/bouncer-integration.test.ts +161 -0
- package/server/mcp/security-patterns.test.ts +258 -0
- package/server/services/analytics.ts +13 -1
- package/server/services/platform.ts +12 -1
- package/server/services/terminal/pty-manager.ts +53 -3
- package/server/services/websocket/autocomplete.test.ts +194 -0
- package/server/services/websocket/file-explorer-handlers.ts +587 -0
- package/server/services/websocket/git-handlers.ts +924 -0
- package/server/services/websocket/git-pr-handlers.ts +363 -0
- package/server/services/websocket/git-worktree-handlers.ts +403 -0
- package/server/services/websocket/handler-context.ts +44 -0
- package/server/services/websocket/handler.test.ts +1 -1
- package/server/services/websocket/handler.ts +83 -2678
- package/server/services/websocket/index.ts +1 -1
- package/server/services/websocket/session-handlers.ts +574 -0
- package/server/services/websocket/settings-handlers.ts +150 -0
- package/server/services/websocket/tab-handlers.ts +150 -0
- package/server/services/websocket/terminal-handlers.ts +277 -0
- package/server/services/websocket/types.ts +135 -0
- package/bin/release.sh +0 -110
package/PRIVACY.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Mstro Privacy Policy
|
|
2
|
+
|
|
3
|
+
**Effective Date:** February 2026
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Mstro, Inc. ("we", "us", "our") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, and safeguard information when you use the Mstro CLI and related services.
|
|
8
|
+
|
|
9
|
+
## Information We Collect
|
|
10
|
+
|
|
11
|
+
### 1. Account Information
|
|
12
|
+
When you create an account, we collect:
|
|
13
|
+
- Email address
|
|
14
|
+
- Name (optional)
|
|
15
|
+
- Device identifiers (for authentication)
|
|
16
|
+
|
|
17
|
+
### 2. Telemetry Data (Anonymous)
|
|
18
|
+
By default, Mstro collects anonymous telemetry to improve the software:
|
|
19
|
+
|
|
20
|
+
**Error Reporting (via Sentry):**
|
|
21
|
+
- Stack traces and error messages
|
|
22
|
+
- Mstro version and environment
|
|
23
|
+
- Operating system and architecture
|
|
24
|
+
- Node.js version
|
|
25
|
+
|
|
26
|
+
**Usage Analytics (via PostHog):**
|
|
27
|
+
- Feature usage patterns
|
|
28
|
+
- Session duration
|
|
29
|
+
- Command frequency
|
|
30
|
+
|
|
31
|
+
**What we DO NOT collect:**
|
|
32
|
+
- File contents or source code
|
|
33
|
+
- API keys or credentials
|
|
34
|
+
- Personal identifying information in error reports
|
|
35
|
+
- IP addresses (stripped before storage)
|
|
36
|
+
|
|
37
|
+
### 3. Session Data
|
|
38
|
+
When using mstro.app:
|
|
39
|
+
- Orchestra configurations
|
|
40
|
+
- Session metadata
|
|
41
|
+
- Connection timestamps
|
|
42
|
+
|
|
43
|
+
## How to Opt Out of Telemetry
|
|
44
|
+
|
|
45
|
+
You can disable all telemetry at any time:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
mstro telemetry off
|
|
49
|
+
|
|
50
|
+
# Or via environment variable
|
|
51
|
+
export MSTRO_TELEMETRY=0
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
When telemetry is disabled:
|
|
55
|
+
- No error reports are sent to Sentry
|
|
56
|
+
- No usage analytics are sent to PostHog
|
|
57
|
+
- Core functionality remains unchanged
|
|
58
|
+
|
|
59
|
+
## How We Use Your Information
|
|
60
|
+
|
|
61
|
+
We use collected information to:
|
|
62
|
+
- Provide and maintain the Mstro service
|
|
63
|
+
- Identify and fix bugs and crashes
|
|
64
|
+
- Improve software performance and features
|
|
65
|
+
- Communicate service updates and changes
|
|
66
|
+
- Ensure security and prevent abuse
|
|
67
|
+
|
|
68
|
+
## Data Sharing
|
|
69
|
+
|
|
70
|
+
We do not sell your personal information. We may share data with:
|
|
71
|
+
|
|
72
|
+
- **Service Providers:** Third-party services that help operate Mstro (e.g., Sentry for error tracking, PostHog for analytics)
|
|
73
|
+
- **Legal Requirements:** When required by law or to protect our rights
|
|
74
|
+
- **Business Transfers:** In connection with a merger or acquisition
|
|
75
|
+
|
|
76
|
+
## Data Retention
|
|
77
|
+
|
|
78
|
+
- **Account Data:** Retained while your account is active
|
|
79
|
+
- **Telemetry Data:** Retained for up to 90 days
|
|
80
|
+
- **Error Reports:** Retained for up to 30 days
|
|
81
|
+
|
|
82
|
+
## Data Security
|
|
83
|
+
|
|
84
|
+
We implement appropriate security measures including:
|
|
85
|
+
- Encryption in transit (TLS/HTTPS)
|
|
86
|
+
- Secure credential storage (chmod 600)
|
|
87
|
+
- Regular security audits
|
|
88
|
+
- Minimal data collection principles
|
|
89
|
+
|
|
90
|
+
## Your Rights
|
|
91
|
+
|
|
92
|
+
Depending on your jurisdiction, you may have rights to:
|
|
93
|
+
- Access your personal data
|
|
94
|
+
- Correct inaccurate data
|
|
95
|
+
- Delete your data
|
|
96
|
+
- Export your data
|
|
97
|
+
- Opt out of data collection
|
|
98
|
+
|
|
99
|
+
To exercise these rights, contact us at the information below.
|
|
100
|
+
|
|
101
|
+
## Children's Privacy
|
|
102
|
+
|
|
103
|
+
Mstro is not intended for users under 13 years of age. We do not knowingly collect information from children.
|
|
104
|
+
|
|
105
|
+
## International Users
|
|
106
|
+
|
|
107
|
+
Mstro is operated from the United States. By using the service, you consent to the transfer of your information to the United States.
|
|
108
|
+
|
|
109
|
+
## Changes to This Policy
|
|
110
|
+
|
|
111
|
+
We may update this Privacy Policy periodically. We will notify users of significant changes through the CLI or our website.
|
|
112
|
+
|
|
113
|
+
## Open Source
|
|
114
|
+
|
|
115
|
+
Mstro CLI is open source under the MIT License. You can review the code at:
|
|
116
|
+
https://github.com/mstro-app/mstro
|
|
117
|
+
|
|
118
|
+
## Contact Us
|
|
119
|
+
|
|
120
|
+
For privacy-related questions or concerns:
|
|
121
|
+
- Website: https://mstro.app
|
|
122
|
+
- GitHub: https://github.com/mstro-app/mstro/issues
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# mstro
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run [Claude Code](https://docs.anthropic.com/en/docs/claude-code) from any browser. The CLI runs locally on your machine and streams live sessions to [mstro.app](https://mstro.app) via a secure WebSocket relay.
|
|
4
4
|
|
|
5
|
-
**mstro**
|
|
6
|
-
|
|
7
|
-
**Get started at [mstro.app](https://mstro.app)** — create an account, then install this CLI to connect your machine.
|
|
5
|
+
**mstro** runs on your laptop, cloud VM, or CI server and connects to the mstro.app web interface. You write prompts in the browser, Claude Code runs in your terminal.
|
|
8
6
|
|
|
9
7
|
## How It Works
|
|
10
8
|
|
|
@@ -14,7 +12,7 @@ Browser (mstro.app) <--WebSocket--> Platform Server (relay) <--WebSocket-->
|
|
|
14
12
|
Claude Code CLI
|
|
15
13
|
```
|
|
16
14
|
|
|
17
|
-
1. `mstro` starts a local server and connects to the mstro.app platform
|
|
15
|
+
1. `mstro` starts a local server and connects to the mstro.app platform
|
|
18
16
|
2. You open [mstro.app](https://mstro.app) in any browser and see your connected machine
|
|
19
17
|
3. Prompts you send in the browser are relayed to your machine
|
|
20
18
|
4. Claude Code runs locally with full access to your project files
|
|
@@ -33,19 +31,25 @@ Requires [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed
|
|
|
33
31
|
## Quick Start
|
|
34
32
|
|
|
35
33
|
```bash
|
|
36
|
-
mstro
|
|
37
|
-
mstro # Start mstro in your project directory
|
|
34
|
+
mstro # Logs in automatically on first run, then starts the server
|
|
38
35
|
```
|
|
39
36
|
|
|
40
|
-
|
|
37
|
+
Or without installing globally:
|
|
41
38
|
|
|
42
|
-
|
|
39
|
+
```bash
|
|
40
|
+
npx mstro-app # Same thing — login + launch in one command
|
|
41
|
+
```
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
On first run, mstro will:
|
|
44
|
+
1. Open your browser to authenticate with your mstro.app account
|
|
45
|
+
2. Offer to set up the **Security Bouncer** — say yes
|
|
46
|
+
3. Connect to the platform
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
Then open [mstro.app](https://mstro.app) in your browser. Your machine appears as a connected workspace. Start prompting.
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
## Security Bouncer
|
|
51
|
+
|
|
52
|
+
The Bouncer automatically approves or blocks Claude Code tool calls so you don't have to. It installs as a hook at `~/.claude/hooks/bouncer.sh` and applies to all Claude Code sessions.
|
|
49
53
|
|
|
50
54
|
**Mstro sessions (headless)** get the full 2-layer system:
|
|
51
55
|
|
|
@@ -64,22 +68,19 @@ The bouncer is set up automatically on first run. To reconfigure or install manu
|
|
|
64
68
|
mstro configure-hooks
|
|
65
69
|
```
|
|
66
70
|
|
|
67
|
-
This installs a hook at `~/.claude/hooks/bouncer.sh` and registers it in `~/.claude/settings.json`.
|
|
68
|
-
|
|
69
|
-
Set `BOUNCER_USE_AI=false` to disable the AI analysis layer (pattern matching only).
|
|
70
|
-
|
|
71
71
|
## CLI Reference
|
|
72
72
|
|
|
73
73
|
### Commands
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
|
-
mstro # Start
|
|
77
|
-
mstro login #
|
|
76
|
+
mstro # Start mstro (logs in automatically if needed)
|
|
77
|
+
mstro login # Re-authenticate or switch accounts
|
|
78
78
|
mstro logout # Sign out
|
|
79
79
|
mstro whoami # Show current user and device info
|
|
80
80
|
mstro status # Show connection and auth status
|
|
81
81
|
mstro setup-terminal # Enable web terminal (compiles native module)
|
|
82
82
|
mstro configure-hooks # Install/reconfigure Security Bouncer
|
|
83
|
+
mstro telemetry [on|off] # Show/toggle anonymous telemetry
|
|
83
84
|
```
|
|
84
85
|
|
|
85
86
|
### Options
|
|
@@ -89,7 +90,6 @@ mstro configure-hooks # Install/reconfigure Security Bouncer
|
|
|
89
90
|
| `-p, --port <port>` | Start on a specific port (default: 4101, auto-increments if busy) |
|
|
90
91
|
| `-w, --working-dir <dir>` | Set working directory |
|
|
91
92
|
| `-v, --verbose` | Verbose output |
|
|
92
|
-
| `--dev` | Connect to local platform at localhost:4102 |
|
|
93
93
|
| `--version` | Show version |
|
|
94
94
|
| `--help` | Show help |
|
|
95
95
|
|
|
@@ -98,11 +98,11 @@ mstro configure-hooks # Install/reconfigure Security Bouncer
|
|
|
98
98
|
Run multiple mstro instances for different projects. Each auto-selects an available port:
|
|
99
99
|
|
|
100
100
|
```
|
|
101
|
-
$ mstro # Project A
|
|
102
|
-
$ mstro # Project B
|
|
101
|
+
$ mstro # Project A
|
|
102
|
+
$ mstro # Project B
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
Each instance appears as a separate
|
|
105
|
+
Each instance appears as a separate workspace in the web interface.
|
|
106
106
|
|
|
107
107
|
## Environment Variables
|
|
108
108
|
|
|
@@ -110,6 +110,7 @@ Each instance appears as a separate orchestra in the web interface.
|
|
|
110
110
|
|----------|-------------|
|
|
111
111
|
| `PORT` | Override server port |
|
|
112
112
|
| `BOUNCER_USE_AI` | Set to `false` to disable AI analysis layer |
|
|
113
|
+
| `MSTRO_TELEMETRY` | Set to `0` to disable telemetry |
|
|
113
114
|
| `PLATFORM_URL` | Platform server URL (default: `https://api.mstro.app`) |
|
|
114
115
|
|
|
115
116
|
## Config Files
|
|
@@ -129,7 +130,7 @@ mstro stores config in `~/.mstro/`:
|
|
|
129
130
|
|
|
130
131
|
### Optional: Web Terminal
|
|
131
132
|
|
|
132
|
-
The web terminal feature requires a native module (`node-pty`). mstro works without it
|
|
133
|
+
The web terminal feature requires a native module (`node-pty`). mstro works without it — you just won't have the terminal tab in the browser.
|
|
133
134
|
|
|
134
135
|
On first run, mstro will automatically attempt to compile `node-pty`. If your system has build tools installed, it just works. If not, mstro will let you know what to install:
|
|
135
136
|
|
package/bin/commands/login.js
CHANGED
|
@@ -236,10 +236,68 @@ async function pollForAuth(deviceCode, interval, platformUrl, maxAttempts = 180)
|
|
|
236
236
|
throw new Error('Authorization timed out. Please try again.');
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Deregister old device before force re-auth
|
|
241
|
+
*/
|
|
242
|
+
async function deregisterOldDevice(platformUrl) {
|
|
243
|
+
const existingCreds = getCredentials();
|
|
244
|
+
if (!existingCreds?.token) return;
|
|
245
|
+
try {
|
|
246
|
+
await fetch(`${platformUrl}/api/auth/device/deregister`, {
|
|
247
|
+
method: 'POST',
|
|
248
|
+
headers: {
|
|
249
|
+
'Content-Type': 'application/json',
|
|
250
|
+
'Authorization': `Bearer ${existingCreds.token}`,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
} catch {
|
|
254
|
+
// Deregister failed (e.g. network issue), force flag on request will handle it
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Run the device code authorization flow. Throws on failure.
|
|
260
|
+
*/
|
|
261
|
+
async function runDeviceCodeFlow(clientId, platformUrl, forceReauth) {
|
|
262
|
+
const { deviceCode, userCode, verificationUrlComplete, interval } = await requestDeviceCode(clientId, platformUrl, forceReauth);
|
|
263
|
+
|
|
264
|
+
log(`Your authorization code: ${userCode}`, colors.bold);
|
|
265
|
+
log('');
|
|
266
|
+
log('Opening browser to complete login...', colors.dim);
|
|
267
|
+
log(` If browser doesn't open, visit: ${verificationUrlComplete}`, colors.dim);
|
|
268
|
+
log('');
|
|
269
|
+
|
|
270
|
+
openBrowser(verificationUrlComplete);
|
|
271
|
+
|
|
272
|
+
log(' Waiting for authorization', colors.dim);
|
|
273
|
+
process.stdout.write(' ');
|
|
274
|
+
|
|
275
|
+
const result = await pollForAuth(deviceCode, interval, platformUrl);
|
|
276
|
+
|
|
277
|
+
const credentials = {
|
|
278
|
+
token: result.accessToken,
|
|
279
|
+
userId: result.user.id,
|
|
280
|
+
email: result.user.email,
|
|
281
|
+
name: result.user.name,
|
|
282
|
+
clientId,
|
|
283
|
+
createdAt: new Date().toISOString(),
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
saveCredentials(credentials);
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
|
|
239
290
|
/**
|
|
240
291
|
* Main login command
|
|
292
|
+
*
|
|
293
|
+
* @param {string[]} args - CLI arguments
|
|
294
|
+
* @param {object} options
|
|
295
|
+
* @param {boolean} options.inline - When true, called from startServer() auto-login:
|
|
296
|
+
* skips "already logged in" check, omits post-login tips, and throws on failure
|
|
297
|
+
* instead of calling process.exit(1).
|
|
241
298
|
*/
|
|
242
|
-
export async function login(args = []) {
|
|
299
|
+
export async function login(args = [], options = {}) {
|
|
300
|
+
const { inline = false } = options;
|
|
243
301
|
const forceReauth = args.includes('--force') || args.includes('-f');
|
|
244
302
|
const devMode = args.includes('--dev');
|
|
245
303
|
const platformUrl = devMode ? DEV_PLATFORM_URL : PROD_PLATFORM_URL;
|
|
@@ -248,8 +306,8 @@ export async function login(args = []) {
|
|
|
248
306
|
log(`[DEV MODE] Using ${platformUrl}\n`, colors.yellow);
|
|
249
307
|
}
|
|
250
308
|
|
|
251
|
-
// Check if already logged in
|
|
252
|
-
if (isLoggedIn() && !forceReauth) {
|
|
309
|
+
// Check if already logged in (skip when called inline — caller already checked)
|
|
310
|
+
if (!inline && isLoggedIn() && !forceReauth) {
|
|
253
311
|
const creds = getCredentials();
|
|
254
312
|
log(` Already logged in as ${creds.email}`, colors.green);
|
|
255
313
|
log(` Use "mstro logout" to sign out, or "mstro login --force" to re-authenticate.\n`, colors.dim);
|
|
@@ -258,64 +316,36 @@ export async function login(args = []) {
|
|
|
258
316
|
|
|
259
317
|
const clientId = getClientId();
|
|
260
318
|
|
|
261
|
-
// If force re-auth and we have existing credentials, deregister the old device first
|
|
262
319
|
if (forceReauth) {
|
|
263
|
-
|
|
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
|
-
}
|
|
320
|
+
await deregisterOldDevice(platformUrl);
|
|
277
321
|
}
|
|
278
322
|
|
|
279
323
|
log('Requesting authorization...', colors.dim);
|
|
280
324
|
|
|
281
325
|
try {
|
|
282
|
-
|
|
283
|
-
const { deviceCode, userCode, verificationUrlComplete, interval } = await requestDeviceCode(clientId, platformUrl, forceReauth);
|
|
284
|
-
|
|
285
|
-
// Step 2: Show code and open browser
|
|
286
|
-
log(`Your authorization code: ${userCode}`, colors.bold);
|
|
287
|
-
log('');
|
|
288
|
-
log('Opening browser to complete login...', colors.dim);
|
|
289
|
-
log(` If browser doesn't open, visit: ${verificationUrlComplete}`, colors.dim);
|
|
290
|
-
log('');
|
|
291
|
-
|
|
292
|
-
openBrowser(verificationUrlComplete);
|
|
293
|
-
|
|
294
|
-
// Step 3: Poll for result
|
|
295
|
-
log(' Waiting for authorization', colors.dim);
|
|
296
|
-
process.stdout.write(' ');
|
|
297
|
-
|
|
298
|
-
const result = await pollForAuth(deviceCode, interval, platformUrl);
|
|
299
|
-
|
|
300
|
-
// Step 4: Save credentials
|
|
301
|
-
const credentials = {
|
|
302
|
-
token: result.accessToken,
|
|
303
|
-
userId: result.user.id,
|
|
304
|
-
email: result.user.email,
|
|
305
|
-
name: result.user.name,
|
|
306
|
-
clientId,
|
|
307
|
-
createdAt: new Date().toISOString(),
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
saveCredentials(credentials);
|
|
326
|
+
const result = await runDeviceCodeFlow(clientId, platformUrl, forceReauth);
|
|
311
327
|
|
|
312
328
|
log('');
|
|
313
329
|
log('');
|
|
314
330
|
log(` Logged in as ${result.user.email}`, colors.bold + colors.green);
|
|
315
331
|
log('');
|
|
316
|
-
|
|
317
|
-
|
|
332
|
+
|
|
333
|
+
if (!inline) {
|
|
334
|
+
log(' Run "mstro" to start a machine.', colors.cyan);
|
|
335
|
+
log('');
|
|
336
|
+
|
|
337
|
+
// Check if node-pty is available, show tip if not
|
|
338
|
+
try {
|
|
339
|
+
await import('node-pty');
|
|
340
|
+
} catch {
|
|
341
|
+
log(' Tip: Terminal support requires native compilation.', colors.dim);
|
|
342
|
+
log(' Run "mstro setup-terminal" to enable web terminal.\n', colors.dim);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
318
345
|
} catch (err) {
|
|
346
|
+
if (inline) {
|
|
347
|
+
throw err;
|
|
348
|
+
}
|
|
319
349
|
log('');
|
|
320
350
|
log(` Login failed: ${err.message}`, colors.red);
|
|
321
351
|
log('');
|