@letterblack/lbe-exec 1.2.16 → 1.2.17
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 +63 -123
- package/dist/index.js +1 -1
- package/hooks/register.cjs +4 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -404,7 +404,7 @@ var init_localPolicy = __esm({
|
|
|
404
404
|
"src/core/localPolicy.js"() {
|
|
405
405
|
init_auditLog();
|
|
406
406
|
init_atomicWrite();
|
|
407
|
-
POLICY_FILE = "lbe
|
|
407
|
+
POLICY_FILE = ".lbe/policy.json";
|
|
408
408
|
AUDIT_FILE = ".lbe/audit.jsonl";
|
|
409
409
|
}
|
|
410
410
|
});
|
|
@@ -2363,12 +2363,8 @@ This project has LetterBlack LBE (Local-first execution Governance) active.
|
|
|
2363
2363
|
|
|
2364
2364
|
## Your role as an agent
|
|
2365
2365
|
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
LBE executor. The executor validates every action against the project policy,
|
|
2369
|
-
signs it, audits it, and either allows or blocks it \u2014 transparently.
|
|
2370
|
-
|
|
2371
|
-
You do not need to know how this works. You only need to use the right methods.
|
|
2366
|
+
Every file write, delete, and shell command must go through the LBE executor.
|
|
2367
|
+
The executor validates each action against the project policy and audits it.
|
|
2372
2368
|
|
|
2373
2369
|
## How to perform actions
|
|
2374
2370
|
|
|
@@ -2376,143 +2372,83 @@ You do not need to know how this works. You only need to use the right methods.
|
|
|
2376
2372
|
import { createLocalExecutor } from '@letterblack/lbe-exec';
|
|
2377
2373
|
const lbe = createLocalExecutor({ rootDir: process.cwd() });
|
|
2378
2374
|
|
|
2379
|
-
// File operations
|
|
2380
2375
|
await lbe.writeFile('output/report.md', content);
|
|
2381
2376
|
await lbe.readFile('src/config.json');
|
|
2382
2377
|
await lbe.patchFile('src/index.js', patch);
|
|
2383
2378
|
await lbe.deleteFile('tmp/scratch.txt');
|
|
2384
|
-
|
|
2385
|
-
// Shell (only explicitly allowlisted commands run)
|
|
2386
2379
|
await lbe.runShell('node', ['scripts/build.js']);
|
|
2387
2380
|
\`\`\`
|
|
2388
2381
|
|
|
2389
2382
|
## What you must never do
|
|
2390
2383
|
|
|
2391
|
-
- Call
|
|
2392
|
-
-
|
|
2393
|
-
-
|
|
2394
|
-
- Attempt to bypass, patch, or disable the executor
|
|
2395
|
-
|
|
2396
|
-
## Proposing a new policy rule
|
|
2384
|
+
- Call fs.* or child_process.* directly
|
|
2385
|
+
- Modify \`.lbe/policy.json\` directly \u2014 propose a rule via \`lbe.policy.proposeRule()\`
|
|
2386
|
+
- Attempt to bypass or disable the executor
|
|
2397
2387
|
|
|
2398
|
-
|
|
2399
|
-
controller decide:
|
|
2388
|
+
## Proposing a policy rule
|
|
2400
2389
|
|
|
2401
2390
|
\`\`\`js
|
|
2402
2391
|
const proposal = lbe.policy.proposeRule({
|
|
2403
|
-
effect: 'deny',
|
|
2404
|
-
type: 'path',
|
|
2405
|
-
pattern: 'secrets/**',
|
|
2392
|
+
effect: 'deny', type: 'path', pattern: 'secrets/**',
|
|
2406
2393
|
from: 'agent: these files should never be modified'
|
|
2407
2394
|
});
|
|
2408
|
-
// Return proposal to the user
|
|
2395
|
+
// Return proposal to the user \u2014 never call lbe.policy.addRule() yourself.
|
|
2409
2396
|
\`\`\`
|
|
2410
2397
|
|
|
2411
2398
|
## Result shape
|
|
2412
2399
|
|
|
2413
|
-
|
|
2414
|
-
\`\`\`
|
|
2415
|
-
{ ok: boolean, decision: 'allow' | 'deny' | 'observe', executed: boolean }
|
|
2416
|
-
\`\`\`
|
|
2417
|
-
|
|
2418
|
-
If \`ok\` is false, the action was blocked. Check \`result.error.message\` for reason.
|
|
2400
|
+
\`{ ok: boolean, decision: 'allow' | 'deny' | 'observe', executed: boolean }\`
|
|
2419
2401
|
|
|
2420
|
-
##
|
|
2402
|
+
## Files
|
|
2421
2403
|
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
## CLI
|
|
2426
|
-
|
|
2427
|
-
\`\`\`bash
|
|
2428
|
-
npx lbe-exec status # mode, rule count, audit entry count
|
|
2429
|
-
npx lbe-exec policy # list active rules
|
|
2430
|
-
npx lbe-exec observe # switch to observer mode
|
|
2431
|
-
npx lbe-exec enforce # switch to enforcement mode
|
|
2432
|
-
\`\`\`
|
|
2404
|
+
- Policy: \`.lbe/policy.json\`
|
|
2405
|
+
- Audit: \`.lbe/audit.jsonl\`
|
|
2406
|
+
- Status: \`npx lbe-exec status\`
|
|
2433
2407
|
`;
|
|
2434
2408
|
}
|
|
2435
|
-
function
|
|
2436
|
-
return `<!-- lbe-governance -->
|
|
2437
|
-
## LBE Execution Governance
|
|
2438
|
-
|
|
2439
|
-
This project has LetterBlack LBE active. All file and shell operations must
|
|
2440
|
-
route through the LBE executor \u2014 do not use file system tools directly.
|
|
2441
|
-
|
|
2442
|
-
### Use these methods
|
|
2443
|
-
|
|
2444
|
-
\`\`\`js
|
|
2445
|
-
import { createLocalExecutor } from '@letterblack/lbe-exec';
|
|
2446
|
-
const lbe = createLocalExecutor({ rootDir: process.cwd() });
|
|
2447
|
-
|
|
2448
|
-
await lbe.writeFile('path/to/file', content);
|
|
2449
|
-
await lbe.readFile('path/to/file');
|
|
2450
|
-
await lbe.patchFile('path/to/file', patch);
|
|
2451
|
-
await lbe.deleteFile('path/to/file');
|
|
2452
|
-
await lbe.runShell('node', ['script.js']);
|
|
2453
|
-
\`\`\`
|
|
2454
|
-
|
|
2455
|
-
### Rules
|
|
2456
|
-
|
|
2457
|
-
- Never call \`fs.*\` or \`child_process.*\` directly
|
|
2458
|
-
- Never write to \`lbe.policy.json\` directly \u2014 use \`lbe.policy.proposeRule()\`
|
|
2459
|
-
- Every blocked action has a reason in \`result.error.message\`
|
|
2460
|
-
- Audit trail is in \`.lbe/audit.jsonl\`
|
|
2461
|
-
|
|
2462
|
-
See \`.lbe/AGENT_CONTRACT.md\` for the full agent contract.
|
|
2463
|
-
<!-- /lbe-governance -->`;
|
|
2464
|
-
}
|
|
2465
|
-
function copilotInstructionsContent() {
|
|
2466
|
-
return `# LBE Governance \u2014 Copilot Instructions
|
|
2467
|
-
|
|
2468
|
-
This project has LetterBlack LBE active. All file and shell operations must
|
|
2469
|
-
route through the LBE executor.
|
|
2470
|
-
|
|
2471
|
-
## Required
|
|
2472
|
-
|
|
2473
|
-
Use \`@letterblack/lbe-exec\` for all file and shell actions:
|
|
2474
|
-
|
|
2475
|
-
\`\`\`js
|
|
2476
|
-
const lbe = createLocalExecutor({ rootDir: process.cwd() });
|
|
2477
|
-
await lbe.writeFile(path, content);
|
|
2478
|
-
await lbe.readFile(path);
|
|
2479
|
-
await lbe.runShell(cmd, args);
|
|
2480
|
-
\`\`\`
|
|
2481
|
-
|
|
2482
|
-
## Forbidden
|
|
2483
|
-
|
|
2484
|
-
- Direct \`fs.*\` calls
|
|
2485
|
-
- Direct \`child_process.*\` or \`exec\` calls
|
|
2486
|
-
- Writing to \`lbe.policy.json\` directly
|
|
2487
|
-
|
|
2488
|
-
See \`.lbe/AGENT_CONTRACT.md\` for the full contract.
|
|
2489
|
-
`;
|
|
2490
|
-
}
|
|
2491
|
-
function writeAgentGovernanceFiles(cwd) {
|
|
2409
|
+
function writeAgentContract(cwd) {
|
|
2492
2410
|
const lbeDir = path4.join(cwd, ".lbe");
|
|
2493
2411
|
fs4.mkdirSync(lbeDir, { recursive: true });
|
|
2494
2412
|
fs4.writeFileSync(path4.join(lbeDir, "AGENT_CONTRACT.md"), agentContractContent());
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2413
|
+
}
|
|
2414
|
+
function migrateLegacyRootFiles(cwd) {
|
|
2415
|
+
const lbeDir = path4.join(cwd, ".lbe");
|
|
2416
|
+
fs4.mkdirSync(lbeDir, { recursive: true });
|
|
2417
|
+
const migrations = [
|
|
2418
|
+
["lbe.policy.json", ".lbe/policy.json"],
|
|
2419
|
+
["lbe.workspace.json", ".lbe/workspace.json"]
|
|
2420
|
+
];
|
|
2421
|
+
const removed = [];
|
|
2422
|
+
for (const [src, dest] of migrations) {
|
|
2423
|
+
const srcPath = path4.join(cwd, src);
|
|
2424
|
+
const destPath = path4.join(cwd, dest);
|
|
2425
|
+
if (fs4.existsSync(srcPath) && !fs4.existsSync(destPath)) {
|
|
2426
|
+
fs4.renameSync(srcPath, destPath);
|
|
2427
|
+
removed.push(src + " \u2192 " + dest);
|
|
2428
|
+
} else if (fs4.existsSync(srcPath)) {
|
|
2429
|
+
fs4.unlinkSync(srcPath);
|
|
2430
|
+
removed.push(src + " (removed \u2014 .lbe/ version exists)");
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
const toDelete = ["CLAUDE.md", path4.join(".github", "copilot-instructions.md")];
|
|
2434
|
+
for (const rel of toDelete) {
|
|
2435
|
+
const p = path4.join(cwd, rel);
|
|
2436
|
+
if (fs4.existsSync(p)) {
|
|
2437
|
+
const content = fs4.readFileSync(p, "utf8");
|
|
2438
|
+
if (content.includes("lbe-governance") || content.includes("LetterBlack LBE")) {
|
|
2439
|
+
fs4.unlinkSync(p);
|
|
2440
|
+
removed.push(rel + " (removed \u2014 LBE-generated file)");
|
|
2441
|
+
}
|
|
2501
2442
|
}
|
|
2502
|
-
} else {
|
|
2503
|
-
fs4.writeFileSync(claudePath, section + "\n");
|
|
2504
|
-
}
|
|
2505
|
-
const githubDir = path4.join(cwd, ".github");
|
|
2506
|
-
fs4.mkdirSync(githubDir, { recursive: true });
|
|
2507
|
-
const copilotPath = path4.join(githubDir, "copilot-instructions.md");
|
|
2508
|
-
if (!fs4.existsSync(copilotPath)) {
|
|
2509
|
-
fs4.writeFileSync(copilotPath, copilotInstructionsContent());
|
|
2510
2443
|
}
|
|
2444
|
+
return removed;
|
|
2511
2445
|
}
|
|
2512
2446
|
async function initCommand(opts2 = {}) {
|
|
2513
2447
|
const cwd = process.cwd();
|
|
2514
2448
|
const yes = opts2.yes || opts2.y || !process.stdin.isTTY;
|
|
2515
|
-
const
|
|
2449
|
+
const lbeDir = path4.join(cwd, ".lbe");
|
|
2450
|
+
fs4.mkdirSync(lbeDir, { recursive: true });
|
|
2451
|
+
const outPath = path4.join(lbeDir, "workspace.json");
|
|
2516
2452
|
console.log("\nScanning workspace...\n");
|
|
2517
2453
|
const { projectTypes, primaryType: primaryType2, semantics, enforcement } = scanWorkspace(cwd);
|
|
2518
2454
|
console.log(formatSummary(projectTypes, semantics, enforcement));
|
|
@@ -2537,21 +2473,24 @@ async function initCommand(opts2 = {}) {
|
|
|
2537
2473
|
enforcement: finalEnforcement
|
|
2538
2474
|
};
|
|
2539
2475
|
fs4.writeFileSync(outPath, JSON.stringify(contract, null, 2));
|
|
2540
|
-
console.log("\u2713 Wrote lbe
|
|
2476
|
+
console.log("\u2713 Wrote .lbe/workspace.json");
|
|
2541
2477
|
setupCrypto(cwd);
|
|
2542
|
-
const localPolicyPath = path4.join(
|
|
2478
|
+
const localPolicyPath = path4.join(lbeDir, "policy.json");
|
|
2543
2479
|
if (!fs4.existsSync(localPolicyPath)) {
|
|
2544
2480
|
fs4.writeFileSync(localPolicyPath, JSON.stringify({ version: 1, mode: "observe", workspace: cwd, rules: [] }, null, 2) + "\n");
|
|
2545
2481
|
}
|
|
2546
|
-
const localAuditPath = path4.join(
|
|
2482
|
+
const localAuditPath = path4.join(lbeDir, "audit.jsonl");
|
|
2547
2483
|
if (!fs4.existsSync(localAuditPath)) fs4.writeFileSync(localAuditPath, "");
|
|
2548
|
-
console.log("\u2713 Keys and policy ready");
|
|
2549
|
-
|
|
2484
|
+
console.log("\u2713 Keys and policy ready (.lbe/)");
|
|
2485
|
+
writeAgentContract(cwd);
|
|
2550
2486
|
console.log("\u2713 Agent contract written \u2192 .lbe/AGENT_CONTRACT.md");
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2487
|
+
const migrated = migrateLegacyRootFiles(cwd);
|
|
2488
|
+
if (migrated.length) {
|
|
2489
|
+
console.log("\n\u2713 Migrated legacy files:");
|
|
2490
|
+
for (const m of migrated) console.log(" " + m);
|
|
2491
|
+
}
|
|
2492
|
+
console.log("\nDone. All LBE state is in .lbe/");
|
|
2493
|
+
console.log("Run npx lbe-exec status to verify.\n");
|
|
2555
2494
|
return { success: true, contract };
|
|
2556
2495
|
}
|
|
2557
2496
|
|
|
@@ -2571,7 +2510,8 @@ var opts = Object.fromEntries(
|
|
|
2571
2510
|
var positional = rest.filter((v) => !v.startsWith("--") && rest[rest.indexOf(v) - 1]?.startsWith("--") === false);
|
|
2572
2511
|
var __dir = path15.dirname(fileURLToPath2(import.meta.url));
|
|
2573
2512
|
function loadPolicy() {
|
|
2574
|
-
const
|
|
2513
|
+
const cwd = process.cwd();
|
|
2514
|
+
const p = fs14.existsSync(path15.join(cwd, ".lbe", "policy.json")) ? path15.join(cwd, ".lbe", "policy.json") : path15.join(cwd, "lbe.policy.json");
|
|
2575
2515
|
return fs14.existsSync(p) ? JSON.parse(fs14.readFileSync(p, "utf8")) : null;
|
|
2576
2516
|
}
|
|
2577
2517
|
function findHookPath() {
|
|
@@ -2895,7 +2835,7 @@ switch (cmd) {
|
|
|
2895
2835
|
case "policy": {
|
|
2896
2836
|
const policy = loadPolicy();
|
|
2897
2837
|
if (!policy) {
|
|
2898
|
-
console.log("No
|
|
2838
|
+
console.log("No policy found. Run: npx lbe-exec init");
|
|
2899
2839
|
break;
|
|
2900
2840
|
}
|
|
2901
2841
|
if (!policy.rules?.length) {
|
package/dist/index.js
CHANGED
|
@@ -1490,7 +1490,7 @@ function verifyAuditLogIntegrity(logPath, options = {}) {
|
|
|
1490
1490
|
import fs8 from "fs";
|
|
1491
1491
|
import path9 from "path";
|
|
1492
1492
|
import crypto4 from "crypto";
|
|
1493
|
-
var POLICY_FILE = "lbe
|
|
1493
|
+
var POLICY_FILE = ".lbe/policy.json";
|
|
1494
1494
|
var AUDIT_FILE = ".lbe/audit.jsonl";
|
|
1495
1495
|
function glob(pattern) {
|
|
1496
1496
|
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
package/hooks/register.cjs
CHANGED
|
@@ -14,7 +14,10 @@ const MODE = process.env.LBE_MODE || 'observe';
|
|
|
14
14
|
// ── Policy loader (inline CJS — ESM cannot be require()'d synchronously) ────
|
|
15
15
|
|
|
16
16
|
function loadPolicy() {
|
|
17
|
-
|
|
17
|
+
// .lbe/policy.json is canonical. Fall back to legacy lbe.policy.json in root.
|
|
18
|
+
const policyPath = fs.existsSync(path.join(ROOT_DIR, '.lbe', 'policy.json'))
|
|
19
|
+
? path.join(ROOT_DIR, '.lbe', 'policy.json')
|
|
20
|
+
: path.join(ROOT_DIR, 'lbe.policy.json');
|
|
18
21
|
try {
|
|
19
22
|
if (fs.existsSync(policyPath)) {
|
|
20
23
|
return JSON.parse(fs.readFileSync(policyPath, 'utf8'));
|