@metaphi-ai/hum 0.1.8 → 0.1.10
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/dist/cli.mjs +176 -59
- package/package.json +1 -1
- package/scripts/postinstall.js +53 -18
package/dist/cli.mjs
CHANGED
|
@@ -84524,7 +84524,7 @@ import net from "node:net";
|
|
|
84524
84524
|
import os4 from "node:os";
|
|
84525
84525
|
import path2 from "node:path";
|
|
84526
84526
|
import crypto from "node:crypto";
|
|
84527
|
-
import { spawn } from "node:child_process";
|
|
84527
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
84528
84528
|
var runDir = () => path2.join(process.env.HUM_HOME || path2.join(os4.homedir(), ".hum"), "run");
|
|
84529
84529
|
var readRun = (id) => {
|
|
84530
84530
|
try {
|
|
@@ -84535,74 +84535,170 @@ var readRun = (id) => {
|
|
|
84535
84535
|
};
|
|
84536
84536
|
var EngineError = class extends Error {
|
|
84537
84537
|
};
|
|
84538
|
-
var
|
|
84539
|
-
|
|
84540
|
-
|
|
84541
|
-
|
|
84542
|
-
|
|
84543
|
-
|
|
84544
|
-
|
|
84545
|
-
|
|
84546
|
-
|
|
84538
|
+
var win = process.platform === "win32";
|
|
84539
|
+
var exists = (p) => {
|
|
84540
|
+
try {
|
|
84541
|
+
fs3.accessSync(p);
|
|
84542
|
+
return true;
|
|
84543
|
+
} catch {
|
|
84544
|
+
return false;
|
|
84545
|
+
}
|
|
84546
|
+
};
|
|
84547
|
+
function uvToolDirs() {
|
|
84548
|
+
const dirs = [];
|
|
84549
|
+
if (process.env.UV_TOOL_DIR) dirs.push(process.env.UV_TOOL_DIR);
|
|
84550
|
+
if (win) {
|
|
84551
|
+
for (const base of [process.env.APPDATA, process.env.LOCALAPPDATA]) if (base) dirs.push(path2.join(base, "uv", "tools"));
|
|
84552
|
+
} else dirs.push(path2.join(process.env.XDG_DATA_HOME || path2.join(os4.homedir(), ".local", "share"), "uv", "tools"));
|
|
84553
|
+
if (!dirs.some(exists)) {
|
|
84554
|
+
for (const uv of ["uv", path2.join(os4.homedir(), ".local", "bin", win ? "uv.exe" : "uv")]) {
|
|
84555
|
+
try {
|
|
84556
|
+
const r = spawnSync(uv, ["tool", "dir"], { encoding: "utf8", windowsHide: true });
|
|
84557
|
+
if (r.status === 0 && r.stdout.trim()) {
|
|
84558
|
+
dirs.push(r.stdout.trim());
|
|
84559
|
+
break;
|
|
84560
|
+
}
|
|
84561
|
+
} catch {
|
|
84562
|
+
}
|
|
84563
|
+
}
|
|
84564
|
+
}
|
|
84565
|
+
return dirs;
|
|
84566
|
+
}
|
|
84567
|
+
function enginePython(dirs = uvToolDirs()) {
|
|
84568
|
+
for (const dir of dirs) for (const name of ["hum-cli", "hum"]) {
|
|
84569
|
+
const py = win ? path2.join(dir, name, "Scripts", "python.exe") : path2.join(dir, name, "bin", "python");
|
|
84570
|
+
if (exists(py)) return py;
|
|
84571
|
+
}
|
|
84572
|
+
return null;
|
|
84573
|
+
}
|
|
84574
|
+
function siblingEngine(execPath = process.execPath) {
|
|
84575
|
+
let dir;
|
|
84576
|
+
try {
|
|
84577
|
+
dir = path2.dirname(fs3.realpathSync(execPath));
|
|
84578
|
+
} catch {
|
|
84579
|
+
return null;
|
|
84580
|
+
}
|
|
84581
|
+
const p = path2.join(dir, "engine", win ? "hum-engine.exe" : "hum-engine");
|
|
84582
|
+
return exists(p) ? p : null;
|
|
84583
|
+
}
|
|
84584
|
+
function homeEngine(env3 = process.env) {
|
|
84585
|
+
const home = env3.HUM_HOME || path2.join(os4.homedir(), ".hum");
|
|
84586
|
+
const py = win ? path2.join(home, "engine", "Scripts", "python.exe") : path2.join(home, "engine", "bin", "python");
|
|
84587
|
+
return exists(py) ? py : null;
|
|
84588
|
+
}
|
|
84589
|
+
function engineCandidates(env3 = process.env) {
|
|
84590
|
+
const out = [];
|
|
84591
|
+
if (env3.HUM_ENGINE) {
|
|
84592
|
+
const [bin, ...args2] = env3.HUM_ENGINE.split(" ");
|
|
84593
|
+
out.push({ bin, args: args2, where: "HUM_ENGINE" });
|
|
84594
|
+
}
|
|
84595
|
+
const sib = siblingEngine();
|
|
84596
|
+
if (sib) out.push({ bin: sib, args: ["engine"], where: "beside hum" });
|
|
84597
|
+
const home = homeEngine(env3);
|
|
84598
|
+
if (home) out.push({ bin: home, args: ["-m", "hum.cli", "engine"], where: "HUM_HOME/engine" });
|
|
84599
|
+
const py = enginePython();
|
|
84600
|
+
if (py) out.push({ bin: py, args: ["-m", "hum.cli", "engine"], where: "the engine's interpreter" });
|
|
84601
|
+
out.push({ bin: "hum-engine", args: [], where: "PATH" });
|
|
84602
|
+
out.push({ bin: path2.join(os4.homedir(), ".local", "bin", win ? "hum-engine.exe" : "hum-engine"), args: [], where: "uv's bin dir" });
|
|
84603
|
+
return out;
|
|
84604
|
+
}
|
|
84605
|
+
function engineCommand(argv, candidates = engineCandidates()) {
|
|
84606
|
+
for (const c of candidates) {
|
|
84607
|
+
if (c.args[c.args.length - 1] !== "engine") continue;
|
|
84608
|
+
return { bin: c.bin, args: [...c.args.slice(0, -1), ...argv], where: c.where };
|
|
84609
|
+
}
|
|
84610
|
+
return null;
|
|
84611
|
+
}
|
|
84612
|
+
function startFailure(failures) {
|
|
84613
|
+
const refused = failures.find((f) => f.error && f.error.code !== "ENOENT");
|
|
84614
|
+
if (!refused) {
|
|
84615
|
+
return "Hum's engine is not installed. Run: uv tool install hum-cli (or set HUM_ENGINE to the command that starts it)";
|
|
84616
|
+
}
|
|
84617
|
+
const what = refused.error.code || refused.error.message;
|
|
84618
|
+
const lines = [
|
|
84619
|
+
`Hum's engine is installed (${refused.bin}) but this machine would not start it: ${what}.`
|
|
84620
|
+
];
|
|
84621
|
+
if (win) {
|
|
84622
|
+
lines.push("On a managed Windows machine that is usually Device Guard / WDAC refusing an executable it does not know.");
|
|
84623
|
+
lines.push(`To see the policy's own message, run: "${refused.bin}" --help`);
|
|
84624
|
+
const dir = uvToolDirs()[0];
|
|
84625
|
+
lines.push(`If uv's environment is elsewhere, point Hum at its interpreter: set HUM_ENGINE=${path2.join(dir || "<uv tool dir>", "hum-cli", "Scripts", "python.exe")} -m hum.cli engine`);
|
|
84626
|
+
} else {
|
|
84627
|
+
lines.push('Set HUM_ENGINE to a command that starts it, e.g. HUM_ENGINE="python -m hum.cli engine"');
|
|
84628
|
+
}
|
|
84629
|
+
return lines.join("\n");
|
|
84630
|
+
}
|
|
84631
|
+
function attempt({ bin, args: args2 }, cwd2, { task, images, resume }) {
|
|
84547
84632
|
return new Promise((resolve, reject) => {
|
|
84548
|
-
const
|
|
84549
|
-
|
|
84550
|
-
|
|
84551
|
-
|
|
84552
|
-
|
|
84553
|
-
|
|
84633
|
+
const runId = crypto.randomBytes(6).toString("hex");
|
|
84634
|
+
fs3.mkdirSync(runDir(), { recursive: true });
|
|
84635
|
+
const logPath = path2.join(runDir(), `${runId}.log`);
|
|
84636
|
+
const log = fs3.openSync(logPath, "a");
|
|
84637
|
+
const argv = [...args2, "-C", cwd2, "--run-id", runId];
|
|
84638
|
+
if (task) argv.push("--task", task);
|
|
84639
|
+
for (const p of images) argv.push("--image", p);
|
|
84640
|
+
if (resume) argv.push("--resume", resume);
|
|
84641
|
+
let settled = false;
|
|
84642
|
+
const settle = (fn) => {
|
|
84643
|
+
if (!settled) {
|
|
84644
|
+
settled = true;
|
|
84645
|
+
fn();
|
|
84646
|
+
}
|
|
84647
|
+
};
|
|
84648
|
+
let child;
|
|
84649
|
+
try {
|
|
84650
|
+
child = spawn(bin, argv, { stdio: ["ignore", log, log], detached: true, windowsHide: true });
|
|
84651
|
+
} catch (e) {
|
|
84652
|
+
fs3.closeSync(log);
|
|
84653
|
+
reject({ spawn: e });
|
|
84654
|
+
return;
|
|
84655
|
+
}
|
|
84656
|
+
fs3.closeSync(log);
|
|
84657
|
+
let died = false;
|
|
84658
|
+
child.on("error", (e) => settle(() => reject({ spawn: e })));
|
|
84659
|
+
child.on("exit", () => {
|
|
84660
|
+
died = true;
|
|
84661
|
+
});
|
|
84662
|
+
const t0 = Date.now();
|
|
84663
|
+
const poll = () => {
|
|
84664
|
+
if (settled) return;
|
|
84665
|
+
const rec = readRun(runId);
|
|
84666
|
+
if (rec && rec.port) {
|
|
84667
|
+
child.unref();
|
|
84668
|
+
settle(() => resolve(rec));
|
|
84554
84669
|
return;
|
|
84555
84670
|
}
|
|
84556
|
-
if (!
|
|
84557
|
-
|
|
84671
|
+
if (died && !rec) {
|
|
84672
|
+
settle(() => reject({ died: logPath }));
|
|
84558
84673
|
return;
|
|
84559
84674
|
}
|
|
84560
|
-
|
|
84561
|
-
|
|
84562
|
-
const log = fs3.openSync(path2.join(runDir(), `${runId}.log`), "a");
|
|
84563
|
-
const [bin, ...rest] = cmd.split(" ");
|
|
84564
|
-
const args2 = [...rest, "-C", cwd2, "--run-id", runId];
|
|
84565
|
-
if (task) args2.push("--task", task);
|
|
84566
|
-
for (const p of images) args2.push("--image", p);
|
|
84567
|
-
if (resume) args2.push("--resume", resume);
|
|
84568
|
-
let child;
|
|
84569
|
-
try {
|
|
84570
|
-
child = spawn(bin, args2, { stdio: ["ignore", log, log], detached: true, windowsHide: true });
|
|
84571
|
-
} catch {
|
|
84572
|
-
tryNext(i + 1);
|
|
84675
|
+
if (Date.now() - t0 > 25e3) {
|
|
84676
|
+
settle(() => reject({ timeout: true }));
|
|
84573
84677
|
return;
|
|
84574
84678
|
}
|
|
84575
|
-
|
|
84576
|
-
child.on("error", () => {
|
|
84577
|
-
failed = true;
|
|
84578
|
-
tryNext(i + 1);
|
|
84579
|
-
});
|
|
84580
|
-
child.on("exit", () => {
|
|
84581
|
-
failed = true;
|
|
84582
|
-
});
|
|
84583
|
-
const t0 = Date.now();
|
|
84584
|
-
const poll = () => {
|
|
84585
|
-
const rec = readRun(runId);
|
|
84586
|
-
if (rec && rec.port) {
|
|
84587
|
-
child.unref();
|
|
84588
|
-
resolve(rec);
|
|
84589
|
-
return;
|
|
84590
|
-
}
|
|
84591
|
-
if (failed && !rec) {
|
|
84592
|
-
reject(new EngineError(`the engine did not start (see ${path2.join(runDir(), `${runId}.log`)})`));
|
|
84593
|
-
return;
|
|
84594
|
-
}
|
|
84595
|
-
if (Date.now() - t0 > 25e3) {
|
|
84596
|
-
reject(new EngineError("the engine did not start in time"));
|
|
84597
|
-
return;
|
|
84598
|
-
}
|
|
84599
|
-
setTimeout(poll, 50);
|
|
84600
|
-
};
|
|
84601
|
-
poll();
|
|
84679
|
+
setTimeout(poll, 50);
|
|
84602
84680
|
};
|
|
84603
|
-
|
|
84681
|
+
poll();
|
|
84604
84682
|
});
|
|
84605
84683
|
}
|
|
84684
|
+
async function spawnEngine(cwd2, { task = null, images = [], resume = null, candidates = engineCandidates() } = {}) {
|
|
84685
|
+
const failures = [];
|
|
84686
|
+
for (const cand of candidates) {
|
|
84687
|
+
if (!cand.bin) continue;
|
|
84688
|
+
try {
|
|
84689
|
+
return await attempt(cand, cwd2, { task, images, resume });
|
|
84690
|
+
} catch (f) {
|
|
84691
|
+
if (f && f.spawn) {
|
|
84692
|
+
failures.push({ ...cand, error: f.spawn });
|
|
84693
|
+
continue;
|
|
84694
|
+
}
|
|
84695
|
+
if (f && f.died) throw new EngineError(`the engine did not start (see ${f.died})`);
|
|
84696
|
+
if (f && f.timeout) throw new EngineError("the engine did not start in time");
|
|
84697
|
+
throw f;
|
|
84698
|
+
}
|
|
84699
|
+
}
|
|
84700
|
+
throw new EngineError(startFailure(failures));
|
|
84701
|
+
}
|
|
84606
84702
|
var Engine = class {
|
|
84607
84703
|
constructor(port2) {
|
|
84608
84704
|
this.port = port2;
|
|
@@ -84676,6 +84772,27 @@ var Engine = class {
|
|
|
84676
84772
|
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
84677
84773
|
import fs4 from "node:fs";
|
|
84678
84774
|
import path3 from "node:path";
|
|
84775
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
84776
|
+
var ENGINE_COMMANDS = /* @__PURE__ */ new Set(["run", "login", "logout", "whoami", "model", "key", "sessions", "show", "sync", "skill", "ps", "stop", "hooks", "engine"]);
|
|
84777
|
+
var argv0 = process.argv.slice(2);
|
|
84778
|
+
if (argv0[0] === "--version" || argv0[0] === "-V") {
|
|
84779
|
+
process.stdout.write(`hum ${true ? "0.1.10" : "0.0.0"}
|
|
84780
|
+
`);
|
|
84781
|
+
process.exit(0);
|
|
84782
|
+
}
|
|
84783
|
+
if (ENGINE_COMMANDS.has(argv0[0])) {
|
|
84784
|
+
const cmd = engineCommand(argv0);
|
|
84785
|
+
if (!cmd) {
|
|
84786
|
+
process.stderr.write(startFailure([]) + "\n");
|
|
84787
|
+
process.exit(1);
|
|
84788
|
+
}
|
|
84789
|
+
const r = spawnSync2(cmd.bin, cmd.args, { stdio: "inherit", windowsHide: true });
|
|
84790
|
+
if (r.error) {
|
|
84791
|
+
process.stderr.write(startFailure([{ bin: cmd.bin, error: r.error }]) + "\n");
|
|
84792
|
+
process.exit(1);
|
|
84793
|
+
}
|
|
84794
|
+
process.exit(r.status === null ? 1 : r.status);
|
|
84795
|
+
}
|
|
84679
84796
|
function parseArgs(argv) {
|
|
84680
84797
|
const a = { task: null, resume: null, cwd: process.cwd(), images: [] };
|
|
84681
84798
|
for (let i = 0; i < argv.length; i += 1) {
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -1,23 +1,58 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
1
|
+
// The engine, installed beside the person's Hum data: ~/.hum/engine (HUM_HOME/engine), a plain
|
|
2
|
+
// Python environment holding hum-cli at this package's version. No launcher shims are written
|
|
3
|
+
// anywhere — `hum` starts the engine through the environment's own interpreter — so nothing of
|
|
4
|
+
// ours lands in ~/.local/bin, and nothing there can shadow the `hum` npm installed.
|
|
5
|
+
// uv creates the environment (and fetches a Python if the machine has none); it is installed
|
|
6
|
+
// when missing. Quiet when the engine is already at this version; never fails the npm install.
|
|
7
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
8
|
+
import fs from 'node:fs';
|
|
4
9
|
import os from 'node:os';
|
|
5
10
|
import path from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
6
12
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
13
|
+
const win = process.platform === 'win32';
|
|
14
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const version = JSON.parse(fs.readFileSync(path.join(here, '..', 'package.json'), 'utf8')).version;
|
|
16
|
+
const home = process.env.HUM_HOME || path.join(os.homedir(), '.hum');
|
|
17
|
+
const env = path.join(home, 'engine');
|
|
18
|
+
const python = win ? path.join(env, 'Scripts', 'python.exe') : path.join(env, 'bin', 'python');
|
|
19
|
+
const localBin = path.join(os.homedir(), '.local', 'bin');
|
|
20
|
+
const say = (m) => console.log(`hum: ${m}`);
|
|
10
21
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
|
|
22
|
+
const run = (cmd, args, opts = {}) => spawnSync(cmd, args, { encoding: 'utf8', windowsHide: true, ...opts });
|
|
23
|
+
const uvNames = ['uv', path.join(localBin, win ? 'uv.exe' : 'uv')];
|
|
24
|
+
const uv = () => uvNames.find((u) => run(u, ['--version']).status === 0);
|
|
25
|
+
|
|
26
|
+
const installed = () => {
|
|
27
|
+
const r = run(python, ['-c', 'import hum; print(hum.__version__)']);
|
|
28
|
+
return r.status === 0 ? r.stdout.trim() : null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
if (installed() === version) process.exit(0);
|
|
33
|
+
let u = uv();
|
|
34
|
+
if (!u) {
|
|
35
|
+
say('installing uv (it creates the engine\'s Python environment) …');
|
|
36
|
+
const r = win
|
|
37
|
+
? run('powershell', ['-ExecutionPolicy', 'ByPass', '-c', 'irm https://astral.sh/uv/install.ps1 | iex'], { stdio: 'inherit' })
|
|
38
|
+
: run('sh', ['-c', 'curl -LsSf https://astral.sh/uv/install.sh | sh'], { stdio: 'inherit' });
|
|
39
|
+
u = r.status === 0 ? uv() : null;
|
|
40
|
+
if (!u) { say('could not install uv; install it from https://astral.sh/uv and run: npm rebuild -g @metaphi-ai/hum'); process.exit(0); }
|
|
41
|
+
}
|
|
42
|
+
say(`installing the engine ${version} into ${env} …`);
|
|
43
|
+
fs.mkdirSync(home, { recursive: true });
|
|
44
|
+
if (run(u, ['venv', '--quiet', '--python', '3.12', env], { stdio: 'inherit' }).status !== 0) throw new Error('uv venv failed');
|
|
45
|
+
if (run(u, ['pip', 'install', '--quiet', '--python', python, `hum-cli==${version}`], { stdio: 'inherit' }).status !== 0) throw new Error('uv pip install failed');
|
|
46
|
+
if (installed() !== version) throw new Error('the engine did not import after install');
|
|
47
|
+
|
|
48
|
+
// An earlier version installed the engine with `uv tool install`, which wrote launcher shims
|
|
49
|
+
// (hum, hum-engine) into ~/.local/bin — where they shadow the `hum` npm installed, and where a
|
|
50
|
+
// managed Windows machine refuses them. That engine is redundant now; take it and its shims out.
|
|
51
|
+
const list = run(u, ['tool', 'list']);
|
|
52
|
+
if (list.status === 0 && /^hum-cli\b/m.test(list.stdout)) {
|
|
53
|
+
if (run(u, ['tool', 'uninstall', 'hum-cli']).status === 0) say(`removed the uv install of hum-cli and its shims in ${localBin}; the engine lives in ${env} now`);
|
|
54
|
+
}
|
|
55
|
+
say(`engine ${version} ready`);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
say(`engine install failed (${e.message}); run: npm rebuild -g @metaphi-ai/hum`);
|
|
23
58
|
}
|