gm-skill 2.0.2013 → 2.0.2014
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/gm-plugkit/package.json +1 -1
- package/gm-plugkit/wrapper/cdp-eval.js +123 -0
- package/gm.json +1 -1
- package/package.json +1 -1
package/gm-plugkit/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2014",
|
|
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": {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
|
|
4
|
+
function httpJson(url, timeoutMs) {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
const req = http.get(url, { timeout: timeoutMs }, (res) => {
|
|
7
|
+
let body = '';
|
|
8
|
+
res.on('data', (c) => { body += c; });
|
|
9
|
+
res.on('end', () => { try { resolve(JSON.parse(body)); } catch (_) { resolve(null); } });
|
|
10
|
+
});
|
|
11
|
+
req.on('error', () => resolve(null));
|
|
12
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function pickPageTarget(port, startUrl, timeoutMs) {
|
|
17
|
+
const deadline = Date.now() + timeoutMs;
|
|
18
|
+
while (Date.now() < deadline) {
|
|
19
|
+
const list = await httpJson(`http://127.0.0.1:${port}/json/list`, 2000);
|
|
20
|
+
if (Array.isArray(list)) {
|
|
21
|
+
const page = list.find((t) => t.type === 'page' && t.webSocketDebuggerUrl);
|
|
22
|
+
if (page) return page;
|
|
23
|
+
}
|
|
24
|
+
if (startUrl) {
|
|
25
|
+
const created = await httpJson(`http://127.0.0.1:${port}/json/new?${encodeURIComponent(startUrl)}`, 3000);
|
|
26
|
+
if (created && created.webSocketDebuggerUrl) return created;
|
|
27
|
+
}
|
|
28
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function cdpSession(wsUrl, timeoutMs) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const ws = new WebSocket(wsUrl);
|
|
36
|
+
let nextId = 1;
|
|
37
|
+
const pending = new Map();
|
|
38
|
+
const timer = setTimeout(() => { try { ws.close(); } catch (_) {} reject(new Error('cdp timeout')); }, timeoutMs);
|
|
39
|
+
ws.addEventListener('open', () => {
|
|
40
|
+
resolve({
|
|
41
|
+
send(method, params) {
|
|
42
|
+
const id = nextId++;
|
|
43
|
+
return new Promise((res, rej) => {
|
|
44
|
+
pending.set(id, { res, rej });
|
|
45
|
+
ws.send(JSON.stringify({ id, method, params: params || {} }));
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
close() { clearTimeout(timer); try { ws.close(); } catch (_) {} },
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
ws.addEventListener('message', (ev) => {
|
|
52
|
+
let msg;
|
|
53
|
+
try { msg = JSON.parse(ev.data); } catch (_) { return; }
|
|
54
|
+
if (msg.id && pending.has(msg.id)) {
|
|
55
|
+
const { res, rej } = pending.get(msg.id);
|
|
56
|
+
pending.delete(msg.id);
|
|
57
|
+
if (msg.error) rej(new Error(msg.error.message || 'cdp error'));
|
|
58
|
+
else res(msg.result);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
ws.addEventListener('error', () => { clearTimeout(timer); reject(new Error('cdp websocket error')); });
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Direct CDP evaluation, replacing the playwriter relay attach+eval that crashes
|
|
66
|
+
// with UV_HANDLE_CLOSING on Windows. Everything up to obtaining Chrome's CDP
|
|
67
|
+
// endpoint is already done by the wrapper (it launches Chrome with
|
|
68
|
+
// --remote-debugging-port and polls /json/version); this drives that endpoint
|
|
69
|
+
// directly over the DevTools websocket, so the crashing relay process is never
|
|
70
|
+
// spawned. Reads {port, startUrl, scriptFile, resultFile, timeoutMs} from argv[2]
|
|
71
|
+
// as JSON, runs the script via Runtime.evaluate (awaitPromise, returnByValue),
|
|
72
|
+
// and writes the returned value to resultFile -- the same result channel the
|
|
73
|
+
// playwriter path used.
|
|
74
|
+
async function main() {
|
|
75
|
+
const cfg = JSON.parse(process.argv[2]);
|
|
76
|
+
const { port, startUrl, scriptFile, resultFile, timeoutMs } = cfg;
|
|
77
|
+
const script = fs.readFileSync(scriptFile, 'utf-8');
|
|
78
|
+
const target = await pickPageTarget(port, startUrl, Math.min(timeoutMs, 30000));
|
|
79
|
+
if (!target) {
|
|
80
|
+
fs.writeFileSync(resultFile, JSON.stringify({ __cdpError: 'no page target on CDP endpoint' }));
|
|
81
|
+
process.stderr.write('cdp-eval: no page target\n');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
const sess = await cdpSession(target.webSocketDebuggerUrl, timeoutMs);
|
|
85
|
+
try {
|
|
86
|
+
await sess.send('Runtime.enable', {});
|
|
87
|
+
if (startUrl) {
|
|
88
|
+
await sess.send('Page.enable', {});
|
|
89
|
+
await sess.send('Page.navigate', { url: startUrl });
|
|
90
|
+
await new Promise((r) => setTimeout(r, 1200));
|
|
91
|
+
}
|
|
92
|
+
// The wrapper's eval body is a function body ending in `return __RET;`.
|
|
93
|
+
// Wrap it in an async IIFE so Runtime.evaluate resolves the returned value.
|
|
94
|
+
const wrapped = `(async () => { ${script} })()`;
|
|
95
|
+
const res = await sess.send('Runtime.evaluate', {
|
|
96
|
+
expression: wrapped,
|
|
97
|
+
awaitPromise: true,
|
|
98
|
+
returnByValue: true,
|
|
99
|
+
userGesture: true,
|
|
100
|
+
timeout: timeoutMs,
|
|
101
|
+
});
|
|
102
|
+
if (res.exceptionDetails) {
|
|
103
|
+
const msg = res.exceptionDetails.exception && res.exceptionDetails.exception.description
|
|
104
|
+
? res.exceptionDetails.exception.description
|
|
105
|
+
: (res.exceptionDetails.text || 'evaluate exception');
|
|
106
|
+
fs.writeFileSync(resultFile, JSON.stringify({ __cdpError: msg }));
|
|
107
|
+
process.stderr.write(`cdp-eval: exception ${msg}\n`);
|
|
108
|
+
sess.close();
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
const value = res.result && ('value' in res.result) ? res.result.value : null;
|
|
112
|
+
fs.writeFileSync(resultFile, JSON.stringify(value === undefined ? null : value));
|
|
113
|
+
sess.close();
|
|
114
|
+
process.exit(0);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
fs.writeFileSync(resultFile, JSON.stringify({ __cdpError: String(e && e.message || e) }));
|
|
117
|
+
process.stderr.write(`cdp-eval: ${e && e.message || e}\n`);
|
|
118
|
+
try { sess.close(); } catch (_) {}
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
main();
|
package/gm.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2014",
|
|
4
4
|
"description": "Canonical universal harness — AI-native software engineering via skill-driven orchestration; bootstraps plugkit for task execution and session isolation. Install in any AI coding agent host.",
|
|
5
5
|
"author": "AnEntrypoint",
|
|
6
6
|
"license": "MIT",
|