wkappbot-code 0.3.20 → 0.3.22
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/bin/wkappbot-code.js +14 -0
- package/lib/python-acquire.js +14 -8
- package/lib/python-pick.js +9 -2
- package/package.json +1 -1
package/bin/wkappbot-code.js
CHANGED
|
@@ -205,6 +205,13 @@ function installBin(o) {
|
|
|
205
205
|
// need no core and still refuse the risky classes, but the install reports the missing core as a FAILED part (0.3.13).
|
|
206
206
|
if (r.status === 3) return { ok: false, degraded: true, dest, note: "core not fetched (release host unreachable); hooks + lite harness run without it -- rerun wkappbot-code install once github.com is reachable" };
|
|
207
207
|
if (r.status !== 0) return { ok: false, dest, note: "wk-bin-acquire rc " + r.status };
|
|
208
|
+
// Core recognizes this marker beside the actual executable; no Core rebuild needed.
|
|
209
|
+
if (IS_WIN) {
|
|
210
|
+
const core = join(target, "wkappbot-core.exe");
|
|
211
|
+
if (!existsSync(core)) return { ok: false, dest, note: "Windows core missing after acquire: " + core };
|
|
212
|
+
try { writeFileSync(join(target, ".wkappbot-code-install"), "wkappbot-code npm install\n"); }
|
|
213
|
+
catch (e) { return { ok: false, dest, note: "npm license marker: " + e.message }; }
|
|
214
|
+
}
|
|
208
215
|
// Put `wkappbot` on PATH beside wkappbot-code itself, unless that name is already taken by someone else.
|
|
209
216
|
let link = "";
|
|
210
217
|
const ld = linkDir();
|
|
@@ -571,6 +578,12 @@ function uninstall(o) {
|
|
|
571
578
|
if (body.includes(core)) { rmSync(cmdShim, { force: true }); linkRemoved = cmdShim; log("shim: removed " + cmdShim + " (it called the core this install placed)"); }
|
|
572
579
|
else log("shim: kept " + cmdShim + " (it no longer calls " + core + ")");
|
|
573
580
|
}
|
|
581
|
+
// Undo only our npm license marker; do not alter a marker owned by another installer.
|
|
582
|
+
if (IS_WIN && st.bin?.ok) {
|
|
583
|
+
const marker = join(dirname(core), ".wkappbot-code-install");
|
|
584
|
+
try { if (existsSync(marker) && readFileSync(marker, "utf8") === "wkappbot-code npm install\n") { rmSync(marker); log("bin: removed npm license marker " + marker); } }
|
|
585
|
+
catch (e) { rc = 1; log("bin: could not remove npm license marker (" + e.message + ")"); }
|
|
586
|
+
}
|
|
574
587
|
// 3d. the AI CLIs this install added (lib/ai-clis.js), never one the user had before
|
|
575
588
|
removeAiClis((st.aiClis && st.aiClis.installed) || [], log, process.env, undefined, (st.aiClis && st.aiClis.prefix) || "");
|
|
576
589
|
// 4. the harness dirs we created, the plugin entry and our config folder
|
|
@@ -578,6 +591,7 @@ function uninstall(o) {
|
|
|
578
591
|
const rmTree = (p, what) => {
|
|
579
592
|
const r = rmTreeHard(p); if (r.ok) { if (what) log(what + ": removed " + p); return; }
|
|
580
593
|
rc = 1; log((what || "remove") + ": could not remove " + p + " -- blocked by " + r.blockers.slice(0, 5).join("; ") + " -- delete it by hand");
|
|
594
|
+
if (IS_WIN) { const d = spawnSync("tasklist.exe", ["/v"], { encoding: "utf8", timeout: 15000 }); log("remove diagnostics (process list, not file owners): " + String(d.stdout || d.stderr || "").slice(0, 4000)); }
|
|
581
595
|
};
|
|
582
596
|
if (st.harness && st.harness.mode === "web" && pr.harnessDirExisted !== true && existsSync(HARNESS_DIR())) rmTree(HARNESS_DIR(), "harness");
|
|
583
597
|
if (pr.hooksDirExisted !== true && existsSync(HOOKS_DIR())) rmTree(HOOKS_DIR(), "push-gate");
|
package/lib/python-acquire.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// Spec: wkappbot skill read harness-installer-owns-all-setup
|
|
11
11
|
import { spawnSync } from "node:child_process";
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
13
|
-
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
|
|
13
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync } from "node:fs";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
import { pickPython, stubWarning } from "./python-pick.js";
|
|
@@ -58,13 +58,13 @@ function setUserPath(dir, run, env) {
|
|
|
58
58
|
export function ensurePython(o = {}) {
|
|
59
59
|
const env = o.env || process.env, log = o.log || (() => {}), run = o.run || realRun;
|
|
60
60
|
const platform = o.platform || process.platform, exists = o.exists || existsSync, list = o.list || readdirSync;
|
|
61
|
-
const home = o.home || homedir(), read = o.read || readFileSync;
|
|
61
|
+
const home = o.home || homedir(), read = o.read || readFileSync, EMB = o.embed || EMBED;
|
|
62
62
|
const report = (ok, msg, extra = {}) => { log((ok ? "OK python " : "FAIL python: ") + msg); return { ok, note: msg, ...extra }; };
|
|
63
63
|
try {
|
|
64
64
|
let p = pickPython({ env, run: o.probeRun });
|
|
65
65
|
const warn = stubWarning(p.stubs);
|
|
66
66
|
if (p.cmd) {
|
|
67
|
-
const v = run(p.cmd, [...p.args, "-c", "import sys;print(sys.version.split()[0]);print(sys.executable)"], env
|
|
67
|
+
const v = run(p.cmd, [...p.args, "-c", "import sys;print(sys.version.split()[0]);print(sys.executable)"], env); // no shell: see python-pick.js
|
|
68
68
|
const [ver, exe] = String((v && v.stdout) || "").trim().split(/\r?\n/);
|
|
69
69
|
return report(true, (ver || "3") + " " + (exe || p.cmd), { cmd: p.cmd, args: p.args, via: "present" });
|
|
70
70
|
}
|
|
@@ -81,12 +81,18 @@ export function ensurePython(o = {}) {
|
|
|
81
81
|
if (!dir) {
|
|
82
82
|
const target = join(home, ".wk-harness", "python"), zip = join(home, ".wk-harness", "python-embed.zip");
|
|
83
83
|
mkdirSync(target, { recursive: true });
|
|
84
|
-
const dl = run("curl", ["-fsSL", "--retry", "6", "--retry-delay", "5", "-m", "300", "-o", zip,
|
|
85
|
-
if (!dl || dl.status !== 0) return report(false, "winget gave no Python and the download of " +
|
|
84
|
+
const dl = run("curl", ["-fsSL", "--retry", "6", "--retry-delay", "5", "-m", "300", "-o", zip, EMB.url], env);
|
|
85
|
+
if (!dl || dl.status !== 0) return report(false, "winget gave no Python and the download of " + EMB.url + " failed (curl rc " + (dl ? dl.status : "?") + ")", { via: "none" });
|
|
86
86
|
const sum = createHash("sha256").update(read(zip)).digest("hex");
|
|
87
|
-
if (sum !==
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
if (sum !== EMB.sha256) return report(false, "the embeddable zip sha256 " + sum + " is not the pinned " + EMB.sha256, { via: "none" });
|
|
88
|
+
// Windows: the bsdtar in System32 reads a zip; the GNU tar of Git Bash, first on PATH there, cannot (rc 128, 0.3.20)
|
|
89
|
+
const tarExe = platform === "win32" ? join(env.SystemRoot || env.windir || "C:\\Windows", "System32", "tar.exe") : "tar";
|
|
90
|
+
let ux = run(tarExe, ["-xf", zip, "-C", target], env);
|
|
91
|
+
if ((!ux || ux.status !== 0) && platform === "win32")
|
|
92
|
+
ux = run("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", "Expand-Archive -Force -LiteralPath $env:WK_ZIP -DestinationPath $env:WK_DEST"], { ...env, WK_ZIP: zip, WK_DEST: target });
|
|
93
|
+
if (!ux || ux.status !== 0) return report(false, "could not unpack " + zip + " (" + tarExe + " rc " + (ux ? ux.status : "?") + ")", { via: "none" });
|
|
94
|
+
// the zip's pythonXY._pth keeps the script folder off sys.path, so harness scripts could not import their siblings
|
|
95
|
+
try { for (const n of list(target)) if (/^python\d*\._pth$/i.test(n)) (o.unlink || unlinkSync)(join(target, n)); } catch {}
|
|
90
96
|
dir = target; via = "embed";
|
|
91
97
|
}
|
|
92
98
|
// 3. a python3.exe beside python.exe (winget and the zip ship only python.exe, so `python3` would still hit the
|
package/lib/python-pick.js
CHANGED
|
@@ -8,10 +8,17 @@
|
|
|
8
8
|
import { spawnSync } from "node:child_process";
|
|
9
9
|
|
|
10
10
|
export const CANDIDATES = [["python3"], ["python"], ["py", "-3"]];
|
|
11
|
+
// Windows asks python and py -3 first: python.org and tool-cache installs ship python.exe with no python3.exe, so
|
|
12
|
+
// python3 there is usually the WindowsApps stub (spokesman order 2026-11-10).
|
|
13
|
+
export const candidatesFor = (platform) => (platform === "win32" ? [["python"], ["py", "-3"], ["python3"]] : CANDIDATES);
|
|
11
14
|
const PROBE = ["-c", "import sys;print(sys.version_info[0])"];
|
|
12
15
|
const STORE_RE = /Microsoft Store|WindowsApps|app execution alias/i;
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
// NEVER a shell: on win32 shell:true handed the probe to cmd.exe unquoted, so the ";" and "(" of -c broke it and every
|
|
18
|
+
// REAL Python failed (0.3.20, SDK os-qa 42730232937 win-x64). Node finds python.exe and py.exe on PATH by itself, and
|
|
19
|
+
// a WindowsApps stub still runs and exits 9009.
|
|
20
|
+
export const spawnOpts = (env, timeout = 15000) => ({ encoding: "utf8", env, timeout, shell: false, windowsHide: true });
|
|
21
|
+
const realRun = (cmd, args, env) => spawnSync(cmd, args, spawnOpts(env));
|
|
15
22
|
|
|
16
23
|
// -> { cmd, args, stubs: [names] } ; cmd "" when no working Python 3 exists.
|
|
17
24
|
export function pickPython(o = {}) {
|
|
@@ -20,7 +27,7 @@ export function pickPython(o = {}) {
|
|
|
20
27
|
const r = run(env.PYTHON_CMD, PROBE, env);
|
|
21
28
|
if (r && r.status === 0 && String(r.stdout || "").trim() === "3") return { cmd: env.PYTHON_CMD, args: [], stubs };
|
|
22
29
|
}
|
|
23
|
-
for (const [cmd, ...pre] of
|
|
30
|
+
for (const [cmd, ...pre] of candidatesFor(o.platform || process.platform)) {
|
|
24
31
|
const r = run(cmd, [...pre, ...PROBE], env);
|
|
25
32
|
if (!r) continue;
|
|
26
33
|
const out = String(r.stdout || "") + String(r.stderr || "");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wkappbot-code",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.22",
|
|
4
4
|
"description": "wkappbot-code -- Wisdom Kernel: the pinned OpenCode bridge plus the reflex harness plugin, the wkappbot core, the web harness and the latest public skills in one install",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|