@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.
Files changed (2) hide show
  1. package/dist/cli.js +126 -153
  2. 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 hookFlag = "--require " + hookPath;
2688
- const nodeOptions = existing.includes(hookFlag) ? existing : (existing + " " + hookFlag).trim();
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 policy = loadPolicy();
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: " + process.cwd());
2702
- if (policy) {
2703
- console.log("mode: " + policy.mode);
2704
- console.log("rules: " + (policy.rules?.length ?? 0));
2705
- console.log("audit: " + countAudit() + " entries (.lbe/audit.jsonl)");
2706
- } else {
2707
- console.log("policy: not found \u2014 run: npx lbe-exec init");
2708
- }
2709
- const statusFile = path15.join(process.cwd(), ".lbe", "runtime", "hook-status.json");
2710
- if (!fs14.existsSync(statusFile)) {
2711
- console.log("\nhook: inactive \u2014 use: npx lbe-exec run-node ./agent.js");
2712
- break;
2713
- }
2714
- let hookStatus;
2715
- try {
2716
- hookStatus = JSON.parse(fs14.readFileSync(statusFile, "utf8"));
2717
- } catch (e) {
2718
- console.log("\nhook: status file unreadable \u2014 " + e.message);
2719
- break;
2720
- }
2721
- let pidAlive = false;
2722
- try {
2723
- process.kill(hookStatus.pid, 0);
2724
- pidAlive = true;
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 activationFile = path15.join(process.cwd(), ".lbe", "activation.json");
2739
- const sessionActive = (process.env.NODE_OPTIONS || "").includes("register.cjs");
2740
- if (fs14.existsSync(activationFile)) {
2723
+ const statusFile = path15.join(root, ".lbe", "runtime", "hook-status.json");
2724
+ if (fs14.existsSync(statusFile)) {
2725
+ let h;
2741
2726
  try {
2742
- const act = JSON.parse(fs14.readFileSync(activationFile, "utf8"));
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
- } else if (sessionActive) {
2747
- console.log("\nactivation: session active NODE_OPTIONS \u2713");
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("\nactivation: none \u2014 run: lbe-exec activate");
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 + "\nRun: npm install @letterblack/lbe-exec");
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
- if (opts.permanent) {
2813
- if (process.platform === "win32") {
2814
- const psRead = spawnSync2("powershell.exe", [
2815
- "-NoProfile",
2816
- "-NonInteractive",
2817
- "-Command",
2818
- '[System.Environment]::GetEnvironmentVariable("NODE_OPTIONS","User")'
2819
- ], { encoding: "utf8" });
2820
- const current = (psRead.stdout || "").trim();
2821
- const merged = current.includes(hookPath) ? current : (current + " " + reqFlag).trim();
2822
- spawnSync2("powershell.exe", [
2823
- "-NoProfile",
2824
- "-NonInteractive",
2825
- "-Command",
2826
- `[System.Environment]::SetEnvironmentVariable('NODE_OPTIONS','${merged}','User');[System.Environment]::SetEnvironmentVariable('LBE_ROOT','${root}','User');[System.Environment]::SetEnvironmentVariable('LBE_MODE','${mode}','User')`
2827
- ], { stdio: "inherit" });
2828
- console.log("\n\u2713 LBE permanently activated \u2014 Windows user environment updated.");
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
- console.log("\u2500\u2500 LBE Workspace Activation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
2849
- console.log("workspace: " + root);
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 activationFile = path15.join(process.cwd(), ".lbe", "activation.json");
2874
- if (fs14.existsSync(activationFile)) fs14.unlinkSync(activationFile);
2875
- if (opts.permanent) {
2876
- if (process.platform === "win32") {
2877
- const hookPath = findHookPath();
2878
- const psRead = spawnSync2("powershell.exe", [
2879
- "-NoProfile",
2880
- "-NonInteractive",
2881
- "-Command",
2882
- '[System.Environment]::GetEnvironmentVariable("NODE_OPTIONS","User")'
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("Run these commands to disarm this session:\n");
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 Arm workspace \u2014 every node/npm/npx is governed");
2968
- console.log(" [--mode observe|enforce] [--permanent]");
2969
- console.log(" deactivate Disarm workspace");
2970
- console.log(" [--permanent]");
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");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterblack/lbe-exec",
3
- "version": "1.2.15",
3
+ "version": "1.2.16",
4
4
  "description": "Local host-signed execution layer for LetterBlack LBE.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",