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 @@
|
|
|
1
|
+
{"version":3,"file":"instances.js","sourceRoot":"","sources":["../../../server/routes/instances.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,gEAAgE;AAEhE;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAE3D,MAAM,UAAU,oBAAoB,CAAC,gBAAkC;IACrE,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAA;IAEzB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE;QACpB,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,gBAAgB,CAAC,eAAe,EAAE,CAAA;YACpD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,KAAe,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAA;QACzD,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE;QAC3B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,gBAAgB,CAAC,kBAAkB,EAAE,CAAA;YACtD,OAAO,CAAC,CAAC,IAAI,CAAC;gBACZ,QAAQ;gBACR,QAAQ,EAAE,WAAW,EAAE;aACxB,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,KAAe,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAA;QACzD,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,gBAAkC;IACpE,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAA;IAEzB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE;QACrB,UAAU,CAAC,GAAG,EAAE;YACd,gBAAgB,CAAC,UAAU,EAAE,CAAA;YAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC,EAAE,GAAG,CAAC,CAAA;QAEP,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAA;IAC/D,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../../server/routes/notifications.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,8EA4B1D"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* Notification Routes
|
|
5
|
+
*
|
|
6
|
+
* Handles notification-related operations including AI-powered summary generation.
|
|
7
|
+
*/
|
|
8
|
+
import { spawn } from 'node:child_process';
|
|
9
|
+
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { Hono } from 'hono';
|
|
12
|
+
export function createNotificationRoutes(workingDir) {
|
|
13
|
+
const routes = new Hono();
|
|
14
|
+
/**
|
|
15
|
+
* Generate a summary for browser notification using Claude Haiku
|
|
16
|
+
* POST /summarize
|
|
17
|
+
* Body: { prompt: string, output: string }
|
|
18
|
+
* Returns: { summary: string }
|
|
19
|
+
*/
|
|
20
|
+
routes.post('/summarize', async (c) => {
|
|
21
|
+
try {
|
|
22
|
+
const body = await c.req.json();
|
|
23
|
+
const { prompt, output } = body;
|
|
24
|
+
if (!prompt || !output) {
|
|
25
|
+
return c.json({ error: 'Missing required fields: prompt and output' }, 400);
|
|
26
|
+
}
|
|
27
|
+
const summary = await generateNotificationSummary(prompt, output, workingDir);
|
|
28
|
+
return c.json({ summary });
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error('[Notifications] Error generating summary:', error);
|
|
32
|
+
// Return a fallback summary on error
|
|
33
|
+
return c.json({ summary: 'Task completed' });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return routes;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Generate a notification summary using Claude Haiku
|
|
40
|
+
*/
|
|
41
|
+
async function generateNotificationSummary(userPrompt, output, workingDir) {
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
// Create temp directory if it doesn't exist
|
|
44
|
+
const tempDir = join(workingDir, '.mstro', 'tmp');
|
|
45
|
+
if (!existsSync(tempDir)) {
|
|
46
|
+
mkdirSync(tempDir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
// Truncate output if too long (keep first and last parts for context)
|
|
49
|
+
let truncatedOutput = output;
|
|
50
|
+
if (output.length > 4000) {
|
|
51
|
+
const firstPart = output.slice(0, 2000);
|
|
52
|
+
const lastPart = output.slice(-1500);
|
|
53
|
+
truncatedOutput = `${firstPart}\n\n... [output truncated] ...\n\n${lastPart}`;
|
|
54
|
+
}
|
|
55
|
+
// Build the prompt for summary generation
|
|
56
|
+
const summaryPrompt = `You are generating a SHORT browser notification summary for a completed task.
|
|
57
|
+
The user ran a task and wants a brief notification to remind them what happened.
|
|
58
|
+
|
|
59
|
+
USER'S ORIGINAL PROMPT:
|
|
60
|
+
"${userPrompt}"
|
|
61
|
+
|
|
62
|
+
TASK OUTPUT (may be truncated):
|
|
63
|
+
${truncatedOutput}
|
|
64
|
+
|
|
65
|
+
Generate a notification summary following these rules:
|
|
66
|
+
1. Maximum 100 characters (this is a browser notification)
|
|
67
|
+
2. Focus on the OUTCOME, not the process
|
|
68
|
+
3. Be specific about what was accomplished
|
|
69
|
+
4. Use past tense (e.g., "Fixed bug in auth.ts", "Added 3 new tests")
|
|
70
|
+
5. If there was an error, mention it briefly
|
|
71
|
+
6. No emojis, no markdown, just plain text
|
|
72
|
+
|
|
73
|
+
Respond with ONLY the summary text, nothing else.`;
|
|
74
|
+
// Write prompt to temp file
|
|
75
|
+
const promptFile = join(tempDir, `notif-summary-${Date.now()}.txt`);
|
|
76
|
+
writeFileSync(promptFile, summaryPrompt);
|
|
77
|
+
const systemPrompt = 'You are a notification summary assistant. Respond with only the summary text, no preamble or explanation.';
|
|
78
|
+
const args = [
|
|
79
|
+
'--print',
|
|
80
|
+
'--model', 'haiku',
|
|
81
|
+
'--system-prompt', systemPrompt,
|
|
82
|
+
promptFile
|
|
83
|
+
];
|
|
84
|
+
const claude = spawn('claude', args, {
|
|
85
|
+
cwd: workingDir,
|
|
86
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
87
|
+
});
|
|
88
|
+
let stdout = '';
|
|
89
|
+
let stderr = '';
|
|
90
|
+
claude.stdout?.on('data', (data) => {
|
|
91
|
+
stdout += data.toString();
|
|
92
|
+
});
|
|
93
|
+
claude.stderr?.on('data', (data) => {
|
|
94
|
+
stderr += data.toString();
|
|
95
|
+
});
|
|
96
|
+
claude.on('close', (code) => {
|
|
97
|
+
// Clean up temp file
|
|
98
|
+
try {
|
|
99
|
+
unlinkSync(promptFile);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Ignore cleanup errors
|
|
103
|
+
}
|
|
104
|
+
if (code === 0 && stdout.trim()) {
|
|
105
|
+
// Truncate if somehow still too long
|
|
106
|
+
const summary = stdout.trim().slice(0, 150);
|
|
107
|
+
resolve(summary);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.error('[Notifications] Claude error:', stderr || 'Unknown error');
|
|
111
|
+
// Fallback to basic summary
|
|
112
|
+
resolve(createFallbackSummary(userPrompt));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
claude.on('error', (err) => {
|
|
116
|
+
console.error('[Notifications] Failed to spawn Claude:', err);
|
|
117
|
+
resolve(createFallbackSummary(userPrompt));
|
|
118
|
+
});
|
|
119
|
+
// Timeout after 10 seconds
|
|
120
|
+
setTimeout(() => {
|
|
121
|
+
claude.kill();
|
|
122
|
+
resolve(createFallbackSummary(userPrompt));
|
|
123
|
+
}, 10000);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Create a fallback summary when AI summarization fails
|
|
128
|
+
*/
|
|
129
|
+
function createFallbackSummary(userPrompt) {
|
|
130
|
+
const truncated = userPrompt.slice(0, 60);
|
|
131
|
+
if (userPrompt.length > 60) {
|
|
132
|
+
return `Completed: "${truncated}..."`;
|
|
133
|
+
}
|
|
134
|
+
return `Completed: "${truncated}"`;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=notifications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../../server/routes/notifications.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialize PostHog client
|
|
3
|
+
* Call this once at server startup
|
|
4
|
+
*/
|
|
5
|
+
export declare function initAnalytics(): Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Shutdown PostHog client gracefully
|
|
8
|
+
* Call this before process exit
|
|
9
|
+
*/
|
|
10
|
+
export declare function shutdownAnalytics(): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Track a custom event
|
|
13
|
+
*/
|
|
14
|
+
export declare function trackEvent(event: string, properties?: Record<string, any>): void;
|
|
15
|
+
/**
|
|
16
|
+
* Identify a user (call after login)
|
|
17
|
+
*/
|
|
18
|
+
export declare function identifyUser(userId: string, properties?: Record<string, any>): void;
|
|
19
|
+
/**
|
|
20
|
+
* Set telemetry preference in config file
|
|
21
|
+
*/
|
|
22
|
+
export declare function setTelemetryEnabled(enabled: boolean): void;
|
|
23
|
+
/**
|
|
24
|
+
* Get current telemetry status
|
|
25
|
+
*/
|
|
26
|
+
export declare function getTelemetryStatus(): {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
reason: string;
|
|
29
|
+
};
|
|
30
|
+
export declare const AnalyticsEvents: {
|
|
31
|
+
readonly CLI_STARTED: "cli_started";
|
|
32
|
+
readonly CLI_COMMAND: "cli_command";
|
|
33
|
+
readonly CLI_LOGIN: "cli_login";
|
|
34
|
+
readonly CLI_LOGOUT: "cli_logout";
|
|
35
|
+
readonly CLI_ERROR: "cli_error";
|
|
36
|
+
readonly SERVER_STARTED: "server_started";
|
|
37
|
+
readonly SERVER_STOPPED: "server_stopped";
|
|
38
|
+
readonly PLATFORM_CONNECTED: "platform_connected";
|
|
39
|
+
readonly PLATFORM_DISCONNECTED: "platform_disconnected";
|
|
40
|
+
readonly WEB_CLIENT_CONNECTED: "web_client_connected";
|
|
41
|
+
readonly WEB_CLIENT_DISCONNECTED: "web_client_disconnected";
|
|
42
|
+
readonly IMPROVISE_SESSION_STARTED: "improvise_session_started";
|
|
43
|
+
readonly IMPROVISE_PROMPT_RECEIVED: "improvise_prompt_received";
|
|
44
|
+
readonly IMPROVISE_MOVEMENT_STARTED: "improvise_movement_started";
|
|
45
|
+
readonly IMPROVISE_MOVEMENT_COMPLETED: "improvise_movement_completed";
|
|
46
|
+
readonly IMPROVISE_MOVEMENT_ERROR: "improvise_movement_error";
|
|
47
|
+
readonly IMPROVISE_SESSION_ENDED: "improvise_session_ended";
|
|
48
|
+
readonly IMPROVISE_ABORTED: "improvise_aborted";
|
|
49
|
+
readonly TERMINAL_SESSION_CREATED: "terminal_session_created";
|
|
50
|
+
readonly TERMINAL_SESSION_CLOSED: "terminal_session_closed";
|
|
51
|
+
readonly BOUNCER_TOOL_ALLOWED: "bouncer_tool_allowed";
|
|
52
|
+
readonly BOUNCER_TOOL_DENIED: "bouncer_tool_denied";
|
|
53
|
+
readonly BOUNCER_HAIKU_REVIEW: "bouncer_haiku_review";
|
|
54
|
+
};
|
|
55
|
+
export type AnalyticsEvent = typeof AnalyticsEvents[keyof typeof AnalyticsEvents];
|
|
56
|
+
//# sourceMappingURL=analytics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../../server/services/analytics.ts"],"names":[],"mappings":"AA6FA;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAmBnD;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKvD;AAuBD;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAWhF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAgBnF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgB1D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAsBzE;AAMD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;CAmClB,CAAA;AAEV,MAAM,MAAM,cAAc,GAAG,OAAO,eAAe,CAAC,MAAM,OAAO,eAAe,CAAC,CAAA"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* PostHog Analytics Service for mstro CLI
|
|
5
|
+
*
|
|
6
|
+
* Provides analytics tracking for the mstro client.
|
|
7
|
+
* Uses PostHog Node SDK for server-side event tracking.
|
|
8
|
+
*
|
|
9
|
+
* Config is fetched from platform server (not hardcoded) so the
|
|
10
|
+
* PostHog key isn't exposed in the npm package.
|
|
11
|
+
*
|
|
12
|
+
* Telemetry is opt-out by default. Users can disable with:
|
|
13
|
+
* - Command: mstro telemetry off
|
|
14
|
+
* - Environment variable: MSTRO_TELEMETRY=0
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { arch, homedir, platform } from 'node:os';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
import { PostHog } from 'posthog-node';
|
|
20
|
+
import { getClientId } from './client-id.js';
|
|
21
|
+
const MSTRO_DIR = join(homedir(), '.mstro');
|
|
22
|
+
const CONFIG_FILE = join(MSTRO_DIR, 'config.json');
|
|
23
|
+
const PLATFORM_URL = process.env.PLATFORM_URL || 'https://api.mstro.app';
|
|
24
|
+
let client = null;
|
|
25
|
+
let telemetryEnabled = null;
|
|
26
|
+
let analyticsConfig = null;
|
|
27
|
+
/**
|
|
28
|
+
* Check if telemetry is enabled
|
|
29
|
+
*/
|
|
30
|
+
function isTelemetryEnabled() {
|
|
31
|
+
if (telemetryEnabled !== null) {
|
|
32
|
+
return telemetryEnabled;
|
|
33
|
+
}
|
|
34
|
+
// Check environment variable first
|
|
35
|
+
const envValue = process.env.MSTRO_TELEMETRY;
|
|
36
|
+
if (envValue === '0' || envValue === 'false') {
|
|
37
|
+
telemetryEnabled = false;
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
// Check config file
|
|
41
|
+
if (existsSync(CONFIG_FILE)) {
|
|
42
|
+
try {
|
|
43
|
+
const config = JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));
|
|
44
|
+
if (config.telemetry === false) {
|
|
45
|
+
telemetryEnabled = false;
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Ignore parse errors
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
telemetryEnabled = true;
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Fetch analytics config from platform server
|
|
58
|
+
* This keeps the PostHog key out of the npm package
|
|
59
|
+
*/
|
|
60
|
+
async function fetchAnalyticsConfig() {
|
|
61
|
+
try {
|
|
62
|
+
const response = await fetch(`${PLATFORM_URL}/api/config/client`, {
|
|
63
|
+
method: 'GET',
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
});
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const data = await response.json();
|
|
70
|
+
return {
|
|
71
|
+
posthogKey: data.analytics?.posthogKey || '',
|
|
72
|
+
posthogHost: data.analytics?.posthogHost || 'https://eu.i.posthog.com',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Network error, platform unavailable - silently disable analytics
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Initialize PostHog client
|
|
82
|
+
* Call this once at server startup
|
|
83
|
+
*/
|
|
84
|
+
export async function initAnalytics() {
|
|
85
|
+
if (!isTelemetryEnabled()) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// Fetch config from platform
|
|
89
|
+
analyticsConfig = await fetchAnalyticsConfig();
|
|
90
|
+
if (!analyticsConfig?.posthogKey) {
|
|
91
|
+
// No key configured on platform, analytics disabled
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
client = new PostHog(analyticsConfig.posthogKey, {
|
|
95
|
+
host: analyticsConfig.posthogHost,
|
|
96
|
+
// Flush events every 10 seconds or 20 events
|
|
97
|
+
flushAt: 20,
|
|
98
|
+
flushInterval: 10000,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Shutdown PostHog client gracefully
|
|
103
|
+
* Call this before process exit
|
|
104
|
+
*/
|
|
105
|
+
export async function shutdownAnalytics() {
|
|
106
|
+
if (client) {
|
|
107
|
+
await client.shutdown();
|
|
108
|
+
client = null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get the distinct ID for this client
|
|
113
|
+
* Uses the persistent client ID from ~/.mstro/client-id
|
|
114
|
+
*/
|
|
115
|
+
function getDistinctId() {
|
|
116
|
+
return getClientId();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get common properties included with all events
|
|
120
|
+
*/
|
|
121
|
+
function getCommonProperties() {
|
|
122
|
+
return {
|
|
123
|
+
os: platform(),
|
|
124
|
+
arch: arch(),
|
|
125
|
+
node_version: process.version,
|
|
126
|
+
mstro_version: process.env.npm_package_version || 'unknown',
|
|
127
|
+
source: 'client',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Track a custom event
|
|
132
|
+
*/
|
|
133
|
+
export function trackEvent(event, properties) {
|
|
134
|
+
if (!client || !isTelemetryEnabled())
|
|
135
|
+
return;
|
|
136
|
+
client.capture({
|
|
137
|
+
distinctId: getDistinctId(),
|
|
138
|
+
event,
|
|
139
|
+
properties: {
|
|
140
|
+
...getCommonProperties(),
|
|
141
|
+
...properties,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Identify a user (call after login)
|
|
147
|
+
*/
|
|
148
|
+
export function identifyUser(userId, properties) {
|
|
149
|
+
if (!client || !isTelemetryEnabled())
|
|
150
|
+
return;
|
|
151
|
+
// Link the client ID to the user ID
|
|
152
|
+
client.alias({
|
|
153
|
+
distinctId: userId,
|
|
154
|
+
alias: getDistinctId(),
|
|
155
|
+
});
|
|
156
|
+
client.identify({
|
|
157
|
+
distinctId: userId,
|
|
158
|
+
properties: {
|
|
159
|
+
...getCommonProperties(),
|
|
160
|
+
...properties,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Set telemetry preference in config file
|
|
166
|
+
*/
|
|
167
|
+
export function setTelemetryEnabled(enabled) {
|
|
168
|
+
let config = {};
|
|
169
|
+
if (existsSync(CONFIG_FILE)) {
|
|
170
|
+
try {
|
|
171
|
+
config = JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Start fresh if parse fails
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
config.telemetry = enabled;
|
|
178
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
179
|
+
// Update cached value
|
|
180
|
+
telemetryEnabled = enabled;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get current telemetry status
|
|
184
|
+
*/
|
|
185
|
+
export function getTelemetryStatus() {
|
|
186
|
+
const envValue = process.env.MSTRO_TELEMETRY;
|
|
187
|
+
if (envValue === '0' || envValue === 'false') {
|
|
188
|
+
return { enabled: false, reason: 'Disabled via MSTRO_TELEMETRY environment variable' };
|
|
189
|
+
}
|
|
190
|
+
if (existsSync(CONFIG_FILE)) {
|
|
191
|
+
try {
|
|
192
|
+
const config = JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));
|
|
193
|
+
if (config.telemetry === false) {
|
|
194
|
+
return { enabled: false, reason: 'Disabled via ~/.mstro/config.json' };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// Ignore
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (!analyticsConfig?.posthogKey) {
|
|
202
|
+
return { enabled: false, reason: 'Analytics not configured on platform' };
|
|
203
|
+
}
|
|
204
|
+
return { enabled: true, reason: 'Enabled (opt-out with MSTRO_TELEMETRY=0)' };
|
|
205
|
+
}
|
|
206
|
+
// ===========================================
|
|
207
|
+
// Event Constants - Use these for consistency
|
|
208
|
+
// ===========================================
|
|
209
|
+
export const AnalyticsEvents = {
|
|
210
|
+
// CLI events
|
|
211
|
+
CLI_STARTED: 'cli_started',
|
|
212
|
+
CLI_COMMAND: 'cli_command',
|
|
213
|
+
CLI_LOGIN: 'cli_login',
|
|
214
|
+
CLI_LOGOUT: 'cli_logout',
|
|
215
|
+
CLI_ERROR: 'cli_error',
|
|
216
|
+
// Server events
|
|
217
|
+
SERVER_STARTED: 'server_started',
|
|
218
|
+
SERVER_STOPPED: 'server_stopped',
|
|
219
|
+
// Connection events
|
|
220
|
+
PLATFORM_CONNECTED: 'platform_connected',
|
|
221
|
+
PLATFORM_DISCONNECTED: 'platform_disconnected',
|
|
222
|
+
WEB_CLIENT_CONNECTED: 'web_client_connected',
|
|
223
|
+
WEB_CLIENT_DISCONNECTED: 'web_client_disconnected',
|
|
224
|
+
// Improvise events
|
|
225
|
+
IMPROVISE_SESSION_STARTED: 'improvise_session_started',
|
|
226
|
+
IMPROVISE_PROMPT_RECEIVED: 'improvise_prompt_received',
|
|
227
|
+
IMPROVISE_MOVEMENT_STARTED: 'improvise_movement_started',
|
|
228
|
+
IMPROVISE_MOVEMENT_COMPLETED: 'improvise_movement_completed',
|
|
229
|
+
IMPROVISE_MOVEMENT_ERROR: 'improvise_movement_error',
|
|
230
|
+
IMPROVISE_SESSION_ENDED: 'improvise_session_ended',
|
|
231
|
+
IMPROVISE_ABORTED: 'improvise_aborted',
|
|
232
|
+
// Terminal events
|
|
233
|
+
TERMINAL_SESSION_CREATED: 'terminal_session_created',
|
|
234
|
+
TERMINAL_SESSION_CLOSED: 'terminal_session_closed',
|
|
235
|
+
// MCP/Bouncer events
|
|
236
|
+
BOUNCER_TOOL_ALLOWED: 'bouncer_tool_allowed',
|
|
237
|
+
BOUNCER_TOOL_DENIED: 'bouncer_tool_denied',
|
|
238
|
+
BOUNCER_HAIKU_REVIEW: 'bouncer_haiku_review',
|
|
239
|
+
};
|
|
240
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../../server/services/analytics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare class AuthService {
|
|
2
|
+
private localToken;
|
|
3
|
+
constructor();
|
|
4
|
+
/**
|
|
5
|
+
* Load existing session token from disk, or create one if missing.
|
|
6
|
+
* This ensures all mstro instances on the same machine share the same token.
|
|
7
|
+
*/
|
|
8
|
+
private loadOrCreateToken;
|
|
9
|
+
/**
|
|
10
|
+
* Write session token to ~/.mstro/session-token
|
|
11
|
+
*/
|
|
12
|
+
private writeToken;
|
|
13
|
+
/**
|
|
14
|
+
* Validate the local session token (used for localhost API/WS auth)
|
|
15
|
+
*/
|
|
16
|
+
validateLocalToken(token: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Get the local session token (for passing to child processes)
|
|
19
|
+
*/
|
|
20
|
+
getLocalToken(): string;
|
|
21
|
+
/**
|
|
22
|
+
* Read the local session token from disk (static utility for clients)
|
|
23
|
+
*/
|
|
24
|
+
static readLocalToken(): string | null;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../server/services/auth.ts"],"names":[],"mappings":"AAkBA,qBAAa,WAAW;IACtB,OAAO,CAAC,UAAU,CAAQ;;IAM1B;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;OAEG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI1C;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,MAAM,CAAC,cAAc,IAAI,MAAM,GAAG,IAAI;CAWvC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* Authentication Service
|
|
5
|
+
*
|
|
6
|
+
* Manages local session token for localhost API/WebSocket auth.
|
|
7
|
+
* All mstro instances on a machine share a single token from ~/.mstro/session-token.
|
|
8
|
+
* The token is created once (by `mstro login` or first server start) and reused.
|
|
9
|
+
*/
|
|
10
|
+
import { randomBytes } from 'node:crypto';
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
const SESSION_TOKEN_PATH = join(homedir(), '.mstro', 'session-token');
|
|
15
|
+
export class AuthService {
|
|
16
|
+
localToken;
|
|
17
|
+
constructor() {
|
|
18
|
+
this.localToken = this.loadOrCreateToken();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Load existing session token from disk, or create one if missing.
|
|
22
|
+
* This ensures all mstro instances on the same machine share the same token.
|
|
23
|
+
*/
|
|
24
|
+
loadOrCreateToken() {
|
|
25
|
+
const existing = AuthService.readLocalToken();
|
|
26
|
+
if (existing) {
|
|
27
|
+
return existing;
|
|
28
|
+
}
|
|
29
|
+
const token = randomBytes(32).toString('hex');
|
|
30
|
+
this.writeToken(token);
|
|
31
|
+
return token;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Write session token to ~/.mstro/session-token
|
|
35
|
+
*/
|
|
36
|
+
writeToken(token) {
|
|
37
|
+
const dir = join(homedir(), '.mstro');
|
|
38
|
+
if (!existsSync(dir)) {
|
|
39
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
40
|
+
}
|
|
41
|
+
writeFileSync(SESSION_TOKEN_PATH, token, { mode: 0o600 });
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Validate the local session token (used for localhost API/WS auth)
|
|
45
|
+
*/
|
|
46
|
+
validateLocalToken(token) {
|
|
47
|
+
return token === this.localToken;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the local session token (for passing to child processes)
|
|
51
|
+
*/
|
|
52
|
+
getLocalToken() {
|
|
53
|
+
return this.localToken;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Read the local session token from disk (static utility for clients)
|
|
57
|
+
*/
|
|
58
|
+
static readLocalToken() {
|
|
59
|
+
try {
|
|
60
|
+
if (existsSync(SESSION_TOKEN_PATH)) {
|
|
61
|
+
const token = readFileSync(SESSION_TOKEN_PATH, 'utf-8').trim();
|
|
62
|
+
if (token.length > 0) {
|
|
63
|
+
return token;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch { }
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../server/services/auth.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,gEAAgE;AAEhE;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAA;AAErE,MAAM,OAAO,WAAW;IACd,UAAU,CAAQ;IAE1B;QACE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAA;IAC5C,CAAC;IAED;;;OAGG;IACK,iBAAiB;QACvB,MAAM,QAAQ,GAAG,WAAW,CAAC,cAAc,EAAE,CAAA;QAC7C,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,QAAQ,CAAA;QACjB,CAAC;QAED,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QAC7C,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;QACtB,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;OAEG;IACK,UAAU,CAAC,KAAa;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAA;QACrC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAClD,CAAC;QACD,aAAa,CAAC,kBAAkB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAC3D,CAAC;IAED;;OAEG;IACH,kBAAkB,CAAC,KAAa;QAC9B,OAAO,KAAK,KAAK,IAAI,CAAC,UAAU,CAAA;IAClC,CAAC;IAED;;OAEG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,UAAU,CAAA;IACxB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,cAAc;QACnB,IAAI,CAAC;YACH,IAAI,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBACnC,MAAM,KAAK,GAAG,YAAY,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA;gBAC9D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACrB,OAAO,KAAK,CAAA;gBACd,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QACV,OAAO,IAAI,CAAA;IACb,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the persistent client ID, creating one if it doesn't exist.
|
|
3
|
+
* This ID is unique to this machine's ~/.mstro directory.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getClientId(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Get the client ID file path (for debugging/display)
|
|
8
|
+
*/
|
|
9
|
+
export declare function getClientIdPath(): string;
|
|
10
|
+
//# sourceMappingURL=client-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-id.d.ts","sourceRoot":"","sources":["../../../server/services/client-id.ts"],"names":[],"mappings":"AA0BA;;;GAGG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAsBpC;AAUD;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* Client ID Service
|
|
5
|
+
*
|
|
6
|
+
* Generates and persists a unique client identifier that survives server restarts.
|
|
7
|
+
* This ID is used to distinguish between different user home directories on different machines.
|
|
8
|
+
*
|
|
9
|
+
* Storage: ~/.mstro/client-id
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
const MSTRO_DIR = join(homedir(), '.mstro');
|
|
15
|
+
const CLIENT_ID_FILE = join(MSTRO_DIR, 'client-id');
|
|
16
|
+
/**
|
|
17
|
+
* Generate a new UUID v4
|
|
18
|
+
*/
|
|
19
|
+
function generateUUID() {
|
|
20
|
+
return crypto.randomUUID();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get the persistent client ID, creating one if it doesn't exist.
|
|
24
|
+
* This ID is unique to this machine's ~/.mstro directory.
|
|
25
|
+
*/
|
|
26
|
+
export function getClientId() {
|
|
27
|
+
// Ensure ~/.mstro directory exists
|
|
28
|
+
if (!existsSync(MSTRO_DIR)) {
|
|
29
|
+
mkdirSync(MSTRO_DIR, { recursive: true, mode: 0o700 });
|
|
30
|
+
}
|
|
31
|
+
// Try to read existing client ID
|
|
32
|
+
if (existsSync(CLIENT_ID_FILE)) {
|
|
33
|
+
try {
|
|
34
|
+
const id = readFileSync(CLIENT_ID_FILE, 'utf-8').trim();
|
|
35
|
+
if (id && isValidUUID(id)) {
|
|
36
|
+
return id;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// File exists but couldn't be read, generate new one
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Generate and persist a new client ID
|
|
44
|
+
const newId = generateUUID();
|
|
45
|
+
writeFileSync(CLIENT_ID_FILE, newId, 'utf-8');
|
|
46
|
+
return newId;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Validate UUID format
|
|
50
|
+
*/
|
|
51
|
+
function isValidUUID(id) {
|
|
52
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
53
|
+
return uuidRegex.test(id);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the client ID file path (for debugging/display)
|
|
57
|
+
*/
|
|
58
|
+
export function getClientIdPath() {
|
|
59
|
+
return CLIENT_ID_FILE;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=client-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-id.js","sourceRoot":"","sources":["../../../server/services/client-id.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,gEAAgE;AAEhE;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAA;AAC3C,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAA;AAEnD;;GAEG;AACH,SAAS,YAAY;IACnB,OAAO,MAAM,CAAC,UAAU,EAAE,CAAA;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,mCAAmC;IACnC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACxD,CAAC;IAED,iCAAiC;IACjC,IAAI,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA;YACvD,IAAI,EAAE,IAAI,WAAW,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC1B,OAAO,EAAE,CAAA;YACX,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,qDAAqD;QACvD,CAAC;IACH,CAAC;IAED,uCAAuC;IACvC,MAAM,KAAK,GAAG,YAAY,EAAE,CAAA;IAC5B,aAAa,CAAC,cAAc,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IAC7C,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,EAAU;IAC7B,MAAM,SAAS,GAAG,wEAAwE,CAAA;IAC1F,OAAO,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,cAAc,CAAA;AACvB,CAAC"}
|