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.
Files changed (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +177 -0
  3. package/bin/commands/config.js +145 -0
  4. package/bin/commands/login.js +313 -0
  5. package/bin/commands/logout.js +75 -0
  6. package/bin/commands/status.js +197 -0
  7. package/bin/commands/whoami.js +161 -0
  8. package/bin/configure-claude.js +298 -0
  9. package/bin/mstro.js +581 -0
  10. package/bin/postinstall.js +45 -0
  11. package/bin/release.sh +110 -0
  12. package/dist/server/cli/headless/claude-invoker.d.ts +17 -0
  13. package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -0
  14. package/dist/server/cli/headless/claude-invoker.js +311 -0
  15. package/dist/server/cli/headless/claude-invoker.js.map +1 -0
  16. package/dist/server/cli/headless/index.d.ts +13 -0
  17. package/dist/server/cli/headless/index.d.ts.map +1 -0
  18. package/dist/server/cli/headless/index.js +10 -0
  19. package/dist/server/cli/headless/index.js.map +1 -0
  20. package/dist/server/cli/headless/mcp-config.d.ts +11 -0
  21. package/dist/server/cli/headless/mcp-config.d.ts.map +1 -0
  22. package/dist/server/cli/headless/mcp-config.js +76 -0
  23. package/dist/server/cli/headless/mcp-config.js.map +1 -0
  24. package/dist/server/cli/headless/output-utils.d.ts +33 -0
  25. package/dist/server/cli/headless/output-utils.d.ts.map +1 -0
  26. package/dist/server/cli/headless/output-utils.js +101 -0
  27. package/dist/server/cli/headless/output-utils.js.map +1 -0
  28. package/dist/server/cli/headless/prompt-utils.d.ts +21 -0
  29. package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -0
  30. package/dist/server/cli/headless/prompt-utils.js +84 -0
  31. package/dist/server/cli/headless/prompt-utils.js.map +1 -0
  32. package/dist/server/cli/headless/runner.d.ts +24 -0
  33. package/dist/server/cli/headless/runner.d.ts.map +1 -0
  34. package/dist/server/cli/headless/runner.js +99 -0
  35. package/dist/server/cli/headless/runner.js.map +1 -0
  36. package/dist/server/cli/headless/types.d.ts +106 -0
  37. package/dist/server/cli/headless/types.d.ts.map +1 -0
  38. package/dist/server/cli/headless/types.js +4 -0
  39. package/dist/server/cli/headless/types.js.map +1 -0
  40. package/dist/server/cli/improvisation-session-manager.d.ts +155 -0
  41. package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -0
  42. package/dist/server/cli/improvisation-session-manager.js +415 -0
  43. package/dist/server/cli/improvisation-session-manager.js.map +1 -0
  44. package/dist/server/index.d.ts +2 -0
  45. package/dist/server/index.d.ts.map +1 -0
  46. package/dist/server/index.js +386 -0
  47. package/dist/server/index.js.map +1 -0
  48. package/dist/server/mcp/bouncer-cli.d.ts +3 -0
  49. package/dist/server/mcp/bouncer-cli.d.ts.map +1 -0
  50. package/dist/server/mcp/bouncer-cli.js +99 -0
  51. package/dist/server/mcp/bouncer-cli.js.map +1 -0
  52. package/dist/server/mcp/bouncer-integration.d.ts +36 -0
  53. package/dist/server/mcp/bouncer-integration.d.ts.map +1 -0
  54. package/dist/server/mcp/bouncer-integration.js +301 -0
  55. package/dist/server/mcp/bouncer-integration.js.map +1 -0
  56. package/dist/server/mcp/security-audit.d.ts +52 -0
  57. package/dist/server/mcp/security-audit.d.ts.map +1 -0
  58. package/dist/server/mcp/security-audit.js +118 -0
  59. package/dist/server/mcp/security-audit.js.map +1 -0
  60. package/dist/server/mcp/security-patterns.d.ts +73 -0
  61. package/dist/server/mcp/security-patterns.d.ts.map +1 -0
  62. package/dist/server/mcp/security-patterns.js +247 -0
  63. package/dist/server/mcp/security-patterns.js.map +1 -0
  64. package/dist/server/mcp/server.d.ts +3 -0
  65. package/dist/server/mcp/server.d.ts.map +1 -0
  66. package/dist/server/mcp/server.js +146 -0
  67. package/dist/server/mcp/server.js.map +1 -0
  68. package/dist/server/routes/files.d.ts +9 -0
  69. package/dist/server/routes/files.d.ts.map +1 -0
  70. package/dist/server/routes/files.js +24 -0
  71. package/dist/server/routes/files.js.map +1 -0
  72. package/dist/server/routes/improvise.d.ts +3 -0
  73. package/dist/server/routes/improvise.d.ts.map +1 -0
  74. package/dist/server/routes/improvise.js +72 -0
  75. package/dist/server/routes/improvise.js.map +1 -0
  76. package/dist/server/routes/index.d.ts +10 -0
  77. package/dist/server/routes/index.d.ts.map +1 -0
  78. package/dist/server/routes/index.js +12 -0
  79. package/dist/server/routes/index.js.map +1 -0
  80. package/dist/server/routes/instances.d.ts +10 -0
  81. package/dist/server/routes/instances.d.ts.map +1 -0
  82. package/dist/server/routes/instances.js +47 -0
  83. package/dist/server/routes/instances.js.map +1 -0
  84. package/dist/server/routes/notifications.d.ts +3 -0
  85. package/dist/server/routes/notifications.d.ts.map +1 -0
  86. package/dist/server/routes/notifications.js +136 -0
  87. package/dist/server/routes/notifications.js.map +1 -0
  88. package/dist/server/services/analytics.d.ts +56 -0
  89. package/dist/server/services/analytics.d.ts.map +1 -0
  90. package/dist/server/services/analytics.js +240 -0
  91. package/dist/server/services/analytics.js.map +1 -0
  92. package/dist/server/services/auth.d.ts +26 -0
  93. package/dist/server/services/auth.d.ts.map +1 -0
  94. package/dist/server/services/auth.js +71 -0
  95. package/dist/server/services/auth.js.map +1 -0
  96. package/dist/server/services/client-id.d.ts +10 -0
  97. package/dist/server/services/client-id.d.ts.map +1 -0
  98. package/dist/server/services/client-id.js +61 -0
  99. package/dist/server/services/client-id.js.map +1 -0
  100. package/dist/server/services/credentials.d.ts +39 -0
  101. package/dist/server/services/credentials.d.ts.map +1 -0
  102. package/dist/server/services/credentials.js +110 -0
  103. package/dist/server/services/credentials.js.map +1 -0
  104. package/dist/server/services/files.d.ts +119 -0
  105. package/dist/server/services/files.d.ts.map +1 -0
  106. package/dist/server/services/files.js +560 -0
  107. package/dist/server/services/files.js.map +1 -0
  108. package/dist/server/services/instances.d.ts +52 -0
  109. package/dist/server/services/instances.d.ts.map +1 -0
  110. package/dist/server/services/instances.js +241 -0
  111. package/dist/server/services/instances.js.map +1 -0
  112. package/dist/server/services/pathUtils.d.ts +47 -0
  113. package/dist/server/services/pathUtils.d.ts.map +1 -0
  114. package/dist/server/services/pathUtils.js +124 -0
  115. package/dist/server/services/pathUtils.js.map +1 -0
  116. package/dist/server/services/platform.d.ts +72 -0
  117. package/dist/server/services/platform.d.ts.map +1 -0
  118. package/dist/server/services/platform.js +368 -0
  119. package/dist/server/services/platform.js.map +1 -0
  120. package/dist/server/services/sentry.d.ts +5 -0
  121. package/dist/server/services/sentry.d.ts.map +1 -0
  122. package/dist/server/services/sentry.js +71 -0
  123. package/dist/server/services/sentry.js.map +1 -0
  124. package/dist/server/services/terminal/pty-manager.d.ts +149 -0
  125. package/dist/server/services/terminal/pty-manager.d.ts.map +1 -0
  126. package/dist/server/services/terminal/pty-manager.js +377 -0
  127. package/dist/server/services/terminal/pty-manager.js.map +1 -0
  128. package/dist/server/services/terminal/tmux-manager.d.ts +82 -0
  129. package/dist/server/services/terminal/tmux-manager.d.ts.map +1 -0
  130. package/dist/server/services/terminal/tmux-manager.js +352 -0
  131. package/dist/server/services/terminal/tmux-manager.js.map +1 -0
  132. package/dist/server/services/websocket/autocomplete.d.ts +50 -0
  133. package/dist/server/services/websocket/autocomplete.d.ts.map +1 -0
  134. package/dist/server/services/websocket/autocomplete.js +361 -0
  135. package/dist/server/services/websocket/autocomplete.js.map +1 -0
  136. package/dist/server/services/websocket/file-utils.d.ts +44 -0
  137. package/dist/server/services/websocket/file-utils.d.ts.map +1 -0
  138. package/dist/server/services/websocket/file-utils.js +272 -0
  139. package/dist/server/services/websocket/file-utils.js.map +1 -0
  140. package/dist/server/services/websocket/handler.d.ts +246 -0
  141. package/dist/server/services/websocket/handler.d.ts.map +1 -0
  142. package/dist/server/services/websocket/handler.js +1771 -0
  143. package/dist/server/services/websocket/handler.js.map +1 -0
  144. package/dist/server/services/websocket/index.d.ts +11 -0
  145. package/dist/server/services/websocket/index.d.ts.map +1 -0
  146. package/dist/server/services/websocket/index.js +14 -0
  147. package/dist/server/services/websocket/index.js.map +1 -0
  148. package/dist/server/services/websocket/types.d.ts +214 -0
  149. package/dist/server/services/websocket/types.d.ts.map +1 -0
  150. package/dist/server/services/websocket/types.js +4 -0
  151. package/dist/server/services/websocket/types.js.map +1 -0
  152. package/dist/server/utils/agent-manager.d.ts +69 -0
  153. package/dist/server/utils/agent-manager.d.ts.map +1 -0
  154. package/dist/server/utils/agent-manager.js +269 -0
  155. package/dist/server/utils/agent-manager.js.map +1 -0
  156. package/dist/server/utils/paths.d.ts +25 -0
  157. package/dist/server/utils/paths.d.ts.map +1 -0
  158. package/dist/server/utils/paths.js +38 -0
  159. package/dist/server/utils/paths.js.map +1 -0
  160. package/dist/server/utils/port-manager.d.ts +10 -0
  161. package/dist/server/utils/port-manager.d.ts.map +1 -0
  162. package/dist/server/utils/port-manager.js +60 -0
  163. package/dist/server/utils/port-manager.js.map +1 -0
  164. package/dist/server/utils/port.d.ts +26 -0
  165. package/dist/server/utils/port.d.ts.map +1 -0
  166. package/dist/server/utils/port.js +83 -0
  167. package/dist/server/utils/port.js.map +1 -0
  168. package/hooks/bouncer.sh +138 -0
  169. package/package.json +74 -0
  170. package/server/README.md +191 -0
  171. package/server/cli/headless/claude-invoker.ts +415 -0
  172. package/server/cli/headless/index.ts +39 -0
  173. package/server/cli/headless/mcp-config.ts +87 -0
  174. package/server/cli/headless/output-utils.ts +109 -0
  175. package/server/cli/headless/prompt-utils.ts +108 -0
  176. package/server/cli/headless/runner.ts +133 -0
  177. package/server/cli/headless/types.ts +118 -0
  178. package/server/cli/improvisation-session-manager.ts +531 -0
  179. package/server/index.ts +456 -0
  180. package/server/mcp/README.md +122 -0
  181. package/server/mcp/bouncer-cli.ts +127 -0
  182. package/server/mcp/bouncer-integration.ts +430 -0
  183. package/server/mcp/security-audit.ts +180 -0
  184. package/server/mcp/security-patterns.ts +290 -0
  185. package/server/mcp/server.ts +174 -0
  186. package/server/routes/files.ts +29 -0
  187. package/server/routes/improvise.ts +82 -0
  188. package/server/routes/index.ts +13 -0
  189. package/server/routes/instances.ts +54 -0
  190. package/server/routes/notifications.ts +158 -0
  191. package/server/services/analytics.ts +277 -0
  192. package/server/services/auth.ts +80 -0
  193. package/server/services/client-id.ts +68 -0
  194. package/server/services/credentials.ts +134 -0
  195. package/server/services/files.ts +710 -0
  196. package/server/services/instances.ts +275 -0
  197. package/server/services/pathUtils.ts +158 -0
  198. package/server/services/platform.test.ts +1314 -0
  199. package/server/services/platform.ts +435 -0
  200. package/server/services/sentry.ts +81 -0
  201. package/server/services/terminal/pty-manager.ts +464 -0
  202. package/server/services/terminal/tmux-manager.ts +426 -0
  203. package/server/services/websocket/autocomplete.ts +438 -0
  204. package/server/services/websocket/file-utils.ts +305 -0
  205. package/server/services/websocket/handler.test.ts +20 -0
  206. package/server/services/websocket/handler.ts +2047 -0
  207. package/server/services/websocket/index.ts +40 -0
  208. package/server/services/websocket/types.ts +339 -0
  209. package/server/tsconfig.json +19 -0
  210. package/server/utils/agent-manager.ts +323 -0
  211. package/server/utils/paths.ts +45 -0
  212. package/server/utils/port-manager.ts +70 -0
  213. 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;