gm-plugkit 2.0.1159 → 2.0.1161
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/bootstrap.js +58 -17
- package/package.json +1 -1
- package/plugkit-wasm-wrapper.js +50 -2
- package/plugkit.sha256 +1 -1
- package/plugkit.version +1 -1
package/bootstrap.js
CHANGED
|
@@ -153,6 +153,23 @@ function sha256OfFile(filePath) {
|
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
function resolveNpxJsCli() {
|
|
157
|
+
if (process.platform !== 'win32') return null;
|
|
158
|
+
const candidates = [];
|
|
159
|
+
if (process.env.npm_config_prefix) {
|
|
160
|
+
candidates.push(path.join(process.env.npm_config_prefix, 'node_modules', 'npm', 'bin', 'npx-cli.js'));
|
|
161
|
+
}
|
|
162
|
+
const programFiles = process.env.ProgramFiles || 'C:\\Program Files';
|
|
163
|
+
candidates.push(path.join(programFiles, 'nodejs', 'node_modules', 'npm', 'bin', 'npx-cli.js'));
|
|
164
|
+
candidates.push(path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npx-cli.js'));
|
|
165
|
+
const appdata = process.env.APPDATA;
|
|
166
|
+
if (appdata) candidates.push(path.join(appdata, 'npm', 'node_modules', 'npm', 'bin', 'npx-cli.js'));
|
|
167
|
+
for (const c of candidates) {
|
|
168
|
+
try { if (fs.existsSync(c)) return c; } catch (_) {}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
156
173
|
async function extractNpmPackageWasm(destPath, version) {
|
|
157
174
|
const tempDir = path.join(path.dirname(destPath), '.npm-extract-' + Date.now());
|
|
158
175
|
try {
|
|
@@ -161,16 +178,28 @@ async function extractNpmPackageWasm(destPath, version) {
|
|
|
161
178
|
log(`extracting npm package ${NPM_PACKAGE}@${version} to ${tempDir}`);
|
|
162
179
|
obsEvent('bootstrap', 'npm.extract.start', { package: NPM_PACKAGE, version });
|
|
163
180
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
{
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
181
|
+
let cmd, args;
|
|
182
|
+
if (process.platform === 'win32') {
|
|
183
|
+
const npxCli = resolveNpxJsCli();
|
|
184
|
+
if (npxCli) {
|
|
185
|
+
cmd = process.execPath;
|
|
186
|
+
args = [npxCli, NPM_PACKAGE + '@' + version, '--prefix', tempDir];
|
|
187
|
+
} else {
|
|
188
|
+
cmd = 'npx.cmd';
|
|
189
|
+
args = [NPM_PACKAGE + '@' + version, '--prefix', tempDir];
|
|
172
190
|
}
|
|
173
|
-
|
|
191
|
+
} else {
|
|
192
|
+
cmd = 'npx';
|
|
193
|
+
args = [NPM_PACKAGE + '@' + version, '--prefix', tempDir];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const result = spawnSync(cmd, args, {
|
|
197
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
198
|
+
timeout: ATTEMPT_TIMEOUT_MS,
|
|
199
|
+
encoding: 'utf8',
|
|
200
|
+
windowsHide: true,
|
|
201
|
+
shell: process.platform === 'win32' && cmd === 'npx.cmd',
|
|
202
|
+
});
|
|
174
203
|
|
|
175
204
|
if (result.error) throw result.error;
|
|
176
205
|
if (result.status !== 0) {
|
|
@@ -192,25 +221,37 @@ async function extractNpmPackageWasm(destPath, version) {
|
|
|
192
221
|
|
|
193
222
|
function httpGetBuffer(url, timeoutMs) {
|
|
194
223
|
const https = require('https');
|
|
224
|
+
const idleTimeoutMs = timeoutMs || 30000;
|
|
225
|
+
const totalDeadlineMs = (timeoutMs || 30000) * 2;
|
|
195
226
|
return new Promise((resolve, reject) => {
|
|
196
|
-
|
|
227
|
+
let bytesReceived = 0;
|
|
228
|
+
let settled = false;
|
|
229
|
+
const settleReject = (err) => { if (!settled) { settled = true; reject(err); } };
|
|
230
|
+
const settleResolve = (v) => { if (!settled) { settled = true; resolve(v); } };
|
|
231
|
+
const absTimer = setTimeout(() => {
|
|
232
|
+
try { req.destroy(new Error(`abs-deadline ${totalDeadlineMs}ms ${url} after ${bytesReceived} bytes`)); } catch (_) {}
|
|
233
|
+
settleReject(new Error(`abs-deadline ${totalDeadlineMs}ms ${url} after ${bytesReceived} bytes`));
|
|
234
|
+
}, totalDeadlineMs);
|
|
235
|
+
const req = https.get(url, { timeout: idleTimeoutMs, headers: { 'user-agent': 'gm-plugkit-bootstrap' } }, (res) => {
|
|
197
236
|
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
198
237
|
res.resume();
|
|
199
|
-
|
|
238
|
+
clearTimeout(absTimer);
|
|
239
|
+
httpGetBuffer(res.headers.location, timeoutMs).then(settleResolve, settleReject);
|
|
200
240
|
return;
|
|
201
241
|
}
|
|
202
242
|
if (res.statusCode !== 200) {
|
|
203
243
|
res.resume();
|
|
204
|
-
|
|
244
|
+
clearTimeout(absTimer);
|
|
245
|
+
settleReject(new Error(`HTTP ${res.statusCode} ${url}`));
|
|
205
246
|
return;
|
|
206
247
|
}
|
|
207
248
|
const chunks = [];
|
|
208
|
-
res.on('data', c => chunks.push(c));
|
|
209
|
-
res.on('end', () =>
|
|
210
|
-
res.on('error',
|
|
249
|
+
res.on('data', c => { chunks.push(c); bytesReceived += c.length; });
|
|
250
|
+
res.on('end', () => { clearTimeout(absTimer); settleResolve(Buffer.concat(chunks)); });
|
|
251
|
+
res.on('error', (e) => { clearTimeout(absTimer); settleReject(e); });
|
|
211
252
|
});
|
|
212
|
-
req.on('timeout', () => req.destroy(new Error(`timeout ${url}`)));
|
|
213
|
-
req.on('error',
|
|
253
|
+
req.on('timeout', () => { try { req.destroy(new Error(`idle-timeout ${idleTimeoutMs}ms ${url}`)); } catch (_) {} settleReject(new Error(`idle-timeout ${idleTimeoutMs}ms ${url}`)); });
|
|
254
|
+
req.on('error', (e) => { clearTimeout(absTimer); settleReject(e); });
|
|
214
255
|
});
|
|
215
256
|
}
|
|
216
257
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1161",
|
|
4
4
|
"description": "Bootstrap and daemon-spawn tool for gm plugkit binary. Downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Includes plugkit-wasm-wrapper for WASM-based spool watching.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
package/plugkit-wasm-wrapper.js
CHANGED
|
@@ -932,6 +932,22 @@ function resolveVersion(instance) {
|
|
|
932
932
|
return 'unknown';
|
|
933
933
|
}
|
|
934
934
|
|
|
935
|
+
function readFileVersionOnly() {
|
|
936
|
+
try { return fs.readFileSync(path.join(os.homedir(), '.claude', 'gm-tools', 'plugkit.version'), 'utf8').trim(); } catch (_) { return null; }
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
function readInstanceVersion(instance) {
|
|
940
|
+
try {
|
|
941
|
+
const fn = instance && instance.exports && instance.exports.plugkit_version;
|
|
942
|
+
if (typeof fn !== 'function') return null;
|
|
943
|
+
const result = fn();
|
|
944
|
+
const ptr = Number(result & 0xffffffffn);
|
|
945
|
+
const len = Number(result >> 32n);
|
|
946
|
+
const bytes = new Uint8Array(instance.exports.memory.buffer, ptr, len);
|
|
947
|
+
return new TextDecoder().decode(bytes).trim();
|
|
948
|
+
} catch (_) { return null; }
|
|
949
|
+
}
|
|
950
|
+
|
|
935
951
|
async function runSpoolWatcher(instance, spoolDir) {
|
|
936
952
|
const inDir = path.join(spoolDir, 'in');
|
|
937
953
|
const outDir = path.join(spoolDir, 'out');
|
|
@@ -947,8 +963,11 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
947
963
|
const lockTs = parseInt(tsStr, 10);
|
|
948
964
|
const age = Date.now() - lockTs;
|
|
949
965
|
if (age < 15000) {
|
|
950
|
-
|
|
951
|
-
|
|
966
|
+
const msg = JSON.stringify({ ok: false, reason: 'another-watcher-active', pid: pidStr, age_ms: age });
|
|
967
|
+
console.error(`[plugkit-wasm] ${msg}; refusing to start`);
|
|
968
|
+
try { fs.writeFileSync(path.join(spoolDir, '.lock-rejection.json'), msg); } catch (_) {}
|
|
969
|
+
try { logEvent('plugkit', 'watcher.lock-rejected', { holder_pid: pidStr, lock_age_ms: age }); } catch (_) {}
|
|
970
|
+
process.exit(75);
|
|
952
971
|
}
|
|
953
972
|
console.error(`[plugkit-wasm] stale lock (age=${age}ms); taking over`);
|
|
954
973
|
}
|
|
@@ -1035,6 +1054,35 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
1035
1054
|
try { reapTimedOutTasks(); } catch (_) {}
|
|
1036
1055
|
}, 5000);
|
|
1037
1056
|
|
|
1057
|
+
const _instanceVersionAtBoot = readInstanceVersion(instance);
|
|
1058
|
+
setInterval(() => {
|
|
1059
|
+
try {
|
|
1060
|
+
const fileV = readFileVersionOnly();
|
|
1061
|
+
const instV = _instanceVersionAtBoot;
|
|
1062
|
+
if (!fileV || !instV || fileV === instV) return;
|
|
1063
|
+
logEvent('plugkit', 'version.drift', {
|
|
1064
|
+
instance_version: instV,
|
|
1065
|
+
file_version: fileV,
|
|
1066
|
+
action: 'exit-for-respawn',
|
|
1067
|
+
});
|
|
1068
|
+
console.error(`[plugkit-wasm] version drift detected: instance=${instV} file=${fileV} → exiting so supervisor reloads fresh wasm`);
|
|
1069
|
+
try {
|
|
1070
|
+
fs.writeFileSync(path.join(spoolDir, '.shutdown-reason.json'), JSON.stringify({
|
|
1071
|
+
reason: 'version-change',
|
|
1072
|
+
ts: Date.now(),
|
|
1073
|
+
pid: process.pid,
|
|
1074
|
+
instance_version: instV,
|
|
1075
|
+
file_version: fileV,
|
|
1076
|
+
}));
|
|
1077
|
+
} catch (_) {}
|
|
1078
|
+
try { releaseLock(); } catch (_) {}
|
|
1079
|
+
try { fs.unlinkSync(STATUS_PATH_FOR_TEARDOWN); } catch (_) {}
|
|
1080
|
+
process.exit(0);
|
|
1081
|
+
} catch (e) {
|
|
1082
|
+
console.error(`[version-drift-check] error: ${e.message}`);
|
|
1083
|
+
}
|
|
1084
|
+
}, 60_000);
|
|
1085
|
+
|
|
1038
1086
|
setInterval(() => {
|
|
1039
1087
|
try {
|
|
1040
1088
|
const idleMs = Date.now() - lastActivityMs;
|
package/plugkit.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"plugkit.wasm":"
|
|
1
|
+
{"plugkit.wasm":"623152434c8b0f75b20cb89f48551e3a1677f132275e0fc43e261db7a69afeed"}
|
package/plugkit.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.419
|