gm-skill 2.0.1978 → 2.0.1980

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.
@@ -1 +1 @@
1
- 0.1.905
1
+ 0.1.906
@@ -1 +1 @@
1
- 5e637cfefaddd54c2fdf06d6e718bc8834b6c026fed6e542ef1d18de2b5bebfc plugkit.wasm
1
+ b78a9260d01e88d27b0841c0831db4a2f3adbbf3d051de99d4fd90a7ab5229d7 plugkit.wasm
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1978",
3
+ "version": "2.0.1980",
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": {
@@ -17,6 +17,20 @@ const _sharedLogEvent = _gmLog.logEvent;
17
17
  const _sharedGmLogRoot = _gmLog.GM_LOG_ROOT;
18
18
  import _gmProcess from './gm-process.js';
19
19
  const _sharedPidCommandLine = _gmProcess.pidCommandLineForKillGuard;
20
+ import { wasiFilesystemRootFor, createWasiShim } from './wrapper/wasi-shim.js';
21
+ import {
22
+ guardWasmRange,
23
+ decodeWasmResult,
24
+ writeWasmInput,
25
+ readWasmBytes,
26
+ readWasmStr,
27
+ writeWasmBytes,
28
+ writeWasmStr,
29
+ writeWasmJson,
30
+ } from './wrapper/wasm-bridge.js';
31
+ import { atomicWriteRaw, atomicWriteJson, readJsonFile, writeJsonFile } from './wrapper/fs-atomic.js';
32
+ import { safeName, projectKvDir, enabledDisciplineNamespaces, jaccardOverlap, makeKvHelpers } from './wrapper/kv-store.js';
33
+ import { makeTaskManager } from './wrapper/task-manager.js';
20
34
 
21
35
  let _writeStatusBusy = () => {};
22
36
  let _lastBusyUntil = 0;
@@ -612,21 +626,6 @@ function stampBrowserLastUse(cwd, claudeSessionId) {
612
626
  } catch (_) {}
613
627
  }
614
628
 
615
- function atomicWriteRaw(filePath, data) {
616
- const tmp = filePath + '.tmp.' + process.pid + '.' + Date.now() + '.' + Math.random().toString(36).slice(2, 8);
617
- fs.writeFileSync(tmp, data);
618
- try {
619
- fs.renameSync(tmp, filePath);
620
- } catch (err) {
621
- try { fs.unlinkSync(tmp); } catch (_) {}
622
- throw err;
623
- }
624
- }
625
-
626
- function atomicWriteJson(filePath, obj) {
627
- atomicWriteRaw(filePath, JSON.stringify(obj, null, 2));
628
- }
629
-
630
629
  function migrateLegacyBrowserState(cwd) {
631
630
  const dst1 = browserPortsFile(cwd);
632
631
  const dst2 = browserSessionsFile(cwd);
@@ -653,13 +652,6 @@ function migrateLegacyBrowserState(cwd) {
653
652
  } catch (_) {}
654
653
  }
655
654
 
