purmemo-mcp 2.0.0 → 2.0.2
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/package.json +1 -1
- package/src/auth/oauth-manager.js +147 -5
- package/src/server-oauth.js +35 -5
- package/src/setup.js +0 -0
package/package.json
CHANGED
|
@@ -6,8 +6,13 @@
|
|
|
6
6
|
import crypto from 'crypto';
|
|
7
7
|
import express from 'express';
|
|
8
8
|
import open from 'open';
|
|
9
|
+
import { spawn, exec } from 'child_process';
|
|
10
|
+
import { promisify } from 'util';
|
|
11
|
+
import os from 'os';
|
|
9
12
|
import TokenStore from './token-store.js';
|
|
10
13
|
|
|
14
|
+
const execAsync = promisify(exec);
|
|
15
|
+
|
|
11
16
|
class OAuthManager {
|
|
12
17
|
constructor(config = {}) {
|
|
13
18
|
this.apiUrl = config.apiUrl || process.env.PUO_MEMO_API_URL || 'https://api.purmemo.ai';
|
|
@@ -16,6 +21,142 @@ class OAuthManager {
|
|
|
16
21
|
this.tokenStore = new TokenStore();
|
|
17
22
|
this.server = null;
|
|
18
23
|
this.pendingAuth = null;
|
|
24
|
+
this.platform = os.platform();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Robust browser opening with multiple fallback strategies
|
|
29
|
+
* Handles macOS security restrictions and provides user-friendly alternatives
|
|
30
|
+
*/
|
|
31
|
+
async openBrowserRobustly(url) {
|
|
32
|
+
console.log(`🌐 Opening OAuth URL...`);
|
|
33
|
+
|
|
34
|
+
const strategies = [
|
|
35
|
+
() => this.tryOpenPackage(url),
|
|
36
|
+
() => this.tryDirectCommand(url),
|
|
37
|
+
() => this.tryAlternativeCommands(url),
|
|
38
|
+
() => this.tryAppleScript(url),
|
|
39
|
+
() => this.provideFallbackInstructions(url)
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < strategies.length; i++) {
|
|
43
|
+
try {
|
|
44
|
+
const result = await strategies[i]();
|
|
45
|
+
|
|
46
|
+
if (result.success) {
|
|
47
|
+
if (result.requiresManualAction) {
|
|
48
|
+
// User needs to manually open the URL
|
|
49
|
+
return { opened: false, manualRequired: true, url };
|
|
50
|
+
}
|
|
51
|
+
console.log(`✅ Browser opened successfully`);
|
|
52
|
+
return { opened: true, manualRequired: false };
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.log(`Strategy ${i + 1} failed, trying next...`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { opened: false, manualRequired: true, url };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async tryOpenPackage(url) {
|
|
63
|
+
try {
|
|
64
|
+
await open(url, { wait: false });
|
|
65
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
66
|
+
return { success: true };
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new Error(`open package failed: ${error.message}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async tryDirectCommand(url) {
|
|
73
|
+
try {
|
|
74
|
+
let command;
|
|
75
|
+
|
|
76
|
+
switch (this.platform) {
|
|
77
|
+
case 'darwin':
|
|
78
|
+
command = `open "${url}"`;
|
|
79
|
+
break;
|
|
80
|
+
case 'win32':
|
|
81
|
+
command = `start "" "${url}"`;
|
|
82
|
+
break;
|
|
83
|
+
default:
|
|
84
|
+
command = `xdg-open "${url}"`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await execAsync(command);
|
|
88
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
89
|
+
return { success: true };
|
|
90
|
+
} catch (error) {
|
|
91
|
+
throw new Error(`direct command failed: ${error.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async tryAlternativeCommands(url) {
|
|
96
|
+
if (this.platform !== 'darwin') {
|
|
97
|
+
throw new Error('Not macOS');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const commands = [
|
|
101
|
+
`open -a Safari "${url}"`,
|
|
102
|
+
`open -a "Google Chrome" "${url}"`,
|
|
103
|
+
`open -a Firefox "${url}"`,
|
|
104
|
+
`/usr/bin/open "${url}"`
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
for (const command of commands) {
|
|
108
|
+
try {
|
|
109
|
+
await execAsync(command);
|
|
110
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
111
|
+
return { success: true };
|
|
112
|
+
} catch (error) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
throw new Error('All alternative commands failed');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async tryAppleScript(url) {
|
|
121
|
+
if (this.platform !== 'darwin') {
|
|
122
|
+
throw new Error('Not macOS');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const script = `tell application "Safari" to open location "${url}"`;
|
|
127
|
+
await execAsync(`osascript -e '${script}'`);
|
|
128
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
129
|
+
return { success: true };
|
|
130
|
+
} catch (error) {
|
|
131
|
+
throw new Error(`AppleScript failed: ${error.message}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async provideFallbackInstructions(url) {
|
|
136
|
+
console.log('\n' + '━'.repeat(60));
|
|
137
|
+
console.log('🚨 BROWSER OPENING BLOCKED BY SECURITY');
|
|
138
|
+
console.log('━'.repeat(60));
|
|
139
|
+
console.log('');
|
|
140
|
+
console.log('Please manually copy and paste this URL into your browser:');
|
|
141
|
+
console.log('');
|
|
142
|
+
console.log('📋 COPY THIS URL:');
|
|
143
|
+
console.log('⬇'.repeat(40));
|
|
144
|
+
console.log(url);
|
|
145
|
+
console.log('⬆'.repeat(40));
|
|
146
|
+
console.log('');
|
|
147
|
+
console.log('📱 QUICK STEPS:');
|
|
148
|
+
console.log('1. Copy the URL above');
|
|
149
|
+
console.log('2. Open your browser');
|
|
150
|
+
console.log('3. Paste and press Enter');
|
|
151
|
+
console.log('4. Sign in with Google/GitHub');
|
|
152
|
+
console.log('');
|
|
153
|
+
console.log('⏰ Waiting for OAuth callback...');
|
|
154
|
+
console.log('━'.repeat(60));
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
success: true,
|
|
158
|
+
requiresManualAction: true
|
|
159
|
+
};
|
|
19
160
|
}
|
|
20
161
|
|
|
21
162
|
/**
|
|
@@ -106,12 +247,13 @@ class OAuthManager {
|
|
|
106
247
|
// Start local server for callback
|
|
107
248
|
const authCode = await this.startCallbackServer(state);
|
|
108
249
|
|
|
109
|
-
// Open browser for authentication
|
|
110
|
-
|
|
111
|
-
console.log(' If browser doesn\'t open, visit:');
|
|
112
|
-
console.log(` ${authUrl.toString()}\n`);
|
|
250
|
+
// Open browser for authentication with robust fallback
|
|
251
|
+
const browserResult = await this.openBrowserRobustly(authUrl.toString());
|
|
113
252
|
|
|
114
|
-
|
|
253
|
+
if (!browserResult.opened && browserResult.manualRequired) {
|
|
254
|
+
// The robust method already displayed instructions
|
|
255
|
+
console.log(''); // Just add some spacing
|
|
256
|
+
}
|
|
115
257
|
|
|
116
258
|
// Wait for callback with auth code
|
|
117
259
|
const code = await authCode;
|
package/src/server-oauth.js
CHANGED
|
@@ -364,11 +364,41 @@ async function initialize() {
|
|
|
364
364
|
console.log('Ready to serve MCP requests\n');
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
const
|
|
370
|
-
|
|
367
|
+
// Handle CLI commands if provided
|
|
368
|
+
async function handleCliCommands() {
|
|
369
|
+
const args = process.argv.slice(2);
|
|
370
|
+
if (args.length > 0) {
|
|
371
|
+
const command = args[0];
|
|
372
|
+
|
|
373
|
+
if (command === 'setup' || command === 'status' || command === 'logout' || command === 'upgrade') {
|
|
374
|
+
// Delegate to setup script for CLI commands
|
|
375
|
+
const { execSync } = await import('child_process');
|
|
376
|
+
const setupPath = new URL('./setup.js', import.meta.url).pathname;
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
execSync(`node "${setupPath}" ${args.join(' ')}`, {
|
|
380
|
+
stdio: 'inherit',
|
|
381
|
+
cwd: process.cwd()
|
|
382
|
+
});
|
|
383
|
+
process.exit(0);
|
|
384
|
+
} catch (error) {
|
|
385
|
+
process.exit(error.status || 1);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Start server or handle CLI commands
|
|
392
|
+
handleCliCommands().then(() => {
|
|
393
|
+
// If we get here, it's not a CLI command, so start the MCP server
|
|
394
|
+
initialize().then(() => {
|
|
395
|
+
const transport = new StdioServerTransport();
|
|
396
|
+
server.connect(transport);
|
|
397
|
+
}).catch(error => {
|
|
398
|
+
console.error('Failed to initialize:', error);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
});
|
|
371
401
|
}).catch(error => {
|
|
372
|
-
console.error('Failed to
|
|
402
|
+
console.error('Failed to handle CLI commands:', error);
|
|
373
403
|
process.exit(1);
|
|
374
404
|
});
|
package/src/setup.js
CHANGED
|
File without changes
|