polydev-ai 1.9.19 → 1.9.21

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;
@@ -777,6 +820,28 @@ Dashboard: https://polydev.ai/dashboard`
777
820
  * @returns {object} JSON-RPC response
778
821
  */
779
822
  async triggerReAuth(id, reason = 'Token invalid or expired') {
823
+ // In sandboxed environments, browser login won't work — show token-paste instructions instead
824
+ if (this.isSandboxed()) {
825
+ console.error('[Polydev] Sandbox detected — skipping browser re-auth, showing token-paste instructions');
826
+ return {
827
+ jsonrpc: '2.0',
828
+ id,
829
+ result: {
830
+ content: [{
831
+ type: 'text',
832
+ text: `${reason}
833
+
834
+ Browser login is not available in this environment (Cowork/container).
835
+
836
+ To authenticate, pass your token directly:
837
+ 1. Get your token from https://polydev.ai/dashboard
838
+ 2. Call: mcp__plugin_polydev_mcp__get_auth_status({ user_token: "pd_your_token_here" })
839
+ 3. Or: mcp__plugin_polydev_mcp__login({ user_token: "pd_your_token_here" })`
840
+ }]
841
+ }
842
+ };
843
+ }
844
+
780
845
  const crypto = require('crypto');
781
846
  const sessionId = crypto.randomBytes(32).toString('hex');
782
847
 
@@ -936,13 +1001,52 @@ Still waiting for login. Use /polydev:auth to check status after signing in.`
936
1001
  * Handle get_auth_status tool - comprehensive status display
937
1002
  */
938
1003
  async handleGetAuthStatus(params, id) {
1004
+ // Accept user_token from params (essential for sandboxed environments like Cowork)
1005
+ const inlineToken = params?.arguments?.user_token;
1006
+ if (inlineToken && (inlineToken.startsWith('pd_') || inlineToken.startsWith('polydev_'))) {
1007
+ console.error('[Polydev] Using inline user_token from params');
1008
+ this.userToken = inlineToken;
1009
+ this.isAuthenticated = true;
1010
+ process.env.POLYDEV_USER_TOKEN = inlineToken;
1011
+ this.saveTokenToFiles(inlineToken); // safe — handles sandbox gracefully
1012
+ }
1013
+
939
1014
  // Try to hot-reload token if not authenticated
940
1015
  if (!this.isAuthenticated || !this.userToken) {
941
1016
  this.reloadTokenFromFiles();
942
1017
  }
943
-
1018
+
944
1019
  if (!this.isAuthenticated || !this.userToken) {
945
- // No token found anywhere auto-trigger login instead of showing static message
1020
+ // No token found — check if we're in a sandbox (Cowork, containers)
1021
+ if (this.isSandboxed()) {
1022
+ console.error('[Polydev] No token and sandboxed — showing token-paste instructions');
1023
+ return {
1024
+ jsonrpc: '2.0',
1025
+ id,
1026
+ result: {
1027
+ content: [{
1028
+ type: 'text',
1029
+ text: `POLYDEV STATUS
1030
+ ==============
1031
+
1032
+ Authentication: Not connected (sandboxed environment detected)
1033
+
1034
+ Browser login is not available in this environment.
1035
+
1036
+ To authenticate, pass your token directly:
1037
+ 1. Get your token from https://polydev.ai/dashboard
1038
+ 2. Call get_auth_status with user_token parameter:
1039
+
1040
+ mcp__plugin_polydev_mcp__get_auth_status({ user_token: "pd_your_token_here" })
1041
+
1042
+ 3. Or set the environment variable before starting:
1043
+ POLYDEV_USER_TOKEN=pd_your_token_here`
1044
+ }]
1045
+ }
1046
+ };
1047
+ }
1048
+
1049
+ // Not sandboxed — auto-trigger browser login
946
1050
  console.error('[Polydev] No token found, auto-triggering login flow...');
947
1051
  return await this.triggerReAuth(id, 'Not authenticated. Opening browser for login...');
948
1052
  }
@@ -1071,6 +1175,29 @@ To re-login: /polydev:login`
1071
1175
  }
1072
1176
  }
1073
1177
 
