purmemo-mcp 2.0.6 ā 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/auth/oauth-manager.js +44 -146
- package/src/setup.js +1 -10
package/package.json
CHANGED
|
@@ -6,13 +6,10 @@
|
|
|
6
6
|
import crypto from 'crypto';
|
|
7
7
|
import express from 'express';
|
|
8
8
|
import open from 'open';
|
|
9
|
-
import {
|
|
10
|
-
import { promisify } from 'util';
|
|
9
|
+
import { exec } from 'child_process';
|
|
11
10
|
import os from 'os';
|
|
12
11
|
import TokenStore from './token-store.js';
|
|
13
12
|
|
|
14
|
-
const execAsync = promisify(exec);
|
|
15
|
-
|
|
16
13
|
class OAuthManager {
|
|
17
14
|
constructor(config = {}) {
|
|
18
15
|
this.apiUrl = config.apiUrl || process.env.PUO_MEMO_API_URL || 'https://api.purmemo.ai';
|
|
@@ -26,140 +23,8 @@ class OAuthManager {
|
|
|
26
23
|
this.platform = os.platform();
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* Handles macOS security restrictions and provides user-friendly alternatives
|
|
32
|
-
*/
|
|
33
|
-
async openBrowserRobustly(url) {
|
|
34
|
-
console.log(`š Opening OAuth URL...`);
|
|
35
|
-
|
|
36
|
-
const strategies = [
|
|
37
|
-
() => this.tryOpenPackage(url),
|
|
38
|
-
() => this.tryDirectCommand(url),
|
|
39
|
-
() => this.tryAlternativeCommands(url),
|
|
40
|
-
() => this.tryAppleScript(url),
|
|
41
|
-
() => this.provideFallbackInstructions(url)
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
for (let i = 0; i < strategies.length; i++) {
|
|
45
|
-
try {
|
|
46
|
-
const result = await strategies[i]();
|
|
47
|
-
|
|
48
|
-
if (result.success) {
|
|
49
|
-
if (result.requiresManualAction) {
|
|
50
|
-
// User needs to manually open the URL
|
|
51
|
-
return { opened: false, manualRequired: true, url };
|
|
52
|
-
}
|
|
53
|
-
console.log(`ā
Browser opened successfully`);
|
|
54
|
-
return { opened: true, manualRequired: false };
|
|
55
|
-
}
|
|
56
|
-
} catch (error) {
|
|
57
|
-
console.log(`Strategy ${i + 1} failed, trying next...`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return { opened: false, manualRequired: true, url };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async tryOpenPackage(url) {
|
|
65
|
-
try {
|
|
66
|
-
await open(url, { wait: false });
|
|
67
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
68
|
-
return { success: true };
|
|
69
|
-
} catch (error) {
|
|
70
|
-
throw new Error(`open package failed: ${error.message}`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async tryDirectCommand(url) {
|
|
75
|
-
try {
|
|
76
|
-
let command;
|
|
77
|
-
|
|
78
|
-
switch (this.platform) {
|
|
79
|
-
case 'darwin':
|
|
80
|
-
command = `open "${url}"`;
|
|
81
|
-
break;
|
|
82
|
-
case 'win32':
|
|
83
|
-
command = `start "" "${url}"`;
|
|
84
|
-
break;
|
|
85
|
-
default:
|
|
86
|
-
command = `xdg-open "${url}"`;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
await execAsync(command);
|
|
90
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
91
|
-
return { success: true };
|
|
92
|
-
} catch (error) {
|
|
93
|
-
throw new Error(`direct command failed: ${error.message}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
async tryAlternativeCommands(url) {
|
|
98
|
-
if (this.platform !== 'darwin') {
|
|
99
|
-
throw new Error('Not macOS');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const commands = [
|
|
103
|
-
`open -a Safari "${url}"`,
|
|
104
|
-
`open -a "Google Chrome" "${url}"`,
|
|
105
|
-
`open -a Firefox "${url}"`,
|
|
106
|
-
`/usr/bin/open "${url}"`
|
|
107
|
-
];
|
|
108
|
-
|
|
109
|
-
for (const command of commands) {
|
|
110
|
-
try {
|
|
111
|
-
await execAsync(command);
|
|
112
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
113
|
-
return { success: true };
|
|
114
|
-
} catch (error) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
throw new Error('All alternative commands failed');
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async tryAppleScript(url) {
|
|
123
|
-
if (this.platform !== 'darwin') {
|
|
124
|
-
throw new Error('Not macOS');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
const script = `tell application "Safari" to open location "${url}"`;
|
|
129
|
-
await execAsync(`osascript -e '${script}'`);
|
|
130
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
131
|
-
return { success: true };
|
|
132
|
-
} catch (error) {
|
|
133
|
-
throw new Error(`AppleScript failed: ${error.message}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async provideFallbackInstructions(url) {
|
|
138
|
-
console.log('\n' + 'ā'.repeat(60));
|
|
139
|
-
console.log('šØ BROWSER OPENING BLOCKED BY SECURITY');
|
|
140
|
-
console.log('ā'.repeat(60));
|
|
141
|
-
console.log('');
|
|
142
|
-
console.log('Please manually copy and paste this URL into your browser:');
|
|
143
|
-
console.log('');
|
|
144
|
-
console.log('š COPY THIS URL:');
|
|
145
|
-
console.log('ā¬'.repeat(40));
|
|
146
|
-
console.log(url);
|
|
147
|
-
console.log('ā¬'.repeat(40));
|
|
148
|
-
console.log('');
|
|
149
|
-
console.log('š± QUICK STEPS:');
|
|
150
|
-
console.log('1. Copy the URL above');
|
|
151
|
-
console.log('2. Open your browser');
|
|
152
|
-
console.log('3. Paste and press Enter');
|
|
153
|
-
console.log('4. Sign in with Google/GitHub');
|
|
154
|
-
console.log('');
|
|
155
|
-
console.log('ā° Waiting for OAuth callback...');
|
|
156
|
-
console.log('ā'.repeat(60));
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
success: true,
|
|
160
|
-
requiresManualAction: true
|
|
161
|
-
};
|
|
162
|
-
}
|
|
26
|
+
// Removed complex browser opening strategies - going manual-first
|
|
27
|
+
// The performOAuthFlow method now handles everything clearly
|
|
163
28
|
|
|
164
29
|
/**
|
|
165
30
|
* Get current authentication token
|
|
@@ -229,8 +94,6 @@ class OAuthManager {
|
|
|
229
94
|
* Perform the actual OAuth flow
|
|
230
95
|
*/
|
|
231
96
|
async performOAuthFlow() {
|
|
232
|
-
console.log('\nš Starting Purmemo authentication...\n');
|
|
233
|
-
|
|
234
97
|
// Generate PKCE challenge
|
|
235
98
|
const codeVerifier = this.generateCodeVerifier();
|
|
236
99
|
const codeChallenge = this.generateCodeChallenge(codeVerifier);
|
|
@@ -246,15 +109,50 @@ class OAuthManager {
|
|
|
246
109
|
authUrl.searchParams.append('code_challenge', codeChallenge);
|
|
247
110
|
authUrl.searchParams.append('code_challenge_method', 'S256');
|
|
248
111
|
|
|
249
|
-
// Start local server for callback
|
|
112
|
+
// Start local server for callback FIRST
|
|
250
113
|
const authCode = await this.startCallbackServer(state);
|
|
251
114
|
|
|
252
|
-
//
|
|
253
|
-
|
|
115
|
+
// MANUAL-FIRST: Always show the URL clearly
|
|
116
|
+
console.log('\n' + 'ā'.repeat(70));
|
|
117
|
+
console.log('š AUTHENTICATION REQUIRED');
|
|
118
|
+
console.log('ā'.repeat(70));
|
|
119
|
+
console.log('');
|
|
120
|
+
console.log('Please complete authentication in your browser:');
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log('š COPY THIS URL:');
|
|
123
|
+
console.log('ā'.repeat(70));
|
|
124
|
+
console.log(authUrl.toString());
|
|
125
|
+
console.log('ā'.repeat(70));
|
|
126
|
+
console.log('');
|
|
127
|
+
console.log('š± STEPS:');
|
|
128
|
+
console.log(' 1. Copy the URL above');
|
|
129
|
+
console.log(' 2. Open your browser');
|
|
130
|
+
console.log(' 3. Paste and press Enter');
|
|
131
|
+
console.log(' 4. Sign in with Google, GitHub, or Email');
|
|
132
|
+
console.log(' 5. You\'ll be redirected back automatically');
|
|
133
|
+
console.log('');
|
|
134
|
+
console.log('ā³ Waiting for you to sign in...');
|
|
135
|
+
console.log('ā'.repeat(70));
|
|
136
|
+
console.log('');
|
|
254
137
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
138
|
+
// Try to open browser quietly in background (might work, might not)
|
|
139
|
+
try {
|
|
140
|
+
if (this.platform === 'darwin') {
|
|
141
|
+
// On macOS, try using 'open' directly without the npm context
|
|
142
|
+
exec(`open "${authUrl.toString()}"`, (error) => {
|
|
143
|
+
if (!error) {
|
|
144
|
+
console.log('⨠Browser opened automatically - check your browser tabs');
|
|
145
|
+
}
|
|
146
|
+
// If it fails, that's fine - user has manual instructions
|
|
147
|
+
});
|
|
148
|
+
} else {
|
|
149
|
+
// Try other platforms
|
|
150
|
+
await open(authUrl.toString(), { wait: false }).catch(() => {
|
|
151
|
+
// Silently fail - manual instructions are already shown
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
} catch {
|
|
155
|
+
// Silent fail - manual instructions are primary
|
|
258
156
|
}
|
|
259
157
|
|
|
260
158
|
// Wait for callback with auth code
|
package/src/setup.js
CHANGED
|
@@ -71,18 +71,9 @@ program
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
console.log(chalk.yellow('\nš± Starting authentication process...\n'));
|
|
74
|
-
console.log(chalk.gray('
|
|
75
|
-
console.log(chalk.gray('Choose your preferred method: Google, GitHub, or Email.\n'));
|
|
76
|
-
|
|
77
|
-
const spinner = ora('Preparing authentication...').start();
|
|
74
|
+
console.log(chalk.gray('Follow the instructions below to sign in.\n'));
|
|
78
75
|
|
|
79
76
|
try {
|
|
80
|
-
await new Promise(resolve => setTimeout(resolve, 1000)); // Brief pause
|
|
81
|
-
spinner.stop();
|
|
82
|
-
|
|
83
|
-
// Ensure manual instructions are visible if browser fails
|
|
84
|
-
console.log(chalk.cyan('\nš Starting Purmemo authentication...\n'));
|
|
85
|
-
|
|
86
77
|
const token = await authManager.authenticate();
|
|
87
78
|
|
|
88
79
|
if (token) {
|