polydev-ai 1.8.73 → 1.8.74

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 (2) hide show
  1. package/mcp/login.js +216 -0
  2. package/package.json +3 -2
package/mcp/login.js ADDED
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Polydev CLI Login - Browser-based authentication for Polydev
5
+ *
6
+ * Usage: npx polydev-ai login
7
+ *
8
+ * This script:
9
+ * 1. Starts a local HTTP server to receive the callback
10
+ * 2. Opens the browser to polydev.ai/auth/cli
11
+ * 3. After authentication, receives the token via callback
12
+ * 4. Saves the token to your shell config (~/.zshrc or ~/.bashrc)
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const os = require('os');
18
+ const http = require('http');
19
+ const { execFile } = require('child_process');
20
+
21
+ const command = process.argv[2];
22
+
23
+ if (command === 'status') {
24
+ showStatus();
25
+ } else if (command === 'help' || command === '--help' || command === '-h') {
26
+ showHelp();
27
+ } else {
28
+ runLogin();
29
+ }
30
+
31
+ function showHelp() {
32
+ console.log(`
33
+ Polydev AI Login
34
+
35
+ Commands:
36
+ login Authenticate with Polydev (opens browser)
37
+ status Show current authentication status
38
+ help Show this help message
39
+
40
+ Usage:
41
+ npx polydev-ai login # Opens browser to authenticate
42
+ npx polydev-ai status # Check if authenticated
43
+
44
+ After login, restart your terminal or run: source ~/.zshrc
45
+ `);
46
+ }
47
+
48
+ function showStatus() {
49
+ loadEnvFile(path.join(os.homedir(), '.polydev.env'));
50
+ loadEnvFile(path.join(os.homedir(), '.zshrc'));
51
+
52
+ const token = process.env.POLYDEV_USER_TOKEN;
53
+ if (token && token.startsWith('pd_')) {
54
+ console.log('✓ Authenticated with Polydev');
55
+ console.log(` Token: ${token.slice(0, 12)}...${token.slice(-4)}`);
56
+ } else {
57
+ console.log('✗ Not authenticated');
58
+ console.log(' Run: npx polydev-ai login');
59
+ }
60
+ }
61
+
62
+ function loadEnvFile(filePath) {
63
+ try {
64
+ if (!fs.existsSync(filePath)) return;
65
+ const content = fs.readFileSync(filePath, 'utf8');
66
+ const match = content.match(/POLYDEV_USER_TOKEN[=\s]["']?([^"'\n]+)["']?/);
67
+ if (match && !process.env.POLYDEV_USER_TOKEN) {
68
+ process.env.POLYDEV_USER_TOKEN = match[1];
69
+ }
70
+ } catch (e) {
71
+ // ignore
72
+ }
73
+ }
74
+
75
+ async function runLogin() {
76
+ console.log('\n🔐 Polydev Authentication\n');
77
+
78
+ const server = http.createServer((req, res) => {
79
+ const url = new URL(req.url, `http://localhost`);
80
+
81
+ if (req.method === 'OPTIONS') {
82
+ res.writeHead(204, {
83
+ 'Access-Control-Allow-Origin': '*',
84
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
85
+ 'Access-Control-Allow-Headers': 'Content-Type'
86
+ });
87
+ res.end();
88
+ return;
89
+ }
90
+
91
+ if (url.pathname === '/callback') {
92
+ const token = url.searchParams.get('token');
93
+
94
+ if (token && token.startsWith('pd_')) {
95
+ const savedTo = saveToken(token);
96
+
97
+ res.writeHead(200, {
98
+ 'Content-Type': 'text/html',
99
+ 'Access-Control-Allow-Origin': '*'
100
+ });
101
+ res.end(`
102
+ <!DOCTYPE html>
103
+ <html>
104
+ <head>
105
+ <title>Polydev - Success</title>
106
+ <style>
107
+ body { font-family: system-ui, -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #fff; }
108
+ .container { text-align: center; padding: 40px; max-width: 400px; }
109
+ h1 { color: #000; margin-bottom: 16px; font-size: 24px; }
110
+ p { color: #666; margin: 8px 0; }
111
+ .success { color: #16a34a; font-size: 48px; margin-bottom: 16px; }
112
+ code { background: #f5f5f5; padding: 2px 8px; border-radius: 4px; font-size: 14px; }
113
+ </style>
114
+ </head>
115
+ <body>
116
+ <div class="container">
117
+ <div class="success">✓</div>
118
+ <h1>Authenticated!</h1>
119
+ <p>Token saved to your shell config.</p>
120
+ <p>Restart your terminal or run:</p>
121
+ <p><code>source ~/.zshrc</code></p>
122
+ <p style="margin-top: 24px; font-size: 14px;">You can close this window.</p>
123
+ </div>
124
+ </body>
125
+ </html>
126
+ `);
127
+
128
+ console.log('✓ Token saved successfully!');
129
+ console.log(` Saved to: ${savedTo}`);
130
+ console.log('\n Restart your terminal or run: source ~/.zshrc\n');
131
+
132
+ setTimeout(() => {
133
+ server.close();
134
+ process.exit(0);
135
+ }, 500);
136
+ } else {
137
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
138
+ res.end('Invalid or missing token');
139
+ }
140
+ } else {
141
+ res.writeHead(404);
142
+ res.end('Not found');
143
+ }
144
+ });
145
+
146
+ server.listen(0, 'localhost', () => {
147
+ const port = server.address().port;
148
+ const callbackUrl = `http://localhost:${port}/callback`;
149
+ const authUrl = `https://polydev.ai/auth/cli?callback=${encodeURIComponent(callbackUrl)}&redirect=claude-code`;
150
+
151
+ console.log('Opening browser for authentication...');
152
+ console.log(`\nIf browser doesn't open, visit:\n${authUrl}\n`);
153
+
154
+ openBrowser(authUrl);
155
+
156
+ setTimeout(() => {
157
+ console.log('\n✗ Login timed out. Please try again.\n');
158
+ server.close();
159
+ process.exit(1);
160
+ }, 5 * 60 * 1000);
161
+ });
162
+
163
+ server.on('error', (err) => {
164
+ console.error('Server error:', err.message);
165
+ process.exit(1);
166
+ });
167
+ }
168
+
169
+ function saveToken(token) {
170
+ const shell = process.env.SHELL || '/bin/zsh';
171
+ const rcFile = shell.includes('zsh') ? path.join(os.homedir(), '.zshrc') :
172
+ shell.includes('bash') ? path.join(os.homedir(), '.bashrc') :
173
+ path.join(os.homedir(), '.profile');
174
+
175
+ let content = '';
176
+ try {
177
+ content = fs.readFileSync(rcFile, 'utf8');
178
+ } catch (e) {}
179
+
180
+ const lines = content.split('\n').filter(line =>
181
+ !line.trim().startsWith('export POLYDEV_USER_TOKEN') &&
182
+ !line.trim().startsWith('POLYDEV_USER_TOKEN')
183
+ );
184
+
185
+ lines.push(`export POLYDEV_USER_TOKEN="${token}"`);
186
+ fs.writeFileSync(rcFile, lines.join('\n').replace(/\n+$/, '') + '\n');
187
+
188
+ const envFile = path.join(os.homedir(), '.polydev.env');
189
+ fs.writeFileSync(envFile, `POLYDEV_USER_TOKEN="${token}"\n`);
190
+
191
+ return rcFile;
192
+ }
193
+
194
+ function openBrowser(url) {
195
+ // Use execFile with explicit command and arguments for safety
196
+ const platform = process.platform;
197
+ let cmd, args;
198
+
199
+ if (platform === 'darwin') {
200
+ cmd = 'open';
201
+ args = [url];
202
+ } else if (platform === 'win32') {
203
+ cmd = 'cmd';
204
+ args = ['/c', 'start', '', url];
205
+ } else {
206
+ cmd = 'xdg-open';
207
+ args = [url];
208
+ }
209
+
210
+ execFile(cmd, args, (err) => {
211
+ if (err) {
212
+ console.log('Could not open browser automatically.');
213
+ console.log(`Please open this URL manually: ${url}`);
214
+ }
215
+ });
216
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.8.73",
3
+ "version": "1.8.74",
4
4
  "engines": {
5
5
  "node": ">=20.x <=22.x"
6
6
  },
@@ -23,7 +23,8 @@
23
23
  "main": "mcp/stdio-wrapper.js",
24
24
  "bin": {
25
25
  "polydev-stdio": "mcp/stdio-wrapper.js",
26
- "polydev-ai": "mcp/stdio-wrapper.js"
26
+ "polydev-ai": "mcp/stdio-wrapper.js",
27
+ "polydev-login": "mcp/login.js"
27
28
  },
28
29
  "files": [
29
30
  "mcp/",