polydev-ai 1.8.73 → 1.8.75

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 +236 -0
  2. package/package.json +3 -2
package/mcp/login.js ADDED
@@ -0,0 +1,236 @@
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; charset=utf-8',
99
+ 'Access-Control-Allow-Origin': '*'
100
+ });
101
+ res.end(`
102
+ <!DOCTYPE html>
103
+ <html>
104
+ <head>
105
+ <meta charset="utf-8">
106
+ <title>Polydev - Authenticated</title>
107
+ <style>
108
+ body { font-family: system-ui, -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #fff; }
109
+ .container { text-align: center; padding: 40px; max-width: 400px; }
110
+ .logo { display: flex; align-items: center; justify-content: center; gap: 8px; margin-bottom: 24px; }
111
+ .logo svg { width: 48px; height: 48px; }
112
+ .logo span { font-size: 32px; font-weight: 700; color: #000; }
113
+ .success-icon { width: 64px; height: 64px; background: #000; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; }
114
+ .success-icon svg { width: 32px; height: 32px; }
115
+ h1 { color: #000; margin-bottom: 16px; font-size: 24px; font-weight: 600; }
116
+ p { color: #666; margin: 8px 0; font-size: 14px; }
117
+ code { background: #f5f5f5; padding: 4px 12px; border-radius: 6px; font-size: 13px; color: #000; }
118
+ .close-msg { margin-top: 32px; color: #999; font-size: 13px; }
119
+ </style>
120
+ </head>
121
+ <body>
122
+ <div class="container">
123
+ <div class="logo">
124
+ <svg viewBox="0 0 600 600" fill="#000">
125
+ <g transform="translate(0,600) scale(0.1,-0.1)">
126
+ <path d="M2938 4023 c-31 -54 -97 -169 -148 -257 -50 -87 -96 -168 -102 -180 -8 -20 7 -52 112 -232 67 -115 149 -256 182 -314 34 -58 75 -130 93 -160 18 -30 76 -131 130 -225 134 -235 124 -221 140 -198 45 65 306 547 301 558 -13 34 -642 1105 -649 1105 -2 0 -28 -44 -59 -97z"/>
127
+ <path d="M2305 2933 c-164 -285 -605 -1057 -605 -1059 0 -2 144 -4 320 -4 l320 0 24 38 c13 20 85 143 159 272 74 129 204 357 289 505 85 149 160 280 167 293 l12 22 -324 0 -323 0 -39 -67z"/>
128
+ <path d="M2678 2418 c5 -7 36 -60 67 -118 32 -58 79 -141 105 -185 26 -44 69 -117 95 -162 l48 -83 653 0 c360 0 654 2 654 3 0 2 -71 127 -159 278 l-159 274 -657 3 c-527 2 -656 0 -647 -10z"/>
129
+ </g>
130
+ </svg>
131
+ <span>Polydev</span>
132
+ </div>
133
+ <div class="success-icon">
134
+ <svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
135
+ <polyline points="20 6 9 17 4 12"></polyline>
136
+ </svg>
137
+ </div>
138
+ <h1>Authenticated!</h1>
139
+ <p>Token saved to your shell config.</p>
140
+ <p>Restart your terminal or run:</p>
141
+ <p><code>source ~/.zshrc</code></p>
142
+ <p class="close-msg">You can close this window.</p>
143
+ </div>
144
+ </body>
145
+ </html>
146
+ `);
147
+
148
+ console.log('✓ Token saved successfully!');
149
+ console.log(` Saved to: ${savedTo}`);
150
+ console.log('\n Restart your terminal or run: source ~/.zshrc\n');
151
+
152
+ setTimeout(() => {
153
+ server.close();
154
+ process.exit(0);
155
+ }, 500);
156
+ } else {
157
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
158
+ res.end('Invalid or missing token');
159
+ }
160
+ } else {
161
+ res.writeHead(404);
162
+ res.end('Not found');
163
+ }
164
+ });
165
+
166
+ server.listen(0, 'localhost', () => {
167
+ const port = server.address().port;
168
+ const callbackUrl = `http://localhost:${port}/callback`;
169
+ const authUrl = `https://polydev.ai/auth/cli?callback=${encodeURIComponent(callbackUrl)}&redirect=claude-code`;
170
+
171
+ console.log('Opening browser for authentication...');
172
+ console.log(`\nIf browser doesn't open, visit:\n${authUrl}\n`);
173
+
174
+ openBrowser(authUrl);
175
+
176
+ setTimeout(() => {
177
+ console.log('\n✗ Login timed out. Please try again.\n');
178
+ server.close();
179
+ process.exit(1);
180
+ }, 5 * 60 * 1000);
181
+ });
182
+
183
+ server.on('error', (err) => {
184
+ console.error('Server error:', err.message);
185
+ process.exit(1);
186
+ });
187
+ }
188
+
189
+ function saveToken(token) {
190
+ const shell = process.env.SHELL || '/bin/zsh';
191
+ const rcFile = shell.includes('zsh') ? path.join(os.homedir(), '.zshrc') :
192
+ shell.includes('bash') ? path.join(os.homedir(), '.bashrc') :
193
+ path.join(os.homedir(), '.profile');
194
+
195
+ let content = '';
196
+ try {
197
+ content = fs.readFileSync(rcFile, 'utf8');
198
+ } catch (e) {}
199
+
200
+ const lines = content.split('\n').filter(line =>
201
+ !line.trim().startsWith('export POLYDEV_USER_TOKEN') &&
202
+ !line.trim().startsWith('POLYDEV_USER_TOKEN')
203
+ );
204
+
205
+ lines.push(`export POLYDEV_USER_TOKEN="${token}"`);
206
+ fs.writeFileSync(rcFile, lines.join('\n').replace(/\n+$/, '') + '\n');
207
+
208
+ const envFile = path.join(os.homedir(), '.polydev.env');
209
+ fs.writeFileSync(envFile, `POLYDEV_USER_TOKEN="${token}"\n`);
210
+
211
+ return rcFile;
212
+ }
213
+
214
+ function openBrowser(url) {
215
+ // Use execFile with explicit command and arguments for safety
216
+ const platform = process.platform;
217
+ let cmd, args;
218
+
219
+ if (platform === 'darwin') {
220
+ cmd = 'open';
221
+ args = [url];
222
+ } else if (platform === 'win32') {
223
+ cmd = 'cmd';
224
+ args = ['/c', 'start', '', url];
225
+ } else {
226
+ cmd = 'xdg-open';
227
+ args = [url];
228
+ }
229
+
230
+ execFile(cmd, args, (err) => {
231
+ if (err) {
232
+ console.log('Could not open browser automatically.');
233
+ console.log(`Please open this URL manually: ${url}`);
234
+ }
235
+ });
236
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.8.73",
3
+ "version": "1.8.75",
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/",