@letterblack/lbe-exec 1.2.15 → 1.2.16
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.js +126 -153
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2574,11 +2574,6 @@ function loadPolicy() {
|
|
|
2574
2574
|
const p = path15.join(process.cwd(), "lbe.policy.json");
|
|
2575
2575
|
return fs14.existsSync(p) ? JSON.parse(fs14.readFileSync(p, "utf8")) : null;
|
|
2576
2576
|
}
|
|
2577
|
-
function countAudit() {
|
|
2578
|
-
const p = path15.join(process.cwd(), ".lbe", "audit.jsonl");
|
|
2579
|
-
if (!fs14.existsSync(p)) return 0;
|
|
2580
|
-
return fs14.readFileSync(p, "utf8").split("\n").filter((l) => l.trim()).length;
|
|
2581
|
-
}
|
|
2582
2577
|
function findHookPath() {
|
|
2583
2578
|
const candidates = [
|
|
2584
2579
|
path15.resolve(__dir, "../hooks/register.cjs"),
|
|
@@ -2684,8 +2679,9 @@ switch (cmd) {
|
|
|
2684
2679
|
process.exit(1);
|
|
2685
2680
|
}
|
|
2686
2681
|
const existing = process.env.NODE_OPTIONS || "";
|
|
2687
|
-
const
|
|
2688
|
-
const
|
|
2682
|
+
const hookPathFwd = hookPath.replace(/\\/g, "/");
|
|
2683
|
+
const hookFlag = '--require "' + hookPathFwd + '"';
|
|
2684
|
+
const nodeOptions = existing.includes(hookPathFwd) ? existing : (existing + " " + hookFlag).trim();
|
|
2689
2685
|
const npmArgs = rest.filter((v) => !v.startsWith("--mode") && v !== opts.mode);
|
|
2690
2686
|
const child = spawn("npm", npmArgs, {
|
|
2691
2687
|
stdio: "inherit",
|
|
@@ -2696,59 +2692,63 @@ switch (cmd) {
|
|
|
2696
2692
|
break;
|
|
2697
2693
|
}
|
|
2698
2694
|
case "status": {
|
|
2699
|
-
const
|
|
2695
|
+
const root = process.cwd();
|
|
2700
2696
|
console.log("\u2500\u2500 LBE Status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
2701
|
-
console.log("workspace: " +
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
const
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
} catch (_) {
|
|
2726
|
-
}
|
|
2727
|
-
console.log("\nhook: " + (pidAlive ? "ACTIVE" : "stale (process exited)"));
|
|
2728
|
-
console.log("hook pid: " + hookStatus.pid + (pidAlive ? " (alive)" : " (gone)"));
|
|
2729
|
-
console.log("hook mode: " + hookStatus.mode);
|
|
2730
|
-
console.log("hook root: " + hookStatus.root);
|
|
2731
|
-
console.log("hook start: " + hookStatus.started_at);
|
|
2732
|
-
if (hookStatus.patched) {
|
|
2733
|
-
console.log("\nPatched functions:");
|
|
2734
|
-
for (const [fn, active] of Object.entries(hookStatus.patched)) {
|
|
2735
|
-
console.log(" " + (active ? "\u2713" : "\u2013") + " " + fn);
|
|
2697
|
+
console.log("workspace: " + root);
|
|
2698
|
+
const hookPath = findHookPath();
|
|
2699
|
+
console.log("hook file: " + hookPath + (fs14.existsSync(hookPath) ? " (found)" : " (MISSING)"));
|
|
2700
|
+
const lbeRoot = process.env.LBE_ROOT || "";
|
|
2701
|
+
console.log("LBE_ROOT: " + (lbeRoot || "(not set)"));
|
|
2702
|
+
const nodeOpts = process.env.NODE_OPTIONS || "";
|
|
2703
|
+
const hookInPath = nodeOpts.includes("register.cjs");
|
|
2704
|
+
console.log("NODE_OPTIONS contains hook: " + (hookInPath ? "yes" : "no"));
|
|
2705
|
+
const eventsFile = path15.join(root, ".lbe", "events.jsonl");
|
|
2706
|
+
const auditExists = fs14.existsSync(eventsFile);
|
|
2707
|
+
console.log("audit log: " + (auditExists ? eventsFile : "(none yet)"));
|
|
2708
|
+
if (auditExists) {
|
|
2709
|
+
try {
|
|
2710
|
+
const lines = fs14.readFileSync(eventsFile, "utf8").split("\n").filter((l) => l.trim());
|
|
2711
|
+
if (lines.length) {
|
|
2712
|
+
const last = JSON.parse(lines[lines.length - 1]);
|
|
2713
|
+
const ts = new Date((last.ts || 0) * 1e3).toISOString().replace("T", " ").slice(0, 19);
|
|
2714
|
+
const target = last.path || last.cmd || "?";
|
|
2715
|
+
console.log("last event: " + ts + " " + last.action + " " + target + " \u2192 " + (last.decision || "?"));
|
|
2716
|
+
} else {
|
|
2717
|
+
console.log("last event: (none)");
|
|
2718
|
+
}
|
|
2719
|
+
} catch (_) {
|
|
2720
|
+
console.log("last event: (unreadable)");
|
|
2736
2721
|
}
|
|
2737
2722
|
}
|
|
2738
|
-
const
|
|
2739
|
-
|
|
2740
|
-
|
|
2723
|
+
const statusFile = path15.join(root, ".lbe", "runtime", "hook-status.json");
|
|
2724
|
+
if (fs14.existsSync(statusFile)) {
|
|
2725
|
+
let h;
|
|
2741
2726
|
try {
|
|
2742
|
-
|
|
2743
|
-
console.log("\nactivation: " + (act.permanent ? "permanent" : "session") + " (" + act.mode + ")" + (sessionActive ? " NODE_OPTIONS \u2713" : " restart terminal to apply"));
|
|
2727
|
+
h = JSON.parse(fs14.readFileSync(statusFile, "utf8"));
|
|
2744
2728
|
} catch (_) {
|
|
2745
2729
|
}
|
|
2746
|
-
|
|
2747
|
-
|
|
2730
|
+
if (h) {
|
|
2731
|
+
let pidAlive = false;
|
|
2732
|
+
try {
|
|
2733
|
+
process.kill(h.pid, 0);
|
|
2734
|
+
pidAlive = true;
|
|
2735
|
+
} catch (_) {
|
|
2736
|
+
}
|
|
2737
|
+
console.log("\nhook process: " + (pidAlive ? "ACTIVE" : "stale (process exited)"));
|
|
2738
|
+
console.log("hook pid: " + h.pid + (pidAlive ? " (alive)" : " (gone)"));
|
|
2739
|
+
console.log("hook mode: " + h.mode);
|
|
2740
|
+
console.log("hook started: " + h.started_at);
|
|
2741
|
+
if (h.patched) {
|
|
2742
|
+
console.log("\nPatched functions:");
|
|
2743
|
+
for (const [fn, active] of Object.entries(h.patched)) {
|
|
2744
|
+
console.log(" " + (active ? "\u2713" : "\u2013") + " " + fn);
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
2748
|
} else {
|
|
2749
|
-
console.log("\
|
|
2749
|
+
console.log("\nhook process: inactive \u2014 run: lbe-exec run-node ./agent.js");
|
|
2750
|
+
console.log(" or: lbe-exec activate then lbe-exec shell");
|
|
2750
2751
|
}
|
|
2751
|
-
console.log("\nevents log: .lbe/events.jsonl");
|
|
2752
2752
|
break;
|
|
2753
2753
|
}
|
|
2754
2754
|
case "audit": {
|
|
@@ -2793,12 +2793,12 @@ switch (cmd) {
|
|
|
2793
2793
|
case "activate": {
|
|
2794
2794
|
const hookPath = findHookPath();
|
|
2795
2795
|
if (!fs14.existsSync(hookPath)) {
|
|
2796
|
-
console.error("Hook not found: " + hookPath
|
|
2796
|
+
console.error("Hook not found: " + hookPath);
|
|
2797
|
+
console.error("Run: npm install @letterblack/lbe-exec");
|
|
2797
2798
|
process.exit(1);
|
|
2798
2799
|
}
|
|
2799
2800
|
const mode = opts.mode || "observe";
|
|
2800
2801
|
const root = process.cwd();
|
|
2801
|
-
const reqFlag = '--require "' + hookPath + '"';
|
|
2802
2802
|
const lbeDir = path15.join(root, ".lbe");
|
|
2803
2803
|
fs14.mkdirSync(lbeDir, { recursive: true });
|
|
2804
2804
|
fs14.writeFileSync(path15.join(lbeDir, "activation.json"), JSON.stringify({
|
|
@@ -2806,111 +2806,83 @@ switch (cmd) {
|
|
|
2806
2806
|
activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2807
2807
|
hookPath,
|
|
2808
2808
|
mode,
|
|
2809
|
-
root
|
|
2810
|
-
permanent: !!opts.permanent
|
|
2809
|
+
root
|
|
2811
2810
|
}, null, 2) + "\n");
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
console.log(" Restart your terminal or IDE to apply.\n");
|
|
2830
|
-
console.log(" Every node / npm / npx process will be governed automatically.");
|
|
2831
|
-
} else {
|
|
2832
|
-
const home = process.env.HOME || "";
|
|
2833
|
-
const rcFiles = [".bashrc", ".zshrc"].map((f) => path15.join(home, f)).filter((f) => fs14.existsSync(f));
|
|
2834
|
-
const block = `
|
|
2835
|
-
# LBE Agent Governance
|
|
2836
|
-
export NODE_OPTIONS='${reqFlag}'
|
|
2837
|
-
export LBE_ROOT="${root}"
|
|
2838
|
-
export LBE_MODE="${mode}"
|
|
2839
|
-
`;
|
|
2840
|
-
for (const rc of rcFiles) {
|
|
2841
|
-
fs14.appendFileSync(rc, block);
|
|
2842
|
-
console.log("\u2713 Appended to " + rc);
|
|
2843
|
-
}
|
|
2844
|
-
if (!rcFiles.length) console.log("No .bashrc/.zshrc found \u2014 add these manually:\n" + block);
|
|
2845
|
-
console.log(" Run: source ~/.bashrc (or restart terminal)");
|
|
2811
|
+
console.log("\u2500\u2500 LBE workspace activated \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
2812
|
+
console.log("workspace: " + root);
|
|
2813
|
+
console.log("hook: " + hookPath);
|
|
2814
|
+
console.log("mode: " + mode);
|
|
2815
|
+
console.log("\nNext: open a governed shell session:");
|
|
2816
|
+
console.log(" lbe-exec shell");
|
|
2817
|
+
console.log("\nAny Node.js agent run inside that shell is intercepted.");
|
|
2818
|
+
console.log("Python, Go, native binaries, and PowerShell are NOT governed.");
|
|
2819
|
+
break;
|
|
2820
|
+
}
|
|
2821
|
+
case "shell": {
|
|
2822
|
+
const activationFile = path15.join(process.cwd(), ".lbe", "activation.json");
|
|
2823
|
+
let activation = null;
|
|
2824
|
+
if (fs14.existsSync(activationFile)) {
|
|
2825
|
+
try {
|
|
2826
|
+
activation = JSON.parse(fs14.readFileSync(activationFile, "utf8"));
|
|
2827
|
+
} catch (_) {
|
|
2846
2828
|
}
|
|
2829
|
+
}
|
|
2830
|
+
const hookPath = activation && activation.hookPath || findHookPath();
|
|
2831
|
+
if (!fs14.existsSync(hookPath)) {
|
|
2832
|
+
console.error("Hook not found. Run: lbe-exec activate");
|
|
2833
|
+
process.exit(1);
|
|
2834
|
+
}
|
|
2835
|
+
const mode = opts.mode || activation && activation.mode || "observe";
|
|
2836
|
+
const root = activation && activation.root || process.cwd();
|
|
2837
|
+
const hookPathFwd = hookPath.replace(/\\/g, "/");
|
|
2838
|
+
const nodeOpts = '--require "' + hookPathFwd + '"';
|
|
2839
|
+
const shellEnv = { ...process.env, NODE_OPTIONS: nodeOpts, LBE_ROOT: root, LBE_MODE: mode };
|
|
2840
|
+
console.log("[lbe] Opening governed shell \u2014 mode: " + mode);
|
|
2841
|
+
console.log("[lbe] NODE_OPTIONS set. Node.js agents are intercepted.");
|
|
2842
|
+
console.log("[lbe] Python / Go / native binaries are NOT governed.");
|
|
2843
|
+
console.log('[lbe] Type "exit" to close.\n');
|
|
2844
|
+
let shellProc;
|
|
2845
|
+
if (process.platform === "win32") {
|
|
2846
|
+
const banner = [
|
|
2847
|
+
`$env:NODE_OPTIONS='--require "${hookPathFwd}"'`,
|
|
2848
|
+
`$env:LBE_ROOT='${root}'`,
|
|
2849
|
+
`$env:LBE_MODE='${mode}'`,
|
|
2850
|
+
`Write-Host '[lbe] Shell armed \u2014 mode: ${mode}' -ForegroundColor Green`
|
|
2851
|
+
].join("; ");
|
|
2852
|
+
shellProc = spawn(
|
|
2853
|
+
"powershell.exe",
|
|
2854
|
+
["-NoExit", "-Command", banner],
|
|
2855
|
+
{ stdio: "inherit", env: shellEnv }
|
|
2856
|
+
);
|
|
2847
2857
|
} else {
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
console.log("hook: " + hookPath);
|
|
2851
|
-
console.log("mode: " + mode);
|
|
2852
|
-
console.log("\nCopy and run in your terminal to arm this session:\n");
|
|
2853
|
-
if (process.platform === "win32") {
|
|
2854
|
-
console.log("# PowerShell:");
|
|
2855
|
-
console.log(`$env:NODE_OPTIONS='--require "` + hookPath + `"'`);
|
|
2856
|
-
console.log('$env:LBE_ROOT="' + root + '"');
|
|
2857
|
-
console.log('$env:LBE_MODE="' + mode + '"');
|
|
2858
|
-
console.log("\n# cmd.exe:");
|
|
2859
|
-
console.log('set NODE_OPTIONS=--require "' + hookPath + '"');
|
|
2860
|
-
console.log("set LBE_ROOT=" + root);
|
|
2861
|
-
console.log("set LBE_MODE=" + mode);
|
|
2862
|
-
} else {
|
|
2863
|
-
console.log(`export NODE_OPTIONS='--require "` + hookPath + `"'`);
|
|
2864
|
-
console.log('export LBE_ROOT="' + root + '"');
|
|
2865
|
-
console.log('export LBE_MODE="' + mode + '"');
|
|
2866
|
-
}
|
|
2867
|
-
console.log("\nAfter running: every node / npm / npx command in this session is governed.");
|
|
2868
|
-
console.log("To persist across all terminals: lbe-exec activate --permanent");
|
|
2858
|
+
const sh = process.env.SHELL || "/bin/bash";
|
|
2859
|
+
shellProc = spawn(sh, [], { stdio: "inherit", env: shellEnv });
|
|
2869
2860
|
}
|
|
2861
|
+
shellProc.on("close", (code) => {
|
|
2862
|
+
console.log("\n[lbe] Governed shell closed.");
|
|
2863
|
+
process.exit(code ?? 0);
|
|
2864
|
+
});
|
|
2870
2865
|
break;
|
|
2871
2866
|
}
|
|
2872
2867
|
case "deactivate": {
|
|
2873
|
-
const
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
], { encoding: "utf8" });
|
|
2884
|
-
const current = (psRead.stdout || "").trim();
|
|
2885
|
-
const cleaned = current.replace('--require "' + hookPath + '"', "").replace("--require " + hookPath, "").trim();
|
|
2886
|
-
spawnSync2("powershell.exe", [
|
|
2887
|
-
"-NoProfile",
|
|
2888
|
-
"-NonInteractive",
|
|
2889
|
-
"-Command",
|
|
2890
|
-
(cleaned ? `[System.Environment]::SetEnvironmentVariable('NODE_OPTIONS','${cleaned}','User');` : `[System.Environment]::SetEnvironmentVariable('NODE_OPTIONS',$null,'User');`) + `[System.Environment]::SetEnvironmentVariable('LBE_ROOT',$null,'User');[System.Environment]::SetEnvironmentVariable('LBE_MODE',$null,'User')`
|
|
2891
|
-
], { stdio: "inherit" });
|
|
2892
|
-
console.log("\u2713 LBE deactivated \u2014 user environment cleared. Restart terminal to apply.");
|
|
2893
|
-
} else {
|
|
2894
|
-
console.log("Remove these lines from your shell RC file (.bashrc / .zshrc):");
|
|
2895
|
-
console.log(" export NODE_OPTIONS=... (the --require lbe line)");
|
|
2896
|
-
console.log(" export LBE_ROOT=...");
|
|
2897
|
-
console.log(" export LBE_MODE=...");
|
|
2868
|
+
const root = process.cwd();
|
|
2869
|
+
const files = [
|
|
2870
|
+
path15.join(root, ".lbe", "activation.json"),
|
|
2871
|
+
path15.join(root, ".lbe", "runtime", "hook-status.json")
|
|
2872
|
+
];
|
|
2873
|
+
let removed = 0;
|
|
2874
|
+
for (const f of files) {
|
|
2875
|
+
if (fs14.existsSync(f)) {
|
|
2876
|
+
fs14.unlinkSync(f);
|
|
2877
|
+
removed++;
|
|
2898
2878
|
}
|
|
2879
|
+
}
|
|
2880
|
+
if (removed) {
|
|
2881
|
+
console.log("\u2713 LBE deactivated \u2014 workspace activation files removed.");
|
|
2899
2882
|
} else {
|
|
2900
|
-
console.log("
|
|
2901
|
-
if (process.platform === "win32") {
|
|
2902
|
-
console.log("# PowerShell:");
|
|
2903
|
-
console.log("Remove-Item Env:NODE_OPTIONS -ErrorAction SilentlyContinue");
|
|
2904
|
-
console.log("Remove-Item Env:LBE_ROOT -ErrorAction SilentlyContinue");
|
|
2905
|
-
console.log("Remove-Item Env:LBE_MODE -ErrorAction SilentlyContinue");
|
|
2906
|
-
console.log("\n# cmd.exe:");
|
|
2907
|
-
console.log("set NODE_OPTIONS=");
|
|
2908
|
-
console.log("set LBE_ROOT=");
|
|
2909
|
-
console.log("set LBE_MODE=");
|
|
2910
|
-
} else {
|
|
2911
|
-
console.log("unset NODE_OPTIONS LBE_ROOT LBE_MODE");
|
|
2912
|
-
}
|
|
2883
|
+
console.log("Nothing to deactivate (workspace was not activated).");
|
|
2913
2884
|
}
|
|
2885
|
+
console.log('Close any open "lbe-exec shell" sessions to fully disarm.');
|
|
2914
2886
|
break;
|
|
2915
2887
|
}
|
|
2916
2888
|
case "observe":
|
|
@@ -2964,10 +2936,11 @@ export LBE_MODE="${mode}"
|
|
|
2964
2936
|
console.log(" status Show workspace, mode, hook state, patched functions");
|
|
2965
2937
|
console.log(" audit Show unified event log (.lbe/events.jsonl)");
|
|
2966
2938
|
console.log(" policy List active policy rules");
|
|
2967
|
-
console.log(" activate
|
|
2968
|
-
console.log(" [--mode observe|enforce]
|
|
2969
|
-
console.log("
|
|
2970
|
-
console.log(" [--
|
|
2939
|
+
console.log(" activate Write workspace activation record (Node.js only)");
|
|
2940
|
+
console.log(" [--mode observe|enforce]");
|
|
2941
|
+
console.log(" shell Open a governed terminal (NODE_OPTIONS pre-set)");
|
|
2942
|
+
console.log(" [--mode observe|enforce]");
|
|
2943
|
+
console.log(" deactivate Remove workspace activation files");
|
|
2971
2944
|
console.log(" observe Switch to observer mode (log only, nothing blocked)");
|
|
2972
2945
|
console.log(" enforce Switch to enforcement mode (violations blocked)");
|
|
2973
2946
|
console.log(" execute Send a JSON request from stdin or --input file");
|