polydev-ai 1.9.38 → 1.9.40
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/lib/tunnelClient.js +60 -2
- package/mcp/stdio-wrapper.js +5 -1
- package/package.json +1 -1
package/lib/tunnelClient.js
CHANGED
|
@@ -21,11 +21,60 @@ class TunnelClient {
|
|
|
21
21
|
this.pollInterval = null;
|
|
22
22
|
this._processing = new Set(); // track in-flight request IDs
|
|
23
23
|
this._started = false;
|
|
24
|
+
this._consecutive401s = 0; // track auth failures for token reload
|
|
24
25
|
|
|
25
26
|
// Configurable intervals
|
|
26
27
|
this.HEARTBEAT_INTERVAL_MS = 30_000; // 30s
|
|
27
28
|
this.POLL_INTERVAL_MS = 3_000; // 3s
|
|
28
29
|
this.CLI_TIMEOUT_MS = 120_000; // 2 min per request
|
|
30
|
+
|
|
31
|
+
// Try to load freshest token from file on construction
|
|
32
|
+
// (env var may be stale if process was started long ago)
|
|
33
|
+
this._reloadTokenFromFile();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Reload token from ~/.polydev.env file.
|
|
38
|
+
* The env var POLYDEV_USER_TOKEN may be stale (set when the IDE started the process).
|
|
39
|
+
* The file is always updated by the latest login.
|
|
40
|
+
*/
|
|
41
|
+
_reloadTokenFromFile() {
|
|
42
|
+
try {
|
|
43
|
+
const fs = require('fs');
|
|
44
|
+
const path = require('path');
|
|
45
|
+
const os = require('os');
|
|
46
|
+
const envFile = path.join(os.homedir(), '.polydev.env');
|
|
47
|
+
|
|
48
|
+
if (!fs.existsSync(envFile)) return false;
|
|
49
|
+
|
|
50
|
+
const content = fs.readFileSync(envFile, 'utf8');
|
|
51
|
+
const match = content.match(/POLYDEV_USER_TOKEN[=\s]["']?([^"'\n]+)["']?/);
|
|
52
|
+
if (match && match[1] && (match[1].startsWith('pd_') || match[1].startsWith('polydev_'))) {
|
|
53
|
+
if (match[1] !== this.authToken) {
|
|
54
|
+
console.error(`[Tunnel] Token reloaded from ${envFile} (was stale)`);
|
|
55
|
+
this.authToken = match[1];
|
|
56
|
+
this._consecutive401s = 0;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// ignore file read errors
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Handle a 401 response — try reloading token from file
|
|
68
|
+
*/
|
|
69
|
+
_handle401() {
|
|
70
|
+
this._consecutive401s++;
|
|
71
|
+
// Try reload every 5 consecutive 401s (every ~15s at 3s poll interval)
|
|
72
|
+
if (this._consecutive401s % 5 === 1) {
|
|
73
|
+
const reloaded = this._reloadTokenFromFile();
|
|
74
|
+
if (reloaded) {
|
|
75
|
+
console.error('[Tunnel] Token refreshed after 401 — retrying');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
29
78
|
}
|
|
30
79
|
|
|
31
80
|
/**
|
|
@@ -36,6 +85,7 @@ class TunnelClient {
|
|
|
36
85
|
this._started = true;
|
|
37
86
|
|
|
38
87
|
console.error('[Tunnel] Starting CLI-as-API tunnel client');
|
|
88
|
+
console.error(`[Tunnel] Auth token prefix: ${this.authToken ? this.authToken.substring(0, 8) + '...' : 'NONE'}`);
|
|
39
89
|
|
|
40
90
|
// Send initial heartbeat immediately
|
|
41
91
|
try {
|
|
@@ -102,9 +152,14 @@ class TunnelClient {
|
|
|
102
152
|
});
|
|
103
153
|
|
|
104
154
|
if (!res.ok) {
|
|
155
|
+
if (res.status === 401) {
|
|
156
|
+
this._handle401();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
105
159
|
const text = await res.text().catch(() => '');
|
|
106
160
|
throw new Error(`Heartbeat failed (${res.status}): ${text}`);
|
|
107
161
|
}
|
|
162
|
+
this._consecutive401s = 0; // reset on success
|
|
108
163
|
}
|
|
109
164
|
|
|
110
165
|
/**
|
|
@@ -120,11 +175,14 @@ class TunnelClient {
|
|
|
120
175
|
});
|
|
121
176
|
|
|
122
177
|
if (!res.ok) {
|
|
123
|
-
|
|
124
|
-
|
|
178
|
+
if (res.status === 401) {
|
|
179
|
+
this._handle401();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
125
182
|
const text = await res.text().catch(() => '');
|
|
126
183
|
throw new Error(`Poll failed (${res.status}): ${text}`);
|
|
127
184
|
}
|
|
185
|
+
this._consecutive401s = 0; // reset on success
|
|
128
186
|
|
|
129
187
|
const data = await res.json();
|
|
130
188
|
const requests = data.requests || [];
|
package/mcp/stdio-wrapper.js
CHANGED
|
@@ -2770,7 +2770,7 @@ To re-login: /polydev:login`
|
|
|
2770
2770
|
const statusFile = path.join(polydevevDir, 'cli-status.json');
|
|
2771
2771
|
|
|
2772
2772
|
// Ensure directory exists
|
|
2773
|
-
if (!fs.existsSync(
|
|
2773
|
+
if (!fs.existsSync(polydevevDir)) {
|
|
2774
2774
|
fs.mkdirSync(polydeveevDir, { recursive: true });
|
|
2775
2775
|
}
|
|
2776
2776
|
|
|
@@ -2962,6 +2962,10 @@ To re-login: /polydev:login`
|
|
|
2962
2962
|
return; // No auth, skip tunnel
|
|
2963
2963
|
}
|
|
2964
2964
|
|
|
2965
|
+
// Reload token from file to get the freshest one
|
|
2966
|
+
// (env var may be stale if IDE started this process long ago)
|
|
2967
|
+
this.reloadTokenFromFiles();
|
|
2968
|
+
|
|
2965
2969
|
try {
|
|
2966
2970
|
this.tunnelClient = new TunnelClient(
|
|
2967
2971
|
this.serverUrl, // https://www.polydev.ai/api/mcp
|