polydev-ai 1.9.19 → 1.9.20

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/mcp/manifest.json CHANGED
@@ -98,6 +98,10 @@
98
98
  "minimum": 30,
99
99
  "maximum": 600,
100
100
  "default": 300
101
+ },
102
+ "user_token": {
103
+ "type": "string",
104
+ "description": "Polydev user token (pd_xxx). Use this in sandboxed environments (Cowork, containers) where browser login is unavailable. Get your token from https://polydev.ai/dashboard"
101
105
  }
102
106
  }
103
107
  },
@@ -300,6 +300,9 @@ class StdioMCPWrapper {
300
300
  // Pending session file for surviving restarts
301
301
  this.PENDING_SESSION_FILE = path.join(os.homedir(), '.polydev-pending-session');
302
302
 
303
+ // Sandbox detection (Cowork, containers with limited disk/no browser)
304
+ this._isSandboxed = null; // lazy-detected
305
+
303
306
  // MCP client info (set during initialize handshake)
304
307
  // Used to detect which IDE is running us and exclude its CLI to avoid recursive calls
305
308
  this.clientInfo = null;
@@ -491,7 +494,18 @@ Token will be saved automatically after login.`
491
494
  */
492
495
  async handleLoginTool(params, id) {
493
496
  const crypto = require('crypto');
494
-
497
+
498
+ // Accept user_token from params (essential for sandboxed environments like Cowork)
499
+ const inlineToken = params?.arguments?.user_token;
500
+ if (inlineToken && (inlineToken.startsWith('pd_') || inlineToken.startsWith('polydev_'))) {
501
+ console.error('[Polydev] Login via inline user_token');
502
+ this.userToken = inlineToken;
503
+ this.isAuthenticated = true;
504
+ process.env.POLYDEV_USER_TOKEN = inlineToken;
505
+ this.saveTokenToFiles(inlineToken);
506
+ return await this.fetchAuthStatusResponse(id);
507
+ }
508
+
495
509
  // Check if already authenticated - verify with server first
496
510
  if (this.isAuthenticated && this.userToken) {
497
511
  console.error('[Polydev] Token exists locally, verifying with server...');
@@ -525,9 +539,38 @@ To re-login: npx polydev-ai`
525
539
  this.userToken = null;
526
540
  }
527
541
 
542
+ // In sandboxed environments, don't try browser login — show token-paste instructions
543
+ if (this.isSandboxed()) {
544
+ console.error('[Polydev] Sandbox detected — browser login unavailable');
545
+ return {
546
+ jsonrpc: '2.0',
547
+ id,
548
+ result: {
549
+ content: [{
550
+ type: 'text',
551
+ text: `LOGIN - Sandboxed Environment
552
+ ==============================
553
+
554
+ Browser login is not available in this environment (Cowork/container).
555
+
556
+ To authenticate, use one of these methods:
557
+
558
+ **Method 1: Pass token directly**
559
+ 1. Get your token from https://polydev.ai/dashboard
560
+ 2. Call login with user_token:
561
+
562
+ mcp__plugin_polydev_mcp__login({ user_token: "pd_your_token_here" })
563
+
564
+ **Method 2: Set environment variable**
565
+ Set POLYDEV_USER_TOKEN=pd_your_token before starting the MCP server.`
566
+ }]
567
+ }
568
+ };
569
+ }
570
+
528
571
  // Generate unique session ID for polling-based auth
529
572
  const sessionId = crypto.randomBytes(32).toString('hex');
530
-
573
+
531
574
  try {
532
575
  // Create session on server (with retry for transient network issues)
533
576
  let createResponse;
@@ -936,13 +979,52 @@ Still waiting for login. Use /polydev:auth to check status after signing in.`
936
979
  * Handle get_auth_status tool - comprehensive status display
937
980
  */
938
981
  async handleGetAuthStatus(params, id) {
982
+ // Accept user_token from params (essential for sandboxed environments like Cowork)
983
+ const inlineToken = params?.arguments?.user_token;
984
+ if (inlineToken && (inlineToken.startsWith('pd_') || inlineToken.startsWith('polydev_'))) {
985
+ console.error('[Polydev] Using inline user_token from params');
986
+ this.userToken = inlineToken;
987
+ this.isAuthenticated = true;
988
+ process.env.POLYDEV_USER_TOKEN = inlineToken;
989
+ this.saveTokenToFiles(inlineToken); // safe — handles sandbox gracefully
990
+ }
991
+
939
992
  // Try to hot-reload token if not authenticated
940
993
  if (!this.isAuthenticated || !this.userToken) {
941
994
  this.reloadTokenFromFiles();
942
995
  }
943
-
996
+
944
997
  if (!this.isAuthenticated || !this.userToken) {
945
- // No token found anywhere auto-trigger login instead of showing static message
998
+ // No token found — check if we're in a sandbox (Cowork, containers)
999
+ if (this.isSandboxed()) {
1000
+ console.error('[Polydev] No token and sandboxed — showing token-paste instructions');
1001
+ return {
1002
+ jsonrpc: '2.0',
1003
+ id,
1004
+ result: {
1005
+ content: [{
1006
+ type: 'text',
1007
+ text: `POLYDEV STATUS
1008
+ ==============
1009
+
1010
+ Authentication: Not connected (sandboxed environment detected)
1011
+
1012
+ Browser login is not available in this environment.
1013
+
1014
+ To authenticate, pass your token directly:
1015
+ 1. Get your token from https://polydev.ai/dashboard
1016
+ 2. Call get_auth_status with user_token parameter:
1017
+
1018
+ mcp__plugin_polydev_mcp__get_auth_status({ user_token: "pd_your_token_here" })
1019
+
1020
+ 3. Or set the environment variable before starting:
1021
+ POLYDEV_USER_TOKEN=pd_your_token_here`
1022
+ }]
1023
+ }
1024
+ };
1025
+ }
1026
+
1027
+ // Not sandboxed — auto-trigger browser login
946
1028
  console.error('[Polydev] No token found, auto-triggering login flow...');
947
1029
  return await this.triggerReAuth(id, 'Not authenticated. Opening browser for login...');
948
1030
  }
@@ -1071,6 +1153,29 @@ To re-login: /polydev:login`
1071
1153
  }
