@nodatachat/mcp 1.6.33 → 1.6.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/gate.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @nodatachat/mcp 1.6.33 | Copyright 2026 Capsule Ltd. | FSL-1.1-ALv2 | see LICENSE.md */
1
+ /*! @nodatachat/mcp 1.6.34 | Copyright 2026 Capsule Ltd. | FSL-1.1-ALv2 | see LICENSE.md */
2
2
  import{appendFileSync as mc,copyFileSync as rh,existsSync as In,mkdirSync as vs,readdirSync as gc,readFileSync as Tn,statSync as xt,writeFileSync as yc}from"node:fs";import{homedir as Y}from"node:os";import{basename as wt,extname as Ss,isAbsolute as ne,join as xe,relative as Es,resolve as D}from"node:path";import{TextDecoder as $r}from"node:util";var H={text:[],header:[],secrets:[],table:null,amounts:null,severity:null};function $t(e){e.table&&(H.table=e.table),e.severity&&(H.severity=e.severity),e.amounts&&(H.amounts=e.amounts);for(let t of e.text??[])H.text.some(r=>r.kind===t.kind)||H.text.push(t);e.header&&!H.header.includes(e.header)&&H.header.push(e.header);for(let t of e.secrets??[])H.secrets.some(r=>r.id===t.id)||H.secrets.push(t)}function Rt(){return H.amounts?.()===!0}function De(){return H.text}function jn(){return H.header}function Se(){return H.table}function Hn(){return H.secrets}var Fe=[{id:"aws-access-key",name:"AWS Access Key",pattern:/(?:^|[^A-Z0-9])AKIA(?!X{16})[A-Z2-7]{16}(?:[^A-Z2-7]|$)/,severity:"critical"},{id:"aws-secret-key",name:"AWS Secret Key",pattern:/(?:aws_secret_access_key|aws_secret_key|secret_access_key)\s*[=:]\s*['"]?[A-Za-z0-9/+=]{40}['"]?/,severity:"critical"},{id:"gcp-api-key",name:"GCP API Key",pattern:/AIza[0-9A-Za-z_-]{35}/,severity:"critical"},{id:"gcp-service-account",name:"GCP Service Account",pattern:/"type"\s*:\s*"service_account"/,severity:"critical"},{id:"github-token",name:"GitHub Token",pattern:/gh[pousr]_[0-9a-zA-Z]{36,}/,severity:"critical"},{id:"github-fine-grained",name:"GitHub Fine-Grained Token",pattern:/github_pat_[0-9a-zA-Z_]{22,}/,severity:"critical"},{id:"gitlab-token",name:"GitLab Token",pattern:/glpat-[0-9a-zA-Z_-]{20,}/,severity:"critical"},{id:"stripe-secret",name:"Stripe Secret Key",pattern:/sk_live_[0-9a-zA-Z]{24,}/,severity:"critical"},{id:"stripe-restricted",name:"Stripe Restricted Key",pattern:/rk_live_[0-9a-zA-Z]{24,}/,severity:"critical"},{id:"slack-token",name:"Slack Token",pattern:/xox[baprs]-[0-9a-zA-Z-]{10,}/,severity:"high"},{id:"slack-webhook",name:"Slack Webhook",pattern:/hooks\.slack\.com\/services\/T[0-9A-Z]{8,}\/B[0-9A-Z]{8,}\/[0-9a-zA-Z]{24}/,severity:"high"},{id:"twilio-api-key",name:"Twilio API Key",pattern:/SK[0-9a-fA-F]{32}/,severity:"high"},{id:"sendgrid-api-key",name:"SendGrid API Key",pattern:/SG\.[0-9a-zA-Z_-]{22}\.[0-9a-zA-Z_-]{43}/,severity:"critical"},{id:"postgres-uri",name:"PostgreSQL Connection String",pattern:/postgres(?:ql)?:\/\/[^\s'"]{10,}/,severity:"critical"},{id:"mongodb-uri",name:"MongoDB Connection String",pattern:/mongodb(?:\+srv)?:\/\/[^\s'"]{10,}/,severity:"critical"},{id:"redis-uri",name:"Redis Connection String",pattern:/redis(?:s)?:\/\/[^\s'"]{10,}/,severity:"high"},{id:"openai-key",name:"OpenAI API Key",pattern:/sk-[0-9a-zA-Z]{20,}T3BlbkFJ[0-9a-zA-Z]{20,}/,severity:"critical"},{id:"openai-project-key",name:"OpenAI API Key",pattern:/sk-(?:proj|svcacct|admin)-[0-9A-Za-z_-]{40,}/,severity:"critical"},{id:"anthropic-key",name:"Anthropic API Key",pattern:/sk-ant-[0-9a-zA-Z_-]{40,}/,severity:"critical"},{id:"firebase-key",name:"Firebase API Key",pattern:/AIza[0-9A-Za-z_-]{35}/,severity:"high"},{id:"supabase-key",name:"Supabase Service Key",pattern:/eyJ[0-9a-zA-Z_-]{100,}/,severity:"critical"},{id:"supabase-access-token",name:"Supabase Access Token",pattern:/\bsbp_[0-9a-f]{40}\b/,severity:"critical"},{id:"supabase-secret-key",name:"Supabase Secret Key",pattern:/\bsb_secret_[0-9A-Za-z_-]{20,}/,severity:"critical"},{id:"private-key",name:"Private Key",pattern:/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,severity:"critical"},{id:"jwt-secret",name:"JWT/Generic Secret",pattern:/(?:jwt_secret|JWT_SECRET|secret_key|SECRET_KEY|api_secret|API_SECRET)\s*[=:]\s*['"][^'"]{8,}['"]/,severity:"high"},{id:"password-in-code",name:"Password in Code",pattern:/(?:password|passwd|pwd)\s*[=:]\s*['"][^'"]{8,}['"]/,severity:"high"},{id:"bearer-token",name:"Bearer Token in Code",pattern:/['"]Bearer\s+[0-9a-zA-Z._-]{20,}['"]/,severity:"high"}];function Ee(){let e=Hn().filter(t=>!Fe.some(r=>r.id===t.id));return[...Fe,...e]}var to=[{pattern:/^(full_?name|first_?name|last_?name|spouse_?name|beneficiary_?name)$/i,type:"name",label:"Person name"},{pattern:/^(email|e_?mail|email_?address)$/i,type:"email",label:"Email address"},{pattern:/^(phone|mobile|telephone|phone_?number|cell_?phone)$/i,type:"phone",label:"Phone number"},{pattern:/^(id_?number|identity|ssn|social_?security|national_?id|teudat_?zehut)$/i,type:"id",label:"ID number"},{pattern:/^(address|street_?address|home_?address)$/i,type:"address",label:"Address"},{pattern:/^(date_?of_?birth|dob|birth_?date|birthday)$/i,type:"dob",label:"Date of birth"},{pattern:/^(bank_?account|iban|account_?number|bank_?details|routing_?number)$/i,type:"financial",label:"Bank account"},{pattern:/^(credit_?card|card_?number|cvv|cc_?number)$/i,type:"financial",label:"Credit card"},{pattern:/^(salary|income|commission|premium|compensation)$/i,type:"financial",label:"Salary / income"},{pattern:/^(medical_?info|health_?conditions|diagnosis|medication|prescription)$/i,type:"medical",label:"Medical"},{pattern:/^(passport|passport_?number|passport_?id)$/i,type:"id",label:"Passport"},{pattern:/^(driver_?license|drivers_?license|dl_?number)$/i,type:"id",label:"Driver license"},{pattern:/^(tax_?id|tin|vat_?number|ein)$/i,type:"financial",label:"Tax ID"},{pattern:/^(insurance_?number|policy_?number|member_?id)$/i,type:"id",label:"Insurance/Policy"},{pattern:/^(biometric|fingerprint|face_?id|retina)$/i,type:"biometric",label:"Biometric"},{pattern:/^(latitude|longitude|lat|lng|geo_?location|coordinates)$/i,type:"location",label:"Geolocation"}],no=[{pattern:/^(?:שם|שם_מלא|שם פרטי|שם_פרטי|שם משפחה|שם_משפחה|שם הלקוח|שם בן זוג|שם המבוטח|שם המוטב|שם הילד|שם הקטין)$/i,type:"name",label:"\u05E9\u05DD \u05D0\u05D3\u05DD"},{pattern:/^(?:ת"ז|ת\.ז|ת ז|תז|תעודת זהות|תעודת_זהות|מספר זהות|מס' זהות|teudat_zehut|tz|israeli_id|il_id)$/i,type:"id",label:"\u05EA\u05E2\u05D5\u05D3\u05EA \u05D6\u05D4\u05D5\u05EA"},{pattern:/^(?:דרכון|מספר דרכון|רישיון נהיגה|רישיון_נהיגה|רשיון נהיגה)$/i,type:"id",label:"\u05D3\u05E8\u05DB\u05D5\u05DF/\u05E8\u05D9\u05E9\u05D9\u05D5\u05DF"},{pattern:/^(?:מייל|דוא"ל|דוא_ל|דואר אלקטרוני|אימייל|כתובת מייל)$/i,type:"email",label:"\u05D3\u05D5\u05D0\u05E8 \u05D0\u05DC\u05E7\u05D8\u05E8\u05D5\u05E0\u05D9"},{pattern:/^(?:טלפון|טל'|נייד|פלאפון|סלולרי|מספר טלפון|טלפון נייד)$/i,type:"phone",label:"\u05DE\u05E1\u05E4\u05E8 \u05D8\u05DC\u05E4\u05D5\u05DF"},{pattern:/^(?:כתובת|כתובת מגורים|רחוב|מס בית|עיר|ישוב|יישוב|מיקוד|כתובת מלאה|מען)$/i,type:"address",label:"\u05DB\u05EA\u05D5\u05D1\u05EA"},{pattern:/^(?:תאריך לידה|ת לידה|תאריך הולדת|יום הולדת|גיל)$/i,type:"dob",label:"\u05EA\u05D0\u05E8\u05D9\u05DA \u05DC\u05D9\u05D3\u05D4"},{pattern:/^(?:חשבון בנק|מספר חשבון|מס חשבון|סניף|iban|iban_il)$/i,type:"financial",label:"\u05D7\u05E9\u05D1\u05D5\u05DF \u05D1\u05E0\u05E7"},{pattern:/^(?:כרטיס אשראי|מספר אשראי|cvv|אשראי)$/i,type:"financial",label:"\u05DB\u05E8\u05D8\u05D9\u05E1 \u05D0\u05E9\u05E8\u05D0\u05D9"},{pattern:/^(?:שכר|משכורת|הכנסה|פרמיה|שכר ברוטו|שכר נטו|תגמול|בונוס|פיצוי)$/i,type:"financial",label:"\u05E9\u05DB\u05E8/\u05D4\u05DB\u05E0\u05E1\u05D4"},{pattern:/^(?:אבחון|אבחנה|מחלה|תרופה|מרשם|בדיקה רפואית|מצב רפואי|חיסון|רגישות|אלרגיה|סוג דם)$/i,type:"medical",label:"\u05DE\u05D9\u05D3\u05E2 \u05E8\u05E4\u05D5\u05D0\u05D9"},{pattern:/^(?:מספר פוליסה|מס פוליסה|פוליסה|מספר חבר|מספר ביטוח)$/i,type:"id",label:"\u05E4\u05D5\u05DC\u05D9\u05E1\u05D4/\u05D7\u05D1\u05E8\u05D5\u05EA"},{pattern:/^(?:שם הילד|תעודת זהות של ילד|ילדים|קטין|מספר ילדים|תעודת לידה)$/i,type:"minor_data",label:"\u05DE\u05D9\u05D3\u05E2 \u05E2\u05DC \u05E7\u05D8\u05D9\u05DF"},{pattern:/^(?:דת|לאום|מין|נטייה מינית|השקפה פוליטית|מצב משפחתי|עדה|מוצא)$/i,type:"sensitive_personal",label:"\u05DE\u05D9\u05D3\u05E2 \u05D0\u05D9\u05E9\u05D9 \u05E8\u05D2\u05D9\u05E9"},{pattern:/^(?:מספר רכב|מס רכב|לוחית רישוי|מספר רישוי)$/i,type:"tracking",label:"\u05DE\u05E1\u05E4\u05E8 \u05E8\u05DB\u05D1"}],ro=/^(id|uuid|created_?at|updated_?at|deleted_?at|status|type|category|description|title|slug|url|path|key|value|enabled|active|public|version|metadata|config|settings|data|content|note|comment|label|color|icon)$/i,so=/^שם[ _](?:ה)?(?:קובץ|מוצר|פריט|חברה|ארגון|עסק|פרויקט|גיליון|טבלה|שדה|קטגוריה|סניף|בנק|עיר|רחוב|ישוב|יישוב|מסמך|תוכנית|חבילה|קורס|מחלקה|אתר|דומיין|מותג|ספק|שירות|תפקיד)$|^(file|product|item|company|org|organization|project|sheet|table|field|category|branch|bank|city|street|document|plan|course|department|site|domain|brand|vendor|service|role)[ _-]?name$/i;function Ae(e){if(!e)return null;let t=e.trim();if(ro.test(t)||/^name$/i.test(t)||/^\[[A-Z_]+(?:-\d+)?\]$/.test(t))return null;let r=t.replace(/[\s-]+/g,"_");for(let n of[...no,...to])if(n.pattern.test(t)||n.pattern.test(r))return{type:n.type,label:n.label};if(so.test(t))return null;if(/^שם[ _][\u0590-\u05FF"'׳ ]{2,20}$/.test(t))return{type:"name",label:"\u05E9\u05DD \u05D0\u05D3\u05DD"};if(/^(customer|client|employee|inspector|supervisor|contact|person|patient|user|owner|manager|agent|member|driver|tenant|student|parent|beneficiary|insured)_?(full_?)?name$/i.test(r))return{type:"name",label:"Person name"};for(let n of jn()){let s=n(t);if(s)return{type:s.type,label:s.label}}return null}function Un(e){let t=String(e??"").replace(/\D/g,"");if(t.length===0||t.length>9)return!1;let r=t.padStart(9,"0"),n=0;for(let s=0;s<9;s++){let o=parseInt(r[s]??"0",10)*(s%2+1);o>9&&(o-=9),n+=o}return n%10===0&&parseInt(r,10)>0}function oo(e){let t=String(e??"").replace(/\D/g,"");if(t.length<12||t.length>19)return!1;let r=0,n=!1;for(let s=t.length-1;s>=0;s-=1){let o=parseInt(t[s]??"0",10);n&&(o*=2,o>9&&(o-=9)),r+=o,n=!n}return r%10===0}function Bn(e){let t=String(e??"").replace(/\s/g,"").toUpperCase();if(!/^[A-Z]{2}\d{2}[A-Z0-9]{10,30}$/.test(t))return!1;let r=t.slice(4)+t.slice(0,4),n=0;for(let s of r){let o=s>="A"&&s<="Z"?String(s.charCodeAt(0)-55):s;for(let i of o)n=(n*10+Number(i))%97}return n===1}var It="(?<!\\d)(?<!\\d[.,])",Tt="(?!\\d)(?![.,]\\d)",Ct=[{subkind:"israeli_id",severity:"high",pattern:new RegExp(`${It}(?:\\d{8}-\\d|\\d{9})${Tt}`,"g"),validate:Un,hint:"Israeli national ID (check digit verified) in plaintext. Encrypt the column or mask it at rest."},{subkind:"credit_card",severity:"critical",pattern:new RegExp(`${It}[2-6][ -]?(?:\\d[ -]?){11,17}\\d${Tt}`,"g"),validate:oo,hint:"Payment card number (Luhn verified) in plaintext. This is PCI scope."},{subkind:"iban",severity:"high",pattern:/\b[A-Z]{2}\d{2}(?: ?[A-Z0-9]{4}){2,7}(?: ?[A-Z0-9]{1,4})?\b/g,validate:Bn,hint:"Bank account in IBAN form (mod-97 verified) in plaintext."},{subkind:"israeli_phone",severity:"medium",pattern:new RegExp(`${It}(?:\\+972[- ]?|0)(?:[57]\\d|[2-489])[- ]?\\d{3}[- ]?\\d{4}${Tt}`,"g"),validate:e=>{let t=e.replace(/\D/g,"").replace(/^972/,"0");return/^0[57]\d{8}$/.test(t)?!0:/^0[2-489]\d{7}$/.test(t)&&(/[- ]/.test(e)||e.startsWith("+972"))},hint:"Israeli phone number in plaintext."}];function Me(e,t){let r=[],n="",s=!1;for(let o=0;o<e.length;o++){let i=e[o];s?i==='"'&&e[o+1]==='"'?(n+='"',o++):i==='"'?s=!1:n+=i:i==='"'?s=!0:i===t?(r.push(n),n=""):n+=i}return r.push(n),r}import{inflateRawSync as ti}from"node:zlib";import{inflateSync as zn}from"node:zlib";var je=new Set([".pdf"]),Wn=8*1024*1024;function Kn(e){let t=e.toString("latin1"),r=new Map,n=/(\d+)\s+(\d+)\s+obj\b/g,s;for(;(s=n.exec(t))!==null;){let o=Number(s[1]),i=n.lastIndex,a=t.indexOf("endobj",i);if(a<0)continue;let c=t.indexOf("stream",i),l,f=null;if(c>=0&&c<a){l=t.slice(i,c);let d=c+6;t[d]==="\r"&&(d+=1),t[d]===`
3
3
  `&&(d+=1);let h=t.indexOf("endstream",d);h>d&&(f=e.subarray(d,h))}else l=t.slice(i,a);r.set(o,{num:o,dict:l,stream:f})}for(let o of[...r.values()]){if(!o.stream||!/\/Type\s*\/ObjStm\b/.test(o.dict))continue;let i=He(o),a=Number(/\/N\s+(\d+)/.exec(o.dict)?.[1]??0),c=Number(/\/First\s+(\d+)/.exec(o.dict)?.[1]??-1);if(i===null||!a||c<0)continue;let l=i.slice(0,c).trim().split(/\s+/).map(Number);for(let f=0;f<a;f+=1){let d=l[2*f],h=l[2*f+1];if(d===void 0||h===void 0||Number.isNaN(d)||Number.isNaN(h))break;let p=f+1<a?l[2*(f+1)+1]:void 0,g=i.slice(c+h,p!==void 0?c+p:void 0);r.has(d)||r.set(d,{num:d,dict:g,stream:null})}}return r}function Pt(e){let t=[...e.replace(/\s+/g,"")];if(t.length<40)return!1;let r=0,n=0;for(let s of t){let o=s.codePointAt(0)??0;o<32||o>=127&&o<=191||o===65533||o>=57344&&o<=63743?r+=1:/[\p{L}\p{N}]/u.test(s)&&(n+=1)}return r/t.length>.2||n/t.length<.25}function io(e){let t=e.toString("latin1").replace(/\s/g,"");t.startsWith("<~")&&(t=t.slice(2));let r=t.indexOf("~>");r>=0&&(t=t.slice(0,r));let n=[],s=0,o=0;for(let i of t){if(i==="z"&&o===0){n.push(0,0,0,0);continue}let a=i.charCodeAt(0)-33;if(a<0||a>84)return null;s=s*85+a,o+=1,o===5&&(n.push(s>>>24&255,s>>>16&255,s>>>8&255,s&255),s=0,o=0)}if(o>0){for(let a=o;a<5;a+=1)s=s*85+84;let i=[s>>>24&255,s>>>16&255,s>>>8&255,s&255];for(let a=0;a<o-1;a+=1)n.push(i[a]??0)}return Buffer.from(n)}function ao(e){let t=e.toString("latin1").replace(/\s/g,"").split(">")[0]??"",r=t.length%2?t+"0":t;return/^[0-9A-Fa-f]*$/.test(r)?Buffer.from(r,"hex"):null}function co(e){let t=/\/Filter\s*(\[[^\]]*\]|\/\w+)/.exec(e);return!t||!t[1]?[]:[...t[1].matchAll(/\/(\w+)/g)].map(r=>r[1]??"")}function He(e,t){if(!e.stream)return null;let r=e.stream;for(let n of co(e.dict)){let s=null;if(n==="FlateDecode")try{s=t!==void 0?zn(r,{maxOutputLength:Math.max(1,t)}):zn(r)}catch{return null}else if(n==="ASCII85Decode")s=io(r);else if(n==="ASCIIHexDecode")s=ao(r);else return null;if(s===null)return null;r=s}return r.toString("latin1")}function lo(e){let t=new Map,r=1,n=/begincodespacerange([\s\S]*?)endcodespacerange/.exec(e);if(n&&n[1]){let c=/<([0-9A-Fa-f]+)>/.exec(n[1]);c&&c[1]&&c[1].length>=4&&(r=2)}let s=c=>{let l="";for(let f=0;f+3<c.length+1;f+=4){let d=parseInt(c.slice(f,f+4),16);Number.isFinite(d)&&d>0&&(l+=String.fromCharCode(d))}return l},o=/beginbfchar([\s\S]*?)endbfchar/g,i;for(;(i=o.exec(e))!==null;){let c=/<([0-9A-Fa-f]+)>\s*<([0-9A-Fa-f]*)>/g,l;for(;(l=c.exec(i[1]??""))!==null;)t.set(parseInt(l[1]??"0",16),s(l[2]??""))}let a=/beginbfrange([\s\S]*?)endbfrange/g;for(;(i=a.exec(e))!==null;){let c=i[1]??"",l=/<([0-9A-Fa-f]+)>\s*<([0-9A-Fa-f]+)>\s*<([0-9A-Fa-f]*)>/g,f;for(;(f=l.exec(c))!==null;){let d=parseInt(f[1]??"0",16),h=parseInt(f[2]??"0",16),p=parseInt((f[3]??"0").slice(0,4)||"0",16);if(!(h-d>65535))for(let g=d;g<=h;g+=1)t.set(g,String.fromCharCode(p+(g-d)))}}return{map:t,bytes:r}}function uo(e){let t=new Map,r=[],n=[];for(let o of e.values()){for(let i of o.dict.matchAll(/\/Font\s*<<([\s\S]*?)>>/g))i[1]&&n.push(i[1]);for(let i of o.dict.matchAll(/\/Font\s+(\d+)\s+\d+\s+R/g)){let a=e.get(Number(i[1]));a&&n.push(a.dict)}}let s=new Set;for(let o of n){let i=/\/([^\s/<>[\]()]+)\s+(\d+)\s+\d+\s+R/g,a;for(;(a=i.exec(o))!==null;){let c=`${a[1]}#${a[2]}`;s.has(c)||(s.add(c),r.push([a[1]??"",Number(a[2])]))}}for(let[o,i]of r){let a=e.get(i);if(!a)continue;let c=null,l=/\/ToUnicode\s+(\d+)\s+\d+\s+R/.exec(a.dict);if(l){let d=e.get(Number(l[1])),h=d?He(d):null;h&&(c=lo(h))}if(c||(c=ho(a.dict,e)),!c)continue;let f=t.get(o)??[];f.push(c),t.set(o,f)}return t}var fo=["alef","bet","gimel","dalet","he","vav","zayin","het","tet","yod","finalkaf","kaf","lamed","finalmem","mem","finalnun","nun","samekh","ayin","finalpe","pe","finaltsadi","tsadi","qof","resh","shin","tav"];function po(e){let t=/^uni([0-9A-Fa-f]{4})$/.exec(e)??/^u([0-9A-Fa-f]{4,6})$/.exec(e);if(t)return String.fromCodePoint(parseInt(t[1],16));let r=/^afii(\d{5})$/.exec(e);if(r){let o=Number(r[1]);if(o>=57664&&o<=57690)return String.fromCharCode(1488+(o-57664))}let n=fo.indexOf(e.replace(/hebrew$/,""));return n>=0?String.fromCharCode(1488+n):e==="space"?" ":/^[A-Za-z]$/.test(e)?e:{zero:"0",one:"1",two:"2",three:"3",four:"4",five:"5",six:"6",seven:"7",eight:"8",nine:"9",period:".",comma:",",hyphen:"-",colon:":",slash:"/",at:"@"}[e]??null}function ho(e,t){let r=e,n=/\/Encoding\s+(\d+)\s+\d+\s+R/.exec(e);n&&(r=t.get(Number(n[1]))?.dict??"");let s=/\/Differences\s*\[([^\]]*)\]/.exec(r);if(!s?.[1])return null;let o=new Map,i=0;for(let a of s[1].match(/\d+|\/[^\s/[\]]+/g)??[]){if(/^\d+$/.test(a)){i=Number(a);continue}let c=po(a.slice(1));c!==null&&o.set(i,c),i+=1}return o.size?{map:o,bytes:1}:null}function Zn(e){let t=[];for(let r=0;r<e.length;r+=1){let n=e[r];if(n!=="\\"){t.push(n.charCodeAt(0)&255);continue}let s=e[r+1];if(s===void 0)break;if(s>="0"&&s<="7"){let i="",a=r+1;for(;a<e.length&&i.length<3&&e[a]>="0"&&e[a]<="7";)i+=e[a],a+=1;t.push(parseInt(i,8)&255),r=a-1;continue}let o={n:10,r:13,t:9,b:8,f:12};t.push(o[s]??s.charCodeAt(0)&255),r+=1}return t}function Yn(e){if(e.length<2||e[0]!==254||e[1]!==255)return null;let t="";for(let r=2;r+1<e.length;r+=2)t+=String.fromCharCode((e[r]??0)<<8|(e[r+1]??0));return t}function Vn(e,t){let r=Yn(e);if(r!==null)return r;if(!t)return e.map(s=>String.fromCharCode(s)).join("");let n="";if(t.bytes===2)for(let s=0;s+1<e.length;s+=2){let o=(e[s]??0)<<8|(e[s+1]??0);n+=t.map.get(o)??""}else for(let s of e)n+=t.map.get(s)??String.fromCharCode(s);return n}function mo(e){let t=0;for(let r of e){let n=r.codePointAt(0)??0;/[\p{L}\p{N}]/u.test(r)?t+=1:(n<32||n>=127&&n<=191||n===65533)&&(t-=2)}return t}function Gn(e,t){if(!t||t.length<=1)return Vn(e,t?.[0]);let r="",n=-1/0;for(let s of t){let o=Vn(e,s),i=mo(o);i>n&&(r=o,n=i)}return r}function go(e,t){let r="",n="",s,o=!1,i="",a=0,c=null,l=()=>{n.trim()&&(r+=n+`
4
4
  `),n="",a=0},f=()=>{n&&!n.endsWith(" ")&&a>1&&(n+=" ")},d=/\/([^\s/<>[\]()]+)\s+-?[\d.]+\s+Tf|(-?[\d.]+)\s+(-?[\d.]+)\s+T[dD]\b|(?:-?[\d.]+\s+){5}(-?[\d.]+)\s+Tm\b|T\*|\((?:\\.|[^\\)])*\)|<[0-9A-Fa-f\s]*>|\[|\]|-?\d+(?:\.\d+)?|TJ|Tj|'|"/g,h;for(;(h=d.exec(e))!==null;){let p=h[0];if(h[1]!==void 0&&p.endsWith("Tf")){s=t.get(h[1]);continue}if(h[2]!==void 0){Math.abs(Number(h[3]))>.01?l():Number(h[2])!==0&&f();continue}if(h[4]!==void 0){let g=Number(h[4]);c!==null&&Math.abs(g-c)>.01?l():c!==null&&f(),c=g;continue}if(p==="T*"){l();continue}if(p==="["){o=!0;continue}if(p==="]"){o=!1;continue}if(p==="TJ"||p==="Tj"||p==="'"||p==='"'){(p==="'"||p==='"')&&l(),i&&(n+=i,a=i.trim().length,i="");continue}if(p.startsWith("("))i+=Gn(Zn(p.slice(1,-1)),s);else if(p.startsWith("<")){let g=p.slice(1,-1).replace(/\s+/g,""),u=[];for(let y=0;y+1<g.length;y+=2)u.push(parseInt(g.slice(y,y+2),16));i+=Gn(u,s)}else o&&Number(p)<-200&&i&&!i.endsWith(" ")&&(i+=" ")}return i&&(n+=i),l(),r}function yo(e,t){let r=e.toString("latin1"),n=/\/Info\s+(\d+)\s+\d+\s+R/.exec(r),s=n?t.get(Number(n[1]))?.dict:void 0;if(!s)return"";let o=[],i=/\/(Author|Title|Subject|Keywords|Creator|Producer)\s*(\((?:\\.|[^\\)])*\)|<[0-9A-Fa-f\s]*>)/g,a;for(;(a=i.exec(s))!==null;){let c=a[2]??"";if(c.startsWith("(")){let l=Zn(c.slice(1,-1));o.push(Yn(l)??l.map(f=>String.fromCharCode(f)).join(""))}else{let l=c.slice(1,-1).replace(/\s+/g,""),f="";if(l.toUpperCase().startsWith("FEFF"))for(let d=4;d+3<l.length+1;d+=4)f+=String.fromCharCode(parseInt(l.slice(d,d+4),16));else for(let d=0;d+1<l.length;d+=2)f+=String.fromCharCode(parseInt(l.slice(d,d+2),16));o.push(f)}}return o.join(`
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- /*! @nodatachat/mcp 1.6.33 | Copyright 2026 Capsule Ltd. | FSL-1.1-ALv2 | see LICENSE.md */
2
+ /*! @nodatachat/mcp 1.6.34 | Copyright 2026 Capsule Ltd. | FSL-1.1-ALv2 | see LICENSE.md */
3
3
  import{createRequire as __ndReq}from"node:module";const[__ndMaj,__ndMin]=process.versions.node.split(".").map(Number);if(!((__ndMaj===20&&__ndMin>=19)||(__ndMaj===22&&__ndMin>=12)||__ndMaj>=23)){process.stderr.write("NoData needs Node.js 20.19+, 22.12+ or 23+; this computer has "+process.version+".\nInstall the current LTS from https://nodejs.org (or: nvm install --lts), then run the same command again.\n");process.exit(1)}const require=__ndReq(import.meta.url);
4
4
  var H1=Object.create;var Qu=Object.defineProperty;var U1=Object.getOwnPropertyDescriptor;var K1=Object.getOwnPropertyNames;var W1=Object.getPrototypeOf,G1=Object.prototype.hasOwnProperty;var re=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,n)=>(typeof require<"u"?require:t)[n]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var H=(e,t)=>()=>(e&&(t=e(e=0)),t);var ge=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),kc=(e,t)=>{for(var n in t)Qu(e,n,{get:t[n],enumerable:!0})},V1=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of K1(t))!G1.call(e,o)&&o!==n&&Qu(e,o,{get:()=>t[o],enumerable:!(r=U1(t,o))||r.enumerable});return e};var Ce=(e,t,n)=>(n=e!=null?H1(W1(e)):{},V1(t||!e||!e.__esModule?Qu(n,"default",{value:e,enumerable:!0}):n,e));function ep(e){e.table&&(at.table=e.table),e.severity&&(at.severity=e.severity),e.amounts&&(at.amounts=e.amounts);for(let t of e.text??[])at.text.some(n=>n.kind===t.kind)||at.text.push(t);e.header&&!at.header.includes(e.header)&&at.header.push(e.header);for(let t of e.secrets??[])at.secrets.some(n=>n.id===t.id)||at.secrets.push(t)}function Sc(e){return at.severity?.(e)}function tp(){return at.amounts?.()===!0}function Ho(){return at.text}function g_(){return at.header}function Uo(){return at.table}function m_(){return at.secrets}var at,rr=H(()=>{"use strict";at={text:[],header:[],secrets:[],table:null,amounts:null,severity:null}});function At(){let e=m_().filter(t=>!xc.some(n=>n.id===t.id));return[...xc,...e]}function q1(e,t){let n=i=>i.replace(/^[^0-9]*/,"").split(".").map(s=>parseInt(s,10)||0),r=n(e),o=n(t);for(let i=0;i<3;i++){if((r[i]||0)<(o[i]||0))return!0;if((r[i]||0)>(o[i]||0))return!1}return!1}function b_(e){let t=[],n;try{n=JSON.parse(e)}catch{return t}let r={...n.dependencies??{},...n.devDependencies??{}};for(let[o,i]of Object.entries(r)){let s=(i||"").replace(/^[\^~>=<\s]+/,""),a=z1.find(c=>c.name===o&&q1(s,c.below));a&&t.push({name:o,version:s,vuln:a})}return t}function Ln(e){if(!e)return null;let t=e.trim();if(X1.test(t)||/^name$/i.test(t)||/^\[[A-Z_]+(?:-\d+)?\]$/.test(t))return null;let n=t.replace(/[\s-]+/g,"_");for(let r of[...J1,...Y1])if(r.pattern.test(t)||r.pattern.test(n))return{type:r.type,label:r.label};if(Z1.test(t))return null;if(/^שם[ _][\u0590-\u05FF"'׳ ]{2,20}$/.test(t))return{type:"name",label:"\u05E9\u05DD \u05D0\u05D3\u05DD"};if(/^(customer|client|employee|inspector|supervisor|contact|person|patient|user|owner|manager|agent|member|driver|tenant|student|parent|beneficiary|insured)_?(full_?)?name$/i.test(n))return{type:"name",label:"Person name"};for(let r of g_()){let o=r(t);if(o)return{type:o.type,label:o.label}}return null}function Ec(e){let t=String(e??"").replace(/\D/g,"");if(t.length===0||t.length>9)return!1;let n=t.padStart(9,"0"),r=0;for(let o=0;o<9;o++){let i=parseInt(n[o]??"0",10)*(o%2+1);i>9&&(i-=9),r+=i}return r%10===0&&parseInt(n,10)>0}function Q1(e){let t=String(e??"").replace(/\D/g,"");if(t.length<12||t.length>19)return!1;let n=0,r=!1;for(let o=t.length-1;o>=0;o-=1){let i=parseInt(t[o]??"0",10);r&&(i*=2,i>9&&(i-=9)),n+=i,r=!r}return n%10===0}function Ac(e){let t=String(e??"").replace(/\s/g,"").toUpperCase();if(!/^[A-Z]{2}\d{2}[A-Z0-9]{10,30}$/.test(t))return!1;let n=t.slice(4)+t.slice(0,4),r=0;for(let o of n){let i=o>="A"&&o<="Z"?String(o.charCodeAt(0)-55):o;for(let s of i)r=(r*10+Number(s))%97}return r===1}function w_(e){if(e.length!==14&&e.length!==17)return!1;let t=+e.slice(0,4),n=+e.slice(4,6),r=+e.slice(6,8),o=+e.slice(8,10),i=+e.slice(10,12),s=+e.slice(12,14);return t>=1970&&t<=2099&&n>=1&&n<=12&&r>=1&&r<=31&&o<24&&i<60&&s<60}function v_(e){let t=Number(e);return!Number.isSafeInteger(t)||t<1024?!1:Number.isInteger(Math.log2(t))||t%1048576===0}function xs(e){return eP.has(e.replace(/\D/g,""))}function Tc(e){let t=e.replace(/\D/g,"");return w_(t)||v_(t)}function Vr(e,t){let n=[];for(let r of Es){r.pattern.lastIndex=0;let o=new Set,i,s=0;for(;(i=r.pattern.exec(e))!==null&&(s+=1,!(s>2e5));){let a=i[0];if(!(r.subkind!=="iban"&&r.subkind!=="israeli_phone"&&Tc(a))&&(r.validate(a)&&o.add(a.replace(/[\s-]/g,"")),o.size>=1e5))break}if(o.size>0){if(t)for(let c of o)xs(c)||t(r.subkind,c);let a=[...o].filter(c=>!xs(c)).length;n.push({subkind:r.subkind,severity:a===0?"low":r.severity,hint:a===0?`${r.hint} All ${o.size} are published test numbers.`:r.hint,count:o.size,real:a})}}for(let r of Ho()){let o=new Set;for(let i of r.find(e))o.add(e.slice(i.start,i.end).trim());o.size>0&&n.push({subkind:r.kind,severity:r.severity,hint:r.hint,count:o.size})}return n}function zr(e,t){let n=[],r="",o=!1;for(let i=0;i<e.length;i++){let s=e[i];o?s==='"'&&e[i+1]==='"'?(r+='"',i++):s==='"'?o=!1:r+=s:s==='"'?o=!0:s===t?(n.push(r),r=""):r+=s}return n.push(r),n}var xc,y_,__,op,z1,Y1,J1,X1,Z1,$c,ip,$s,np,rp,Es,eP,or=H(()=>{"use strict";rr();xc=[{id:"aws-access-key",name:"AWS Access Key",pattern:/(?:^|[^A-Z0-9])AKIA(?!X{16})[A-Z2-7]{16}(?:[^A-Z2-7]|$)/,severity:"critical"},{id:"aws-secret-key",name:"AWS Secret Key",pattern:/(?:aws_secret_access_key|aws_secret_key|secret_access_key)\s*[=:]\s*['"]?[A-Za-z0-9/+=]{40}['"]?/,severity:"critical"},{id:"gcp-api-key",name:"GCP API Key",pattern:/AIza[0-9A-Za-z_-]{35}/,severity:"critical"},{id:"gcp-service-account",name:"GCP Service Account",pattern:/"type"\s*:\s*"service_account"/,severity:"critical"},{id:"github-token",name:"GitHub Token",pattern:/gh[pousr]_[0-9a-zA-Z]{36,}/,severity:"critical"},{id:"github-fine-grained",name:"GitHub Fine-Grained Token",pattern:/github_pat_[0-9a-zA-Z_]{22,}/,severity:"critical"},{id:"gitlab-token",name:"GitLab Token",pattern:/glpat-[0-9a-zA-Z_-]{20,}/,severity:"critical"},{id:"stripe-secret",name:"Stripe Secret Key",pattern:/sk_live_[0-9a-zA-Z]{24,}/,severity:"critical"},{id:"stripe-restricted",name:"Stripe Restricted Key",pattern:/rk_live_[0-9a-zA-Z]{24,}/,severity:"critical"},{id:"slack-token",name:"Slack Token",pattern:/xox[baprs]-[0-9a-zA-Z-]{10,}/,severity:"high"},{id:"slack-webhook",name:"Slack Webhook",pattern:/hooks\.slack\.com\/services\/T[0-9A-Z]{8,}\/B[0-9A-Z]{8,}\/[0-9a-zA-Z]{24}/,severity:"high"},{id:"twilio-api-key",name:"Twilio API Key",pattern:/SK[0-9a-fA-F]{32}/,severity:"high"},{id:"sendgrid-api-key",name:"SendGrid API Key",pattern:/SG\.[0-9a-zA-Z_-]{22}\.[0-9a-zA-Z_-]{43}/,severity:"critical"},{id:"postgres-uri",name:"PostgreSQL Connection String",pattern:/postgres(?:ql)?:\/\/[^\s'"]{10,}/,severity:"critical"},{id:"mongodb-uri",name:"MongoDB Connection String",pattern:/mongodb(?:\+srv)?:\/\/[^\s'"]{10,}/,severity:"critical"},{id:"redis-uri",name:"Redis Connection String",pattern:/redis(?:s)?:\/\/[^\s'"]{10,}/,severity:"high"},{id:"openai-key",name:"OpenAI API Key",pattern:/sk-[0-9a-zA-Z]{20,}T3BlbkFJ[0-9a-zA-Z]{20,}/,severity:"critical"},{id:"openai-project-key",name:"OpenAI API Key",pattern:/sk-(?:proj|svcacct|admin)-[0-9A-Za-z_-]{40,}/,severity:"critical"},{id:"anthropic-key",name:"Anthropic API Key",pattern:/sk-ant-[0-9a-zA-Z_-]{40,}/,severity:"critical"},{id:"firebase-key",name:"Firebase API Key",pattern:/AIza[0-9A-Za-z_-]{35}/,severity:"high"},{id:"supabase-key",name:"Supabase Service Key",pattern:/eyJ[0-9a-zA-Z_-]{100,}/,severity:"critical"},{id:"supabase-access-token",name:"Supabase Access Token",pattern:/\bsbp_[0-9a-f]{40}\b/,severity:"critical"},{id:"supabase-secret-key",name:"Supabase Secret Key",pattern:/\bsb_secret_[0-9A-Za-z_-]{20,}/,severity:"critical"},{id:"private-key",name:"Private Key",pattern:/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,severity:"critical"},{id:"jwt-secret",name:"JWT/Generic Secret",pattern:/(?:jwt_secret|JWT_SECRET|secret_key|SECRET_KEY|api_secret|API_SECRET)\s*[=:]\s*['"][^'"]{8,}['"]/,severity:"high"},{id:"password-in-code",name:"Password in Code",pattern:/(?:password|passwd|pwd)\s*[=:]\s*['"][^'"]{8,}['"]/,severity:"high"},{id:"bearer-token",name:"Bearer Token in Code",pattern:/['"]Bearer\s+[0-9a-zA-Z._-]{20,}['"]/,severity:"high"}];y_=/node_modules\/|\.next\/|\.test\.|\.spec\.|__tests__\/|\.nodata-proof\.json$|\.git\//,__=/(^|\/)\.env(\.[\w-]+)*\.(example|sample|template|dist)$|\.(example|sample|template)$|(^|\/)[^/]*\.(example|sample)\.[a-z0-9]+$/i,op=/(^|\/)(__tests__|__mocks__|tests?|spec|fixtures?|mocks?|samples?|examples?|demo|testdata)(\/|$)|\.(test|spec)\.[a-z0-9]+$|\.min\.(m?js|css)$|(^|\/)[^/]*\.worker\.min\.m?js$/i,z1=[{name:"lodash",below:"4.17.21",severity:"critical",cve:"CVE-2021-23337",desc:"Command injection via template"},{name:"minimist",below:"1.2.6",severity:"critical",cve:"CVE-2021-44906",desc:"Prototype pollution"},{name:"json5",below:"2.2.2",severity:"high",cve:"CVE-2022-46175",desc:"Prototype pollution"},{name:"express",below:"4.19.2",severity:"high",cve:"CVE-2024-29041",desc:"Open redirect"},{name:"path-to-regexp",below:"6.3.0",severity:"high",cve:"CVE-2024-45296",desc:"ReDoS"},{name:"sanitize-html",below:"2.12.1",severity:"high",cve:"CVE-2024-21501",desc:"XSS bypass"},{name:"dompurify",below:"3.0.9",severity:"high",cve:"CVE-2024-45801",desc:"mXSS"},{name:"jsonwebtoken",below:"9.0.0",severity:"critical",cve:"CVE-2022-23529",desc:"Key confusion attack"},{name:"jose",below:"4.15.5",severity:"high",cve:"CVE-2024-28176",desc:"DoS via JWE"},{name:"axios",below:"1.6.0",severity:"high",cve:"CVE-2023-45857",desc:"CSRF cookie leak"},{name:"node-fetch",below:"2.6.7",severity:"high",cve:"CVE-2022-0235",desc:"Header leak on redirect"},{name:"serialize-javascript",below:"6.0.2",severity:"critical",cve:"CVE-2024-11831",desc:"XSS via serialization"},{name:"xml2js",below:"0.6.0",severity:"high",cve:"CVE-2023-0842",desc:"Prototype pollution"},{name:"next",below:"14.1.1",severity:"high",cve:"CVE-2024-34350",desc:"SSRF via headers"},{name:"sharp",below:"0.32.6",severity:"high",cve:"CVE-2023-4863",desc:"libwebp heap overflow"},{name:"multer",below:"1.4.5",severity:"high",cve:"CVE-2022-24434",desc:"Prototype pollution"},{name:"tough-cookie",below:"4.1.3",severity:"medium",cve:"CVE-2023-26136",desc:"Prototype pollution"},{name:"semver",below:"7.5.4",severity:"medium",cve:"CVE-2022-25883",desc:"ReDoS"},{name:"ip",below:"2.0.1",severity:"critical",cve:"CVE-2024-29415",desc:"SSRF bypass via octal IP"},{name:"tar",below:"6.2.1",severity:"high",cve:"CVE-2024-28863",desc:"DoS via path traversal"}];Y1=[{pattern:/^(full_?name|first_?name|last_?name|spouse_?name|beneficiary_?name)$/i,type:"name",label:"Person name"},{pattern:/^(email|e_?mail|email_?address)$/i,type:"email",label:"Email address"},{pattern:/^(phone|mobile|telephone|phone_?number|cell_?phone)$/i,type:"phone",label:"Phone number"},{pattern:/^(id_?number|identity|ssn|social_?security|national_?id|teudat_?zehut)$/i,type:"id",label:"ID number"},{pattern:/^(address|street_?address|home_?address)$/i,type:"address",label:"Address"},{pattern:/^(date_?of_?birth|dob|birth_?date|birthday)$/i,type:"dob",label:"Date of birth"},{pattern:/^(bank_?account|iban|account_?number|bank_?details|routing_?number)$/i,type:"financial",label:"Bank account"},{pattern:/^(credit_?card|card_?number|cvv|cc_?number)$/i,type:"financial",label:"Credit card"},{pattern:/^(salary|income|commission|premium|compensation)$/i,type:"financial",label:"Salary / income"},{pattern:/^(medical_?info|health_?conditions|diagnosis|medication|prescription)$/i,type:"medical",label:"Medical"},{pattern:/^(passport|passport_?number|passport_?id)$/i,type:"id",label:"Passport"},{pattern:/^(driver_?license|drivers_?license|dl_?number)$/i,type:"id",label:"Driver license"},{pattern:/^(tax_?id|tin|vat_?number|ein)$/i,type:"financial",label:"Tax ID"},{pattern:/^(insurance_?number|policy_?number|member_?id)$/i,type:"id",label:"Insurance/Policy"},{pattern:/^(biometric|fingerprint|face_?id|retina)$/i,type:"biometric",label:"Biometric"},{pattern:/^(latitude|longitude|lat|lng|geo_?location|coordinates)$/i,type:"location",label:"Geolocation"}],J1=[{pattern:/^(?:שם|שם_מלא|שם פרטי|שם_פרטי|שם משפחה|שם_משפחה|שם הלקוח|שם בן זוג|שם המבוטח|שם המוטב|שם הילד|שם הקטין)$/i,type:"name",label:"\u05E9\u05DD \u05D0\u05D3\u05DD"},{pattern:/^(?:ת"ז|ת\.ז|ת ז|תז|תעודת זהות|תעודת_זהות|מספר זהות|מס' זהות|teudat_zehut|tz|israeli_id|il_id)$/i,type:"id",label:"\u05EA\u05E2\u05D5\u05D3\u05EA \u05D6\u05D4\u05D5\u05EA"},{pattern:/^(?:דרכון|מספר דרכון|רישיון נהיגה|רישיון_נהיגה|רשיון נהיגה)$/i,type:"id",label:"\u05D3\u05E8\u05DB\u05D5\u05DF/\u05E8\u05D9\u05E9\u05D9\u05D5\u05DF"},{pattern:/^(?:מייל|דוא"ל|דוא_ל|דואר אלקטרוני|אימייל|כתובת מייל)$/i,type:"email",label:"\u05D3\u05D5\u05D0\u05E8 \u05D0\u05DC\u05E7\u05D8\u05E8\u05D5\u05E0\u05D9"},{pattern:/^(?:טלפון|טל'|נייד|פלאפון|סלולרי|מספר טלפון|טלפון נייד)$/i,type:"phone",label:"\u05DE\u05E1\u05E4\u05E8 \u05D8\u05DC\u05E4\u05D5\u05DF"},{pattern:/^(?:כתובת|כתובת מגורים|רחוב|מס בית|עיר|ישוב|יישוב|מיקוד|כתובת מלאה|מען)$/i,type:"address",label:"\u05DB\u05EA\u05D5\u05D1\u05EA"},{pattern:/^(?:תאריך לידה|ת לידה|תאריך הולדת|יום הולדת|גיל)$/i,type:"dob",label:"\u05EA\u05D0\u05E8\u05D9\u05DA \u05DC\u05D9\u05D3\u05D4"},{pattern:/^(?:חשבון בנק|מספר חשבון|מס חשבון|סניף|iban|iban_il)$/i,type:"financial",label:"\u05D7\u05E9\u05D1\u05D5\u05DF \u05D1\u05E0\u05E7"},{pattern:/^(?:כרטיס אשראי|מספר אשראי|cvv|אשראי)$/i,type:"financial",label:"\u05DB\u05E8\u05D8\u05D9\u05E1 \u05D0\u05E9\u05E8\u05D0\u05D9"},{pattern:/^(?:שכר|משכורת|הכנסה|פרמיה|שכר ברוטו|שכר נטו|תגמול|בונוס|פיצוי)$/i,type:"financial",label:"\u05E9\u05DB\u05E8/\u05D4\u05DB\u05E0\u05E1\u05D4"},{pattern:/^(?:אבחון|אבחנה|מחלה|תרופה|מרשם|בדיקה רפואית|מצב רפואי|חיסון|רגישות|אלרגיה|סוג דם)$/i,type:"medical",label:"\u05DE\u05D9\u05D3\u05E2 \u05E8\u05E4\u05D5\u05D0\u05D9"},{pattern:/^(?:מספר פוליסה|מס פוליסה|פוליסה|מספר חבר|מספר ביטוח)$/i,type:"id",label:"\u05E4\u05D5\u05DC\u05D9\u05E1\u05D4/\u05D7\u05D1\u05E8\u05D5\u05EA"},{pattern:/^(?:שם הילד|תעודת זהות של ילד|ילדים|קטין|מספר ילדים|תעודת לידה)$/i,type:"minor_data",label:"\u05DE\u05D9\u05D3\u05E2 \u05E2\u05DC \u05E7\u05D8\u05D9\u05DF"},{pattern:/^(?:דת|לאום|מין|נטייה מינית|השקפה פוליטית|מצב משפחתי|עדה|מוצא)$/i,type:"sensitive_personal",label:"\u05DE\u05D9\u05D3\u05E2 \u05D0\u05D9\u05E9\u05D9 \u05E8\u05D2\u05D9\u05E9"},{pattern:/^(?:מספר רכב|מס רכב|לוחית רישוי|מספר רישוי)$/i,type:"tracking",label:"\u05DE\u05E1\u05E4\u05E8 \u05E8\u05DB\u05D1"}],X1=/^(id|uuid|created_?at|updated_?at|deleted_?at|status|type|category|description|title|slug|url|path|key|value|enabled|active|public|version|metadata|config|settings|data|content|note|comment|label|color|icon)$/i,Z1=/^שם[ _](?:ה)?(?:קובץ|מוצר|פריט|חברה|ארגון|עסק|פרויקט|גיליון|טבלה|שדה|קטגוריה|סניף|בנק|עיר|רחוב|ישוב|יישוב|מסמך|תוכנית|חבילה|קורס|מחלקה|אתר|דומיין|מותג|ספק|שירות|תפקיד)$|^(file|product|item|company|org|organization|project|sheet|table|field|category|branch|bank|city|street|document|plan|course|department|site|domain|brand|vendor|service|role)[ _-]?name$/i;$c={id:"critical",financial:"critical",medical:"critical",biometric:"critical",minor_data:"critical",sensitive_personal:"critical",email:"high",phone:"high",name:"high",address:"high",dob:"high",tracking:"medium",location:"high",auth:"critical",employment:"medium",education:"medium",behavior:"high"};ip=/(?<!\d)(?<!\d[.,])(?:\+972[\s-]?|0)(?:5[0-9]|7[0-9]|[2-4]|[89])(?:[\s-]?\d){7}(?!\d)(?![.,]\d)/g,$s=/\bIL\d{2}\s?(?:\d{4}\s?){4}\d{3}\b/g;np="(?<!\\d)(?<!\\d[.,])",rp="(?!\\d)(?![.,]\\d)",Es=[{subkind:"israeli_id",severity:"high",pattern:new RegExp(`${np}(?:\\d{8}-\\d|\\d{9})${rp}`,"g"),validate:Ec,hint:"Israeli national ID (check digit verified) in plaintext. Encrypt the column or mask it at rest."},{subkind:"credit_card",severity:"critical",pattern:new RegExp(`${np}[2-6][ -]?(?:\\d[ -]?){11,17}\\d${rp}`,"g"),validate:Q1,hint:"Payment card number (Luhn verified) in plaintext. This is PCI scope."},{subkind:"iban",severity:"high",pattern:/\b[A-Z]{2}\d{2}(?: ?[A-Z0-9]{4}){2,7}(?: ?[A-Z0-9]{1,4})?\b/g,validate:Ac,hint:"Bank account in IBAN form (mod-97 verified) in plaintext."},{subkind:"israeli_phone",severity:"medium",pattern:new RegExp(`${np}(?:\\+972[- ]?|0)(?:[57]\\d|[2-489])[- ]?\\d{3}[- ]?\\d{4}${rp}`,"g"),validate:e=>{let t=e.replace(/\D/g,"").replace(/^972/,"0");return/^0[57]\d{8}$/.test(t)?!0:/^0[2-489]\d{7}$/.test(t)&&(/[- ]/.test(e)||e.startsWith("+972"))},hint:"Israeli phone number in plaintext."}];eP=new Set(["000000018","123456782","111111118","222222226","333333334","999999998","4111111111111111","4242424242424242","4000056655665556","5555555555554444","5105105105105100","2223003122003222","378282246310005","371449635398431","6011111111111117","3056930009020004","4012888888881881","4000000000000002"])});import{inflateSync as k_}from"node:zlib";function Pc(e){let t=e.toString("latin1"),n=new Map,r=/(\d+)\s+(\d+)\s+obj\b/g,o;for(;(o=r.exec(t))!==null;){let i=Number(o[1]),s=r.lastIndex,a=t.indexOf("endobj",s);if(a<0)continue;let c=t.indexOf("stream",s),l,d=null;if(c>=0&&c<a){l=t.slice(s,c);let u=c+6;t[u]==="\r"&&(u+=1),t[u]===`
5
5
  `&&(u+=1);let p=t.indexOf("endstream",u);p>u&&(d=e.subarray(u,p))}else l=t.slice(s,a);n.set(i,{num:i,dict:l,stream:d})}for(let i of[...n.values()]){if(!i.stream||!/\/Type\s*\/ObjStm\b/.test(i.dict))continue;let s=Ts(i),a=Number(/\/N\s+(\d+)/.exec(i.dict)?.[1]??0),c=Number(/\/First\s+(\d+)/.exec(i.dict)?.[1]??-1);if(s===null||!a||c<0)continue;let l=s.slice(0,c).trim().split(/\s+/).map(Number);for(let d=0;d<a;d+=1){let u=l[2*d],p=l[2*d+1];if(u===void 0||p===void 0||Number.isNaN(u)||Number.isNaN(p))break;let f=d+1<a?l[2*(d+1)+1]:void 0,h=s.slice(c+p,f!==void 0?c+f:void 0);n.has(u)||n.set(u,{num:u,dict:h,stream:null})}}return n}function sp(e){let t=[...e.replace(/\s+/g,"")];if(t.length<40)return!1;let n=0,r=0;for(let o of t){let i=o.codePointAt(0)??0;i<32||i>=127&&i<=191||i===65533||i>=57344&&i<=63743?n+=1:/[\p{L}\p{N}]/u.test(o)&&(r+=1)}return n/t.length>.2||r/t.length<.25}function tP(e){let t=e.toString("latin1").replace(/\s/g,"");t.startsWith("<~")&&(t=t.slice(2));let n=t.indexOf("~>");n>=0&&(t=t.slice(0,n));let r=[],o=0,i=0;for(let s of t){if(s==="z"&&i===0){r.push(0,0,0,0);continue}let a=s.charCodeAt(0)-33;if(a<0||a>84)return null;o=o*85+a,i+=1,i===5&&(r.push(o>>>24&255,o>>>16&255,o>>>8&255,o&255),o=0,i=0)}if(i>0){for(let a=i;a<5;a+=1)o=o*85+84;let s=[o>>>24&255,o>>>16&255,o>>>8&255,o&255];for(let a=0;a<i-1;a+=1)r.push(s[a]??0)}return Buffer.from(r)}function nP(e){let t=e.toString("latin1").replace(/\s/g,"").split(">")[0]??"",n=t.length%2?t+"0":t;return/^[0-9A-Fa-f]*$/.test(n)?Buffer.from(n,"hex"):null}function rP(e){let t=/\/Filter\s*(\[[^\]]*\]|\/\w+)/.exec(e);return!t||!t[1]?[]:[...t[1].matchAll(/\/(\w+)/g)].map(n=>n[1]??"")}function Ts(e,t){if(!e.stream)return null;let n=e.stream;for(let r of rP(e.dict)){let o=null;if(r==="FlateDecode")try{o=t!==void 0?k_(n,{maxOutputLength:Math.max(1,t)}):k_(n)}catch{return null}else if(r==="ASCII85Decode")o=tP(n);else if(r==="ASCIIHexDecode")o=nP(n);else return null;if(o===null)return null;n=o}return n.toString("latin1")}function oP(e){let t=new Map,n=1,r=/begincodespacerange([\s\S]*?)endcodespacerange/.exec(e);if(r&&r[1]){let c=/<([0-9A-Fa-f]+)>/.exec(r[1]);c&&c[1]&&c[1].length>=4&&(n=2)}let o=c=>{let l="";for(let d=0;d+3<c.length+1;d+=4){let u=parseInt(c.slice(d,d+4),16);Number.isFinite(u)&&u>0&&(l+=String.fromCharCode(u))}return l},i=/beginbfchar([\s\S]*?)endbfchar/g,s;for(;(s=i.exec(e))!==null;){let c=/<([0-9A-Fa-f]+)>\s*<([0-9A-Fa-f]*)>/g,l;for(;(l=c.exec(s[1]??""))!==null;)t.set(parseInt(l[1]??"0",16),o(l[2]??""))}let a=/beginbfrange([\s\S]*?)endbfrange/g;for(;(s=a.exec(e))!==null;){let c=s[1]??"",l=/<([0-9A-Fa-f]+)>\s*<([0-9A-Fa-f]+)>\s*<([0-9A-Fa-f]*)>/g,d;for(;(d=l.exec(c))!==null;){let u=parseInt(d[1]??"0",16),p=parseInt(d[2]??"0",16),f=parseInt((d[3]??"0").slice(0,4)||"0",16);if(!(p-u>65535))for(let h=u;h<=p;h+=1)t.set(h,String.fromCharCode(f+(h-u)))}}return{map:t,bytes:n}}function iP(e){let t=new Map,n=[],r=[];for(let i of e.values()){for(let s of i.dict.matchAll(/\/Font\s*<<([\s\S]*?)>>/g))s[1]&&r.push(s[1]);for(let s of i.dict.matchAll(/\/Font\s+(\d+)\s+\d+\s+R/g)){let a=e.get(Number(s[1]));a&&r.push(a.dict)}}let o=new Set;for(let i of r){let s=/\/([^\s/<>[\]()]+)\s+(\d+)\s+\d+\s+R/g,a;for(;(a=s.exec(i))!==null;){let c=`${a[1]}#${a[2]}`;o.has(c)||(o.add(c),n.push([a[1]??"",Number(a[2])]))}}for(let[i,s]of n){let a=e.get(s);if(!a)continue;let c=null,l=/\/ToUnicode\s+(\d+)\s+\d+\s+R/.exec(a.dict);if(l){let u=e.get(Number(l[1])),p=u?Ts(u):null;p&&(c=oP(p))}if(c||(c=cP(a.dict,e)),!c)continue;let d=t.get(i)??[];d.push(c),t.set(i,d)}return t}function aP(e){let t=/^uni([0-9A-Fa-f]{4})$/.exec(e)??/^u([0-9A-Fa-f]{4,6})$/.exec(e);if(t)return String.fromCodePoint(parseInt(t[1],16));let n=/^afii(\d{5})$/.exec(e);if(n){let i=Number(n[1]);if(i>=57664&&i<=57690)return String.fromCharCode(1488+(i-57664))}let r=sP.indexOf(e.replace(/hebrew$/,""));return r>=0?String.fromCharCode(1488+r):e==="space"?" ":/^[A-Za-z]$/.test(e)?e:{zero:"0",one:"1",two:"2",three:"3",four:"4",five:"5",six:"6",seven:"7",eight:"8",nine:"9",period:".",comma:",",hyphen:"-",colon:":",slash:"/",at:"@"}[e]??null}function cP(e,t){let n=e,r=/\/Encoding\s+(\d+)\s+\d+\s+R/.exec(e);r&&(n=t.get(Number(r[1]))?.dict??"");let o=/\/Differences\s*\[([^\]]*)\]/.exec(n);if(!o?.[1])return null;let i=new Map,s=0;for(let a of o[1].match(/\d+|\/[^\s/[\]]+/g)??[]){if(/^\d+$/.test(a)){s=Number(a);continue}let c=aP(a.slice(1));c!==null&&i.set(s,c),s+=1}return i.size?{map:i,bytes:1}:null}function P_(e){let t=[];for(let n=0;n<e.length;n+=1){let r=e[n];if(r!=="\\"){t.push(r.charCodeAt(0)&255);continue}let o=e[n+1];if(o===void 0)break;if(o>="0"&&o<="7"){let s="",a=n+1;for(;a<e.length&&s.length<3&&e[a]>="0"&&e[a]<="7";)s+=e[a],a+=1;t.push(parseInt(s,8)&255),n=a-1;continue}let i={n:10,r:13,t:9,b:8,f:12};t.push(i[o]??o.charCodeAt(0)&255),n+=1}return t}function R_(e){if(e.length<2||e[0]!==254||e[1]!==255)return null;let t="";for(let n=2;n+1<e.length;n+=2)t+=String.fromCharCode((e[n]??0)<<8|(e[n+1]??0));return t}function x_(e,t){let n=R_(e);if(n!==null)return n;if(!t)return e.map(o=>String.fromCharCode(o)).join("");let r="";if(t.bytes===2)for(let o=0;o+1<e.length;o+=2){let i=(e[o]??0)<<8|(e[o+1]??0);r+=t.map.get(i)??""}else for(let o of e)r+=t.map.get(o)??String.fromCharCode(o);return r}function lP(e){let t=0;for(let n of e){let r=n.codePointAt(0)??0;/[\p{L}\p{N}]/u.test(n)?t+=1:(r<32||r>=127&&r<=191||r===65533)&&(t-=2)}return t}function $_(e,t){if(!t||t.length<=1)return x_(e,t?.[0]);let n="",r=-1/0;for(let o of t){let i=x_(e,o),s=lP(i);s>r&&(n=i,r=s)}return n}function dP(e,t){let n="",r="",o,i=!1,s="",a=0,c=null,l=()=>{r.trim()&&(n+=r+`
@@ -273,10 +273,10 @@ Restart Claude Code to load it. Remove with: npx @nodatachat/mcp uninstall-gate`
273
273
  `,{mode:384}))}function LE(e,t,n){let r=e,o={...t},i=[];for(let s of n){let a=(s.label??"").match(/ · invite:([0-9a-f]{16})$/)?.[1];if(!a||!(a in o)||s.revoked_at||!ou.test(s.subject_kid))continue;let c=nc[o[a]];if(delete o[a],sn(r,s.subject_kid)||s.partner_name&&sn(r,s.partner_name))continue;let l=s.partner_name||s.subject_kid;r=Gm(r,{partner:l,partner_kids:[s.subject_kid],send:c.send,receive:c.receive}),i.push(l)}return{cards:r,pending:o,added:i}}var FE=()=>Ye("commands-seen.json"),BM=5e3;function ME(){try{let e=FE();return e&&DM(e)?JSON.parse(LM(e,"utf8")).ids??[]:[]}catch{return[]}}function zm(e){if(!e.length)return;let t=[...ME(),...e].slice(-BM),n=FE();n&&(jM(MM(n),{recursive:!0}),FM(n,JSON.stringify({ids:t})+`
274
274
  `,{mode:384}))}async function cu(e){let t=su();if(!Object.keys(t).length)return[];let n=await e("GET","/api/v1/fabric-grants");if(!n.ok)return[];let r=n.data.grants??[],o=LE(er(),t,r);return o.added.length&&iu(o.cards),Object.keys(o.pending).length!==Object.keys(t).length&&au(o.pending),o.added}async function rc(e,t,n,r,o){if(!ru.test(n))return{ok:!1,error:`'${n}' is not a command name (e.g. delivery.status)`};sn(er(),t)||await cu(e);let i=sn(er(),t),s=Km[n];if(!i&&!(s&&ou.test(t.trim().toLowerCase())))return{ok:!1,error:`no command card for '${t}' in ~/.nodata/commands.json; without a card nothing is sent`};let a=i?.send.find(_=>_.cmd===n)??s;if(!a)return{ok:!1,error:`your card does not send '${n}' to ${i.partner}; it sends: ${i.send.map(_=>_.cmd).join(", ")||"(nothing)"}`};let c=OE(a,r),l=ou.test(t.trim().toLowerCase())?t.trim().toLowerCase():i?.partner_kids[0];if(!l)return{ok:!1,error:`no address for ${i?.partner??t} on the card`};let d={nodata:Hm,id:IM(),cmd:n,fields:c.fields,reply_to:o,issued_at:new Date().toISOString()},u=Um(d),p=await e("GET",`/api/v1/relay/resolve?handle=${encodeURIComponent(l)}`),f=String(p.data.recipient_pubkey??"");if(!/^[0-9a-f]{64}$/.test(f))return{ok:!1,error:`${i?.partner??l}'s address has no public key to seal to`};let h=await Co(f,new TextEncoder().encode(u)),g=await e("POST","/api/v1/relay/send",{recipient_kid:l,...h,route_taken:"capsule",content_type_hint:RE,purpose:"connect-command",ttl_hours:Math.min(Math.max(a.lifetime_h??24,1),720),max_fetches:Math.min(Math.max(a.opens??1,1),20),released_sha256:Wm(u)});return g.ok?{ok:!0,partner:i?.partner??l,commandId:d.id,receiptId:String(g.data.id??""),left:Object.keys(c.fields),masked:c.masked,dropped:c.dropped}:{ok:!1,error:`refused (HTTP ${g.status}): ${String(g.data.refused??g.data.error??"")}`}}async function Vm(e,t,n){try{let r=await e("POST","/api/v1/relay/inbox",{blob_id:t,action:"refuse",reason:n.split(":")[0]});return r.ok&&r.data.proven===!0}catch{return!1}}async function qm(e,t={skipSeen:!0}){let n=Fr();if(!n?.kid)return{ok:!1,error:"this computer has no address yet (nodata_inbox action address)"};let r=await e("GET","/api/v1/relay/inbox");if(!r.ok)return{ok:!1,error:`could not read the inbox (HTTP ${r.status})`};let o=new Set(t.skipSeen?ME():[]),i=(r.data.items??[]).filter(c=>(c.purpose==="connect-command"||c.purpose==="connect-test")&&!o.has(c.id));i.length&&await cu(e);let s=er(),a=[];for(let c of i.slice(0,50)){let l=c.sender?.org??c.sender_kid,d=await e("GET",`/api/v1/relay/fetch/${encodeURIComponent(c.id)}`);if(!d.ok){a.push({relayId:c.id,from:l,status:"unopened",reason:String(d.data.refused??d.data.error??d.status),committed:null,receipt:null});continue}let u=null;try{let _=await os(n.seed_hex,d.data),y=NE(JSON.parse(new TextDecoder().decode(_)));u=y.ok?y.envelope:null}catch{u=null}let p=IE(d.data.receipt);if(!u){a.push({relayId:c.id,from:l,status:"refused",reason:"not_a_command",committed:null,receipt:p,proven:await Vm(e,c.id,"not_a_command")});continue}let f=typeof d.data.released_sha256=="string"?Wm(Um(u))===d.data.released_sha256:null,h=sn(s,c.sender?.org??"")??sn(s,c.sender_kid),g=CE(h,u);if(f===!1){a.push({relayId:c.id,from:l,status:"refused",reason:"hash_mismatch",committed:f,receipt:p,proven:await Vm(e,c.id,"hash_mismatch")});continue}if(!g.ok){a.push({relayId:c.id,from:l,status:"refused",reason:g.reason,committed:f,receipt:p,proven:await Vm(e,c.id,g.reason)});continue}a.push({relayId:c.id,from:l,status:"accepted",command:u,spec:g.spec,committed:f,receipt:p})}return{ok:!0,items:a}}function HM(e,t,n){return new Promise(r=>{let o=CM(e,{shell:!0,stdio:["pipe","pipe","inherit"]}),i="",s=setTimeout(()=>o.kill("SIGTERM"),n);o.stdout.on("data",a=>{i.length<64*1024&&(i+=a.toString("utf8"))}),o.on("close",a=>{clearTimeout(s),r({code:a,stdout:i})}),o.on("error",()=>{clearTimeout(s),r({code:-1,stdout:i})}),o.stdin.end(JSON.stringify(t))})}async function UM(e,t,n){let r=await qm(e,{skipSeen:!0});if(!r.ok)return n(`inbox: ${r.error}`),0;let o=[];for(let i of r.items){if(i.status==="unopened"){n(`${i.relayId.slice(0,8)} from ${i.from}: not opened (${i.reason})`);continue}if(o.push(i.relayId),i.status==="refused"){n(`${i.relayId.slice(0,8)} from ${i.from}: refused (${i.reason})`);continue}let s=i.command;if(n(`${s.cmd} from ${i.from} (id ${s.id})${i.spec?.action?` \u2192 ${i.spec.action}`:""}`),!t)continue;let a={nodata:"nodata.partner-event/1",from:i.from,command:s,card_action:i.spec?.action??null,verified:{hash_committed:i.committed,receipt_valid:i.receipt}},c=await HM(t,a,6e4);if(c.code!==0){n(` handler exited ${c.code}; no reply sent`);continue}let l=DE(c.stdout);if(!l)continue;let d=await rc(e,i.from,l.cmd,l.fields,s.id);n(d.ok?` replied ${l.cmd} (id ${d.commandId})${d.dropped.length?`; held back by your card: ${d.dropped.join(", ")}`:""}`:` reply not sent: ${d.error}`)}return zm(o),r.items.length}async function BE(e,t){let n=a=>{let c=e.indexOf(a);return c>=0?e[c+1]:void 0},r=n("--handler")??null,o=Math.min(Math.max(Number(n("--every")??"5")||5,2),300),i=e.includes("--once"),s=a=>process.stdout.write(`[${new Date().toISOString()}] ${a}
275
275
  `);if(!Fr()?.kid)return s("this computer has no NoData address yet: run the MCP tool nodata_inbox (action address) or accept an invitation first"),1;s(`watching for partner commands every ${o}s${r?` \xB7 handler: ${r}`:" \xB7 no handler (log only)"}`);do{try{await UM(t,r,s)}catch(a){s(`pass failed: ${a instanceof Error?a.message:String(a)}`)}if(i)break;await new Promise(a=>setTimeout(a,o*1e3))}while(!0);return 0}async function HE(e,t){let n=f=>process.stdout.write(f+`
276
- `),r=f=>{let h=e.indexOf(f);return h>=0?e[h+1]:void 0},o=f=>e.reduce((h,g,_)=>g===f&&e[_+1]?[...h,e[_+1]]:h,[]),i=r("--category");if(!i)return n("Usage: nodata-mcp publish --category <procurement|logistics|legal|insurance|finance|\u2026>"),n(" [--capability <id> \u2026] [--template <ping|orders|delivery> \u2026]"),n(" [--summary <text>] [--kid <16-hex address>] --accept-terms"),n(""),n("Lists your agent in the NoData directory under your organisation's policy."),1;let s=o("--capability"),a=o("--template"),c=r("--summary"),l=e.includes("--accept-terms"),d=(r("--kid")??"").toLowerCase();if(!d){let f=await t("GET","/api/v1/fabric-grants");if(d=((f.ok&&Array.isArray(f.data.my_addresses)?f.data.my_addresses:[])[0]?.kid??"").toLowerCase(),!d)return n("No fabric address on this organisation yet \u2014 create one (nodata_inbox address) or pass --kid <16-hex>."),1}if(!l)return n("Publishing lists your agent in the NoData directory under your organisation's policy."),n("Re-run with --accept-terms to accept the Publisher Agreement and publish."),1;let u=await t("POST","/api/v1/directory",{action:"publish",category:i,capabilities:s,templates:a,summary:c,my_kid:d,terms_accepted:!0});if(!u.ok){let f=typeof u.data.error=="string"?u.data.error:`http_${u.status}`,h=Array.isArray(u.data.allowed)?` (allowed: ${u.data.allowed.join(", ")})`:"";return n(`Could not publish: ${f}${h}`),1}let p=u.data;return n(JSON.stringify({agent:{kid:p.address_kid,category:p.category},actions:p.capabilities??[]},null,2)),n(""),n("Published. Your agent appears in the directory within ~2 minutes:"),n(" https://www.nodatacapsule.com/agent-connect/directory"),0}var oc=Ce($r(),1);var Wt=Ce($r(),1);mt();import{existsSync as KM,mkdirSync as UE,readFileSync as WM,renameSync as GM,writeFileSync as VM}from"node:fs";import{basename as KE,dirname as WE,join as Ym}from"node:path";var GE=/^[0-9a-f]{16}$/,zM=/^[0-9a-f]{64}$/;function VE(e,t,n=new Date().toISOString()){let r;try{r=JSON.parse(new TextDecoder().decode(e))}catch{return null}let o=r.projection,i=(0,Wt.parseFolderPolicy)(r.policy);return r.nodata!=="folder-projection/1"||typeof r.exposure_nonce!="string"||!GE.test(r.exposure_nonce)||typeof r.exposure_token!="string"||!o||o.v!==1||typeof o.root!="string"||!zM.test(o.root)||!o.total||!i.ok?null:{nonce:r.exposure_nonce,sender_kid:t,received_at:n,exposure_token:r.exposure_token,folder:typeof r.folder=="string"?KE(r.folder).slice(0,120):"",policy:i.policy,projection:o}}var zE=e=>Ye(Ym("received-projections",`${e}.json`));function qE(e){let t=zE(e.nonce);return t?(UE(WE(t),{recursive:!0}),VM(t,JSON.stringify(e),{mode:384}),t):null}function YE(e){if(!GE.test(e))return null;let t=zE(e);try{return t&&KM(t)?JSON.parse(WM(t,"utf8")):null}catch{return null}}function JE(e){try{let t=JSON.parse(Buffer.from(e.split(".")[0],"base64url").toString("utf8")).org;return typeof t=="string"?t:null}catch{return null}}function lu(e,t,n){let r=e.filter(a=>!t||a.subject_kid===t),o=n?r.filter(a=>a.partner_org_id===n):[],s=(o.length?o:r).map(a=>{if(a.folder_in==null)return Wt.DEFAULT_FOLDER_POLICY;let c=(0,Wt.parseFolderPolicy)(a.folder_in);return c.ok?c.policy:Wt.DEFAULT_FOLDER_POLICY});return s.length?{policy:s.reduce((a,c)=>(0,Wt.effectiveFolderPolicy)(a,c)),known:o.length>0}:{policy:Wt.DEFAULT_FOLDER_POLICY,known:!1}}var Jm=["critical","high","medium","low","none"];function XE(e,t){let n=e.projection.total,r=(0,Wt.effectiveFolderPolicy)(e.policy,t),o=Jm.filter(a=>n.severity[a]).map(a=>`${n.severity[a]} ${a}`).join(", ")||"none",i=Object.entries(n.types).map(([a,c])=>`${c} ${a}`).join(", "),s=(0,Wt.projectionOutsidePolicy)(e.projection,r);return[`A PROJECTION of ${e.folder?`the folder "${e.folder}"`:"a folder"} from ${e.sender_kid}: ${n.files} file(s), ${(n.bytes/(1024*1024)).toFixed(1)} MB, largest ${(n.largest/(1024*1024)).toFixed(1)} MB.`,`Sensitivity of the originals: ${o}. Types: ${i||"none"}.`,...e.projection.folders.slice(0,8).map(a=>` ${a.name==="."?"(top)":a.name}: ${a.files} file(s)${Object.keys(a.categories).length?`, findings: ${Object.keys(a.categories).slice(0,4).join(", ")}`:""}`),`They let out: ${e.policy.view==="clean"?"clean copies":"ORIGINAL files"}. What passes is the stricter of theirs and yours.`,s.length?`DOES NOT FIT what you accept: ${s.join("; ")}. Those files would be refused at the door even if approved.`:"It fits what you accept.",`Root ${e.projection.root.slice(0,16)}\u2026 (what an approval names; files outside it, or changed since, are refused).`]}function ZE(e,t,n){let r=s=>Jm.length-1-Jm.indexOf(s);if(r(t)<=r(n.max_severity))return null;let o=Ym(WE(e),".held");UE(o,{recursive:!0});let i=Ym(o,KE(e));return GM(e,i),i}Oo();var D=(e,t=!1)=>({content:[{type:"text",text:e}],...t?{isError:t}:{}}),ay=()=>Ye("fabric-key.json"),uu=/^[0-9a-f]{16}$/;function ny(e){return!!e&&uu.test(e.trim().toLowerCase())}function nA(e){return ny(e)||!!e&&/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e.trim())}function Fr(){try{let e=ay();return e&&ps(e)?JSON.parse(iy(e,"utf8")):null}catch{return null}}function du(e){let t=ay();if(!t)throw new Error("this folder works for no organisation, so a Fabric address has no organisation to belong to (npx -y @nodatachat/mcp@latest orgs)");tA(JM(t),{recursive:!0}),sy(t,JSON.stringify(e,null,2)+`
276
+ `),r=f=>{let h=e.indexOf(f);return h>=0?e[h+1]:void 0},o=f=>e.reduce((h,g,_)=>g===f&&e[_+1]?[...h,e[_+1]]:h,[]),i=r("--category");if(!i)return n("Usage: nodata-mcp publish --category <procurement|logistics|legal|insurance|finance|\u2026>"),n(" [--capability <id> \u2026] [--template <ping|orders|delivery> \u2026]"),n(" [--summary <text>] [--kid <16-hex address>] --accept-terms"),n(""),n("Lists your agent in the NoData directory under your organisation's policy."),1;let s=o("--capability"),a=o("--template"),c=r("--summary"),l=e.includes("--accept-terms"),d=(r("--kid")??"").toLowerCase();if(!d){let f=await t("GET","/api/v1/fabric-grants");if(d=((f.ok&&Array.isArray(f.data.my_addresses)?f.data.my_addresses:[])[0]?.kid??"").toLowerCase(),!d)return n("No fabric address on this organisation yet \u2014 create one (nodata_inbox address) or pass --kid <16-hex>."),1}if(!l)return n("Publishing lists your agent in the NoData directory under your organisation's policy."),n("Re-run with --accept-terms to accept the Publisher Agreement and publish."),1;let u=await t("POST","/api/v1/directory",{action:"publish",category:i,capabilities:s,templates:a,summary:c,my_kid:d,terms_accepted:!0});if(!u.ok){let f=typeof u.data.error=="string"?u.data.error:`http_${u.status}`,h=Array.isArray(u.data.allowed)?` (allowed: ${u.data.allowed.join(", ")})`:"";return n(`Could not publish: ${f}${h}`),1}let p=u.data;return n(JSON.stringify({agent:{kid:p.address_kid,category:p.category},actions:p.capabilities??[]},null,2)),n(""),n("Published. Your agent appears in the directory within ~2 minutes:"),n(" https://www.nodatacapsule.com/agent-connect/directory"),0}var oc=Ce($r(),1);var Wt=Ce($r(),1);mt();import{existsSync as KM,mkdirSync as UE,readFileSync as WM,renameSync as GM,writeFileSync as VM}from"node:fs";import{basename as KE,dirname as WE,join as Ym}from"node:path";var GE=/^[0-9a-f]{16}$/,zM=/^[0-9a-f]{64}$/;function VE(e,t,n=new Date().toISOString()){let r;try{r=JSON.parse(new TextDecoder().decode(e))}catch{return null}let o=r.projection,i=(0,Wt.parseFolderPolicy)(r.policy);return r.nodata!=="folder-projection/1"||typeof r.exposure_nonce!="string"||!GE.test(r.exposure_nonce)||typeof r.exposure_token!="string"||!o||o.v!==1||typeof o.root!="string"||!zM.test(o.root)||!o.total||!i.ok?null:{nonce:r.exposure_nonce,sender_kid:t,received_at:n,exposure_token:r.exposure_token,folder:typeof r.folder=="string"?KE(r.folder).slice(0,120):"",policy:i.policy,projection:o}}var zE=e=>Ye(Ym("received-projections",`${e}.json`));function qE(e){let t=zE(e.nonce);return t?(UE(WE(t),{recursive:!0}),VM(t,JSON.stringify(e),{mode:384}),t):null}function YE(e){if(!GE.test(e))return null;let t=zE(e);try{return t&&KM(t)?JSON.parse(WM(t,"utf8")):null}catch{return null}}function JE(e){try{let t=JSON.parse(Buffer.from(e.split(".")[0],"base64url").toString("utf8")).org;return typeof t=="string"?t:null}catch{return null}}function lu(e,t,n){let r=e.filter(a=>!t||a.subject_kid===t),o=n?r.filter(a=>a.partner_org_id===n):[],s=(o.length?o:r).map(a=>{if(a.folder_in==null)return Wt.DEFAULT_FOLDER_POLICY;let c=(0,Wt.parseFolderPolicy)(a.folder_in);return c.ok?c.policy:Wt.DEFAULT_FOLDER_POLICY});return s.length?{policy:s.reduce((a,c)=>(0,Wt.effectiveFolderPolicy)(a,c)),known:o.length>0}:{policy:Wt.DEFAULT_FOLDER_POLICY,known:!1}}var Jm=["critical","high","medium","low","none"];function XE(e,t){let n=e.projection.total,r=(0,Wt.effectiveFolderPolicy)(e.policy,t),o=Jm.filter(a=>n.severity[a]).map(a=>`${n.severity[a]} ${a}`).join(", ")||"none",i=Object.entries(n.types).map(([a,c])=>`${c} ${a}`).join(", "),s=(0,Wt.projectionOutsidePolicy)(e.projection,r);return[`A PROJECTION of ${e.folder?`the folder "${e.folder}"`:"a folder"} from ${e.sender_kid}: ${n.files} file(s), ${(n.bytes/(1024*1024)).toFixed(1)} MB, largest ${(n.largest/(1024*1024)).toFixed(1)} MB.`,`Sensitivity of the originals: ${o}. Types: ${i||"none"}.`,...e.projection.folders.slice(0,8).map(a=>` ${a.name==="."?"(top)":a.name}: ${a.files} file(s)${Object.keys(a.categories).length?`, findings: ${Object.keys(a.categories).slice(0,4).join(", ")}`:""}`),`They let out: ${e.policy.view==="clean"?"clean copies":"ORIGINAL files"}. What passes is the stricter of theirs and yours.`,s.length?`DOES NOT FIT what you accept: ${s.join("; ")}. Those files would be refused at the door even if approved.`:"It fits what you accept.",`Root ${e.projection.root.slice(0,16)}\u2026 (what an approval names; files outside it, or changed since, are refused).`]}function ZE(e,t,n){let r=s=>Jm.length-1-Jm.indexOf(s);if(r(t)<=r(n.max_severity))return null;let o=Ym(WE(e),".held");UE(o,{recursive:!0});let i=Ym(o,KE(e));return GM(e,i),i}Oo();var D=(e,t=!1)=>({content:[{type:"text",text:e}],...t?{isError:t}:{}}),ay=()=>Ye("fabric-key.json"),uu=/^[0-9a-f]{16}$/;function ny(e){return!!e&&uu.test(e.trim().toLowerCase())}function nA(e){return ny(e)||!!e&&/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e.trim())}function Fr(){try{let e=ay();return e&&ps(e)?JSON.parse(iy(e,"utf8")):null}catch{return null}}function du(e){let t=ay();if(!t)throw new Error("this folder works for no organisation, so a Mycelium address has no organisation to belong to (npx -y @nodatachat/mcp@latest orgs)");tA(JM(t),{recursive:!0}),sy(t,JSON.stringify(e,null,2)+`
277
277
  `,{mode:384})}function Hr(e,t){let n=async(r,o,i)=>{let s=await t(),a=await fetch(`${e}${o}`,{method:r,headers:{authorization:`Bearer ${s}`,...i?{"content-type":"application/json"}:{}},body:i?JSON.stringify(i):void 0,signal:AbortSignal.timeout(3e4)}),c=await a.json().catch(()=>({})),l=c&&typeof c.data=="object"&&c.data!==null?c.data:c;return{ok:a.ok,status:a.status,data:{...l,...c.error?{error:c.error}:{}}}};return async(r,o,i)=>{let s=await n(r,o,i);return s.status!==412||o.startsWith("/api/v1/relay/enroll")||!/not enrolled/i.test(String(s.data.error??""))||"error"in await rA(n)?s:n(r,o,i)}}function ry(){let e=Qx();return{seed_hex:Qg(e),pub_hex:Qg(tm(e)),kid:null}}async function oy(e,t,n){return e("POST","/api/v1/relay/enroll",{ecdh_pubkey_hex:t.pub_hex,...n?{policy:n}:{}})}async function rA(e){let t=Fr()??ry(),n=await oy(e,t);if(n.status===409){let o=ay();o&&ps(o)&&sy(`${o}.prev-${t.kid??"unbound"}`,iy(o),{mode:384}),t=ry(),du(t),n=await oy(e,t)}if(!n.ok)return{error:`enroll failed (HTTP ${n.status}): ${String(n.data.error??"")}`};let r=String(n.data.ecdh_kid??"");return uu.test(r)?(du({...t,kid:r}),{kid:r}):{error:"enroll returned no address"}}async function Br(e,t){let n=Fr();if(n||(n=ry(),du(n)),n.kid&&!t)return{kid:n.kid};let r=await oy(e,n,t);if(r.status===409&&!t)return rA(e);if(!r.ok)return{error:`enroll failed (HTTP ${r.status}): ${String(r.data.error??"")}`};let o=String(r.data.ecdh_kid??"");return uu.test(o)?(n.kid!==o&&du({...n,kid:o}),{kid:o}):{error:"enroll returned no address"}}function XM(e){let t=e??{};return t.org?`${t.org} (verified)`:t.verified?"a verified organisation":"unverified"}function ZM(e){let t=e??{};return t.org?t.org:"the sender waiting for approval"}var QM=e=>e?`
278
278
  ${e}`:"";async function oA(e,t,n,r){let o=Hr(e,t),i=r.to.trim().toLowerCase(),s=ty(r.path);if(!ps(s)||!QE(s).isFile())return D(`Not a file: ${s}`,!0);let a=QE(s).size;if(a>Do)return D(`${Cn(s)} is ${(a/1024**3).toFixed(2)} GB; the road carries up to 4 GB per file.`,!0);let c=await Br(o);if("error"in c)return D(`Could not set up this computer's address: ${c.error}`,!0);let l=await o("GET",`/api/v1/relay/resolve?handle=${encodeURIComponent(i)}`),d=typeof l.data.recipient_pubkey=="string"?l.data.recipient_pubkey:null;if(!l.ok||!d)return D(i.includes("@")?`${i} has no computer on the NoData road yet. Send a governed link instead (nodata_send without "to"), or ask them for their NoData address.`:`No NoData address ${i} was found. Check the address with the recipient (they see it with nodata_inbox).`,!0);let u=null,p;if(a>ls){let k=Math.round(Math.min(Math.max(r.ttl_hours??72,1),168)*3600),b=Math.min(Math.max(r.max_fetches??1,1),20),S;try{S=await ds(e,await t(),s,{filename:Cn(s),contentType:Jd(s),ttlSeconds:k,maxOpens:b})}catch($){return D(`Could not reach NoData: ${$ instanceof Error?$.message:String($)}`,!0)}if("error"in S)return D(`NOT SENT: ${S.error}${S.status?` (HTTP ${S.status})`:""}${S.body?` ${JSON.stringify(S.body)}`:""}`,!0);u=S;let x={nodata:"large-file/1",link:`${e}/lock/open/${S.id}#${S.fragment}`,...r.share_filename?{name:Cn(s)}:{},size:S.size,sha256:S.sha256};p=new TextEncoder().encode(JSON.stringify(x))}else p=new Uint8Array(iy(s));let f=async()=>{u&&await fetch(`${e}/api/lock/public/burn/${u.id}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({burnSecretB64Url:u.burnSecretB64Url})}).catch(()=>null)},h=Array.isArray(l.data.devices)?l.data.devices:[],g=h.length>1?await o$(h,p):await Co(d,p),_=ey(s).toLowerCase(),y=u?u.sha256:Qm("sha256").update(p).digest("hex"),v=await o("POST","/api/v1/relay/send",{released_sha256:y,...Xx(s,y)?{released_view:"clean"}:{},recipient_kid:h.length>1?h[0].kid:typeof l.data.recipient_kid=="string"&&uu.test(l.data.recipient_kid)?l.data.recipient_kid:i,...g,filename_hint:u?cs:r.share_filename?Cn(s):_?`file${_}`:null,purpose:r.purpose?.slice(0,64)??null,ttl_hours:r.ttl_hours??72,max_fetches:Math.min(Math.max(r.max_fetches??1,1),20)});if((v.status===403||!v.ok)&&await f(),v.status===403){if(v.data.error==="org_stopped")return D(`NOT SENT \u2014 your organisation has stopped sending${v.data.reason?` (${String(v.data.reason)})`:""}. An organisation admin lifts it on the site.`,!0);let k=v.data.refused,b=String(k??v.data.error??"refused");return D(`NOT SENT \u2014 ${{blocked:"the recipient has blocked this sender",closed:"the recipient accepts only senders it listed",unattributed_sender:"the recipient accepts only senders with an organisation behind them \u2014 connect this computer (nodata_connect)",purpose_required:"the recipient requires a purpose \u2014 send again with `purpose`",connection_ended:"the connection with this recipient has ended \u2014 ask them for a new invitation (nodata_connect)",no_files:"between connected organisations only commands travel, never a file",commands_only:"between connected organisations only commands travel, never a file",command_too_large:"between connected organisations only a short command travels; this is too large"}[b]??b}.${k?" Nothing was stored; the refusal is on both sides' proof chains.":""}`,!0)}if(!v.ok)return D(`Send failed (HTTP ${v.status}): ${String(v.data.error??"")}`,!0);let m=v.data.delivery==="pending";return u&&W$(u.id,{id:u.id,file:s,filename:Cn(s),sha256:u.sha256,link:"(inside a road envelope; never shown here)",burn_secret_b64url:u.burnSecretB64Url,expires_at:u.expiresAt,max_opens:Math.min(Math.max(r.max_fetches??1,1),20),sent_by:n(),sent_at:new Date().toISOString(),receipt_id:u.receiptId,proof_url:u.proofUrl,road_to:i}),D([m?`SENT \u2014 WAITING FOR THE RECIPIENT'S APPROVAL. ${n()}`:`SENT over the NoData road. ${n()}`,` to ${i}${h.length>1?` (a person \u2014 ${h.length} computers, it opens on each)`:""}${r.purpose?` \xB7 purpose: ${r.purpose}`:""}`,` file ${Cn(s)} (${a>1048576?`${(a/1048576).toFixed(1)} MB`:`${(a/1024).toFixed(1)} KB`}, sha256 ${y.slice(0,16)}\u2026) \u2014 sealed here; NoData holds ciphertext only`,...u?[` large sealed in ${u.parts} part(s); the envelope carries its link, which only their computer can read. Opens counted on link ${u.id}${u.proofUrl?` (proof ${u.proofUrl})`:""}; revoke it any time: nodata_burn id: "${u.id}"`]:[],` from ${c.kid} (this computer)`,m?" status the recipient has not accepted this sender yet; it waits, unread, until they approve (or block, which shreds it)":" status delivered to their inbox; they open it on their computer",` proof recorded on your chain and on the recipient's before it moved (envelope ${String(v.data.id??"")})`,Id("org")].filter(Boolean).join(`
279
- `))}function iA(e,t,n,r,o=()=>!1){let i=Hr(t,n);e.registerTool("nodata_inbox",{description:"The NoData road (Fabric) on THIS computer: its address, what arrived, and who may reach it. action 'address' (default) \u2014 this computer's NoData address, to give to a partner so they can send to it (nodata_send to:<address>). 'list' \u2014 delivered files, and files WAITING for approval of their sender (who sent it, verified or not, and the declared purpose \u2014 never content). 'open' (id) \u2014 unseal a delivered file here and save it to a folder (default ./nodata-inbox); returns the path, size and hash, never the content. 'approve' / 'block' (id of a waiting file) \u2014 admit that sender from now on and deliver what it sent, or block it and shred what it sent unread. 'policy' \u2014 who may reach this address: mode open | verified (an unknown organisation waits for approval) | closed (only the allow list); allow / block lists; require_purpose; disclose (what senders learn about you: org_name | org_verified | address). 'ask' (to, path) \u2014 ask a partner's computer for a file in a folder it exposed to this one (nodata_expose there); the answer arrives here as a delivered file \u2014 by default a verified clean copy, not the original. 'serve' \u2014 answer the requests waiting here now (it also runs by itself every few minutes while this computer exposes a folder). 'propose_policy' (to, side, policy fields, note) \u2014 propose a policy for the connection folder to the partner, as a command: it changes nothing on their side until their person applies it. A partner's folder: 'open' on its PROJECTION shows a summary (how many files, how sensitive, which types; never a name) against what you accept; 'approve_folder' (id = the projection's id shown there) approves it \u2014 this computer's owner approves in the browser; 'folder_policy' (grant_id, max_severity, max_mb, types, view, approve_contents) sets what you accept from that connection (without grant_id it lists your connections and what each accepts). A file from a partner's folder is scanned on arrival and held aside (.held) if it is more sensitive than you accept. 'org_policy' (organisation admins) \u2014 the same settings as a FLOOR for every address in the organisation: a person's own plate can only be stricter (closed = only the organisation's list, which only the admin changes). Every decision is recorded on both sides' proof chains. ASKS FIRST for approve, block, policy and org_policy: without confirm:true it only says what would change.",inputSchema:{action:se.enum(["address","list","open","approve","block","policy","org_policy","ask","serve","approve_folder","folder_policy","propose_policy"]).optional(),side:se.enum(["accept","let_out"]).optional().describe("For 'propose_policy': propose what THEY accept ('accept') or what THEY let out ('let_out')."),note:se.string().optional().describe("For 'propose_policy': a short note for them (why)."),grant_id:se.string().optional().describe("For 'folder_policy': the connection (from 'folder_policy' without it)."),max_severity:se.enum(["none","low","medium","high","critical"]).optional().describe("For 'folder_policy': the most sensitive original you accept."),max_mb:se.number().optional().describe("For 'folder_policy': the largest file you accept, in MB."),types:se.array(se.string()).optional().describe("For 'folder_policy': the only file types you accept."),view:se.enum(["clean","original"]).optional().describe("For 'folder_policy': 'clean' accepts only clean copies."),approve_contents:se.boolean().optional().describe("For 'folder_policy': accept only files of a projection you approved."),days:se.number().optional().describe("For 'folder_policy' / 'approve_folder': how long it lasts."),id:se.string().optional().describe("The file (envelope) id, for open / approve / block."),to:se.string().optional().describe("For 'ask': the partner computer's NoData address."),path:se.string().optional().describe("For 'ask': the file inside the folder the partner exposed to you."),purpose:se.string().optional().describe("For 'ask': why you need it (the partner sees it)."),folder:se.string().optional().describe("Where 'open' saves the file. Default ./nodata-inbox"),mode:se.enum(["open","verified","closed"]).optional(),allow:se.array(se.string()).optional().describe("Addresses (16 hex) or organisation ids always admitted."),block:se.array(se.string()).optional().describe("Addresses or organisation ids always refused."),require_purpose:se.boolean().optional(),disclose:se.enum(["org_name","org_verified","address"]).optional(),confirm:se.boolean().optional()}},async s=>{let a=s.action??"address";if(a==="address"){let d=await Br(i);return"error"in d?D(`Could not set up this computer's address: ${d.error}`,!0):D([`This computer's NoData address: ${d.kid} ${r()}`,`Give it to a partner: they send with nodata_send (to: ${d.kid}) or the console, and it lands in nodata_inbox here.`,"The private key stays on this computer (~/.nodata/fabric-key.json); only its fingerprint is known to NoData."].join(`
279
+ `))}function iA(e,t,n,r,o=()=>!1){let i=Hr(t,n);e.registerTool("nodata_inbox",{description:"The NoData road (Mycelium) on THIS computer: its address, what arrived, and who may reach it. action 'address' (default) \u2014 this computer's NoData address, to give to a partner so they can send to it (nodata_send to:<address>). 'list' \u2014 delivered files, and files WAITING for approval of their sender (who sent it, verified or not, and the declared purpose \u2014 never content). 'open' (id) \u2014 unseal a delivered file here and save it to a folder (default ./nodata-inbox); returns the path, size and hash, never the content. 'approve' / 'block' (id of a waiting file) \u2014 admit that sender from now on and deliver what it sent, or block it and shred what it sent unread. 'policy' \u2014 who may reach this address: mode open | verified (an unknown organisation waits for approval) | closed (only the allow list); allow / block lists; require_purpose; disclose (what senders learn about you: org_name | org_verified | address). 'ask' (to, path) \u2014 ask a partner's computer for a file in a folder it exposed to this one (nodata_expose there); the answer arrives here as a delivered file \u2014 by default a verified clean copy, not the original. 'serve' \u2014 answer the requests waiting here now (it also runs by itself every few minutes while this computer exposes a folder). 'propose_policy' (to, side, policy fields, note) \u2014 propose a policy for the connection folder to the partner, as a command: it changes nothing on their side until their person applies it. A partner's folder: 'open' on its PROJECTION shows a summary (how many files, how sensitive, which types; never a name) against what you accept; 'approve_folder' (id = the projection's id shown there) approves it \u2014 this computer's owner approves in the browser; 'folder_policy' (grant_id, max_severity, max_mb, types, view, approve_contents) sets what you accept from that connection (without grant_id it lists your connections and what each accepts). A file from a partner's folder is scanned on arrival and held aside (.held) if it is more sensitive than you accept. 'org_policy' (organisation admins) \u2014 the same settings as a FLOOR for every address in the organisation: a person's own plate can only be stricter (closed = only the organisation's list, which only the admin changes). Every decision is recorded on both sides' proof chains. ASKS FIRST for approve, block, policy and org_policy: without confirm:true it only says what would change.",inputSchema:{action:se.enum(["address","list","open","approve","block","policy","org_policy","ask","serve","approve_folder","folder_policy","propose_policy"]).optional(),side:se.enum(["accept","let_out"]).optional().describe("For 'propose_policy': propose what THEY accept ('accept') or what THEY let out ('let_out')."),note:se.string().optional().describe("For 'propose_policy': a short note for them (why)."),grant_id:se.string().optional().describe("For 'folder_policy': the connection (from 'folder_policy' without it)."),max_severity:se.enum(["none","low","medium","high","critical"]).optional().describe("For 'folder_policy': the most sensitive original you accept."),max_mb:se.number().optional().describe("For 'folder_policy': the largest file you accept, in MB."),types:se.array(se.string()).optional().describe("For 'folder_policy': the only file types you accept."),view:se.enum(["clean","original"]).optional().describe("For 'folder_policy': 'clean' accepts only clean copies."),approve_contents:se.boolean().optional().describe("For 'folder_policy': accept only files of a projection you approved."),days:se.number().optional().describe("For 'folder_policy' / 'approve_folder': how long it lasts."),id:se.string().optional().describe("The file (envelope) id, for open / approve / block."),to:se.string().optional().describe("For 'ask': the partner computer's NoData address."),path:se.string().optional().describe("For 'ask': the file inside the folder the partner exposed to you."),purpose:se.string().optional().describe("For 'ask': why you need it (the partner sees it)."),folder:se.string().optional().describe("Where 'open' saves the file. Default ./nodata-inbox"),mode:se.enum(["open","verified","closed"]).optional(),allow:se.array(se.string()).optional().describe("Addresses (16 hex) or organisation ids always admitted."),block:se.array(se.string()).optional().describe("Addresses or organisation ids always refused."),require_purpose:se.boolean().optional(),disclose:se.enum(["org_name","org_verified","address"]).optional(),confirm:se.boolean().optional()}},async s=>{let a=s.action??"address";if(a==="address"){let d=await Br(i);return"error"in d?D(`Could not set up this computer's address: ${d.error}`,!0):D([`This computer's NoData address: ${d.kid} ${r()}`,`Give it to a partner: they send with nodata_send (to: ${d.kid}) or the console, and it lands in nodata_inbox here.`,"The private key stays on this computer (~/.nodata/fabric-key.json); only its fingerprint is known to NoData."].join(`
280
280
  `))}if(a==="list"){let d=await Br(i);if("error"in d)return D(d.error,!0);let u=await i("GET","/api/v1/relay/inbox");if(!u.ok)return D(`Inbox failed (HTTP ${u.status}): ${String(u.data.error??"")}`,!0);let p=u.data.items??[],f=u.data.pending??[],h=u.data.plate??{},g=_=>` ${String(_.id)} from ${XM(_.sender)} (${String(_.sender_kid)}) ${((Number(_.size_bytes)||0)/1024).toFixed(1)} KB${_.purpose?` \xB7 ${String(_.purpose)}`:""} \xB7 expires ${String(_.ttl_at).slice(0,16)}`;return D([`INBOX of ${d.kid} \u2014 plate: ${h.mode??"open"}${h.allow?.length?`, ${h.allow.length} allowed`:""}${h.block?.length?`, ${h.block.length} blocked`:""}. ${r()}`,p.length?`Delivered (${p.length}) \u2014 open with action:'open', id:`:"Delivered: none.",...p.map(g),f.length?`WAITING for your approval (${f.length}) \u2014 still sealed; approve or block the sender:`:"",...f.map(g),f.length?Id("org",ZM(f[0].sender)):""].filter(Boolean).join(`
281
281
  `))}if(a==="open"){if(!s.id)return D("Which one? Pass id (from action:'list').",!0);let d=Fr();if(!d)return D("This computer has no NoData address yet (action:'address').",!0);let u=await i("GET",`/api/v1/relay/fetch/${encodeURIComponent(s.id)}`);if(u.status===409&&u.data.pending)return D("That file is WAITING for your approval of its sender. Approve (action:'approve') or block it first.",!0);if(!u.ok)return D(`Could not open it (HTTP ${u.status}): ${String(u.data.error??"")}`,!0);let p=u.data;if(!p.ciphertext_b64||!p.wrapped_key_b64||!p.payload_iv_b64)return D("The envelope came back incomplete; nothing was saved.",!0);let f;try{f=await os(d.seed_hex,{ciphertext_b64:p.ciphertext_b64,wrapped_key_b64:p.wrapped_key_b64,payload_iv_b64:p.payload_iv_b64})}catch{return D("It does not open with this computer's key \u2014 it was sealed for another address.",!0)}if(p.filename_hint==="refused.json"){let m="refused";try{m=String(JSON.parse(new TextDecoder().decode(f)).refused??m).replace(/_/g," ")}catch{}return D(`The partner's computer REFUSED that request: ${m}. Nothing was sent to you.`)}if(p.purpose==="folder-projection"||p.filename_hint==="projection.json"){let m=VE(f,p.sender_kid??"?");if(!m)return D("That note is not a folder projection NoData can read; nothing was kept.",!0);if(!qE(m))return D("This folder works for no organisation, so there is nowhere to keep the projection.",!0);let k=await i("GET","/api/v1/fabric-grants"),b=lu(k.data.inbound??[],p.recipient_kid??null,JE(m.exposure_token));return D([...XE(m,b.policy),`What you accept${b.known?" from this partner":" (no connection policy found for this partner: the strictest of yours applies)"}: ${Kt(b.policy)}.`,`To approve it: nodata_inbox action:'approve_folder', id:'${m.nonce}' (this computer's owner approves in the browser). To change what you accept: action:'folder_policy'.`].join(`
282
282
  `))}let h=Qm("sha256").update(f).digest("hex");if(p.filename_hint!==cs&&p.released_sha256&&p.released_sha256!==h)return D("NOT SAVED \u2014 what opened is not what the sender committed to (sha256 differs). Ask them to send it again.",!0);let g=ty(s.folder??"nodata-inbox");if(tA(g,{recursive:!0}),p.filename_hint===cs){let m;try{if(m=JSON.parse(new TextDecoder().decode(f)),m.nodata!=="large-file/1"||typeof m.link!="string")throw new Error("not a large-file note")}catch{return D("The envelope's large-file note did not read; nothing was saved.",!0)}let k=Xm(g,m.name?Cn(m.name):`${s.id}.part`);if(ps(k))return D(`${k} already exists; nothing was overwritten. Pass another folder.`,!0);let b=await G$(m.link,k);if("error"in b)return D(`Could not open the large file: ${b.error}. Nothing was kept.`,!0);let S=p.released_sha256??m.sha256;if(b.sha256!==S)return YM(k,{force:!0}),D("NOT SAVED: what opened is not what the sender committed to (sha256 differs). Ask them to send it again.",!0);let x=k;if(!m.name){let $=Xm(g,`${s.id}${ey(b.name)||".bin"}`);ps($)||(qM(k,$),x=$)}return D([`OPENED on this computer: saved to ${x} (${(b.size/1048576).toFixed(1)} MB, sha256 ${b.sha256.slice(0,16)}\u2026, the same the sender committed to).`,`From ${p.sender_kid??"?"}. Unsealed here in parts, never whole in memory. The release is on both sides' proof chains, and the open on the sender's link receipt.`,"The content was not shown here. To work on it safely: nodata_scan_folder / nodata_redact on that file."].join(`
@@ -294,10 +294,10 @@ ${e}`:"";async function oA(e,t,n,r){let o=Hr(e,t),i=r.to.trim().toLowerCase(),s=
294
294
  `),gs=_u(dy(),".nodata","shares"),fu=_u(dy(),".nodata","org-master-pins.json");function fB(e,t,n){let r=Buffer.from(n.trim().replace(/-/g,"+").replace(/_/g,"/"),"base64"),o=`${t}/${ly("sha256").update(r).digest("hex")}`,i={};try{hu(fu)&&(i=JSON.parse(mu(fu,"utf8")))}catch{return{ok:!1,pinned:"unreadable",seen:o}}let s=i[e],a=s&&typeof s.tenant_id=="string"&&typeof s.key_sha256=="string"?`${s.tenant_id}/${s.key_sha256}`:null;if(s&&a!==o)return{ok:!1,pinned:a??"malformed",seen:o};if(!s){gu(_u(dy(),".nodata"),{recursive:!0});let[,c]=o.split("/");ic(fu,JSON.stringify({...i,[e]:{tenant_id:t,key_sha256:c}},null,2)+`
295
295
  `,{mode:384})}return{ok:!0}}var hB=12*1024*1024,gB={".pdf":"application/pdf",".txt":"text/plain",".csv":"text/csv",".md":"text/markdown",".json":"application/json",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation",".mp4":"video/mp4",".m4v":"video/mp4",".mov":"video/quicktime",".webm":"video/webm",".mkv":"video/x-matroska",".mp3":"audio/mpeg",".m4a":"audio/mp4",".wav":"audio/wav",".zip":"application/zip"};function sc(e){return _u(gs,`${e.replace(/[^a-f0-9]/gi,"")}.json`)}function dA(e){let t=sc(e);if(!hu(t))return null;try{return JSON.parse(mu(t,"utf8"))}catch{return null}}function uA(e){let t=e.match(/\/(?:lock|sovereign)\/open\/([a-f0-9]{8,64})/i);return(t?t[1]:e.trim()).toLowerCase()}async function yu(e,t,n){let r=await fetch(e,{method:"POST",headers:{"content-type":"application/json",...n?{authorization:`Bearer ${n}`}:{}},body:JSON.stringify(t)});return{status:r.status,body:await r.json().catch(()=>({}))}}function pA(e,t,n,r){e.registerTool("nodata_send",{description:"SEND a file as a governed link: encrypted on this machine (post-quantum hybrid), stored by NoData only as ciphertext \u2014 the key rides in the link's #fragment, which NoData never receives. Expires after ttl and after max_opens opens; can be burned (nodata_burn) at any time, after which nobody can open it again. Filed under your organization with a signed receipt and a proof_url. With `to` set to a NoData address (16 hex, from the recipient's nodata_inbox) or the email of a person on the road, it goes over the NoData road instead: sealed here to that computer's key (to every computer of that person), no link at all, subject to the recipient's plate (it may wait for their approval), proof on both sides' chains. With `code`, the link alone does not open: the recipient also types a code you send on a different channel (SMS, a call) \u2014 a forwarded link is useless without it, and the code never reaches NoData. ASKS FIRST: without confirm:true it only shows what would be sent. Returns the link to hand to the recipient (or the delivery status, with `to`).",inputSchema:{path:bt.string().describe("The file to send, up to 4 GB (a finished film too): above 3 MB it is sealed and uploaded in parts, never held whole."),to:bt.string().optional().describe("A NoData address (16 hex), or the email of a person on the road (it reaches all their computers), to send over the road instead of making a link."),purpose:bt.string().optional().describe("With `to`: the declared purpose the recipient sees (some plates require it)."),share_filename:bt.boolean().optional().describe("With `to`: send the file name too. Default only its type travels."),ttl_hours:bt.number().optional().describe("Hours until the link expires. Default 72, max 168 (7 days)."),max_opens:bt.number().optional().describe("Opens allowed before it burns itself. Default 3, max 1000."),view_only:bt.boolean().optional().describe("The recipient views it in the browser and gets no download (the same view-only dial as Seal Studio). Default: download allowed."),code:bt.union([bt.boolean(),bt.string()]).optional().describe("Protect the link with a code sent separately: true = generate one (XXXX-XXXX), or give your own (8+ letters/digits). Not with `to`."),confirm:bt.boolean().optional().describe("true = send it. Omit to preview.")}},async o=>{let{path:i,ttl_hours:s,max_opens:a,confirm:c}=o,l=dB(i);if(!hu(l)||!lA(l).isFile())return ue(`Not a file: ${l}`,!0);let d=lA(l).size;if(d<=hB&&uB(mu(l,"utf8")))return ue(pB,!0);if(o.to!==void 0)return o.code!==void 0&&o.code!==!1?ue("`code` protects a link; over the road the file is sealed to the recipient's own computers, so there is no link to protect. Drop `code`, or drop `to` to make a coded link.",!0):nA(o.to)?c?oA(t,n,r,{to:o.to,path:l,purpose:o.purpose,share_filename:o.share_filename,ttl_hours:s,max_fetches:a}):ue([`PREVIEW \u2014 nothing sent yet. ${r()}`,`Would send "${pu(l)}" (${(d/1024).toFixed(1)} KB) over the NoData road to ${o.to.trim().toLowerCase()}:`," \xB7 sealed here to that computer's key; NoData stores ciphertext only, and there is no link to leak",` \xB7 ${o.share_filename?"the file name travels":"only the file type travels, not its name"}${o.purpose?`; declared purpose: ${o.purpose}`:""}`," \xB7 the recipient's plate decides: delivered, waiting for their approval, or refused",` \xB7 expires in ${Math.round(Math.min(Math.max(s??72,1),720))} h; can be opened ${Math.min(Math.max(a??1,1),20)} time(s)`,"","Ask the user to approve, then call nodata_send again with the same arguments and confirm: true."].join(`
296
296
  `)):ue(`"${o.to}" is not a NoData address (16 hex characters) or an email. Omit \`to\` to make a governed link instead.`,!0);if(d>Do)return ue(`${pu(l)} is ${(d/1024**3).toFixed(2)} GB; a governed link carries up to 4 GB.`,!0);let u=d>ls,p=Math.round(Math.min(Math.max(s??72,1/60),168)*3600),f=Math.round(Math.min(Math.max(a??3,1),1e3)),h=o.code!==void 0&&o.code!==!1;if(typeof o.code=="string"&&(0,$t.normalizeLinkCode)(o.code).length<8)return ue("A code needs at least 8 letters or digits (spaces and dashes do not count). Or pass code: true to generate one.",!0);if(!c)return ue([`PREVIEW \u2014 nothing sent yet. ${r()}`,`Would send "${pu(l)}" (${d>1048576?`${(d/1048576).toFixed(1)} MB`:`${(d/1024).toFixed(1)} KB`}) as a governed link:`," \xB7 encrypted here; NoData stores only ciphertext and never gets the key",...u?[" \xB7 large file: sealed and uploaded in ~32 MB parts (never held whole); the recipient plays or saves it as it streams"]:[],` \xB7 expires in ${(p/3600).toFixed(p<3600?2:0)} h, or after ${f} open(s)`," \xB7 revocable any time with nodata_burn; every open is counted",...o.view_only?[" \xB7 view only: the recipient sees it in the browser, with no download"]:[],...h?[` \xB7 opens only with a code${typeof o.code=="string"?" you chose":" generated here"}, which you send on a different channel than the link`]:[],"","Ask the user to approve, then call nodata_send again with the same arguments and confirm: true."].join(`
297
- `));let g=pu(l),_=gB[lB(l).toLowerCase()]??"application/octet-stream";if(u){let N=typeof o.code=="string"?o.code.trim():h?(0,$t.newLinkCode)():null,E;try{E=await ds(t,await n(),l,{filename:g,contentType:_,ttlSeconds:p,maxOpens:f,viewOnly:o.view_only,code:N})}catch(M){return ue(`Could not reach NoData: ${M instanceof Error?M.message:String(M)}`,!0)}if("error"in E){if(E.body?.reason==="org_key_only")return ue(['NOT SENT: your organisation seals every outside link to its own 12 words ("org key only"), and a large file goes from here only as a streamed /lock link, which is not one.',`To a NoData computer (a partner, a vendor's server): nodata_send with to: "<its 16-character address>". It is delivered over the Fabric, a receipt on both sides.`].join(`
297
+ `));let g=pu(l),_=gB[lB(l).toLowerCase()]??"application/octet-stream";if(u){let N=typeof o.code=="string"?o.code.trim():h?(0,$t.newLinkCode)():null,E;try{E=await ds(t,await n(),l,{filename:g,contentType:_,ttlSeconds:p,maxOpens:f,viewOnly:o.view_only,code:N})}catch(M){return ue(`Could not reach NoData: ${M instanceof Error?M.message:String(M)}`,!0)}if("error"in E){if(E.body?.reason==="org_key_only")return ue(['NOT SENT: your organisation seals every outside link to its own 12 words ("org key only"), and a large file goes from here only as a streamed /lock link, which is not one.',`To a NoData computer (a partner, a vendor's server): nodata_send with to: "<its 16-character address>". It is delivered over the Mycelium, a receipt on both sides.`].join(`
298
298
  `),!0);let M=$i(E.body,t);return ue(M?`NOT SENT. ${M}`:`Send refused${E.status?` (HTTP ${E.status})`:""}: ${E.error}${E.body?` ${JSON.stringify(E.body)}`:""}`,!0)}let O={id:E.id,file:l,filename:g,sha256:E.sha256,link:`${t}/lock/open/${E.id}#${E.fragment}`,burn_secret_b64url:E.burnSecretB64Url,...N?{coded:!0}:{},expires_at:E.expiresAt,max_opens:f,sent_by:r(),sent_at:new Date().toISOString(),receipt_id:E.receiptId,proof_url:E.proofUrl};return gu(gs,{recursive:!0}),ic(sc(E.id),JSON.stringify(O,null,2)+`
299
299
  `,{mode:384}),ue([`SENT "${g}" (${(E.size/1048576).toFixed(1)} MB in ${E.parts} part(s)): ${r()}`,...N?["Link (give this to the recipient):",` ${O.link}`,`Code: ${N}`," Send the code on a DIFFERENT channel than the link (SMS or a call if the link goes by email). The link alone does not open; NoData never receives the code and cannot recover it."]:["Link (give this to the recipient; anyone holding it can open it until it expires or is burned):",` ${O.link}`],`Expires ${O.expires_at} \xB7 max ${f} open(s)${o.view_only?" \xB7 view only (no download)":""} \xB7 id ${E.id}`,O.proof_url?`Proof (signed receipt, public): ${O.proof_url}`:"No receipt: the link was not attributed to an organization.",`Check it: nodata_link_status id: "${E.id}" \xB7 Revoke it: nodata_burn id: "${E.id}"`].join(`
300
- `))}let y=mu(l),v=await(0,$t.sealBytesForPublicShare)({bytes:y,filename:g,contentType:_,ttlSeconds:p,server:t}),m=typeof o.code=="string"?o.code.trim():h?(0,$t.newLinkCode)():null,k=m?await(0,$t.wrapFragmentForCodedLink)(v.fragment,m).then(N=>({fragment:N.linkFragment,wrappedFragmentB64:N.wrappedFragmentB64,wrapIvB64:N.wrapIvB64})):await(0,$t.wrapFragmentForShortLink)(v.fragment).then(N=>({fragment:N.wrapKeyB64Url,wrappedFragmentB64:N.wrappedFragmentB64,wrapIvB64:N.wrapIvB64})),b=await(0,$t.newBurnSecret)(),S;try{S=await yu(`${t}/api/lock/public/issue`,{formatVersion:3,lockboxId:v.lockboxId,containerB64:Buffer.from(v.container).toString("base64"),filename:g,contentType:_,ttlSeconds:p,maxOpens:f,...o.view_only?{allowDownload:!1}:{},wrappedFragmentB64:k.wrappedFragmentB64,wrapIvB64:k.wrapIvB64,burnHashHex:b.burnHashHex},await n())}catch(N){return ue(`Could not reach NoData: ${N instanceof Error?N.message:String(N)}`,!0)}if(S.body.reason==="org_key_only"){let N=typeof S.body.tenant_id=="string"?S.body.tenant_id:"",E=typeof S.body.org_master_pubkey=="string"?S.body.org_master_pubkey:"";return N&&E&&y.length<=hs?mB({baseUrl:t,token:n,who:r,abs:l,bytes:y,filename:g,contentType:_,ttlSeconds:p,opens:f,viewOnly:o.view_only===!0,code:m,tenantId:N,masterPub:E}):ue([`NOT SENT: your organisation seals every outside link to its own 12 words ("org key only")${y.length>hs?`, and such a link from here carries up to ${hs/1048576} MB`:""}.`,`To a NoData computer (a partner, a vendor's server): nodata_send with to: "<its 16-character address>". It is delivered over the Fabric, a receipt on both sides.`,`To a person outside NoData: seal it in the browser at ${t}/seal-studio, a sovereign seal with a code, recoverable by your 12 words.`].join(`
300
+ `))}let y=mu(l),v=await(0,$t.sealBytesForPublicShare)({bytes:y,filename:g,contentType:_,ttlSeconds:p,server:t}),m=typeof o.code=="string"?o.code.trim():h?(0,$t.newLinkCode)():null,k=m?await(0,$t.wrapFragmentForCodedLink)(v.fragment,m).then(N=>({fragment:N.linkFragment,wrappedFragmentB64:N.wrappedFragmentB64,wrapIvB64:N.wrapIvB64})):await(0,$t.wrapFragmentForShortLink)(v.fragment).then(N=>({fragment:N.wrapKeyB64Url,wrappedFragmentB64:N.wrappedFragmentB64,wrapIvB64:N.wrapIvB64})),b=await(0,$t.newBurnSecret)(),S;try{S=await yu(`${t}/api/lock/public/issue`,{formatVersion:3,lockboxId:v.lockboxId,containerB64:Buffer.from(v.container).toString("base64"),filename:g,contentType:_,ttlSeconds:p,maxOpens:f,...o.view_only?{allowDownload:!1}:{},wrappedFragmentB64:k.wrappedFragmentB64,wrapIvB64:k.wrapIvB64,burnHashHex:b.burnHashHex},await n())}catch(N){return ue(`Could not reach NoData: ${N instanceof Error?N.message:String(N)}`,!0)}if(S.body.reason==="org_key_only"){let N=typeof S.body.tenant_id=="string"?S.body.tenant_id:"",E=typeof S.body.org_master_pubkey=="string"?S.body.org_master_pubkey:"";return N&&E&&y.length<=hs?mB({baseUrl:t,token:n,who:r,abs:l,bytes:y,filename:g,contentType:_,ttlSeconds:p,opens:f,viewOnly:o.view_only===!0,code:m,tenantId:N,masterPub:E}):ue([`NOT SENT: your organisation seals every outside link to its own 12 words ("org key only")${y.length>hs?`, and such a link from here carries up to ${hs/1048576} MB`:""}.`,`To a NoData computer (a partner, a vendor's server): nodata_send with to: "<its 16-character address>". It is delivered over the Mycelium, a receipt on both sides.`,`To a person outside NoData: seal it in the browser at ${t}/seal-studio, a sovereign seal with a code, recoverable by your 12 words.`].join(`
301
301
  `),!0)}if(S.status>=400||S.body.ok!==!0){let N=$i(S.body,t);return ue(N?`NOT SENT. ${N}`:`Send refused (HTTP ${S.status}): ${JSON.stringify(S.body)}`,!0)}let x=String(S.body.id),$={id:x,file:l,filename:g,sha256:ly("sha256").update(y).digest("hex"),link:`${t}/lock/open/${x}#${k.fragment}`,burn_secret_b64url:b.burnSecretB64Url,...m?{coded:!0}:{},expires_at:String(S.body.expiresAt??""),max_opens:f,sent_by:r(),sent_at:new Date().toISOString(),receipt_id:S.body.receipt_id??null,proof_url:S.body.proof_url??null};return gu(gs,{recursive:!0}),ic(sc(x),JSON.stringify($,null,2)+`
302
302
  `,{mode:384}),ue([`SENT "${g}" \u2014 ${r()}`,...m?["Link (give this to the recipient):",` ${$.link}`,`Code: ${m}`," Send the code on a DIFFERENT channel than the link (SMS or a call if the link goes by email). The link alone does not open; NoData never receives the code and cannot recover it."]:["Link (give this to the recipient; anyone holding it can open it until it expires or is burned):",` ${$.link}`],`Expires ${$.expires_at} \xB7 max ${f} open(s)${o.view_only?" \xB7 view only (no download)":""} \xB7 id ${x}`,$.proof_url?`Proof (signed receipt, public): ${$.proof_url}`:"No receipt: the link was not attributed to an organization.",`Check it: nodata_link_status id: "${x}" \xB7 Revoke it: nodata_burn id: "${x}"`].join(`
303
303
  `))}),e.registerTool("nodata_link_status",{description:"STATUS of links sent with nodata_send: opens so far, expiry, whether burned. Pass an id or link for one; omit to list every link sent from this machine.",inputSchema:{id:bt.string().optional().describe("Share id or /lock/open link. Omit to list all.")}},async({id:o})=>{let i=o?[uA(o)]:hu(gs)?cB(gs).filter(a=>a.endsWith(".json")).map(a=>a.slice(0,-5)):[];if(i.length===0)return ue("No links sent from this machine yet. Send one with nodata_send.");let s=[`LINKS \u2014 ${r()}`];for(let a of i.slice(0,50)){let c=dA(a);if(c?.sovereign){let u=c.revoked_at?"BURNED":c.expires_at&&Date.parse(c.expires_at)<Date.now()?"expired":"sealed";s.push(` ${a} ${u.padEnd(10)} opens ?/${c.max_opens} expires ${c.expires_at||"?"} ${c.filename} (org key only: opens are counted in your organisation's console)`);continue}let l={};try{let u=await fetch(`${t}/api/lock/public/status/${a}`);l=await u.json().catch(()=>({})),u.status===404&&(l={missing:!0})}catch{l={unreachable:!0}}let d=l.missing?"not found":l.unreachable?"status unavailable":l.burned?"BURNED":l.expired?"expired":"live";s.push(` ${a} ${d.padEnd(10)} opens ${l.openCount??"?"}/${l.maxOpens??c?.max_opens??"?"} expires ${l.expiresAt??c?.expires_at??"?"} ${c?c.filename:""}`)}return ue(s.join(`
@@ -309,7 +309,7 @@ Ask the user to approve, then call nodata_burn again with confirm: true.`);let d
309
309
  `))}async function yB(e,t,n,r,o){if(!o)return ue(`PREVIEW: nothing burned yet. ${n()}
310
310
  Would burn "${r.filename}" (${r.id}). After this NOBODY can open it again; it cannot be undone.
311
311
  Ask the user to approve, then call nodata_burn again with confirm: true.`);let i;try{i=await yu(`${e}/api/tenant/${encodeURIComponent(r.sovereign.tenant_id)}/sovereign-seal/${encodeURIComponent(r.id)}/revoke`,{},await t())}catch(s){return ue(`Could not reach NoData: ${s instanceof Error?s.message:String(s)}`,!0)}return i.status>=400||i.body.ok!==!0?ue(`Burn refused (HTTP ${i.status}): ${JSON.stringify(i.body)}`,!0):(ic(sc(r.id),JSON.stringify({...r,revoked_at:new Date().toISOString()},null,2)+`
312
- `,{mode:384}),ue(`BURNED ${r.id} (${n()}). NoData's share is destroyed; the link is dead for everyone. The revoke is on your organisation's proof chain.`))}import{z as fe}from"zod";import{existsSync as kB,mkdirSync as SB}from"node:fs";import{homedir as xB}from"node:os";import{join as $B}from"node:path";mt();var ms={controller:{role:"controller",he:"\u05D1\u05E2\u05DC \u05D4\u05E9\u05DC\u05D9\u05D8\u05D4",en:"Controller",heLine:"\u05D4\u05DE\u05D9\u05D3\u05E2 \u05D5\u05D4\u05DE\u05E4\u05EA\u05D7 \u05E9\u05DC\u05D5; \u05D4\u05DC\u05D5\u05D7 \u05E9\u05DC\u05D5 \u05DE\u05D7\u05DC\u05D9\u05D8 \u05E2\u05DC \u05DB\u05DC \u05E9\u05D7\u05E8\u05D5\u05E8",enLine:"the information and its key are theirs; their board decides every release",color:"#0f8a7e",colorDark:"#4fd1c0",tint:"#e2f4f1",tintDark:"#173633"},approver:{role:"approver",he:"\u05DE\u05D0\u05E9\u05E8",en:"Approver",heLine:"\u05D0\u05D3\u05DD \u05DE\u05D8\u05E2\u05DD \u05D1\u05E2\u05DC \u05D4\u05E9\u05DC\u05D9\u05D8\u05D4 \u05E9\u05D7\u05D5\u05EA\u05DD \u05E2\u05DC \u05E9\u05D7\u05E8\u05D5\u05E8",enLine:"a person of the controller who signs a release",color:"#0a5f57",colorDark:"#8fe3d7",tint:"#d3ece8",tintDark:"#10302c"},processor:{role:"processor",he:"\u05DE\u05D7\u05D6\u05D9\u05E7",en:"Processor",heLine:"\u05DE\u05E9\u05EA\u05DE\u05E9 \u05D1\u05DE\u05D9\u05D3\u05E2 \u05D1\u05E9\u05DD \u05D1\u05E2\u05DC \u05D4\u05E9\u05DC\u05D9\u05D8\u05D4",enLine:"uses the information on the controller's behalf",color:"#4b4bd6",colorDark:"#9b9bff",tint:"#e9e9fb",tintDark:"#25264a"},recipient:{role:"recipient",he:"\u05E0\u05DE\u05E2\u05DF",en:"Recipient",heLine:"\u05DE\u05E7\u05D1\u05DC \u05D0\u05EA \u05D4\u05EA\u05D5\u05E6\u05E8: \u05E7\u05D9\u05E9\u05D5\u05E8, \u05E7\u05D5\u05D1\u05E5 \u05D0\u05D5 \u05D4\u05D5\u05D3\u05E2\u05D4",enLine:"receives the outcome: a link, a file or a message",color:"#b4570b",colorDark:"#f0a35c",tint:"#fdf0e3",tintDark:"#3a2a18"},gate:{role:"gate",he:"\u05E9\u05E2\u05E8 \u05D4\u05D2\u05D9\u05E9\u05D4",en:"Access gate",heLine:"NoData: \u05DE\u05D7\u05DC\u05D9\u05D8 \u05D5\u05E8\u05D5\u05E9\u05DD, \u05D0\u05E3 \u05E4\u05E2\u05DD \u05DC\u05D0 \u05DE\u05D7\u05D6\u05D9\u05E7 \u05DE\u05E4\u05EA\u05D7 \u05E9\u05DC\u05DD",enLine:"NoData: decides and records, never holds a whole key",color:"#3b4054",colorDark:"#c7cbe0",tint:"#eceef4",tintDark:"#262a3a"}};function uy(e,t,n="en"){let r=ms[e];return`${n==="he"?r.he:r.en}: ${t}`}var et=(e,t=!1)=>({content:[{type:"text",text:e}],...t?{isError:t}:{}}),bu="npx -y @nodatachat/mcp@latest",_B=e=>e?e.slice(0,10):"never",fA=(e,t)=>e==="allow"?t?`yes \u2264${t}/day`:"yes":e==="approve"?"with approval":"no";function bB(e){let t=new Map((e.keys??[]).map(s=>[`${s.project}\0${s.name}`,s])),n=new Map;for(let s of e.accounts)n.set(s.project,[...n.get(s.project)??[],s]);if(!n.size)return["No client account yet. Add one: account_op 'add' (project, platform, account) gives the line the user runs in their own terminal."];let r=[],o=new Map;for(let[s,a]of n){let c=a[0].client,l=a.flatMap(p=>p.keys.map(f=>t.get(`${s}\0${f}`))).filter(p=>!!p);r.push(`\u25A0 ${c}${c!==s?` (project ${s})`:""} \xB7 today ${l.reduce((p,f)=>p+f.today.used,0)} used, ${l.reduce((p,f)=>p+f.today.refused,0)} refused`);let d=e.agents?.find(p=>p.project===s);r.push(d?` agent: ${d.name}${d.role?` (${d.role})`:""} \xB7 only from ${d.device} \xB7 uses ${d.keys.join(", ")||"no key"} \xB7 every other agent is refused this client's keys`:" agent: none yet (account_op 'agent' gives the client its own agent)");let u=new Map;for(let p of a)u.set(p.platform.id,[...u.get(p.platform.id)??[],p]);for(let[p,f]of u){if(p!=="other"){let h=o.get(p)??{name:f[0].platform.name,accounts:0,clients:new Set};h.accounts+=f.length,h.clients.add(s),o.set(p,h)}for(let h of f){let _=h.keys.map(b=>t.get(`${s}\0${b}`)).filter(b=>!!b),y=_.length>0&&_.every(b=>b.off),v=_.some(b=>b.login&&!b.off&&b.who.agent!=="deny"),m=_[0]?`person ${fA(y?"deny":_[0].who.person)} \xB7 agent ${fA(y?"deny":_[0].who.agent,_[0].who.agentCap)}${_[0].who.partners?` \xB7 ${_[0].who.partners} partner(s)`:""}`:"",k=_.map(b=>b.rotatedAt).filter(b=>!!b).sort().at(-1)??h.updatedAt;r.push(` ${p==="other"?"Other keys":h.platform.name}${h.account?` \xB7 ${h.account}`:""} \xB7 ${h.keys.length} key(s): ${h.keys.join(", ")} \xB7 rotated ${_B(k)}${m?` \xB7 ${m}`:""}${y?" \xB7 SWITCHED OFF":""}${v?" \xB7 \u26A0 an agent can use this login (account_op person_only)":""}`)}}}let i=[...o.values()].sort((s,a)=>a.accounts-s.accounts).map(s=>`${s.name} ${s.accounts} (${s.clients.size} client${s.clients.size===1?"":"s"})`);return[`${n.size} client(s) \xB7 ${i.join(" \xB7 ")}`,"",...r,"","Names only: no value is shown. The same board is in the NoData console (Fabric \u2192 Data sources)."]}function wB(e,t){if(t.keys?.length)return t.keys.map(o=>o.trim()).filter(Boolean);if(!t.project)return{error:"name the client's `project` (and `platform`, optionally `account`), or the `keys`."};let r=e.accounts.filter(o=>o.project===t.project&&(!t.platform||o.platform.id===t.platform||o.platform.name.toLowerCase()===t.platform.toLowerCase())&&(!t.account||(o.account??"").toLowerCase()===t.account.trim().toUpperCase().replace(/[^A-Z0-9]+/g,"_").toLowerCase())).flatMap(o=>o.keys);return r.length?r:{error:`no keys of ${t.project}${t.platform?` on ${t.platform}`:""}${t.account?` (${t.account})`:""} in the vault. List with account_op 'list'.`}}async function hA(e,t){let n=t.account_op??"list",r=n==="add"&&t.platform?`&add=${encodeURIComponent(`${t.platform}:${t.account??""}:${t.use??"agent"}`)}`:"",o=await e("GET",`/api/v1/fabric-grants?accounts=1${r}`);if(!o.ok)return et(`could not read the accounts board (HTTP ${o.status}): ${String(o.data.error??"")}`,!0);let i=o.data.identities??{accounts:[]};if(!Array.isArray(i.accounts))return et("this NoData server does not show the accounts board yet.",!0);if(n==="list")return et(bB(i).join(`
312
+ `,{mode:384}),ue(`BURNED ${r.id} (${n()}). NoData's share is destroyed; the link is dead for everyone. The revoke is on your organisation's proof chain.`))}import{z as fe}from"zod";import{existsSync as kB,mkdirSync as SB}from"node:fs";import{homedir as xB}from"node:os";import{join as $B}from"node:path";mt();var ms={controller:{role:"controller",he:"\u05D1\u05E2\u05DC \u05D4\u05E9\u05DC\u05D9\u05D8\u05D4",en:"Controller",heLine:"\u05D4\u05DE\u05D9\u05D3\u05E2 \u05D5\u05D4\u05DE\u05E4\u05EA\u05D7 \u05E9\u05DC\u05D5; \u05D4\u05DC\u05D5\u05D7 \u05E9\u05DC\u05D5 \u05DE\u05D7\u05DC\u05D9\u05D8 \u05E2\u05DC \u05DB\u05DC \u05E9\u05D7\u05E8\u05D5\u05E8",enLine:"the information and its key are theirs; their board decides every release",color:"#0f8a7e",colorDark:"#4fd1c0",tint:"#e2f4f1",tintDark:"#173633"},approver:{role:"approver",he:"\u05DE\u05D0\u05E9\u05E8",en:"Approver",heLine:"\u05D0\u05D3\u05DD \u05DE\u05D8\u05E2\u05DD \u05D1\u05E2\u05DC \u05D4\u05E9\u05DC\u05D9\u05D8\u05D4 \u05E9\u05D7\u05D5\u05EA\u05DD \u05E2\u05DC \u05E9\u05D7\u05E8\u05D5\u05E8",enLine:"a person of the controller who signs a release",color:"#0a5f57",colorDark:"#8fe3d7",tint:"#d3ece8",tintDark:"#10302c"},processor:{role:"processor",he:"\u05DE\u05D7\u05D6\u05D9\u05E7",en:"Processor",heLine:"\u05DE\u05E9\u05EA\u05DE\u05E9 \u05D1\u05DE\u05D9\u05D3\u05E2 \u05D1\u05E9\u05DD \u05D1\u05E2\u05DC \u05D4\u05E9\u05DC\u05D9\u05D8\u05D4",enLine:"uses the information on the controller's behalf",color:"#4b4bd6",colorDark:"#9b9bff",tint:"#e9e9fb",tintDark:"#25264a"},recipient:{role:"recipient",he:"\u05E0\u05DE\u05E2\u05DF",en:"Recipient",heLine:"\u05DE\u05E7\u05D1\u05DC \u05D0\u05EA \u05D4\u05EA\u05D5\u05E6\u05E8: \u05E7\u05D9\u05E9\u05D5\u05E8, \u05E7\u05D5\u05D1\u05E5 \u05D0\u05D5 \u05D4\u05D5\u05D3\u05E2\u05D4",enLine:"receives the outcome: a link, a file or a message",color:"#b4570b",colorDark:"#f0a35c",tint:"#fdf0e3",tintDark:"#3a2a18"},gate:{role:"gate",he:"\u05E9\u05E2\u05E8 \u05D4\u05D2\u05D9\u05E9\u05D4",en:"Access gate",heLine:"NoData: \u05DE\u05D7\u05DC\u05D9\u05D8 \u05D5\u05E8\u05D5\u05E9\u05DD, \u05D0\u05E3 \u05E4\u05E2\u05DD \u05DC\u05D0 \u05DE\u05D7\u05D6\u05D9\u05E7 \u05DE\u05E4\u05EA\u05D7 \u05E9\u05DC\u05DD",enLine:"NoData: decides and records, never holds a whole key",color:"#3b4054",colorDark:"#c7cbe0",tint:"#eceef4",tintDark:"#262a3a"}};function uy(e,t,n="en"){let r=ms[e];return`${n==="he"?r.he:r.en}: ${t}`}var et=(e,t=!1)=>({content:[{type:"text",text:e}],...t?{isError:t}:{}}),bu="npx -y @nodatachat/mcp@latest",_B=e=>e?e.slice(0,10):"never",fA=(e,t)=>e==="allow"?t?`yes \u2264${t}/day`:"yes":e==="approve"?"with approval":"no";function bB(e){let t=new Map((e.keys??[]).map(s=>[`${s.project}\0${s.name}`,s])),n=new Map;for(let s of e.accounts)n.set(s.project,[...n.get(s.project)??[],s]);if(!n.size)return["No client account yet. Add one: account_op 'add' (project, platform, account) gives the line the user runs in their own terminal."];let r=[],o=new Map;for(let[s,a]of n){let c=a[0].client,l=a.flatMap(p=>p.keys.map(f=>t.get(`${s}\0${f}`))).filter(p=>!!p);r.push(`\u25A0 ${c}${c!==s?` (project ${s})`:""} \xB7 today ${l.reduce((p,f)=>p+f.today.used,0)} used, ${l.reduce((p,f)=>p+f.today.refused,0)} refused`);let d=e.agents?.find(p=>p.project===s);r.push(d?` agent: ${d.name}${d.role?` (${d.role})`:""} \xB7 only from ${d.device} \xB7 uses ${d.keys.join(", ")||"no key"} \xB7 every other agent is refused this client's keys`:" agent: none yet (account_op 'agent' gives the client its own agent)");let u=new Map;for(let p of a)u.set(p.platform.id,[...u.get(p.platform.id)??[],p]);for(let[p,f]of u){if(p!=="other"){let h=o.get(p)??{name:f[0].platform.name,accounts:0,clients:new Set};h.accounts+=f.length,h.clients.add(s),o.set(p,h)}for(let h of f){let _=h.keys.map(b=>t.get(`${s}\0${b}`)).filter(b=>!!b),y=_.length>0&&_.every(b=>b.off),v=_.some(b=>b.login&&!b.off&&b.who.agent!=="deny"),m=_[0]?`person ${fA(y?"deny":_[0].who.person)} \xB7 agent ${fA(y?"deny":_[0].who.agent,_[0].who.agentCap)}${_[0].who.partners?` \xB7 ${_[0].who.partners} partner(s)`:""}`:"",k=_.map(b=>b.rotatedAt).filter(b=>!!b).sort().at(-1)??h.updatedAt;r.push(` ${p==="other"?"Other keys":h.platform.name}${h.account?` \xB7 ${h.account}`:""} \xB7 ${h.keys.length} key(s): ${h.keys.join(", ")} \xB7 rotated ${_B(k)}${m?` \xB7 ${m}`:""}${y?" \xB7 SWITCHED OFF":""}${v?" \xB7 \u26A0 an agent can use this login (account_op person_only)":""}`)}}}let i=[...o.values()].sort((s,a)=>a.accounts-s.accounts).map(s=>`${s.name} ${s.accounts} (${s.clients.size} client${s.clients.size===1?"":"s"})`);return[`${n.size} client(s) \xB7 ${i.join(" \xB7 ")}`,"",...r,"","Names only: no value is shown. The same board is in the NoData console (Mycelium \u2192 Data sources)."]}function wB(e,t){if(t.keys?.length)return t.keys.map(o=>o.trim()).filter(Boolean);if(!t.project)return{error:"name the client's `project` (and `platform`, optionally `account`), or the `keys`."};let r=e.accounts.filter(o=>o.project===t.project&&(!t.platform||o.platform.id===t.platform||o.platform.name.toLowerCase()===t.platform.toLowerCase())&&(!t.account||(o.account??"").toLowerCase()===t.account.trim().toUpperCase().replace(/[^A-Z0-9]+/g,"_").toLowerCase())).flatMap(o=>o.keys);return r.length?r:{error:`no keys of ${t.project}${t.platform?` on ${t.platform}`:""}${t.account?` (${t.account})`:""} in the vault. List with account_op 'list'.`}}async function hA(e,t){let n=t.account_op??"list",r=n==="add"&&t.platform?`&add=${encodeURIComponent(`${t.platform}:${t.account??""}:${t.use??"agent"}`)}`:"",o=await e("GET",`/api/v1/fabric-grants?accounts=1${r}`);if(!o.ok)return et(`could not read the accounts board (HTTP ${o.status}): ${String(o.data.error??"")}`,!0);let i=o.data.identities??{accounts:[]};if(!Array.isArray(i.accounts))return et("this NoData server does not show the accounts board yet.",!0);if(n==="list")return et(bB(i).join(`
313
313
  `));if(n==="add"){if(!t.project||!t.platform)return et("add needs the client (`project`, a short name) and the `platform` (tiktok, instagram, github, vercel\u2026); `account` for a platform; `use`: agent (a token), login (username + password) or both.",!0);let l=Array.isArray(o.data.add_keys)?o.data.add_keys:[];return l.length?et([`The user puts ${t.project}'s ${t.platform} account into the vault from their own terminal (each value typed hidden, split; never through this chat, and you never run it for them):`,` ${bu} secrets-set --project ${t.project} ${l.join(" ")}`,"On a connected computer the owner approves these key names once in the browser. The client's cube then appears on the board (account_op 'list').",`Or the client keeps the keys in THEIR vault: nodata_partner action 'invite' with ask: true, project "${t.project}", keys ${JSON.stringify(l)}.`].join(`
314
314
  `)):et("add needs the `account` (its name on the platform).",!0)}if(n==="agent")return vB(e,i,t);let s=wB(i,t);if("error"in s)return et(s.error,!0);let a=t.project??i.accounts.find(l=>l.keys.some(d=>s.includes(d)))?.project??"";if(n==="rotate")return et(["The user rotates in their own terminal (the new values typed hidden; the version goes up and every command gets the new one at its next use):",` ${bu} secrets-set --project ${a} ${s.join(" ")}`].join(`
315
315
  `));if(n==="use")return et([`To use ${s.join(", ")} for a job: nodata_run with project "${a}" and the command (the keys reach the command's memory only, under each key's rule; you never see them).`,`In a terminal: ${bu} secrets-run --project ${a} -- <command>`].join(`
@@ -328,7 +328,7 @@ ${u} allows this computer to use ${p.keys.join(", ")} (project ${p.project}) \u2
328
328
  ${u}'s invitation named keys, but the rule could not be written on their board yet; ask ${u} to add it (Courier \u2192 keys).`:"",_=IB(l.data.asked),y=_?`
329
329
  ${u} may now use ${_.keys.join(", ")} (project ${_.project}) by the rule on your board. Last step, yours: put them in your own vault, in a terminal you type in yourself (one approval in the browser, each value typed hidden): ${DB(_)}`:"",v=d.sender_kid?BB(u):null,m=d.sender_kid?`
330
330
  A folder with ${u}${v?`, made empty for you at ${v}`:""}: nothing passes without a policy. What you accept from them holds by default (clean copies, nothing critical, up to 10 MB, 30 days); see or change it: nodata_inbox action:'folder_policy'. To share it with them (one approval in the browser): nodata_expose action:'add', folder:'${v??"<a folder>"}', to:['${d.sender_kid}'] with its policy (max_severity, max_mb, types, approve_contents).`:"",k=x=>{let $=m?{...x,content:x.content.map((N,E)=>E===0&&N.type==="text"?{...N,text:`${N.text}${m}`}:N)}:x;return _?{...$,asked:_}:$},b=d.org_id?` To use keys ${u} lets you use: npx -y @nodatachat/mcp@latest secrets-run --from ${d.org_id} --project <project> -- <command>.`:"",S=typeof l.data.template=="string"?nc[l.data.template]:void 0;if(l.data.renewed===!0){let x=typeof l.data.grant?.expires_at=="string"?` until ${String(l.data.grant.expires_at).slice(0,10)}`:"",$=sn(er(),u);return k(ie(`Already connected with ${u}: the connection is renewed${x}. ${$?`Your command card for them stays as it is (${Mr()}).`:"Download your command card from the Connect page to decide what you accept."} Both organisations have a receipt.${b}${h}${g}${y}`))}return S?(iu(Gm(er(),{partner:u,partner_kids:d.sender_kid?[d.sender_kid]:[],send:S.send,receive:S.receive})),k(ie(`Connected with ${u}. Your command card for them is in ${Mr()} (${l.data.template}): you accept ${S.receive.map(x=>x.cmd).join(", ")} and send ${S.send.map(x=>x.cmd).join(", ")}. Both organisations have a receipt. Edit the card on the Connect page and download it again to change it.${b}${h}${g}${y}${CB(u,S.send,!!(g||y))}`))):k(ie(`Connected with ${u}. They can send commands to this computer's address ${a.kid}. Both organisations have a receipt. Download your command card from the Connect page to decide what you accept.${b}${h}${g}${y}`))}function CB(e,t,n){if(n)return"";let r=t.find(s=>s.cmd==="connect.ping")??t[0];if(!r)return"";let o=Object.keys(r.fields).filter(s=>r.fields[s]!=="never"),i=r.cmd==="connect.ping"?"{message:'hello from us'}":`{${o.map(s=>`${s}:\u2026`).join(", ")}}`;return`
331
- Next, your first exchange with ${e}: nodata_partner action:'send', to:'${e}', cmd:'${r.cmd}', fields:${i}. It is sealed here, both organisations get a receipt, and it counts as the connection's first success once they open it.`}function IB(e){let t=e&&typeof e=="object"?e:null;if(!t||typeof t.project!="string"||!t.project||/[\u0000-\u001f]/.test(t.project)||!Array.isArray(t.keys))return null;let n=t.keys.filter(r=>typeof r=="string"&&/^[A-Za-z_][A-Za-z0-9_]{0,199}$/.test(r));return n.length?{project:t.project,keys:n}:null}function DB(e){return`npx -y @nodatachat/mcp@latest secrets-set --project ${/^[A-Za-z0-9._-]{1,120}$/.test(e.project)?e.project:"<your project>"} ${e.keys.join(" ")}`}function jB(e,t){let n=String(t.error??"");return{invite_used:"This invitation was already used. If you are already connected, `nodata_partner connections` shows it; otherwise ask for a new link.",invite_own_org:"This computer is signed in as the inviting organisation itself. The link is for the partner's computer.",subject_kid_not_yours:"This computer's address does not belong to the organisation it is signed in to. Run `npx -y @nodatachat/mcp@latest connect` again.",invalid_subject_kid:"This computer has no valid Fabric address yet. Run `npx -y @nodatachat/mcp@latest connect` again.",already_connected:"Already connected with this organisation.",receipt_failed:"The proof of this connection could not be recorded, so nothing was connected. Try again.",create_failed:"The connection could not be made. Try again.",invite_invalid:t.reason==="expired"?"This invitation has expired. Ask the inviting organisation for a new link.":"This invitation link is not valid. Ask for a new one."}[n]??`not accepted (HTTP ${e}): ${n}${t.reason?` \xB7 ${String(t.reason)}`:""}`}function LB(e,t=process.cwd(),n){let r=St(t)??(Be().length===1?Be()[0]:null);if(!r)return"";let o=Yw(qw(e.name,t),r,e);return"error"in o?` (No work folder: ${o.error}.)`:(n&&zw(o.folder,n),`
331
+ Next, your first exchange with ${e}: nodata_partner action:'send', to:'${e}', cmd:'${r.cmd}', fields:${i}. It is sealed here, both organisations get a receipt, and it counts as the connection's first success once they open it.`}function IB(e){let t=e&&typeof e=="object"?e:null;if(!t||typeof t.project!="string"||!t.project||/[\u0000-\u001f]/.test(t.project)||!Array.isArray(t.keys))return null;let n=t.keys.filter(r=>typeof r=="string"&&/^[A-Za-z_][A-Za-z0-9_]{0,199}$/.test(r));return n.length?{project:t.project,keys:n}:null}function DB(e){return`npx -y @nodatachat/mcp@latest secrets-set --project ${/^[A-Za-z0-9._-]{1,120}$/.test(e.project)?e.project:"<your project>"} ${e.keys.join(" ")}`}function jB(e,t){let n=String(t.error??"");return{invite_used:"This invitation was already used. If you are already connected, `nodata_partner connections` shows it; otherwise ask for a new link.",invite_own_org:"This computer is signed in as the inviting organisation itself. The link is for the partner's computer.",subject_kid_not_yours:"This computer's address does not belong to the organisation it is signed in to. Run `npx -y @nodatachat/mcp@latest connect` again.",invalid_subject_kid:"This computer has no valid Mycelium address yet. Run `npx -y @nodatachat/mcp@latest connect` again.",already_connected:"Already connected with this organisation.",receipt_failed:"The proof of this connection could not be recorded, so nothing was connected. Try again.",create_failed:"The connection could not be made. Try again.",invite_invalid:t.reason==="expired"?"This invitation has expired. Ask the inviting organisation for a new link.":"This invitation link is not valid. Ask for a new one."}[n]??`not accepted (HTTP ${e}): ${n}${t.reason?` \xB7 ${String(t.reason)}`:""}`}function LB(e,t=process.cwd(),n){let r=St(t)??(Be().length===1?Be()[0]:null);if(!r)return"";let o=Yw(qw(e.name,t),r,e);return"error"in o?` (No work folder: ${o.error}.)`:(n&&zw(o.folder,n),`
332
332
  ${uy("controller",e.name)} (${ms.controller.enLine}) \xB7 ${uy("processor","this organisation")} (${ms.processor.enLine}) \xB7 ${ms.gate.en}: NoData (${ms.gate.enLine}).
333
333
  Your folder for ${e.name}: ${o.folder}. Work there (cd "${o.folder}" && claude): key commands in it use ${e.name}'s keys, under ${e.name}'s own rules, with nothing to type.`)}async function FB(e,t,n,r,o){let i=await rc(e,t,n,r,o);return i.ok?ie([`${n} sent to ${i.partner} (command id ${i.commandId}${o?`, answering ${o}`:""}).`,`Left: ${i.left.join(", ")||"(no fields)"}`,i.masked.length?`Masked by your card: ${i.masked.join(", ")}`:"",i.dropped.length?`Held back by your card: ${i.dropped.join(", ")}`:"",`Receipt ${i.receiptId.slice(0,8)} on both organisations' chains; the server stored ciphertext and the hash you committed to.`].filter(Boolean).join(`
334
334
  `)):ie(i.error,!0)}async function MB(e){let t=await qm(e,{skipSeen:!0});if(!t.ok)return ie(t.error,!0);if(!t.items.length)return ie("No new commands.");let n=t.items.map(r=>{if(r.status==="unopened")return`\xB7 ${r.relayId.slice(0,8)} from ${r.from}: not opened (${r.reason})`;if(r.status==="refused")return`\xB7 ${r.relayId.slice(0,8)} from ${r.from}: REFUSED (${r.reason}). Fields not shown. ${r.proven?"The refusal is on both proof chains.":"The refusal could not be put on the proof chains (it stands all the same)."}`;let o=r.command,i=o.cmd==="folder.policy.propose"?lE(o.fields):null;return i?[`\xB7 ${r.from} PROPOSES for your connection folder (id ${o.id}): ${i.side==="accept"?"that you accept":"that you let out"}: ${Kt(i.policy)}.${i.note?` Their note: ${i.note}`:""}`,i.side==="accept"?" Nothing changed. To apply it: nodata_inbox action:'folder_policy' with grant_id (list them with action:'folder_policy') and these fields; your owner approves in the browser.":" Nothing changed. To apply it: nodata_expose action:'add' on the folder you share with them, with these fields; your owner approves in the browser."].join(`
@@ -435,10 +435,10 @@ This computer's half is in ~/.nodata/api-shares.json (closed to agents). In Clau
435
435
  `,{mode:384}),{privateKey:r.privateKey,publicPem:r.publicKey.export({format:"pem",type:"spki"}),label:"this machine"}}async function e3(e){let{lexicon:t,line:n}=await wa(),r=await Qr({target_id:"mcp",target_label:bc(e),target_path:e,log:eo,...t?{lexicon:t}:{}});return{files:Mn({...r,findings:r.findings.filter(i=>!wo(i))}).byFile.map(i=>({path:Qy(e,i.key),classification:QH[i.worst]??"internal"})),termsLine:n}}function r1(e,t,n,r,o){e.registerTool("nodata_wrap",{description:"WRAP files into your organization's sovereign archive (a zero-knowledge capsule): each file is encrypted ON THIS MACHINE to the public half of your org master key \u2014 the 12 words from signup \u2014 and only ciphertext is uploaded. NoData can never open it; your 12 words (or recovery file) always can, even if this computer is lost or NoData is offline. Each document gets a signed receipt and proof_url. Given a folder, wraps the files the scan finds exposure in (classified by severity); given a file, wraps that file. ASKS FIRST: without confirm:true it only lists what would be wrapped. keep_local:true (default) also writes <file>.capsule.ndc next to the original; remove:true deletes the plaintext originals after a successful upload.",inputSchema:{path:Vu.string().describe("File or folder to wrap."),confirm:Vu.boolean().optional().describe("true = wrap it. Omit to preview."),keep_local:Vu.boolean().optional().describe("Also keep <file>.capsule.ndc next to the original. Default true."),remove:Vu.boolean().optional().describe("Delete plaintext originals after upload. Default false.")}},async({path:i,confirm:s,keep_local:a,remove:c})=>{let l=XH(i);if(!Xy(l))return dn(`Not found: ${l}`,!0);let d,u="";if(zu(l).isDirectory()?{files:d,termsLine:u}=await e3(l):d=[{path:l,classification:"confidential"}],d=d.filter(x=>Xy(x.path)&&zu(x.path).isFile()&&!$e(x.path)),d.length===0)return dn(`Nothing to wrap in ${l} \u2014 no file with findings.`);let p=d.filter(x=>zu(x.path).size>e1);d=d.filter(x=>zu(x.path).size<=e1);let f=await t.get("/api/v1/recovery/pubkey");if(f.status===401||f.status===403)return dn(`Not authorized to read the org key (HTTP ${f.status}). Connect with nodata_connect.`,!0);let h=typeof f.body.pubkeyB64=="string"?f.body.pubkeyB64:null,g=h?Sg(o,h):null;if(g&&!g.ok)return dn(new Fa(g,o,wd()).message,!0);if(!h)return dn(`Your organization has no master key yet \u2014 the 12-word recovery setup was not completed, so there is nothing to wrap to.
436
436
  Finish it once in the console (Recovery setup), then wrap again. NoData never sees the words.`,!0);if(!s){let x=d.slice(0,Jy).map($=>` \xB7 [${$.classification}] ${$.path}`);return d.length>Jy&&x.push(` \u2026 and ${d.length-Jy} more`),dn([`PREVIEW \u2014 nothing wrapped yet. ${r()}`,`Would wrap ${d.length} file(s) into the "${_c}" capsule, sealed to your org master key (fingerprint ${g?.fingerprint.slice(0,12)??"?"}):`,...x,u,p.length?`Skipped (over 25 MB): ${p.map($=>bc($.path)).join(", ")}`:"",`Encrypted here; only ciphertext is uploaded. Opens with your 12 words \u2014 never with NoData.${c?" Plaintext originals WILL BE DELETED after upload.":""}`,"","Ask the user to approve, then call nodata_wrap again with the same arguments and confirm: true."].filter(Boolean).join(`
437
437
  `))}let _=await t.get("/api/v1/capsules");if(_.status>=400)return dn(`Could not list capsules (HTTP ${_.status}): ${JSON.stringify(_.body)}`,!0);let v=(_.body.capsules??[]).find(x=>x.slug===_c);if(v&&v.key_mode!=="zk")return dn(`A "${_c}" capsule exists but is not zero-knowledge (key_mode ${String(v.key_mode)}) \u2014 refusing to wrap into it.`,!0);if(!v){let x=await t.post("/api/v1/capsules",{handle:_c,display_name:"MCP archive",key_mode:"zk",capsule_pubkey:h});if(x.status>=400)return dn(`Could not create the archive capsule (HTTP ${x.status}): ${JSON.stringify(x.body)}`,!0);v=x.body}let m=String(v.slug??v.capsule_id??"");if(!m)return dn(`Capsule id missing in ${JSON.stringify(v)}`,!0);let k=n(),b=[],S=[];for(let x of d){let $=Zy(x.path),{ndcBytes:N,report:E}=await Vy({fileBytes:$,filename:bc(x.path),contentType:ZH[JH(x.path).toLowerCase()]??"application/octet-stream",recipientPubkeyB64:h,issuerPrivateKey:k.privateKey,issuerPubkeyPem:k.publicPem}),O=await t.post(`/api/v1/capsules/${encodeURIComponent(m)}/documents`,{title:bc(x.path),ndc_b64:N.toString("base64"),classification:x.classification,plaintext_sha256:E.plaintext_sha256,size_bytes:E.size_bytes});if(O.status>=400||O.body.ok!==!0){S.push(` \u2717 ${x.path} \u2014 HTTP ${O.status}: ${JSON.stringify(O.body).slice(0,200)}`);continue}a!==!1&&t1(`${x.path}.capsule.ndc`,N,{mode:384});let M="";if(c)try{let L=JT(N,E.content_key_b64);if(!L.equals(Zy(x.path)))throw new Error("the archived content differs from the original");L.fill(0),YH(x.path)}catch(L){M=` (original KEPT: ${L instanceof Error?L.message:String(L)})`}b.push(` \u2713 [${x.classification}] ${bc(x.path)}${O.body.proof_url?` proof: ${O.body.proof_url}`:""}${M}`)}return dn([`WRAPPED ${b.length}/${d.length} into "${_c}" \u2014 ${r()} \xB7 signed by ${k.label}`,...b,...S,"Sealed to your org master key: NoData stores ciphertext only and cannot open it. Your 12 words (or recovery file) open every document.",a!==!1?"Local copies: <file>.capsule.ndc next to each original.":"",c?"Plaintext originals deleted (only for files that uploaded).":""].filter(Boolean).join(`
438
- `),S.length>0&&b.length===0)})}ui();import{createHash as t_}from"node:crypto";import{appendFileSync as t3,existsSync as n3,mkdirSync as r3,readFileSync as o3}from"node:fs";import{homedir as o1}from"node:os";import{join as i1}from"node:path";var n_=[{id:"overview",topics:["what","nodata","does","do","product","about","can","\u05DE\u05D4","\u05E2\u05D5\u05E9\u05D4","\u05D9\u05D5\u05D3\u05E2","\u05DE\u05D5\u05E6\u05E8","\u05E0\u05D5 \u05D3\u05D0\u05D8\u05D4","\u05D9\u05DB\u05D5\u05DC\u05D5\u05EA"],answer:"NoData keeps sensitive information safe where you work: on your computer and in your AI agents' work. It finds what is exposed in your files (ID numbers, cards, bank numbers, phones, emails, names, API keys: in text, Excel, Word, PowerPoint and PDF), locks files so only the approved computer that sealed them can open them, makes clean copies to work on, runs commands with secrets they never write to disk, shares files through links you can cancel, gives AI agents access to exactly the columns you allow, and keeps a proof of every step. Scanning and clean copies run entirely on this computer; locking encrypts here and registers at NoData only a key half sealed to this computer."},{id:"privacy",topics:["safe","secure","privacy","private","who can see","trust","upload","\u05D1\u05D8\u05D5\u05D7","\u05E4\u05E8\u05D8\u05D9\u05D5\u05EA","\u05DE\u05D9 \u05D9\u05DB\u05D5\u05DC \u05DC\u05E8\u05D0\u05D5\u05EA","\u05DE\u05D9 \u05E8\u05D5\u05D0\u05D4","\u05D4\u05DE\u05D9\u05D3\u05E2 \u05E9\u05DC\u05D9","\u05E2\u05D5\u05DC\u05D4 \u05DC\u05E2\u05E0\u05DF","\u05D0\u05DE\u05D5\u05DF"],answer:"Where your information stays: scanning and clean copies (nodata_scan_folder, nodata_redact) run on this computer and upload nothing, and the scan reports counts and places, not the values. Locking (nodata_protect) encrypts on this computer; the file never leaves it, and NoData receives only the file key sealed to this computer's key, which only this computer opens. A locked file opens on the computer that sealed it and on the same person's other connected computers (its key is sealed to each of them, each with its own key), after NoData checks that computer, a person approves the opening, and a proof is recorded. A file sent with nodata_send is stored by NoData only as ciphertext, with the key in the part of the link NoData never receives. An archive made with nodata_wrap opens only with your organisation's 12 words."},{id:"capsule",topics:["capsule","ndc","\u05E7\u05E4\u05E1\u05D5\u05DC\u05D4","\u05E7\u05E4\u05E1\u05D5\u05DC\u05D5\u05EA"],answer:"A capsule is a file sealed so it opens only under its own policy: on the approved computer that sealed it (nodata_protect, a .ndc file; it also opens on your other connected computers; every opening is recorded and can be revoked), as a governed link with expiry and cancel (nodata_send), or to your organisation's 12-word key (nodata_wrap)."},{id:"scan",topics:["scan","find","folder","exposed","sensitive","pii","report","\u05E1\u05E8\u05D9\u05E7\u05D4","\u05DC\u05E1\u05E8\u05D5\u05E7","\u05DC\u05DE\u05E6\u05D5\u05D0","\u05EA\u05D9\u05E7\u05D9\u05D9\u05D4","\u05E8\u05D2\u05D9\u05E9","\u05D7\u05E9\u05D5\u05E3","\u05D3\u05D5\u05D7"],answer:"nodata_scan_folder scans a folder on this computer and reports what is exposed: checksum-validated Israeli IDs, credit cards (Luhn), IBANs, phones, emails, names and addresses, and hard-coded keys and passwords. It reads Excel, Word, PowerPoint and PDF, including Hebrew. The result shows counts by type, folder and file, a full HTML report, and a line saying how many documents were read. Nothing is uploaded."},{id:"pdf",topics:["pdf","scanned","ocr","hebrew","read","document","\u05E1\u05E8\u05D5\u05E7","\u05E1\u05E8\u05D9\u05E7\u05D4","\u05E2\u05D1\u05E8\u05D9\u05EA","\u05DE\u05E1\u05DE\u05DA","\u05DC\u05E7\u05E8\u05D5\u05D0","\u05E7\u05E8\u05D9\u05D0\u05D4"],answer:"PDFs are read by NoData's own reader, including Hebrew and documents stamped by marking tools. With two free tools installed on this computer it reads more: Poppler (pdftotext) reads every PDF a second time, and Tesseract reads scanned pages (OCR). They run only here. See what is installed: npx -y @nodatachat/mcp@latest pdf-tools"},{id:"gate",topics:["claude","read","gate","block","ask before","\u05E7\u05DC\u05D5\u05D3","\u05E1\u05D5\u05DB\u05DF","\u05D7\u05E1\u05D9\u05DE\u05D4","\u05DC\u05D7\u05E1\u05D5\u05DD","\u05E9\u05E2\u05E8","\u05E7\u05E8\u05D0","\u05E7\u05D5\u05E8\u05D0","\u05D9\u05E7\u05E8\u05D0"],answer:"The read gate asks you before Claude Code reads a file where the scan found sensitive data, or refuses it if you choose. It also covers env files and never lets an agent read the device key. Turn it on in a terminal: npx -y @nodatachat/mcp@latest install-gate (add --block to refuse instead of asking)."},{id:"protect",topics:["lock","protect","encrypt","seal","unlock","release","\u05E0\u05E2\u05D9\u05DC\u05D4","\u05DC\u05E0\u05E2\u05D5\u05DC","\u05E0\u05D5\u05E2\u05DC","\u05E0\u05E2\u05D5\u05DC","\u05D4\u05E6\u05E4\u05E0\u05D4","\u05DC\u05D4\u05E6\u05E4\u05D9\u05DF","\u05DE\u05E6\u05E4\u05D9\u05DF","\u05DC\u05E4\u05EA\u05D5\u05D7"],answer:"nodata_protect locks files: each is sealed as <file>.ndc (AES-256-GCM) to this computer's key, which never leaves it. Locking needs this computer to be connected and approved by a person in the organisation: every seal belongs to that computer, is registered at NoData with a signed proof (NoData holds only a key half sealed to this computer), and can be revoked. Given a folder, it locks the files the scan found exposure in. nodata_open opens a sealed file back next to it, only on the computer that sealed it, and checks its hash; the opening is recorded. A kept original is still readable, so for .env secrets use nodata_run. nodata_policy lists every sealed file of the organisation, which computer it belongs to and its openings; nodata_seal_revoke opens the console where a person revokes one for good (a leaked file, a lost computer). Both show what they will do and ask before acting."},{id:"redact",topics:["clean","copy","redact","anonymise","anonymize","share","work","\u05E2\u05D5\u05EA\u05E7","\u05E0\u05E7\u05D9","\u05D4\u05EA\u05DE\u05DE\u05D4","\u05DC\u05D4\u05E1\u05EA\u05D9\u05E8","\u05DC\u05E2\u05D1\u05D5\u05D3"],answer:"nodata_redact makes a clean copy to work on: IDs, cards, IBANs, phones, emails and keys become typed tokens ([ID], [CARD], [SECRET] \u2026); in Excel and CSV a personal-data column becomes one stable token per person, so the copy still groups and counts. The copy is re-scanned before it is handed back, and a receipt is written."},{id:"plan",topics:["policy","recommend","plan","decide","what to do","\u05DE\u05D3\u05D9\u05E0\u05D9\u05D5\u05EA","\u05D4\u05DE\u05DC\u05E6\u05D4","\u05EA\u05D5\u05DB\u05E0\u05D9\u05EA","\u05DE\u05D4 \u05DC\u05E2\u05E9\u05D5\u05EA"],answer:"nodata_plan recommends a policy per file after a scan \u2014 a tier (Restricted / Confidential / Internal) and an action: clean copy, lock, both, or keep \u2014 and applies it only after you approve. Leaked keys also get where-and-how-to-replace steps per provider."},{id:"scan-feedback",topics:["false positive","wrong","mistake","missed","not sensitive","feedback","report","\u05D8\u05E2\u05D5\u05EA","\u05E9\u05D2\u05D5\u05D9","\u05E4\u05E1\u05E4\u05E1","\u05DC\u05D0 \u05E8\u05D2\u05D9\u05E9","\u05DE\u05E9\u05D5\u05D1","\u05D3\u05D9\u05D5\u05D5\u05D7"],answer:'When a scan gets it wrong, say so: nodata_scan_feedback reports what it flagged that is not sensitive and what kinds it missed. Only kind labels and counts leave your machine ("ID\xD72 not sensitive \xB7 phone\xD71 missed") \u2014 never a value, a file name or free text \u2014 and you see the exact line sent. It changes no file; to leave a file alone use nodata_plan with keep.'},{id:"secrets",topics:["secret","secrets","env",".env","key","keys","password","token","vault","run","\u05E1\u05D5\u05D3","\u05E1\u05D5\u05D3\u05D5\u05EA","\u05DE\u05E4\u05EA\u05D7","\u05DE\u05E4\u05EA\u05D7\u05D5\u05EA","\u05E1\u05D9\u05E1\u05DE\u05D4","\u05DB\u05E1\u05E4\u05EA"],answer:"Secrets reach a command without a .env on disk. In a terminal: npx -y @nodatachat/mcp@latest secrets-login --project <name> once, then npx -y @nodatachat/mcp@latest secrets-run -- <command>; the secrets exist only in that command's memory. An agent does the same with nodata_run: every command needs your approval, and the output it sees has every secret value blanked."},{id:"leaked-keys",topics:["leaked","leak","rotate","rotation","git","history","commit","exposed key","\u05D3\u05DC\u05E3","\u05D3\u05DC\u05D9\u05E4\u05D4","\u05DC\u05D4\u05D7\u05DC\u05D9\u05E3","\u05D2\u05D9\u05D8","\u05D4\u05D9\u05E1\u05D8\u05D5\u05E8\u05D9\u05D4"],answer:"The scan finds API keys and passwords written in files (OpenAI, Anthropic, Supabase, AWS, Stripe, database URLs and more) and, with git_history, in past commits too \u2014 including ones deleted since. nodata_plan then lists, per provider, where to replace each key, which is the step that stops its use."},{id:"send",topics:["send","share","link","burn","revoke","expire","recipient","\u05DC\u05E9\u05DC\u05D5\u05D7","\u05DC\u05E9\u05EA\u05E3","\u05E7\u05D9\u05E9\u05D5\u05E8","\u05DC\u05D1\u05D8\u05DC","\u05DC\u05E9\u05E8\u05D5\u05E3","\u05E0\u05DE\u05E2\u05DF"],answer:"nodata_send shares a file as a governed link (or, with to:<NoData address>, over the NoData road \u2014 see fabric): encrypted on this computer, stored by NoData only as ciphertext, with the key in the part of the link NoData never receives. It expires after a time and a number of opens; nodata_link_status shows opens so far; nodata_burn cancels it for good, with a signed receipt. With code:true (or your own code of 8+ letters) the link opens only together with a code you send on a different channel; the recipient types it and their browser checks it, and NoData never receives it \u2014 a forwarded link alone opens nothing."},{id:"fabric",topics:["fabric","road","network","address","partner","organisation","organization","other company","inbox","mcp to mcp","\u05E4\u05D1\u05E8\u05D9\u05E7","\u05E8\u05E9\u05EA","\u05DB\u05EA\u05D5\u05D1\u05EA","\u05E9\u05D5\u05EA\u05E3","\u05D0\u05E8\u05D2\u05D5\u05DF \u05D0\u05D7\u05E8","\u05D7\u05D1\u05E8\u05D4 \u05D0\u05D7\u05E8\u05EA","\u05EA\u05D9\u05D1\u05D4","\u05D3\u05D5\u05D0\u05E8 \u05E0\u05DB\u05E0\u05E1","\u05D1\u05D9\u05DF \u05D0\u05E8\u05D2\u05D5\u05E0\u05D9\u05DD"],answer:"The NoData road (Fabric) connects this computer to other organisations with no link to leak. nodata_inbox (action 'address') makes this computer's address: a key pair made here, the private half never leaves it (~/.nodata/fabric-key.json), NoData knows only its fingerprint. A partner sends with nodata_send to:<address> \u2014 or to your email once your computers are on the road, and it reaches each of them \u2014 (or from the console); it is sealed on their computer to yours, NoData stores ciphertext only, and a proof is written on both organisations' chains before anything moves. nodata_inbox 'list' shows what arrived, 'open' unseals it here into a folder without showing its content to the agent."},{id:"fabric-plate",topics:["who can send","who can reach","approve","block","spam","unknown sender","plate","policy","disclose","\u05DE\u05D9 \u05D9\u05DB\u05D5\u05DC \u05DC\u05E9\u05DC\u05D5\u05D7","\u05DC\u05D0\u05E9\u05E8","\u05DC\u05D7\u05E1\u05D5\u05DD","\u05E9\u05D5\u05DC\u05D7 \u05DC\u05D0 \u05DE\u05D5\u05DB\u05E8","\u05DC\u05D5\u05D7\u05D9\u05EA","\u05DE\u05D3\u05D9\u05E0\u05D9\u05D5\u05EA","\u05DC\u05D7\u05E9\u05D5\u05E3","\u05DE\u05D4 \u05E8\u05D5\u05D0\u05D9\u05DD \u05E2\u05DC\u05D9\u05D9"],answer:"Your address carries a plate \u2014 its policy, changeable any time with nodata_inbox action 'policy': open, verified (a first message from an unknown organisation waits, still sealed, until you approve or block it) or closed (only your allow list); allow and block lists; whether a sender must declare a purpose; and what senders learn about you (organisation name, only 'verified', or just the address). nodata_inbox 'approve' admits a waiting sender and delivers what it sent; 'block' refuses it and shreds what it sent, unread. Every decision is on both sides' proof chains, and each asks you first."},{id:"file-folder-rules",topics:["rules","rule","folder rules","file rules","folder policy","per folder","per file","agent may","block folder","\u05D7\u05D5\u05E7\u05D9\u05DD","\u05DB\u05DC\u05DC\u05D9\u05DD","\u05DE\u05D3\u05D9\u05E0\u05D9\u05D5\u05EA \u05DC\u05EA\u05D9\u05E7\u05D9\u05D9\u05D4"],answer:"nodata_rules is the board of rules for each file and folder on this computer: what an AI agent may do there \u2014 allowed, ask you, clean copy only (it works on a nodata_redact copy), or not allowed \u2014 with several rules per path and conditions on the action (read, search, run, write), the hour, the weekday and the file type. The most specific path decides; among its rules, the first whose conditions hold. The read gate enforces them on every read, search, command and write, whatever the agent. Tightening is a preview then confirm; allowing a path, or removing a restriction, needs your own approval. The rules are signed on this computer: changed by hand, every path they name is closed until you save them again. Your organisation can also set folder rules for every computer (in the browser: the capsule page \u2192 the organisation's folder rules); they arrive with its policy, and on your computer the stricter of its rule and yours wins. For keys, each key has its own rules in the vault (Courier \u2192 secrets \u2192 key policy board), applied by nodata_run."},{id:"access-not-copies",topics:["access instead","not send","without sending","ownership","copy","query","share data","expose","folder","partner ask","\u05D2\u05D9\u05E9\u05D4","\u05D1\u05DC\u05D9 \u05DC\u05E9\u05DC\u05D5\u05D7","\u05D1\u05E2\u05DC\u05D5\u05EA","\u05D4\u05E2\u05EA\u05E7","\u05DC\u05E9\u05EA\u05E3 \u05DE\u05D9\u05D3\u05E2","\u05DC\u05D7\u05E9\u05D5\u05E3","\u05D7\u05E9\u05D9\u05E4\u05D4","\u05EA\u05D9\u05E7\u05D9\u05D9\u05D4","\u05DC\u05EA\u05EA \u05D2\u05D9\u05E9\u05D4"],answer:"Most sharing should be access, not a copy. For files on this computer: nodata_expose lets a partner's computer ask for files in a folder you choose \u2014 it gets the verified clean copy of each file it asks for (identifiers replaced, checked before it leaves), only while the rule lives, and you stop it at once; they ask with nodata_inbox action 'ask'. For your tables: the other side's agent gets a scoped grant (nodata_grant) and reads only the fields it is allowed (nodata_retrieve / nodata_read), cut at once with nodata_revoke. A file moves whole only when a copy must: nodata_send, as a governed link or over the NoData road."},{id:"archive",topics:["archive","wrap","backup","recovery","12 words","lost","\u05D0\u05E8\u05DB\u05D9\u05D5\u05DF","\u05D2\u05D9\u05D1\u05D5\u05D9","\u05E9\u05D7\u05D6\u05D5\u05E8","12 \u05DE\u05D9\u05DC\u05D9\u05DD","\u05D0\u05D1\u05D3"],answer:"nodata_wrap archives files encrypted on this computer to your organisation's 12-word key; only ciphertext is uploaded, and only your 12 words (or recovery file) open it \u2014 even if this computer is lost."},{id:"key-backup",topics:["device key","back up","backup","usb","lost computer","\u05DE\u05E4\u05EA\u05D7 \u05D4\u05DE\u05DB\u05E9\u05D9\u05E8","\u05DE\u05E4\u05EA\u05D7","\u05D2\u05D9\u05D1\u05D5\u05D9","\u05DC\u05D2\u05D1\u05D5\u05EA","\u05DE\u05D2\u05D1\u05D9\u05DD","\u05DE\u05D2\u05D1\u05D4","\u05D3\u05D9\u05E1\u05E7 \u05D0\u05D5\u05DF \u05E7\u05D9"],answer:"Locked files open on the computer that sealed them, with its device key. Back it up once, in a terminal: npx -y @nodatachat/mcp@latest key-backup E: (E: is your USB drive): it copies the key, checks the copy is identical, and records the backup."},{id:"agents-data",topics:["agent","agents","database","table","grant","access","columns","revoke","ai","\u05E1\u05D5\u05DB\u05E0\u05D9\u05DD","\u05D8\u05D1\u05DC\u05D4","\u05DE\u05E1\u05D3","\u05D4\u05E8\u05E9\u05D0\u05D4","\u05D2\u05D9\u05E9\u05D4","\u05E2\u05DE\u05D5\u05D3\u05D5\u05EA"],answer:"AI agents get exactly what you allow over your own tables: nodata_register_table declares the sensitive columns, nodata_grant gives one agent a scoped grant (after your approval), nodata_policy shows every agent and what it can reach, and nodata_revoke cuts it at once. An agent asks with nodata_decide, pulls allowed fields with nodata_retrieve, and denied columns never decrypt."},{id:"proofs",topics:["proof","proofs","receipt","audit","verify","evidence","\u05D4\u05D5\u05DB\u05D7\u05D4","\u05D4\u05D5\u05DB\u05D7\u05D5\u05EA","\u05E7\u05D1\u05DC\u05D4","\u05D1\u05D9\u05E7\u05D5\u05E8\u05EA","\u05DC\u05D0\u05DE\u05EA"],answer:"nodata_proofs lists your organisation's signed proofs \u2014 every grant, agent read and denial \u2014 content-free. Anyone can verify one without an account at /api/access/verify. Locking, clean copies and cancelled links write their own receipts too."},{id:"start",topics:["start","install","connect","signup","setup","begin","\u05DC\u05D4\u05EA\u05D7\u05D9\u05DC","\u05DE\u05EA\u05D7\u05D9\u05DC\u05D9\u05DD","\u05D4\u05EA\u05E7\u05E0\u05D4","\u05D4\u05E8\u05E9\u05DE\u05D4","\u05D7\u05D9\u05D1\u05D5\u05E8"],answer:"Accept the terms once (nodata_terms) and scanning, locking and clean copies work on this computer. For the organisation tools (agents, sending, proofs) call nodata_connect: a page opens in your browser, you sign in (or create a free account) and click Approve \u2014 nothing to copy. Then type /mcp."}],s1=t_("sha256").update(JSON.stringify(n_)).digest("hex").slice(0,12);function i3(e,t){return e.topics.reduce((n,r)=>n+(t.includes(r.toLowerCase())?1:0),0)}function s3(e){let t=` ${e.toLowerCase()} `,r=n_.map(o=>({a:o,s:i3(o,t)})).filter(o=>o.s>0).sort((o,i)=>i.s-o.s).filter(o=>o.a.id!=="overview").slice(0,3).map(o=>o.a);return r.length?r:[n_[0]]}var e_=()=>i1(o1(),".nodata","answers.log");function a3(e){let t="genesis";try{let r=n3(e_())?o3(e_(),"utf8").trimEnd().split(`
438
+ `),S.length>0&&b.length===0)})}ui();import{createHash as t_}from"node:crypto";import{appendFileSync as t3,existsSync as n3,mkdirSync as r3,readFileSync as o3}from"node:fs";import{homedir as o1}from"node:os";import{join as i1}from"node:path";var n_=[{id:"overview",topics:["what","nodata","does","do","product","about","can","\u05DE\u05D4","\u05E2\u05D5\u05E9\u05D4","\u05D9\u05D5\u05D3\u05E2","\u05DE\u05D5\u05E6\u05E8","\u05E0\u05D5 \u05D3\u05D0\u05D8\u05D4","\u05D9\u05DB\u05D5\u05DC\u05D5\u05EA"],answer:"NoData keeps sensitive information safe where you work: on your computer and in your AI agents' work. It finds what is exposed in your files (ID numbers, cards, bank numbers, phones, emails, names, API keys: in text, Excel, Word, PowerPoint and PDF), locks files so only the approved computer that sealed them can open them, makes clean copies to work on, runs commands with secrets they never write to disk, shares files through links you can cancel, gives AI agents access to exactly the columns you allow, and keeps a proof of every step. Scanning and clean copies run entirely on this computer; locking encrypts here and registers at NoData only a key half sealed to this computer."},{id:"privacy",topics:["safe","secure","privacy","private","who can see","trust","upload","\u05D1\u05D8\u05D5\u05D7","\u05E4\u05E8\u05D8\u05D9\u05D5\u05EA","\u05DE\u05D9 \u05D9\u05DB\u05D5\u05DC \u05DC\u05E8\u05D0\u05D5\u05EA","\u05DE\u05D9 \u05E8\u05D5\u05D0\u05D4","\u05D4\u05DE\u05D9\u05D3\u05E2 \u05E9\u05DC\u05D9","\u05E2\u05D5\u05DC\u05D4 \u05DC\u05E2\u05E0\u05DF","\u05D0\u05DE\u05D5\u05DF"],answer:"Where your information stays: scanning and clean copies (nodata_scan_folder, nodata_redact) run on this computer and upload nothing, and the scan reports counts and places, not the values. Locking (nodata_protect) encrypts on this computer; the file never leaves it, and NoData receives only the file key sealed to this computer's key, which only this computer opens. A locked file opens on the computer that sealed it and on the same person's other connected computers (its key is sealed to each of them, each with its own key), after NoData checks that computer, a person approves the opening, and a proof is recorded. A file sent with nodata_send is stored by NoData only as ciphertext, with the key in the part of the link NoData never receives. An archive made with nodata_wrap opens only with your organisation's 12 words."},{id:"capsule",topics:["capsule","ndc","\u05E7\u05E4\u05E1\u05D5\u05DC\u05D4","\u05E7\u05E4\u05E1\u05D5\u05DC\u05D5\u05EA"],answer:"A capsule is a file sealed so it opens only under its own policy: on the approved computer that sealed it (nodata_protect, a .ndc file; it also opens on your other connected computers; every opening is recorded and can be revoked), as a governed link with expiry and cancel (nodata_send), or to your organisation's 12-word key (nodata_wrap)."},{id:"scan",topics:["scan","find","folder","exposed","sensitive","pii","report","\u05E1\u05E8\u05D9\u05E7\u05D4","\u05DC\u05E1\u05E8\u05D5\u05E7","\u05DC\u05DE\u05E6\u05D5\u05D0","\u05EA\u05D9\u05E7\u05D9\u05D9\u05D4","\u05E8\u05D2\u05D9\u05E9","\u05D7\u05E9\u05D5\u05E3","\u05D3\u05D5\u05D7"],answer:"nodata_scan_folder scans a folder on this computer and reports what is exposed: checksum-validated Israeli IDs, credit cards (Luhn), IBANs, phones, emails, names and addresses, and hard-coded keys and passwords. It reads Excel, Word, PowerPoint and PDF, including Hebrew. The result shows counts by type, folder and file, a full HTML report, and a line saying how many documents were read. Nothing is uploaded."},{id:"pdf",topics:["pdf","scanned","ocr","hebrew","read","document","\u05E1\u05E8\u05D5\u05E7","\u05E1\u05E8\u05D9\u05E7\u05D4","\u05E2\u05D1\u05E8\u05D9\u05EA","\u05DE\u05E1\u05DE\u05DA","\u05DC\u05E7\u05E8\u05D5\u05D0","\u05E7\u05E8\u05D9\u05D0\u05D4"],answer:"PDFs are read by NoData's own reader, including Hebrew and documents stamped by marking tools. With two free tools installed on this computer it reads more: Poppler (pdftotext) reads every PDF a second time, and Tesseract reads scanned pages (OCR). They run only here. See what is installed: npx -y @nodatachat/mcp@latest pdf-tools"},{id:"gate",topics:["claude","read","gate","block","ask before","\u05E7\u05DC\u05D5\u05D3","\u05E1\u05D5\u05DB\u05DF","\u05D7\u05E1\u05D9\u05DE\u05D4","\u05DC\u05D7\u05E1\u05D5\u05DD","\u05E9\u05E2\u05E8","\u05E7\u05E8\u05D0","\u05E7\u05D5\u05E8\u05D0","\u05D9\u05E7\u05E8\u05D0"],answer:"The read gate asks you before Claude Code reads a file where the scan found sensitive data, or refuses it if you choose. It also covers env files and never lets an agent read the device key. Turn it on in a terminal: npx -y @nodatachat/mcp@latest install-gate (add --block to refuse instead of asking)."},{id:"protect",topics:["lock","protect","encrypt","seal","unlock","release","\u05E0\u05E2\u05D9\u05DC\u05D4","\u05DC\u05E0\u05E2\u05D5\u05DC","\u05E0\u05D5\u05E2\u05DC","\u05E0\u05E2\u05D5\u05DC","\u05D4\u05E6\u05E4\u05E0\u05D4","\u05DC\u05D4\u05E6\u05E4\u05D9\u05DF","\u05DE\u05E6\u05E4\u05D9\u05DF","\u05DC\u05E4\u05EA\u05D5\u05D7"],answer:"nodata_protect locks files: each is sealed as <file>.ndc (AES-256-GCM) to this computer's key, which never leaves it. Locking needs this computer to be connected and approved by a person in the organisation: every seal belongs to that computer, is registered at NoData with a signed proof (NoData holds only a key half sealed to this computer), and can be revoked. Given a folder, it locks the files the scan found exposure in. nodata_open opens a sealed file back next to it, only on the computer that sealed it, and checks its hash; the opening is recorded. A kept original is still readable, so for .env secrets use nodata_run. nodata_policy lists every sealed file of the organisation, which computer it belongs to and its openings; nodata_seal_revoke opens the console where a person revokes one for good (a leaked file, a lost computer). Both show what they will do and ask before acting."},{id:"redact",topics:["clean","copy","redact","anonymise","anonymize","share","work","\u05E2\u05D5\u05EA\u05E7","\u05E0\u05E7\u05D9","\u05D4\u05EA\u05DE\u05DE\u05D4","\u05DC\u05D4\u05E1\u05EA\u05D9\u05E8","\u05DC\u05E2\u05D1\u05D5\u05D3"],answer:"nodata_redact makes a clean copy to work on: IDs, cards, IBANs, phones, emails and keys become typed tokens ([ID], [CARD], [SECRET] \u2026); in Excel and CSV a personal-data column becomes one stable token per person, so the copy still groups and counts. The copy is re-scanned before it is handed back, and a receipt is written."},{id:"plan",topics:["policy","recommend","plan","decide","what to do","\u05DE\u05D3\u05D9\u05E0\u05D9\u05D5\u05EA","\u05D4\u05DE\u05DC\u05E6\u05D4","\u05EA\u05D5\u05DB\u05E0\u05D9\u05EA","\u05DE\u05D4 \u05DC\u05E2\u05E9\u05D5\u05EA"],answer:"nodata_plan recommends a policy per file after a scan \u2014 a tier (Restricted / Confidential / Internal) and an action: clean copy, lock, both, or keep \u2014 and applies it only after you approve. Leaked keys also get where-and-how-to-replace steps per provider."},{id:"scan-feedback",topics:["false positive","wrong","mistake","missed","not sensitive","feedback","report","\u05D8\u05E2\u05D5\u05EA","\u05E9\u05D2\u05D5\u05D9","\u05E4\u05E1\u05E4\u05E1","\u05DC\u05D0 \u05E8\u05D2\u05D9\u05E9","\u05DE\u05E9\u05D5\u05D1","\u05D3\u05D9\u05D5\u05D5\u05D7"],answer:'When a scan gets it wrong, say so: nodata_scan_feedback reports what it flagged that is not sensitive and what kinds it missed. Only kind labels and counts leave your machine ("ID\xD72 not sensitive \xB7 phone\xD71 missed") \u2014 never a value, a file name or free text \u2014 and you see the exact line sent. It changes no file; to leave a file alone use nodata_plan with keep.'},{id:"secrets",topics:["secret","secrets","env",".env","key","keys","password","token","vault","run","\u05E1\u05D5\u05D3","\u05E1\u05D5\u05D3\u05D5\u05EA","\u05DE\u05E4\u05EA\u05D7","\u05DE\u05E4\u05EA\u05D7\u05D5\u05EA","\u05E1\u05D9\u05E1\u05DE\u05D4","\u05DB\u05E1\u05E4\u05EA"],answer:"Secrets reach a command without a .env on disk. In a terminal: npx -y @nodatachat/mcp@latest secrets-login --project <name> once, then npx -y @nodatachat/mcp@latest secrets-run -- <command>; the secrets exist only in that command's memory. An agent does the same with nodata_run: every command needs your approval, and the output it sees has every secret value blanked."},{id:"leaked-keys",topics:["leaked","leak","rotate","rotation","git","history","commit","exposed key","\u05D3\u05DC\u05E3","\u05D3\u05DC\u05D9\u05E4\u05D4","\u05DC\u05D4\u05D7\u05DC\u05D9\u05E3","\u05D2\u05D9\u05D8","\u05D4\u05D9\u05E1\u05D8\u05D5\u05E8\u05D9\u05D4"],answer:"The scan finds API keys and passwords written in files (OpenAI, Anthropic, Supabase, AWS, Stripe, database URLs and more) and, with git_history, in past commits too \u2014 including ones deleted since. nodata_plan then lists, per provider, where to replace each key, which is the step that stops its use."},{id:"send",topics:["send","share","link","burn","revoke","expire","recipient","\u05DC\u05E9\u05DC\u05D5\u05D7","\u05DC\u05E9\u05EA\u05E3","\u05E7\u05D9\u05E9\u05D5\u05E8","\u05DC\u05D1\u05D8\u05DC","\u05DC\u05E9\u05E8\u05D5\u05E3","\u05E0\u05DE\u05E2\u05DF"],answer:"nodata_send shares a file as a governed link (or, with to:<NoData address>, over the NoData road \u2014 see fabric): encrypted on this computer, stored by NoData only as ciphertext, with the key in the part of the link NoData never receives. It expires after a time and a number of opens; nodata_link_status shows opens so far; nodata_burn cancels it for good, with a signed receipt. With code:true (or your own code of 8+ letters) the link opens only together with a code you send on a different channel; the recipient types it and their browser checks it, and NoData never receives it \u2014 a forwarded link alone opens nothing."},{id:"fabric",topics:["fabric","road","network","address","partner","organisation","organization","other company","inbox","mcp to mcp","\u05DE\u05D9\u05D9\u05E1\u05DC\u05D9\u05D5\u05DD","\u05E8\u05E9\u05EA","\u05DB\u05EA\u05D5\u05D1\u05EA","\u05E9\u05D5\u05EA\u05E3","\u05D0\u05E8\u05D2\u05D5\u05DF \u05D0\u05D7\u05E8","\u05D7\u05D1\u05E8\u05D4 \u05D0\u05D7\u05E8\u05EA","\u05EA\u05D9\u05D1\u05D4","\u05D3\u05D5\u05D0\u05E8 \u05E0\u05DB\u05E0\u05E1","\u05D1\u05D9\u05DF \u05D0\u05E8\u05D2\u05D5\u05E0\u05D9\u05DD"],answer:"The NoData road (Mycelium) connects this computer to other organisations with no link to leak. nodata_inbox (action 'address') makes this computer's address: a key pair made here, the private half never leaves it (~/.nodata/fabric-key.json), NoData knows only its fingerprint. A partner sends with nodata_send to:<address> \u2014 or to your email once your computers are on the road, and it reaches each of them \u2014 (or from the console); it is sealed on their computer to yours, NoData stores ciphertext only, and a proof is written on both organisations' chains before anything moves. nodata_inbox 'list' shows what arrived, 'open' unseals it here into a folder without showing its content to the agent."},{id:"fabric-plate",topics:["who can send","who can reach","approve","block","spam","unknown sender","plate","policy","disclose","\u05DE\u05D9 \u05D9\u05DB\u05D5\u05DC \u05DC\u05E9\u05DC\u05D5\u05D7","\u05DC\u05D0\u05E9\u05E8","\u05DC\u05D7\u05E1\u05D5\u05DD","\u05E9\u05D5\u05DC\u05D7 \u05DC\u05D0 \u05DE\u05D5\u05DB\u05E8","\u05DC\u05D5\u05D7\u05D9\u05EA","\u05DE\u05D3\u05D9\u05E0\u05D9\u05D5\u05EA","\u05DC\u05D7\u05E9\u05D5\u05E3","\u05DE\u05D4 \u05E8\u05D5\u05D0\u05D9\u05DD \u05E2\u05DC\u05D9\u05D9"],answer:"Your address carries a plate \u2014 its policy, changeable any time with nodata_inbox action 'policy': open, verified (a first message from an unknown organisation waits, still sealed, until you approve or block it) or closed (only your allow list); allow and block lists; whether a sender must declare a purpose; and what senders learn about you (organisation name, only 'verified', or just the address). nodata_inbox 'approve' admits a waiting sender and delivers what it sent; 'block' refuses it and shreds what it sent, unread. Every decision is on both sides' proof chains, and each asks you first."},{id:"file-folder-rules",topics:["rules","rule","folder rules","file rules","folder policy","per folder","per file","agent may","block folder","\u05D7\u05D5\u05E7\u05D9\u05DD","\u05DB\u05DC\u05DC\u05D9\u05DD","\u05DE\u05D3\u05D9\u05E0\u05D9\u05D5\u05EA \u05DC\u05EA\u05D9\u05E7\u05D9\u05D9\u05D4"],answer:"nodata_rules is the board of rules for each file and folder on this computer: what an AI agent may do there \u2014 allowed, ask you, clean copy only (it works on a nodata_redact copy), or not allowed \u2014 with several rules per path and conditions on the action (read, search, run, write), the hour, the weekday and the file type. The most specific path decides; among its rules, the first whose conditions hold. The read gate enforces them on every read, search, command and write, whatever the agent. Tightening is a preview then confirm; allowing a path, or removing a restriction, needs your own approval. The rules are signed on this computer: changed by hand, every path they name is closed until you save them again. Your organisation can also set folder rules for every computer (in the browser: the capsule page \u2192 the organisation's folder rules); they arrive with its policy, and on your computer the stricter of its rule and yours wins. For keys, each key has its own rules in the vault (Courier \u2192 secrets \u2192 key policy board), applied by nodata_run."},{id:"access-not-copies",topics:["access instead","not send","without sending","ownership","copy","query","share data","expose","folder","partner ask","\u05D2\u05D9\u05E9\u05D4","\u05D1\u05DC\u05D9 \u05DC\u05E9\u05DC\u05D5\u05D7","\u05D1\u05E2\u05DC\u05D5\u05EA","\u05D4\u05E2\u05EA\u05E7","\u05DC\u05E9\u05EA\u05E3 \u05DE\u05D9\u05D3\u05E2","\u05DC\u05D7\u05E9\u05D5\u05E3","\u05D7\u05E9\u05D9\u05E4\u05D4","\u05EA\u05D9\u05E7\u05D9\u05D9\u05D4","\u05DC\u05EA\u05EA \u05D2\u05D9\u05E9\u05D4"],answer:"Most sharing should be access, not a copy. For files on this computer: nodata_expose lets a partner's computer ask for files in a folder you choose \u2014 it gets the verified clean copy of each file it asks for (identifiers replaced, checked before it leaves), only while the rule lives, and you stop it at once; they ask with nodata_inbox action 'ask'. For your tables: the other side's agent gets a scoped grant (nodata_grant) and reads only the fields it is allowed (nodata_retrieve / nodata_read), cut at once with nodata_revoke. A file moves whole only when a copy must: nodata_send, as a governed link or over the NoData road."},{id:"archive",topics:["archive","wrap","backup","recovery","12 words","lost","\u05D0\u05E8\u05DB\u05D9\u05D5\u05DF","\u05D2\u05D9\u05D1\u05D5\u05D9","\u05E9\u05D7\u05D6\u05D5\u05E8","12 \u05DE\u05D9\u05DC\u05D9\u05DD","\u05D0\u05D1\u05D3"],answer:"nodata_wrap archives files encrypted on this computer to your organisation's 12-word key; only ciphertext is uploaded, and only your 12 words (or recovery file) open it \u2014 even if this computer is lost."},{id:"key-backup",topics:["device key","back up","backup","usb","lost computer","\u05DE\u05E4\u05EA\u05D7 \u05D4\u05DE\u05DB\u05E9\u05D9\u05E8","\u05DE\u05E4\u05EA\u05D7","\u05D2\u05D9\u05D1\u05D5\u05D9","\u05DC\u05D2\u05D1\u05D5\u05EA","\u05DE\u05D2\u05D1\u05D9\u05DD","\u05DE\u05D2\u05D1\u05D4","\u05D3\u05D9\u05E1\u05E7 \u05D0\u05D5\u05DF \u05E7\u05D9"],answer:"Locked files open on the computer that sealed them, with its device key. Back it up once, in a terminal: npx -y @nodatachat/mcp@latest key-backup E: (E: is your USB drive): it copies the key, checks the copy is identical, and records the backup."},{id:"agents-data",topics:["agent","agents","database","table","grant","access","columns","revoke","ai","\u05E1\u05D5\u05DB\u05E0\u05D9\u05DD","\u05D8\u05D1\u05DC\u05D4","\u05DE\u05E1\u05D3","\u05D4\u05E8\u05E9\u05D0\u05D4","\u05D2\u05D9\u05E9\u05D4","\u05E2\u05DE\u05D5\u05D3\u05D5\u05EA"],answer:"AI agents get exactly what you allow over your own tables: nodata_register_table declares the sensitive columns, nodata_grant gives one agent a scoped grant (after your approval), nodata_policy shows every agent and what it can reach, and nodata_revoke cuts it at once. An agent asks with nodata_decide, pulls allowed fields with nodata_retrieve, and denied columns never decrypt."},{id:"proofs",topics:["proof","proofs","receipt","audit","verify","evidence","\u05D4\u05D5\u05DB\u05D7\u05D4","\u05D4\u05D5\u05DB\u05D7\u05D5\u05EA","\u05E7\u05D1\u05DC\u05D4","\u05D1\u05D9\u05E7\u05D5\u05E8\u05EA","\u05DC\u05D0\u05DE\u05EA"],answer:"nodata_proofs lists your organisation's signed proofs \u2014 every grant, agent read and denial \u2014 content-free. Anyone can verify one without an account at /api/access/verify. Locking, clean copies and cancelled links write their own receipts too."},{id:"start",topics:["start","install","connect","signup","setup","begin","\u05DC\u05D4\u05EA\u05D7\u05D9\u05DC","\u05DE\u05EA\u05D7\u05D9\u05DC\u05D9\u05DD","\u05D4\u05EA\u05E7\u05E0\u05D4","\u05D4\u05E8\u05E9\u05DE\u05D4","\u05D7\u05D9\u05D1\u05D5\u05E8"],answer:"Accept the terms once (nodata_terms) and scanning, locking and clean copies work on this computer. For the organisation tools (agents, sending, proofs) call nodata_connect: a page opens in your browser, you sign in (or create a free account) and click Approve \u2014 nothing to copy. Then type /mcp."}],s1=t_("sha256").update(JSON.stringify(n_)).digest("hex").slice(0,12);function i3(e,t){return e.topics.reduce((n,r)=>n+(t.includes(r.toLowerCase())?1:0),0)}function s3(e){let t=` ${e.toLowerCase()} `,r=n_.map(o=>({a:o,s:i3(o,t)})).filter(o=>o.s>0).sort((o,i)=>i.s-o.s).filter(o=>o.a.id!=="overview").slice(0,3).map(o=>o.a);return r.length?r:[n_[0]]}var e_=()=>i1(o1(),".nodata","answers.log");function a3(e){let t="genesis";try{let r=n3(e_())?o3(e_(),"utf8").trimEnd().split(`
439
439
  `):[],o=r[r.length-1];o&&(t=t_("sha256").update(o).digest("hex").slice(0,16))}catch{}let n=JSON.stringify({at:new Date().toISOString(),entries:e,bank:s1,prev:t});try{r3(i1(o1(),".nodata"),{recursive:!0}),t3(e_(),n+`
440
440
  `,{mode:384})}catch{}return t_("sha256").update(n).digest("hex").slice(0,16)}function a1(e){let t=s3(e),n=a3(t.map(r=>r.id));return["ANSWER FROM THE NODATA ANSWER BANK \u2014 relay this in the user's language, and nothing beyond it:","",...t.map(r=>`\u2022 ${r.answer}`),"",`Source: NoData answer bank ${s1} \xB7 entries: ${t.map(r=>r.id).join(", ")} \xB7 recorded ${n} in ~/.nodata/answers.log`].join(`
441
- `)}ya();import{existsSync as p1,mkdirSync as c3,readFileSync as l3,writeFileSync as d3}from"node:fs";import{homedir as i_}from"node:os";import{join as s_}from"node:path";var u3=60*6e4;function p3(e){try{return process.kill(e,0),!0}catch(t){return t.code==="EPERM"}}var r_=()=>s_(i_(),".nodata","connection.json"),f3=()=>s_(i_(),".nodata","pending-link.json"),o_=6e4,c1=15*o_,l1=[3e4,2*o_,5*o_];function d1(e,t){switch(e.connection){case"connected":return{state:"connected"};case"unstable":case"offline":return{state:"reconnecting"};case"restart":return{state:"restart",step:"/mcp"};case"not-connected":return t?{state:"waiting"}:{state:"local"};case"reconnect":return t?{state:"waiting"}:{state:"needs-you",step:e.refused==="org-has-no-key"?"console \u2192 API keys":e.refused==="flag-key"||e.refused==="env-key"?"remove the old key":"nodata_connect"}}}function qu(e,t=new Date){try{c3(s_(i_(),".nodata"),{recursive:!0});let n=t.toISOString(),r={};for(let[o,i]of Object.entries(h3()?.versions??{}))t.getTime()-Date.parse(i.at)<u3&&p3(Number(o))&&(r[o]=i);e.version&&(r[String(process.pid)]={version:e.version,at:n}),d3(r_(),JSON.stringify({...e,...Object.keys(r).length?{versions:r}:{},at:n}),{mode:384})}catch{}}function h3(){try{return p1(r_())?JSON.parse(l3(r_(),"utf8")):null}catch{return null}}var u1=()=>p1(f3());function g3(e,t){return e==="reconnecting"?l1[Math.min(t,l1.length-1)]??c1:e==="waiting"?3e4:c1}function f1(e,t,n,r){let o=0,i=n?{version:n}:{},s=d1(t,u1());qu({...s,...i});let a=null,c=()=>{a=setTimeout(l,g3(s.state,o)),a.unref?.()},l=async()=>{try{let d=d1(await e(),u1());o=d.state==="reconnecting"?o+1:0,(d.state!==s.state||d.step!==s.step)&&r?.(d),s=d,qu({...d,...i})}catch{o+=1}c()};return c(),()=>{a&&clearTimeout(a)}}mt();import{homedir as I1,hostname as P3,userInfo as R3}from"node:os";import{join as x1,resolve as Yu}from"node:path";import{existsSync as l_,readFileSync as f_,writeFileSync as N3,mkdirSync as O3}from"node:fs";import{spawn as Ju,spawnSync as Xu}from"node:child_process";import{generateKeyPairSync as C3,createPrivateKey as I3,sign as D3,randomBytes as j3}from"node:crypto";async function h1(e=process.env){if(!(e.HTTPS_PROXY||e.https_proxy||e.HTTP_PROXY||e.http_proxy)||/^(1|true)$/i.test(e.NODE_USE_ENV_PROXY??""))return!1;try{let{EnvHttpProxyAgent:t,setGlobalDispatcher:n}=await import("undici");return n(new t),!0}catch{return!1}}import{readFileSync as m3}from"node:fs";function ks(e=process.env,t=process.platform,n=m1()){return!(t==="linux"&&!n&&!e.DISPLAY&&!e.WAYLAND_DISPLAY)}function g1(e=process.env,t=process.platform,n=m1()){return e.VITEST||/^(1|true|yes)$/i.test(e.NODATA_NO_BROWSER??"")?!1:ks(e,t,n)}function m1(){try{return process.platform==="linux"&&/microsoft/i.test(m3("/proc/version","utf8"))}catch{return!1}}var nr="1.6.33";function wc(e=process.cwd()){try{let t=St(e),n=t?Sf(t):null;if(!n||!l_(n))return null;let r=JSON.parse(f_(n,"utf8"));if(!r)return null;let o=r.device&&typeof r.device.private_key_pkcs8_b64=="string",i=typeof r.api_key=="string"&&r.api_key;return o||i?r:null}catch{return null}}function d_(e,t=process.cwd()){let n=hl(e);if(!n)throw new Error("nothing to save: no organisation in the credential");Kw(n,e),t&&Ww(t,n)}var L3=`nodata-mcp v${nr} \u2014 NoData MCP server
441
+ `)}ya();import{existsSync as p1,mkdirSync as c3,readFileSync as l3,writeFileSync as d3}from"node:fs";import{homedir as i_}from"node:os";import{join as s_}from"node:path";var u3=60*6e4;function p3(e){try{return process.kill(e,0),!0}catch(t){return t.code==="EPERM"}}var r_=()=>s_(i_(),".nodata","connection.json"),f3=()=>s_(i_(),".nodata","pending-link.json"),o_=6e4,c1=15*o_,l1=[3e4,2*o_,5*o_];function d1(e,t){switch(e.connection){case"connected":return{state:"connected"};case"unstable":case"offline":return{state:"reconnecting"};case"restart":return{state:"restart",step:"/mcp"};case"not-connected":return t?{state:"waiting"}:{state:"local"};case"reconnect":return t?{state:"waiting"}:{state:"needs-you",step:e.refused==="org-has-no-key"?"console \u2192 API keys":e.refused==="flag-key"||e.refused==="env-key"?"remove the old key":"nodata_connect"}}}function qu(e,t=new Date){try{c3(s_(i_(),".nodata"),{recursive:!0});let n=t.toISOString(),r={};for(let[o,i]of Object.entries(h3()?.versions??{}))t.getTime()-Date.parse(i.at)<u3&&p3(Number(o))&&(r[o]=i);e.version&&(r[String(process.pid)]={version:e.version,at:n}),d3(r_(),JSON.stringify({...e,...Object.keys(r).length?{versions:r}:{},at:n}),{mode:384})}catch{}}function h3(){try{return p1(r_())?JSON.parse(l3(r_(),"utf8")):null}catch{return null}}var u1=()=>p1(f3());function g3(e,t){return e==="reconnecting"?l1[Math.min(t,l1.length-1)]??c1:e==="waiting"?3e4:c1}function f1(e,t,n,r){let o=0,i=n?{version:n}:{},s=d1(t,u1());qu({...s,...i});let a=null,c=()=>{a=setTimeout(l,g3(s.state,o)),a.unref?.()},l=async()=>{try{let d=d1(await e(),u1());o=d.state==="reconnecting"?o+1:0,(d.state!==s.state||d.step!==s.step)&&r?.(d),s=d,qu({...d,...i})}catch{o+=1}c()};return c(),()=>{a&&clearTimeout(a)}}mt();import{homedir as I1,hostname as P3,userInfo as R3}from"node:os";import{join as x1,resolve as Yu}from"node:path";import{existsSync as l_,readFileSync as f_,writeFileSync as N3,mkdirSync as O3}from"node:fs";import{spawn as Ju,spawnSync as Xu}from"node:child_process";import{generateKeyPairSync as C3,createPrivateKey as I3,sign as D3,randomBytes as j3}from"node:crypto";async function h1(e=process.env){if(!(e.HTTPS_PROXY||e.https_proxy||e.HTTP_PROXY||e.http_proxy)||/^(1|true)$/i.test(e.NODE_USE_ENV_PROXY??""))return!1;try{let{EnvHttpProxyAgent:t,setGlobalDispatcher:n}=await import("undici");return n(new t),!0}catch{return!1}}import{readFileSync as m3}from"node:fs";function ks(e=process.env,t=process.platform,n=m1()){return!(t==="linux"&&!n&&!e.DISPLAY&&!e.WAYLAND_DISPLAY)}function g1(e=process.env,t=process.platform,n=m1()){return e.VITEST||/^(1|true|yes)$/i.test(e.NODATA_NO_BROWSER??"")?!1:ks(e,t,n)}function m1(){try{return process.platform==="linux"&&/microsoft/i.test(m3("/proc/version","utf8"))}catch{return!1}}var nr="1.6.34";function wc(e=process.cwd()){try{let t=St(e),n=t?Sf(t):null;if(!n||!l_(n))return null;let r=JSON.parse(f_(n,"utf8"));if(!r)return null;let o=r.device&&typeof r.device.private_key_pkcs8_b64=="string",i=typeof r.api_key=="string"&&r.api_key;return o||i?r:null}catch{return null}}function d_(e,t=process.cwd()){let n=hl(e);if(!n)throw new Error("nothing to save: no organisation in the credential");Kw(n,e),t&&Ww(t,n)}var L3=`nodata-mcp v${nr} \u2014 NoData MCP server
442
442
 
443
443
  ADMIN mode: nodata-mcp --api-key sk_live_...
444
444
  AGENT mode: nodata-mcp --grant-token ndca-...
@@ -1,4 +1,4 @@
1
- /*! @nodatachat/mcp 1.6.33 | Copyright 2026 Capsule Ltd. | FSL-1.1-ALv2 | see LICENSE.md */
1
+ /*! @nodatachat/mcp 1.6.34 | Copyright 2026 Capsule Ltd. | FSL-1.1-ALv2 | see LICENSE.md */
2
2
  import{createRequire as __ndReq}from"node:module";const require=__ndReq(import.meta.url);
3
3
  import{parentPort as At}from"node:worker_threads";import{createHash as Js}from"node:crypto";import{closeSync as Ys,existsSync as qs,openSync as Qs,readSync as eo,readdirSync as mr,readFileSync as to,statSync as xt}from"node:fs";import{homedir as no}from"node:os";import{basename as gr,join as Ge}from"node:path";import{TextDecoder as Sr}from"node:util";var Q={text:[],header:[],secrets:[],table:null,amounts:null,severity:null};function Tt(e){e.table&&(Q.table=e.table),e.severity&&(Q.severity=e.severity),e.amounts&&(Q.amounts=e.amounts);for(let t of e.text??[])Q.text.some(n=>n.kind===t.kind)||Q.text.push(t);e.header&&!Q.header.includes(e.header)&&Q.header.push(e.header);for(let t of e.secrets??[])Q.secrets.some(n=>n.id===t.id)||Q.secrets.push(t)}function Ye(e){return Q.severity?.(e)}function qe(){return Q.text}function yn(){return Q.header}function Ce(){return Q.table}function bn(){return Q.secrets}var _n=[{id:"aws-access-key",name:"AWS Access Key",pattern:/(?:^|[^A-Z0-9])AKIA(?!X{16})[A-Z2-7]{16}(?:[^A-Z2-7]|$)/,severity:"critical"},{id:"aws-secret-key",name:"AWS Secret Key",pattern:/(?:aws_secret_access_key|aws_secret_key|secret_access_key)\s*[=:]\s*['"]?[A-Za-z0-9/+=]{40}['"]?/,severity:"critical"},{id:"gcp-api-key",name:"GCP API Key",pattern:/AIza[0-9A-Za-z_-]{35}/,severity:"critical"},{id:"gcp-service-account",name:"GCP Service Account",pattern:/"type"\s*:\s*"service_account"/,severity:"critical"},{id:"github-token",name:"GitHub Token",pattern:/gh[pousr]_[0-9a-zA-Z]{36,}/,severity:"critical"},{id:"github-fine-grained",name:"GitHub Fine-Grained Token",pattern:/github_pat_[0-9a-zA-Z_]{22,}/,severity:"critical"},{id:"gitlab-token",name:"GitLab Token",pattern:/glpat-[0-9a-zA-Z_-]{20,}/,severity:"critical"},{id:"stripe-secret",name:"Stripe Secret Key",pattern:/sk_live_[0-9a-zA-Z]{24,}/,severity:"critical"},{id:"stripe-restricted",name:"Stripe Restricted Key",pattern:/rk_live_[0-9a-zA-Z]{24,}/,severity:"critical"},{id:"slack-token",name:"Slack Token",pattern:/xox[baprs]-[0-9a-zA-Z-]{10,}/,severity:"high"},{id:"slack-webhook",name:"Slack Webhook",pattern:/hooks\.slack\.com\/services\/T[0-9A-Z]{8,}\/B[0-9A-Z]{8,}\/[0-9a-zA-Z]{24}/,severity:"high"},{id:"twilio-api-key",name:"Twilio API Key",pattern:/SK[0-9a-fA-F]{32}/,severity:"high"},{id:"sendgrid-api-key",name:"SendGrid API Key",pattern:/SG\.[0-9a-zA-Z_-]{22}\.[0-9a-zA-Z_-]{43}/,severity:"critical"},{id:"postgres-uri",name:"PostgreSQL Connection String",pattern:/postgres(?:ql)?:\/\/[^\s'"]{10,}/,severity:"critical"},{id:"mongodb-uri",name:"MongoDB Connection String",pattern:/mongodb(?:\+srv)?:\/\/[^\s'"]{10,}/,severity:"critical"},{id:"redis-uri",name:"Redis Connection String",pattern:/redis(?:s)?:\/\/[^\s'"]{10,}/,severity:"high"},{id:"openai-key",name:"OpenAI API Key",pattern:/sk-[0-9a-zA-Z]{20,}T3BlbkFJ[0-9a-zA-Z]{20,}/,severity:"critical"},{id:"openai-project-key",name:"OpenAI API Key",pattern:/sk-(?:proj|svcacct|admin)-[0-9A-Za-z_-]{40,}/,severity:"critical"},{id:"anthropic-key",name:"Anthropic API Key",pattern:/sk-ant-[0-9a-zA-Z_-]{40,}/,severity:"critical"},{id:"firebase-key",name:"Firebase API Key",pattern:/AIza[0-9A-Za-z_-]{35}/,severity:"high"},{id:"supabase-key",name:"Supabase Service Key",pattern:/eyJ[0-9a-zA-Z_-]{100,}/,severity:"critical"},{id:"supabase-access-token",name:"Supabase Access Token",pattern:/\bsbp_[0-9a-f]{40}\b/,severity:"critical"},{id:"supabase-secret-key",name:"Supabase Secret Key",pattern:/\bsb_secret_[0-9A-Za-z_-]{20,}/,severity:"critical"},{id:"private-key",name:"Private Key",pattern:/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,severity:"critical"},{id:"jwt-secret",name:"JWT/Generic Secret",pattern:/(?:jwt_secret|JWT_SECRET|secret_key|SECRET_KEY|api_secret|API_SECRET)\s*[=:]\s*['"][^'"]{8,}['"]/,severity:"high"},{id:"password-in-code",name:"Password in Code",pattern:/(?:password|passwd|pwd)\s*[=:]\s*['"][^'"]{8,}['"]/,severity:"high"},{id:"bearer-token",name:"Bearer Token in Code",pattern:/['"]Bearer\s+[0-9a-zA-Z._-]{20,}['"]/,severity:"high"}];function Lt(){let e=bn().filter(t=>!_n.some(n=>n.id===t.id));return[..._n,...e]}var xn=/node_modules\/|\.next\/|\.test\.|\.spec\.|__tests__\/|\.nodata-proof\.json$|\.git\//,wn=/(^|\/)\.env(\.[\w-]+)*\.(example|sample|template|dist)$|\.(example|sample|template)$|(^|\/)[^/]*\.(example|sample)\.[a-z0-9]+$/i,Ct=/(^|\/)(__tests__|__mocks__|tests?|spec|fixtures?|mocks?|samples?|examples?|demo|testdata)(\/|$)|\.(test|spec)\.[a-z0-9]+$|\.min\.(m?js|css)$|(^|\/)[^/]*\.worker\.min\.m?js$/i,Kr=[{name:"lodash",below:"4.17.21",severity:"critical",cve:"CVE-2021-23337",desc:"Command injection via template"},{name:"minimist",below:"1.2.6",severity:"critical",cve:"CVE-2021-44906",desc:"Prototype pollution"},{name:"json5",below:"2.2.2",severity:"high",cve:"CVE-2022-46175",desc:"Prototype pollution"},{name:"express",below:"4.19.2",severity:"high",cve:"CVE-2024-29041",desc:"Open redirect"},{name:"path-to-regexp",below:"6.3.0",severity:"high",cve:"CVE-2024-45296",desc:"ReDoS"},{name:"sanitize-html",below:"2.12.1",severity:"high",cve:"CVE-2024-21501",desc:"XSS bypass"},{name:"dompurify",below:"3.0.9",severity:"high",cve:"CVE-2024-45801",desc:"mXSS"},{name:"jsonwebtoken",below:"9.0.0",severity:"critical",cve:"CVE-2022-23529",desc:"Key confusion attack"},{name:"jose",below:"4.15.5",severity:"high",cve:"CVE-2024-28176",desc:"DoS via JWE"},{name:"axios",below:"1.6.0",severity:"high",cve:"CVE-2023-45857",desc:"CSRF cookie leak"},{name:"node-fetch",below:"2.6.7",severity:"high",cve:"CVE-2022-0235",desc:"Header leak on redirect"},{name:"serialize-javascript",below:"6.0.2",severity:"critical",cve:"CVE-2024-11831",desc:"XSS via serialization"},{name:"xml2js",below:"0.6.0",severity:"high",cve:"CVE-2023-0842",desc:"Prototype pollution"},{name:"next",below:"14.1.1",severity:"high",cve:"CVE-2024-34350",desc:"SSRF via headers"},{name:"sharp",below:"0.32.6",severity:"high",cve:"CVE-2023-4863",desc:"libwebp heap overflow"},{name:"multer",below:"1.4.5",severity:"high",cve:"CVE-2022-24434",desc:"Prototype pollution"},{name:"tough-cookie",below:"4.1.3",severity:"medium",cve:"CVE-2023-26136",desc:"Prototype pollution"},{name:"semver",below:"7.5.4",severity:"medium",cve:"CVE-2022-25883",desc:"ReDoS"},{name:"ip",below:"2.0.1",severity:"critical",cve:"CVE-2024-29415",desc:"SSRF bypass via octal IP"},{name:"tar",below:"6.2.1",severity:"high",cve:"CVE-2024-28863",desc:"DoS via path traversal"}];function Jr(e,t){let n=s=>s.replace(/^[^0-9]*/,"").split(".").map(o=>parseInt(o,10)||0),r=n(e),i=n(t);for(let s=0;s<3;s++){if((r[s]||0)<(i[s]||0))return!0;if((r[s]||0)>(i[s]||0))return!1}return!1}function vn(e){let t=[],n;try{n=JSON.parse(e)}catch{return t}let r={...n.dependencies??{},...n.devDependencies??{}};for(let[i,s]of Object.entries(r)){let o=(s||"").replace(/^[\^~>=<\s]+/,""),a=Kr.find(c=>c.name===i&&Jr(o,c.below));a&&t.push({name:i,version:o,vuln:a})}return t}var Yr=[{pattern:/^(full_?name|first_?name|last_?name|spouse_?name|beneficiary_?name)$/i,type:"name",label:"Person name"},{pattern:/^(email|e_?mail|email_?address)$/i,type:"email",label:"Email address"},{pattern:/^(phone|mobile|telephone|phone_?number|cell_?phone)$/i,type:"phone",label:"Phone number"},{pattern:/^(id_?number|identity|ssn|social_?security|national_?id|teudat_?zehut)$/i,type:"id",label:"ID number"},{pattern:/^(address|street_?address|home_?address)$/i,type:"address",label:"Address"},{pattern:/^(date_?of_?birth|dob|birth_?date|birthday)$/i,type:"dob",label:"Date of birth"},{pattern:/^(bank_?account|iban|account_?number|bank_?details|routing_?number)$/i,type:"financial",label:"Bank account"},{pattern:/^(credit_?card|card_?number|cvv|cc_?number)$/i,type:"financial",label:"Credit card"},{pattern:/^(salary|income|commission|premium|compensation)$/i,type:"financial",label:"Salary / income"},{pattern:/^(medical_?info|health_?conditions|diagnosis|medication|prescription)$/i,type:"medical",label:"Medical"},{pattern:/^(passport|passport_?number|passport_?id)$/i,type:"id",label:"Passport"},{pattern:/^(driver_?license|drivers_?license|dl_?number)$/i,type:"id",label:"Driver license"},{pattern:/^(tax_?id|tin|vat_?number|ein)$/i,type:"financial",label:"Tax ID"},{pattern:/^(insurance_?number|policy_?number|member_?id)$/i,type:"id",label:"Insurance/Policy"},{pattern:/^(biometric|fingerprint|face_?id|retina)$/i,type:"biometric",label:"Biometric"},{pattern:/^(latitude|longitude|lat|lng|geo_?location|coordinates)$/i,type:"location",label:"Geolocation"}],qr=[{pattern:/^(?:שם|שם_מלא|שם פרטי|שם_פרטי|שם משפחה|שם_משפחה|שם הלקוח|שם בן זוג|שם המבוטח|שם המוטב|שם הילד|שם הקטין)$/i,type:"name",label:"\u05E9\u05DD \u05D0\u05D3\u05DD"},{pattern:/^(?:ת"ז|ת\.ז|ת ז|תז|תעודת זהות|תעודת_זהות|מספר זהות|מס' זהות|teudat_zehut|tz|israeli_id|il_id)$/i,type:"id",label:"\u05EA\u05E2\u05D5\u05D3\u05EA \u05D6\u05D4\u05D5\u05EA"},{pattern:/^(?:דרכון|מספר דרכון|רישיון נהיגה|רישיון_נהיגה|רשיון נהיגה)$/i,type:"id",label:"\u05D3\u05E8\u05DB\u05D5\u05DF/\u05E8\u05D9\u05E9\u05D9\u05D5\u05DF"},{pattern:/^(?:מייל|דוא"ל|דוא_ל|דואר אלקטרוני|אימייל|כתובת מייל)$/i,type:"email",label:"\u05D3\u05D5\u05D0\u05E8 \u05D0\u05DC\u05E7\u05D8\u05E8\u05D5\u05E0\u05D9"},{pattern:/^(?:טלפון|טל'|נייד|פלאפון|סלולרי|מספר טלפון|טלפון נייד)$/i,type:"phone",label:"\u05DE\u05E1\u05E4\u05E8 \u05D8\u05DC\u05E4\u05D5\u05DF"},{pattern:/^(?:כתובת|כתובת מגורים|רחוב|מס בית|עיר|ישוב|יישוב|מיקוד|כתובת מלאה|מען)$/i,type:"address",label:"\u05DB\u05EA\u05D5\u05D1\u05EA"},{pattern:/^(?:תאריך לידה|ת לידה|תאריך הולדת|יום הולדת|גיל)$/i,type:"dob",label:"\u05EA\u05D0\u05E8\u05D9\u05DA \u05DC\u05D9\u05D3\u05D4"},{pattern:/^(?:חשבון בנק|מספר חשבון|מס חשבון|סניף|iban|iban_il)$/i,type:"financial",label:"\u05D7\u05E9\u05D1\u05D5\u05DF \u05D1\u05E0\u05E7"},{pattern:/^(?:כרטיס אשראי|מספר אשראי|cvv|אשראי)$/i,type:"financial",label:"\u05DB\u05E8\u05D8\u05D9\u05E1 \u05D0\u05E9\u05E8\u05D0\u05D9"},{pattern:/^(?:שכר|משכורת|הכנסה|פרמיה|שכר ברוטו|שכר נטו|תגמול|בונוס|פיצוי)$/i,type:"financial",label:"\u05E9\u05DB\u05E8/\u05D4\u05DB\u05E0\u05E1\u05D4"},{pattern:/^(?:אבחון|אבחנה|מחלה|תרופה|מרשם|בדיקה רפואית|מצב רפואי|חיסון|רגישות|אלרגיה|סוג דם)$/i,type:"medical",label:"\u05DE\u05D9\u05D3\u05E2 \u05E8\u05E4\u05D5\u05D0\u05D9"},{pattern:/^(?:מספר פוליסה|מס פוליסה|פוליסה|מספר חבר|מספר ביטוח)$/i,type:"id",label:"\u05E4\u05D5\u05DC\u05D9\u05E1\u05D4/\u05D7\u05D1\u05E8\u05D5\u05EA"},{pattern:/^(?:שם הילד|תעודת זהות של ילד|ילדים|קטין|מספר ילדים|תעודת לידה)$/i,type:"minor_data",label:"\u05DE\u05D9\u05D3\u05E2 \u05E2\u05DC \u05E7\u05D8\u05D9\u05DF"},{pattern:/^(?:דת|לאום|מין|נטייה מינית|השקפה פוליטית|מצב משפחתי|עדה|מוצא)$/i,type:"sensitive_personal",label:"\u05DE\u05D9\u05D3\u05E2 \u05D0\u05D9\u05E9\u05D9 \u05E8\u05D2\u05D9\u05E9"},{pattern:/^(?:מספר רכב|מס רכב|לוחית רישוי|מספר רישוי)$/i,type:"tracking",label:"\u05DE\u05E1\u05E4\u05E8 \u05E8\u05DB\u05D1"}],Qr=/^(id|uuid|created_?at|updated_?at|deleted_?at|status|type|category|description|title|slug|url|path|key|value|enabled|active|public|version|metadata|config|settings|data|content|note|comment|label|color|icon)$/i,ei=/^שם[ _](?:ה)?(?:קובץ|מוצר|פריט|חברה|ארגון|עסק|פרויקט|גיליון|טבלה|שדה|קטגוריה|סניף|בנק|עיר|רחוב|ישוב|יישוב|מסמך|תוכנית|חבילה|קורס|מחלקה|אתר|דומיין|מותג|ספק|שירות|תפקיד)$|^(file|product|item|company|org|organization|project|sheet|table|field|category|branch|bank|city|street|document|plan|course|department|site|domain|brand|vendor|service|role)[ _-]?name$/i;function _e(e){if(!e)return null;let t=e.trim();if(Qr.test(t)||/^name$/i.test(t)||/^\[[A-Z_]+(?:-\d+)?\]$/.test(t))return null;let n=t.replace(/[\s-]+/g,"_");for(let r of[...qr,...Yr])if(r.pattern.test(t)||r.pattern.test(n))return{type:r.type,label:r.label};if(ei.test(t))return null;if(/^שם[ _][\u0590-\u05FF"'׳ ]{2,20}$/.test(t))return{type:"name",label:"\u05E9\u05DD \u05D0\u05D3\u05DD"};if(/^(customer|client|employee|inspector|supervisor|contact|person|patient|user|owner|manager|agent|member|driver|tenant|student|parent|beneficiary|insured)_?(full_?)?name$/i.test(n))return{type:"name",label:"Person name"};for(let r of yn()){let i=r(t);if(i)return{type:i.type,label:i.label}}return null}var Qe={id:"critical",financial:"critical",medical:"critical",biometric:"critical",minor_data:"critical",sensitive_personal:"critical",email:"high",phone:"high",name:"high",address:"high",dob:"high",tracking:"medium",location:"high",auth:"critical",employment:"medium",education:"medium",behavior:"high"};function et(e){let t=String(e??"").replace(/\D/g,"");if(t.length===0||t.length>9)return!1;let n=t.padStart(9,"0"),r=0;for(let i=0;i<9;i++){let s=parseInt(n[i]??"0",10)*(i%2+1);s>9&&(s-=9),r+=s}return r%10===0&&parseInt(n,10)>0}var Pt=/(?<!\d)(?<!\d[.,])(?:\+972[\s-]?|0)(?:5[0-9]|7[0-9]|[2-4]|[89])(?:[\s-]?\d){7}(?!\d)(?![.,]\d)/g;var Oe=/\bIL\d{2}\s?(?:\d{4}\s?){4}\d{3}\b/g;function ti(e){let t=String(e??"").replace(/\D/g,"");if(t.length<12||t.length>19)return!1;let n=0,r=!1;for(let i=t.length-1;i>=0;i-=1){let s=parseInt(t[i]??"0",10);r&&(s*=2,s>9&&(s-=9)),n+=s,r=!r}return n%10===0}function tt(e){let t=String(e??"").replace(/\s/g,"").toUpperCase();if(!/^[A-Z]{2}\d{2}[A-Z0-9]{10,30}$/.test(t))return!1;let n=t.slice(4)+t.slice(0,4),r=0;for(let i of n){let s=i>="A"&&i<="Z"?String(i.charCodeAt(0)-55):i;for(let o of s)r=(r*10+Number(o))%97}return r===1}var $t="(?<!\\d)(?<!\\d[.,])",Rt="(?!\\d)(?![.,]\\d)",De=[{subkind:"israeli_id",severity:"high",pattern:new RegExp(`${$t}(?:\\d{8}-\\d|\\d{9})${Rt}`,"g"),validate:et,hint:"Israeli national ID (check digit verified) in plaintext. Encrypt the column or mask it at rest."},{subkind:"credit_card",severity:"critical",pattern:new RegExp(`${$t}[2-6][ -]?(?:\\d[ -]?){11,17}\\d${Rt}`,"g"),validate:ti,hint:"Payment card number (Luhn verified) in plaintext. This is PCI scope."},{subkind:"iban",severity:"high",pattern:/\b[A-Z]{2}\d{2}(?: ?[A-Z0-9]{4}){2,7}(?: ?[A-Z0-9]{1,4})?\b/g,validate:tt,hint:"Bank account in IBAN form (mod-97 verified) in plaintext."},{subkind:"israeli_phone",severity:"medium",pattern:new RegExp(`${$t}(?:\\+972[- ]?|0)(?:[57]\\d|[2-489])[- ]?\\d{3}[- ]?\\d{4}${Rt}`,"g"),validate:e=>{let t=e.replace(/\D/g,"").replace(/^972/,"0");return/^0[57]\d{8}$/.test(t)?!0:/^0[2-489]\d{7}$/.test(t)&&(/[- ]/.test(e)||e.startsWith("+972"))},hint:"Israeli phone number in plaintext."}];function kn(e){if(e.length!==14&&e.length!==17)return!1;let t=+e.slice(0,4),n=+e.slice(4,6),r=+e.slice(6,8),i=+e.slice(8,10),s=+e.slice(10,12),o=+e.slice(12,14);return t>=1970&&t<=2099&&n>=1&&n<=12&&r>=1&&r<=31&&i<24&&s<60&&o<60}function Sn(e){let t=Number(e);return!Number.isSafeInteger(t)||t<1024?!1:Number.isInteger(Math.log2(t))||t%1048576===0}var ni=new Set(["000000018","123456782","111111118","222222226","333333334","999999998","4111111111111111","4242424242424242","4000056655665556","5555555555554444","5105105105105100","2223003122003222","378282246310005","371449635398431","6011111111111117","3056930009020004","4012888888881881","4000000000000002"]);function Pe(e){return ni.has(e.replace(/\D/g,""))}function nt(e){let t=e.replace(/\D/g,"");return kn(t)||Sn(t)}function Ne(e,t){let n=[];for(let r of De){r.pattern.lastIndex=0;let i=new Set,s,o=0;for(;(s=r.pattern.exec(e))!==null&&(o+=1,!(o>2e5));){let a=s[0];if(!(r.subkind!=="iban"&&r.subkind!=="israeli_phone"&&nt(a))&&(r.validate(a)&&i.add(a.replace(/[\s-]/g,"")),i.size>=1e5))break}if(i.size>0){if(t)for(let c of i)Pe(c)||t(r.subkind,c);let a=[...i].filter(c=>!Pe(c)).length;n.push({subkind:r.subkind,severity:a===0?"low":r.severity,hint:a===0?`${r.hint} All ${i.size} are published test numbers.`:r.hint,count:i.size,real:a})}}for(let r of qe()){let i=new Set;for(let s of r.find(e))i.add(e.slice(s.start,s.end).trim());i.size>0&&n.push({subkind:r.kind,severity:r.severity,hint:r.hint,count:i.size})}return n}function Fe(e,t){let n=[],r="",i=!1;for(let s=0;s<e.length;s++){let o=e[s];i?o==='"'&&e[s+1]==='"'?(r+='"',s++):o==='"'?i=!1:r+=o:o==='"'?i=!0:o===t?(n.push(r),r=""):r+=o}return n.push(r),n}import{inflateRawSync as ns}from"node:zlib";import{inflateSync as En}from"node:zlib";var Me=new Set([".pdf"]),An=8*1024*1024;function rt(e){let t=e.toString("latin1"),n=new Map,r=/(\d+)\s+(\d+)\s+obj\b/g,i;for(;(i=r.exec(t))!==null;){let s=Number(i[1]),o=r.lastIndex,a=t.indexOf("endobj",o);if(a<0)continue;let c=t.indexOf("stream",o),l,p=null;if(c>=0&&c<a){l=t.slice(o,c);let u=c+6;t[u]==="\r"&&(u+=1),t[u]===`
4
4
  `&&(u+=1);let h=t.indexOf("endstream",u);h>u&&(p=e.subarray(u,h))}else l=t.slice(o,a);n.set(s,{num:s,dict:l,stream:p})}for(let s of[...n.values()]){if(!s.stream||!/\/Type\s*\/ObjStm\b/.test(s.dict))continue;let o=je(s),a=Number(/\/N\s+(\d+)/.exec(s.dict)?.[1]??0),c=Number(/\/First\s+(\d+)/.exec(s.dict)?.[1]??-1);if(o===null||!a||c<0)continue;let l=o.slice(0,c).trim().split(/\s+/).map(Number);for(let p=0;p<a;p+=1){let u=l[2*p],h=l[2*p+1];if(u===void 0||h===void 0||Number.isNaN(u)||Number.isNaN(h))break;let y=p+1<a?l[2*(p+1)+1]:void 0,_=o.slice(c+h,y!==void 0?c+y:void 0);n.has(u)||n.set(u,{num:u,dict:_,stream:null})}}return n}function Ot(e){let t=[...e.replace(/\s+/g,"")];if(t.length<40)return!1;let n=0,r=0;for(let i of t){let s=i.codePointAt(0)??0;s<32||s>=127&&s<=191||s===65533||s>=57344&&s<=63743?n+=1:/[\p{L}\p{N}]/u.test(i)&&(r+=1)}return n/t.length>.2||r/t.length<.25}function ri(e){let t=e.toString("latin1").replace(/\s/g,"");t.startsWith("<~")&&(t=t.slice(2));let n=t.indexOf("~>");n>=0&&(t=t.slice(0,n));let r=[],i=0,s=0;for(let o of t){if(o==="z"&&s===0){r.push(0,0,0,0);continue}let a=o.charCodeAt(0)-33;if(a<0||a>84)return null;i=i*85+a,s+=1,s===5&&(r.push(i>>>24&255,i>>>16&255,i>>>8&255,i&255),i=0,s=0)}if(s>0){for(let a=s;a<5;a+=1)i=i*85+84;let o=[i>>>24&255,i>>>16&255,i>>>8&255,i&255];for(let a=0;a<s-1;a+=1)r.push(o[a]??0)}return Buffer.from(r)}function ii(e){let t=e.toString("latin1").replace(/\s/g,"").split(">")[0]??"",n=t.length%2?t+"0":t;return/^[0-9A-Fa-f]*$/.test(n)?Buffer.from(n,"hex"):null}function si(e){let t=/\/Filter\s*(\[[^\]]*\]|\/\w+)/.exec(e);return!t||!t[1]?[]:[...t[1].matchAll(/\/(\w+)/g)].map(n=>n[1]??"")}function je(e,t){if(!e.stream)return null;let n=e.stream;for(let r of si(e.dict)){let i=null;if(r==="FlateDecode")try{i=t!==void 0?En(n,{maxOutputLength:Math.max(1,t)}):En(n)}catch{return null}else if(r==="ASCII85Decode")i=ri(n);else if(r==="ASCIIHexDecode")i=ii(n);else return null;if(i===null)return null;n=i}return n.toString("latin1")}function oi(e){let t=new Map,n=1,r=/begincodespacerange([\s\S]*?)endcodespacerange/.exec(e);if(r&&r[1]){let c=/<([0-9A-Fa-f]+)>/.exec(r[1]);c&&c[1]&&c[1].length>=4&&(n=2)}let i=c=>{let l="";for(let p=0;p+3<c.length+1;p+=4){let u=parseInt(c.slice(p,p+4),16);Number.isFinite(u)&&u>0&&(l+=String.fromCharCode(u))}return l},s=/beginbfchar([\s\S]*?)endbfchar/g,o;for(;(o=s.exec(e))!==null;){let c=/<([0-9A-Fa-f]+)>\s*<([0-9A-Fa-f]*)>/g,l;for(;(l=c.exec(o[1]??""))!==null;)t.set(parseInt(l[1]??"0",16),i(l[2]??""))}let a=/beginbfrange([\s\S]*?)endbfrange/g;for(;(o=a.exec(e))!==null;){let c=o[1]??"",l=/<([0-9A-Fa-f]+)>\s*<([0-9A-Fa-f]+)>\s*<([0-9A-Fa-f]*)>/g,p;for(;(p=l.exec(c))!==null;){let u=parseInt(p[1]??"0",16),h=parseInt(p[2]??"0",16),y=parseInt((p[3]??"0").slice(0,4)||"0",16);if(!(h-u>65535))for(let _=u;_<=h;_+=1)t.set(_,String.fromCharCode(y+(_-u)))}}return{map:t,bytes:n}}function ai(e){let t=new Map,n=[],r=[];for(let s of e.values()){for(let o of s.dict.matchAll(/\/Font\s*<<([\s\S]*?)>>/g))o[1]&&r.push(o[1]);for(let o of s.dict.matchAll(/\/Font\s+(\d+)\s+\d+\s+R/g)){let a=e.get(Number(o[1]));a&&r.push(a.dict)}}let i=new Set;for(let s of r){let o=/\/([^\s/<>[\]()]+)\s+(\d+)\s+\d+\s+R/g,a;for(;(a=o.exec(s))!==null;){let c=`${a[1]}#${a[2]}`;i.has(c)||(i.add(c),n.push([a[1]??"",Number(a[2])]))}}for(let[s,o]of n){let a=e.get(o);if(!a)continue;let c=null,l=/\/ToUnicode\s+(\d+)\s+\d+\s+R/.exec(a.dict);if(l){let u=e.get(Number(l[1])),h=u?je(u):null;h&&(c=oi(h))}if(c||(c=ui(a.dict,e)),!c)continue;let p=t.get(s)??[];p.push(c),t.set(s,p)}return t}var ci=["alef","bet","gimel","dalet","he","vav","zayin","het","tet","yod","finalkaf","kaf","lamed","finalmem","mem","finalnun","nun","samekh","ayin","finalpe","pe","finaltsadi","tsadi","qof","resh","shin","tav"];function li(e){let t=/^uni([0-9A-Fa-f]{4})$/.exec(e)??/^u([0-9A-Fa-f]{4,6})$/.exec(e);if(t)return String.fromCodePoint(parseInt(t[1],16));let n=/^afii(\d{5})$/.exec(e);if(n){let s=Number(n[1]);if(s>=57664&&s<=57690)return String.fromCharCode(1488+(s-57664))}let r=ci.indexOf(e.replace(/hebrew$/,""));return r>=0?String.fromCharCode(1488+r):e==="space"?" ":/^[A-Za-z]$/.test(e)?e:{zero:"0",one:"1",two:"2",three:"3",four:"4",five:"5",six:"6",seven:"7",eight:"8",nine:"9",period:".",comma:",",hyphen:"-",colon:":",slash:"/",at:"@"}[e]??null}function ui(e,t){let n=e,r=/\/Encoding\s+(\d+)\s+\d+\s+R/.exec(e);r&&(n=t.get(Number(r[1]))?.dict??"");let i=/\/Differences\s*\[([^\]]*)\]/.exec(n);if(!i?.[1])return null;let s=new Map,o=0;for(let a of i[1].match(/\d+|\/[^\s/[\]]+/g)??[]){if(/^\d+$/.test(a)){o=Number(a);continue}let c=li(a.slice(1));c!==null&&s.set(o,c),o+=1}return s.size?{map:s,bytes:1}:null}function Cn(e){let t=[];for(let n=0;n<e.length;n+=1){let r=e[n];if(r!=="\\"){t.push(r.charCodeAt(0)&255);continue}let i=e[n+1];if(i===void 0)break;if(i>="0"&&i<="7"){let o="",a=n+1;for(;a<e.length&&o.length<3&&e[a]>="0"&&e[a]<="7";)o+=e[a],a+=1;t.push(parseInt(o,8)&255),n=a-1;continue}let s={n:10,r:13,t:9,b:8,f:12};t.push(s[i]??i.charCodeAt(0)&255),n+=1}return t}function Pn(e){if(e.length<2||e[0]!==254||e[1]!==255)return null;let t="";for(let n=2;n+1<e.length;n+=2)t+=String.fromCharCode((e[n]??0)<<8|(e[n+1]??0));return t}function In(e,t){let n=Pn(e);if(n!==null)return n;if(!t)return e.map(i=>String.fromCharCode(i)).join("");let r="";if(t.bytes===2)for(let i=0;i+1<e.length;i+=2){let s=(e[i]??0)<<8|(e[i+1]??0);r+=t.map.get(s)??""}else for(let i of e)r+=t.map.get(i)??String.fromCharCode(i);return r}function di(e){let t=0;for(let n of e){let r=n.codePointAt(0)??0;/[\p{L}\p{N}]/u.test(n)?t+=1:(r<32||r>=127&&r<=191||r===65533)&&(t-=2)}return t}function Tn(e,t){if(!t||t.length<=1)return In(e,t?.[0]);let n="",r=-1/0;for(let i of t){let s=In(e,i),o=di(s);o>r&&(n=s,r=o)}return n}function fi(e,t){let n="",r="",i,s=!1,o="",a=0,c=null,l=()=>{r.trim()&&(n+=r+`