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 +4 -0
- package/mcp/stdio-wrapper.js +159 -20
- package/package.json +1 -1
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
|
},
|
package/mcp/stdio-wrapper.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
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
|
-
|
|
1142
|
-
|
|
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
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
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
|
-
|
|
1150
|
-
|
|
1284
|
+
lines.push(`export POLYDEV_USER_TOKEN="${token}"`);
|
|
1285
|
+
fs.writeFileSync(rcFile, lines.join('\n').replace(/\n+$/, '') + '\n');
|
|
1151
1286
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
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
|
-
|
|
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
|
/**
|