1072
1154
  }
1073
1155
 
1156
+ /**
1157
+ * Detect if running in a sandboxed environment (Cowork, containers, etc.)
1158
+ * where filesystem is read-only/limited and browser is unavailable.
1159
+ * Result is cached after first check.
1160
+ */
1161
+ isSandboxed() {
1162
+ if (this._isSandboxed !== null) return this._isSandboxed;
1163
+
1164
+ try {
1165
+ // Try writing a test file to home directory
1166
+ const testFile = path.join(os.homedir(), '.polydev-sandbox-test');
1167
+ fs.writeFileSync(testFile, 'test');
1168
+ fs.unlinkSync(testFile);
1169
+ this._isSandboxed = false;
1170
+ } catch (e) {
1171
+ // ENOSPC, EROFS, EACCES all indicate sandbox-like constraints
1172
+ console.error(`[Polydev] Sandbox detected: ${e.code || e.message}`);
1173
+ this._isSandboxed = true;
1174
+ }
1175
+
1176
+ return this._isSandboxed;
1177
+ }
1178
+
1074
1179
  /**
1075
1180
  * Open URL in browser - best-in-class cross-platform implementation
1076
1181
  * Uses 'open' npm package with fallbacks for reliability in MCP/stdio context
@@ -1133,27 +1238,39 @@ To re-login: /polydev:login`
1133
1238
  * Save token to shell config files
1134
1239
  */
1135
1240
  saveTokenToFiles(token) {
1136
- const shell = process.env.SHELL || '/bin/zsh';
1137
- const rcFile = shell.includes('zsh') ? path.join(os.homedir(), '.zshrc') :
1138
- shell.includes('bash') ? path.join(os.homedir(), '.bashrc') :
1139
- path.join(os.homedir(), '.profile');
1241
+ if (this.isSandboxed()) {
1242
+ // In sandbox, just set in-memory — can't write files
1243
+ console.error('[Polydev] Sandbox: skipping file writes, token set in-memory only');
1244
+ process.env.POLYDEV_USER_TOKEN = token;
1245
+ return;
1246
+ }
1140
1247
 
1141
- let content = '';
1142
- try { content = fs.readFileSync(rcFile, 'utf8'); } catch (e) {}
1248
+ try {
1249
+ const shell = process.env.SHELL || '/bin/zsh';
1250
+ const rcFile = shell.includes('zsh') ? path.join(os.homedir(), '.zshrc') :
1251
+ shell.includes('bash') ? path.join(os.homedir(), '.bashrc') :
1252
+ path.join(os.homedir(), '.profile');
1143
1253
 
1144
- const lines = content.split('\n').filter(line =>
1145
- !line.trim().startsWith('export POLYDEV_USER_TOKEN') &&
1146
- !line.trim().startsWith('POLYDEV_USER_TOKEN')
1147
- );
1254
+ let content = '';
1255
+ try { content = fs.readFileSync(rcFile, 'utf8'); } catch (e) {}
1148
1256
 
1149
- lines.push(`export POLYDEV_USER_TOKEN="${token}"`);
1150
- fs.writeFileSync(rcFile, lines.join('\n').replace(/\n+$/, '') + '\n');
1257
+ const lines = content.split('\n').filter(line =>
1258
+ !line.trim().startsWith('export POLYDEV_USER_TOKEN') &&
1259
+ !line.trim().startsWith('POLYDEV_USER_TOKEN')
1260
+ );
1151
1261
 
1152
- // Also save to .polydev.env
1153
- const envFile = path.join(os.homedir(), '.polydev.env');
1154
- fs.writeFileSync(envFile, `POLYDEV_USER_TOKEN="${token}"\n`);
1262
+ lines.push(`export POLYDEV_USER_TOKEN="${token}"`);
1263
+ fs.writeFileSync(rcFile, lines.join('\n').replace(/\n+$/, '') + '\n');
1155
1264
 
1156
- console.error(`[Polydev] Token saved to ${rcFile} and ${envFile}`);
1265
+ // Also save to .polydev.env
1266
+ const envFile = path.join(os.homedir(), '.polydev.env');
1267
+ fs.writeFileSync(envFile, `POLYDEV_USER_TOKEN="${token}"\n`);
1268
+
1269
+ console.error(`[Polydev] Token saved to ${rcFile} and ${envFile}`);
1270
+ } catch (e) {
1271
+ console.error(`[Polydev] Could not write token to files (${e.code || e.message}), set in-memory only`);
1272
+ process.env.POLYDEV_USER_TOKEN = token;
1273
+ }
1157
1274
  }
1158
1275
 
1159
1276
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.9.19",
3
+ "version": "1.9.20",
4
4
  "engines": {
5
5
  "node": ">=20.x <=22.x"
6
6
  },