656
- function readJsonFile(fp, fallback) {
657
- try { return JSON.parse(fs.readFileSync(fp, 'utf-8')); } catch (_) { return fallback; }
658
- }
659
- function writeJsonFile(fp, value) {
660
- try { atomicWriteJson(fp, value); } catch (_) {}
661
- }
662
-
663
655
  const AGGREGATE_CPU_PROFILE_SRC = `function aggregateCpuProfile(profile, topN, isBrowserCtx) {
664
656
  const N = topN || 20;
665
657
  if (!profile || !Array.isArray(profile.nodes) || !Array.isArray(profile.samples)) {
@@ -1772,737 +1764,45 @@ function cosineSim(a, b) {
1772
1764
  }
1773
1765
 
1774
1766
  let __wasmAbortFlag = { aborted: false, code: 0 };
1775
- const WASI_PROJECT_SLUG = crypto.createHash('sha256').update(String(process.env.CLAUDE_PROJECT_DIR || process.cwd()).toLowerCase().replace(/\\/g, '/')).digest('hex').slice(0, 16);
1776
- const WASI_FILESYSTEM_ROOT = path.join(GM_TOOLS_ROOT, 'wasi-fs', WASI_PROJECT_SLUG);
1767
+ const WASI_FILESYSTEM_ROOT = wasiFilesystemRootFor(GM_TOOLS_ROOT);
1777
1768
  const wasiOpenFiles = new Map();
1778
1769
  let wasiNextFd = 100;
1779
-
1780
- function wasiResolvePath(relPath) {
1781
- const rel = String(relPath || '').replace(/\\/g, '/').replace(/^\/+/, '');
1782
- const resolved = path.resolve(WASI_FILESYSTEM_ROOT, rel);
1783
- const rootResolved = path.resolve(WASI_FILESYSTEM_ROOT) + path.sep;
1784
- if (resolved !== path.resolve(WASI_FILESYSTEM_ROOT) && !resolved.startsWith(rootResolved)) {
1785
- throw new Error(`wasi-path-traversal-refused: ${relPath} escapes ${WASI_FILESYSTEM_ROOT}`);
1786
- }
1787
- return resolved;
1788
- }
1789
-
1790
- function createWasiShim(instanceRef) {
1791
- const getMemory = () => instanceRef.value.exports.memory.buffer;
1792
- const shim = {
1793
- proc_exit: (code) => {
1794
- __wasmAbortFlag.aborted = true;
1795
- __wasmAbortFlag.code = code;
1796
- try {
1797
- const spoolDir = spoolDirForSentinel();
1798
- fs.mkdirSync(spoolDir, { recursive: true });
1799
- fs.writeFileSync(path.join(spoolDir, '.wasm-abort.json'), JSON.stringify({
1800
- ts: Date.now(),
1801
- exit_code: code,
1802
- verb_in_flight: __currentVerbContext,
1803
- }));
1804
- } catch (_) {}
1805
- try { console.error(`[plugkit-wasm] wasm proc_exit(${code}) intercepted; throwing to abort current verb without killing watcher`); } catch (_) {}
1806
- throw new Error(`wasm proc_exit(${code}) during verb ${__currentVerbContext && __currentVerbContext.verb}`);
1807
- },
1808
- fd_write: (fd, iovs_ptr, iovs_len, nwritten_ptr) => {
1809
- try {
1810
- const buf = getMemory();
1811
- const dv = new DataView(buf);
1812
- const chunks = [];
1813
- let total = 0;
1814
- const iovsBase = iovs_ptr >>> 0; // >>>0: high-bit iovs pointer is negative in JS -> getUint32 would throw
1815
- for (let i = 0; i < iovs_len; i++) {
1816
- const base = iovsBase + i * 8;
1817
- const ptr = dv.getUint32(base, true);
1818
- const len = dv.getUint32(base + 4, true);
1819
- if (len > 0 && ptr + len <= buf.byteLength) {
1820
- chunks.push(new Uint8Array(buf, ptr, len).slice());
1821
- total += len;
1822
- }
1823
- }
1824
- const merged = new Uint8Array(total);
1825
- let off = 0;
1826
- for (const c of chunks) { merged.set(c, off); off += c.length; }
1827
- const text = new TextDecoder('utf-8').decode(merged);
1828
- if (fd === 2) process.stderr.write(text);
1829
- else process.stdout.write(text);
1830
- new DataView(getMemory()).setUint32(nwritten_ptr, total, true);
1831
- return 0;
1832
- } catch (e) {
1833
- return 28;
1834
- }
1835
- },
1836
- random_get: (buf_ptr, buf_len) => {
1837
- try {
1838
- crypto.randomFillSync(new Uint8Array(getMemory(), buf_ptr >>> 0, buf_len >>> 0)); // >>>0: high-bit ptr is negative in JS
1839
- return 0;
1840
- } catch (e) {
1841
- return 28;
1842
- }
1843
- },
1844
- clock_time_get: (clock_id, precision, time_ptr) => {
1845
- try {
1846
- const ns = BigInt(Date.now()) * 1000000n;
1847
- new DataView(getMemory()).setBigUint64(time_ptr >>> 0, ns, true); // >>>0: high-bit ptr is negative in JS
1848
- return 0;
1849
- } catch (e) {
1850
- return 28;
1851
- }
1852
- },
1853
- environ_get: () => 0,
1854
- environ_sizes_get: () => 0,
1855
- fd_prestat_get: (fd, buf_ptr) => {
1856
- if (fd !== 3) return 8;
1857
- try {
1858
- const dv = new DataView(getMemory());
1859
- dv.setUint8(buf_ptr, 0);
1860
- dv.setUint32(buf_ptr + 4, 1, true);
1861
- return 0;
1862
- } catch (e) { return 8; }
1863
- },
1864
- fd_prestat_dir_name: (fd, path_ptr, path_len) => {
1865
- if (fd !== 3) return 8;
1866
- try {
1867
- const buf = getMemory();
1868
- new Uint8Array(buf, path_ptr >>> 0, Math.min(path_len, 1)).set([0x2e]);
1869
- return 0;
1870
- } catch (e) { return 8; }
1871
- },
1872
- fd_close: (fd) => {
1873
- const entry = wasiOpenFiles.get(fd);
1874
- if (!entry) return 0;
1875
- try { fs.closeSync(entry.nodeFd); } catch (_) {}
1876
- wasiOpenFiles.delete(fd);
1877
- return 0;
1878
- },
1879
- fd_fdstat_get: (fd, stat_ptr) => {
1880
- try {
1881
- const dv = new DataView(getMemory());
1882
- const entry = wasiOpenFiles.get(fd);
1883
- dv.setUint8(stat_ptr, entry ? 4 : 0);
1884
- dv.setUint8(stat_ptr + 1, 0);
1885
- dv.setBigUint64(stat_ptr + 8, 0xffffffffffffffffn, true);
1886
- dv.setBigUint64(stat_ptr + 16, 0xffffffffffffffffn, true);
1887
- return 0;
1888
- } catch (e) { return 8; }
1889
- },
1890
- fd_fdstat_set_flags: () => 0,
1891
- fd_filestat_get: (fd, buf_ptr) => {
1892
- const entry = wasiOpenFiles.get(fd);
1893
- if (!entry) { if (process.env.PLUGKIT_DEBUG) console.error(`[plugkit-wasm] fd_filestat_get FAILED: no entry for fd=${fd}`); return 8; }
1894
- try {
1895
- const st = fs.fstatSync(entry.nodeFd);
1896
- const dv = new DataView(getMemory());
1897
- dv.setBigUint64(buf_ptr, 0n, true);
1898
- dv.setBigUint64(buf_ptr + 8, 0n, true);
1899
- dv.setUint8(buf_ptr + 16, 4);
1900
- dv.setBigUint64(buf_ptr + 24, 1n, true);
1901
- dv.setBigUint64(buf_ptr + 32, BigInt(st.size), true);
1902
- dv.setBigUint64(buf_ptr + 40, BigInt(Math.floor(st.atimeMs * 1e6)), true);
1903
- dv.setBigUint64(buf_ptr + 48, BigInt(Math.floor(st.mtimeMs * 1e6)), true);
1904
- dv.setBigUint64(buf_ptr + 56, BigInt(Math.floor(st.ctimeMs * 1e6)), true);
1905
- return 0;
1906
- } catch (e) { if (process.env.PLUGKIT_DEBUG) console.error(`[plugkit-wasm] fd_filestat_get FAILED: ${e && e.message}`); return 8; }
1907
- },
1908
- fd_seek: (fd, offset64, whence, newoffset_ptr) => {
1909
- const entry = wasiOpenFiles.get(fd);
1910
- if (!entry) { try { new DataView(getMemory()).setBigUint64(newoffset_ptr, 0n, true); } catch (_) {} return 8; }
1911
- try {
1912
- const offset = BigInt.asIntN(64, BigInt(offset64));
1913
- let base;
1914
- if (whence === 0) base = 0n;
1915
- else if (whence === 1) base = BigInt(entry.pos);
1916
- else base = BigInt(fs.fstatSync(entry.nodeFd).size);
1917
- const next = base + offset;
1918
- entry.pos = Number(next < 0n ? 0n : next);
1919
- new DataView(getMemory()).setBigUint64(newoffset_ptr, BigInt(entry.pos), true);
1920
- return 0;
1921
- } catch (e) { if (process.env.PLUGKIT_DEBUG) console.error(`[plugkit-wasm] fd_seek FAILED: ${e && e.message}`); return 8; }
1922
- },
1923
- fd_read: (fd, iovs_ptr, iovs_len, nread_ptr) => {
1924
- const entry = wasiOpenFiles.get(fd);
1925
- if (!entry) { try { new DataView(getMemory()).setUint32(nread_ptr, 0, true); } catch (_) {} return 8; }
1926
- try {
1927
- const buf = getMemory();
1928
- const dv = new DataView(buf);
1929
- let total = 0;
1930
- const iovsBase = iovs_ptr >>> 0;
1931
- for (let i = 0; i < iovs_len; i++) {
1932
- const base = iovsBase + i * 8;
1933
- const ptr = dv.getUint32(base, true) >>> 0;
1934
- const len = dv.getUint32(base + 4, true) >>> 0;
1935
- if (len === 0) continue;
1936
- const dest = Buffer.from(buf, ptr, len);
1937
- const n = fs.readSync(entry.nodeFd, dest, 0, len, entry.pos);
1938
- entry.pos += n;
1939
- total += n;
1940
- if (n < len) break;
1941
- }
1942
- dv.setUint32(nread_ptr, total, true);
1943
- return 0;
1944
- } catch (e) { return 8; }
1945
- },
1946
- fd_pread: (fd, iovs_ptr, iovs_len, offset64, nread_ptr) => {
1947
- const entry = wasiOpenFiles.get(fd);
1948
- if (!entry) { try { new DataView(getMemory()).setUint32(nread_ptr, 0, true); } catch (_) {} return 8; }
1949
- try {
1950
- const offset = Number(BigInt.asUintN(64, BigInt(offset64)));
1951
- const buf = getMemory();
1952
- const dv = new DataView(buf);
1953
- let total = 0;
1954
- const iovsBase = iovs_ptr >>> 0;
1955
- let pos = offset;
1956
- for (let i = 0; i < iovs_len; i++) {
1957
- const base = iovsBase + i * 8;
1958
- const ptr = dv.getUint32(base, true) >>> 0;
1959
- const len = dv.getUint32(base + 4, true) >>> 0;
1960
- if (len === 0) continue;
1961
- const dest = Buffer.from(buf, ptr, len);
1962
- const n = fs.readSync(entry.nodeFd, dest, 0, len, pos);
1963
- pos += n;
1964
- total += n;
1965
- if (n < len) break;
1966
- }
1967
- dv.setUint32(nread_ptr, total, true);
1968
- return 0;
1969
- } catch (e) { if (process.env.PLUGKIT_DEBUG) console.error(`[plugkit-wasm] fd_pread FAILED: ${e && e.message}`); return 8; }
1970
- },
1971
- fd_pwrite: (fd, iovs_ptr, iovs_len, offset64, nwritten_ptr) => {
1972
- const entry = wasiOpenFiles.get(fd);
1973
- if (!entry) { try { new DataView(getMemory()).setUint32(nwritten_ptr, 0, true); } catch (_) {} return 8; }
1974
- try {
1975
- const offset = Number(BigInt.asUintN(64, BigInt(offset64)));
1976
- const buf = getMemory();
1977
- const dv = new DataView(buf);
1978
- let total = 0;
1979
- const iovsBase = iovs_ptr >>> 0;
1980
- let pos = offset;
1981
- for (let i = 0; i < iovs_len; i++) {
1982
- const base = iovsBase + i * 8;
1983
- const ptr = dv.getUint32(base, true) >>> 0;
1984
- const len = dv.getUint32(base + 4, true) >>> 0;
1985
- if (len === 0) continue;
1986
- const src = Buffer.from(buf, ptr, len);
1987
- const n = fs.writeSync(entry.nodeFd, src, 0, len, pos);
1988
- pos += n;
1989
- total += n;
1990
- }
1991
- dv.setUint32(nwritten_ptr, total, true);
1992
- return 0;
1993
- } catch (e) { if (process.env.PLUGKIT_DEBUG) console.error(`[plugkit-wasm] fd_pwrite FAILED: ${e && e.message}`); return 8; }
1994
- },
1995
- fd_sync: (fd) => {
1996
- const entry = wasiOpenFiles.get(fd);
1997
- if (!entry) return 8;
1998
- try { fs.fsyncSync(entry.nodeFd); return 0; } catch (e) { if (process.env.PLUGKIT_DEBUG) console.error(`[plugkit-wasm] fd_sync FAILED: ${e && e.message}`); return 8; }
1999
- },
2000
- fd_datasync: (fd) => {
2001
- const entry = wasiOpenFiles.get(fd);
2002
- if (!entry) return 8;
2003
- try { fs.fdatasyncSync(entry.nodeFd); return 0; } catch (e) { return 8; }
2004
- },
2005
- fd_filestat_set_size: (fd, size64) => {
2006
- const entry = wasiOpenFiles.get(fd);
2007
- if (!entry) return 8;
2008
- try {
2009
- const size = Number(BigInt.asUintN(64, BigInt(size64)));
2010
- fs.ftruncateSync(entry.nodeFd, size);
2011
- return 0;
2012
- } catch (e) { if (process.env.PLUGKIT_DEBUG) console.error(`[plugkit-wasm] fd_filestat_set_size FAILED: ${e && e.message}`); return 8; }
2013
- },
2014
- path_create_directory: (_dirfd, path_ptr, path_len) => {
2015
- try {
2016
- const buf = getMemory();
2017
- const relPath = new TextDecoder('utf-8').decode(new Uint8Array(buf, path_ptr >>> 0, path_len >>> 0));
2018
- const absPath = wasiResolvePath(relPath);
2019
- fs.mkdirSync(absPath, { recursive: true });
2020
- return 0;
2021
- } catch (e) {
2022
- if (process.env.PLUGKIT_DEBUG) console.error(`[plugkit-wasm] path_create_directory FAILED: ${e && e.message}`);
2023
- return e && e.code === 'EEXIST' ? 0 : 8;
2024
- }
2025
- },
2026
- path_unlink_file: (_dirfd, path_ptr, path_len) => {
2027
- try {
2028
- const buf = getMemory();
2029
- const relPath = new TextDecoder('utf-8').decode(new Uint8Array(buf, path_ptr >>> 0, path_len >>> 0));
2030
- const absPath = wasiResolvePath(relPath);
2031
- fs.unlinkSync(absPath);
2032
- return 0;
2033
- } catch (e) {
2034
- return e && e.code === 'ENOENT' ? 44 : 8;
2035
- }
2036
- },
2037
- path_remove_directory: (_dirfd, path_ptr, path_len) => {
2038
- try {
2039
- const buf = getMemory();
2040
- const relPath = new TextDecoder('utf-8').decode(new Uint8Array(buf, path_ptr >>> 0, path_len >>> 0));
2041
- const absPath = wasiResolvePath(relPath);
2042
- fs.rmdirSync(absPath);
2043
- return 0;
2044
- } catch (e) {
2045
- if (e && e.code === 'ENOENT') return 44;
2046
- if (e && e.code === 'ENOTEMPTY') return 55;
2047
- return 8;
2048
- }
2049
- },
2050
- path_filestat_set_times: (_dirfd, _flags, path_ptr, path_len, atim64, mtim64, fst_flags) => {
2051
- try {
2052
- const buf = getMemory();
2053
- const relPath = new TextDecoder('utf-8').decode(new Uint8Array(buf, path_ptr >>> 0, path_len >>> 0));
2054
- const absPath = wasiResolvePath(relPath);
2055
- const FILESTAT_SET_ATIM = 0x1, FILESTAT_SET_ATIM_NOW = 0x2, FILESTAT_SET_MTIM = 0x4, FILESTAT_SET_MTIM_NOW = 0x8;
2056
- const st = fs.statSync(absPath);
2057
- const nowMs = Date.now();
2058
- let atimeMs = st.atimeMs;
2059
- let mtimeMs = st.mtimeMs;
2060
- if (fst_flags & FILESTAT_SET_ATIM_NOW) atimeMs = nowMs;
2061
- else if (fst_flags & FILESTAT_SET_ATIM) atimeMs = Number(BigInt.asUintN(64, BigInt(atim64))) / 1e6;
2062
- if (fst_flags & FILESTAT_SET_MTIM_NOW) mtimeMs = nowMs;
2063
- else if (fst_flags & FILESTAT_SET_MTIM) mtimeMs = Number(BigInt.asUintN(64, BigInt(mtim64))) / 1e6;
2064
- fs.utimesSync(absPath, atimeMs / 1000, mtimeMs / 1000);
2065
- return 0;
2066
- } catch (e) {
2067
- if (process.env.PLUGKIT_DEBUG) console.error(`[plugkit-wasm] path_filestat_set_times FAILED: ${e && e.message}`);
2068
- return e && e.code === 'ENOENT' ? 44 : 8;
2069
- }
2070
- },
2071
- path_open: (_dirfd, _dirflags, path_ptr, path_len, oflags, _rights_base, _rights_inherit, fdflags, opened_fd_ptr) => {
2072
- try {
2073
- const buf = getMemory();
2074
- const relPath = new TextDecoder('utf-8').decode(new Uint8Array(buf, path_ptr >>> 0, path_len >>> 0));
2075
- const absPath = wasiResolvePath(relPath);
2076
- fs.mkdirSync(path.dirname(absPath), { recursive: true });
2077
- const OFLAGS_CREAT = 1, OFLAGS_EXCL = 2, OFLAGS_TRUNC = 8;
2078
- let nodeFlags = 'r+';
2079
- const creat = (oflags & OFLAGS_CREAT) !== 0;
2080
- const excl = (oflags & OFLAGS_EXCL) !== 0;
2081
- const trunc = (oflags & OFLAGS_TRUNC) !== 0;
2082
- if (excl && creat) nodeFlags = 'wx+';
2083
- else if (trunc) nodeFlags = 'w+';
2084
- else if (creat) nodeFlags = fs.existsSync(absPath) ? 'r+' : 'w+';
2085
- else nodeFlags = 'r+';
2086
- if (process.env.PLUGKIT_DEBUG) console.error(`[plugkit-wasm] path_open: rel=${relPath} abs=${absPath} oflags=${oflags} nodeFlags=${nodeFlags}`);
2087
- const nodeFd = fs.openSync(absPath, nodeFlags);
2088
- const wasiFd = wasiNextFd++;
2089
- wasiOpenFiles.set(wasiFd, { nodeFd, pos: 0, path: absPath });
2090
- new DataView(buf).setUint32(opened_fd_ptr, wasiFd, true);
2091
- return 0;
2092
- } catch (e) {
2093
- if (process.env.PLUGKIT_DEBUG) console.error(`[plugkit-wasm] path_open FAILED: ${e && e.message}`);
2094
- return e && /ENOENT/.test(e.code || '') ? 44 : 8;
2095
- }
2096
- },
2097
- path_filestat_get: (_dirfd, _flags, path_ptr, path_len, buf_ptr) => {
2098
- try {
2099
- const buf = getMemory();
2100
- const relPath = new TextDecoder('utf-8').decode(new Uint8Array(buf, path_ptr >>> 0, path_len >>> 0));
2101
- const absPath = wasiResolvePath(relPath);
2102
- const st = fs.statSync(absPath);
2103
- const dv = new DataView(buf);
2104
- dv.setBigUint64(buf_ptr, 0n, true);
2105
- dv.setBigUint64(buf_ptr + 8, 0n, true);
2106
- dv.setUint8(buf_ptr + 16, st.isDirectory() ? 3 : 4);
2107
- dv.setBigUint64(buf_ptr + 24, 1n, true);
2108
- dv.setBigUint64(buf_ptr + 32, BigInt(st.size), true);
2109
- dv.setBigUint64(buf_ptr + 40, BigInt(Math.floor(st.atimeMs * 1e6)), true);
2110
- dv.setBigUint64(buf_ptr + 48, BigInt(Math.floor(st.mtimeMs * 1e6)), true);
2111
- dv.setBigUint64(buf_ptr + 56, BigInt(Math.floor(st.ctimeMs * 1e6)), true);
2112
- return 0;
2113
- } catch (e) {
2114
- return e && /ENOENT/.test(e.code || '') ? 44 : 8;
2115
- }
2116
- },
2117
- poll_oneoff: () => 0,
2118
- sched_yield: () => 0,
2119
- };
2120
- if (process.env.PLUGKIT_DEBUG_WASI) {
2121
- for (const k of Object.keys(shim)) {
2122
- const orig = shim[k];
2123
- shim[k] = (...args) => {
2124
- const r = orig(...args);
2125
- try { console.error(`[plugkit-wasm] wasi.${k}(${args.map(a => typeof a === 'bigint' ? a.toString() : a).join(',')}) -> ${r}`); } catch (_) {}
2126
- return r;
2127
- };
2128
- }
2129
- }
2130
- return new Proxy(shim, {
2131
- get(target, prop) {
2132
- if (prop in target) return target[prop];
2133
- return (...args) => {
2134
- console.error(`[plugkit-wasm] unimplemented WASI call: ${String(prop)} args=${args.length}`);
2135
- return 8;
2136
- };
2137
- }
2138
- });
2139
- }
2140
-
2141
- function guardWasmRange(buffer, ptr, len, where) {
2142
- const total = buffer.byteLength;
2143
- if (!Number.isInteger(ptr) || !Number.isInteger(len) || ptr < 0 || len < 0 || ptr + len > total) {
2144
- throw new Error(`wasm-memory-read-out-of-bounds at ${where}: ptr=${ptr} len=${len} buffer=${total} -- corrupt (ptr,len) from wasm, refusing the read instead of crashing the dispatch loop`);
2145
- }
2146
- }
2147
-
2148
- function decodeWasmResult(instance, result, where) {
2149
- const u = BigInt.asUintN(64, BigInt(result));
2150
- const ptr = Number(u & 0xffffffffn);
2151
- const len = Number(u >> 32n);
2152
- if (ptr === 0 || len === 0) return '';
2153
- const buffer = instance.exports.memory.buffer;
2154
- guardWasmRange(buffer, ptr, len, where);
2155
- const out = new TextDecoder().decode(new Uint8Array(buffer, ptr, len));
2156
- try { instance.exports.plugkit_free(ptr, len); } catch (_) {}
2157
- return out;
2158
- }
2159
-
2160
- function writeWasmInput(instance, bytes, where) {
2161
- if (bytes.length === 0) return 0;
2162
- const ptr = instance.exports.plugkit_alloc(bytes.length) >>> 0;
2163
- if (ptr === 0) throw new Error(`wasm-alloc-failed at ${where}: plugkit_alloc returned 0 (wasm OOM)`);
2164
- guardWasmRange(instance.exports.memory.buffer, ptr, bytes.length, `${where}:writeWasmInput`);
2165
- new Uint8Array(instance.exports.memory.buffer, ptr, bytes.length).set(bytes);
2166
- return ptr;
2167
- }
2168
-
2169
- function readWasmBytes(instance, ptr, len) {
2170
- if (ptr === 0 || len === 0) return new Uint8Array(0);
2171
- const buffer = instance.exports.memory.buffer;
2172
- guardWasmRange(buffer, ptr, len, 'readWasmBytes');
2173
- return new Uint8Array(buffer, ptr, len).slice();
2174
- }
2175
-
2176
- function readWasmStr(instance, ptr, len) {
2177
- if (ptr === 0 || len === 0) return '';
2178
- const buffer = instance.exports.memory.buffer;
2179
- guardWasmRange(buffer, ptr, len, 'readWasmStr');
2180
- const bytes = new Uint8Array(buffer, ptr, len);
2181
- return new TextDecoder('utf-8').decode(bytes);
2182
- }
2183
-
2184
- function writeWasmBytes(instance, bytes) {
2185
- if (bytes.length === 0) return 0n;
2186
- const ptr = instance.exports.plugkit_alloc(bytes.length) >>> 0;
2187
- if (ptr === 0) return 0n;
2188
- guardWasmRange(instance.exports.memory.buffer, ptr, bytes.length, 'writeWasmBytes');
2189
- new Uint8Array(instance.exports.memory.buffer, ptr, bytes.length).set(bytes);
2190
- return (BigInt(ptr) & 0xffffffffn) | (BigInt(bytes.length) << 32n);
2191
- }
2192
-
2193
- function writeWasmStr(instance, str) {
2194
- if (!str) return 0n;
2195
- return writeWasmBytes(instance, new TextEncoder().encode(str));
2196
- }
2197
-
2198
- function writeWasmJson(instance, value) {
2199
- return writeWasmStr(instance, JSON.stringify(value));
2200
- }
2201
-
2202
- function safeName(s) { return String(s).replace(/[^A-Za-z0-9._-]/g, '_'); }
2203
-
2204
- function projectKvDir(ns) {
2205
- const projectRoot = process.env.CLAUDE_PROJECT_DIR || process.cwd();
2206
- return path.join(projectRoot, '.gm', 'disciplines', safeName(ns));
2207
- }
2208
-
2209
- function legacyKvDir(ns) {
2210
- return path.join(KV_DIR, safeName(ns));
2211
- }
2212
-
2213
- function kvFilePath(ns, key, ensureDir) {
2214
- const dir = projectKvDir(ns);
2215
- if (ensureDir) fs.mkdirSync(dir, { recursive: true });
2216
- return path.join(dir, safeName(key) + '.json');
2217
- }
2218
-
2219
- function kvReadResolve(ns, key) {
2220
- const fp = kvFilePath(ns, key);
2221
- if (fs.existsSync(fp)) return fp;
2222
- const legacy = path.join(legacyKvDir(ns), safeName(key) + '.json');
2223
- if (fs.existsSync(legacy)) return legacy;
2224
- return null;
2225
- }
2226
-
2227
- function kvNamespaceDirs(ns) {
2228
- const out = [];
2229
- const proj = projectKvDir(ns);
2230
- if (fs.existsSync(proj)) out.push(proj);
2231
- const legacy = legacyKvDir(ns);
2232
- if (fs.existsSync(legacy)) out.push(legacy);
2233
- return out;
2234
- }
2235
-
2236
- function enabledDisciplineNamespaces(baseNs) {
2237
- const projectRoot = process.env.CLAUDE_PROJECT_DIR || process.cwd();
2238
- const set = new Set([baseNs]);
2239
- try {
2240
- const enabledPath = path.join(projectRoot, '.gm', 'disciplines', 'enabled.txt');
2241
- if (fs.existsSync(enabledPath)) {
2242
- const lines = fs.readFileSync(enabledPath, 'utf-8').split(/\r?\n/);
2243
- for (const ln of lines) {
2244
- const name = ln.trim();
2245
- if (name && !name.startsWith('#')) set.add(name);
2246
- }
2247
- }
2248
- } catch (_) {}
2249
- return Array.from(set);
2250
- }
2251
-
2252
- function jaccardOverlap(a, b) {
2253
- if (!a || !b) return 0;
2254
- const tokenize = (s) => new Set(String(s).toLowerCase().split(/[^a-z0-9]+/).filter(t => t.length >= 3));
2255
- const A = tokenize(a), B = tokenize(b);
2256
- if (A.size === 0 || B.size === 0) return 0;
2257
- let inter = 0;
2258
- for (const t of A) if (B.has(t)) inter++;
2259
- return inter / (A.size + B.size - inter);
2260
- }
2261
-
2262
- const __tasks = new Map();
2263
-
2264
- function tasksDir(cwd) {
2265
- const d = path.join(cwd || process.cwd(), '.gm', 'exec-spool', 'tasks');
2266
- try { fs.mkdirSync(d, { recursive: true }); } catch (_) {}
2267
- return d;
2268
- }
2269
-
2270
- function taskMetaPath(cwd, id) { return path.join(tasksDir(cwd), `${id}.json`); }
2271
- function taskOutPath(cwd, id, which) { return path.join(tasksDir(cwd), `${id}.${which}.log`); }
2272
-
2273
- function writeTaskMeta(cwd, id, meta) {
2274
- try { fs.writeFileSync(taskMetaPath(cwd, id), JSON.stringify(meta, null, 2)); } catch (_) {}
2275
- }
2276
-
2277
- function nextTaskId(cwd) {
2278
- const counterPath = path.join(tasksDir(cwd), '.counter');
2279
- let n = 0;
2280
- try { n = parseInt(fs.readFileSync(counterPath, 'utf-8'), 10) || 0; } catch (_) {}
2281
- n += 1;
2282
- try { fs.writeFileSync(counterPath, String(n)); } catch (_) {}
2283
- return `t${n}`;
2284
- }
2285
-
2286
- let _jsRuntimeCmd = null;
2287
- function resolveJsRuntimeCmd() {
2288
- if (_jsRuntimeCmd) return _jsRuntimeCmd;
2289
- if (!/(^|[\\/])node(\.exe)?$/i.test(String(process.execPath || ''))) {
2290
- _jsRuntimeCmd = process.execPath;
2291
- return _jsRuntimeCmd;
2292
- }
2293
- try {
2294
- const which = process.platform === 'win32' ? 'where' : 'which';
2295
- const out = spawnSync(which, ['bun'], { encoding: 'utf-8', windowsHide: true });
2296
- const first = (out && out.stdout || '').split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0];
2297
- if (first) { _jsRuntimeCmd = first; return _jsRuntimeCmd; }
2298
- } catch (_) {}
2299
- _jsRuntimeCmd = process.execPath;
2300
- return _jsRuntimeCmd;
2301
- }
2302
-
2303
- function langToCmd(lang, code) {
2304
- if (lang === 'nodejs' || lang === 'js' || lang === 'javascript' || lang === 'node') return { cmd: resolveJsRuntimeCmd(), args: ['-e', code], stdinCode: null };
2305
- if (lang === 'python' || lang === 'py') return { cmd: 'python', args: ['-c', code], stdinCode: null };
2306
- if (lang === 'bash' || lang === 'sh' || lang === 'shell' || lang === 'zsh') return { cmd: 'bash', args: ['-c', code], stdinCode: null };
2307
- if (lang === 'powershell' || lang === 'ps1') return { cmd: 'powershell', args: ['-NoProfile', '-NonInteractive', '-Command', code], stdinCode: null };
2308
- if (lang === 'deno') return { cmd: 'deno', args: ['eval', code], stdinCode: null };
2309
- return null;
2310
- }
2311
-
2312
- const TASK_MAX_TIMEOUT_MS = 10 * 60 * 1000;
2313
-
2314
- function spawnTask({ cwd, lang, code, timeoutMs }) {
2315
- const id = nextTaskId(cwd);
2316
- const built = langToCmd(lang, code);
2317
- if (!built) return { ok: false, error: `unsupported lang: ${lang}` };
2318
- if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > TASK_MAX_TIMEOUT_MS) {
2319
- timeoutMs = TASK_MAX_TIMEOUT_MS;
2320
- }
2321
- const outLog = taskOutPath(cwd, id, 'stdout');
2322
- const errLog = taskOutPath(cwd, id, 'stderr');
2323
- let outFd = null, errFd = null;
2324
- try { outFd = fs.openSync(outLog, 'a'); } catch (_) {}
2325
- try { errFd = fs.openSync(errLog, 'a'); } catch (_) {}
2326
- const startedMs = Date.now();
2327
- const isPosix = process.platform !== 'win32';
2328
- const child = spawn(built.cmd, built.args, {
2329
- cwd: cwd || process.cwd(),
2330
- detached: isPosix,
2331
- stdio: ['ignore', outFd || 'ignore', errFd || 'ignore'],
2332
- windowsHide: true,
2333
- env: process.env,
1770
+ const __wasiNextFdRef = { get value() { return wasiNextFd; }, set value(v) { wasiNextFd = v; } };
1771
+ const __currentVerbContextRef = { get value() { return __currentVerbContext; } };
1772
+ function __createWasiShimBound(instanceRef) {
1773
+ return createWasiShim(instanceRef, {
1774
+ wasiFilesystemRoot: WASI_FILESYSTEM_ROOT,
1775
+ wasiOpenFiles,
1776
+ wasiNextFdRef: __wasiNextFdRef,
1777
+ wasmAbortFlag: __wasmAbortFlag,
1778
+ spoolDirForSentinel,
1779
+ currentVerbContextRef: __currentVerbContextRef,
2334
1780
  });
2335
- try { if (outFd !== null) fs.closeSync(outFd); } catch (_) {}
2336
- try { if (errFd !== null) fs.closeSync(errFd); } catch (_) {}
2337
- const meta = {
2338
- id,
2339
- pid: child.pid,
2340
- pgid: isPosix ? child.pid : null,
2341
- lang,
2342
- cmd: built.cmd,
2343
- cwd: cwd || process.cwd(),
2344
- started_ms: startedMs,
2345
- timeout_ms: timeoutMs,
2346
- deadline_ms: startedMs + timeoutMs,
2347
- status: 'running',
2348
- exit_code: null,
2349
- stdout_log: outLog,
2350
- stderr_log: errLog,
2351
- };
2352
- __tasks.set(id, { child, meta });
2353
- writeTaskMeta(cwd, id, meta);
2354
- child.on('exit', (code, signal) => {
2355
- meta.status = signal ? 'killed' : (code === 0 ? 'completed' : 'failed');
2356
- meta.exit_code = code;
2357
- meta.signal = signal;
2358
- meta.ended_ms = Date.now();
2359
- writeTaskMeta(meta.cwd, id, meta);
2360
- });
2361
- child.on('error', (err) => {
2362
- meta.status = 'error';
2363
- meta.error = err.message;
2364
- meta.ended_ms = Date.now();
2365
- writeTaskMeta(meta.cwd, id, meta);
2366
- });
2367
- logEvent('plugkit', 'task.spawn', { task_id: id, pid: child.pid, lang, timeout_ms: timeoutMs });
2368
- return { ok: true, task_id: id, pid: child.pid, started_ms: startedMs };
2369
- }
2370
-
2371
- function stopTaskById(id) {
2372
- const entry = __tasks.get(id);
2373
- if (!entry) {
2374
- return { ok: false, error: 'unknown task_id', task_id: id };
2375
- }
2376
- const { child, meta } = entry;
2377
- if (meta.status !== 'running') return { ok: true, already: meta.status, task_id: id };
2378
- const pid = meta.pid;
2379
- const isPosix = process.platform !== 'win32';
2380
- try {
2381
- if (isPosix && meta.pgid) {
2382
- try { process.kill(-meta.pgid, 'SIGTERM'); } catch (_) {}
2383
- } else {
2384
- try { child.kill('SIGTERM'); } catch (_) {}
2385
- }
2386
- } catch (_) {}
2387
- const graceTimer = setTimeout(() => {
2388
- if (meta.status !== 'running') return;
2389
- if (isPosix && meta.pgid) {
2390
- try { process.kill(-meta.pgid, 'SIGKILL'); } catch (_) {}
2391
- } else if (process.platform === 'win32') {
2392
- try { spawnSync('taskkill', ['/F', '/T', '/PID', String(pid)], { stdio: 'ignore', timeout: 3000 }); } catch (_) {}
2393
- } else {
2394
- try { child.kill('SIGKILL'); } catch (_) {}
2395
- }
2396
- }, 2000);
2397
- graceTimer.unref && graceTimer.unref();
2398
- logEvent('plugkit', 'task.stop', { task_id: id, pid });
2399
- return { ok: true, task_id: id, pid };
2400
- }
2401
-
2402
- function tailFile(filePath, maxBytes) {
2403
- try {
2404
- const stat = fs.statSync(filePath);
2405
- if (stat.size <= maxBytes) return fs.readFileSync(filePath, 'utf-8');
2406
- const fd = fs.openSync(filePath, 'r');
2407
- try {
2408
- const buf = Buffer.alloc(maxBytes);
2409
- fs.readSync(fd, buf, 0, maxBytes, stat.size - maxBytes);
2410
- return buf.toString('utf-8');
2411
- } finally { try { fs.closeSync(fd); } catch (_) {} }
2412
- } catch (_) { return ''; }
2413
1781
  }
2414
1782
 
2415
- function listTasks(cwd) {
2416
- const d = tasksDir(cwd);
2417
- const out = [];
2418
- try {
2419
- for (const entry of fs.readdirSync(d)) {
2420
- if (!entry.endsWith('.json') || entry.startsWith('.')) continue;
2421
- try {
2422
- const meta = JSON.parse(fs.readFileSync(path.join(d, entry), 'utf-8'));
2423
- out.push(meta);
2424
- } catch (_) {}
2425
- }
2426
- } catch (_) {}
2427
- return out;
2428
- }
2429
-
2430
- function reapTimedOutTasks() {
2431
- const now = Date.now();
2432
- for (const [id, entry] of __tasks) {
2433
- const m = entry.meta;
2434
- if (m.status === 'running' && m.deadline_ms && now > m.deadline_ms) {
2435
- logEvent('plugkit', 'task.timeout', { task_id: id, pid: m.pid, deadline_ms: m.deadline_ms, now_ms: now });
2436
- stopTaskById(id);
2437
- }
2438
- }
2439
- }
2440
-
2441
- function killAllTasks(reason) {
2442
- let killed = 0;
2443
- for (const [id, entry] of __tasks) {
2444
- if (entry.meta.status === 'running') {
2445
- stopTaskById(id);
2446
- killed += 1;
2447
- }
2448
- }
2449
- if (killed > 0) logEvent('plugkit', 'task.killAll', { reason, count: killed });
2450
- return killed;
2451
- }
2452
-
2453
- function pidAliveLocal(pid) {
2454
- if (!Number.isFinite(pid) || pid <= 0) return false;
2455
- try { process.kill(pid, 0); return true; } catch (_) { return false; }
2456
- }
2457
-
2458
- function sweepOrphanedTaskMetaOnBoot(cwd) {
2459
- let swept = 0;
2460
- try {
2461
- const dir = tasksDir(cwd);
2462
- const now = Date.now();
2463
- for (const name of fs.readdirSync(dir)) {
2464
- if (!/^t\d+\.json$/.test(name)) continue;
2465
- const metaPath = path.join(dir, name);
2466
- let meta = null;
2467
- try { meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8')); } catch (_) { continue; }
2468
- if (!meta || meta.status !== 'running') continue;
2469
- const stale = !pidAliveLocal(meta.pid) || (meta.deadline_ms && now > meta.deadline_ms);
2470
- if (!stale) continue;
2471
- if (pidAliveLocal(meta.pid)) {
2472
- try {
2473
- if (process.platform === 'win32') {
2474
- spawnSync('taskkill', ['/F', '/T', '/PID', String(meta.pid)], { stdio: 'ignore', windowsHide: true, timeout: 3000 });
2475
- } else {
2476
- try { process.kill(-meta.pid, 'SIGKILL'); } catch (_) { try { process.kill(meta.pid, 'SIGKILL'); } catch (_) {} }
2477
- }
2478
- } catch (_) {}
2479
- }
2480
- meta.status = 'reaped-on-boot';
2481
- meta.ended_ms = now;
2482
- try { fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2)); } catch (_) {}
2483
- swept += 1;
2484
- }
2485
- } catch (_) {}
2486
- if (swept > 0) logEvent('plugkit', 'task.bootSweepReaped', { cwd: cwd || process.cwd(), count: swept });
2487
- return swept;
2488
- }
2489
-
2490
- function hostTaskProc(action, params) {
2491
- switch (action) {
2492
- case 'spawn': return spawnTask(params);
2493
- case 'stop': return stopTaskById(params.id || params.task_id);
2494
- case 'list': return { ok: true, tasks: listTasks(params.cwd) };
2495
- case 'output': return {
2496
- ok: true,
2497
- task_id: params.id || params.task_id,
2498
- stdout: tailFile(taskOutPath(params.cwd, params.id || params.task_id, 'stdout'), params.max_bytes || 65536),
2499
- stderr: tailFile(taskOutPath(params.cwd, params.id || params.task_id, 'stderr'), params.max_bytes || 65536),
2500
- };
2501
- case 'reap': { reapTimedOutTasks(); return { ok: true }; }
2502
- case 'killAll': { const n = killAllTasks(params.reason || 'host_task_proc'); return { ok: true, killed: n }; }
2503
- default: return { ok: false, error: `unknown action: ${action}` };
2504
- }
2505
- }
1783
+ const { kvFilePath, kvReadResolve, kvNamespaceDirs } = makeKvHelpers(KV_DIR);
1784
+
1785
+ const __taskManager = makeTaskManager({ spawn, spawnSync, logEvent: (sub, event, fields) => logEvent(sub, event, fields) });
1786
+ const {
1787
+ __tasks,
1788
+ tasksDir,
1789
+ taskMetaPath,
1790
+ taskOutPath,
1791
+ writeTaskMeta,
1792
+ nextTaskId,
1793
+ resolveJsRuntimeCmd,
1794
+ langToCmd,
1795
+ TASK_MAX_TIMEOUT_MS,
1796
+ spawnTask,
1797
+ stopTaskById,
1798
+ tailFile,
1799
+ listTasks,
1800
+ reapTimedOutTasks,
1801
+ killAllTasks,
1802
+ pidAliveLocal,
1803
+ sweepOrphanedTaskMetaOnBoot,
1804
+ hostTaskProc,
1805
+ } = __taskManager;
2506
1806
 
2507
1807
  let _gmRunnerEmbedBinPath;
2508
1808
  function resolveGmRunnerEmbedBin() {
@@ -5436,7 +4736,7 @@ async function tryInstantiate(wasmPath) {
5436
4736
  const hostFunctions = makeHostFunctions(instanceRef);
5437
4737
  const importObject = {
5438
4738
  env: hostFunctions,
5439
- wasi_snapshot_preview1: createWasiShim(instanceRef),
4739
+ wasi_snapshot_preview1: __createWasiShimBound(instanceRef),
5440
4740
  };
5441
4741
  const instance = await WebAssembly.instantiate(wasmModule, importObject);
5442
4742
  instanceRef.value = instance;
@@ -1 +1 @@
1
- 0.1.905
1
+ 0.1.906
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.1978",
3
+ "version": "2.0.1980",
4
4
  "description": "Spool-dispatch orchestration engine with unified state machine, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -17,5 +17,5 @@
17
17
  "publishConfig": {
18
18
  "access": "public"
19
19
  },
20
- "plugkitVersion": "0.1.905"
20
+ "plugkitVersion": "0.1.906"
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-skill",
3
- "version": "2.0.1978",
3
+ "version": "2.0.1980",
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",