@letterblack/lbe-sdk 0.4.0

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.
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
3
+ console.log(`LBE SDK MCP Server
4
+
5
+ Usage:
6
+ npx lbe-mcp
7
+
8
+ Setup:
9
+ npx lbe init
10
+
11
+ Tools:
12
+ lbe_workspace_context Read local workspace context
13
+ lbe_write_file Governed file write
14
+ lbe_read_file Governed file read
15
+ lbe_patch_file Governed file patch
16
+ lbe_shell_command Governed command run
17
+ lbe_health Check SDK readiness`);
18
+ process.exit(0);
19
+ }import ze from"fs";import Mt from"path";import _e from"crypto";import Pt from"fs";import Pr from"os";import _ from"path";import Oe from"tweetnacl";import{canonicalize as tt}from"json-canonicalize";function Le(e){return Buffer.from(e,"base64")}function Re(e){return Buffer.from(e).toString("base64")}function Z({payloadObj:e,sigB64:t,pubKeyB64:r}){try{let s=Buffer.from(tt(e),"utf8"),o=Le(t),n=Le(r),i=Oe.sign.detached.verify(new Uint8Array(s),new Uint8Array(o),new Uint8Array(n));return{valid:i,message:i?"Signature verified":"Signature verification failed"}}catch(s){return{valid:!1,message:`Signature verification error: ${s.message}`}}}function Ce(){let e=Oe.sign.keyPair();return{publicKey:Re(e.publicKey),secretKey:Re(e.secretKey)}}function de({payloadObj:e,secretKeyB64:t}){try{let r=Buffer.from(tt(e),"utf8"),s=Le(t),o=Oe.sign.detached(new Uint8Array(r),new Uint8Array(s));return{signature:Re(o),error:null}}catch(r){return{signature:null,error:`Signing failed: ${r.message}`}}}import rt from"fs";import nt from"path";import{fileURLToPath as Xt}from"url";var Qt=nt.dirname(Xt(import.meta.url)),Ne=nt.join(Qt,"../runtime/lbe_engine.wasm"),st={0:{allowed:!0,reason:null,message:"Policy check passed"},1:{allowed:!1,reason:"POLICY_NOT_CONFIGURED",message:"No policy configured"},2:{allowed:!1,reason:"REQUESTER_NOT_ALLOWED",message:"Requester not in policy"},3:{allowed:!1,reason:"COMMAND_NOT_ALLOWED",message:"Command not allowed for requester"},4:{allowed:!1,reason:"ADAPTER_NOT_ALLOWED",message:"Adapter not allowed"},5:{allowed:!1,reason:"NO_FILESYSTEM_ROOTS_DEFINED",message:"No filesystem roots defined for requester"},6:{allowed:!1,reason:"CWD_OUTSIDE_ALLOWED_ROOT",message:"Path not under allowed roots"},7:{allowed:!1,reason:"PATH_DENIED_BY_PATTERN",message:"Path matches deny pattern"},8:{allowed:!1,reason:"SHELL_CMD_DENIED",message:"Shell command not allowed"}},ot={0:{valid:!0,error:null},1:{valid:!1,error:"Missing required field: id"},2:{valid:!1,error:"Missing required field: commandId"},3:{valid:!1,error:"Missing required field: requesterId"},4:{valid:!1,error:"Missing required field: sessionId"},5:{valid:!1,error:"Missing required field: timestamp"},6:{valid:!1,error:"Missing required field: nonce"},7:{valid:!1,error:"Missing required field: requires"},8:{valid:!1,error:"Missing required field: payload"},9:{valid:!1,error:"Missing required field: signature"},10:{valid:!1,error:"Field 'id' is invalid"},11:{valid:!1,error:"Field 'commandId' is invalid"},12:{valid:!1,error:"Field 'requesterId' is invalid"},13:{valid:!1,error:"Field 'sessionId' is invalid"},14:{valid:!1,error:"Field 'timestamp' is invalid"},15:{valid:!1,error:"Field 'nonce' is invalid"},16:{valid:!1,error:"Field 'requires' is invalid"},17:{valid:!1,error:"payload.Missing required field: adapter"},18:{valid:!1,error:"payload.Field 'adapter' is invalid"},19:{valid:!1,error:"signature.Missing required field: alg"},20:{valid:!1,error:"signature.Missing required field: keyId"},21:{valid:!1,error:"signature.Missing required field: sig"},22:{valid:!1,error:"signature.Field 'alg' must be ed25519"},23:{valid:!1,error:"signature.Field 'sig' is invalid"},24:{valid:!1,error:"Field 'risk' is invalid"}},ue=null;function at(){if(ue)return ue;if(!rt.existsSync(Ne))throw new Error(`LBE compiled engine missing: ${Ne}`);let e=rt.readFileSync(Ne),t=new WebAssembly.Module(e);return ue=new WebAssembly.Instance(t,{}),ue}function it(e){let r=at().exports.lbe_policy_decision(e.policyConfigured?1:0,e.requesterConfigured?1:0,e.commandAllowed?1:0,e.adapterAllowed?1:0,e.filesystemRequired?1:0,e.filesystemRootsDefined?1:0,e.filesystemOk?1:0,e.pathDenied?1:0,e.shellRequired?1:0,e.shellCommandOk?1:0);return{...st[r]||st[1],code:r}}function ct(e){let r=at().exports.lbe_schema_decision(e.hasId?1:0,e.idValid?1:0,e.hasCommandId?1:0,e.commandIdValid?1:0,e.hasRequesterId?1:0,e.requesterIdValid?1:0,e.hasSessionId?1:0,e.sessionIdValid?1:0,e.hasTimestamp?1:0,e.timestampValid?1:0,e.hasNonce?1:0,e.nonceValid?1:0,e.hasRequires?1:0,e.requiresValid?1:0,e.hasPayload?1:0,e.hasPayloadAdapter?1:0,e.payloadAdapterValid?1:0,e.hasSignature?1:0,e.hasSignatureAlg?1:0,e.signatureAlgValid?1:0,e.hasSignatureKeyId?1:0,e.hasSignatureSig?1:0,e.signatureSigValid?1:0,e.hasRisk?1:0,e.riskValid?1:0);return{...ot[r]||ot[10],code:r}}var lt={type:"object",required:["id","commandId","requesterId","sessionId","timestamp","nonce","requires","payload","signature"],properties:{id:{type:"string",pattern:"^[A-Z_]+$",minLength:1,maxLength:50},commandId:{type:"string",pattern:"^[a-f0-9\\-]+$",minLength:36,maxLength:36},requesterId:{type:"string",minLength:3,maxLength:100},sessionId:{type:"string",minLength:3},timestamp:{type:"number",minimum:1e9},nonce:{type:"string",minLength:32,maxLength:128},requires:{type:"array",items:{type:"string"},minItems:1},risk:{type:"string",enum:["LOW","MEDIUM","HIGH","CRITICAL"]},payload:{type:"object",required:["adapter"],properties:{adapter:{type:"string"},cmd:{type:"string"},args:{type:"array"},cwd:{type:"string"}}},signature:{type:"object",required:["alg","keyId","sig"],properties:{alg:{type:"string",enum:["ed25519"]},keyId:{type:"string"},sig:{type:"string",minLength:10}}}}};function dt(e,t){let r=l=>e&&Object.prototype.hasOwnProperty.call(e,l),s=l=>typeof l=="string",o=(l,d)=>s(l)&&d.test(l),n=e?.payload,i=e?.signature,a=ct({hasId:r("id"),idValid:o(e?.id,/^[A-Z_]+$/)&&e.id.length>=1&&e.id.length<=50,hasCommandId:r("commandId"),commandIdValid:o(e?.commandId,/^[a-f0-9-]+$/)&&e.commandId.length===36,hasRequesterId:r("requesterId"),requesterIdValid:s(e?.requesterId)&&e.requesterId.length>=3&&e.requesterId.length<=100,hasSessionId:r("sessionId"),sessionIdValid:s(e?.sessionId)&&e.sessionId.length>=3,hasTimestamp:r("timestamp"),timestampValid:typeof e?.timestamp=="number"&&e.timestamp>=1e9,hasNonce:r("nonce"),nonceValid:s(e?.nonce)&&e.nonce.length>=32&&e.nonce.length<=128,hasRequires:r("requires"),requiresValid:Array.isArray(e?.requires)&&e.requires.length>=1&&e.requires.every(s),hasPayload:r("payload")&&typeof n=="object"&&n!==null&&!Array.isArray(n),hasPayloadAdapter:n&&Object.prototype.hasOwnProperty.call(n,"adapter"),payloadAdapterValid:s(n?.adapter),hasSignature:r("signature")&&typeof i=="object"&&i!==null&&!Array.isArray(i),hasSignatureAlg:i&&Object.prototype.hasOwnProperty.call(i,"alg"),signatureAlgValid:i?.alg==="ed25519",hasSignatureKeyId:i&&Object.prototype.hasOwnProperty.call(i,"keyId"),hasSignatureSig:i&&Object.prototype.hasOwnProperty.call(i,"sig"),signatureSigValid:s(i?.sig)&&i.sig.length>=10,hasRisk:r("risk"),riskValid:["LOW","MEDIUM","HIGH","CRITICAL"].includes(e?.risk)}),c=a.valid?[]:[a.error];return{valid:c.length===0,errors:c}}import pe from"fs";import sr from"path";import E from"fs";import De from"path";import Zt from"crypto";var er={timeoutMs:5e3,pollMs:15,staleMs:3e4};function tr(e){return e+".lock"}function ut(e){try{let t=E.openSync(e,"wx");return E.writeSync(t,`pid:${process.pid}:${Date.now()}`),E.closeSync(t),!0}catch(t){if(t.code==="EEXIST"||t.code==="EPERM"||t.code==="EBUSY"||t.code==="EACCES")return!1;throw t}}function pt(e,t){try{let r=E.statSync(e);if(Date.now()-r.mtimeMs>t)try{E.unlinkSync(e)}catch{}}catch{}}function rr(e){let t=Date.now()+e;for(;Date.now()<t;)try{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,Math.max(1,t-Date.now()))}catch{}}function ft(e,t,r){let s=typeof t=="function"?t:r,o=typeof t=="function"?{}:t||{},{timeoutMs:n,pollMs:i,staleMs:a}={...er,...o},c=De.dirname(e);E.existsSync(c)||E.mkdirSync(c,{recursive:!0});let l=tr(e),d=Date.now()+n,u=!1;for(;!u&&(u=ut(l),!u);){if(Date.now()>=d){if(pt(l,a),u=ut(l),u)break;let p=new Error(`withFileLock: timeout acquiring ${l} after ${n}ms`);throw p.code="ELOCKTIMEOUT",p}pt(l,a);let m=Math.floor(Math.random()*i);rr(i+m)}try{return s()}finally{try{E.unlinkSync(l)}catch{}}}function I(e,t,r={}){let s=De.dirname(e);E.existsSync(s)||E.mkdirSync(s,{recursive:!0});let o=De.join(s,`.tmp-${Date.now()}-${Zt.randomBytes(4).toString("hex")}`);try{E.writeFileSync(o,t,r),E.renameSync(o,e)}catch(n){try{E.existsSync(o)&&E.unlinkSync(o)}catch{}throw n}}function mt(e){try{if(!E.existsSync(e))return null;let t=E.readFileSync(e,"utf8");return JSON.parse(t)}catch(t){return console.error(`[atomicWrite] Failed to read JSON from ${e}:`,t.message),null}}var fe=class{constructor(t,r=3600){this.dbPath=t,this.ttlSec=r,this.db={entries:[]}}async load(){if(!pe.existsSync(this.dbPath)){this.db={entries:[]};return}try{let t=pe.readFileSync(this.dbPath,"utf8");this.db=JSON.parse(t),this.prune()}catch(t){throw new Error(`Nonce DB at ${this.dbPath} is corrupt or unreadable: ${t.message}`)}}async save(){try{let t=sr.dirname(this.dbPath);pe.existsSync(t)||pe.mkdirSync(t,{recursive:!0}),I(this.dbPath,JSON.stringify(this.db,null,2),{encoding:"utf8"})}catch(t){throw new Error(`Failed to save nonce DB: ${t.message}`)}}checkAndRecord({requesterId:t,sessionId:r,nonce:s}){let o=Math.floor(Date.now()/1e3);this.db.entries=this.db.entries.filter(i=>o-i.timestamp<=this.ttlSec);let n=`${t}|${r}|${s}`;return this.db.entries.some(i=>i.key===n)?{ok:!1,reason:"REPLAY_NONCE",message:"Nonce has already been used"}:(this.db.entries.push({key:n,timestamp:o}),{ok:!0,reason:null,message:"Nonce accepted"})}prune(){let t=Math.floor(Date.now()/1e3),r=this.db.entries.length;this.db.entries=this.db.entries.filter(o=>t-o.timestamp<=this.ttlSec);let s=this.db.entries.length;return{prunedCount:r-s,remainingCount:s}}};function yt(e,{requesterId:t,sessionId:r,nonce:s,timestamp:o},n=3600){let i=Math.floor(Date.now()/1e3);e.entries=e.entries.filter(c=>i-c.timestamp<=n);let a=`${t}|${r}|${s}`;return e.entries.some(c=>c.key===a)?{ok:!1,reason:"REPLAY_NONCE",message:"Nonce has already been used"}:(e.entries.push({key:a,timestamp:o}),{ok:!0,reason:null,message:"Nonce accepted"})}import Te from"path";function or(e,t){if(!t||t.length===0)return!1;let r=Te.resolve(e);return t.some(s=>{let o=Te.resolve(s);return r===o||r.startsWith(o+Te.sep)})}function nr(e,t){let r=t.replace(/\./g,"\\.").replace(/\*\*/g,".*").replace(/\*/g,"[^/]*");return new RegExp(`^${r}$`).test(e)}function ht(e,t){let r=!!(e&&!(e.default==="DENY"&&!e.requesters)),s=e?.requesters?.[t.requesterId],o=!!s,n=t.id.toLowerCase(),i=s?.allowCommands?.some(S=>S.toLowerCase()===n)||!1,a=s?.allowAdapters?.includes(t.payload?.adapter)||!1,c=!1,l=!1,d=!0,u=!1,m=null;if(t.payload?.cwd){c=!0;let S=s?.filesystem?.roots||[];l=S.length>0,d=l&&or(t.payload.cwd,S);let k=s?.filesystem?.denyPatterns||[];for(let D of k)if(nr(t.payload.cwd,D)){u=!0,m=D;break}}let p=!1,N=!0,v=!1;if(t.id==="RUN_SHELL"){p=!0;let S=s?.exec?.allowCmds||[],k=s?.exec?.denyCmds||[],D=t.payload?.cmd;v=k.includes(D),N=!v&&(S.length===0||S.includes(D))}let h=it({policyConfigured:r,requesterConfigured:o,commandAllowed:i,adapterAllowed:a,filesystemRequired:c,filesystemRootsDefined:l,filesystemOk:d,pathDenied:u,shellRequired:p,shellCommandOk:N});if(h.allowed)return h;if(h.reason==="REQUESTER_NOT_ALLOWED")return{...h,message:`Requester '${t.requesterId}' not in policy`};if(h.reason==="COMMAND_NOT_ALLOWED")return{...h,message:`Command '${t.id}' not allowed for requester`};if(h.reason==="ADAPTER_NOT_ALLOWED")return{...h,message:`Adapter '${t.payload?.adapter}' not allowed`};if(h.reason==="CWD_OUTSIDE_ALLOWED_ROOT")return{...h,message:`Path '${t.payload.cwd}' not under allowed roots`};if(h.reason==="PATH_DENIED_BY_PATTERN")return{...h,message:`Path '${t.payload.cwd}' matches deny pattern: ${m}`};if(h.reason==="SHELL_CMD_DENIED"){let S=t.payload?.cmd;return{...h,reason:v?"SHELL_CMD_DENIED":"SHELL_CMD_NOT_ALLOWLISTED",message:v?`Shell command '${S}' is explicitly denied`:`Shell command '${S}' not in allowlist`}}return h}function gt(e){return e.payload?.cmd==="rm"?"CRITICAL":["delete","destroy","remove"].some(t=>e.id.toLowerCase().includes(t))?"HIGH":["write","create","update"].some(t=>e.id.toLowerCase().includes(t))?"MEDIUM":"LOW"}import kt from"fs";import ar from"path";var ir=/^[A-Za-z0-9:_-]{3,128}$/;function Pe(e){return typeof e=="string"&&ir.test(e)&&e!=="default"}function St(e){let t=ar.resolve(e);if(!kt.existsSync(t))return{ok:!1,reason:"KEY_STORE_MISSING",message:`Key store not found: ${t}`,store:null};try{let r=kt.readFileSync(t,"utf-8"),s=JSON.parse(r);return!s||typeof s!="object"||typeof s.trustedKeys!="object"?{ok:!1,reason:"KEY_STORE_INVALID",message:`Invalid key store format: ${t}`,store:null}:{ok:!0,reason:null,message:"Key store loaded",store:s}}catch(r){return{ok:!1,reason:"KEY_STORE_INVALID_JSON",message:`Unable to parse key store: ${r.message}`,store:null}}}function me({keyStore:e,keyId:t,requesterId:r,now:s=new Date}){if(!e||typeof e!="object")return{ok:!1,reason:"KEY_STORE_UNAVAILABLE",message:"Trusted key store is not available",publicKey:null};if(!Pe(t))return{ok:!1,reason:"KEY_ID_INVALID",message:`Invalid keyId '${t}'. Use versioned key IDs like 'agent:gpt-v1-2026Q1'`,publicKey:null};let o=e.trustedKeys?.[t];if(!o)return{ok:!1,reason:"KEY_NOT_TRUSTED",message:`Key '${t}' is not in trusted key store`,publicKey:null};if(o.deprecated)return{ok:!1,reason:"KEY_DEPRECATED",message:`Key '${t}' is deprecated`,publicKey:null};if(o.requesterId&&o.requesterId!==r)return{ok:!1,reason:"KEY_REQUESTER_MISMATCH",message:`Key '${t}' is not authorized for requester '${r}'`,publicKey:null};let n=o.notBefore||o.validFrom,i=o.expiresAt||o.validUntil;if(typeof n!="string"||typeof i!="string")return{ok:!1,reason:"KEY_LIFECYCLE_INVALID",message:`Key '${t}' must define lifecycle fields 'notBefore' and 'expiresAt'`,publicKey:null};let a=new Date(n),c=new Date(i);return Number.isNaN(a.getTime())||Number.isNaN(c.getTime())?{ok:!1,reason:"KEY_LIFECYCLE_INVALID",message:`Key '${t}' has invalid lifecycle timestamp(s)`,publicKey:null}:a>=c?{ok:!1,reason:"KEY_LIFECYCLE_INVALID",message:`Key '${t}' has notBefore >= expiresAt`,publicKey:null}:s<a?{ok:!1,reason:"KEY_NOT_YET_VALID",message:`Key '${t}' not valid until ${n}`,publicKey:null}:s>c?{ok:!1,reason:"KEY_EXPIRED",message:`Key '${t}' expired on ${i}`,publicKey:null}:!o.publicKey||typeof o.publicKey!="string"?{ok:!1,reason:"KEY_CONFIG_INVALID",message:`Trusted key '${t}' is missing publicKey`,publicKey:null}:{ok:!0,reason:null,message:"Trusted key resolved",publicKey:o.publicKey}}import Et from"fs";import cr from"path";function It(e){if(typeof e=="number"&&Number.isFinite(e))return{ok:!0,kind:"int",parts:[Math.floor(e)],raw:String(e)};if(typeof e!="string"||!e.trim())return{ok:!1,reason:"POLICY_VERSION_INVALID",message:"Policy version is required"};let t=e.trim();if(/^\d+$/.test(t))return{ok:!0,kind:"int",parts:[Number(t)],raw:t};let r=t.replace(/^v/i,"");if(/^\d+(\.\d+){0,2}$/.test(r)){let s=r.split(".").map(o=>Number(o));for(;s.length<3;)s.push(0);return{ok:!0,kind:"semver",parts:s,raw:t}}return{ok:!1,reason:"POLICY_VERSION_INVALID",message:`Unsupported policy version format '${e}' (use integer or semver)`}}function lr(e,t){let r=Math.max(e.parts.length,t.parts.length);for(let s=0;s<r;s++){let o=e.parts[s]??0,n=t.parts[s]??0;if(o>n)return 1;if(o<n)return-1}return 0}function _t(e){if(typeof e=="number"&&Number.isFinite(e))return{ok:!0,epochSec:e>1e12?Math.floor(e/1e3):Math.floor(e)};if(typeof e!="string"||!e.trim())return{ok:!1,reason:"POLICY_CREATED_AT_INVALID",message:"Policy createdAt is required"};let t=Date.parse(e);return Number.isNaN(t)?{ok:!1,reason:"POLICY_CREATED_AT_INVALID",message:`Invalid policy createdAt '${e}'`}:{ok:!0,epochSec:Math.floor(t/1e3)}}function dr(e){if(!Et.existsSync(e))return{schemaVersion:"1",lastAccepted:null,updatedAt:null};try{let t=JSON.parse(Et.readFileSync(e,"utf8"));if(!t||typeof t!="object")throw new Error("Policy state file has invalid structure");return{schemaVersion:String(t.schemaVersion||"1"),lastAccepted:t.lastAccepted&&typeof t.lastAccepted=="object"?t.lastAccepted:null,updatedAt:t.updatedAt||null}}catch(t){throw new Error(`Policy state at ${e} is corrupt or unreadable: ${t.message}`)}}function ur(e,t){let r=JSON.stringify(t,null,2);I(e,r,{encoding:"utf8"})}function ye({policyObj:e,statePath:t=cr.resolve("data/policy.state.json"),maxCreatedAtSkewSec:r=31536e3,nowSec:s=Math.floor(Date.now()/1e3),persist:o=!0}){let n=It(e?.version);if(!n.ok)return{ok:!1,reason:n.reason,message:n.message,updated:!1};let i=_t(e?.createdAt);if(!i.ok)return{ok:!1,reason:i.reason,message:i.message,updated:!1};let a=Math.abs(s-i.epochSec),c=Number.isFinite(r)&&r>0?Math.floor(r):31536e3;if(a>c)return{ok:!1,reason:"POLICY_CREATED_AT_SKEW_EXCEEDED",message:`Policy createdAt skew ${a}s exceeds allowed ${c}s`,updated:!1};let l;try{l=dr(t)}catch(v){return{ok:!1,reason:"POLICY_STATE_CORRUPT",message:v.message,updated:!1}}let d=l.lastAccepted,u=null,m=null,p=0;if(d&&(u=It(d.version),m=_t(d.createdAt),u.ok&&m.ok)){if(p=lr(n,u),p<0)return{ok:!1,reason:"POLICY_VERSION_REGRESSION",message:`Policy version regression: current '${n.raw}' < last '${u.raw}'`,updated:!1};if(p===0&&i.epochSec<m.epochSec)return{ok:!1,reason:"POLICY_CREATED_AT_REGRESSION",message:`Policy createdAt regression: current '${e.createdAt}' < last '${d.createdAt}'`,updated:!1};if(p>0&&i.epochSec<m.epochSec)return{ok:!1,reason:"POLICY_CREATED_AT_REGRESSION",message:"Policy createdAt must be monotonic when version increases",updated:!1}}let N=!d||!u?.ok||!m?.ok||p>0||p===0&&i.epochSec>m.epochSec;if(o&&N){let v={schemaVersion:"1",lastAccepted:{version:e.version,createdAt:e.createdAt,environment:e.environment||null},updatedAt:new Date().toISOString()};ur(t,v)}return{ok:!0,reason:null,message:"Policy version guard passed",updated:N}}function $e({commandObj:e,pubKeyB64:t,keyStore:r,nonceDb:s,policy:o,rateLimiter:n,policyStatePath:i}){let a={valid:!1,commandId:e?.commandId,checks:{},errors:[]},c=dt(e,lt);if(a.checks.schema=c.valid,!c.valid)return a.errors.push(...c.errors.map(k=>({type:"SCHEMA_ERROR",message:k}))),a;if(i&&o?.version!==void 0)try{let k=ye({policyObj:o,statePath:i});if(a.checks.policyVersion=k.ok,!k.ok)return a.errors.push({type:"POLICY_VERSION_INVALID",message:k.message}),a}catch{a.checks.policyVersion=!0}else a.checks.policyVersion=!0;let l=e.signature?.keyId;if(a.checks.keyId=Pe(l),!a.checks.keyId)return a.errors.push({type:"KEY_ID_INVALID",message:`Invalid keyId '${l}'. Use versioned IDs like 'agent:gpt-v1-2026Q1'`}),a;let d=Math.floor(Date.now()/1e3),u=Number.isFinite(o?.security?.maxClockSkewSec)?o.security.maxClockSkewSec:600,m=Math.abs(d-e.timestamp);if(a.checks.timestamp=m<=u,!a.checks.timestamp)return a.errors.push({type:"TIMESTAMP_SKEW_EXCEEDED",message:`Command timestamp skew ${m}s exceeds allowed ${u}s`}),a;let p=null;if(r){let k=me({keyStore:r,keyId:l,requesterId:e.requesterId});if(!k.ok)return a.checks.signature=!1,a.errors.push({type:k.reason,message:k.message}),a;p=k.publicKey}if(!p&&t&&(p=t),!p)return a.checks.signature=!1,a.errors.push({type:"SIGNATURE_KEY_UNAVAILABLE",message:"No public key available. Provide --pub-key/--pub-key-file or config/keys.json"}),a;let N={...e};delete N.signature;let v=Z({payloadObj:N,sigB64:e.signature.sig,pubKeyB64:p});if(a.checks.signature=v.valid,!v.valid)return a.errors.push({type:"SIGNATURE_INVALID",message:v.message}),a;if(n&&typeof n.checkAndRecord=="function"){let k=o?.requesters?.[e.requesterId]?.rateLimit||{},D=o?.security?.defaultRateLimit||{},T=n.checkAndRecord({requesterId:e.requesterId,nowSec:d,windowSec:k.windowSec??D.windowSec??60,maxRequests:k.maxRequests??D.maxRequests??30});if(a.checks.rateLimit=T.ok,!T.ok)return a.errors.push({type:T.reason,message:`${T.message}. Retry after ${T.retryAfterSec}s`}),a}let h;if(s&&typeof s.checkAndRecord=="function"?h=s.checkAndRecord({requesterId:e.requesterId,sessionId:e.sessionId,nonce:e.nonce}):h=yt(s,{requesterId:e.requesterId,sessionId:e.sessionId,nonce:e.nonce,timestamp:e.timestamp}),a.checks.nonce=h.ok,!h.ok)return a.errors.push({type:h.reason,message:h.message}),a;let S=ht(o,e);return a.checks.policy=S.allowed,a.risk=gt(e),S.allowed?(a.valid=!0,a.message="Command validation successful",a):(a.errors.push({type:S.reason,message:S.message}),a)}import he from"fs";import pr from"path";var ge=class{constructor(t){this.dbPath=t,this.db={entries:[]}}async load(){try{if(!he.existsSync(this.dbPath)){this.db={entries:[]};return}let t=he.readFileSync(this.dbPath,"utf8");this.db=JSON.parse(t),Array.isArray(this.db.entries)||(this.db={entries:[]})}catch{this.db={entries:[]}}}async save(){let t=pr.dirname(this.dbPath);he.existsSync(t)||he.mkdirSync(t,{recursive:!0}),I(this.dbPath,JSON.stringify(this.db,null,2),{encoding:"utf8"})}checkAndRecord({requesterId:t,nowSec:r,windowSec:s,maxRequests:o}){let n=Number.isFinite(r)?r:Math.floor(Date.now()/1e3),i=Number.isFinite(s)&&s>0?s:60,a=Number.isFinite(o)&&o>0?o:30,c=n-i;this.db.entries=this.db.entries.filter(d=>d.timestamp>=c);let l=this.db.entries.filter(d=>d.requesterId===t);if(l.length>=a){let d=l.sort((m,p)=>m.timestamp-p.timestamp)[0],u=Math.max(1,i-(n-d.timestamp));return{ok:!1,reason:"RATE_LIMIT_EXCEEDED",message:`Rate limit exceeded for '${t}' (${a}/${i}s)`,retryAfterSec:u}}return this.db.entries.push({requesterId:t,timestamp:n}),{ok:!0,reason:null,message:"Rate limit check passed",retryAfterSec:0}}};import G from"fs";import fr from"path";import mr from"crypto";function yr(e){return mr.createHash("sha256").update(e).digest("hex")}function hr(e){try{if(!G.existsSync(e))return"GENESIS";let t=G.readFileSync(e,"utf8").trim();if(!t)return"GENESIS";let r=t.split(`
20
+ `),s=r[r.length-1];try{return JSON.parse(s).hash||"GENESIS"}catch{return"GENESIS"}}catch{return"GENESIS"}}function ke(e,t){let r=fr.dirname(e);G.existsSync(r)||G.mkdirSync(r,{recursive:!0});let s;return ft(e,()=>{let o=hr(e),n={...t,prevHash:o,timestamp:new Date().toISOString()};delete n.hash;let i=JSON.stringify(n),a=yr(i),c=JSON.stringify({...n,hash:a}),l="";G.existsSync(e)&&(l=G.readFileSync(e,"utf8"));try{I(e,l+c+`
21
+ `,{encoding:"utf8"})}catch(d){throw new Error(`Audit log write failed: ${d.message}`)}s={success:!0,hash:a,prevHash:o,message:"Audit entry appended"}}),s}async function vt(e){return{adapter:"noop",commandId:e.commandId||"unknown",command:e.id||"unknown",status:"completed",output:`[NOOP] Would execute: ${e.id||"unknown"} on adapter: ${e.payload?.adapter||"unknown"}`,exitCode:0,timestamp:new Date().toISOString()}}import{spawnSync as gr}from"child_process";import Fe from"path";function kr(e){if(e===void 0)return{ok:!0,args:[]};if(!Array.isArray(e))return{ok:!1,error:"payload.args must be an array"};let t=[];for(let r of e){if(typeof r!="string"&&typeof r!="number"&&typeof r!="boolean")return{ok:!1,error:"payload.args may only contain string, number, or boolean values"};t.push(String(r))}return{ok:!0,args:t}}async function wt(e,t,r){let s=e.payload,o=3e4,n=1024*1024;if(s.adapter!=="shell")return{adapter:"shell",commandId:e.commandId,status:"error",error:"Adapter mismatch",exitCode:1};let i=r?.exec?.allowCmds||[];if((r?.exec?.denyCmds||[]).includes(s.cmd))return{adapter:"shell",commandId:e.commandId,status:"blocked",error:`Command '${s.cmd}' is denied`,exitCode:2};if(i.length>0&&!i.includes(s.cmd))return{adapter:"shell",commandId:e.commandId,status:"blocked",error:`Command '${s.cmd}' not in allowlist`,exitCode:2};if(!(r?.filesystem?.roots||[]).some(u=>{let m=Fe.resolve(u),p=Fe.resolve(s.cwd);return p===m||p.startsWith(m+Fe.sep)}))return{adapter:"shell",commandId:e.commandId,status:"blocked",error:`CWD '${s.cwd}' not authorized`,exitCode:2};let d=kr(s.args);if(!d.ok)return{adapter:"shell",commandId:e.commandId,status:"blocked",error:d.error,exitCode:2};try{let u=gr(s.cmd,d.args,{cwd:s.cwd,timeout:o,encoding:"utf8",maxBuffer:n,stdio:["pipe","pipe","pipe"],shell:!1});if(u.error)throw u.error;let m=`${u.stdout||""}${u.stderr||""}`,p=u.status??1;return p!==0?{adapter:"shell",commandId:e.commandId,command:s.cmd,status:"error",error:m.substring(0,n)||`Command exited with code ${p}`,exitCode:p,timestamp:new Date().toISOString()}:{adapter:"shell",commandId:e.commandId,command:s.cmd,status:"completed",output:m.substring(0,n),exitCode:0,timestamp:new Date().toISOString()}}catch(u){return{adapter:"shell",commandId:e.commandId,command:s.cmd,status:"error",error:u.message,exitCode:u.status||1,timestamp:new Date().toISOString()}}}import te from"fs";import J from"path";import q from"fs";import Se from"path";import Sr from"crypto";function ee(e,t){let r=t||Se.resolve("data/backups");q.existsSync(r)||q.mkdirSync(r,{recursive:!0});let s=Se.resolve(e),o=q.existsSync(s),n=null,i=null;o&&(n=q.readFileSync(s),i=Sr.createHash("sha256").update(n).digest("hex"));let a=Se.basename(s).replace(/[^a-zA-Z0-9._-]/g,"_"),c=`${Date.now()}-${i?i.slice(0,8):"new"}-${a}`,l=o?Se.join(r,c):null;return o&&n!==null&&I(l,n),{originalPath:s,backupPath:l,existed:o,hash:i,createdAt:new Date().toISOString()}}function B(e){if(!e)return{restored:!1,error:"No backup metadata"};let{originalPath:t,backupPath:r,existed:s}=e;if(!s)try{return q.existsSync(t)&&q.unlinkSync(t),{restored:!0,action:"deleted"}}catch(o){return{restored:!1,error:o.message}}if(!r||!q.existsSync(r))return{restored:!1,error:"Backup file not found at: "+r};try{let o=q.readFileSync(r);return I(t,o),{restored:!0,action:"restored"}}catch(o){return{restored:!1,error:o.message}}}var Er=10*1024*1024;function Ir(e,t){return e?J.isAbsolute(e)?J.resolve(e):J.resolve(t||process.cwd(),e):null}function _r(e,t){let r=J.resolve(e);return t.some(s=>{let o=J.resolve(s);return r===o||r.startsWith(o+J.sep)})}function vr(e,t){for(let r of t||[])if(new RegExp("^"+r.replace(/\./g,"\\.").replace(/\*\*/g,".*").replace(/\*/g,"[^/\\\\]*")+"$").test(e))return r;return null}function j(e,t,r,s=2){return{adapter:"file",commandId:e.commandId,status:"blocked",errorCode:t,error:r,exitCode:s}}function $(e,t,r,s=null,o=1){return{adapter:"file",commandId:e.commandId,status:"error",errorCode:t,error:r,backup:s?Ee(s):null,exitCode:o}}function Ee(e){return e?{path:e.backupPath,existed:e.existed,hash:e.hash,createdAt:e.createdAt}:null}async function At(e,t,r){let s=e.payload,o=s.action,n=s.cwd||process.cwd(),i=Ir(s.target,n);if(!o)return j(e,"FILE_NO_ACTION","payload.action is required");if(!i&&o!=="noop")return j(e,"FILE_NO_TARGET","payload.target is required");let a=r?.filesystem?.roots||[];if(a.length===0)return j(e,"FILE_NO_ROOTS","No filesystem roots defined for requester");if(!_r(i,a))return j(e,"FILE_OUTSIDE_ROOT",`'${i}' is outside allowed roots`);let c=vr(i,r?.filesystem?.denyPatterns);if(c)return j(e,"FILE_PATH_DENIED",`'${i}' matches deny pattern: ${c}`);switch(o){case"read":return wr(e,i);case"write":return Ar(e,i,s);case"patch":return br(e,i,s);case"delete":return xr(e,i);default:return j(e,"FILE_UNKNOWN_ACTION",`Unknown action: '${o}'`)}}function wr(e,t){if(!te.existsSync(t))return $(e,"FILE_NOT_FOUND",`Not found: ${t}`);try{let r=te.statSync(t);if(r.size>Er)return $(e,"FILE_TOO_LARGE","File exceeds 10 MB read limit");let s=te.readFileSync(t,"utf8");return{adapter:"file",action:"read",commandId:e.commandId,status:"completed",target:t,output:s,bytesRead:r.size,exitCode:0}}catch(r){return $(e,"FILE_READ_ERROR",r.message)}}function Ar(e,t,r){let s=r.content;if(s==null)return $(e,"FILE_MISSING_CONTENT","payload.content is required for write");let o=qe(t);try{return I(t,s,{encoding:"utf8"}),{adapter:"file",action:"write",commandId:e.commandId,status:"completed",target:t,backup:Ee(o),output:`Wrote ${Buffer.byteLength(s,"utf8")} bytes to ${t}`,exitCode:0}}catch(n){return B(o),$(e,"FILE_WRITE_ERROR",n.message,o)}}function br(e,t,r){let s=r.content;if(s==null)return $(e,"FILE_MISSING_CONTENT","payload.content is required for patch");let o=qe(t);try{return I(t,s,{encoding:"utf8"}),{adapter:"file",action:"patch",commandId:e.commandId,status:"completed",target:t,backup:Ee(o),output:`Patched ${t} (${Buffer.byteLength(s,"utf8")} bytes)`,exitCode:0}}catch(n){return B(o),$(e,"FILE_PATCH_ERROR",n.message,o)}}function xr(e,t){if(!te.existsSync(t))return $(e,"FILE_NOT_FOUND",`Not found: ${t}`);let r=qe(t);try{return te.unlinkSync(t),{adapter:"file",action:"delete",commandId:e.commandId,status:"completed",target:t,backup:Ee(r),output:`Deleted ${t}`,exitCode:0}}catch(s){return B(r),$(e,"FILE_DELETE_ERROR",s.message,r)}}function qe(e){try{return ee(e)}catch{return null}}async function bt(e,t,r){let s=Date.now();try{let o=e.id||"";if(!o.toUpperCase().startsWith("OBSERVE"))return{adapter:"observer",commandId:e.commandId,status:"error",error:`Observer adapter only handles OBSERVE_* commands, got '${o}'`,exitCode:1};let{source:n,context:i,issueType:a,description:c,severity:l,metadata:d}=e.payload||{};if(!a||!c)return{adapter:"observer",commandId:e.commandId,status:"error",error:"Observer payload must include issueType and description",exitCode:1};let u=["low","medium","high","critical"];return l&&!u.includes(l)?{adapter:"observer",commandId:e.commandId,status:"error",error:`Invalid severity '${l}'. Must be one of: ${u.join(", ")}`,exitCode:1}:{adapter:"observer",commandId:e.commandId,status:"recorded",timestamp:new Date().toISOString(),requesterId:e.requesterId,observation:{source:n||"unknown",context:i||"unknown",issueType:a,description:c,severity:l||"info",metadata:d||{}},duration_ms:Date.now()-s,exitCode:0}}catch(o){return{adapter:"observer",commandId:e.commandId,status:"error",error:`Observer execution failed: ${o.message}`,exitCode:9}}}var xt={noop:vt,shell:wt,file:At,observer:bt};function Lr(e){return xt[e]}async function Lt(e,t,r,s){let o=Lr(e);if(!o)return{adapter:e,commandId:t.commandId,status:"error",error:`Adapter '${e}' not found`,exitCode:1};try{return await o(t,r,s)}catch(n){return{adapter:e,commandId:t.commandId,status:"error",error:`Adapter execution failed: ${n.message}`,exitCode:9}}}var Rt=Object.keys(xt);import Ot from"fs";import Rr from"path";function Ct({policyObj:e,keyStore:t,policySigPath:r="./config/policy.sig.json",allowUnsigned:s=!1}){let o=Rr.resolve(r);if(!Ot.existsSync(o))return s?{ok:!0,skipped:!0,reason:"POLICY_SIGNATURE_SKIPPED",message:`Policy signature not found: ${o} (allowed by flag)`}:{ok:!1,skipped:!1,reason:"POLICY_SIGNATURE_MISSING",message:`Policy signature file not found: ${o}`};let n;try{n=JSON.parse(Ot.readFileSync(o,"utf-8"))}catch(c){return{ok:!1,skipped:!1,reason:"POLICY_SIGNATURE_INVALID",message:`Unable to parse policy signature file: ${c.message}`}}if(!n||n.alg!=="ed25519"||typeof n.keyId!="string"||typeof n.sig!="string")return{ok:!1,skipped:!1,reason:"POLICY_SIGNATURE_INVALID",message:"Policy signature envelope must include {alg, keyId, sig}"};if(!t)return{ok:!1,skipped:!1,reason:"POLICY_SIGNER_KEY_STORE_UNAVAILABLE",message:"Trusted key store is required for policy signature verification"};let i=me({keyStore:t,keyId:n.keyId,requesterId:void 0});if(!i.ok)return{ok:!1,skipped:!1,reason:"POLICY_SIGNER_NOT_TRUSTED",message:i.message};let a=Z({payloadObj:e,sigB64:n.sig,pubKeyB64:i.publicKey});return a.valid?{ok:!0,skipped:!1,reason:null,message:"Policy signature verified",keyId:n.keyId}:{ok:!1,skipped:!1,reason:"POLICY_SIGNATURE_INVALID",message:a.message}}import Cr from"crypto";import Or from"path";var Me=class{constructor(t){this.dbPath=t||Or.resolve("data/checkpoints.db.json"),this.store={checkpoints:{},tokens:{}},this._load()}_load(){let t=mt(this.dbPath);t&&(this.store=t,this.store.checkpoints=this.store.checkpoints||{},this.store.tokens=this.store.tokens||{})}_save(){let t=JSON.stringify(this.store,null,2);I(this.dbPath,t,{encoding:"utf8"})}saveCheckpoint(t,r){this.store.checkpoints[t]={jobId:t,...r,updatedAt:Date.now()},this._save()}getCheckpoint(t){return this.store.checkpoints[t]||null}getAllCheckpoints(){return Object.values(this.store.checkpoints)}removeCheckpoint(t){return this.store.checkpoints[t]?(delete this.store.checkpoints[t],this._save(),!0):!1}saveToken(t,r){this.store.tokens[t]={tokenId:t,...r,createdAt:Date.now()},this._save()}getToken(t){return this.store.tokens[t]||null}getAllTokens(){return Object.values(this.store.tokens)}removeToken(t){return this.store.tokens[t]?(delete this.store.tokens[t],this._save(),!0):!1}},Ke=null;function Nt(e){return Ke||(Ke=new Me(e)),Ke}var Ve=class{constructor(t){this.store=Nt(t),this._pendingResolvers=new Map}createToken(t,r={}){let s=Cr.randomBytes(16).toString("hex"),o={jobId:t,context:r,status:"pending",expiresAt:Date.now()+1440*60*1e3};return this.store.saveToken(s,o),s}awaitApproval(t){let r=this.store.getToken(t);return r?r.status!=="pending"?Promise.reject(new Error(`Approval token ${t} is no longer pending (status: ${r.status})`)):Date.now()>r.expiresAt?(this.store.removeToken(t),Promise.reject(new Error(`Approval token ${t} expired`))):new Promise((s,o)=>{this._pendingResolvers.set(t,{resolve:s,reject:o})}):Promise.reject(new Error(`Approval token ${t} not found`))}approve(t,r={}){let s=this.store.getToken(t);if(!s)throw new Error("Token not found");if(s.status!=="pending")throw new Error("Token not pending");this.store.saveToken(t,{...s,status:"approved",approverData:r,resolvedAt:Date.now()});let o=this._pendingResolvers.get(t);return o&&(o.resolve({approved:!0,approverData:r}),this._pendingResolvers.delete(t)),!0}deny(t,r="Manually denied"){let s=this.store.getToken(t);if(!s)throw new Error("Token not found");if(s.status!=="pending")throw new Error("Token not pending");this.store.saveToken(t,{...s,status:"denied",reason:r,resolvedAt:Date.now()});let o=this._pendingResolvers.get(t);return o&&(o.reject(new Error(`Approval denied: ${r}`)),this._pendingResolvers.delete(t)),!0}},Be=null;function Dt(e){return Be||(Be=new Ve(e)),Be}var Ue={DEBUG:0,INFO:1,WARN:2,ERROR:3};function Ie({level:e="INFO",maxHistory:t=500,silent:r=!1}={}){let s=Ue[e]??Ue.INFO,o=[];function n(a,c,l,d){let u={ts:new Date().toISOString(),level:a,scope:c,message:l,...d!==void 0?{meta:d}:{}};if(o.length>=t&&o.shift(),o.push(u),!r&&Ue[a]>=s){let m=d!==void 0?`[${a}] [${c}] ${l} ${JSON.stringify(d)}`:`[${a}] [${c}] ${l}`;process.stderr.write(m+`
22
+ `)}}function i(a){return{debug:(c,l)=>n("DEBUG",a,c,l),info:(c,l)=>n("INFO",a,c,l),warn:(c,l)=>n("WARN",a,c,l),error:(c,l)=>n("ERROR",a,c,l)}}return{scope:i,debug:(a,c)=>n("DEBUG","lbe",a,c),info:(a,c)=>n("INFO","lbe",a,c),warn:(a,c)=>n("WARN","lbe",a,c),error:(a,c)=>n("ERROR","lbe",a,c),exportLogs:()=>[...o],clearHistory:()=>{o.length=0},get historyLength(){return o.length}}}var Nr=process.env.LBE_LOG_LEVEL||"INFO",Dr=process.env.LBE_LOG_SILENT==="1",ho=Ie({level:Nr,silent:Dr});function re(e){if(e===null||typeof e!="object"||Object.isFrozen(e))return e;Object.freeze(e);for(let t of Object.getOwnPropertyNames(e)){let r=e[t];typeof r=="object"&&r!==null&&!Object.isFrozen(r)&&re(r)}return e}import Ye from"fs";import He from"path";var z=class extends Error{constructor(t,r,s){super(t),this.name="InvariantGateError",this.checks=r,this.failures=s}};function Tt(e,t,r){let s={},o=[],n=!!(t&&typeof t.version<"u"&&t.requesters&&typeof t.requesters=="object"&&t.default==="DENY");s.policy_structure=n,n||o.push("Policy missing required fields: version, requesters, default=DENY");let i=!!(r&&typeof r=="object"&&Object.keys(r).length>0);s.keys_available=i,i||o.push("No trusted keys loaded \u2014 provide config/keys.json");let a=He.dirname(e.auditLog);s.audit_log_writable=We(a),s.audit_log_writable||o.push(`Audit log directory not writable: ${a}`);let c=He.dirname(e.nonceDb);s.nonce_db_writable=We(c),s.nonce_db_writable||o.push(`Nonce DB directory not writable: ${c}`);let l=He.dirname(e.rateLimit);if(s.rate_limit_writable=We(l),s.rate_limit_writable||o.push(`Rate-limit DB directory not writable: ${l}`),t&&t.requesters){let d=[];for(let[u,m]of Object.entries(t.requesters))for(let p of m.allowAdapters||[])Rt.includes(p)||d.push(`${u}\u2192${p}`);s.adapter_chain_valid=d.length===0,s.adapter_chain_warnings=d.length>0?d:[]}else s.adapter_chain_valid=!0,s.adapter_chain_warnings=[];return s.secret_key_present=!!e.secretKey,s.secret_key_present||o.push("secretKey not provided to createLBE()"),{ok:o.length===0,checks:s,failures:o}}function Ge(e,t,r){let s=Tt(e,t,r);if(!s.ok){let o=s.failures.length;throw new z(`Invariant gate: ${o} violation${o===1?"":"s"} \u2014 ${s.failures.join(" | ")}`,s.checks,s.failures)}return s}function Tr(e){try{return Ye.accessSync(e,Ye.constants.W_OK),!0}catch{return!1}}function We(e){if(Tr(e))return!0;try{return Ye.mkdirSync(e,{recursive:!0}),!0}catch{return!1}}function $r(e,t){let r=_.resolve(e||process.cwd());if(!t||t==="local"){let s=_e.createHash("sha256").update(r).digest("hex").slice(0,16);return _.join(Pr.homedir(),".lbe","workspaces",s)}if(t==="workspace")return _.join(r,".lbe");if(t&&typeof t=="object"&&t.adapter)return null;throw new Error(`createLBE: unknown state option: ${JSON.stringify(t)}`)}var Fr={patch_file:"PATCH_FILE",write_file:"WRITE_FILE",read_file:"READ_FILE",delete_file:"DELETE_FILE",run_shell:"RUN_SHELL",echo:"ECHO"},qr={PATCH_FILE:"file",WRITE_FILE:"file",READ_FILE:"file",DELETE_FILE:"file",RUN_SHELL:"shell",ECHO:"noop"},Kr=new Set(["HIGH","CRITICAL"]);function je(e){return _e.createHash("sha256").update(JSON.stringify(e)).digest("hex")}function Mr(e,t){if(!t?.requireApproval)return!1;let r=t.requireApproval;return r===!0?!0:Array.isArray(r)?r.includes(e)||r.includes("*")||Kr.has(e)&&r.includes("HIGH+"):!1}function $t(e={}){if(e.rootDir&&!e.secretKey){let A=Ce(),g=e.keyId||"sdk-auto-key";e={defaultActor:"agent:sdk",logLevel:"WARN",...e,secretKey:A.secretKey,keyId:g,keyStore:e.keyStore||Br({publicKey:A.publicKey,keyId:g}),policy:e.policy||{version:1,default:"DENY",requesters:{"agent:sdk":{allowCommands:["write_file","read_file","patch_file","delete_file"],allowAdapters:["file"],filesystem:{roots:[e.rootDir],denyPatterns:["*.key","*.env","*.secret"]}}}}}}let{secretKey:t,keyId:r="sdk-key-v1",sessionId:s,defaultActor:o="agent:sdk",policy:n,keyStore:i,policyPath:a,keysStorePath:c,policySigPath:l,policyStatePath:d,nonceDbPath:u,rateLimitDbPath:m,auditLogPath:p,backupDir:N,allowUnsignedPolicy:v=!1,state:h="local",rootDir:S,logLevel:k=process.env.LBE_LOG_LEVEL||"INFO",logSilent:D=process.env.LBE_LOG_SILENT==="1"}=e,T=n&&typeof n=="object"?n:null,Qe=i&&typeof i=="object"?i:null,X=$r(S,h),L={secretKey:t,policy:a||_.resolve("config/policy.default.json"),keys:c||_.resolve("config/keys.json"),policySig:l||_.resolve("config/policy.sig.json"),policyState:d||_.join(X,"policy.state.json"),nonceDb:u||_.join(X,"nonce.db.json"),rateLimit:m||_.join(X,"rate-limit.db.json"),auditLog:p||_.join(X,"audit.log.jsonl"),backupDir:N||_.join(X,"backups")},K=Ie({level:k,silent:D}),w=K.scope("Executor"),V=K.scope("Validator"),U=K.scope("Policy");async function we({actor:A,intent:g,target:y,content:se,args:Yt=[],transaction:Gt={}}){let Q={validate:!0,backup:!0,rollbackOnFailure:!0,audit:!0,...Gt};w.info("execute() called",{actor:A,intent:g,target:y});let oe=Fr[g]||g.toUpperCase().replace(/-/g,"_"),H=qr[oe]||"noop",R;if(T)R=T,U.debug("Using inline policy",{version:R.version});else try{R=JSON.parse(Pt.readFileSync(L.policy,"utf8")),U.debug("Policy loaded from file",{version:R.version})}catch(f){return U.error("Policy load failed",{error:f.message}),{ok:!1,stage:"policy_load",error:"POLICY_LOAD_FAILED",message:f.message}}let M;if(Qe)M=Qe,V.debug("Using inline keyStore");else{let f=St(L.keys);M=f.ok?f.store:null,V.debug("Keys loaded from file",{ok:f.ok})}re(R),M&&re(M);try{let f=Ge(L,R,M);V.debug("Invariant gate passed",f.checks)}catch(f){if(f instanceof z)return V.error("Invariant gate failed",{failures:f.failures}),{ok:!1,stage:"invariant_gate",error:"INVARIANT_GATE_FAILED",message:f.message,checks:f.checks,failures:f.failures};throw f}if(!T){let f=Ct({policyObj:R,keyStore:M,policySigPath:L.policySig,allowUnsigned:v});if(!f.ok)return U.error("Policy signature invalid",{reason:f.reason}),{ok:!1,stage:"policy_sig",error:f.reason,message:f.message};let x=ye({policyObj:R,statePath:L.policyState});if(!x.ok)return U.error("Policy version guard failed",{reason:x.reason}),{ok:!1,stage:"policy_version",error:x.reason,message:x.message};U.debug("Policy signature and version valid")}if(!t)return{ok:!1,stage:"sign",error:"NO_SECRET_KEY",message:"createLBE requires secretKey"};let jt=Math.floor(Date.now()/1e3),Jt=_e.randomBytes(32).toString("hex"),zt=s||`sdk-${Date.now()}`,O=_e.randomUUID();w.debug("Proposal built",{commandId:O,commandId_str:oe,adapter:H});let ne={id:oe,commandId:O,requesterId:A,sessionId:zt,timestamp:jt,nonce:Jt,requires:["policy","signature"],payload:{adapter:H,action:g.includes("_")?g.split("_")[0]:g,target:y?_.resolve(y):null,content:se||null,args:Yt,cwd:y?_.dirname(_.resolve(y)):process.cwd()}},ae=de({payloadObj:ne,secretKeyB64:t});if(ae.error)return w.error("Signing failed",{error:ae.error}),{ok:!1,stage:"sign",commandId:O,error:"SIGN_FAILED",message:ae.error};let ie={...ne,signature:{alg:"ed25519",keyId:r,sig:ae.signature}},Ae=new fe(L.nonceDb);await Ae.load();let be=new ge(L.rateLimit);await be.load();let C=$e({commandObj:ie,keyStore:M,nonceDb:Ae,policy:R,rateLimiter:be,policyStatePath:T?null:L.policyState}),ce=async()=>{await Ae.save().catch(()=>{}),await be.save().catch(()=>{})};if(!C.valid)return V.warn("Validation failed",{error:C.errors[0]?.type,checks:C.checks}),await ce(),Q.audit&&ke(L.auditLog,{commandId:O,status:"rejected",requesterId:A,payloadHash:je(ie),reason:C.errors[0]?.type,intent:g}),{ok:!1,stage:"validate",commandId:O,error:C.errors[0]?.type,message:C.errors[0]?.message,checks:C.checks,operationLog:K.exportLogs()};V.info("Validation passed",{risk:C.risk,checks:C.checks});let F=C.risk||"LOW",Ze=R.requesters?.[A];if(Mr(F,Ze)){w.warn("Approval required",{risk:F,commandId:O}),await ce();let x=Dt(L.policyState).createToken(O,{actor:A,intent:g,target:y,risk:F,commandId:oe});return{ok:!1,stage:"approval_pending",commandId:O,approvalToken:x,risk:F,message:`${F} risk operation requires approval. Token: ${x}`,operationLog:K.exportLogs()}}let P=null;if(Q.backup&&y){let f=["write","patch","delete"].includes(ne?.payload?.action??g.split("_")[0]);try{P=ee(_.resolve(y),L.backupDir),w.debug("Backup created",{existed:P.existed,path:P.backupPath})}catch(x){if(f)return w.error("Backup failed \u2014 aborting write transaction",{error:x.message}),await ce(),{ok:!1,stage:"backup",error:"BACKUP_FAILED",message:x.message};w.warn("Backup failed (non-fatal for read)",{error:x.message})}}w.info("Executing adapter",{adapter:H,target:y});let b;try{b=await Lt(H,ie,R,Ze)}catch(f){b={adapter:H,commandId:O,status:"error",error:f.message,exitCode:1}}w.debug("Adapter returned",{status:b.status,exitCode:b.exitCode});let xe=b.status==="error"||b.exitCode!==0&&b.exitCode!==void 0,W=null;if(Q.validate&&y&&!xe){let f=["write","patch"],x=ne.payload.action;if(f.includes(x)){let et=Pt.existsSync(_.resolve(y));W={ok:et,check:"target_exists",target:y},et?w.debug("Post-execution validation passed"):(w.error("Post-execution validation failed \u2014 target missing after write",{target:y}),b.status="error")}}let Y=null;if((xe||W&&!W.ok)&&Q.rollbackOnFailure&&P)try{Y=B(P),w.warn("Rollback executed",Y)}catch(f){Y={restored:!1,error:f.message},w.error("Rollback failed",{error:f.message})}Q.audit&&ke(L.auditLog,{commandId:O,status:Y?.restored?"rolled_back":b.status||"completed",requesterId:A,payloadHash:je(ie),executionHash:je(b),adapter:H,intent:g,riskLevel:F,exitCode:b.exitCode||0,rolledBack:Y?.restored||!1}),await ce();let le=!xe&&(!W||W.ok);return w.info("execute() complete",{ok:le,stage:le?"executed":"failed",risk:F}),{ok:le,commandId:O,intent:g,actor:A,target:y,risk:F,stage:le?"executed":"failed",status:b.status,output:b.output||null,exitCode:b.exitCode??0,checks:C.checks,backup:P?{path:P.backupPath,existed:P.existed,hash:P.hash}:null,rollback:Y,postValidation:W,operationLog:k==="DEBUG"?K.exportLogs():void 0}}return{execute:we,exportLogs:()=>K.exportLogs(),async writeFile(A,g){let y=await we({actor:o,intent:"write_file",target:A,content:g,transaction:{backup:!0,rollbackOnFailure:!0,audit:!0}});if(!y.ok){let se=new Error(`LBE write failed [${y.error||y.stage}]${y.message?": "+y.message:""}`);throw se.lbeResult=y,se}return y},async readFile(A){let g=await we({actor:o,intent:"read_file",target:A,transaction:{audit:!0}});if(!g.ok){let y=new Error(`LBE read failed [${g.error||g.stage}]${g.message?": "+g.message:""}`);throw y.lbeResult=g,y}return g.output}}}function Br({publicKey:e,keyId:t,validDays:r=365}){let s=new Date,o=new Date(s.getTime()+r*24*3600*1e3);return{defaultKeyId:t,trustedKeys:{[t]:{publicKey:e,notBefore:s.toISOString(),expiresAt:o.toISOString()}}}}import Ft from"fs";import Vr from"path";var qt=[{name:"lbe_workspace_context",description:["Read the workspace contract for this project.","Call this FIRST \u2014 before writing, modifying, or planning any file changes.","Returns the semantics contract: what this project is, where things live,","what must never be touched, and how changes are expected to happen here.","Agents that read this contract before acting produce fewer mistakes and","require fewer corrections. If no contract exists, run: npx lbe init"].join(" "),inputSchema:{type:"object",properties:{include_enforcement:{type:"boolean",description:"Also return allow/approval/deny enforcement rules. Default: false."}},required:[]}},{name:"lbe_write_file",description:["Write content to a file under the governance controller.","The write is validated (schema \u2192 signature \u2192 nonce \u2192 policy), backed up,","and appended to the immutable audit log before any bytes are written.","Requires the actor to be in the policy allowlist."].join(" "),inputSchema:{type:"object",properties:{path:{type:"string",description:"File path to write (must be within policy filesystem.roots)"},content:{type:"string",description:"Content to write"},actor:{type:"string",description:'Actor identifier, e.g. "agent:gpt". Defaults to MCP_DEFAULT_ACTOR env.'}},required:["path","content"]}},{name:"lbe_read_file",description:"Read a file via the governance controller. Audited; policy-checked.",inputSchema:{type:"object",properties:{path:{type:"string",description:"File path to read"},actor:{type:"string",description:"Actor identifier"}},required:["path"]}},{name:"lbe_shell_command",description:["Run a shell command via the governance controller.","Command must be in the actor's policy allowCommands list.","Audited. Does not allow shell expansion beyond the policy."].join(" "),inputSchema:{type:"object",properties:{command:{type:"string",description:"Command name (must be in policy allowlist)"},args:{type:"array",items:{type:"string"},description:"Command arguments"},actor:{type:"string",description:"Actor identifier"}},required:["command"]}},{name:"lbe_patch_file",description:"Append or patch content in an existing file. Backed up before write.",inputSchema:{type:"object",properties:{path:{type:"string",description:"File path to patch"},content:{type:"string",description:"Content to append/patch"},actor:{type:"string",description:"Actor identifier"}},required:["path","content"]}},{name:"lbe_health",description:"Check LBE controller health. Returns ok status and SDK log entry count.",inputSchema:{type:"object",properties:{},required:[]}}];async function Kt(e,t,r,s="mcp:client"){let o=typeof t.actor=="string"&&t.actor?t.actor:s;switch(e){case"lbe_workspace_context":{let n=Vr.resolve(process.cwd(),"lbe.workspace.json");if(!Ft.existsSync(n))return{content:[{type:"text",text:JSON.stringify({ok:!1,error:"NO_WORKSPACE_CONTRACT",message:"No lbe.workspace.json found. Run: npx lbe init"},null,2)}]};let i;try{i=JSON.parse(Ft.readFileSync(n,"utf8"))}catch{return{content:[{type:"text",text:JSON.stringify({ok:!1,error:"CONTRACT_PARSE_ERROR",message:"lbe.workspace.json is malformed. Run: npx lbe init to regenerate."},null,2)}]}}let a={ok:!0,projectTypes:i.projectTypes??null,primaryType:i.primaryType??null,semantics:i.semantics??{}};return t.include_enforcement&&(a.enforcement=i.enforcement??{}),{content:[{type:"text",text:JSON.stringify(a,null,2)}]}}case"lbe_write_file":{let n=await r.execute({actor:o,intent:"write_file",target:t.path,content:t.content,transaction:{backup:!0,rollbackOnFailure:!0,audit:!0}});return ve(n)}case"lbe_read_file":{let n=await r.execute({actor:o,intent:"read_file",target:t.path,transaction:{backup:!1,audit:!0}});return ve(n)}case"lbe_shell_command":{let n=await r.execute({actor:o,intent:"shell_command",target:t.command,content:JSON.stringify({args:Array.isArray(t.args)?t.args:[]}),transaction:{backup:!1,audit:!0}});return ve(n)}case"lbe_patch_file":{let n=await r.execute({actor:o,intent:"patch_file",target:t.path,content:t.content,transaction:{backup:!0,rollbackOnFailure:!0,audit:!0}});return ve(n)}case"lbe_health":{let n=r.exportLogs();return{content:[{type:"text",text:JSON.stringify({ok:!0,sdkLogEntries:n.length},null,2)}]}}default:return{content:[{type:"text",text:`Unknown tool: ${e}`}],isError:!0}}}function ve(e){let t={ok:e.ok,commandId:e.commandId,stage:e.stage,risk:e.risk??void 0,output:e.output??void 0,error:e.error??void 0,message:e.message??void 0};return Object.keys(t).forEach(r=>t[r]===void 0&&delete t[r]),{content:[{type:"text",text:JSON.stringify(t,null,2)}],isError:!e.ok}}(process.argv.includes("--help")||process.argv.includes("-h"))&&(console.log(`
23
+ lbe-mcp \u2014 LBE SDK MCP Server v0.4.0
24
+
25
+ Wraps LBE SDK as a Model Context Protocol (MCP) server. Every tool call
26
+ is handled by the local LBE SDK.
27
+
28
+ Usage:
29
+ node bin/lbe-mcp-server.js Start server on stdio
30
+ npx lbe-mcp Same via npx
31
+ npm run mcp Same via npm script
32
+
33
+ Setup:
34
+ npm install @modelcontextprotocol/sdk
35
+ npm run init Generate keys and policy
36
+
37
+ Environment:
38
+ LBE_SECRET_KEY_PATH Path to secret key (default: keys/secret.key)
39
+ LBE_KEYS_STORE_PATH Path to keys.json (default: config/keys.json)
40
+ LBE_MCP_DEFAULT_ACTOR Default actor id (default: mcp:client)
41
+ LBE_LOG_LEVEL DEBUG|INFO|WARN|ERROR (default: WARN)
42
+
43
+ Tools exposed:
44
+ lbe_workspace_context Read the workspace contract \u2014 call this FIRST
45
+ lbe_write_file Write a file \u2014 backup, audit, rollback-on-failure
46
+ lbe_read_file Read a file \u2014 policy-checked, audited
47
+ lbe_patch_file Append/patch a file \u2014 backed up before write
48
+ lbe_shell_command Run a command \u2014 must be in policy allowlist
49
+ lbe_health Check SDK readiness
50
+
51
+ Claude Desktop config:
52
+ {
53
+ "mcpServers": {
54
+ "lbe": {
55
+ "command": "npx",
56
+ "args": ["lbe-mcp"],
57
+ "cwd": "/path/to/your/lbe-workspace",
58
+ "env": { "LBE_MCP_DEFAULT_ACTOR": "agent:claude" }
59
+ }
60
+ }
61
+ }
62
+ `),process.exit(0));var Bt,Vt,Ut,Ht;try{let{Server:e}=await import("@modelcontextprotocol/sdk/server/index.js"),{StdioServerTransport:t}=await import("@modelcontextprotocol/sdk/server/stdio.js"),{CallToolRequestSchema:r,ListToolsRequestSchema:s}=await import("@modelcontextprotocol/sdk/types.js");Bt=e,Vt=t,Ut=r,Ht=s}catch{console.error("[lbe-mcp] @modelcontextprotocol/sdk is not installed."),console.error("[lbe-mcp] Run: npm install @modelcontextprotocol/sdk"),process.exit(1)}var Je=Mt.resolve(process.env.LBE_SECRET_KEY_PATH||"keys/secret.key"),Ur=Mt.resolve(process.env.LBE_KEYS_STORE_PATH||"config/keys.json");ze.existsSync(Je)||(console.error("[lbe-mcp] Secret key not found at:",Je),console.error("[lbe-mcp] Run: npm run init"),process.exit(1));var Hr=ze.readFileSync(Je,"utf8").trim(),Wr=JSON.parse(ze.readFileSync(Ur,"utf8")),Wt=process.env.LBE_MCP_DEFAULT_ACTOR||"mcp:client",Yr=$t({secretKey:Hr,keyId:Wr.defaultKeyId,logLevel:process.env.LBE_LOG_LEVEL||"WARN",logSilent:!1}),Xe=new Bt({name:"lbe-sdk",version:"0.4.0"},{capabilities:{tools:{}}});Xe.setRequestHandler(Ht,async()=>({tools:qt}));Xe.setRequestHandler(Ut,async e=>{let{name:t,arguments:r}=e.params;return Kt(t,r||{},Yr,Wt)});var Gr=new Vt;await Xe.connect(Gr);console.error("[lbe-mcp] LBE SDK MCP server ready \u2014 default actor:",Wt);
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@letterblack/lbe-sdk",
3
+ "version": "0.4.0",
4
+ "description": "Commercial SDK distribution for the Lockstep Boundary Engine.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "types.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./types.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "bin": {
15
+ "lbe": "dist/cli.js",
16
+ "lbe-mcp": "dist/mcp-server.js"
17
+ },
18
+ "files": [
19
+ "dist/index.js",
20
+ "dist/engine.js",
21
+ "dist/cli.js",
22
+ "dist/mcp-server.js",
23
+ "runtime/lbe_engine.wasm",
24
+ "types.d.ts",
25
+ "README.md",
26
+ "CHANGELOG.md",
27
+ "COMMERCIAL_DISTRIBUTION.md",
28
+ "LICENSE"
29
+ ],
30
+ "scripts": {
31
+ "pack:check": "npm pack --dry-run"
32
+ },
33
+ "keywords": [
34
+ "ai-governance",
35
+ "execution-governance",
36
+ "ai-safety",
37
+ "agent-safety",
38
+ "policy-engine",
39
+ "audit-log",
40
+ "ed25519",
41
+ "local-first"
42
+ ],
43
+ "author": "LetterBlack",
44
+ "license": "SEE LICENSE IN LICENSE",
45
+ "dependencies": {
46
+ "json-canonicalize": "^1.0.4",
47
+ "tweetnacl": "^1.0.3"
48
+ },
49
+ "peerDependencies": {
50
+ "@modelcontextprotocol/sdk": ">=1.0.0"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "@modelcontextprotocol/sdk": {
54
+ "optional": true
55
+ }
56
+ },
57
+ "engines": {
58
+ "node": ">=20.9.0"
59
+ }
60
+ }
Binary file
package/types.d.ts ADDED
@@ -0,0 +1,72 @@
1
+ export type LBERisk = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
2
+
3
+ export interface LBEResult {
4
+ ok: boolean;
5
+ commandId?: string;
6
+ intent?: string;
7
+ actor?: string;
8
+ target?: string;
9
+ risk?: LBERisk;
10
+ stage?: string;
11
+ status?: string;
12
+ output?: string | null;
13
+ exitCode?: number;
14
+ checks?: Record<string, unknown>;
15
+ backup?: { path?: string | null; existed?: boolean; hash?: string | null } | null;
16
+ rollback?: Record<string, unknown> | null;
17
+ postValidation?: Record<string, unknown> | null;
18
+ error?: string;
19
+ message?: string;
20
+ }
21
+
22
+ export interface LBETransactionOptions {
23
+ validate?: boolean;
24
+ backup?: boolean;
25
+ rollbackOnFailure?: boolean;
26
+ audit?: boolean;
27
+ }
28
+
29
+ export interface LBEExecuteOptions {
30
+ actor?: string;
31
+ intent: string;
32
+ target?: string;
33
+ content?: string;
34
+ args?: string[];
35
+ transaction?: LBETransactionOptions;
36
+ }
37
+
38
+ export interface LBEClient {
39
+ execute(options: LBEExecuteOptions): Promise<LBEResult>;
40
+ exportLogs(): unknown[];
41
+ writeFile(target: string, content: string): Promise<LBEResult>;
42
+ readFile(target: string): Promise<string>;
43
+ }
44
+
45
+ export interface SandboxClient {
46
+ write(target: string, content: string): Promise<void>;
47
+ read(target: string): Promise<string>;
48
+ patch(target: string, content: string): Promise<void>;
49
+ lbe: LBEClient;
50
+ }
51
+
52
+ export interface CreateLBEOptions {
53
+ rootDir?: string;
54
+ state?: 'local' | 'workspace' | { adapter: unknown };
55
+ secretKey?: string;
56
+ keyId?: string;
57
+ sessionId?: string;
58
+ defaultActor?: string;
59
+ policy?: Record<string, unknown>;
60
+ keyStore?: Record<string, unknown>;
61
+ logLevel?: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
62
+ logSilent?: boolean;
63
+ }
64
+
65
+ export interface SandboxOptions {
66
+ audit?: boolean;
67
+ rollback?: boolean;
68
+ state?: 'local' | 'workspace' | { adapter: unknown };
69
+ }
70
+
71
+ export function createLBE(options?: CreateLBEOptions): LBEClient;
72
+ export function sandbox(root: string, opts?: SandboxOptions): SandboxClient;