mstro-app 0.1.47
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/LICENSE +21 -0
- package/README.md +177 -0
- package/bin/commands/config.js +145 -0
- package/bin/commands/login.js +313 -0
- package/bin/commands/logout.js +75 -0
- package/bin/commands/status.js +197 -0
- package/bin/commands/whoami.js +161 -0
- package/bin/configure-claude.js +298 -0
- package/bin/mstro.js +581 -0
- package/bin/postinstall.js +45 -0
- package/bin/release.sh +110 -0
- package/dist/server/cli/headless/claude-invoker.d.ts +17 -0
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -0
- package/dist/server/cli/headless/claude-invoker.js +311 -0
- package/dist/server/cli/headless/claude-invoker.js.map +1 -0
- package/dist/server/cli/headless/index.d.ts +13 -0
- package/dist/server/cli/headless/index.d.ts.map +1 -0
- package/dist/server/cli/headless/index.js +10 -0
- package/dist/server/cli/headless/index.js.map +1 -0
- package/dist/server/cli/headless/mcp-config.d.ts +11 -0
- package/dist/server/cli/headless/mcp-config.d.ts.map +1 -0
- package/dist/server/cli/headless/mcp-config.js +76 -0
- package/dist/server/cli/headless/mcp-config.js.map +1 -0
- package/dist/server/cli/headless/output-utils.d.ts +33 -0
- package/dist/server/cli/headless/output-utils.d.ts.map +1 -0
- package/dist/server/cli/headless/output-utils.js +101 -0
- package/dist/server/cli/headless/output-utils.js.map +1 -0
- package/dist/server/cli/headless/prompt-utils.d.ts +21 -0
- package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -0
- package/dist/server/cli/headless/prompt-utils.js +84 -0
- package/dist/server/cli/headless/prompt-utils.js.map +1 -0
- package/dist/server/cli/headless/runner.d.ts +24 -0
- package/dist/server/cli/headless/runner.d.ts.map +1 -0
- package/dist/server/cli/headless/runner.js +99 -0
- package/dist/server/cli/headless/runner.js.map +1 -0
- package/dist/server/cli/headless/types.d.ts +106 -0
- package/dist/server/cli/headless/types.d.ts.map +1 -0
- package/dist/server/cli/headless/types.js +4 -0
- package/dist/server/cli/headless/types.js.map +1 -0
- package/dist/server/cli/improvisation-session-manager.d.ts +155 -0
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -0
- package/dist/server/cli/improvisation-session-manager.js +415 -0
- package/dist/server/cli/improvisation-session-manager.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +386 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/mcp/bouncer-cli.d.ts +3 -0
- package/dist/server/mcp/bouncer-cli.d.ts.map +1 -0
- package/dist/server/mcp/bouncer-cli.js +99 -0
- package/dist/server/mcp/bouncer-cli.js.map +1 -0
- package/dist/server/mcp/bouncer-integration.d.ts +36 -0
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -0
- package/dist/server/mcp/bouncer-integration.js +301 -0
- package/dist/server/mcp/bouncer-integration.js.map +1 -0
- package/dist/server/mcp/security-audit.d.ts +52 -0
- package/dist/server/mcp/security-audit.d.ts.map +1 -0
- package/dist/server/mcp/security-audit.js +118 -0
- package/dist/server/mcp/security-audit.js.map +1 -0
- package/dist/server/mcp/security-patterns.d.ts +73 -0
- package/dist/server/mcp/security-patterns.d.ts.map +1 -0
- package/dist/server/mcp/security-patterns.js +247 -0
- package/dist/server/mcp/security-patterns.js.map +1 -0
- package/dist/server/mcp/server.d.ts +3 -0
- package/dist/server/mcp/server.d.ts.map +1 -0
- package/dist/server/mcp/server.js +146 -0
- package/dist/server/mcp/server.js.map +1 -0
- package/dist/server/routes/files.d.ts +9 -0
- package/dist/server/routes/files.d.ts.map +1 -0
- package/dist/server/routes/files.js +24 -0
- package/dist/server/routes/files.js.map +1 -0
- package/dist/server/routes/improvise.d.ts +3 -0
- package/dist/server/routes/improvise.d.ts.map +1 -0
- package/dist/server/routes/improvise.js +72 -0
- package/dist/server/routes/improvise.js.map +1 -0
- package/dist/server/routes/index.d.ts +10 -0
- package/dist/server/routes/index.d.ts.map +1 -0
- package/dist/server/routes/index.js +12 -0
- package/dist/server/routes/index.js.map +1 -0
- package/dist/server/routes/instances.d.ts +10 -0
- package/dist/server/routes/instances.d.ts.map +1 -0
- package/dist/server/routes/instances.js +47 -0
- package/dist/server/routes/instances.js.map +1 -0
- package/dist/server/routes/notifications.d.ts +3 -0
- package/dist/server/routes/notifications.d.ts.map +1 -0
- package/dist/server/routes/notifications.js +136 -0
- package/dist/server/routes/notifications.js.map +1 -0
- package/dist/server/services/analytics.d.ts +56 -0
- package/dist/server/services/analytics.d.ts.map +1 -0
- package/dist/server/services/analytics.js +240 -0
- package/dist/server/services/analytics.js.map +1 -0
- package/dist/server/services/auth.d.ts +26 -0
- package/dist/server/services/auth.d.ts.map +1 -0
- package/dist/server/services/auth.js +71 -0
- package/dist/server/services/auth.js.map +1 -0
- package/dist/server/services/client-id.d.ts +10 -0
- package/dist/server/services/client-id.d.ts.map +1 -0
- package/dist/server/services/client-id.js +61 -0
- package/dist/server/services/client-id.js.map +1 -0
- package/dist/server/services/credentials.d.ts +39 -0
- package/dist/server/services/credentials.d.ts.map +1 -0
- package/dist/server/services/credentials.js +110 -0
- package/dist/server/services/credentials.js.map +1 -0
- package/dist/server/services/files.d.ts +119 -0
- package/dist/server/services/files.d.ts.map +1 -0
- package/dist/server/services/files.js +560 -0
- package/dist/server/services/files.js.map +1 -0
- package/dist/server/services/instances.d.ts +52 -0
- package/dist/server/services/instances.d.ts.map +1 -0
- package/dist/server/services/instances.js +241 -0
- package/dist/server/services/instances.js.map +1 -0
- package/dist/server/services/pathUtils.d.ts +47 -0
- package/dist/server/services/pathUtils.d.ts.map +1 -0
- package/dist/server/services/pathUtils.js +124 -0
- package/dist/server/services/pathUtils.js.map +1 -0
- package/dist/server/services/platform.d.ts +72 -0
- package/dist/server/services/platform.d.ts.map +1 -0
- package/dist/server/services/platform.js +368 -0
- package/dist/server/services/platform.js.map +1 -0
- package/dist/server/services/sentry.d.ts +5 -0
- package/dist/server/services/sentry.d.ts.map +1 -0
- package/dist/server/services/sentry.js +71 -0
- package/dist/server/services/sentry.js.map +1 -0
- package/dist/server/services/terminal/pty-manager.d.ts +149 -0
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -0
- package/dist/server/services/terminal/pty-manager.js +377 -0
- package/dist/server/services/terminal/pty-manager.js.map +1 -0
- package/dist/server/services/terminal/tmux-manager.d.ts +82 -0
- package/dist/server/services/terminal/tmux-manager.d.ts.map +1 -0
- package/dist/server/services/terminal/tmux-manager.js +352 -0
- package/dist/server/services/terminal/tmux-manager.js.map +1 -0
- package/dist/server/services/websocket/autocomplete.d.ts +50 -0
- package/dist/server/services/websocket/autocomplete.d.ts.map +1 -0
- package/dist/server/services/websocket/autocomplete.js +361 -0
- package/dist/server/services/websocket/autocomplete.js.map +1 -0
- package/dist/server/services/websocket/file-utils.d.ts +44 -0
- package/dist/server/services/websocket/file-utils.d.ts.map +1 -0
- package/dist/server/services/websocket/file-utils.js +272 -0
- package/dist/server/services/websocket/file-utils.js.map +1 -0
- package/dist/server/services/websocket/handler.d.ts +246 -0
- package/dist/server/services/websocket/handler.d.ts.map +1 -0
- package/dist/server/services/websocket/handler.js +1771 -0
- package/dist/server/services/websocket/handler.js.map +1 -0
- package/dist/server/services/websocket/index.d.ts +11 -0
- package/dist/server/services/websocket/index.d.ts.map +1 -0
- package/dist/server/services/websocket/index.js +14 -0
- package/dist/server/services/websocket/index.js.map +1 -0
- package/dist/server/services/websocket/types.d.ts +214 -0
- package/dist/server/services/websocket/types.d.ts.map +1 -0
- package/dist/server/services/websocket/types.js +4 -0
- package/dist/server/services/websocket/types.js.map +1 -0
- package/dist/server/utils/agent-manager.d.ts +69 -0
- package/dist/server/utils/agent-manager.d.ts.map +1 -0
- package/dist/server/utils/agent-manager.js +269 -0
- package/dist/server/utils/agent-manager.js.map +1 -0
- package/dist/server/utils/paths.d.ts +25 -0
- package/dist/server/utils/paths.d.ts.map +1 -0
- package/dist/server/utils/paths.js +38 -0
- package/dist/server/utils/paths.js.map +1 -0
- package/dist/server/utils/port-manager.d.ts +10 -0
- package/dist/server/utils/port-manager.d.ts.map +1 -0
- package/dist/server/utils/port-manager.js +60 -0
- package/dist/server/utils/port-manager.js.map +1 -0
- package/dist/server/utils/port.d.ts +26 -0
- package/dist/server/utils/port.d.ts.map +1 -0
- package/dist/server/utils/port.js +83 -0
- package/dist/server/utils/port.js.map +1 -0
- package/hooks/bouncer.sh +138 -0
- package/package.json +74 -0
- package/server/README.md +191 -0
- package/server/cli/headless/claude-invoker.ts +415 -0
- package/server/cli/headless/index.ts +39 -0
- package/server/cli/headless/mcp-config.ts +87 -0
- package/server/cli/headless/output-utils.ts +109 -0
- package/server/cli/headless/prompt-utils.ts +108 -0
- package/server/cli/headless/runner.ts +133 -0
- package/server/cli/headless/types.ts +118 -0
- package/server/cli/improvisation-session-manager.ts +531 -0
- package/server/index.ts +456 -0
- package/server/mcp/README.md +122 -0
- package/server/mcp/bouncer-cli.ts +127 -0
- package/server/mcp/bouncer-integration.ts +430 -0
- package/server/mcp/security-audit.ts +180 -0
- package/server/mcp/security-patterns.ts +290 -0
- package/server/mcp/server.ts +174 -0
- package/server/routes/files.ts +29 -0
- package/server/routes/improvise.ts +82 -0
- package/server/routes/index.ts +13 -0
- package/server/routes/instances.ts +54 -0
- package/server/routes/notifications.ts +158 -0
- package/server/services/analytics.ts +277 -0
- package/server/services/auth.ts +80 -0
- package/server/services/client-id.ts +68 -0
- package/server/services/credentials.ts +134 -0
- package/server/services/files.ts +710 -0
- package/server/services/instances.ts +275 -0
- package/server/services/pathUtils.ts +158 -0
- package/server/services/platform.test.ts +1314 -0
- package/server/services/platform.ts +435 -0
- package/server/services/sentry.ts +81 -0
- package/server/services/terminal/pty-manager.ts +464 -0
- package/server/services/terminal/tmux-manager.ts +426 -0
- package/server/services/websocket/autocomplete.ts +438 -0
- package/server/services/websocket/file-utils.ts +305 -0
- package/server/services/websocket/handler.test.ts +20 -0
- package/server/services/websocket/handler.ts +2047 -0
- package/server/services/websocket/index.ts +40 -0
- package/server/services/websocket/types.ts +339 -0
- package/server/tsconfig.json +19 -0
- package/server/utils/agent-manager.ts +323 -0
- package/server/utils/paths.ts +45 -0
- package/server/utils/port-manager.ts +70 -0
- package/server/utils/port.ts +102 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mstro logout command
|
|
3
|
+
*
|
|
4
|
+
* Signs out of the current mstro.app account and removes local credentials.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readFileSync, unlinkSync } from 'node:fs';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
|
|
11
|
+
const colors = {
|
|
12
|
+
reset: '\x1b[0m',
|
|
13
|
+
bold: '\x1b[1m',
|
|
14
|
+
green: '\x1b[32m',
|
|
15
|
+
yellow: '\x1b[33m',
|
|
16
|
+
red: '\x1b[31m',
|
|
17
|
+
dim: '\x1b[2m',
|
|
18
|
+
cyan: '\x1b[36m',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function log(msg, color = '') {
|
|
22
|
+
console.log(`${color}${msg}${colors.reset}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const MSTRO_DIR = join(homedir(), '.mstro');
|
|
26
|
+
const CREDENTIALS_FILE = join(MSTRO_DIR, 'credentials.json');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get stored credentials
|
|
30
|
+
*/
|
|
31
|
+
function getCredentials() {
|
|
32
|
+
if (!existsSync(CREDENTIALS_FILE)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(readFileSync(CREDENTIALS_FILE, 'utf-8'));
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Main logout command
|
|
44
|
+
*/
|
|
45
|
+
export async function logout() {
|
|
46
|
+
log('\n Mstro Logout\n', colors.bold + colors.cyan);
|
|
47
|
+
|
|
48
|
+
const creds = getCredentials();
|
|
49
|
+
|
|
50
|
+
if (!creds) {
|
|
51
|
+
log(' Not currently logged in.', colors.yellow);
|
|
52
|
+
log(' Use "mstro login" to sign in.\n', colors.dim);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const email = creds.email;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
// Delete credentials file
|
|
60
|
+
if (existsSync(CREDENTIALS_FILE)) {
|
|
61
|
+
unlinkSync(CREDENTIALS_FILE);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
log(` Logged out from ${email}`, colors.green);
|
|
65
|
+
log('');
|
|
66
|
+
log(' Your device credentials have been removed.', colors.dim);
|
|
67
|
+
log(' Use "mstro login" to sign in again.\n', colors.dim);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
log(` Failed to logout: ${err.message}`, colors.red);
|
|
70
|
+
log('');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default logout;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mstro status command
|
|
3
|
+
*
|
|
4
|
+
* Shows the status of the mstro CLI including:
|
|
5
|
+
* - Login status
|
|
6
|
+
* - Platform connection status
|
|
7
|
+
* - Device information
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
11
|
+
import { arch, homedir, hostname, type } from 'node:os';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
|
|
14
|
+
const colors = {
|
|
15
|
+
reset: '\x1b[0m',
|
|
16
|
+
bold: '\x1b[1m',
|
|
17
|
+
green: '\x1b[32m',
|
|
18
|
+
yellow: '\x1b[33m',
|
|
19
|
+
red: '\x1b[31m',
|
|
20
|
+
dim: '\x1b[2m',
|
|
21
|
+
cyan: '\x1b[36m',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function log(msg, color = '') {
|
|
25
|
+
console.log(`${color}${msg}${colors.reset}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const MSTRO_DIR = join(homedir(), '.mstro');
|
|
29
|
+
const CREDENTIALS_FILE = join(MSTRO_DIR, 'credentials.json');
|
|
30
|
+
const CLIENT_ID_FILE = join(MSTRO_DIR, 'client-id');
|
|
31
|
+
const PLATFORM_URL = process.env.PLATFORM_URL || 'https://api.mstro.app';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get stored credentials
|
|
35
|
+
*/
|
|
36
|
+
function getCredentials() {
|
|
37
|
+
if (!existsSync(CREDENTIALS_FILE)) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(readFileSync(CREDENTIALS_FILE, 'utf-8'));
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get client ID
|
|
49
|
+
*/
|
|
50
|
+
function getClientId() {
|
|
51
|
+
if (!existsSync(CLIENT_ID_FILE)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return readFileSync(CLIENT_ID_FILE, 'utf-8').trim();
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check platform health
|
|
63
|
+
*/
|
|
64
|
+
async function checkPlatform() {
|
|
65
|
+
try {
|
|
66
|
+
const controller = new AbortController();
|
|
67
|
+
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
68
|
+
|
|
69
|
+
const response = await fetch(`${PLATFORM_URL}/health`, {
|
|
70
|
+
signal: controller.signal,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
clearTimeout(timeoutId);
|
|
74
|
+
|
|
75
|
+
if (response.ok) {
|
|
76
|
+
const data = await response.json();
|
|
77
|
+
return { online: true, data };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { online: false, error: `HTTP ${response.status}` };
|
|
81
|
+
} catch (err) {
|
|
82
|
+
return { online: false, error: err.name === 'AbortError' ? 'Timeout' : err.message };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Verify token with platform
|
|
88
|
+
*/
|
|
89
|
+
async function verifyToken(token) {
|
|
90
|
+
try {
|
|
91
|
+
const controller = new AbortController();
|
|
92
|
+
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
93
|
+
|
|
94
|
+
const response = await fetch(`${PLATFORM_URL}/api/auth/device/verify`, {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
headers: {
|
|
97
|
+
'Authorization': `Bearer ${token}`,
|
|
98
|
+
'Content-Type': 'application/json',
|
|
99
|
+
},
|
|
100
|
+
signal: controller.signal,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
clearTimeout(timeoutId);
|
|
104
|
+
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
const data = await response.json();
|
|
107
|
+
return { valid: false, error: data.error };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return await response.json();
|
|
111
|
+
} catch (err) {
|
|
112
|
+
return { valid: false, error: err.name === 'AbortError' ? 'Timeout' : err.message };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Main status command
|
|
118
|
+
*/
|
|
119
|
+
export async function status() {
|
|
120
|
+
log('\n Mstro Status\n', colors.bold + colors.cyan);
|
|
121
|
+
|
|
122
|
+
const creds = getCredentials();
|
|
123
|
+
const clientId = getClientId();
|
|
124
|
+
|
|
125
|
+
// Device info
|
|
126
|
+
log(' Device', colors.bold);
|
|
127
|
+
log(` Hostname: ${hostname()}`, colors.dim);
|
|
128
|
+
log(` OS: ${type()} (${arch()})`, colors.dim);
|
|
129
|
+
log(` Node: ${process.version}`, colors.dim);
|
|
130
|
+
log(` Client ID: ${clientId ? `${clientId.slice(0, 8)}...` : 'Not set'}`, colors.dim);
|
|
131
|
+
log('');
|
|
132
|
+
|
|
133
|
+
// Platform status
|
|
134
|
+
log(' Platform', colors.bold);
|
|
135
|
+
log(` URL: ${PLATFORM_URL}`, colors.dim);
|
|
136
|
+
|
|
137
|
+
process.stdout.write(' Status: Checking...');
|
|
138
|
+
|
|
139
|
+
const platformStatus = await checkPlatform();
|
|
140
|
+
|
|
141
|
+
process.stdout.write('\r \r'); // Clear line
|
|
142
|
+
|
|
143
|
+
if (platformStatus.online) {
|
|
144
|
+
log(` Status: ${colors.green}Online${colors.reset}`, colors.dim);
|
|
145
|
+
if (platformStatus.data?.relay) {
|
|
146
|
+
const relay = platformStatus.data.relay;
|
|
147
|
+
log(` Orchestras: ${relay.pairedClients || 0} connected`, colors.dim);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
log(` Status: ${colors.red}Offline${colors.reset} (${platformStatus.error})`, colors.dim);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
log('');
|
|
154
|
+
|
|
155
|
+
// Auth status
|
|
156
|
+
log(' Authentication', colors.bold);
|
|
157
|
+
|
|
158
|
+
if (!creds) {
|
|
159
|
+
log(` Status: ${colors.yellow}Not logged in${colors.reset}`, colors.dim);
|
|
160
|
+
log('');
|
|
161
|
+
log(' Run "mstro login" to authenticate this device.', colors.dim);
|
|
162
|
+
log('');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
log(` Email: ${creds.email}`, colors.dim);
|
|
167
|
+
|
|
168
|
+
if (platformStatus.online) {
|
|
169
|
+
process.stdout.write(' Session: Verifying...');
|
|
170
|
+
|
|
171
|
+
const tokenStatus = await verifyToken(creds.token);
|
|
172
|
+
|
|
173
|
+
process.stdout.write('\r \r');
|
|
174
|
+
|
|
175
|
+
if (tokenStatus.valid) {
|
|
176
|
+
log(` Session: ${colors.green}Valid${colors.reset}`, colors.dim);
|
|
177
|
+
} else {
|
|
178
|
+
log(` Session: ${colors.red}Invalid${colors.reset} (${tokenStatus.error})`, colors.dim);
|
|
179
|
+
log('');
|
|
180
|
+
log(' Run "mstro login" to re-authenticate.', colors.dim);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
log(` Session: ${colors.yellow}Cannot verify (platform offline)${colors.reset}`, colors.dim);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
log('');
|
|
187
|
+
|
|
188
|
+
// Quick commands
|
|
189
|
+
log(' Commands', colors.bold);
|
|
190
|
+
log(' mstro Start an orchestra', colors.dim);
|
|
191
|
+
log(' mstro login Sign in to your account', colors.dim);
|
|
192
|
+
log(' mstro logout Sign out', colors.dim);
|
|
193
|
+
log(' mstro whoami Show account details', colors.dim);
|
|
194
|
+
log('');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export default status;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mstro whoami command
|
|
3
|
+
*
|
|
4
|
+
* Shows the currently logged in user and device information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { arch, homedir, hostname, type } from 'node:os';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
|
|
11
|
+
const colors = {
|
|
12
|
+
reset: '\x1b[0m',
|
|
13
|
+
bold: '\x1b[1m',
|
|
14
|
+
green: '\x1b[32m',
|
|
15
|
+
yellow: '\x1b[33m',
|
|
16
|
+
red: '\x1b[31m',
|
|
17
|
+
dim: '\x1b[2m',
|
|
18
|
+
cyan: '\x1b[36m',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function log(msg, color = '') {
|
|
22
|
+
console.log(`${color}${msg}${colors.reset}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const MSTRO_DIR = join(homedir(), '.mstro');
|
|
26
|
+
const CREDENTIALS_FILE = join(MSTRO_DIR, 'credentials.json');
|
|
27
|
+
const CLIENT_ID_FILE = join(MSTRO_DIR, 'client-id');
|
|
28
|
+
const PLATFORM_URL = process.env.PLATFORM_URL || 'https://api.mstro.app';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get stored credentials
|
|
32
|
+
*/
|
|
33
|
+
function getCredentials() {
|
|
34
|
+
if (!existsSync(CREDENTIALS_FILE)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(readFileSync(CREDENTIALS_FILE, 'utf-8'));
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get client ID
|
|
46
|
+
*/
|
|
47
|
+
function getClientId() {
|
|
48
|
+
if (!existsSync(CLIENT_ID_FILE)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
return readFileSync(CLIENT_ID_FILE, 'utf-8').trim();
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Verify token with platform and get fresh user info
|
|
60
|
+
*/
|
|
61
|
+
async function verifyToken(token) {
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetch(`${PLATFORM_URL}/api/auth/device/verify`, {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: {
|
|
66
|
+
'Authorization': `Bearer ${token}`,
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
const data = await response.json();
|
|
73
|
+
return { valid: false, error: data.error };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return await response.json();
|
|
77
|
+
} catch (_err) {
|
|
78
|
+
return { valid: false, error: 'Network error' };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Format date nicely
|
|
84
|
+
*/
|
|
85
|
+
function formatDate(dateStr) {
|
|
86
|
+
if (!dateStr) return 'Unknown';
|
|
87
|
+
const date = new Date(dateStr);
|
|
88
|
+
return date.toLocaleDateString('en-US', {
|
|
89
|
+
year: 'numeric',
|
|
90
|
+
month: 'short',
|
|
91
|
+
day: 'numeric',
|
|
92
|
+
hour: '2-digit',
|
|
93
|
+
minute: '2-digit',
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Main whoami command
|
|
99
|
+
*/
|
|
100
|
+
export async function whoami(args = []) {
|
|
101
|
+
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
102
|
+
|
|
103
|
+
log('\n Mstro Account\n', colors.bold + colors.cyan);
|
|
104
|
+
|
|
105
|
+
const creds = getCredentials();
|
|
106
|
+
const clientId = getClientId();
|
|
107
|
+
|
|
108
|
+
if (!creds) {
|
|
109
|
+
log(' Not logged in.', colors.yellow);
|
|
110
|
+
log(' Use "mstro login" to sign in.\n', colors.dim);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Verify with platform (unless --offline flag)
|
|
115
|
+
const offline = args.includes('--offline');
|
|
116
|
+
let verified = null;
|
|
117
|
+
|
|
118
|
+
if (!offline) {
|
|
119
|
+
process.stdout.write(' Verifying with server...');
|
|
120
|
+
verified = await verifyToken(creds.token);
|
|
121
|
+
|
|
122
|
+
if (!verified.valid) {
|
|
123
|
+
console.log('');
|
|
124
|
+
log('');
|
|
125
|
+
log(` Session invalid: ${verified.error}`, colors.red);
|
|
126
|
+
log(' Use "mstro login" to re-authenticate.\n', colors.dim);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
process.stdout.write('\r \r'); // Clear the line
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Display user info
|
|
134
|
+
const user = verified?.user || creds;
|
|
135
|
+
|
|
136
|
+
log(` Email: ${user.email}`, colors.bold);
|
|
137
|
+
if (user.name) {
|
|
138
|
+
log(` Name: ${user.name}`, colors.dim);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
log('');
|
|
142
|
+
log(' Device Information', colors.bold);
|
|
143
|
+
log(` Hostname: ${hostname()}`, colors.dim);
|
|
144
|
+
log(` OS: ${type()} (${arch()})`, colors.dim);
|
|
145
|
+
log(` Node: ${process.version}`, colors.dim);
|
|
146
|
+
|
|
147
|
+
if (verbose) {
|
|
148
|
+
log('');
|
|
149
|
+
log(' Credentials', colors.bold);
|
|
150
|
+
log(` Client ID: ${clientId || 'Not set'}`, colors.dim);
|
|
151
|
+
log(` Logged in: ${formatDate(creds.createdAt)}`, colors.dim);
|
|
152
|
+
if (creds.lastRefreshedAt) {
|
|
153
|
+
log(` Refreshed: ${formatDate(creds.lastRefreshedAt)}`, colors.dim);
|
|
154
|
+
}
|
|
155
|
+
log(` Creds file: ${CREDENTIALS_FILE}`, colors.dim);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
log('');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export default whoami;
|