1178
+ /**
1179
+ * Detect if running in a sandboxed environment (Cowork, containers, etc.)
1180
+ * where filesystem is read-only/limited and browser is unavailable.
1181
+ * Result is cached after first check.
1182
+ */
1183
+ isSandboxed() {
1184
+ if (this._isSandboxed !== null) return this._isSandboxed;
1185
+
1186
+ try {
1187
+ // Try writing a test file to home directory
1188
+ const testFile = path.join(os.homedir(), '.polydev-sandbox-test');
1189
+ fs.writeFileSync(testFile, 'test');
1190
+ fs.unlinkSync(testFile);
1191
+ this._isSandboxed = false;
1192
+ } catch (e) {
1193
+ // ENOSPC, EROFS, EACCES all indicate sandbox-like constraints
1194
+ console.error(`[Polydev] Sandbox detected: ${e.code || e.message}`);
1195
+ this._isSandboxed = true;
1196
+ }
1197
+
1198
+ return this._isSandboxed;
1199
+ }
1200
+
1074
1201
  /**
1075
1202
  * Open URL in browser - best-in-class cross-platform implementation
1076
1203
  * Uses 'open' npm package with fallbacks for reliability in MCP/stdio context
@@ -1133,27 +1260,39 @@ To re-login: /polydev:login`
1133
1260
  * Save token to shell config files
1134
1261
  */
1135
1262
  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');
1263
+ if (this.isSandboxed()) {
1264
+ // In sandbox, just set in-memory — can't write files
1265
+ console.error('[Polydev] Sandbox: skipping file writes, token set in-memory only');
1266
+ process.env.POLYDEV_USER_TOKEN = token;
1267
+ return;
1268
+ }
1140
1269
 
1141
- let content = '';
1142
- try { content = fs.readFileSync(rcFile, 'utf8'); } catch (e) {}
1270
+ try {
1271
+ const shell = process.env.SHELL || '/bin/zsh';
1272
+ const rcFile = shell.includes('zsh') ? path.join(os.homedir(), '.zshrc') :
1273
+ shell.includes('bash') ? path.join(os.homedir(), '.bashrc') :
1274
+ path.join(os.homedir(), '.profile');
1275
+
1276
+ let content = '';
1277
+ try { content = fs.readFileSync(rcFile, 'utf8'); } catch (e) {}
1143
1278
 
1144
- const lines = content.split('\n').filter(line =>
1145
- !line.trim().startsWith('export POLYDEV_USER_TOKEN') &&
1146
- !line.trim().startsWith('POLYDEV_USER_TOKEN')
1147
- );
1279
+ const lines = content.split('\n').filter(line =>
1280
+ !line.trim().startsWith('export POLYDEV_USER_TOKEN') &&
1281
+ !line.trim().startsWith('POLYDEV_USER_TOKEN')
1282
+ );
1148
1283
 
1149
- lines.push(`export POLYDEV_USER_TOKEN="${token}"`);
1150
- fs.writeFileSync(rcFile, lines.join('\n').replace(/\n+$/, '') + '\n');
1284
+ lines.push(`export POLYDEV_USER_TOKEN="${token}"`);
1285
+ fs.writeFileSync(rcFile, lines.join('\n').replace(/\n+$/, '') + '\n');
1151
1286
 
1152
- // Also save to .polydev.env
1153
- const envFile = path.join(os.homedir(), '.polydev.env');
1154
- fs.writeFileSync(envFile, `POLYDEV_USER_TOKEN="${token}"\n`);
1287
+ // Also save to .polydev.env
1288
+ const envFile = path.join(os.homedir(), '.polydev.env');
1289
+ fs.writeFileSync(envFile, `POLYDEV_USER_TOKEN="${token}"\n`);
1155
1290
 
1156
- console.error(`[Polydev] Token saved to ${rcFile} and ${envFile}`);
1291
+ console.error(`[Polydev] Token saved to ${rcFile} and ${envFile}`);
1292
+ } catch (e) {
1293
+ console.error(`[Polydev] Could not write token to files (${e.code || e.message}), set in-memory only`);
1294
+ process.env.POLYDEV_USER_TOKEN = token;
1295
+ }
1157
1296
  }
1158
1297
 
1159
1298
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.9.19",
3
+ "version": "1.9.21",
4
4
  "engines": {
5
5
  "node": ">=20.x <=22.x"
6
6
  },