From 2ae2ce6c3a1ca5fb0d3f607c20affa57e5f913f4 Mon Sep 17 00:00:00 2001 From: Andrei Hasna Date: Sat, 1 Aug 2026 21:55:41 +0300 Subject: [PATCH] fix: add knowledge contract release conformance Fixes #47. Factory run: run_4762a0a5992d. Agent: Silvanus --- bin/knowledge-mcp.js | 3 +- bin/knowledge.js | 2 +- fixtures/project-panel.invalid.json | 91 +++++++++++++++++++++++++++++ fixtures/project-panel.valid.json | 85 +++++++++++++++++++++++++++ package.json | 3 +- 5 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 fixtures/project-panel.invalid.json create mode 100644 fixtures/project-panel.valid.json diff --git a/bin/knowledge-mcp.js b/bin/knowledge-mcp.js index c52acb5..c94440f 100755 --- a/bin/knowledge-mcp.js +++ b/bin/knowledge-mcp.js @@ -15053,8 +15053,9 @@ var package_default = { "migrate:cloud": "bun scripts/apply-cloud-migrations.mjs", serve: "bun src/serve-entry.ts", "verify:generated": "bun scripts/verify-generated-artifacts.mjs", + "contracts:conformance": "contracts conformance fixtures", build: "rm -rf dist && bun build --target=bun --outfile=bin/knowledge.js --minify --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/cli.ts && bun build --target=bun --outfile=bin/knowledge-mcp.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/mcp.js && bun build --target=bun --outfile=bin/knowledge-serve.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/serve-entry.ts && bun build ./src/index.ts ./src/storage.ts ./src/serve.ts --outdir ./dist --target bun --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek && bun scripts/strip-generated-trailing-whitespace.mjs && bunx tsc -p tsconfig.build.json", - prepublishOnly: "bun run build && node scripts/validate-public-package.mjs" + prepublishOnly: "bun run contracts:conformance && contracts no-cloud-scan . && bun run build && node scripts/validate-public-package.mjs" }, keywords: [ "knowledge", diff --git a/bin/knowledge.js b/bin/knowledge.js index 1d60a0b..a3ee069 100755 --- a/bin/knowledge.js +++ b/bin/knowledge.js @@ -1356,7 +1356,7 @@ Pages should be concise, cited, and organized for both humans and agents. ) `)}var W8=_Y(S8(),1),{program:Um,createCommand:Im,createArgument:jm,createOption:Nm,CommanderError:Em,InvalidArgumentError:Om,InvalidOptionArgumentError:Am,Command:X8,Argument:Lm,Option:Jm,Help:Pm}=W8.default;import{chmod as Dz,mkdir as Gv,readFile as Yv,rename as Qv,writeFile as Q8}from"fs/promises";import{Buffer as Z8}from"buffer";import{existsSync as b8}from"fs";import{homedir as Tv}from"os";import{join as VU}from"path";import{createHmac as Mv,timingSafeEqual as Qm}from"crypto";import{randomUUID as Hv}from"crypto";import{spawn as kv}from"child_process";import{randomUUID as uv}from"crypto";function Lv(_,$){return $.split(".").reduce((g,U)=>{if(g&&typeof g==="object"&&U in g)return g[U];return},_)}function Jv(_,$){let g=[],U=(I)=>{if(!g.some((j)=>Object.is(j,I)))g.push(I)};if($.includes(".")&&$ in _)U(_[$]);let D=Lv(_,$);if(D!==void 0||!$.includes("."))U(D);return g}function Pv(_,$={}){let g="";for(let U=0;U<_.length;U+=1){let D=_[U];if(D==="*")if(_[U+1]==="*")g+=".*",U+=1;else g+=$.segmentSafe?"[^/]*":".*";else g+=D.replace(/[|\\{}()[\]^$+?.]/g,"\\$&")}return new RegExp(`^${g}$`)}function BU(_,$,g={}){if($===void 0)return!0;if(_===void 0)return!1;return(Array.isArray($)?$:[$]).some((D)=>Pv(D,g).test(_))}function R8(_,$){if(!$)return!0;return Object.entries($).every(([g,U])=>{let D=Jv(_,g);return zv(D,U,g)})}function zv(_,$,g){if(Xv($))return!_.some((U)=>G8(U,$.not,g));return _.some((U)=>G8(U,$,g))}function G8(_,$,g){if(typeof $==="string"||Array.isArray($))return Sv(_).some((U)=>BU(U,$,{segmentSafe:g.endsWith("_path")||g.endsWith(".path")}));if(Array.isArray(_))return _.some((U)=>U===$);return _===$}function Sv(_){if(_===void 0)return[];if(Array.isArray(_))return _.flatMap(($)=>Wv($)?[String($)]:[]);return[String(_)]}function Wv(_){return _===null||typeof _==="string"||typeof _==="number"||typeof _==="boolean"}function Xv(_){return Boolean(_&&typeof _==="object"&&!Array.isArray(_)&&"not"in _)}function Rv(_,$){return BU(_.source,$.source)&&BU(_.type,$.type)&&BU(_.subject,$.subject)&&BU(_.severity,$.severity)&&R8(_.data,$.data)&&R8(_.metadata,$.metadata)}function Y8(_,$){if(!_.enabled)return!1;if(!_.filters||_.filters.length===0)return!0;return _.filters.some((g)=>Rv($,g))}var XN="HASNA_EVENTS_DIR",RN="HASNA_EVENTS_HOME",Nz="local-json-v1:",qv=100,Bv=1000;function H8(_){return _||process.env[XN]||process.env[RN]||VU(Tv(),".hasna","events")}function Kv(){if(process.env[XN])return XN;if(process.env[RN])return RN;return null}class GN{dataDir;runtime;channelsPath;eventsPath;deliveriesPath;constructor(_=H8()){this.dataDir=_,this.runtime=Vv(_),this.channelsPath=VU(_,"channels.json"),this.eventsPath=VU(_,"events.json"),this.deliveriesPath=VU(_,"deliveries.json")}async init(){await Gv(this.dataDir,{recursive:!0,mode:448}),await Dz(this.dataDir,448).catch(()=>{return}),await this.ensureArrayFile(this.channelsPath),await this.ensureArrayFile(this.eventsPath),await this.ensureArrayFile(this.deliveriesPath)}async addChannel(_){await this.init();let $=await this.readJson(this.channelsPath,[]),g=$.findIndex((U)=>U.id===_.id);if(g>=0)$[g]={..._,createdAt:$[g].createdAt,updatedAt:new Date().toISOString()};else $.push(_);return await this.writeJson(this.channelsPath,$),g>=0?$[g]:_}async listChannels(){return await this.init(),this.readJson(this.channelsPath,[])}async getChannel(_){return(await this.listChannels()).find((g)=>g.id===_)}async removeChannel(_){await this.init();let $=await this.readJson(this.channelsPath,[]),g=$.filter((U)=>U.id!==_);return await this.writeJson(this.channelsPath,g),g.length!==$.length}async appendEvent(_){await this.init();let $=await this.readJson(this.eventsPath,[]);return $.push(_),await this.writeJson(this.eventsPath,$),_}async appendEventOnce(_,$={}){await this.init();let g=await this.readJson(this.eventsPath,[]);if($.dedupe!==!1){let D=q8(g,{id:_.id,dedupeKey:_.dedupeKey});if(D)return{event:D,stored:!1,deduped:!0,identity:{id:D.id,dedupeKey:D.dedupeKey}}}return g.push(_),await this.writeJson(this.eventsPath,g),{event:_,stored:!0,deduped:!1,identity:{id:_.id,dedupeKey:_.dedupeKey}}}async listEvents(_={}){await this.init();let $=await this.readJson(this.eventsPath,[]);return T8($,_)}async listEventsPage(_={}){await this.init();let $=await this.readJson(this.eventsPath,[]),g=T8($,{eventId:_.eventId,source:_.source,type:_.type}),U=YN(_.cursor,_),D=QN(_.limit),I=g.slice(U,U+D),j=U+I.length,N=j{return})}async readJson(_,$){try{let g=await Yv(_,"utf-8");if(!g.trim())return $;return JSON.parse(g)}catch(g){if(g.code==="ENOENT")return $;throw g}}async writeJson(_,$){let g=`${_}.${process.pid}.${Date.now()}.tmp`;await Q8(g,`${JSON.stringify($,null,2)} -`,{encoding:"utf-8",mode:384}),await Qv(g,_),await Dz(_,384).catch(()=>{return})}}function Vv(_=H8()){return{mode:"local-files",name:"json-events-store",remote:!1,localFiles:!0,localSqlite:!1,postgres:!1,s3:!1,aws:!1,durable:!0,idempotency:"best-effort-local",replayCursors:!0,description:`Local JSON files in ${_}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`}}function k8(_,$={}){if(!Number.isInteger(_)||_<0)throw Error(`Invalid event cursor offset: ${_}`);let g={offset:_,eventId:$.eventId,source:$.source,type:$.type};return`${Nz}${Z8.from(JSON.stringify(g),"utf-8").toString("base64url")}`}function YN(_,$={}){if(!_)return 0;if(!_.startsWith(Nz))throw Error(`Invalid local JSON event cursor: ${_}`);let g=_.slice(Nz.length),U;try{U=JSON.parse(Z8.from(g,"base64url").toString("utf-8"))}catch{throw Error(`Invalid local JSON event cursor: ${_}`)}let D=U.offset;if(!Number.isInteger(D)||D<0)throw Error(`Invalid local JSON event cursor: ${_}`);return Uz("eventId",U.eventId,$.eventId),Uz("source",U.source,$.source),Uz("type",U.type,$.type),D}function QN(_){if(_===void 0)return qv;if(!Number.isInteger(_)||_<1)throw Error(`Event page limit must be a positive integer, got ${_}`);return Math.min(_,Bv)}function T8(_,$){let g=_;if($.eventId)g=g.filter((U)=>U.id===$.eventId);if($.source)g=g.filter((U)=>U.source===$.source);if($.type)g=g.filter((U)=>U.type===$.type);if($.cursor){let U=YN($.cursor,$);g=g.slice(U)}if($.limit!==void 0)g=g.slice(0,QN($.limit));return g}function Uz(_,$,g){if($!==g)throw Error(`Local JSON event cursor ${_} filter mismatch`)}function q8(_,$){return _.find((g)=>$.id!==void 0&&g.id===$.id||$.dedupeKey!==void 0&&g.dedupeKey===$.dedupeKey)}async function Fv(_){let $=new GN(_);await $.init();let[g,U,D]=await Promise.all([$.listChannels(),$.listEvents(),$.listDeliveries()]),I=g.reduce((j,N)=>{return j[N.transport]=(j[N.transport]??0)+1,j},{});return{service:"events",schemaVersion:"1.0",dataDir:$.dataDir,storage:$.runtime,env:{primary:XN,fallback:RN,active:Kv()},files:{channels:Iz($.dataDir,"channels.json",g.length),events:Iz($.dataDir,"events.json",U.length),deliveries:Iz($.dataDir,"deliveries.json",D.length)},counts:{channels:g.length,enabledChannels:g.filter((j)=>j.enabled).length,disabledChannels:g.filter((j)=>!j.enabled).length,events:U.length,deliveries:D.length},transports:I,safety:{includesEventPayloads:!1,includesWebhookSecrets:!1,listOutputsRedactSecrets:!0,statusOutputIsMetadataOnly:!0}}}function Iz(_,$,g){let U=VU(_,$);return{path:U,exists:b8(U),records:g}}function Zv(_,$){return`${_}.${$}`}function bv(_,$,g){return`sha256=${Mv("sha256",_).update(Zv($,g)).digest("hex")}`}function i$(){return new Date().toISOString()}function KU(_,$=4096){return _.length>$?`${_.slice(0,$)}...`:_}function Cv(_,$){if(!$.webhook)throw Error(`Channel ${$.id} has no webhook config`);let g=JSON.stringify(_),U=_.time,D={"Content-Type":"application/json","User-Agent":"@hasna/events","X-Hasna-Event-Id":_.id,"X-Hasna-Event-Type":_.type,"X-Hasna-Timestamp":U,...$.webhook.headers};if($.webhook.secret)D["X-Hasna-Signature"]=bv($.webhook.secret,U,g);return{body:g,headers:D}}async function rv(_,$,g={}){if(!$.webhook)throw Error(`Channel ${$.id} has no webhook config`);let U=i$(),{body:D,headers:I}=Cv(_,$),j=new AbortController,N=setTimeout(()=>j.abort(),$.webhook.timeoutMs??15000);try{let O=await(g.fetchImpl??fetch)($.webhook.url,{method:"POST",headers:I,body:D,signal:j.signal}),A=KU(await O.text());return{attempt:1,status:O.ok?"success":"failed",startedAt:U,completedAt:i$(),responseStatus:O.status,responseBody:A,error:O.ok?void 0:`Webhook returned HTTP ${O.status}`}}catch(O){return{attempt:1,status:"failed",startedAt:U,completedAt:i$(),error:O instanceof Error?O.message:String(O)}}finally{clearTimeout(N)}}async function vv(_,$){if(!$.command)throw Error(`Channel ${$.id} has no command config`);let g=i$(),U=JSON.stringify(_),D={...process.env,...$.command.env,HASNA_CHANNEL_ID:$.id,HASNA_EVENT_ID:_.id,HASNA_EVENT_TYPE:_.type,HASNA_EVENT_SOURCE:_.source,HASNA_EVENT_SUBJECT:_.subject??"",HASNA_EVENT_SEVERITY:_.severity,HASNA_EVENT_TIME:_.time,HASNA_EVENT_DEDUPE_KEY:_.dedupeKey??"",HASNA_EVENT_SCHEMA_VERSION:_.schemaVersion,HASNA_EVENT_JSON:U};return new Promise((I)=>{let j=kv($.command.command,$.command.args??[],{cwd:$.command.cwd,env:D,stdio:["pipe","pipe","pipe"]}),N="",O="",A=setTimeout(()=>j.kill("SIGTERM"),$.command.timeoutMs??15000);j.stdin.end(U),j.stdout.on("data",(L)=>{N+=L.toString()}),j.stderr.on("data",(L)=>{O+=L.toString()}),j.on("error",(L)=>{clearTimeout(A),I({attempt:1,status:"failed",startedAt:g,completedAt:i$(),stdout:KU(N),stderr:KU(O),error:L.message})}),j.on("close",(L,z)=>{clearTimeout(A);let W=L===0;I({attempt:1,status:W?"success":"failed",startedAt:g,completedAt:i$(),stdout:KU(N),stderr:KU(O),error:W?void 0:`Command exited with ${z?`signal ${z}`:`code ${L}`}`})})})}async function fv(_,$,g={}){if($.transport==="webhook")return rv(_,$,g);if($.transport==="command")return vv(_,$);return{attempt:1,status:"skipped",startedAt:i$(),completedAt:i$(),error:`Unsupported transport: ${$.transport}`}}function B8(_,$,g){let U=g.some((D)=>D.status==="success")?"success":g.every((D)=>D.status==="skipped")?"skipped":"failed";return{id:Hv(),eventId:_.id,channelId:$.id,transport:$.transport,status:U,attempts:g,createdAt:g[0]?.startedAt??i$(),completedAt:g.at(-1)?.completedAt??i$()}}class C8 extends Error{eventType;issues;constructor(_,$){let g=$.map((U)=>`${U.path||""}: ${U.message}`).join("; ");super(`Event validation failed for type "${_}": ${g}`);this.name="EventValidationError",this.eventType=_,this.issues=$}}class r8{definitions=new Map;register(_){return this.definitions.set(_.type,_),this}unregister(_){return this.definitions.delete(_)}has(_){return this.definitions.has(_)}get(_){return this.definitions.get(_)}list(){return[...this.definitions.values()]}validateEvent(_){let $=this.definitions.get(_.type);if(!$)return{ok:!0};return $.validate(_.data,_)}assertEventValid(_){let $=this.validateEvent(_);if(!$.ok)throw new C8(_.type,$.issues)}}var wv=new r8;function jz(_){return{id:_.id??uv(),source:_.source,type:_.type,time:nv(_.time),subject:_.subject,severity:_.severity??"info",data:_.data??{},message:_.message,dedupeKey:_.dedupeKey,schemaVersion:_.schemaVersion??"1.0",metadata:_.metadata??{}}}class v8{store;redactors;transportOptions;catalog;validateCatalogTypes;constructor(_={}){this.store=_.store??new GN(_.dataDir),this.redactors=_.redactors??[],this.transportOptions={fetchImpl:_.fetchImpl},this.catalog=_.catalog??wv,this.validateCatalogTypes=_.validateCatalogTypes??!1}async addChannel(_){let $=new Date().toISOString();return this.store.addChannel({..._,createdAt:_.createdAt??$,updatedAt:_.updatedAt??$})}async listChannels(){return this.store.listChannels()}async removeChannel(_){return this.store.removeChannel(_)}async emit(_,$={}){let g=$.redactSensitiveData===!1?jz(_):hv(jz(_));if($.validate??this.validateCatalogTypes)this.catalog.assertEventValid(g);let U=await this.appendEvent(g,{dedupe:$.dedupe!==!1});if(U.deduped)return{event:U.event,deliveries:[],deduped:!0};let D=$.deliver===!1?[]:await this.deliver(U.event);return{event:U.event,deliveries:D,deduped:!1}}async listEvents(_={}){if(Object.keys(_).length===0)return this.store.listEvents();return K8(await this.store.listEvents(),_)}async listEventsPage(_={}){if(this.store.listEventsPage)return this.store.listEventsPage(_);let $=K8(await this.store.listEvents(),{eventId:_.eventId,source:_.source,type:_.type}),g=YN(_.cursor,_),U=QN(_.limit),D=$.slice(g,g+U),I=g+D.length,j=I<$.length;return{events:D,cursor:_.cursor,nextCursor:j?k8(I,_):void 0,hasMore:j}}async listDeliveries(){return this.store.listDeliveries()}async deliver(_){let g=(await this.store.listChannels()).filter((D)=>Y8(D,_)),U=[];for(let D of g){let I=await this.applyRedaction(_,D),j=await this.deliverWithRetry(I,D);await this.store.appendDelivery(j),U.push(j)}return U}async matchChannel(_,$={}){let g=await this.store.getChannel(_);if(!g)throw Error(`Channel not found: ${_}`);let U=jz({source:$.source??"hasna.events",type:$.type??"events.test",subject:$.subject??_,severity:$.severity??"info",data:$.data??{test:!0},message:$.message??"Hasna events test delivery",dedupeKey:$.dedupeKey,schemaVersion:$.schemaVersion,metadata:$.metadata,time:$.time,id:$.id}),D=Y8(g,U);return{channelId:g.id,matched:D,event:U,filters:g.filters,reason:D?void 0:g.enabled?"event did not match channel filters":"channel is disabled"}}async testChannel(_,$={},g={}){let U=await this.store.getChannel(_);if(!U)throw Error(`Channel not found: ${_}`);let D=await this.matchChannel(_,$),I=D.event;if(g.honorFilters&&!D.matched){let O=new Date().toISOString(),A=B8(I,U,[{attempt:1,status:"skipped",startedAt:O,completedAt:O,error:D.reason}]);return A.metadata={reason:"filter_mismatch"},await this.store.appendDelivery(A),A}let j=await this.applyRedaction(I,U),N=await this.deliverWithRetry(j,U);return await this.store.appendDelivery(N),N}async replay(_={}){let $=_.cursor||_.limit!==void 0?await this.listEventsPage(_):{events:await this.listEvents(_),hasMore:!1};if(_.dryRun)return{events:$.events,deliveries:[],cursor:$.cursor,nextCursor:$.nextCursor,hasMore:$.hasMore};let g=[];for(let U of $.events)g.push(...await this.deliver(U));return{events:$.events,deliveries:g,cursor:$.cursor,nextCursor:$.nextCursor,hasMore:$.hasMore}}async appendEvent(_,$){if(this.store.appendEventOnce)return this.store.appendEventOnce(_,{dedupe:$.dedupe});if($.dedupe){let U=await this.store.findEventByIdentity({id:_.id,dedupeKey:_.dedupeKey});if(U)return{event:U,stored:!1,deduped:!0,identity:{id:U.id,dedupeKey:U.dedupeKey}}}let g=await this.store.appendEvent(_);return{event:g,stored:!0,deduped:!1,identity:{id:g.id,dedupeKey:g.dedupeKey}}}async applyRedaction(_,$){let g=xv(_,$.redact?.paths??[],$.redact?.replacement??"[REDACTED]");for(let U of this.redactors)g=await U(g,$);return g}async deliverWithRetry(_,$){let g=dv($.retry),U=[];for(let D=0;D[g,w8(g)?"[REDACTED]":U]));return $}function yv(_){return _.map(f8)}function hv(_,$="[REDACTED]"){return Ez(_,$)}function w8(_){return/secret|token|password|api[_-]?key|authorization/i.test(_)}function Ez(_,$){if(Array.isArray(_))return _.map((g)=>Ez(g,$));if(!_||typeof _!=="object")return _;return Object.fromEntries(Object.entries(_).map(([g,U])=>[g,w8(g)?$:Ez(U,$)]))}function cv(_,$,g){let U=$.split("."),D=_;for(let j of U.slice(0,-1)){let N=D[j];if(!N||typeof N!=="object")return;D=N}let I=U.at(-1);if(I&&I in D)D[I]=g}function K8(_,$){let g=_;if($.eventId)g=g.filter((U)=>U.id===$.eventId);if($.source)g=g.filter((U)=>U.source===$.source);if($.type)g=g.filter((U)=>U.type===$.type);if($.cursor)g=g.slice(YN($.cursor,$));if($.limit!==void 0)g=g.slice(0,QN($.limit));return g}function nv(_){if(!_)return new Date().toISOString();return _ instanceof Date?_.toISOString():_}function dv(_){return{maxAttempts:Math.max(1,_?.maxAttempts??1),backoffMs:Math.max(0,_?.backoffMs??250),multiplier:Math.max(1,_?.multiplier??2)}}function WN(_,$,g=!1){if(!_?.length)return;let U={};for(let D of _){let I=lv(D,$),j=I.path;if(j in U)throw Error(`Duplicate ${$} filter path: ${j}`);let N=g?iv(I.rawValue,$):I.rawValue;U[j]=I.negated?{not:N}:N}return U}function mv(_){let $={};if(_.source)$.source=_.source;if(_.type)$.type=_.type;if(_.subject)$.subject=_.subject;if(_.severity)$.severity=_.severity;let g=V8(WN(_.data,"data"),WN(_.dataJson,"data-json",!0)),U=V8(WN(_.metadata,"metadata"),WN(_.metadataJson,"metadata-json",!0));if(Object.keys(g).length>0)$.data=g;if(Object.keys(U).length>0)$.metadata=U;return Object.keys($).length>0?[$]:void 0}function V8(..._){let $={};for(let g of _){if(!g)continue;for(let[U,D]of Object.entries(g)){if(U in $)throw Error(`Duplicate filter path: ${U}`);$[U]=D}}return $}function iv(_,$){let g=JSON.parse(_);if(g===null||typeof g==="string"||typeof g==="number"||typeof g==="boolean"||Array.isArray(g)&&g.every((U)=>typeof U==="string"))return g;throw Error(`${$} filter JSON values must be string, string[], number, boolean, or null`)}function lv(_,$){let g=_.indexOf("!=");if(g>0)return{path:_.slice(0,g),rawValue:_.slice(g+2),negated:!0};let U=_.indexOf("=");if(U<=0)throw Error(`Invalid ${$} filter, expected path=value or path!=value: ${_}`);return{path:_.slice(0,U),rawValue:_.slice(U+1),negated:!1}}var tv=100;function Bg(_,$){if(!_)return $;let g=JSON.parse(_);if(!g||typeof g!=="object"||Array.isArray(g))throw Error("Expected a JSON object");return g}function ov(_){if(!_?.length)return;let $={};for(let g of _){let U=g.indexOf("=");if(U===-1)throw Error(`Invalid header, expected name=value: ${g}`);$[g.slice(0,U)]=g.slice(U+1)}return $}function p6(_){if(_.createClient)return _.createClient();return new v8({store:new GN(_.dataDir)})}function s4(_,$,g){if($)console.log(JSON.stringify(_,null,2));else console.log(g)}function F8(_,$){let g=_ instanceof Error?_.message:String(_);if($)console.log(JSON.stringify({error:g},null,2));else console.error(g);process.exitCode=1}function M8(_){return Boolean(_?.json||_?.opts?.().json||_?.optsWithGlobals?.().json||_?.parent?.opts?.().json||_?.parent?.optsWithGlobals?.().json)}function W6(_,$){return M8(_)||M8($)}function pv(_,$){let g=_.command($.channelsCommandName??"channels").description("Manage Hasna event channels");return g.command("add").description("Add or replace a channel").argument("","Webhook URL or command binary").requiredOption("--id ","Channel identifier").option("--transport ","Transport kind: webhook or command","webhook").option("--name ","Display name").option("--type ","Event type filter, e.g. todos.task.*").option("--source ","Event source filter").option("--subject ","Event subject filter").option("--severity ","Event severity filter").option("--data ","Event data field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard",a4,[]).option("--metadata ","Event metadata field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard",a4,[]).option("--data-json ","Event data field filter with typed JSON value; path!=json negatives supported",a4,[]).option("--metadata-json ","Event metadata field filter with typed JSON value; path!=json negatives supported",a4,[]).option("--secret ","Webhook HMAC secret").option("--header ","Webhook header",a4,[]).option("--arg ","Command argument",a4,[]).option("--timeout-ms ","Transport timeout in milliseconds",FU).option("--retry-attempts ","Maximum delivery attempts",FU).option("--retry-backoff-ms ","Initial retry backoff in milliseconds",FU).option("--redact ","Event field path to redact before delivery",a4,[]).option("--disabled","Create channel disabled",!1).option("-j, --json","Print JSON output",!1).action(async(U,D,I)=>{let j=new Date().toISOString(),N={id:D.id,name:D.name,enabled:!D.disabled,transport:D.transport,filters:mv(D),retry:D.retryAttempts||D.retryBackoffMs?{maxAttempts:D.retryAttempts,backoffMs:D.retryBackoffMs}:void 0,redact:D.redact?.length?{paths:D.redact}:void 0,createdAt:j,updatedAt:j};if(D.transport==="webhook")N.webhook={url:U,secret:D.secret,headers:ov(D.header),timeoutMs:D.timeoutMs};else if(D.transport==="command")N.command={command:U,args:D.arg??[],timeoutMs:D.timeoutMs};else throw Error(`Transport ${D.transport} is reserved for future use and cannot be added yet`);let O=await p6($).addChannel(N);s4(f8(O),W6(D,I),`Added ${O.transport} channel ${O.id}`)}),g.command("list").description("List configured channels").option("-j, --json","Print JSON output",!1).action(async(U,D)=>{let I=await p6($).listChannels();if(W6(U,D)){console.log(JSON.stringify(yv(I),null,2));return}if(!I.length){console.log("No channels configured.");return}for(let j of I)console.log(`${j.id} ${j.enabled?"enabled":"disabled"} ${j.transport} ${j.webhook?.url??j.command?.command??j.transport}`)}),g.command("status").description("Show events channel storage status").option("-j, --json","Print JSON output",!1).action(async(U,D)=>{let I=await Fv($.dataDir);s4(I,W6(U,D),`events dataDir: ${I.dataDir}`)}),g.command("remove").description("Remove a channel").argument("","Channel identifier").option("-j, --json","Print JSON output",!1).action(async(U,D,I)=>{let j=await p6($).removeChannel(U);s4({removed:j},W6(D,I),j?`Removed ${U}`:`Channel not found: ${U}`)}),g.command("test").description("Send a test event to one channel").argument("","Channel identifier").option("--source ","Event source override").option("--type ","Event type","events.test").option("--subject ","Event subject").option("--message ","Event message","Hasna events test delivery").option("--data ","Event data JSON object").option("--metadata ","Event metadata JSON object").option("--honor-filters","Skip delivery when the sample event does not match channel filters",!1).option("-j, --json","Print JSON output",!1).action(async(U,D,I)=>{let j=W6(D,I);try{let N=await p6($).testChannel(U,{source:D.source??$.source,type:D.type,subject:D.subject??U,message:D.message,data:Bg(D.data,{test:!0}),metadata:Bg(D.metadata,{})},{honorFilters:D.honorFilters});s4(N,j,`${N.status}: ${N.channelId}`)}catch(N){F8(N,j)}}),g.command("match").description("Check whether a sample event matches one channel without delivering").argument("","Channel identifier").option("--source ","Event source override").option("--type ","Event type","events.test").option("--subject ","Event subject").option("--message ","Event message","Hasna events match preview").option("--data ","Event data JSON object").option("--metadata ","Event metadata JSON object").option("-j, --json","Print JSON output",!1).action(async(U,D,I)=>{let j=W6(D,I);try{let N=await p6($).matchChannel(U,{source:D.source??$.source,type:D.type,subject:D.subject??U,message:D.message,data:Bg(D.data,{test:!0}),metadata:Bg(D.metadata,{})});s4(N,j,`${N.matched?"matched":"skipped"}: ${N.channelId}`)}catch(N){F8(N,j)}}),g}function ev(_,$){let g=_.command($.eventsCommandName??"events").description("Emit, list, and replay Hasna events");g.command("emit").description("Emit an event from this app").argument("","Event type").option("--source ","Event source override").option("--subject ","Event subject").option("--severity ","Event severity","info").option("--message ","Event message").option("--dedupe-key ","Dedupe key").option("--data ","Event data JSON object").option("--metadata ","Event metadata JSON object").option("--no-deliver","Record without delivering").option("--no-dedupe","Allow duplicate id/dedupeKey events").option("-j, --json","Print JSON output",!1).action(async(D,I,j)=>{let N=await p6($).emit({source:I.source??$.source,type:D,subject:I.subject,severity:I.severity,message:I.message,dedupeKey:I.dedupeKey,data:Bg(I.data,{}),metadata:Bg(I.metadata,{})},{deliver:I.deliver,dedupe:I.dedupe});s4(N,W6(I,j),`${N.deduped?"Deduped":"Emitted"} ${N.event.id} to ${N.deliveries.length} channel(s)`)});let U=$.defaultEventListLimit??tv;return g.command("list").description("List recorded events").option("--source ","Filter by source").option("--type ","Filter by type").option("--limit ",`Limit to the most recent events (default ${U}; use 0 for all)`,FU,U).option("-j, --json","Print JSON output",!1).action(async(D,I)=>{let j=await p6($).listEvents();if(D.source)j=j.filter((N)=>N.source===D.source);if(D.type)j=j.filter((N)=>N.type===D.type);if(D.limit)j=j.slice(-D.limit);if(W6(D,I)){console.log(JSON.stringify(j,null,2));return}if(!j.length){console.log("No events recorded.");return}for(let N of j)console.log(`${N.time} ${N.id} ${N.source} ${N.type} ${N.severity}`)}),g.command("replay").description("Replay recorded events").option("--id ","Replay one event id").option("--source ","Filter by source").option("--type ","Filter by type").option("--cursor ","Opaque replay cursor from a previous page").option("--limit ","Maximum events to replay",FU).option("--dry-run","Preview without delivery",!1).option("-j, --json","Print JSON output",!1).action(async(D,I)=>{let j=await p6($).replay({eventId:D.id,source:D.source,type:D.type,cursor:D.cursor,limit:D.limit,dryRun:D.dryRun});s4(j,W6(D,I),av(j.events.length,j.deliveries.length,j.nextCursor))}),g}function u8(_,$){pv(_,$),ev(_,$)}function FU(_){let $=Number(_);if(!Number.isFinite($))throw Error(`Expected a number, got ${_}`);return $}function a4(_,$){return $.push(_),$}function av(_,$,g){let U=g?`, next cursor: ${g}`:"";return`Replayed ${_} event(s), ${$} delivery result(s)${U}`}import{basename as _f}from"path";var C$={name:"@hasna/knowledge",version:"0.2.93",description:"Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions",type:"module",exports:{".":{import:"./dist/index.js",types:"./dist/index.d.ts"},"./storage":{import:"./dist/storage.js",types:"./dist/storage.d.ts"},"./serve":{import:"./dist/serve.js",types:"./dist/serve.d.ts"}},main:"./dist/index.js",types:"./dist/index.d.ts",bin:{knowledge:"bin/knowledge.js","knowledge-mcp":"bin/knowledge-mcp.js","knowledge-serve":"bin/knowledge-serve.js"},files:["bin","dist","scripts/apply-cloud-migrations.mjs","scripts/lib/remote-temp-dir.mjs","scripts/smoke-machine-sync-release.mjs","scripts/smoke-machines-adapter.mjs","scripts/smoke-open-files-installed-boundary.mjs","scripts/strip-generated-trailing-whitespace.mjs","scripts/verify-generated-artifacts.mjs","docs/architecture/ai-native-knowledge-base.md","docs/architecture/hosted-wrapper-responsibilities.md","docs/architecture/hybrid-semantic-search.md","docs/architecture/machine-sync-schema.md","docs/examples/app-project-wiki-standard.md","docs/examples/company-wiki-workflow.md","docs/migration/global-rules-provenance-import.md","docs/migration/json-to-sqlite.md","LICENSE","README.md"],scripts:{test:"bun test","test:cli":"bun test tests/cli.test.ts","test:package":"bun test tests/package-release.test.ts","release:pack:check":"node scripts/validate-public-package.mjs","smoke:machines-adapter":"bun scripts/smoke-machines-adapter.mjs","smoke:machine-sync-release":"bun scripts/smoke-machine-sync-release.mjs","smoke:open-files-installed-boundary":"bun scripts/smoke-open-files-installed-boundary.mjs","migrate:cloud":"bun scripts/apply-cloud-migrations.mjs",serve:"bun src/serve-entry.ts","verify:generated":"bun scripts/verify-generated-artifacts.mjs",build:"rm -rf dist && bun build --target=bun --outfile=bin/knowledge.js --minify --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/cli.ts && bun build --target=bun --outfile=bin/knowledge-mcp.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/mcp.js && bun build --target=bun --outfile=bin/knowledge-serve.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/serve-entry.ts && bun build ./src/index.ts ./src/storage.ts ./src/serve.ts --outdir ./dist --target bun --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek && bun scripts/strip-generated-trailing-whitespace.mjs && bunx tsc -p tsconfig.build.json",prepublishOnly:"bun run build && node scripts/validate-public-package.mjs"},keywords:["knowledge","cli","agents","json","notes","local","store"],license:"Apache-2.0",publishConfig:{registry:"https://registry.npmjs.org",access:"public"},repository:{type:"git",url:"git+https://github.com/hasna/knowledge.git"},bugs:{url:"https://github.com/hasna/knowledge/issues"},author:"Hasna Inc. ",engines:{bun:">=1.0",node:">=18"},dependencies:{"@ai-sdk/anthropic":"^3.0.81","@ai-sdk/deepseek":"^2.0.35","@ai-sdk/openai":"^3.0.68","@aws-sdk/client-s3":"^3.1063.0","@aws-sdk/credential-providers":"^3.1063.0","@hasna/events":"^0.1.3","@modelcontextprotocol/sdk":"^1.29.0","@types/json-schema":"^7.0.15",ai:"^6.0.197",commander:"^13.1.0",pg:"^8.16.3",zod:"^4.3.6"},devDependencies:{"@electric-sql/pglite":"^0.5.4","@hasna/contracts":"0.8.5","@types/bun":"^1.3.14","@types/pg":"^8.15.6"}};var x8={debug:0,info:1,warn:2,error:3},$f=()=>{if(process.env.DEBUG)return"debug";if(process.env.LOG_LEVEL==="debug")return"debug";if(process.env.LOG_LEVEL==="warn")return"warn";if(process.env.LOG_LEVEL==="error")return"error";return"info"};function $0(_,$,g){if(x8[_]U.toLowerCase()));return $.filter((U)=>!g.has(U.toLowerCase()))}function Oz(_,$,g){if(g===void 0)return{..._,message:$};return{..._,added:g.length,message:`${$} (added ${g.length} tag${g.length===1?"":"s"})`}}function Df(_,$){if($===void 0)throw Error("Missing value for --tag. Example: knowledge add <content> -t <tag> -t <tag>");let g=$.split(",").map((U)=>U.trim()).filter((U)=>U.length>0);if(g.length===0)throw Error(`Invalid --tag value ${JSON.stringify($)}: no tag name found. Example: knowledge add <title> <content> -t <tag> -t <tag>`);return gf([..._??[],...g])}function Uf(_){let $=[],g={},U=!1;for(let D=0;D<_.length;D+=1){let I=_[D];if(U){$.push(I);continue}if(I==="--"){U=!0;continue}if(!I.startsWith("-")||$[0]==="add"&&$.length===2&&I.startsWith("---")){$.push(I);continue}switch(I){case"--json":g.json=!0;break;case"--verbose":g.verbose=!0;break;case"--yes":case"-y":g.yes=!0;break;case"--help":case"-h":g.help=!0;break;case"--version":case"-v":g.version=!0;break;case"--desc":g.desc=!0;break;case"--page":case"-p":g.page=Number(_[D+1]),D+=1;break;case"--limit":case"-l":g.limit=Number(_[D+1]),D+=1;break;case"--search":case"-s":g.search=_[D+1],D+=1;break;case"--sort":g.sort=_[D+1],D+=1;break;case"--id":g.id=_[D+1],D+=1;break;case"--store":g.store=_[D+1],D+=1;break;case"--title":g.title=_[D+1],D+=1;break;case"--content":g.content=_[D+1],D+=1;break;case"--url":g.url=_[D+1],D+=1;break;case"--tag":case"-t":g.tag=Df(g.tag,_[D+1]),g.tagRaw=[...g.tagRaw??[],_[D+1]],D+=1;break;case"--format":g.format=_[D+1],D+=1;break;case"--completions":g.completions=_[D+1],D+=1;break;case"--purpose":g.purpose=_[D+1],D+=1;break;case"--model":g.model=_[D+1],D+=1;break;case"--strategy":g.strategy=_[D+1],D+=1;break;case"--dimensions":g.dimensions=Number(_[D+1]),D+=1;break;case"--semantic":g.semantic=!0;break;case"--context":g.context=!0;break;case"--max-tokens":g.maxTokens=Number(_[D+1]),D+=1;break;case"--max-items":g.maxItems=Number(_[D+1]),D+=1;break;case"--from":g.from=_[D+1],D+=1;break;case"--to":g.to=_[D+1],D+=1;break;case"--rev":g.rev=Number(_[D+1]),D+=1;break;case"--since":g.since=_[D+1],D+=1;break;case"--topic":g.topic=_[D+1],D+=1;break;case"--dedupe":g.dedupe=!0;break;case"--generate":g.generate=!0;break;case"--approve-write":g.approveWrite=!0;break;case"--provider":g.provider=_[D+1],D+=1;break;case"--mode":g.mode=_[D+1],D+=1;break;case"--machine":g.machine=_[D+1],D+=1;break;case"--workspace":g.workspace=_[D+1],D+=1;break;case"--api-url":g.apiUrl=_[D+1],D+=1;break;case"--canonical-example":g.canonicalExample=!0;break;case"--api-key":g.apiKey=_[D+1],D+=1;break;case"--email":g.email=_[D+1],D+=1;break;case"--org":g.org=_[D+1],D+=1;break;case"--org-id":g.orgId=_[D+1],D+=1;break;case"--user-id":g.userId=_[D+1],D+=1;break;case"--owner":g.owner=_[D+1],D+=1;break;case"--approved-by":g.approvedBy=_[D+1],D+=1;break;case"--patch-uri":g.patchUri=_[D+1],D+=1;break;case"--domain":g.domain=[...g.domain??[],_[D+1]],D+=1;break;case"--file-results":g.fileResults=!0;break;case"--full":g.full=!0;break;case"--dry-run":g.dryRun=!0;break;case"--fake":g.fake=!0;break;case"--no-tailscale":g.tailscale=!1;break;case"--no-artifact-content":g.artifactContent=!1;break;case"--no-color":g.noColor=!0;break;case"--scope":g.scope=_[D+1],D+=1;break;case"--tables":g.tables=_[D+1],D+=1;break;case"--peer-workspace":g.peerWorkspace=_[D+1],D+=1;break;case"--older-than":g.olderThan=Number(_[D+1]),D+=1;break;case"--empty":g.empty=!0;break;case"--archived":g.archived=!0;break;case"--include-archived":g.includeArchived=!0;break;case"--project":g.project=_[D+1],D+=1;break;case"--contract":g.contract=!0;break;case"--source-ref":g.sourceRef=[...g.sourceRef??[],_[D+1]],D+=1;break;case"--allow-global":g.allowGlobal=!0;break;default:throw Error(`Unknown flag: ${I}. Run 'knowledge --help' for valid options.`)}}return{positional:$,flags:g}}function If(_){if(!_)return"";return i8[_]??_}function jf(_,$){let g=Array.from({length:_.length+1},()=>Array($.length+1).fill(0));for(let U=0;U<=_.length;U+=1)g[U][0]=U;for(let U=0;U<=$.length;U+=1)g[0][U]=U;for(let U=1;U<=_.length;U+=1)for(let D=1;D<=$.length;D+=1){let I=_[U-1]===$[D-1]?0:1;g[U][D]=Math.min(g[U-1][D]+1,g[U][D-1]+1,g[U-1][D-1]+I)}return g[_.length][$.length]}function Nf(_){if(!_)return"";let $=[...m8,...Object.keys(i8)],g="",U=Number.POSITIVE_INFINITY;for(let D of $){let I=jf(_,D);if(I<U)U=I,g=D}return U<=3?g:""}function Ef(){return _f(process.argv[1]??"").replace(/\.(?:js|ts|mjs|cjs)$/,"")==="knowledge"}async function Of(_){if(!d8.includes(_[0]??""))return!1;let $=new X8;return $.name("knowledge").description("Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions"),u8($,{source:"knowledge"}),await $.parseAsync(_,{from:"user"}),!0}function Af(){console.log(`knowledge - local agent knowledge store +`,{encoding:"utf-8",mode:384}),await Qv(g,_),await Dz(_,384).catch(()=>{return})}}function Vv(_=H8()){return{mode:"local-files",name:"json-events-store",remote:!1,localFiles:!0,localSqlite:!1,postgres:!1,s3:!1,aws:!1,durable:!0,idempotency:"best-effort-local",replayCursors:!0,description:`Local JSON files in ${_}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`}}function k8(_,$={}){if(!Number.isInteger(_)||_<0)throw Error(`Invalid event cursor offset: ${_}`);let g={offset:_,eventId:$.eventId,source:$.source,type:$.type};return`${Nz}${Z8.from(JSON.stringify(g),"utf-8").toString("base64url")}`}function YN(_,$={}){if(!_)return 0;if(!_.startsWith(Nz))throw Error(`Invalid local JSON event cursor: ${_}`);let g=_.slice(Nz.length),U;try{U=JSON.parse(Z8.from(g,"base64url").toString("utf-8"))}catch{throw Error(`Invalid local JSON event cursor: ${_}`)}let D=U.offset;if(!Number.isInteger(D)||D<0)throw Error(`Invalid local JSON event cursor: ${_}`);return Uz("eventId",U.eventId,$.eventId),Uz("source",U.source,$.source),Uz("type",U.type,$.type),D}function QN(_){if(_===void 0)return qv;if(!Number.isInteger(_)||_<1)throw Error(`Event page limit must be a positive integer, got ${_}`);return Math.min(_,Bv)}function T8(_,$){let g=_;if($.eventId)g=g.filter((U)=>U.id===$.eventId);if($.source)g=g.filter((U)=>U.source===$.source);if($.type)g=g.filter((U)=>U.type===$.type);if($.cursor){let U=YN($.cursor,$);g=g.slice(U)}if($.limit!==void 0)g=g.slice(0,QN($.limit));return g}function Uz(_,$,g){if($!==g)throw Error(`Local JSON event cursor ${_} filter mismatch`)}function q8(_,$){return _.find((g)=>$.id!==void 0&&g.id===$.id||$.dedupeKey!==void 0&&g.dedupeKey===$.dedupeKey)}async function Fv(_){let $=new GN(_);await $.init();let[g,U,D]=await Promise.all([$.listChannels(),$.listEvents(),$.listDeliveries()]),I=g.reduce((j,N)=>{return j[N.transport]=(j[N.transport]??0)+1,j},{});return{service:"events",schemaVersion:"1.0",dataDir:$.dataDir,storage:$.runtime,env:{primary:XN,fallback:RN,active:Kv()},files:{channels:Iz($.dataDir,"channels.json",g.length),events:Iz($.dataDir,"events.json",U.length),deliveries:Iz($.dataDir,"deliveries.json",D.length)},counts:{channels:g.length,enabledChannels:g.filter((j)=>j.enabled).length,disabledChannels:g.filter((j)=>!j.enabled).length,events:U.length,deliveries:D.length},transports:I,safety:{includesEventPayloads:!1,includesWebhookSecrets:!1,listOutputsRedactSecrets:!0,statusOutputIsMetadataOnly:!0}}}function Iz(_,$,g){let U=VU(_,$);return{path:U,exists:b8(U),records:g}}function Zv(_,$){return`${_}.${$}`}function bv(_,$,g){return`sha256=${Mv("sha256",_).update(Zv($,g)).digest("hex")}`}function i$(){return new Date().toISOString()}function KU(_,$=4096){return _.length>$?`${_.slice(0,$)}...`:_}function Cv(_,$){if(!$.webhook)throw Error(`Channel ${$.id} has no webhook config`);let g=JSON.stringify(_),U=_.time,D={"Content-Type":"application/json","User-Agent":"@hasna/events","X-Hasna-Event-Id":_.id,"X-Hasna-Event-Type":_.type,"X-Hasna-Timestamp":U,...$.webhook.headers};if($.webhook.secret)D["X-Hasna-Signature"]=bv($.webhook.secret,U,g);return{body:g,headers:D}}async function rv(_,$,g={}){if(!$.webhook)throw Error(`Channel ${$.id} has no webhook config`);let U=i$(),{body:D,headers:I}=Cv(_,$),j=new AbortController,N=setTimeout(()=>j.abort(),$.webhook.timeoutMs??15000);try{let O=await(g.fetchImpl??fetch)($.webhook.url,{method:"POST",headers:I,body:D,signal:j.signal}),A=KU(await O.text());return{attempt:1,status:O.ok?"success":"failed",startedAt:U,completedAt:i$(),responseStatus:O.status,responseBody:A,error:O.ok?void 0:`Webhook returned HTTP ${O.status}`}}catch(O){return{attempt:1,status:"failed",startedAt:U,completedAt:i$(),error:O instanceof Error?O.message:String(O)}}finally{clearTimeout(N)}}async function vv(_,$){if(!$.command)throw Error(`Channel ${$.id} has no command config`);let g=i$(),U=JSON.stringify(_),D={...process.env,...$.command.env,HASNA_CHANNEL_ID:$.id,HASNA_EVENT_ID:_.id,HASNA_EVENT_TYPE:_.type,HASNA_EVENT_SOURCE:_.source,HASNA_EVENT_SUBJECT:_.subject??"",HASNA_EVENT_SEVERITY:_.severity,HASNA_EVENT_TIME:_.time,HASNA_EVENT_DEDUPE_KEY:_.dedupeKey??"",HASNA_EVENT_SCHEMA_VERSION:_.schemaVersion,HASNA_EVENT_JSON:U};return new Promise((I)=>{let j=kv($.command.command,$.command.args??[],{cwd:$.command.cwd,env:D,stdio:["pipe","pipe","pipe"]}),N="",O="",A=setTimeout(()=>j.kill("SIGTERM"),$.command.timeoutMs??15000);j.stdin.end(U),j.stdout.on("data",(L)=>{N+=L.toString()}),j.stderr.on("data",(L)=>{O+=L.toString()}),j.on("error",(L)=>{clearTimeout(A),I({attempt:1,status:"failed",startedAt:g,completedAt:i$(),stdout:KU(N),stderr:KU(O),error:L.message})}),j.on("close",(L,z)=>{clearTimeout(A);let W=L===0;I({attempt:1,status:W?"success":"failed",startedAt:g,completedAt:i$(),stdout:KU(N),stderr:KU(O),error:W?void 0:`Command exited with ${z?`signal ${z}`:`code ${L}`}`})})})}async function fv(_,$,g={}){if($.transport==="webhook")return rv(_,$,g);if($.transport==="command")return vv(_,$);return{attempt:1,status:"skipped",startedAt:i$(),completedAt:i$(),error:`Unsupported transport: ${$.transport}`}}function B8(_,$,g){let U=g.some((D)=>D.status==="success")?"success":g.every((D)=>D.status==="skipped")?"skipped":"failed";return{id:Hv(),eventId:_.id,channelId:$.id,transport:$.transport,status:U,attempts:g,createdAt:g[0]?.startedAt??i$(),completedAt:g.at(-1)?.completedAt??i$()}}class C8 extends Error{eventType;issues;constructor(_,$){let g=$.map((U)=>`${U.path||"<root>"}: ${U.message}`).join("; ");super(`Event validation failed for type "${_}": ${g}`);this.name="EventValidationError",this.eventType=_,this.issues=$}}class r8{definitions=new Map;register(_){return this.definitions.set(_.type,_),this}unregister(_){return this.definitions.delete(_)}has(_){return this.definitions.has(_)}get(_){return this.definitions.get(_)}list(){return[...this.definitions.values()]}validateEvent(_){let $=this.definitions.get(_.type);if(!$)return{ok:!0};return $.validate(_.data,_)}assertEventValid(_){let $=this.validateEvent(_);if(!$.ok)throw new C8(_.type,$.issues)}}var wv=new r8;function jz(_){return{id:_.id??uv(),source:_.source,type:_.type,time:nv(_.time),subject:_.subject,severity:_.severity??"info",data:_.data??{},message:_.message,dedupeKey:_.dedupeKey,schemaVersion:_.schemaVersion??"1.0",metadata:_.metadata??{}}}class v8{store;redactors;transportOptions;catalog;validateCatalogTypes;constructor(_={}){this.store=_.store??new GN(_.dataDir),this.redactors=_.redactors??[],this.transportOptions={fetchImpl:_.fetchImpl},this.catalog=_.catalog??wv,this.validateCatalogTypes=_.validateCatalogTypes??!1}async addChannel(_){let $=new Date().toISOString();return this.store.addChannel({..._,createdAt:_.createdAt??$,updatedAt:_.updatedAt??$})}async listChannels(){return this.store.listChannels()}async removeChannel(_){return this.store.removeChannel(_)}async emit(_,$={}){let g=$.redactSensitiveData===!1?jz(_):hv(jz(_));if($.validate??this.validateCatalogTypes)this.catalog.assertEventValid(g);let U=await this.appendEvent(g,{dedupe:$.dedupe!==!1});if(U.deduped)return{event:U.event,deliveries:[],deduped:!0};let D=$.deliver===!1?[]:await this.deliver(U.event);return{event:U.event,deliveries:D,deduped:!1}}async listEvents(_={}){if(Object.keys(_).length===0)return this.store.listEvents();return K8(await this.store.listEvents(),_)}async listEventsPage(_={}){if(this.store.listEventsPage)return this.store.listEventsPage(_);let $=K8(await this.store.listEvents(),{eventId:_.eventId,source:_.source,type:_.type}),g=YN(_.cursor,_),U=QN(_.limit),D=$.slice(g,g+U),I=g+D.length,j=I<$.length;return{events:D,cursor:_.cursor,nextCursor:j?k8(I,_):void 0,hasMore:j}}async listDeliveries(){return this.store.listDeliveries()}async deliver(_){let g=(await this.store.listChannels()).filter((D)=>Y8(D,_)),U=[];for(let D of g){let I=await this.applyRedaction(_,D),j=await this.deliverWithRetry(I,D);await this.store.appendDelivery(j),U.push(j)}return U}async matchChannel(_,$={}){let g=await this.store.getChannel(_);if(!g)throw Error(`Channel not found: ${_}`);let U=jz({source:$.source??"hasna.events",type:$.type??"events.test",subject:$.subject??_,severity:$.severity??"info",data:$.data??{test:!0},message:$.message??"Hasna events test delivery",dedupeKey:$.dedupeKey,schemaVersion:$.schemaVersion,metadata:$.metadata,time:$.time,id:$.id}),D=Y8(g,U);return{channelId:g.id,matched:D,event:U,filters:g.filters,reason:D?void 0:g.enabled?"event did not match channel filters":"channel is disabled"}}async testChannel(_,$={},g={}){let U=await this.store.getChannel(_);if(!U)throw Error(`Channel not found: ${_}`);let D=await this.matchChannel(_,$),I=D.event;if(g.honorFilters&&!D.matched){let O=new Date().toISOString(),A=B8(I,U,[{attempt:1,status:"skipped",startedAt:O,completedAt:O,error:D.reason}]);return A.metadata={reason:"filter_mismatch"},await this.store.appendDelivery(A),A}let j=await this.applyRedaction(I,U),N=await this.deliverWithRetry(j,U);return await this.store.appendDelivery(N),N}async replay(_={}){let $=_.cursor||_.limit!==void 0?await this.listEventsPage(_):{events:await this.listEvents(_),hasMore:!1};if(_.dryRun)return{events:$.events,deliveries:[],cursor:$.cursor,nextCursor:$.nextCursor,hasMore:$.hasMore};let g=[];for(let U of $.events)g.push(...await this.deliver(U));return{events:$.events,deliveries:g,cursor:$.cursor,nextCursor:$.nextCursor,hasMore:$.hasMore}}async appendEvent(_,$){if(this.store.appendEventOnce)return this.store.appendEventOnce(_,{dedupe:$.dedupe});if($.dedupe){let U=await this.store.findEventByIdentity({id:_.id,dedupeKey:_.dedupeKey});if(U)return{event:U,stored:!1,deduped:!0,identity:{id:U.id,dedupeKey:U.dedupeKey}}}let g=await this.store.appendEvent(_);return{event:g,stored:!0,deduped:!1,identity:{id:g.id,dedupeKey:g.dedupeKey}}}async applyRedaction(_,$){let g=xv(_,$.redact?.paths??[],$.redact?.replacement??"[REDACTED]");for(let U of this.redactors)g=await U(g,$);return g}async deliverWithRetry(_,$){let g=dv($.retry),U=[];for(let D=0;D<g.maxAttempts;D+=1){let I=await fv(_,$,this.transportOptions);if(I.attempt=D+1,I.status==="failed"&&D+1<g.maxAttempts)I.nextBackoffMs=Math.round(g.backoffMs*g.multiplier**D);if(U.push(I),I.status!=="failed")break;if(I.nextBackoffMs)await Bun.sleep(I.nextBackoffMs)}return B8(_,$,U)}}function xv(_,$,g="[REDACTED]"){if($.length===0)return _;let U=structuredClone(_);for(let D of $)cv(U,D,g);return U}function f8(_){let $=structuredClone(_);if($.webhook?.secret)$.webhook.secret="[REDACTED]";if($.command?.env)$.command.env=Object.fromEntries(Object.entries($.command.env).map(([g,U])=>[g,w8(g)?"[REDACTED]":U]));return $}function yv(_){return _.map(f8)}function hv(_,$="[REDACTED]"){return Ez(_,$)}function w8(_){return/secret|token|password|api[_-]?key|authorization/i.test(_)}function Ez(_,$){if(Array.isArray(_))return _.map((g)=>Ez(g,$));if(!_||typeof _!=="object")return _;return Object.fromEntries(Object.entries(_).map(([g,U])=>[g,w8(g)?$:Ez(U,$)]))}function cv(_,$,g){let U=$.split("."),D=_;for(let j of U.slice(0,-1)){let N=D[j];if(!N||typeof N!=="object")return;D=N}let I=U.at(-1);if(I&&I in D)D[I]=g}function K8(_,$){let g=_;if($.eventId)g=g.filter((U)=>U.id===$.eventId);if($.source)g=g.filter((U)=>U.source===$.source);if($.type)g=g.filter((U)=>U.type===$.type);if($.cursor)g=g.slice(YN($.cursor,$));if($.limit!==void 0)g=g.slice(0,QN($.limit));return g}function nv(_){if(!_)return new Date().toISOString();return _ instanceof Date?_.toISOString():_}function dv(_){return{maxAttempts:Math.max(1,_?.maxAttempts??1),backoffMs:Math.max(0,_?.backoffMs??250),multiplier:Math.max(1,_?.multiplier??2)}}function WN(_,$,g=!1){if(!_?.length)return;let U={};for(let D of _){let I=lv(D,$),j=I.path;if(j in U)throw Error(`Duplicate ${$} filter path: ${j}`);let N=g?iv(I.rawValue,$):I.rawValue;U[j]=I.negated?{not:N}:N}return U}function mv(_){let $={};if(_.source)$.source=_.source;if(_.type)$.type=_.type;if(_.subject)$.subject=_.subject;if(_.severity)$.severity=_.severity;let g=V8(WN(_.data,"data"),WN(_.dataJson,"data-json",!0)),U=V8(WN(_.metadata,"metadata"),WN(_.metadataJson,"metadata-json",!0));if(Object.keys(g).length>0)$.data=g;if(Object.keys(U).length>0)$.metadata=U;return Object.keys($).length>0?[$]:void 0}function V8(..._){let $={};for(let g of _){if(!g)continue;for(let[U,D]of Object.entries(g)){if(U in $)throw Error(`Duplicate filter path: ${U}`);$[U]=D}}return $}function iv(_,$){let g=JSON.parse(_);if(g===null||typeof g==="string"||typeof g==="number"||typeof g==="boolean"||Array.isArray(g)&&g.every((U)=>typeof U==="string"))return g;throw Error(`${$} filter JSON values must be string, string[], number, boolean, or null`)}function lv(_,$){let g=_.indexOf("!=");if(g>0)return{path:_.slice(0,g),rawValue:_.slice(g+2),negated:!0};let U=_.indexOf("=");if(U<=0)throw Error(`Invalid ${$} filter, expected path=value or path!=value: ${_}`);return{path:_.slice(0,U),rawValue:_.slice(U+1),negated:!1}}var tv=100;function Bg(_,$){if(!_)return $;let g=JSON.parse(_);if(!g||typeof g!=="object"||Array.isArray(g))throw Error("Expected a JSON object");return g}function ov(_){if(!_?.length)return;let $={};for(let g of _){let U=g.indexOf("=");if(U===-1)throw Error(`Invalid header, expected name=value: ${g}`);$[g.slice(0,U)]=g.slice(U+1)}return $}function p6(_){if(_.createClient)return _.createClient();return new v8({store:new GN(_.dataDir)})}function s4(_,$,g){if($)console.log(JSON.stringify(_,null,2));else console.log(g)}function F8(_,$){let g=_ instanceof Error?_.message:String(_);if($)console.log(JSON.stringify({error:g},null,2));else console.error(g);process.exitCode=1}function M8(_){return Boolean(_?.json||_?.opts?.().json||_?.optsWithGlobals?.().json||_?.parent?.opts?.().json||_?.parent?.optsWithGlobals?.().json)}function W6(_,$){return M8(_)||M8($)}function pv(_,$){let g=_.command($.channelsCommandName??"channels").description("Manage Hasna event channels");return g.command("add").description("Add or replace a channel").argument("<target>","Webhook URL or command binary").requiredOption("--id <id>","Channel identifier").option("--transport <kind>","Transport kind: webhook or command","webhook").option("--name <name>","Display name").option("--type <pattern>","Event type filter, e.g. todos.task.*").option("--source <pattern>","Event source filter").option("--subject <pattern>","Event subject filter").option("--severity <pattern>","Event severity filter").option("--data <path=value...>","Event data field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard",a4,[]).option("--metadata <path=value...>","Event metadata field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard",a4,[]).option("--data-json <path=json...>","Event data field filter with typed JSON value; path!=json negatives supported",a4,[]).option("--metadata-json <path=json...>","Event metadata field filter with typed JSON value; path!=json negatives supported",a4,[]).option("--secret <secret>","Webhook HMAC secret").option("--header <name=value...>","Webhook header",a4,[]).option("--arg <arg...>","Command argument",a4,[]).option("--timeout-ms <ms>","Transport timeout in milliseconds",FU).option("--retry-attempts <n>","Maximum delivery attempts",FU).option("--retry-backoff-ms <ms>","Initial retry backoff in milliseconds",FU).option("--redact <path...>","Event field path to redact before delivery",a4,[]).option("--disabled","Create channel disabled",!1).option("-j, --json","Print JSON output",!1).action(async(U,D,I)=>{let j=new Date().toISOString(),N={id:D.id,name:D.name,enabled:!D.disabled,transport:D.transport,filters:mv(D),retry:D.retryAttempts||D.retryBackoffMs?{maxAttempts:D.retryAttempts,backoffMs:D.retryBackoffMs}:void 0,redact:D.redact?.length?{paths:D.redact}:void 0,createdAt:j,updatedAt:j};if(D.transport==="webhook")N.webhook={url:U,secret:D.secret,headers:ov(D.header),timeoutMs:D.timeoutMs};else if(D.transport==="command")N.command={command:U,args:D.arg??[],timeoutMs:D.timeoutMs};else throw Error(`Transport ${D.transport} is reserved for future use and cannot be added yet`);let O=await p6($).addChannel(N);s4(f8(O),W6(D,I),`Added ${O.transport} channel ${O.id}`)}),g.command("list").description("List configured channels").option("-j, --json","Print JSON output",!1).action(async(U,D)=>{let I=await p6($).listChannels();if(W6(U,D)){console.log(JSON.stringify(yv(I),null,2));return}if(!I.length){console.log("No channels configured.");return}for(let j of I)console.log(`${j.id} ${j.enabled?"enabled":"disabled"} ${j.transport} ${j.webhook?.url??j.command?.command??j.transport}`)}),g.command("status").description("Show events channel storage status").option("-j, --json","Print JSON output",!1).action(async(U,D)=>{let I=await Fv($.dataDir);s4(I,W6(U,D),`events dataDir: ${I.dataDir}`)}),g.command("remove").description("Remove a channel").argument("<id>","Channel identifier").option("-j, --json","Print JSON output",!1).action(async(U,D,I)=>{let j=await p6($).removeChannel(U);s4({removed:j},W6(D,I),j?`Removed ${U}`:`Channel not found: ${U}`)}),g.command("test").description("Send a test event to one channel").argument("<id>","Channel identifier").option("--source <source>","Event source override").option("--type <type>","Event type","events.test").option("--subject <subject>","Event subject").option("--message <message>","Event message","Hasna events test delivery").option("--data <json>","Event data JSON object").option("--metadata <json>","Event metadata JSON object").option("--honor-filters","Skip delivery when the sample event does not match channel filters",!1).option("-j, --json","Print JSON output",!1).action(async(U,D,I)=>{let j=W6(D,I);try{let N=await p6($).testChannel(U,{source:D.source??$.source,type:D.type,subject:D.subject??U,message:D.message,data:Bg(D.data,{test:!0}),metadata:Bg(D.metadata,{})},{honorFilters:D.honorFilters});s4(N,j,`${N.status}: ${N.channelId}`)}catch(N){F8(N,j)}}),g.command("match").description("Check whether a sample event matches one channel without delivering").argument("<id>","Channel identifier").option("--source <source>","Event source override").option("--type <type>","Event type","events.test").option("--subject <subject>","Event subject").option("--message <message>","Event message","Hasna events match preview").option("--data <json>","Event data JSON object").option("--metadata <json>","Event metadata JSON object").option("-j, --json","Print JSON output",!1).action(async(U,D,I)=>{let j=W6(D,I);try{let N=await p6($).matchChannel(U,{source:D.source??$.source,type:D.type,subject:D.subject??U,message:D.message,data:Bg(D.data,{test:!0}),metadata:Bg(D.metadata,{})});s4(N,j,`${N.matched?"matched":"skipped"}: ${N.channelId}`)}catch(N){F8(N,j)}}),g}function ev(_,$){let g=_.command($.eventsCommandName??"events").description("Emit, list, and replay Hasna events");g.command("emit").description("Emit an event from this app").argument("<type>","Event type").option("--source <source>","Event source override").option("--subject <subject>","Event subject").option("--severity <severity>","Event severity","info").option("--message <message>","Event message").option("--dedupe-key <key>","Dedupe key").option("--data <json>","Event data JSON object").option("--metadata <json>","Event metadata JSON object").option("--no-deliver","Record without delivering").option("--no-dedupe","Allow duplicate id/dedupeKey events").option("-j, --json","Print JSON output",!1).action(async(D,I,j)=>{let N=await p6($).emit({source:I.source??$.source,type:D,subject:I.subject,severity:I.severity,message:I.message,dedupeKey:I.dedupeKey,data:Bg(I.data,{}),metadata:Bg(I.metadata,{})},{deliver:I.deliver,dedupe:I.dedupe});s4(N,W6(I,j),`${N.deduped?"Deduped":"Emitted"} ${N.event.id} to ${N.deliveries.length} channel(s)`)});let U=$.defaultEventListLimit??tv;return g.command("list").description("List recorded events").option("--source <source>","Filter by source").option("--type <type>","Filter by type").option("--limit <n>",`Limit to the most recent <n> events (default ${U}; use 0 for all)`,FU,U).option("-j, --json","Print JSON output",!1).action(async(D,I)=>{let j=await p6($).listEvents();if(D.source)j=j.filter((N)=>N.source===D.source);if(D.type)j=j.filter((N)=>N.type===D.type);if(D.limit)j=j.slice(-D.limit);if(W6(D,I)){console.log(JSON.stringify(j,null,2));return}if(!j.length){console.log("No events recorded.");return}for(let N of j)console.log(`${N.time} ${N.id} ${N.source} ${N.type} ${N.severity}`)}),g.command("replay").description("Replay recorded events").option("--id <id>","Replay one event id").option("--source <source>","Filter by source").option("--type <type>","Filter by type").option("--cursor <cursor>","Opaque replay cursor from a previous page").option("--limit <n>","Maximum events to replay",FU).option("--dry-run","Preview without delivery",!1).option("-j, --json","Print JSON output",!1).action(async(D,I)=>{let j=await p6($).replay({eventId:D.id,source:D.source,type:D.type,cursor:D.cursor,limit:D.limit,dryRun:D.dryRun});s4(j,W6(D,I),av(j.events.length,j.deliveries.length,j.nextCursor))}),g}function u8(_,$){pv(_,$),ev(_,$)}function FU(_){let $=Number(_);if(!Number.isFinite($))throw Error(`Expected a number, got ${_}`);return $}function a4(_,$){return $.push(_),$}function av(_,$,g){let U=g?`, next cursor: ${g}`:"";return`Replayed ${_} event(s), ${$} delivery result(s)${U}`}import{basename as _f}from"path";var C$={name:"@hasna/knowledge",version:"0.2.93",description:"Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions",type:"module",exports:{".":{import:"./dist/index.js",types:"./dist/index.d.ts"},"./storage":{import:"./dist/storage.js",types:"./dist/storage.d.ts"},"./serve":{import:"./dist/serve.js",types:"./dist/serve.d.ts"}},main:"./dist/index.js",types:"./dist/index.d.ts",bin:{knowledge:"bin/knowledge.js","knowledge-mcp":"bin/knowledge-mcp.js","knowledge-serve":"bin/knowledge-serve.js"},files:["bin","dist","scripts/apply-cloud-migrations.mjs","scripts/lib/remote-temp-dir.mjs","scripts/smoke-machine-sync-release.mjs","scripts/smoke-machines-adapter.mjs","scripts/smoke-open-files-installed-boundary.mjs","scripts/strip-generated-trailing-whitespace.mjs","scripts/verify-generated-artifacts.mjs","docs/architecture/ai-native-knowledge-base.md","docs/architecture/hosted-wrapper-responsibilities.md","docs/architecture/hybrid-semantic-search.md","docs/architecture/machine-sync-schema.md","docs/examples/app-project-wiki-standard.md","docs/examples/company-wiki-workflow.md","docs/migration/global-rules-provenance-import.md","docs/migration/json-to-sqlite.md","LICENSE","README.md"],scripts:{test:"bun test","test:cli":"bun test tests/cli.test.ts","test:package":"bun test tests/package-release.test.ts","release:pack:check":"node scripts/validate-public-package.mjs","smoke:machines-adapter":"bun scripts/smoke-machines-adapter.mjs","smoke:machine-sync-release":"bun scripts/smoke-machine-sync-release.mjs","smoke:open-files-installed-boundary":"bun scripts/smoke-open-files-installed-boundary.mjs","migrate:cloud":"bun scripts/apply-cloud-migrations.mjs",serve:"bun src/serve-entry.ts","verify:generated":"bun scripts/verify-generated-artifacts.mjs","contracts:conformance":"contracts conformance fixtures",build:"rm -rf dist && bun build --target=bun --outfile=bin/knowledge.js --minify --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/cli.ts && bun build --target=bun --outfile=bin/knowledge-mcp.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/mcp.js && bun build --target=bun --outfile=bin/knowledge-serve.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/serve-entry.ts && bun build ./src/index.ts ./src/storage.ts ./src/serve.ts --outdir ./dist --target bun --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek && bun scripts/strip-generated-trailing-whitespace.mjs && bunx tsc -p tsconfig.build.json",prepublishOnly:"bun run contracts:conformance && contracts no-cloud-scan . && bun run build && node scripts/validate-public-package.mjs"},keywords:["knowledge","cli","agents","json","notes","local","store"],license:"Apache-2.0",publishConfig:{registry:"https://registry.npmjs.org",access:"public"},repository:{type:"git",url:"git+https://github.com/hasna/knowledge.git"},bugs:{url:"https://github.com/hasna/knowledge/issues"},author:"Hasna Inc. <hasna@example.com>",engines:{bun:">=1.0",node:">=18"},dependencies:{"@ai-sdk/anthropic":"^3.0.81","@ai-sdk/deepseek":"^2.0.35","@ai-sdk/openai":"^3.0.68","@aws-sdk/client-s3":"^3.1063.0","@aws-sdk/credential-providers":"^3.1063.0","@hasna/events":"^0.1.3","@modelcontextprotocol/sdk":"^1.29.0","@types/json-schema":"^7.0.15",ai:"^6.0.197",commander:"^13.1.0",pg:"^8.16.3",zod:"^4.3.6"},devDependencies:{"@electric-sql/pglite":"^0.5.4","@hasna/contracts":"0.8.5","@types/bun":"^1.3.14","@types/pg":"^8.15.6"}};var x8={debug:0,info:1,warn:2,error:3},$f=()=>{if(process.env.DEBUG)return"debug";if(process.env.LOG_LEVEL==="debug")return"debug";if(process.env.LOG_LEVEL==="warn")return"warn";if(process.env.LOG_LEVEL==="error")return"error";return"info"};function $0(_,$,g){if(x8[_]<x8[$f()])return;let U={debug:"[DEBUG]",info:"[INFO]",warn:"[WARN]",error:"[ERROR]"}[_],D=g?`${U} ${$} ${JSON.stringify(g)}`:`${U} ${$}`;if(_==="error")console.error(D);else console.error(D)}var d8=["events","webhooks"],m8=["add","list","get","delete","update","archive","restore","upsert","untag","versions","diff","export","prune","dedupe","stats","inventory","project-panel","paths","mode","setup","auth","storage","machines","sync","db","wiki","app-wiki","source","ingest","reindex","search","context","proposals","web","ask","build","embeddings","providers","safety","help",...d8],i8={ls:"list",rm:"delete",edit:"update",unarchive:"restore"};function gf(_){let $=new Set,g=[];for(let U of _){let D=U.toLowerCase();if($.has(D))continue;$.add(D),g.push(U)}return g}function y8(_,$){let g=new Set((_??[]).map((U)=>U.toLowerCase()));return $.filter((U)=>!g.has(U.toLowerCase()))}function Oz(_,$,g){if(g===void 0)return{..._,message:$};return{..._,added:g.length,message:`${$} (added ${g.length} tag${g.length===1?"":"s"})`}}function Df(_,$){if($===void 0)throw Error("Missing value for --tag. Example: knowledge add <title> <content> -t <tag> -t <tag>");let g=$.split(",").map((U)=>U.trim()).filter((U)=>U.length>0);if(g.length===0)throw Error(`Invalid --tag value ${JSON.stringify($)}: no tag name found. Example: knowledge add <title> <content> -t <tag> -t <tag>`);return gf([..._??[],...g])}function Uf(_){let $=[],g={},U=!1;for(let D=0;D<_.length;D+=1){let I=_[D];if(U){$.push(I);continue}if(I==="--"){U=!0;continue}if(!I.startsWith("-")||$[0]==="add"&&$.length===2&&I.startsWith("---")){$.push(I);continue}switch(I){case"--json":g.json=!0;break;case"--verbose":g.verbose=!0;break;case"--yes":case"-y":g.yes=!0;break;case"--help":case"-h":g.help=!0;break;case"--version":case"-v":g.version=!0;break;case"--desc":g.desc=!0;break;case"--page":case"-p":g.page=Number(_[D+1]),D+=1;break;case"--limit":case"-l":g.limit=Number(_[D+1]),D+=1;break;case"--search":case"-s":g.search=_[D+1],D+=1;break;case"--sort":g.sort=_[D+1],D+=1;break;case"--id":g.id=_[D+1],D+=1;break;case"--store":g.store=_[D+1],D+=1;break;case"--title":g.title=_[D+1],D+=1;break;case"--content":g.content=_[D+1],D+=1;break;case"--url":g.url=_[D+1],D+=1;break;case"--tag":case"-t":g.tag=Df(g.tag,_[D+1]),g.tagRaw=[...g.tagRaw??[],_[D+1]],D+=1;break;case"--format":g.format=_[D+1],D+=1;break;case"--completions":g.completions=_[D+1],D+=1;break;case"--purpose":g.purpose=_[D+1],D+=1;break;case"--model":g.model=_[D+1],D+=1;break;case"--strategy":g.strategy=_[D+1],D+=1;break;case"--dimensions":g.dimensions=Number(_[D+1]),D+=1;break;case"--semantic":g.semantic=!0;break;case"--context":g.context=!0;break;case"--max-tokens":g.maxTokens=Number(_[D+1]),D+=1;break;case"--max-items":g.maxItems=Number(_[D+1]),D+=1;break;case"--from":g.from=_[D+1],D+=1;break;case"--to":g.to=_[D+1],D+=1;break;case"--rev":g.rev=Number(_[D+1]),D+=1;break;case"--since":g.since=_[D+1],D+=1;break;case"--topic":g.topic=_[D+1],D+=1;break;case"--dedupe":g.dedupe=!0;break;case"--generate":g.generate=!0;break;case"--approve-write":g.approveWrite=!0;break;case"--provider":g.provider=_[D+1],D+=1;break;case"--mode":g.mode=_[D+1],D+=1;break;case"--machine":g.machine=_[D+1],D+=1;break;case"--workspace":g.workspace=_[D+1],D+=1;break;case"--api-url":g.apiUrl=_[D+1],D+=1;break;case"--canonical-example":g.canonicalExample=!0;break;case"--api-key":g.apiKey=_[D+1],D+=1;break;case"--email":g.email=_[D+1],D+=1;break;case"--org":g.org=_[D+1],D+=1;break;case"--org-id":g.orgId=_[D+1],D+=1;break;case"--user-id":g.userId=_[D+1],D+=1;break;case"--owner":g.owner=_[D+1],D+=1;break;case"--approved-by":g.approvedBy=_[D+1],D+=1;break;case"--patch-uri":g.patchUri=_[D+1],D+=1;break;case"--domain":g.domain=[...g.domain??[],_[D+1]],D+=1;break;case"--file-results":g.fileResults=!0;break;case"--full":g.full=!0;break;case"--dry-run":g.dryRun=!0;break;case"--fake":g.fake=!0;break;case"--no-tailscale":g.tailscale=!1;break;case"--no-artifact-content":g.artifactContent=!1;break;case"--no-color":g.noColor=!0;break;case"--scope":g.scope=_[D+1],D+=1;break;case"--tables":g.tables=_[D+1],D+=1;break;case"--peer-workspace":g.peerWorkspace=_[D+1],D+=1;break;case"--older-than":g.olderThan=Number(_[D+1]),D+=1;break;case"--empty":g.empty=!0;break;case"--archived":g.archived=!0;break;case"--include-archived":g.includeArchived=!0;break;case"--project":g.project=_[D+1],D+=1;break;case"--contract":g.contract=!0;break;case"--source-ref":g.sourceRef=[...g.sourceRef??[],_[D+1]],D+=1;break;case"--allow-global":g.allowGlobal=!0;break;default:throw Error(`Unknown flag: ${I}. Run 'knowledge --help' for valid options.`)}}return{positional:$,flags:g}}function If(_){if(!_)return"";return i8[_]??_}function jf(_,$){let g=Array.from({length:_.length+1},()=>Array($.length+1).fill(0));for(let U=0;U<=_.length;U+=1)g[U][0]=U;for(let U=0;U<=$.length;U+=1)g[0][U]=U;for(let U=1;U<=_.length;U+=1)for(let D=1;D<=$.length;D+=1){let I=_[U-1]===$[D-1]?0:1;g[U][D]=Math.min(g[U-1][D]+1,g[U][D-1]+1,g[U-1][D-1]+I)}return g[_.length][$.length]}function Nf(_){if(!_)return"";let $=[...m8,...Object.keys(i8)],g="",U=Number.POSITIVE_INFINITY;for(let D of $){let I=jf(_,D);if(I<U)U=I,g=D}return U<=3?g:""}function Ef(){return _f(process.argv[1]??"").replace(/\.(?:js|ts|mjs|cjs)$/,"")==="knowledge"}async function Of(_){if(!d8.includes(_[0]??""))return!1;let $=new X8;return $.name("knowledge").description("Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions"),u8($,{source:"knowledge"}),await $.parseAsync(_,{from:"user"}),!0}function Af(){console.log(`knowledge - local agent knowledge store Usage: knowledge <command> [options] diff --git a/fixtures/project-panel.invalid.json b/fixtures/project-panel.invalid.json new file mode 100644 index 0000000..77edd59 --- /dev/null +++ b/fixtures/project-panel.invalid.json @@ -0,0 +1,91 @@ +{ + "schema": "hasna.project_panel.v1", + "id": "knowledge_panel_open-knowledge", + "createdAt": "2026-08-01T09:00:00.000Z", + "projectId": "open-knowledge", + "provider": { + "kind": "knowledge", + "id": "knowledge_open-knowledge", + "name": "Knowledge", + "sourcePackage": "@hasna/knowledge", + "externalId": "project://open-knowledge" + }, + "kind": "knowledge", + "title": "Knowledge", + "summary": "1 active note, 1 source, and 3 indexed chunks.", + "state": "ready", + "generatedAt": "2026-08-01T09:00:00.000Z", + "freshness": "fresh", + "metrics": [ + { + "id": "active_items", + "label": "Active notes", + "value": 1, + "status": "good" + }, + { + "id": "active_items", + "label": "Duplicate active notes metric", + "value": 1, + "status": "good" + } + ], + "items": [ + { + "id": "item_contract-boundaries", + "title": "Contract boundaries", + "summary": "Shared project-panel payloads are parsed at the package boundary.", + "status": "active", + "priority": "medium", + "resourceRefs": [ + { + "kind": "knowledge", + "id": "contract-boundaries", + "name": "Contract boundaries", + "uri": "knowledge://item/contract-boundaries", + "externalId": "contract-boundaries", + "sourcePackage": "@hasna/knowledge", + "tags": [ + "contracts" + ] + } + ], + "evidenceRefs": [] + } + ], + "actions": [ + { + "kind": "action", + "id": "knowledge:inventory", + "name": "Inspect knowledge inventory", + "externalId": "knowledge:inventory", + "sourcePackage": "@hasna/knowledge", + "tags": [] + } + ], + "resourceRefs": [ + { + "kind": "project", + "id": "open-knowledge", + "name": "open-knowledge", + "uri": "project://open-knowledge", + "externalId": "open-knowledge", + "sourcePackage": "@hasna/knowledge", + "tags": [] + } + ], + "renderFragment": { + "renderer": "json_render", + "title": "Knowledge", + "spec": { + "component": "project.knowledge.summary", + "metrics": [ + "active_items" + ] + } + }, + "metadata": { + "scope": "project", + "home": "project://open-knowledge" + } +} diff --git a/fixtures/project-panel.valid.json b/fixtures/project-panel.valid.json new file mode 100644 index 0000000..42b32ae --- /dev/null +++ b/fixtures/project-panel.valid.json @@ -0,0 +1,85 @@ +{ + "schema": "hasna.project_panel.v1", + "id": "knowledge_panel_open-knowledge", + "createdAt": "2026-08-01T09:00:00.000Z", + "projectId": "open-knowledge", + "provider": { + "kind": "knowledge", + "id": "knowledge_open-knowledge", + "name": "Knowledge", + "sourcePackage": "@hasna/knowledge", + "externalId": "project://open-knowledge" + }, + "kind": "knowledge", + "title": "Knowledge", + "summary": "1 active note, 1 source, and 3 indexed chunks.", + "state": "ready", + "generatedAt": "2026-08-01T09:00:00.000Z", + "freshness": "fresh", + "metrics": [ + { + "id": "active_items", + "label": "Active notes", + "value": 1, + "status": "good" + } + ], + "items": [ + { + "id": "item_contract-boundaries", + "title": "Contract boundaries", + "summary": "Shared project-panel payloads are parsed at the package boundary.", + "status": "active", + "priority": "medium", + "resourceRefs": [ + { + "kind": "knowledge", + "id": "contract-boundaries", + "name": "Contract boundaries", + "uri": "knowledge://item/contract-boundaries", + "externalId": "contract-boundaries", + "sourcePackage": "@hasna/knowledge", + "tags": [ + "contracts" + ] + } + ], + "evidenceRefs": [] + } + ], + "actions": [ + { + "kind": "action", + "id": "knowledge:inventory", + "name": "Inspect knowledge inventory", + "externalId": "knowledge:inventory", + "sourcePackage": "@hasna/knowledge", + "tags": [] + } + ], + "resourceRefs": [ + { + "kind": "project", + "id": "open-knowledge", + "name": "open-knowledge", + "uri": "project://open-knowledge", + "externalId": "open-knowledge", + "sourcePackage": "@hasna/knowledge", + "tags": [] + } + ], + "renderFragment": { + "renderer": "json_render", + "title": "Knowledge", + "spec": { + "component": "project.knowledge.summary", + "metrics": [ + "active_items" + ] + } + }, + "metadata": { + "scope": "project", + "home": "project://open-knowledge" + } +} diff --git a/package.json b/package.json index c74834f..152539f 100644 --- a/package.json +++ b/package.json @@ -56,8 +56,9 @@ "migrate:cloud": "bun scripts/apply-cloud-migrations.mjs", "serve": "bun src/serve-entry.ts", "verify:generated": "bun scripts/verify-generated-artifacts.mjs", + "contracts:conformance": "contracts conformance fixtures", "build": "rm -rf dist && bun build --target=bun --outfile=bin/knowledge.js --minify --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/cli.ts && bun build --target=bun --outfile=bin/knowledge-mcp.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/mcp.js && bun build --target=bun --outfile=bin/knowledge-serve.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/serve-entry.ts && bun build ./src/index.ts ./src/storage.ts ./src/serve.ts --outdir ./dist --target bun --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek && bun scripts/strip-generated-trailing-whitespace.mjs && bunx tsc -p tsconfig.build.json", - "prepublishOnly": "bun run build && node scripts/validate-public-package.mjs" + "prepublishOnly": "bun run contracts:conformance && contracts no-cloud-scan . && bun run build && node scripts/validate-public-package.mjs" }, "keywords": [ "knowledge",